From 7813a7cde0e7c3fdcd6c342ff1b135c264137e46 Mon Sep 17 00:00:00 2001 From: Degradin Date: Mon, 13 Jan 2025 01:38:51 +0300 Subject: [PATCH] v5.6 Battle Update Preparations --- models/battle.model.js | 5 + rpg.js | 202 +++++++++++++++++++++++++++-------------- 2 files changed, 141 insertions(+), 66 deletions(-) diff --git a/models/battle.model.js b/models/battle.model.js index 713d85d..08afae1 100644 --- a/models/battle.model.js +++ b/models/battle.model.js @@ -28,6 +28,11 @@ const Battle = sequelize.define('battle', { allowNull: false, defaultValue: "inactive" }, + logs: { + type: DataTypes.ARRAY(DataTypes.STRING), + allowNull: true, + defaultValue: [] + }, }); module.exports = Battle; diff --git a/rpg.js b/rpg.js index b56ac04..060c380 100644 --- a/rpg.js +++ b/rpg.js @@ -1551,6 +1551,14 @@ const generateBattles = async () => { const locations = await Location.findAll(); for (const location of locations) { + // Проверяем количество битв для текущей локации + const existingBattlesCount = await Battle.count({ where: { location: location.id, status: 'inactive' } }); + + if (existingBattlesCount >= 10) { + console.log(`Пропущена локация ${location.id}, уже есть 10 битв.`); + continue; // Пропускаем локацию, если уже есть 10 битв + } + const enemies = location.enemies; const generatedEnemies = enemies.map((enemyId) => { const rarity = Math.random(); @@ -1561,18 +1569,29 @@ const generateBattles = async () => { const enemy = await Enemy.findByPk(enemyId); if (!enemy) continue; + // Проверяем, нужно ли создавать битвы (не превышать лимит в 10) + const currentBattlesCount = await Battle.count({ where: { location: location.id, status: 'inactive' } }); + if (currentBattlesCount >= 5) { + console.log(`Достигнут лимит битв для локации ${location.id}.`); + break; + } + await Battle.create({ enemy: enemy.id, location: location.id, character: null, enemy_hp: enemy.hp, status: "inactive", + logs: [], }); } } console.log("Battles generated."); }; + +generateBattles() +schedule.scheduleJob('0 * * * *', generateBattles); // Каждый час в начале часа rpg.command("locations", async (ctx) => { const locations = await Location.findAll(); if (!locations.length) { @@ -1620,13 +1639,23 @@ rpg.action(/hunt_location_\d+/, async (ctx) => { return ctx.reply("Локация не найдена."); } + const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } }); + if (!character) { + return ctx.reply("Ваш персонаж не найден."); + } + + const inventory = await InventoryModel.findAll({ where: { telegram_id: ctx.from.id } }); + const hasGrenadeLow = inventory.some((item) => item.text_id === "grenade_low"); + const hasGrenadeMedium = inventory.some((item) => item.text_id === "grenade_medium"); + const hasGrenadeHard = inventory.some((item) => item.text_id === "grenade_hard"); + const enemyIds = location.enemies || []; if (enemyIds.length === 0) { return ctx.reply("В этой локации сейчас нет врагов. Попробуйте позже."); } const activeBattles = await Battle.findAll({ - where: { location: location.id, status: 'inactive' }, + where: { location: location.id, status: "inactive" }, }); if (activeBattles.length === 0) { @@ -1648,9 +1677,14 @@ rpg.action(/hunt_location_\d+/, async (ctx) => { }; }); - const keyboard = Markup.inlineKeyboard( - buttons.map((btn) => [btn]) - ); + if (hasGrenadeLow || hasGrenadeMedium || hasGrenadeHard) { + buttons.push({ + text: "💣 Использовать гранату", + callback_data: `use_grenade_${locationId}`, + }); + } + + const keyboard = Markup.inlineKeyboard(buttons.map((btn) => [btn])); await ctx.reply( `🔍 Локация: ${location.name}\n\nВыберите врага для охоты:`, @@ -1658,6 +1692,67 @@ rpg.action(/hunt_location_\d+/, async (ctx) => { ); }); +// Использование гранаты +rpg.action(/use_grenade_\d+/, async (ctx) => { + const locationId = ctx.match[0].split('_')[2]; + const location = await Location.findByPk(locationId); + + if (!location) { + return ctx.reply("Локация не найдена."); + } + + const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } }); + if (!character) { + return ctx.reply("Ваш персонаж не найден."); + } + + const inventory = await InventoryModel.findAll({ where: { telegram_id: ctx.from.id } }); + const grenade = + inventory.find((item) => item.text_id === "grenade_hard") || + inventory.find((item) => item.text_id === "grenade_medium") || + inventory.find((item) => item.text_id === "grenade_low"); + + if (!grenade) { + return ctx.reply("У вас нет гранаты для использования."); + } + + const activeBattles = await Battle.findAll({ + where: { location: location.id, status: "inactive" }, + }); + + if (activeBattles.length === 0) { + return ctx.reply("В этой локации нет активных врагов."); + } + + // Урон гранаты + const damage = + grenade.text_id === "grenade_hard" + ? 50 + : grenade.text_id === "grenade_medium" + ? 30 + : 10; + + let message = `💥 Вы использовали гранату (${grenade.text_id}) и нанесли ${damage} урона всем врагам!`; + + for (const battle of activeBattles) { + battle.enemy_hp -= damage; + battle.logs.push(`💣 Враг получил ${damage} урона от гранаты.`); + + if (battle.enemy_hp <= 0) { + battle.status = "completed"; + battle.logs.push("💀 Враг был убит гранатой."); + } + + await battle.save({ fields: ["enemy_hp", "logs", "status"] }); + } + + // Удаляем гранату из инвентаря + await InventoryModel.destroy({ where: { id: grenade.id } }); + + await ctx.reply(message); +}); + + rpg.action(/start_battle_\d+/, async (ctx) => { const battleId = ctx.match[0].split('_')[2]; // Получаем ID битвы const battle = await Battle.findByPk(battleId); @@ -1701,22 +1796,24 @@ const enemyTurn = async (ctx, character, battle) => { const isDodged = Math.random() < dodgeChance; if (isDodged) { - return ctx.reply("💨 Вы уклонились от атаки противника!"); + battle.logs.push(`💨 ${character.name} уклонились от атаки противника!`); + return await battle.save({ fields: ["logs"] }); } 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}.` + battle.status = "inactive"; + battle.logs.push( + `💔 Противник нанес ${character.name} ${damage} урона!\n\n${character.name} потерпел поражение от ${enemy.name}.` ); + return await battle.save({ fields: ["logs", "status"] }); } await character.save(); - await ctx.reply(`💔 Противник нанес вам ${damage} урона. У вас осталось ${character.hp} HP.`); + await battle.logs.push(`💔 Противник нанес ${character.name} ${damage} урона. У ${character.name} осталось ${character.hp} HP.`); + await battle.save({ fields: ["logs"] }); }; rpg.action(/attack_\d+/, async (ctx) => { @@ -1738,44 +1835,8 @@ rpg.action(/attack_\d+/, async (ctx) => { } // Генерация кнопок - 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 buttons = generateBattleButtons(character, battle); + const keyboard = Markup.inlineKeyboard(buttons); // Логи битвы const logs = battle.logs || []; const logMessage = logs.slice(-5).map((log) => `• ${log}`).join("\n"); @@ -1816,13 +1877,14 @@ rpg.action(/hit_\d+/, async (ctx) => { // Логи battle.logs = battle.logs || []; - battle.logs.push(`Вы нанесли ${damage} урона врагу.`); + battle.logs.push(`${character.name} нанес ${damage} урона врагу.`); + await battle.save({ fields: ["enemy_hp", "logs"] }); // Проверка на победу if (battle.enemy_hp <= 0) { battle.status = "completed"; await battle.save(); - return ctx.editMessageText(`🎉 Вы победили ${enemy.name}!`); + return ctx.editMessageText(`🎉 ${character.name} победил ${enemy.name}!`); } await battle.save(); @@ -1843,32 +1905,35 @@ rpg.action(/hit_\d+/, async (ctx) => { ); }); -// Функция для генерации кнопок 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); + const intelligenceFactor = Math.min(character.intelligence || 1, totalButtons - 2); // Уменьшаем на 2 для кнопки критического удара и гарантированного промаха + const buttons = []; // Генерация кнопок с уроном от атаки for (let i = 0; i < intelligenceFactor; i++) { buttons.push({ - text: `Урон: ${hitDamage()}`, + text: `🎯`, callback_data: `hit_${battle.id}`, }); } // Генерация кнопки с критическим уроном buttons.push({ - text: `Крит: ${criticalDamage}`, + text: `💥`, callback_data: `critical_${battle.id}`, }); - // Заполнение оставшихся кнопок промахами + // Гарантированная кнопка промаха + buttons.push({ + text: "❌", + callback_data: `miss_${battle.id}`, + }); + + // Заполнение оставшихся кнопок (если не хватает до totalButtons) while (buttons.length < totalButtons) { buttons.push({ - text: "Промах", + text: "❌", callback_data: `miss_${battle.id}`, }); } @@ -1876,7 +1941,7 @@ function generateBattleButtons(character, battle) { // Перетасовывание кнопок const shuffledButtons = buttons.sort(() => Math.random() - 0.5); - // Разбиение кнопок на 2 ряда (5 кнопок в каждой строке) + // Разбиение кнопок на 2 строки (по 5 кнопок в каждой строке) const rows = []; for (let i = 0; i < shuffledButtons.length; i += 5) { rows.push(shuffledButtons.slice(i, i + 5)); @@ -1886,6 +1951,7 @@ function generateBattleButtons(character, battle) { } + // Обработка критического удара rpg.action(/critical_\d+/, async (ctx) => { const battleId = ctx.match[0].split("_")[1]; @@ -1911,18 +1977,18 @@ rpg.action(/critical_\d+/, async (ctx) => { // Логи battle.logs = battle.logs || []; - battle.logs.push(`Критический удар! Вы нанесли ${damage} урона врагу.`); + battle.logs.push(`Критический удар! ${character.name} нанес ${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")}` + `🎉 ${character.name} победил ${enemy.name}!\n\n📜 Логи битвы:\n${battle.logs.slice(-5).map((log) => `• ${log}`).join("\n")}` ); } - await battle.save(); + await battle.save({ fields: ["enemy_hp", "logs"] }); // Генерация новых кнопок const buttons = generateBattleButtons(character, battle); @@ -1962,16 +2028,20 @@ rpg.action(/miss_\d+/, async (ctx) => { // Логи battle.logs = battle.logs || []; - battle.logs.push("Вы промахнулись."); + battle.logs.push(`${character.name} промахнулся.`); await enemyTurn(ctx, character, battle); - await battle.save(); + await battle.save({ fields: ["enemy_hp", "logs"] }); // Генерация новых кнопок const buttons = generateBattleButtons(character, battle); const keyboard = Markup.inlineKeyboard(buttons); const logs = battle.logs.slice(-5).map((log) => `• ${log}`).join("\n"); - + if (character.hp <= 0) { + return ctx.editMessageText( + `💔 ${character.name} потерпел поражение от ${enemy.name}!\n\n📜 Логи битвы:\n${logs}` + ); + } await ctx.editMessageText( `⚔️ Сражение с ${enemy.name}\n\n` + `❤️ Здоровье врага: ${battle.enemy_hp}/${enemy.hp}\n` +