v5.5
Battle Update Prepare
This commit is contained in:
parent
fb757f9f0a
commit
b4087cf26c
@ -19,6 +19,9 @@ module.exports = {
|
|||||||
CharacterModel : require('../models/character.model'),
|
CharacterModel : require('../models/character.model'),
|
||||||
InventoryModel : require('../models/inventory.model'),
|
InventoryModel : require('../models/inventory.model'),
|
||||||
ItemsModel : require('../models/items.model'),
|
ItemsModel : require('../models/items.model'),
|
||||||
|
Enemy : require('../models/enemy.model'),
|
||||||
|
Location : require('../models/location.model'),
|
||||||
|
Battle : require('../models/battle.model'),
|
||||||
StolenCardsModel : require('../models/stolencards.model'),
|
StolenCardsModel : require('../models/stolencards.model'),
|
||||||
WorldModel : require('../models/world.model'),
|
WorldModel : require('../models/world.model'),
|
||||||
JobModel : require('../models/job.model'),
|
JobModel : require('../models/job.model'),
|
||||||
|
33
models/battle.model.js
Normal file
33
models/battle.model.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const { DataTypes } = require('sequelize');
|
||||||
|
const sequelize = require('../db');
|
||||||
|
|
||||||
|
const Battle = sequelize.define('battle', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
enemy: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
location: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
character: {
|
||||||
|
type: DataTypes.BIGINT,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
enemy_hp: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: "inactive"
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = Battle;
|
40
models/enemy.model.js
Normal file
40
models/enemy.model.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
const { DataTypes } = require('sequelize');
|
||||||
|
const sequelize = require('../db');
|
||||||
|
|
||||||
|
const Enemy = sequelize.define('enemy', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
level: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
hp: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
damage: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
loot: {
|
||||||
|
type: DataTypes.ARRAY(DataTypes.INTEGER),
|
||||||
|
allowNull: true, defaultValue: []
|
||||||
|
},
|
||||||
|
rarity: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = Enemy;
|
38
models/location.model.js
Normal file
38
models/location.model.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
const { DataTypes } = require('sequelize');
|
||||||
|
const sequelize = require('../db');
|
||||||
|
|
||||||
|
const Location = sequelize.define('location', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
enemies: {
|
||||||
|
type: DataTypes.ARRAY(DataTypes.INTEGER),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: []
|
||||||
|
},
|
||||||
|
level: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
loot: {
|
||||||
|
type: DataTypes.ARRAY(DataTypes.INTEGER),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: []
|
||||||
|
},
|
||||||
|
rarity: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = Location;
|
470
rpg.js
470
rpg.js
@ -14,6 +14,9 @@ const {
|
|||||||
CharacterModel,
|
CharacterModel,
|
||||||
InventoryModel,
|
InventoryModel,
|
||||||
ItemsModel,
|
ItemsModel,
|
||||||
|
Enemy,
|
||||||
|
Location,
|
||||||
|
Battle,
|
||||||
StolenCardsModel,
|
StolenCardsModel,
|
||||||
WorldModel,
|
WorldModel,
|
||||||
PropertyModel,
|
PropertyModel,
|
||||||
@ -1502,13 +1505,480 @@ 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
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user