diff --git a/rpg.js b/rpg.js index b6909e2..e70fb9a 100644 --- a/rpg.js +++ b/rpg.js @@ -1997,6 +1997,39 @@ rpg.action(/viewlocation_\d+/, async (ctx) => { await ctx.editMessageText(description, keyboard); }); +rpg.action(/explore_\d+/, async (ctx) => { + const locationId = ctx.match[0].split("_")[1]; + 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("Персонаж не найден."); + } + +// Проверяем, не превышает ли уровень локации уровень персонажа на 3 + if (character.level < location.level || character.level > location.level + 3) { + return ctx.reply("📛 Здесь слишком опасно."); + } + +// Проверяем стамину + if (character.stamina < location.level) { + return ctx.reply("📛 У вас недостаточно стамины для исследования."); + } + + character.stamina -= location.level; // Снимаем стамину за исследование + await character.save(); +// У локации такой же свой массив лута как и у врагов, поэтому можно использовать ту же функцию dropLoot + const loot = dropLoot(location.loot); + const experience = Math.floor(Math.random() * 10) + 5; // Опыт за исследование + await Exp(ctx, character, experience) + await ctx.reply(`Вы исследовали локацию ${location.name} и нашли:\n${loot.join("\n")}\n\n`); +}); + rpg.action(/hunt_location_\d+/, async (ctx) => { const locationId = ctx.match[0].split('_')[2]; // Получаем ID локации const location = await Location.findByPk(locationId); @@ -2244,8 +2277,6 @@ rpg.action(/attack_\d+/, async (ctx) => { const battleId = ctx.match[0].split("_")[1]; const battle = await Battle.findByPk(battleId); - - if (!battle || battle.status !== "active") { logs(ctx, "Сражение завершено или не существует", { battleId }); return ctx.reply("Сражение завершено или не существует."); @@ -2287,134 +2318,19 @@ rpg.action(/attack_\d+/, async (ctx) => { ); }); -// Обработка попадания -rpg.action(/hit_\d+/, async (ctx) => { - const battleId = ctx.match[0].split("_")[1]; - const battle = await Battle.findByPk(battleId); +async function generateBattleButtons(character) { + const buttons = [ + { + text: "🗡️ Атака", + callback_data: "hit", + }, + { + text: "🛡️ Защита", + callback_data: "defend", + }, + ]; - if (!battle || battle.status !== "active") { - logs(ctx, "Сражение завершено или не существует", { battleId }); - return ctx.reply("Сражение завершено или не существует."); - } - - const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } }); - if (!character || battle.character != ctx.from.id) { - logs(ctx, "Персонаж не найден или не участвует в битве", { telegramId: ctx.from.id, battleId }); - return ctx.reply("Это не ваша битва."); - } - - const enemy = await Enemy.findByPk(battle.enemy); - if (!enemy) { - logs(ctx, "Противник не найден", { enemyId: battle.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(`${character.name} нанес ${damage} урона врагу.`); - await battle.save({ fields: ["enemy_hp", "logs"] }); - - // Проверка на победу - if (battle.enemy_hp <= 0) { - battle.status = "completed"; - await battle.save(); - let items = ""; - logs(ctx, "Победа персонажа", { enemy, battle }); - Exp(ctx, character, enemy.level * 5) - character.enemiesKilled += 1; - await character.save(); - let droppedItems = await dropItem(character, enemy.id); - logs(ctx, "Предметы с врага", { droppedItems }); - if (droppedItems) { - for (const item of droppedItems) { - await InventoryModel.create({ - telegram_id: character.telegram_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, - img: item.img - }); - } - items = droppedItems.map((item) => item.name).join(", "); - } - return ctx.editMessageText( - `🎉 ${character.name} победил ${enemy.name}!\n\n📜 Логи битвы:\n${battle.logs.slice(-5).map((log) => `• ${log}`).join("\n")}\n🧩 Выпавшие предметы: ${items}` - ); - } - - await battle.save(); - - // Обновление сообщения с новыми кнопками - const buttons = generateBattleButtons(character, battle); // Генерация новых кнопок - const keyboard = Markup.inlineKeyboard(buttons); - - const logMessage = battle.logs.slice(-5).map((log) => `• ${log}`).join("\n"); - - logs(ctx, "Попадание", { enemy, battle, damage }); - await ctx.editMessageText( - `⚔️ Сражение с ${enemy.name}\n\n` + - `❤️ Здоровье врага: ${battle.enemy_hp}/${enemy.hp}\n` + - `⚔️ Урон врага: ${enemy.damage}\n\n` + - `📜 Логи битвы:\n${logMessage|| "Пока ничего не произошло."}\n\n` + - `🎯 Выберите цель для атаки:`, - keyboard - ); -}); - -function generateBattleButtons(character, battle) { - let ctx = { from: { id: character.telegram_id } }; - const totalButtons = 10; - const intelligenceFactor = Math.min(character.intelligence || 1, 100); // Ограничиваем интеллект значением 100 (или любым другим максимальным значением) - const attackChance = (intelligenceFactor / 100); // Вероятность появления кнопки атаки - const missChance = 1 - attackChance; // Вероятность промаха - - const buttons = []; - - // Обязательно добавляем одну кнопку с критическим ударом - buttons.push({ - text: `🎯`, // Символ для критического удара - callback_data: `critical_${battle.id}`, - }); - - // Генерация оставшихся кнопок для битвы - while (buttons.length < totalButtons) { - if (Math.random() < attackChance) { - // Кнопка атаки (с шансом, определяемым интеллектом) - buttons.push({ - text: `🎯`, // Символ для атаки - callback_data: `hit_${battle.id}`, - }); - } else if (Math.random() < missChance) { - // Кнопка промаха (с шансом появления промаха) - 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)); - } - - logs(ctx, "Генерация кнопок для битвы", { battle, keyboard: rows }); - return rows; + return [buttons]; // Кнопки выводятся в одной строке } @@ -2560,136 +2476,123 @@ async function testDropItemFast() { //setTimeout(testDropItemFast, 3000); - - -// Обработка критического удара -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("Сражение завершено или не существует."); - } - +rpg.action("attack", async (ctx) => { const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } }); - if (!character || battle.character != ctx.from.id) { - return ctx.reply("Это не ваша битва."); - } - + const battle = await Battle.findOne({ where: { character: character.telegram_id, status: "active" } }); const enemy = await Enemy.findByPk(battle.enemy); - if (!enemy) { - return ctx.reply("Противник не найден."); + + if (!character || !battle || !enemy) { + return ctx.reply("Ошибка: персонаж или битва не найдены."); } - // Критический урон - const damage = Math.floor((character.force || 10) * 2); - battle.enemy_hp -= damage; + // Расчет шансов + const hitChance = 50 + character.endurance / 10; // Шанс попадания + const critChance = character.intelligence / 20; // Шанс крита + const critMultiplier = 1.5 + character.force / 100; // Критический урон - // Логи + // Генерация результата атаки + const hitRoll = Math.random() * 100; + let damage = 0; + let result = "промах"; + + if (hitRoll <= hitChance) { + // Попадание + if (Math.random() * 100 <= critChance) { + damage = Math.floor(character.force * critMultiplier); // Критический удар + result = "критический удар"; + } else { + damage = character.force; // Обычная атака + result = "попадание"; + } + battle.enemy_hp -= damage; + } else { + // Промах — враг делает ход + await enemyTurn(ctx, character, battle); + } + + // Логи боя battle.logs = battle.logs || []; - battle.logs.push(`Критический удар! ${character.name} нанес ${damage} урона врагу.`); + battle.logs.push(`${character.name} нанес ${damage} урона (${result}).`); + await battle.save({ fields: ["enemy_hp", "logs"] }); // Проверка на победу if (battle.enemy_hp <= 0) { battle.status = "completed"; await battle.save(); - let items = ''; - Exp(ctx, character, enemy.level * 6) + let items = ""; + Exp(ctx, character, enemy.level * 6); character.enemiesKilled += 1; await character.save(); - let droppedItems = await dropItem(character, enemy.id); - logs(ctx, "Предметы с врага", { droppedItems }); + const droppedItems = await dropItem(character, enemy.id); + if (droppedItems.length > 0) { for (const item of droppedItems) { await InventoryModel.create({ telegram_id: character.telegram_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, - img: item.img + ...item, // Запись всех параметров предмета }); } items = droppedItems.map((item) => item.name).join(", "); } - + return ctx.editMessageText( - `🎉 ${character.name} победил ${enemy.name}!\n\n📜 Логи битвы:\n${battle.logs.slice(-5).map((log) => `• ${log}`).join("\n")}\n🧩 Выпавшие предметы: ${items}` + `🎉 ${character.name} победил ${enemy.name}!\n\n📜 Логи битвы:\n${battle.logs + .slice(-5) + .map((log) => `• ${log}`) + .join("\n")}\n🧩 Выпавшие предметы: ${items}` ); } - await battle.save({ fields: ["enemy_hp", "logs"] }); - - // Генерация новых кнопок - const buttons = generateBattleButtons(character, battle); + // Продолжение боя + const logMessage = battle.logs.slice(-5).map((log) => `• ${log}`).join("\n"); + const buttons = generateBattleButtons(character); const keyboard = Markup.inlineKeyboard(buttons); - const logMessage = battle.logs.slice(-5).map((log) => `• ${log}`).join("\n"); - logs(ctx, "Критический удар", { enemy, battle, damage }); await ctx.editMessageText( `⚔️ Сражение с ${enemy.name}\n\n` + `❤️ Здоровье врага: ${battle.enemy_hp}/${enemy.hp}\n` + - `⚔️ Урон врага: ${enemy.damage}\n\n` + - `📜 Логи битвы:\n${logMessage|| "Пока ничего не произошло."}\n\n` + - `🎯 Выберите цель для атаки:`, + `📜 Логи битвы:\n${logMessage}\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("Сражение завершено или не существует."); +rpg.command('checkstats', async (ctx) => { + const telegramId = ctx.from.id; + + // Ищем персонажа + const character = await CharacterModel.findOne({ where: { telegram_id: telegramId } }); + + if (!character) { + return ctx.reply('Персонаж не найден. Создайте нового персонажа, чтобы начать игру!'); } + const hitChance = Math.min(90, 50 + character.endurance / 10); + const critChance = Math.min(90, character.intelligence / 20); + const critMultiplier = 1.5 + character.force / 100; + // Формируем профиль + const profile = ` +🎭 Профиль персонажа - const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } }); - if (!character || battle.character != ctx.from.id) { - return ctx.reply("Это не ваша битва."); - } +👤 Имя: ${character.name || 'Не указано'} +🏆 Уровень: ${character.level} (${character.exp}/${utils.spaces(expToUp[character.level])}) - const enemy = await Enemy.findByPk(battle.enemy); - if (!enemy) { - return ctx.reply("Противник не найден."); - } +❤️ Здоровье: ${character.hp}/${character.max_hp} +🔥 Стамина: ${character.stamina}/${character.max_stamina} - // Логи - battle.logs = battle.logs || []; - battle.logs.push(`${character.name} промахнулся.`); - await battle.save({ fields: ["enemy_hp", "logs"] }); - await enemyTurn(ctx, character, battle); - +💪 Сила (F): ${character.force} +🧠 Интеллект (I): ${character.intelligence} +🛡️ Устойчивость (R): ${character.resilience} +🔋 Выносливость (E): ${character.endurance} - // Генерация новых кнопок - const buttons = generateBattleButtons(character, battle); - const keyboard = Markup.inlineKeyboard(buttons); + 🎯 Шанс попадания: ${hitChance.toFixed(1)}% + 🔥 Шанс крита: ${critChance.toFixed(1)}% + 💥 Критический урон: ${(critMultiplier * 100).toFixed(1)}% - const logMessage = battle.logs.slice(-5).map((log) => `• ${log}`).join("\n"); - if (character.hp <= 0) { - logs(ctx, "Поражение персонажа", { enemy, battle }); - return ctx.editMessageText( - `💔 ${character.name} потерпел поражение от ${enemy.name}!\n\n📜 Логи битвы:\n${logMessage}` - ); - } - logs(ctx, "Промах", { enemy, battle }); - await ctx.editMessageText( - `⚔️ Сражение с ${enemy.name}\n\n` + - `❤️ Здоровье врага: ${battle.enemy_hp}/${enemy.hp}\n` + - `⚔️ Урон врага: ${enemy.damage}\n\n` + - `📜 Логи битвы:\n${logMessage|| "Пока ничего не произошло."}\n\n` + - `🎯 Выберите цель для атаки:`, - keyboard - ); + `; + // Отправляем сообщение + ctx.reply(profile.trim()); }); - module.exports = rpg;