CampFirePlay/rpg.js
2025-02-20 09:04:38 +03:00

2619 lines
107 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { Telegraf, Scenes, session, Markup, Stage, Composer } = require('telegraf');
const schedule = require('node-schedule');
const path = require('path');
const fs = require('fs');
const { faker, fakerRU } = require('@faker-js/faker');
// Подключаем необходимые библиотеки
const { Op } = require('sequelize');
const sequelize = require('./db'); // Подключение базы данных
// Подключаем обработчики
const utils = require('./utils');
const handlers = require('./handlers');
const {
phones,
expToUp,
UserModel,
CharacterModel,
InventoryModel,
ItemsModel,
Enemy,
Location,
Battle,
StolenCardsModel,
WorldModel,
PropertyModel,
DailyModel,
logs
} = global.config
const rpg = new Composer();
rpg.use(async (ctx, next) => {
if (ctx.update.edited_channel_post) return console.log(`[RPG] Channel post updated`);
let id = ctx.from.id
global.messagesCounter.inc({ type: ctx.updateType });
let username = ctx.from.username;
if (username == null) username = ctx.from.id;
const currentTime = utils.getCurrentTime();
switch (ctx.updateType) {
case 'message':
console.log(`${currentTime}: [RPG] ${username}: ${ctx.update.message.text}`);
break;
case 'callback_query':
console.log(`${currentTime}: [RPG] ${username}: ${ctx.update.callback_query.data}`);
break;
case 'edited_message':
console.log(`${currentTime}: [RPG] ${username} edited: ${ctx.update.edited_message.text}`);
break;
default:
console.log(`${currentTime}: [RPG] Unknown update type:`, ctx.update);
}
await next(); // Передаем управление следующему middleware
});
const votesFile = path.join(__dirname, '/json/votes.json');
// Функция для загрузки данных из JSON
function loadVotes() {
if (!fs.existsSync(votesFile)) return {};
return JSON.parse(fs.readFileSync(votesFile, 'utf8'));
}
// Функция для сохранения данных в JSON
function saveVotes(votes) {
fs.writeFileSync(votesFile, JSON.stringify(votes, null, 2), 'utf8');
}
rpg.command('offer', async (ctx) => {
try {
const args = ctx.message.text.split(' ').slice(1);
const amount = parseInt(args[0]);
if (!amount || amount <= 0) {
return await ctx.reply('❌ Введите корректную сумму (например: /offer 5000)');
}
let votes = loadVotes();
votes[ctx.from.id] = amount; // Обновление суммы пользователя
saveVotes(votes);
return await ctx.reply(`✅ Вы предложили сумму: ${amount}`);
} catch (error) {
console.error(error);
return await ctx.reply('❌ Произошла ошибка при обработке команды.');
}
});
rpg.command('voteinfo', async (ctx) => {
if (ctx.from.id != 275416286) return;
let users = await UserModel.findAll({ attributes: ['telegram_id', 'username'] });
let userMap = Object.fromEntries(users.map(user => [user.telegram_id, user.username || `ID: ${user.telegram_id}`]));
let votes = loadVotes();
if (Object.keys(votes).length === 0) {
return await ctx.reply('🔹 Пока никто не предложил сумму.');
}
let total = Object.values(votes).reduce((sum, amount) => sum + amount, 0);
let details = Object.entries(votes)
.map(([id, amount]) => `👤 ${userMap[id] || `ID: ${id}`}: ${utils.spaces(amount)}`)
.join('\n');
await ctx.replyWithHTML(`📊 <b>Общий банк:</b> ${utils.spaces(total)}\n\n${details}`);
});
rpg.action('rpg_profile', async (ctx) => {
const telegramId = ctx.from.id;
// Ищем персонажа
const character = await CharacterModel.findOne({ where: { telegram_id: telegramId } });
const stolenCards = await StolenCardsModel.findAll({
where: { userId: character.telegram_id }
});
if (!character) {
return ctx.reply('Персонаж не найден. Создайте нового персонажа, чтобы начать игру!');
}
// Формируем профиль
const profile = `
🎭 Профиль персонажа
👤 Имя: ${character.name || 'Не указано'}
🏆 Уровень: ${character.level} (${character.exp}/${utils.spaces(expToUp[character.level])})
❤️ Здоровье: ${character.hp}/${character.max_hp}
🔥 Стамина: ${character.stamina}/${character.max_stamina}
💪 Сила (F): ${character.force}
🧠 Интеллект (I): ${character.intelligence}
🛡️ Устойчивость (R): ${character.resilience}
🔋 Выносливость (E): ${character.endurance}
Свободные очки F.I.R.E: ${character.firePoints}
💰 Баланс: ${utils.spaces(character.dirtymoney)}Ð
🃏 Украденные карты: ${stolenCards.length}
`;
const buttons = [
[Markup.button.callback('💳 Карточки', 'view_cards')],
[Markup.button.callback('🎒 Инвентарь', 'inventory')],
];
if (character.firePoints >= 1) {
buttons.push([Markup.button.callback(' Распределить очки', 'fire_distribute')]);
}
buttons.push([Markup.button.callback('🔙 В меню', 'crime_menu')]);
// Отправляем сообщение
ctx.editMessageText(profile.trim(), Markup.inlineKeyboard(buttons));
});
// Основная команда для вывода кнопок распределения F.I.R.E.
rpg.action('fire_distribute', async (ctx) => {
// Получаем данные пользователя
const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } });
if (!character) {
return ctx.reply('Вы ещё не создали персонажа!');
}
// Проверяем, есть ли свободные очки
if (character.firePoints <= 0) {
return ctx.reply('У вас нет свободных очков для распределения.');
}
// Формируем текст сообщения
const message = `
🔥 Распределение очков F.I.R.E 🔥
У вас есть ${character.firePoints} свободных очков.
Выберите характеристику, чтобы увеличить её уровень:
💪 Сила (F): ${character.force}
🧠 Интеллект (I): ${character.intelligence}
🛡️ Устойчивость (R): ${character.resilience}
🔋 Выносливость (E): ${character.endurance}
`;
// Создаём кнопки для каждой характеристики
const keyboard = Markup.inlineKeyboard([
Markup.button.callback('💪', 'increase_force'),
Markup.button.callback('🧠', 'increase_intelligence'),
Markup.button.callback('🎯', 'increase_resilience'),
Markup.button.callback('💖', 'increase_endurance'),
], { columns: 2 });
logs(ctx, "Распределение очков F.I.R.E", {firePoints: character.firePoints, force: character.force, intelligence: character.intelligence, resilience: character.resilience, endurance: character.endurance});
// Отправляем сообщение с кнопками
await ctx.reply(message, keyboard);
});
// Обработчики для нажатий на кнопки
rpg.action(/^increase_(force|intelligence|resilience|endurance)$/, async (ctx) => {
const attribute = ctx.match[1]; // Получаем характеристику из callback_data
const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } });
if (!character) {
return ctx.answerCbQuery('Персонаж не найден.');
}
// Проверяем наличие свободных очков
if (character.firePoints <= 0) {
return ctx.answerCbQuery('У вас недостаточно очков!');
}
// Прокачиваем выбранную характеристику
switch (attribute) {
case 'force':
character.force++;
break;
case 'intelligence':
character.intelligence++;
break;
case 'resilience':
character.resilience++;
break;
case 'endurance':
character.endurance++;
break;
}
// Уменьшаем количество очков и сохраняем изменения
character.firePoints--;
logs(ctx, "Увеличение характеристики", { attribute: attribute, firePoints: character.firePoints, force: character.force, intelligence: character.intelligence, resilience: character.resilience, endurance: character.endurance });
await character.save();
// Отправляем обновлённое сообщение
const updatedMessage = `
🔥 Распределение очков F.I.R.E 🔥
У вас есть ${character.firePoints} свободных очков.
Выберите характеристику, чтобы увеличить её уровень:
💪 Сила (F): ${character.force}
🧠 Интеллект (I): ${character.intelligence}
🛡️ Устойчивость (R): ${character.resilience}
🔋 Выносливость (E): ${character.endurance}
`;
await ctx.editMessageText(updatedMessage, { reply_markup: ctx.update.callback_query.message.reply_markup });
ctx.answerCbQuery(`Вы увеличили ${attribute.toUpperCase()}!`);
});
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
// Хранилище для отслеживания пользователей, которые уже прошли ввод
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) && character.level === 1) {
// Если пользователь вводит команду впервые
usersCrimeNet.add(userId);
const username = ctx.from.username || 'User';
const randomServer = `secure.net-${Math.floor(Math.random() * 99 + 1)}.campfiregg.crime`;
const sshCommand = `ssh ${username}@${randomServer}`;
// Сообщение для симуляции
const lines = [
`${sshCommand}`,
`> Connecting to secure servers with RSA key...`,
`> Authentication in progress...`,
`> User "${username}" identified.`,
`> Accessing CampFireGG.Crime...`,
`> Connection established. Welcome, ${username}.`,
];
// Отправляем первое сообщение
let sentMessage = await ctx.reply('> Initializing CampFireGG.Crime...');
// Симуляция показа строки ssh блоками
let currentText = '';
let index = 0;
while (index < lines[0].length) {
const blockSize = Math.floor(Math.random() * 3) + 2; // Генерируем блок от 2 до 4 символов
currentText += lines[0].slice(index, index + blockSize); // Добавляем блок текста
index += blockSize; // Увеличиваем индекс
await ctx.telegram.editMessageText(sentMessage.chat.id, sentMessage.message_id, undefined, currentText);
await delay(100); // Задержка между блоками
}
// Последующие строки добавляются полностью, по одной
for (let i = 1; i < lines.length; i++) {
await delay(1500); // Задержка между строками
currentText += `\n${lines[i]}`;
await ctx.telegram.editMessageText(sentMessage.chat.id, sentMessage.message_id, undefined, currentText);
}
// Финальное меню после задержки
await delay(2000);
const buttons = [
[Markup.button.callback('💼 Задачи', 'crime_missions')],
[Markup.button.callback('📊 Профиль', 'rpg_profile')],
];
//buttons.push([Markup.button.callback('💳 Карточки', 'view_cards')]);
// Добавляем кнопку сообщения, если уровень персонажа равен 1
if (character && character.level === 1) {
buttons.push([Markup.button.callback('📩 (1) Сообщение', '1st_message')]);
}
await ctx.telegram.editMessageText(
sentMessage.chat.id,
sentMessage.message_id,
undefined,
`🚨 Добро пожаловать в CampFireGG.Crime 🚨\n\n`,
Markup.inlineKeyboard(buttons)
)
} else {
// Показываем только меню, если команда уже была введена
await ctx.reply(
`🚨 Добро пожаловать в CampFireGG.Crime 🚨\n\n`,
Markup.inlineKeyboard([
[Markup.button.callback('💼 Задачи', 'crime_missions')],
[Markup.button.callback('📊 Профиль', 'rpg_profile')],
[Markup.button.callback('💰 Магазин', 'shop')],
])
);
}
});
rpg.action('1st_message', async (ctx) => {
await ctx.answerCbQuery();
const message = `👤 Сообщение от неизвестного пользователя:
\`\`\`
"Добро пожаловать в CampFireGG.Crime. Если ты здесь, значит, у тебя есть вопросы. Забудь о них. Просто слушай."
🔥 F.I.R.E — твой путь к успеху:
"Каждый из нас что-то может. Кто-то силён, кто-то вынослив, а кто-то дохуя умный. В мире CampFireGG.Crime это называется F.I.R.E. Запомни эти буквы — от них зависит твоя жизнь здесь."
Force: Твоя мощь. Определяет, сколько импакта ты можешь внести. Чем выше, тем проще ликвидировать цель.
Intelligence: Твой интеллект. Чем ты умнее, тем больше шансов, что операция пройдет гладко. А ещё мозги ускоряют твое обучение.
Resilience: Твоя стойкость. Сколько времени тебе нужно, чтобы встать на ноги после провала? Чем выше этот параметр, тем быстрее ты восстанавливаешься.
Endurance: Твоя выносливость. Без неё ты не сможешь много работать, а значит, и много зарабатывать.
💰 Деньги? Грязные. Иначе здесь не бывает.
"Всё, что ты заработаешь здесь, будет грязным. Потратить можно только здесь, но есть способ отмыть деньги и перенести их на свой основной счёт. Всё зависит от твоего желания и возможностей."
💳 Карты? Да, их можно обналичить.
"Карточки — это всегда плюс. Взломал, достал, получил деньги. Всё просто. Главное — не профукай их."
🕶️ Последний совет:
"Каждое действие — это риск. Чем больше ты идёшь на риск, тем больше получаешь. Но если ошибёшься, можешь встрять. Так что думай."
"Добро пожаловать. Удачи тебе, новобранец."
\`\`\`
`;
await ctx.replyWithMarkdownV2(message, { disable_web_page_preview: true });
});
rpg.action('view_cards', async (ctx) => {
try {
// Получаем пользователя по Telegram ID
const character = await CharacterModel.findByPk(ctx.from.id);
if (!character) {
return ctx.reply('Персонаж не найден!');
}
// Получаем все украденные карточки для этого пользователя
const stolenCards = await StolenCardsModel.findAll({
where: { userId: character.telegram_id }
});
if (stolenCards.length === 0) {
return ctx.reply('У вас нет украденных карточек.');
}
const buttons = [];
let message = 'Ваши украденные карты:\n\n';
stolenCards.forEach(card => {
const lastFourDigits = card.cardNumber.slice(-4); // Берем последние 4 цифры карточки
message += `🔒 Карта: *${lastFourDigits}\n`;
// Генерация кнопок для брутфорса и покупки за лимит
buttons.push([
Markup.button.callback(`💳 *${lastFourDigits}`, `brutecard_${card.id}`),
//Markup.button.callback(`💰 *${lastFourDigits}`, `limitbuy_${card.id}`)
Markup.button.callback('🔙 В меню', 'crime_menu'),
]);
});
// Отправляем сообщение с кнопками
await ctx.reply(message, Markup.inlineKeyboard(buttons));
} catch (error) {
console.error('Ошибка при выводе карточек:', error);
await ctx.reply('Произошла ошибка при получении ваших карточек.');
}
});
rpg.action(/limitbuy_/, async (ctx) => {
try {
const cardId = ctx.match.input.split('_')[1]; // ID карточки
const card = await StolenCardsModel.findByPk(cardId); // Получаем карточку
if (!card) {
return ctx.answerCbQuery('Карточка не найдена.');
}
const character = await CharacterModel.findByPk(ctx.from.id); // Получаем персонажа
if (!character) {
return ctx.answerCbQuery('Персонаж не найден.');
}
// Получаем 10 случайных предметов из базы
const items = await ItemsModel.findAll({ limit: 10 });
// Если у пользователя нет записей о попытках, создаем их
if (!attempts[ctx.from.id]) {
attempts[ctx.from.id] = 0;
}
// Генерация кнопок с предметами
const buttons = [];
items.forEach(item => {
const buttonText = `${item.name} - ¥${item.price}`;
buttons.push([Markup.button.callback(buttonText, `purchase_${item.id}_${card.id}`)]);
});
await ctx.reply(
'Выберите товар для покупки:\nПопробуйте угадать лимит карты.\n\n',
Markup.inlineKeyboard(buttons)
);
} catch (error) {
console.error('Ошибка при обработке покупки через лимит:', error);
}
});
rpg.action(/purchase_/, async (ctx) => {
try {
const [_, itemId, cardId] = ctx.match.input.split('_');
const item = await ItemsModel.findByPk(itemId);
const card = await StolenCardsModel.findByPk(cardId);
const character = await CharacterModel.findByPk(ctx.from.id);
if (!item || !card || !character) {
return ctx.answerCbQuery('Ошибка при обработке покупки.');
}
// Проверяем, не исчерпаны ли попытки
if (attempts[ctx.from.id] >= 3) {
// Сбрасываем попытки и уничтожаем карточку
delete attempts[ctx.from.id];
await StolenCardsModel.destroy({ where: { id: cardId } });
return ctx.editMessageText(
'❌ Все попытки исчерпаны! Карточка стала недействительной.'
);
}
// Если баланс карты меньше, чем цена товара, увеличиваем количество попыток
if (card.balance < item.price) {
attempts[ctx.from.id]++;
await ctx.answerCbQuery(`Недостаточно средств! Попыток осталось: ${3 - ctx.session.attempts}`);
return ctx.editMessageText(`Вы выбрали товар за ¥${item.price}, но у вас недостаточно средств.`);
}
// Если баланс карты достаточно для покупки, добавляем предмет в инвентарь
character.InventoryModel.push(item.id);
await character.save();
// Если товар электронный, начисляем процент от стоимости на счет
if (item.type === 'digital') {
const profit = Math.round(item.price * 0.2); // 20% от стоимости товара
character.dirtymoney += profit;
await character.save();
await ctx.answerCbQuery(`Вы приобрели ${item.name} и перепродали за ¥${profit}.`);
} else {
await ctx.answerCbQuery(`Вы купили ${item.name}.`);
}
// Блокируем карту после покупки
card.destroy({ where: { id: cardId } });
await card.save();
// Оповещаем пользователя
await ctx.editMessageText(`${item.name} успешно куплен! Карта заблокирована.`);
} catch (error) {
console.error('Ошибка при обработке покупки:', error);
}
});
rpg.action(/purchase_fail_/, async (ctx) => {
try {
const [_, itemId, cardId] = ctx.match.input.split('_');
const item = await ItemsModel.findByPk(itemId);
const card = await StolenCardsModel.findByPk(cardId);
if (!item || !card) {
return ctx.answerCbQuery('Ошибка при проверке предмета или карты.');
}
await ctx.answerCbQuery('Недостаточно средств для покупки.');
} catch (error) {
console.error('Ошибка при обработке неудачной покупки:', error);
}
});
// Обработка попытки взлома карточки
rpg.action(/brutecard_/, async (ctx) => {
const cardId = ctx.match.input.split('_')[1]; // Получаем ID карточки из callback_data
const card = await StolenCardsModel.findByPk(cardId); // Ищем карточку в базе данных
const inventory = await InventoryModel.findAll({ where: { telegram_id: ctx.from.id } }); // Загружаем предметы из инвентаря
if (!card) {
return ctx.answerCbQuery('Карточка не найдена.');
}
// Проверяем наличие "Эмулятора картридера" в инвентаре
const emulator = inventory.find((item) => item.text_id === 'cardreader_emulator');
if (!emulator) {
return ctx.reply('Для запуска брутфорса вам нужен "Эмулятор картридера".');
}
// Удаляем "Эмулятор картридера" из инвентаря
await InventoryModel.destroy({ where: { id: emulator.id } });
const buttonsCount = 10; // Количество кнопок
const winButton = utils.rand(1, buttonsCount); // Выбираем случайную кнопку для успешного пина
const maxAttempts = 3; // Максимальное количество попыток
// Если у пользователя нет записей о попытках, создаем их
if (!attempts[ctx.from.id]) {
attempts[ctx.from.id] = 0;
}
// Генерация кнопок
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: `${fakePin}`, callback_data: `brute_fail_${cardId}_${i}` });
}
}
// Разбиваем кнопки на строки по 5 кнопок
const rows = [];
while (buttons.length > 0) {
rows.push(buttons.splice(0, 5)); // Разбиваем на подмассивы по 5 кнопок
}
// Сортируем кнопки случайным образом
const keyboard = Markup.inlineKeyboard(
rows.sort(() => Math.random() - 0.5).map((row) => row) // Сортируем строки кнопок случайным образом
);
logs(ctx, "Брутфорс карты", {card: card, keyboard: keyboard, emulator: emulator});
// Отправляем сообщение с клавиатурой
await ctx.reply(
`💳 Брутфорс карты\n
━━━━━━━━━━━━━━━━━━━━
${card.bankName.toUpperCase()}
━━━━━━━━━━━━━━━━━━━━
Номер: ${card.cardNumber}
CVV: ${card.cvv}
Владелец: ${card.holderName.toUpperCase()}
Срок действия: ${card.expiresDate}
━━━━━━━━━━━━━━━━━━━━
Попробуйте подобрать правильный ПИН-код. У вас есть ${maxAttempts - attempts[ctx.from.id]} попыток.
"Эмулятор картридера" был использован.`,
keyboard
);
});
// Обработка неудачной попытки взлома
rpg.action(/brute_fail_/, async (ctx) => {
const cardId = ctx.match.input.split('_')[2];
const maxAttempts = 3; // Максимальное количество попыток
// Увеличиваем количество попыток
attempts[ctx.from.id]++;
// Проверяем, не исчерпаны ли попытки
if (attempts[ctx.from.id] >= maxAttempts) {
// Сбрасываем попытки и уничтожаем карточку
delete attempts[ctx.from.id];
await StolenCardsModel.destroy({ where: { id: cardId } });
return ctx.editMessageText(
'❌ Все попытки исчерпаны! Карточка стала недействительной.'
);
}
// Ответ на нажатие кнопки
ctx.answerCbQuery(`❌ Неправильный ПИН. Попыток осталось: ${maxAttempts - attempts[ctx.from.id]}`);
// Обновляем клавиатуру
const card = await StolenCardsModel.findByPk(cardId);
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: `${fakePin}`, callback_data: `brute_fail_${cardId}_${i}` });
}
}
const rows = [];
while (buttons.length > 0) {
rows.push(buttons.splice(0, 5));
}
const keyboard = Markup.inlineKeyboard(
rows.sort(() => Math.random() - 0.5).map((row) => row)
);
logs(ctx, "Брутфорс карты (Fail)", {card: card, keyboard: keyboard});
// Обновляем сообщение с клавиатурой
await ctx.editMessageText(
`💳 Брутфорс карты\n
━━━━━━━━━━━━━━━━━━━━
${card.bankName.toUpperCase()}
━━━━━━━━━━━━━━━━━━━━
Номер: ${card.cardNumber}
CVV: ${card.cvv}
Владелец: ${card.holderName.toUpperCase()}
Срок действия: ${card.expiresDate}
━━━━━━━━━━━━━━━━━━━━
Попробуйте подобрать правильный ПИН-код. У вас есть ${maxAttempts - attempts[ctx.from.id]} попыток.`,
keyboard
);
});
// Обработка успешной попытки взлома
rpg.action(/brute_success_/, async (ctx) => {
const cardId = ctx.match.input.split('_')[2];
const card = await StolenCardsModel.findByPk(cardId);
if (!card) {
return ctx.answerCbQuery('Карточка не найдена.');
}
delete attempts[ctx.from.id];
const amount = card.balance; // Сумма денег за карточку
const character = await CharacterModel.findByPk(ctx.from.id);
logs(ctx, "Брутфорс карты (Success)", {card: card, amount: amount});
// Увеличиваем грязные деньги пользователя
character.dirtymoney += amount;
await character.save();
// Удаляем карточку
await card.destroy();
// Отправляем сообщение о выигрыше
await ctx.editMessageText(
`💳 Брутфорс карты\n
━━━━━━━━━━━━━━━━━━━━
${card.bankName.toUpperCase()}
━━━━━━━━━━━━━━━━━━━━
Номер: ${card.cardNumber}
CVV: ${card.cvv}
Владелец: ${card.holderName.toUpperCase()}
Срок действия: ${card.expiresDate}
━━━━━━━━━━━━━━━━━━━━
✅ Взлом успешен! Вы получили Ð${amount}.`
);
});
// Обработчики кнопок
rpg.action('crime_missions', async (ctx) => {
await ctx.answerCbQuery();
await ctx.editMessageText(`💼 **Задачи:**\n Для карманных краж тебе не нужно ничего, главное не попадись.\n А вот после ограбления магазина в переулке не спрячешься, без тачки на миссию нельзя.`,
Markup.inlineKeyboard([
[{ text: 'Карманные кражи [1 lvl.]', callback_data: `POCKET_ACTION` }],
[{ text: 'Магазин [5 lvl.]', callback_data: `SHOP_ACTION` }],
//[{text: 'Банкомат', callback_data: `WIP`}],
// [{text: 'Банковское отделение', callback_data: `WIP`}],
//[{text: 'Угон', callback_data: `WIP`}],
// [{text: 'Ювелирка', callback_data: `WIP`}],
//[{text: 'Банк', callback_data: `WIP`}],
[{ text: '🔙 В меню', callback_data: `crime_menu` }]
]),
);
});
rpg.action(`POCKET_ACTION`, async (ctx) => {
let user = await UserModel.findByPk(ctx.from.id)
let character = await CharacterModel.findByPk(ctx.from.id);
let pocketsteal = character.pocketstealcd
if (character.level < 1) return ctx.editMessageText('Доступно с 1 уровня!')
if (character.stamina < 10) return ctx.editMessageText('Вы устали!')
let cooldown = utils.setCooldown(character, 3600, pocketsteal)
if (character.pocketstealcd > cooldown.currentTime) return ctx.editMessageText(`📛 Данное действие будет доступно через ${cooldown.timeLeftInMinutes} мин.`);
character.pocketstealcd = cooldown.endTime
character.stamina -= 10
logs(ctx, "Карманная кража (Start)", { cdBefore: pocketsteal, cdAfter: character.pocketstealcd, stamina: character.stamina });
character.save()
ctx.editMessageText('Выберите объект', Markup.inlineKeyboard([
[
{ text: 'Карман', callback_data: `POCKET_TARGET` },
{ text: 'Бумажник', callback_data: `POCKET_WALLET` },
{ text: 'Сумка', callback_data: `POCKET_BAG` }
]
]))
});
rpg.action(`POCKET_TARGET`, async (ctx) => {
ctx.editMessageText('В кармане обнаружено', Markup.inlineKeyboard([
[
{ text: 'Деньги', callback_data: `MONEY_IN_POCKET` },
{ text: 'Телефон', callback_data: `PHONE` }
]
]))
});
rpg.action(`MONEY_IN_POCKET`, async (ctx) => {
// Получаем пользователя и его персонажа
//let user = await UserModel.findByPk(ctx.from.id);
let character = await CharacterModel.findByPk(ctx.from.id);
if (!character) {
return ctx.editMessageText('У вас нет персонажа. Создайте его перед началом.');
}
// Расчёт шанса на успешную кражу
let baseChance = 40; // Базовый шанс
let chance = baseChance + character.intelligence * 2; // Увеличиваем шанс на 2% за каждый пункт "Разума".
let randomRoll = utils.rand(0, 100); // Случайное число от 0 до 100
if (randomRoll > chance) {
Exp(ctx, character, 1)
return ctx.editMessageText('Вы были замечены во время кражи.');
}
// Успешная кража
let moneyIn = utils.rand(5, 1000);
Exp(ctx, character, character.intelligence + 3)
character.dirtymoney += moneyIn;
logs(ctx, "Карманная кража (Кошелек)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, moneyIn: moneyIn});
await character.save();
return ctx.editMessageText(`Вы успешно украли Ð${utils.spaces(moneyIn)} из кармана.`);
});
rpg.action(`PHONE`, async (ctx) => {
let character = await CharacterModel.findByPk(ctx.from.id);
//let user = await UserModel.findByPk(ctx.from.id)
let property = await PropertyModel.findByPk(ctx.from.id);
// Расчёт шанса на успешную кражу
let baseChance = 20; // Базовый шанс
let chance = baseChance + character.intelligence * 2; // Увеличиваем шанс на 2% за каждый пункт "Разума".
let randomRoll = utils.rand(0, 100); // Случайное число от 0 до 100
if (chance < randomRoll) {
Exp(ctx, character, 1)
return ctx.editMessageText('Вы были замечены во время кражи.')
}
let randPhone = utils.rand(1, 10)
if (property.mobile.name) {
let dirtyMoney = Math.round(phones[randPhone].price / 100 * 70)
Exp(ctx, character, character.intelligence + 5)
character.dirtymoney += dirtyMoney
return await ctx.reply(`Вы сбыли украденный ${phones[randPhone].name} за Ð${utils.spaces(dirtyMoney)}`)
}
property.mobile = phones[randPhone]
logs(ctx, "Карманная кража (Телефон)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, phone: phones[randPhone]});
await character.save()
await property.save()
return ctx.editMessageText(`Вы успешно украли ${phones[randPhone].name} из кармана.`)
});
rpg.action(`POCKET_WALLET`, async (ctx) => {
ctx.editMessageText('В бумажнике обнаружено', Markup.inlineKeyboard([
[
{ text: 'Деньги', callback_data: `MONEY_IN_WALLET` },
{ text: 'Карточка', callback_data: `CARD_IN_WALLET` }
]
]))
});
rpg.action(`MONEY_IN_WALLET`, async (ctx) => {
//let user = await UserModel.findByPk(ctx.from.id)
let character = await CharacterModel.findByPk(ctx.from.id);
// Расчёт шанса на успешную кражу
let baseChance = 35; // Базовый шанс
let chance = baseChance + character.intelligence * 2; // Увеличиваем шанс на 2% за каждый пункт "Разума".
let randomRoll = utils.rand(0, 100); // Случайное число от 0 до 100
if (chance < randomRoll) {
Exp(ctx, character, 1)
return ctx.editMessageText('Вы были замечены во время кражи.')
}
let moneyIn = utils.rand(1000, 10000)
Exp(ctx, character, character.intelligence + 5)
character.dirtymoney += moneyIn
logs(ctx, "Карманная кража (Бумажник)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, moneyIn: moneyIn});
character.save()
return ctx.editMessageText(`Вы успешно украли Ð${utils.spaces(moneyIn)} из бумажника.`)
});
rpg.action(`CARD_IN_WALLET`, async (ctx) => {
//let user = await UserModel.findByPk(ctx.from.id)
let character = await CharacterModel.findByPk(ctx.from.id);
let baseChance = 20; // Базовый шанс
let chance = baseChance + character.intelligence * 2; // Увеличиваем шанс на 2% за каждый пункт "Разума".
let randomRoll = utils.rand(0, 100); // Случайное число от 0 до 100
if (chance < randomRoll) {
Exp(ctx, character, 2)
return ctx.editMessageText('Вы были замечены во время кражи.')
}
Exp(ctx, character, character.intelligence + 5)
let newCard = generateCard(ctx.from.id)
logs(ctx, "Карманная кража (Карта)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, card: newCard});
character.save()
return ctx.editMessageText(`Вы успешно украли 💳 из бумажника.`)
});
rpg.action(`POCKET_BAG`, async (ctx) => {
//let user = await UserModel.findByPk(ctx.from.id)
let character = await CharacterModel.findByPk(ctx.from.id);
let baseChance = 20; // Базовый шанс
let chance = baseChance + character.intelligence * 2; // Увеличиваем шанс на 2% за каждый пункт "Разума".
let randomRoll = utils.rand(0, 100); // Случайное число от 0 до 100
if (chance < randomRoll) {
Exp(ctx, character, 1)
return ctx.editMessageText('Вы были замечены во время кражи.')
}
let times = utils.rand(2, 20)
let moneyIn = 0
let text = ``
let values = 0
for (i = 1; i <= times; i++) {
randomize = utils.rand(1, 100)
switch (randomize) {
case 2:
values = utils.rand(10000, 50000)
moneyIn += values
text += `+ Ð${utils.spaces(values)}\n`
break;
case 7:
values = utils.rand(10000, 100000)
moneyIn += values
text += `+ Ð${utils.spaces(values)}\n`
break;
default:
values = utils.rand(100, 3000)
moneyIn += values
text += `+ Ð${utils.spaces(values)}\n`
break;
}
}
Exp(ctx, character, character.intelligence + 5)
character.dirtymoney += moneyIn
logs(ctx, "Карманная кража (Сумка)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, moneyIn: moneyIn});
character.save()
return ctx.editMessageText(`Вы успешно украли сумку и сбыли все ценности из нее:\n${text}\nОбщий куш: Ð${utils.spaces(moneyIn)}`)
});
rpg.action('crime_menu', async (ctx) => {
await ctx.answerCbQuery();
await ctx.editMessageText(
`💻 CampFireGG.Crime Menu`,
Markup.inlineKeyboard([
[Markup.button.callback('💼 Задачи', 'crime_missions')],
[Markup.button.callback('📊 Профиль', 'rpg_profile')],
[Markup.button.callback('💰 Магазин', 'shop')],
])
);
});
rpg.action('SHOP_ACTION', async (ctx) => {
let user = await UserModel.findByPk(ctx.from.id)
let character = await CharacterModel.findByPk(ctx.from.id);
let property = await PropertyModel.findByPk(ctx.from.id);
if (character.level < 5) return ctx.editMessageText('Доступно с 5 уровня!')
if (property.car1 == 0) return ctx.editMessageText('Для данного задания нужна тачка.')
if (character.stamina < 25) return ctx.editMessageText('Вы устали!')
let shoprobcd = character.shoprobcd
let cooldown = utils.setCooldown(character, 3600, shoprobcd)
if (character.shoprobcd > cooldown.currentTime) return ctx.editMessageText(`📛 Данное действие будет доступно через ${cooldown.timeLeftInMinutes} мин.`);
character.shoprobcd = cooldown.endTime
character.stamina -= 25
logs(ctx, "Ограбление магазина (Start)", {cdBefore: shoprobcd, cdAfter: character.shoprobcd, stamina: character.stamina});
character.save()
return ctx.editMessageText('Стадии:', Markup.inlineKeyboard([
[{ text: 'Взлом кассы', callback_data: `SHOP_CASH_BREAK` }],
[{ text: 'Разбить кассу', callback_data: `SHOP_CASH_SMASH` }]
]))
});
rpg.action(`SHOP_CASH_BREAK`, async (ctx) => {
let character = await CharacterModel.findByPk(ctx.from.id);
let baseChance = 20; // Базовый шанс
let chance = baseChance + character.intelligence * 2; // Увеличиваем шанс на 2% за каждый пункт "Разума".
let randomRoll = utils.rand(0, 100); // Случайное число от 0 до 100
let cashIn = utils.rand(1000, 10000)
let timer = 1000
if (chance < randomRoll) {
const keyboard = generateKeyboard();
ctx.deleteMessage()
logs(ctx, "Ограбление магазина (Взлом кассы ручной)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, keyboard: keyboard, cashIn: cashIn});
return ctx.reply('Касса закрыта, вы начали взлом замка:', keyboard);
//ctx.editMessageText('Вы начали взлом кассы.');
//return ctx.scene.enter('LOCKPICK')
}
for (i = 0; i < utils.rand(character.endurance, character.endurance + 15); i++) {
setTimeout(() => {
cashIn += utils.rand(1000, 10000)
ctx.editMessageText(`⏏️ Вы достали из кассы: Ð${utils.spaces(cashIn)}`)
}, timer)
timer += 500
}
setTimeout(() => {
Exp(ctx, character, character.intelligence + 10)
character.dirtymoney += cashIn
character.save()
return ctx.editMessageText(`Вы достали из кассы Ð${utils.spaces(cashIn)}, пора валить.`, Markup.inlineKeyboard([
[{ text: 'Завершить ограбление', callback_data: `SHOP_END` }]
]))
}, timer + 300)
logs(ctx, "Ограбление магазина (Взлом кассы быстро)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, cashIn: cashIn});
});
rpg.action(`SHOP_CASH_SMASH`, async (ctx) => {
let character = await CharacterModel.findByPk(ctx.from.id);
let baseChance = 20; // Базовый шанс
let chance = baseChance + character.intelligence * 2; // Увеличиваем шанс на 2% за каждый пункт "Разума".
let randomRoll = utils.rand(0, 100); // Случайное число от 0 до 100
let cashIn = utils.rand(1000, 10000)
let timer = 1000
if (chance < randomRoll) {
Exp(ctx, character, 1)
return ctx.editMessageText('Вы разбили кассовый аппарат, и сработала сигнализация. Вы сбежали.')
}
ctx.editMessageText('Вы разбили кассовый аппарат, и сработала сигнализация.')
for (i = 0; i < utils.rand(character.endurance, character.endurance + 8); i++) {
setTimeout(() => {
cashIn += utils.rand(500, 5000)
ctx.editMessageText(`⏏️ Вы в спешке достали из кассы: Ð${utils.spaces(cashIn)}`)
}, timer)
timer += 500
}
setTimeout(() => {
Exp(ctx, character, character.intelligence + 10)
character.dirtymoney += cashIn
character.save()
return ctx.editMessageText(`Вы в спешке достали из кассы Ð${utils.spaces(cashIn)}, пора валить.`, Markup.inlineKeyboard([
[{ text: 'Завершить ограбление', callback_data: `SHOP_END` }]
]))
}, timer + 300)
logs(ctx, "Ограбление магазина (Разбитие кассы)", {baseChance: baseChance, chance: chance, randomRoll: randomRoll, cashIn: cashIn});
});
rpg.action(`SHOP_CASH_BREAK_SUCCESS`, async (ctx) => {
let user = await UserModel.findByPk(ctx.from.id)
let character = await CharacterModel.findByPk(ctx.from.id);
delete attempts[ctx.from.id];
let cashIn = utils.rand(1000, 10000)
let timer = 100
for (i = 0; i < utils.rand(character.endurance, character.endurance + 15); i++) {
setTimeout(() => {
cashIn += utils.rand(3000, 10000)
ctx.editMessageText(`⏏️ Вы достали из кассы: Ð${utils.spaces(cashIn)}`)
}, timer)
timer += 500
}
setTimeout(() => {
Exp(ctx, character, character.intelligence + 15)
character.dirtymoney += cashIn
character.save()
return ctx.editMessageText(`Вы достали из кассы Ð${utils.spaces(cashIn)}, пора валить.`, Markup.inlineKeyboard([
[{ text: 'Завершить ограбление', callback_data: `SHOP_END` }]
]))
}, timer + 300)
logs(ctx, "Ограбление магазина (Взлом кассы)", {cashIn: cashIn});
});
rpg.action(`SHOP_END`, async (ctx) => {
return ctx.editMessageText('Ограбление завершено!')
});
const attempts = {}; // Храним количество попыток взлома для каждого пользователя
rpg.action(/lock_*/, async (ctx) => {
const userId = ctx.from.id;
const buttonId = ctx.update.callback_query.data;
// Если пользователя нет в списке попыток, добавляем его
if (!attempts[userId]) {
attempts[userId] = 0;
}
// Увеличиваем количество попыток
attempts[userId] += 1;
// Проверяем, не исчерпаны ли попытки
if (attempts[userId] >= 5) {
delete attempts[userId]; // Сбрасываем попытки после провала
logs(ctx, "Взлом замка (Full Fail)", {attempts: attempts[userId]});
return ctx.editMessageText('Взлом провалился. Замок остается нетронутым.');
}
logs(ctx, "Взлом замка (Fail)", {attempts: attempts[userId]});
// Ответ на нажатие кнопки
ctx.answerCbQuery(`Штифт не сдвинулся. Попыток осталось: ${5 - attempts[userId]}`);
removeButton(ctx, buttonId);
});
//Функция для отображения читаемого типа предмета "implant" "consumable" "accessory" "weapon" "armor" "helmet" "boots" и другие недостающие
function getItemType(type) {
switch (type) {
case 'implant':
return 'Имплант';
case 'consumable':
return 'Расходник';
case 'accessory':
return 'Аксессуар';
case 'weapon':
return 'Оружие';
case 'armor':
return 'Броня';
case 'helmet':
return 'Шлем';
case 'boots':
return 'Обувь';
default:
return type;
}
}
rpg.action('inventory', async (ctx) => {
try {
const character = await CharacterModel.findByPk(ctx.from.id);
if (!character) {
return ctx.reply('Персонаж не найден!');
}
// Загружаем предметы из инвентаря
const inventory = await InventoryModel.findAll({ where: { telegram_id: ctx.from.id } });
if (inventory.length === 0) {
return ctx.reply('Ваш инвентарь пуст.');
}
let message = '*Ваш инвентарь:*\n\n';
// Группируем предметы по их состоянию (снаряженные/обычные)
const equippedItems = inventory.filter((item) => item.equipped);
const unequippedItems = inventory.filter((item) => !item.equipped);
if (equippedItems.length > 0) {
message += '🛡️ Снаряженные предметы:\n';
equippedItems.forEach((item) => {
message += `- ${item.name} (${getItemType(item.type)})\n`;
});
message += '\n';
}
if (unequippedItems.length > 0) {
message += '🎒 Предметы в инвентаре:\n';
unequippedItems.forEach((item) => {
message += `- ${item.name} (${getItemType(item.type)})\n`;
});
message += '\n';
}
// Кнопки для взаимодействия
const buttons = [
...unequippedItems.map((item) =>
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.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 = [];
if (!item.equipped) {
buttons.push({ text: `🎯 Использовать`, callback_data: `use_item_${item.id}` });
}
if (item.equipped && item.canBeEquipped) {
buttons.push({ text: `🚫 Снять`, callback_data: `unequip_item_${item.id}` });
}
buttons.push({ text: `💰 Продать`, callback_data: `sell_item_${item.id}` });
// Формируем сообщение с эффектами
let effectsMessage = '*Эффекты:\n*';
if (item.effectData) {
effectsMessage += getEffectDescription(item.effectData);
}
logs(ctx, "Просмотр предмета", { item });
try {
// Формируем путь к картинке
let imagePath = path.join(__dirname, 'media/items', item.text_id + '.png'); // Путь к картинке
// Проверяем существует ли файл
if (!fs.existsSync(imagePath)) {
imagePath = path.join(__dirname, 'media/items', 'template.png'); // Путь к картинке по умолчанию
}
// Отправляем фото и сообщение с кнопками
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 : ""}`,
parse_mode: 'MarkdownV2', // Используем MarkdownV2 для форматирования
reply_markup: {
inline_keyboard: [buttons] // Кнопки передаем как часть структуры inline_keyboard
},
});
} catch (err) {
console.error("Ошибка при отправке сообщения:", err);
return ctx.reply('Произошла ошибка при отправке сообщения.');
}
});
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);
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} уже снаряжен.`);
}
if (!item.canBeEquipped) {
return ctx.answerCbQuery(`🚫 ${item.name} нельзя сейчас использовать.`);
}
// Проверяем, не надеты ли другие предметы того же типа
const equippedItems = await InventoryModel.findAll({
where: { telegram_id: ctx.from.id, type: item.type, equipped: true }
});
if (equippedItems.length > 0) {
return ctx.answerCbQuery(`🚫 Вы не можете использовать ${item.name}, пока надеты другие предметы того же типа.`, { show_alert: true } );
}
// Применяем эффекты предмета
if (item.effectData) {
const resultMessages = processEffects(character, item.effectData, true);
await ctx.answerCbQuery(resultMessages, { show_alert: true });
}
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) => {
const itemId = parseInt(ctx.match[1], 10);
const character = await CharacterModel.findByPk(ctx.from.id);
const item = await InventoryModel.findByPk(itemId);
if (!item || item.telegram_id != ctx.from.id) {
return ctx.answerCbQuery('Этот предмет не найден в вашем инвентаре.');
}
if (!item.equipped) {
return ctx.answerCbQuery(`${item.name} не снаряжен.`);
}
if (item.effectData) {
const resultMessages = processEffects(character, item.effectData, false);
await ctx.editMessageText(resultMessages);
}
item.equipped = false;
await item.save();
ctx.answerCbQuery(`Вы успешно сняли ${item.name}.`);
});
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 (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_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);
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('Предмет не найден.');
}
const character = await CharacterModel.findByPk(ctx.from.id);
if (!character) {
return ctx.reply('Персонаж не найден.');
}
if (character.balance < item.price) {
return ctx.answerCbQuery('У вас недостаточно средств для покупки этого предмета.', { show_alert: true });
}
// Снимаем деньги с баланса
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,
canBeEquipped: item.canBeEquipped,
equipped: false,
img: item.img
});
await character.save();
ctx.answerCbQuery(`Вы успешно купили ${item.name} за ${item.price}₽!`, { show_alert: true });
});
//Команда для админа, выдача предмета по id игроку
rpg.command('giveitem', async (ctx) => {
if (ctx.from.id != 275416286) return ctx.reply('Команда недоступна.')
const itemId = parseInt(ctx.message.text.split(' ')[1], 10);
let user = await UserModel.findByPk(ctx.from.id)
let character = await CharacterModel.findByPk(ctx.from.id);
let item = await ItemsModel.findByPk(itemId);
let inventory = await InventoryModel.findAll({ where: { telegram_id: ctx.from.id } });
if (inventory.length >= 15) {
return ctx.reply('Инвентарь полон.');
}
if (!item) {
return ctx.reply('Предмет не найден.');
}
if (!character) {
return ctx.reply('Персонаж не найден.');
}
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,
canBeEquipped: item.canBeEquipped,
equipped: false,
img: item.img
});
await character.save();
ctx.reply(`Вы успешно купили ${item.name} за ${item.price}₽!`);
}
);
////////////////////////////////////////////////////////////////////////////////////////////
CharacterModel.prototype.addEquippedItem = async function (item) {
// Проверяем, находится ли предмет в инвентаре персонажа
const inventoryItem = await InventoryModel.findOne({
where: { telegram_id: this.telegram_id, id: item.id },
});
if (!inventoryItem) {
throw new Error('Предмет не найден в вашем инвентаре.');
}
// Проверяем, не снаряжен ли уже этот предмет
if (inventoryItem.equipped) {
throw new Error('Этот предмет уже снаряжен.');
}
// Снаряжаем предмет
inventoryItem.equipped = true;
await inventoryItem.save();
// Применяем эффекты предмета
let resultMessages = [];
if (inventoryItem.effectData) {
resultMessages = processEffects(this, inventoryItem.effectData, true);
await this.save(); // Сохраняем изменения в характеристиках персонажа
}
return resultMessages;
};
CharacterModel.prototype.removeEquippedItem = async function (item) {
// Проверяем, находится ли предмет в инвентаре персонажа
const inventoryItem = await InventoryModel.findOne({
where: { telegram_id: this.telegram_id, id: item.id },
});
if (!inventoryItem || !inventoryItem.equipped) {
throw new Error('Этот предмет не снаряжен.');
}
// Снимаем предмет
inventoryItem.equipped = false;
await inventoryItem.save();
// Убираем эффекты предмета
let resultMessages = [];
if (inventoryItem.effectData) {
resultMessages = processEffects(this, inventoryItem.effectData, false);
await this.save(); // Сохраняем изменения в характеристиках персонажа
}
return resultMessages;
};
const processEffects = (character, effects, isEquipping) => {
let messages = [];
effects.forEach((effect) => {
if (!isEquipping) {
// Снимаем эффекты
switch (effect.type) {
case 'damage_boost':
character.force = Math.max(0, character.force - effect.amount);
character.save()
messages.push(`Ваш урон уменьшен на ${effect.amount}.`);
break;
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;
case 'stamina_penalty':
character.max_stamina += effect.amount;
character.save()
messages.push(`Ваша стамина увеличена на ${effect.amount}.`);
break;
case 'intelligence_boost':
character.intelligence = Math.max(0, character.intelligence - effect.amount);
character.save()
messages.push(`Ваш интеллект уменьшена на ${effect.amount}.`);
break;
case 'resilience_boost':
character.resilience = Math.max(0, character.resilience - effect.amount);
character.save()
messages.push(`Ваша устойчивость уменьшена на ${effect.amount}.`);
break;
case 'endurance_boost':
character.endurance = Math.max(0, character.endurance - effect.amount);
character.save()
messages.push(`Ваша выносливость уменьшена на ${effect.amount}.`);
break;
case 'force_penalty':
character.force += effect.amount;
character.save()
messages.push(`Ваш урон увеличен на ${effect.amount}.`);
break;
case 'intelligence_penalty':
character.intelligence += effect.amount;
character.save()
messages.push(`Ваш интеллект увеличен на ${effect.amount}.`);
break;
case 'resilience_penalty':
character.resilience += effect.amount;
character.save()
messages.push(`Ваша устойчивость увеличена на ${effect.amount}.`);
break;
case 'endurance_penalty':
character.endurance += effect.amount;
character.save()
messages.push(`Ваша выносливость увеличена на ${effect.amount}.`);
break;
default:
messages.push('Неизвестный эффект при снятии.');
}
} else {
// Применяем эффекты
switch (effect.type) {
case 'heal':
character.hp = Math.min(character.max_hp, character.hp + effect.amount);
character.save()
messages.push(`Вы восстановили ${effect.amount} HP.`);
break;
case 'damage_boost':
character.force += effect.amount;
character.save()
messages.push(`Ваша сила увеличена на ${effect.amount}.`);
break;
case 'max_health_boost':
character.max_hp += effect.amount;
character.hp += effect.amount;
character.save()
messages.push(`Ваше максимальное здоровье увеличено на ${effect.amount}.`);
break;
case 'stamina_penalty':
character.max_stamina -= effect.amount;
character.save()
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;
case 'resilience_boost':
character.resilience += effect.amount;
character.save()
messages.push(`Ваша устойчивость увеличена на ${effect.amount}.`);
break;
case 'endurance_boost':
character.endurance += effect.amount;
character.save()
messages.push(`Ваша выносливость увеличена на ${effect.amount}.`);
break;
case 'force_penalty':
character.force -= effect.amount;
character.save()
messages.push(`Ваш урон уменьшен на ${effect.amount}.`);
break
case 'intelligence_penalty':
character.intelligence -= effect.amount;
character.save()
messages.push(`Ваш интеллект уменьшен на ${effect.amount}.`);
break
case 'resilience_penalty':
character.resilience -= effect.amount;
character.save()
messages.push(`Ваша устойчивость уменьшена на ${effect.amount}.`);
break
case 'endurance_penalty':
character.endurance -= effect.amount;
character.save()
messages.push(`Ваша выносливость уменьшена на ${effect.amount}.`);
break
default:
messages.push('Неизвестный эффект при снаряжении.');
}
}
});
return messages.join('\n');
};
// Функция для читаемого отображения эффектов предметов (пример item.effectData: "[{""type"": ""max_health_boost"", ""amount"": 100}, {""type"": ""resilience_boost"", ""amount"": 20}, {""type"": ""intelligence_boost"", ""amount"": 75}, {""type"": ""endurance_boost"", ""amount"": 15}]")
function getEffectDescription(effectData) {
const effects = effectData;
let description = '';
effects.forEach((effect) => {
switch (effect.type) {
case 'heal':
description += `Восстанавливает ${effect.amount} HP\n`;
break;
case 'damage_boost':
description += `Увеличивает урон на ${effect.amount}\n`;
break;
case 'max_health_boost':
description += `Увеличивает максимальное здоровье на ${effect.amount}\n`;
break;
case 'stamina_penalty':
description += `Уменьшает стамина на ${effect.amount}\n`;
break;
case 'stamina_recover':
description += `Восстанавливает ${effect.amount} стамины\n`;
break;
case 'intelligence_boost':
description += `Увеличивает интеллект на ${effect.amount}\n`;
break;
case 'resilience_boost':
description += `Увеличивает устойчивость на ${effect.amount}\n`;
break;
case 'endurance_boost':
description += `Увеличивает выносливость на ${effect.amount}\n`;
break;
case 'force_penalty':
description += `Уменьшает урон на ${effect.amount}\n`;
break;
case 'intelligence_penalty':
description += `Уменьшает интеллект на ${effect.amount}\n`;
break;
case 'resilience_penalty':
description += `Уменьшает устойчивость на ${effect.amount}\n`;
break;
case 'endurance_penalty':
description += `Уменьшает выносливость на ${effect.amount}\n`;
break;
default:
description += 'Неизвестный эффект\n';
}
});
return description;
}
function generateKeyboard() {
const buttonsCount = 10;
const buttons = [];
const winButton = utils.rand(1, 10);
for (let i = 1; i <= buttonsCount; i++) {
if (i === winButton) {
buttons.push({ text: `🔒`, callback_data: `SHOP_CASH_BREAK_SUCCESS` });
} else {
buttons.push({ text: `🔒`, callback_data: `lock_${i}` });
}
}
const rows = [];
while (buttons.length > 0) {
rows.push(buttons.splice(0, 5)); // Разбиваем на подмассивы по 5 кнопок
}
console.log(rows)
return {
reply_markup: {
inline_keyboard: rows,
},
};
}
function removeButton(ctx, buttonId) {
const keyboard = ctx.update.callback_query.message.reply_markup.inline_keyboard;
for (let row of keyboard) {
for (let i = 0; i < row.length; i++) {
if (row[i].callback_data === buttonId) {
row[i].text = `🔓`;
break;
}
}
}
ctx.editMessageText('Взлом замка:', Markup.inlineKeyboard(keyboard).resize());
}
// Восстановление здоровья (HP)
async function recoverHealth() {
const characters = await CharacterModel.findAll()
// Восстанавливаем только тем, у кого HP меньше максимума
for (const character of characters) {
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()
// Восстанавливаем только тем, у кого стамина меньше максимума
for (const character of characters) {
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();
}
}
const reduceStealedCards = async () => {
try {
const characters = await CharacterModel.findAll();
for (const character of characters) {
const stolenCards = await StolenCardsModel.findAll({
where: { userId: character.telegram_id }
});
if (stolenCards.length > 0) {
// Для каждой карточки уменьшаем баланс на половину
for (const card of stolenCards) {
card.balance = Math.ceil(card.balance / 2); // Баланс делим на 2 и округляем вверх
await card.save(); // Сохраняем изменения карточки
}
// Удаляем половину карточек
const cardsToDelete = Math.ceil(stolenCards.length / 2);
const cardsToRemove = stolenCards.slice(0, cardsToDelete);
for (const card of cardsToRemove) {
await card.destroy(); // Удаляем карточки
}
console.log(`Карточки для персонажа ${character.name} успешно обновлены.`);
}
}
console.log('Карточки всех игроков успешно обновлены.');
} catch (error) {
console.error('Ошибка при обновлении карточек:', error);
}
};
async function generateCard(userId, balance) {
if (!Number(balance)) {
balance = faker.finance.amount({ dec: 0 }) * 100
console.log('Random balance: ' + balance)
} else {
balance = Math.floor(balance);
console.log('Set balance: ' + balance)
}
const cardNumber = `${faker.finance.creditCardNumber()}`; // Генерация номера карты
const pin = `${faker.finance.pin()}`; // Четырехзначный ПИН
const cvv = `${faker.finance.creditCardCVV()}`; // Трехзначный CVV
const holderName = `${faker.person.firstName()} ${faker.person.lastName()}`; // Имя и фамилия
const bankName = faker.company.name(); // Название банка
const expiresDate = `${Math.floor(Math.random() * 12) + 1}/${2025 + Math.floor(Math.random() * 5)}`; // Срок действия
await StolenCardsModel.create({
userId,
balance,
cardNumber,
pin,
cvv,
holderName,
bankName,
expiresDate,
});
}
async function Exp(ctx, character, experience) {
character.exp += experience
await ctx.sendMessage(`❇️ +${experience} exp.`)
await character.save()
for (i in expToUp) {
if (character.exp >= expToUp[character.level]) {
character.exp -= expToUp[character.level]
character.level += 1
character.firePoints += 1
await character.save()
await ctx.sendMessage(`⤴️ Уровень вашего персонажа повысился до ${character.level}!`)
}
}
}
// Периодическое выполнение задач
function startRecoveryIntervals() {
const interval = 60000; // 1 мин Интервал в миллисекундах (например, 1 минута)
setInterval(recoverHealth, interval);
setInterval(recoverStamina, interval);
}
// Планируем выполнение задачи каждый день в 12 ночи
schedule.scheduleJob('0 0 * * *', reduceStealedCards);
rpg.command('force_reduce', async (ctx) => {
reduceStealedCards()
return await ctx.reply(`Принудительно`)
})
rpg.command('steal_card', async (ctx) => {
await generateCard(ctx.from.id)
ctx.reply('💳 Вы украли новую карточку! Проверьте её в вашем инвентаре.');
});
startRecoveryIntervals()
const fillEnemies = async () => {
const enemies = [
{ name: "Карманник", description: "Мелкий вор, пытающийся выудить деньги у прохожих", level: 1, hp: 40, damage: 5, loot: [1], rarity: 1, experience: 10 },
{ name: "Гопник", description: "Уличный хулиган с цепью", level: 2, hp: 70, damage: 15, loot: [2], rarity: 2, experience: 15 },
{ name: "Пьяный бродяга", description: "Пьяный человек, не опасен, но может ударить при попытке забрать его бутылку", level: 1, hp: 30, damage: 3, loot: [3], rarity: 1, experience: 5 },
{ name: "Угонщик", description: "Опытный угонщик машин, вооружённый пистолетом", level: 5, hp: 100, damage: 20, loot: [4], rarity: 2, experience: 25 },
];
for (const enemy of enemies) {
await Enemy.create(enemy);
}
console.log("Enemies filled.");
};
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();
return rarity < 0.7 ? enemyId : null; // Шанс 70% для обычных врагов
}).filter(Boolean);
for (const enemyId of generatedEnemies) {
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.action("locations", async (ctx) => {
const locations = await Location.findAll();
const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } });
if (!locations.length) {
return ctx.reply("Нет доступных локаций для исследования.");
}
// Создаем кнопки для каждой локации и сортируем их по уровню
const buttons = locations
.sort((a, b) => a.level - b.level)
.map((location) => {
return Markup.button.callback(location.name + ` [${character.level < location.level + 3 ? `⚠️ ` + `${location.level} - ${location.level + 3}` : `${location.level} - ${location.level + 3}`} Ур.]`, `viewlocation_${location.id}`);
});
const keyboard = Markup.inlineKeyboard(buttons, { columns: 1 });
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.editMessageText(description, keyboard);
});
rpg.action(/explore_\d+/, async (ctx) => {
return ctx.answerCbQuery("Функционал в разработке.");
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 = dropItem(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);
if (!location) {
logs(ctx, "Локация не найдена", { locationId });
return ctx.reply("Локация не найдена.");
}
const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } });
if (!character) {
logs(ctx, "Персонаж не найден", { telegramId: ctx.from.id });
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) {
logs(ctx, "Нет врагов в локации", { location });
return ctx.answerCbQuery("В этой локации сейчас нет врагов. Попробуйте позже.");
}
const activeBattles = await Battle.findAll({
where: { location: location.id, status: "inactive" },
});
if (activeBattles.length === 0) {
logs(ctx, "Нет активных врагов в локации", { location });
return ctx.answerCbQuery("В этой локации сейчас нет активных врагов. Попробуйте позже.");
}
const activeEnemyIds = activeBattles.map((battle) => battle.enemy);
const enemies = await Enemy.findAll({ where: { id: activeEnemyIds } });
if (enemies.length === 0) {
logs(ctx, "Нет врагов в локации", { location });
return ctx.answerCbQuery("В этой локации сейчас нет врагов.");
}
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}`,
};
});
if (hasGrenadeLow || hasGrenadeMedium || hasGrenadeHard) {
buttons.push({
text: "💣 Использовать гранату",
callback_data: `use_grenade_${locationId}`,
});
}
const keyboard = Markup.inlineKeyboard(buttons.map((btn) => [btn]));
logs(ctx, "Выбор врага для охоты", { location, enemies });
await ctx.editMessageText(
`🔍 Локация: ${location.name}\n\nВыберите врага для охоты:`,
keyboard
);
});
// Использование гранаты
rpg.action(/use_grenade_\d+/, async (ctx) => {
const locationId = ctx.match[0].split('_')[2];
const location = await Location.findByPk(locationId);
if (!location) {
logs(ctx, "Локация не найдена", { locationId });
return ctx.reply("Локация не найдена.");
}
const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } });
if (!character) {
logs(ctx, "Персонаж не найден", { telegramId: ctx.from.id });
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) {
logs(ctx, "Граната не найдена", { telegramId: ctx.from.id });
return ctx.reply("У вас нет гранаты для использования.");
}
const activeBattles = await Battle.findAll({
where: { location: location.id, status: "inactive" },
});
if (activeBattles.length === 0) {
logs(ctx, "Нет активных врагов в локации", { location });
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;
await addBattleLog(battle, `💣 Враг получил ${damage} урона от гранаты.`);
if (battle.enemy_hp <= 0) {
battle.status = "completed";
await addBattleLog(battle, "💀 Враг был убит гранатой.");
}
await battle.save({ fields: ["enemy_hp", "logs", "status"] });
}
// Удаляем гранату из инвентаря
await InventoryModel.destroy({ where: { id: grenade.id } });
logs(ctx, "Использование гранаты", { grenade, damage });
await ctx.answerCbQuery(message, { show_alert: true });
});
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') {
logs(ctx, "Битва не найдена или уже активна", { battleId });
return ctx.reply("Этот враг уже сражается с другим игроком или сражение завершено.");
}
const existingBattle = await Battle.findOne({
where: { character: ctx.from.id, status: 'active' },
});
if (existingBattle) {
logs(ctx, "Игрок уже участвует в другой битве", { telegramId: ctx.from.id });
return ctx.reply("Вы уже участвуете в другом сражении!");
}
const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } });
if (!character) {
logs(ctx, "Персонаж не найден", { telegramId: ctx.from.id });
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` }]])
);
logs(ctx, "Начало сражения", { enemy, battle });
};
const enemyTurn = async (ctx, character, battle) => {
const enemy = await Enemy.findByPk(battle.enemy);
const dodgeChance = Math.min(character.resilience * 0.05, 0.5);
const isDodged = Math.random() < dodgeChance;
if (isDodged) {
// Генерация кнопок
const buttons = await generateBattleButtons();
const keyboard = Markup.inlineKeyboard(buttons);
// Сообщение с информацией
let battleLogs = await addBattleLog(battle, `💨 ${character.name} уклонился от атаки ${enemy.name}!`);
// Логи битвы
let logMessage = battleLogs.slice(-5).map((log) => `${log}`).join("\n");
logs(ctx, "Уклонение от атаки", { enemy, battle });
return await ctx.editMessageText(
`⚔️ Сражение с ${enemy.name}\n\n` +
`❤️ Здоровье врага: ${battle.enemy_hp}/${enemy.hp}\n` +
`⚔️ Урон врага: ${enemy.damage}\n\n` +
`📜 Логи битвы:\n${logMessage || "Пока ничего не произошло."}\n\n` +
`🎯 Выберите цель для атаки:`,
keyboard
);
}
const damage = enemy.damage;
character.hp -= damage;
await character.save();
if (character.hp <= 0) {
battle.status = "inactive";
let battleLogs = await addBattleLog(battle, `💔 ${enemy.name} нанес ${character.name} ${damage} урона!\n\n${character.name} потерпел поражение от ${enemy.name}.`);
await battle.save({ fields: ["logs", "status"] });
// При проигрыше игрок потеряет деньги или экипированные предметы
const inventory = await InventoryModel.findAll({ where: { telegram_id: ctx.from.id } });
const equippedItems = inventory.filter((item) => item.equipped);
if (equippedItems.length) {
const randomItem = equippedItems[Math.floor(Math.random() * equippedItems.length)];
// Снимаем предмет и снимает эффекты с персонажа
if (randomItem) {
processEffects(character, randomItem.effectData, false);
await randomItem.destroy();
await addBattleLog(battle, `💼 ${character.name} потерял ${randomItem.name}.`);
}
} else {
// Теряет 10% от денег
const lostMoney = Math.floor(character.dirtymoney * 0.1);
character.dirtymoney -= lostMoney;
await addBattleLog(battle, `💰 ${character.name} потерял ${lostMoney} монет.`);
}
await character.save();
await battle.save({ fields: ["logs", "status"] });
let logMessage = battleLogs.slice(-5).map((log) => `${log}`).join("\n");
logs(ctx, "Поражение персонажа", { enemy, battle });
return await ctx.editMessageText( `⚔️ Вы проиграли!\n\n` + `💔 ${enemy.name} нанес ${character.name} ${damage} урона!\n\n` + `📜 Логи битвы:\n${logMessage || "Пока ничего не произошло."}`);
}
let battleLogs = await addBattleLog(battle, `💔 ${enemy.name} нанес ${character.name} ${damage} урона. У ${character.name} осталось ${character.hp} HP.`);
let logMessage = battleLogs.slice(-5).map((log) => `${log}`).join("\n");
logs(ctx, "Атака врага", { enemy, battle, damage });
// Генерация кнопок
const buttons = await generateBattleButtons();
const keyboard = Markup.inlineKeyboard(buttons);
console.log(buttons)
return await ctx.editMessageText(
`⚔️ Сражение с ${enemy.name}\n\n` +
`❤️ Здоровье врага: ${battle.enemy_hp}/${enemy.hp}\n` +
`⚔️ Урон врага: ${enemy.damage}\n\n` +
`📜 Логи битвы:\n${logMessage || "Пока ничего не произошло."}\n\n` +
`🎯 Выберите цель для атаки:`,
keyboard
);
};
async function generateBattleButtons() {
const buttons = [
{
text: "🗡️ Атака",
callback_data: "attack",
},
{
text: "🛡️ Защита",
callback_data: "defend",
},
];
return buttons.map((btn) => [btn]);
}
// Функция выпадения предмета с врага или локации с учетом аксессуаров и управляемого шанса
async function dropItem(character, enemyId, locationId) {
let items = [];
// Получаем аксессуары игрока
const playerAccessories = await InventoryModel.findAll({
where: { telegram_id: character.telegram_id, type: "accessory", equipped: true },
});
// Загружаем данные о враге или локации
let source = null;
if (enemyId) {
source = await Enemy.findByPk(enemyId);
if (!source) return null;
items = source.loot; // Лут из врага
} else if (locationId) {
source = await Location.findByPk(locationId);
if (!source) return null;
items = source.loot; // Лут из локации
} else {
return null; // Если нет ни врага, ни локации
}
// Функция для получения модификации шанса от аксессуаров
function getChanceModifier(playerAccessories) {
let modifier = 1;
for (const accessory of playerAccessories) {
modifier += accessory.effectData.chance || 0; // Добавляем к шансу эффект аксессуара
}
return modifier;
}
// Получаем модификатор шанса от аксессуаров
const chanceModifier = getChanceModifier(playerAccessories);
// Перебираем все предметы из лута врага или локации
let droppedItems = [];
for (const itemId of items) {
const item = await ItemsModel.findByPk(itemId);
if (!item) continue;
// Шанс выпадения предмета (от 0 до 1) и его модификация через аксессуары
let baseChance = item.dropChance; // Этот шанс хранится в самом предмете, от 0 до 1
let finalChance = baseChance * chanceModifier; // Применяем модификатор к базовому шансу
// Ограничиваем шанс в пределах от 0 до 1
finalChance = Math.min(1, Math.max(0, finalChance));
// Генерация случайного числа для проверки выпадения
const dropRoll = Math.random(); // Случайное число от 0 до 1
// Проверка, выпал ли предмет
if (dropRoll <= finalChance) {
droppedItems.push(item); // Добавляем выпавший предмет в массив
console.log(`Предмет ${item.name} (id: ${item.id}) выпал с шансом ${finalChance * 100}%`);
}
}
// Теперь среди выпавших рандомом выбираем треть предметов (минимум 1)
const itemsToDrop = Math.max(1, Math.floor(droppedItems.length / 3));
droppedItems = droppedItems.sort(() => Math.random() - 0.5).slice(0, itemsToDrop);
console.log(`${droppedItems.length} предметов выпало`);
// Если предметы выпали, возвращаем их, иначе null
if (droppedItems.length > 0) {
// Отправляем сообщение игроку о найденных предметах
return droppedItems; // Вернем массив выпавших предметов
} else {
return null; // Если ничего не выпало
}
}
// Функция dropItem, но с возможностью передать врага или локацию напрямую для быстрого тестирования
async function dropItemFast(character, inventory, items) {
// Получаем аксессуары игрока
const playerAccessories = inventory.filter((item) => item.type === "accessory" && item.equipped);
// Функция для получения модификации шанса от аксессуаров
function getChanceModifier(playerAccessories) {
let modifier = 1;
for (const accessory of playerAccessories) {
modifier += accessory.effectData.chance || 0; // Добавляем к шансу эффект аксессуара
}
return modifier;
}
// Получаем модификатор шанса от аксессуаров
const chanceModifier = getChanceModifier(playerAccessories);
// Перебираем все предметы из лута врага или локации
let droppedItems = [];
for (const item of items) {
// Шанс выпадения предмета (от 0 до 1) и его модификация через аксессуары
let baseChance = item.dropChance; // Этот шанс хранится в самом предмете, от 0 до 1
let finalChance = baseChance * chanceModifier; // Применяем модификатор к базовому шансу
// Ограничиваем шанс в пределах от 0 до 1
finalChance = Math.min(1, Math.max(0, finalChance));
// Генерация случайного числа для проверки выпадения
const dropRoll = Math.random(); // Случайное число от 0 до 1
// Проверка, выпал ли предмет
if (dropRoll <= finalChance) {
droppedItems.push(item); // Добавляем выпавший предмет в массив
console.log(`Предмет ${item.name} (id: ${item.id}) выпал с шансом ${finalChance * 100}%`);
}
}
// Если предметы выпали, возвращаем их, иначе null
if (droppedItems.length > 0) {
rpg.sendMessage(character.telegram_id, `🎉 Вы нашли: ${droppedItems.map((item) => item.name).join(", ")}`);
return droppedItems; // Вернем массив выпавших предметов
} else {
return null; // Если ничего не выпало
}
}
// Запускаем быструю функцию dropItemFast для тестирования и передаем в нее уже готовые данные character, inventory и массив items
async function testDropItemFast() {
let character = await CharacterModel.findByPk(275416286);
let inventory = await InventoryModel.findAll({ where: { telegram_id: character.telegram_id } });
let items = await ItemsModel.findAll();
// Запускаем тест из 100 попыток и выводим подробную статистику
let itemsCount = {};
for (let i = 0; i < 10000; i++) {
const droppedItems = await dropItemFast(character, inventory, items);
if (!droppedItems) continue;
for (const item of droppedItems) {
itemsCount[item.rarity] = itemsCount[item.rarity] ? itemsCount[item.rarity] + 1 : 1;
}
}
console.log(itemsCount);
}
//setTimeout(testDropItemFast, 3000);
rpg.action("attack", async (ctx) => {
const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } });
const battle = await Battle.findOne({ where: { character: character.telegram_id, status: "active" } });
const enemy = await Enemy.findByPk(battle.enemy);
if (!character || !battle || !enemy) {
return ctx.reply("Ошибка: персонаж или битва не найдены.");
}
// Расчет шансов
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 {
// Промах — враг делает ход
return await enemyTurn(ctx, character, battle);
}
// Логи боя
let battleLogs = await addBattleLog(battle, `${character.name} нанес ${damage} урона (${result}).`);
await battle.save();
// Проверка на победу
if (battle.enemy_hp <= 0) {
battle.status = "completed";
await battle.save();
let items = '';
Exp(ctx, character, enemy.level * 6)
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(", ");
}
let logMessage = battleLogs.slice(-5).map((log) => `${log}`).join("\n");
return ctx.editMessageText(
`🎉 ${character.name} победил ${enemy.name}!\n\n📜 Логи битвы:\n${logMessage}\n🧩 Выпавшие предметы: ${items}`
);
}
// Продолжение боя
let logMessage = battleLogs.slice(-5).map((log) => `${log}`).join("\n");
const buttons = await generateBattleButtons();
const keyboard = Markup.inlineKeyboard(buttons);
await ctx.editMessageText(
`⚔️ Сражение с ${enemy.name}\n\n` +
`❤️ Здоровье врага: ${battle.enemy_hp}/${enemy.hp}\n` +
`📜 Логи битвы:\n${logMessage}\n\n` +
`🎯 Доступные действия:`,
keyboard
);
});
async function addBattleLog(battle, logMessage) {
if (!battle.logs) battle.logs = []; // Инициализация массива, если он пустой
// Добавляем перед сообщением дату и время до миллисекунды
logMessage = `[${new Date().toISOString()}] ${logMessage}`;
battle.logs = [...battle.logs, logMessage]; // Добавляем сообщение в массив логов
await battle.save({ fields: ["logs"] }); // Сохранение изменений
return battle.logs; // Возвращаем обновленный массив логов
}
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 = `
🎭 Профиль персонажа
👤 Имя: ${character.name || 'Не указано'}
🏆 Уровень: ${character.level} (${character.exp}/${utils.spaces(expToUp[character.level])})
❤️ Здоровье: ${character.hp}/${character.max_hp}
🔥 Стамина: ${character.stamina}/${character.max_stamina}
💪 Сила (F): ${character.force}
🧠 Интеллект (I): ${character.intelligence}
🛡️ Устойчивость (R): ${character.resilience}
🔋 Выносливость (E): ${character.endurance}
🎯 Шанс попадания: ${hitChance.toFixed(1)}%
🔥 Шанс крита: ${critChance.toFixed(1)}%
💥 Критический урон: ${(critMultiplier * 100).toFixed(1)}%
`;
// Отправляем сообщение
ctx.reply(profile.trim());
});
module.exports = rpg;