From 3eb1d2ffa9a517dd7b178e4f45c23a2ddaeb6086 Mon Sep 17 00:00:00 2001 From: Degradin <42292046+Degradin@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:04:00 +0300 Subject: [PATCH] v0.0.38 --- bot.js | 477 +++++++++++++++++++++++++------------- config/index.js | 1 + models/truck.model.js | 10 + models/warehouse.model.js | 8 +- 4 files changed, 333 insertions(+), 163 deletions(-) create mode 100644 models/truck.model.js diff --git a/bot.js b/bot.js index fdbdf04..690e09a 100644 --- a/bot.js +++ b/bot.js @@ -18,6 +18,7 @@ const { PromocodeModel, EnterpriseModel, WarehouseModel, + TruckModel, SaleModel, ResourcePriceModel, SkillsModel, @@ -406,20 +407,38 @@ bot.action('my_enterprises', async (ctx) => { let message = `🏭 Мои предприятия:\n\n`; const buttons = []; - enterprises.forEach((enterprise) => { - message += `🔹 ${enterprise.name} (Уровень: ${enterprise.level}, Эффективность: ${enterprise.efficiency} ед/ч)\n`; - buttons.push([ - { text: `📈 Улучшить ${enterprise.name}`, callback_data: `upgrade_${enterprise.id}` }, - { text: `🚛 Управлять ${enterprise.name}`, callback_data: `manage_${enterprise.id}` } - ]); - }); + for (const enterprise of enterprises) { + const { id, name, level, efficiency, currentResources, resourceType, warehouseCapacity } = enterprise; + message += `🔹 [ID: ${id}] ${getEnterpriseEmoji(resourceType)} ${name} st. ${level}\n └── ${currentResources}/${warehouseCapacity} [${efficiency} ед/ч]\n\n`; + + const truck = await TruckModel.findOne({ where: { enterpriseId: id } }); + const warehouse = await WarehouseModel.findOne({ where: { playerId: user.telegram_id } }); + + const enterpriseButtons = [ + { text: `ID: 📈 ${id}`, callback_data: `upgrade_${id}` }, + ]; + + if (!truck) { + enterpriseButtons.push({ text: `🛻 ID: ${id}`, callback_data: `hire_truck_${id}` }); + }else{ + enterpriseButtons.push({ text: `🚛 ID: ${id}`, callback_data: `upgrade_truck_${id}` }); + } + + enterpriseButtons.push({ text: `💰 ID: ${id}`, callback_data: `sell_all_${id}` }); + + if (warehouse) { + enterpriseButtons.push({ text: `🔄 ID: ${id}`, callback_data: `transfer_from_${id}` }); + } + + buttons.push(enterpriseButtons); + } + message += '\n\n[📈 - Улучшить]\n[🛻 - Нанять грузовик]\n 🚛 - Улучшить грузовик\n[💰 - Продать ресурсы с предприятия]\n[🔄 - Перевезти все на склад]' buttons.push([{ text: '⬅️ Назад', callback_data: 'back_to_menu' }]); return await ctx.reply(message, Markup.inlineKeyboard(buttons).resize()); }); - // Построение предприятия bot.action('build_enterprise', async (ctx) => { try { @@ -567,42 +586,18 @@ bot.action('manage_warehouse', async (ctx) => { return await ctx.reply(message, Markup.inlineKeyboard(buttons).resize()); }); -bot.action('warehouse_management', async (ctx) => { - let user = await UserModel.findByPk(ctx.from.id) - let warehouse = await WarehouseModel.findOne({ where: { playerId: user.telegram_id } }) - - if (!warehouse) { - return await ctx.reply(`У вас нет склада. Вы можете купить его за 500,000 руб.`, Markup.inlineKeyboard([ - [{ text: 'Купить склад', callback_data: 'buy_warehouse' }] - ]).resize()) - } - - let message = ` - 🏗️ Единый склад - Емкость: ${warehouse.capacity} ед. - Ресурсы: - 🌲 Дерево: ${warehouse.wood || 0} - ⛏️ Уголь: ${warehouse.coal || 0} - 🛢️ Нефть: ${warehouse.oil || 0} - 🛠️ Металл: ${warehouse.metall || 0} - 💰 Золото: ${warehouse.gold || 0} - 💎 Алмазы: ${warehouse.diamond || 0} - ` - return await ctx.reply(message, Markup.inlineKeyboard([ - [{ text: 'Управление транспортом', callback_data: 'manage_trucks' }], - [{ text: 'Продать ресурсы', callback_data: 'sell_from_warehouse' }] - ]).resize()) -}) - // Управление предприятием bot.action(/manage_(\d+)/, async (ctx) => { let enterpriseId = ctx.match[1] let enterprise = await EnterpriseModel.findByPk(enterpriseId) if (!enterprise) return await ctx.reply(`Предприятие не найдено.`) - + const buttons = [ + [{ text: 'Продать ресурсы', callback_data: 'sell_resources' }], + [{ text: 'Нанять грузовик', callback_data: 'hire_truck' }] + ]; // Логика управления предприятием (например, прокачка или продажа ресурсов) - return await ctx.reply(`Вы управляете предприятием ${enterprise.name} (ID: ${enterprise.id}).`) + return await ctx.reply(`Вы управляете предприятием ${enterprise.name} (ID: ${enterprise.id}).`, Markup.inlineKeyboard(buttons).resize()) }) bot.action('transfer_resources', async (ctx) => { const user = await UserModel.findByPk(ctx.from.id); @@ -630,24 +625,41 @@ bot.action('transfer_resources', async (ctx) => { // Перевозка с конкретного предприятия bot.action(/transfer_from_(\d+)/, async (ctx) => { const enterpriseId = ctx.match[1]; - const user = await UserModel.findByPk(ctx.from.id); - const warehouse = await WarehouseModel.findOne({ where: { playerId: user.telegram_id } }); - const enterprise = await EnterpriseModel.findByPk(enterpriseId); + if (!enterprise) { return await ctx.reply('Предприятие не найдено.'); } - let message = `🚛 Перевозка с предприятия ${enterprise.name}:\n`; - message += `🌲 Дерево: ${enterprise.wood || 0}\n`; - message += `⛏️ Уголь: ${enterprise.coal || 0}\n`; - message += `🛢️ Нефть: ${enterprise.oil || 0}\n`; - message += `⚙️ Металл: ${enterprise.metall || 0}\n`; - message += `🥇 Золото: ${enterprise.gold || 0}\n`; - message += `💎 Алмазы: ${enterprise.diamond || 0}\n`; - message += `\nВведите количество ресурса для перевозки.`; + const { currentResources, resourceType, playerId } = enterprise; - return await ctx.reply(message); + if (currentResources === 0) { + return await ctx.reply('На предприятии нет ресурсов для перевода.'); + } + + const warehouse = await WarehouseModel.findOne({ where: { playerId } }); + + if (!warehouse) { + return await ctx.reply('У вас нет склада для перевода ресурсов.'); + } + + const freeCapacity = warehouse.capacity - getWarehouseUsedCapacity(warehouse); + const transferAmount = Math.min(currentResources, freeCapacity); + + if (transferAmount === 0) { + return await ctx.reply('Склад заполнен. Перевод невозможен.'); + } + + // Перевод ресурсов + warehouse[resourceType] = (warehouse[resourceType] || 0) + transferAmount; + enterprise.currentResources -= transferAmount; + + await warehouse.save(); + await enterprise.save(); + + return await ctx.reply( + `Успешно переведено ${transferAmount} ед. ${resourceType} с предприятия в склад.` + ); }); bot.command('ent_rename', async (ctx) => { @@ -712,70 +724,226 @@ bot.action(/upgrade_(\d+)/, async (ctx) => { return await ctx.reply(`Предприятие ${enterprise.name} успешно прокачано! Уровень: ${enterprise.level}, Производительность: ${enterprise.efficiency} ед/ч.`) }) +// Показ списка предприятий bot.action('sell_resources', async (ctx) => { - let user = await UserModel.findByPk(ctx.from.id) - let enterprises = await EnterpriseModel.findAll({ - where: { - playerId: user.telegram_id - } - }) + const user = await UserModel.findByPk(ctx.from.id); + const enterprises = await EnterpriseModel.findAll({ + where: { playerId: user.telegram_id } + }); - if (enterprises.length === 0) return await ctx.reply(`У вас нет предприятий для продажи ресурсов.`) - - let buttons = [] - enterprises.forEach(enterprise => { - buttons.push({ text: `Продать ресурсы с ${enterprise.name}`, callback_data: `sell_${enterprise.id}` }) - }) - - return await ctx.reply(`Выберите предприятие для продажи ресурсов:`, Markup.inlineKeyboard(buttons).resize()) -}) - -bot.action(/sell_(\d+)/, async (ctx) => { - let user = await UserModel.findByPk(ctx.from.id) - let enterpriseId = ctx.match[1] - let enterprise = await EnterpriseModel.findByPk(enterpriseId) - - if (!enterprise) return await ctx.reply(`Предприятие не найдено.`) - - // Логика продажи ресурсов с предприятия - const resourceQuantity = await ResourceModel.findOne({ - where: { playerId: user.telegram_id, type: enterprise.resourceType } - }) - - if (!resourceQuantity || resourceQuantity.quantity <= 0) { - return await ctx.reply(`У вас нет ресурсов для продажи на предприятии ${enterprise.name}.`) + if (enterprises.length === 0) { + return await ctx.reply('У вас нет предприятий для продажи ресурсов.'); } - const salePrice = getResourcePrice(enterprise.resourceType) // Цена за единицу ресурса - const totalSale = salePrice * resourceQuantity.quantity + let message = 'Список ваших предприятий и их ресурсы:\n\n'; + enterprises.forEach((enterprise, index) => { + message += `${index + 1}. ${enterprise.name} (${enterprise.resourceType}): ${enterprise.currentResources} единиц\n`; + }); - // Обновляем количество ресурсов - await ResourceModel.update( - { quantity: 0 }, // Все ресурсы продаются - { where: { playerId: user.telegram_id, type: enterprise.resourceType } } - ) + message += `\nЧтобы продать ресурсы, используйте команду:\n/sellres <номер_предприятия> <количество>\nПример: /sellres 1 50`; + return await ctx.reply(message); +}); - // Добавляем деньги пользователю - await user.update({ balance: user.balance + totalSale }) +// Обработка продажи ресурсов +bot.command('sellres', async (ctx) => { + const user = await UserModel.findByPk(ctx.from.id); + const [enterpriseId, amountToSell] = ctx.message.text.split(' ').slice(1); - return await ctx.reply(`Вы продали все ресурсы с ${enterprise.name} за ${totalSale} монет.`) -}) + if (!enterpriseId || !amountToSell) { + return await ctx.reply('Неверный формат команды. Пример: /sellres 1 50'); + } + const enterprise = await EnterpriseModel.findByPk(enterpriseId, { + where: { playerId: user.telegram_id } + }); + + if (!enterprise) { + return await ctx.reply('Предприятие с таким id не найдено.'); + } + + const amount = parseInt(amountToSell, 10); + + if (isNaN(amount) || amount <= 0) { + return await ctx.reply('Укажите корректное количество для продажи.'); + } + + if (enterprise.currentResources < amount) { + return await ctx.reply( + `На предприятии ${enterprise.name} недостаточно ресурсов. Доступно: ${enterprise.currentResources} единиц.` + ); + } + + const resourcePrice = await ResourcePriceModel.findOne({ + where: { resource: enterprise.resourceType } + }); + + if (!resourcePrice) { + return await ctx.reply('Не удалось получить цену ресурса.'); + } + + const totalSale = Math.floor(amount * resourcePrice.price); + + // Обновляем данные + enterprise.currentResources -= amount; + await enterprise.save(); + + user.money += totalSale; + await user.save(); + + // Инфляция: уменьшение цены ресурса + resourcePrice.price = Math.round( + resourcePrice.price * Math.exp(-resourcePrice.fluctuationRate * amount) + ); + + // Гарантируем, что цена не упадет ниже 50% от базовой цены + resourcePrice.price = Math.max(resourcePrice.price, Math.round(resourcePrice.basePrice * 0.5)); + + await resourcePrice.save(); + + return await ctx.reply( + `Вы продали ${amount} единиц ${enterprise.resourceType} с предприятия ${enterprise.name} за ${totalSale} монет.\nТекущая цена за единицу: ${resourcePrice.price.toFixed(0)} монет.` + ); +}); + + +// Восстановление цен ресурсов +const recoverResourcePrices = async () => { + const resources = await ResourcePriceModel.findAll(); + + for (const resource of resources) { + if (resource.price < resource.basePrice) { + resource.price = Math.min( + resource.price + resource.basePrice * resource.recoveryRate, + resource.basePrice // Не превышать базовую цену + ); + await resource.save(); + } + } +}; + +// Запускаем процесс восстановления цен каждые 15 минут +setInterval(recoverResourcePrices, 15 * 60 * 1000); + +bot.action(/sell_all_(\d+)/, async (ctx) => { + const enterpriseId = ctx.match[1]; + const enterprise = await EnterpriseModel.findByPk(enterpriseId); + + if (!enterprise) { + return await ctx.reply('Предприятие не найдено.'); + } + + const { currentResources, resourceType, playerId } = enterprise; + const resourcePrice = await ResourcePriceModel.findOne({ where: { resource: resourceType } }); + const totalPrice = currentResources * resourcePrice.price; + + if (currentResources === 0) { + return await ctx.reply('На предприятии нет ресурсов для продажи.'); + } + + await ctx.reply( + `Вы уверены, что хотите продать все ${currentResources} ед. ${resourceType} за ${totalPrice} руб.? Это необратимо!\n\nРекомендуем воспользоваться командой /sell для более гибкого управления продажами.`, + Markup.inlineKeyboard([ + [ + { text: '✅ Продать всё', callback_data: `confirm_ressell_${enterpriseId}` }, + { text: '❌ Отмена', callback_data: 'cancel' } + ] + ]) + ); +}); + +bot.action(/confirm_ressell_(\d+)/, async (ctx) => { + const enterpriseId = ctx.match[1]; + const enterprise = await EnterpriseModel.findByPk(enterpriseId); + + if (!enterprise) { + return await ctx.reply('Предприятие не найдено.'); + } + + const { currentResources, resourceType, playerId } = enterprise; + const resourcePrice = await ResourcePriceModel.findOne({ where: { resource: resourceType } }); + const totalPrice = currentResources * resourcePrice.price; + + if (currentResources === 0) { + return await ctx.reply('На предприятии нет ресурсов для продажи.'); + } + + // Обновление баланса игрока + const user = await UserModel.findByPk(playerId); + user.money += totalPrice; + enterprise.currentResources = 0; + + await user.save(); + await enterprise.save(); + // Инфляция: уменьшение цены ресурса + resourcePrice.price = Math.round( + resourcePrice.price * Math.exp(-resourcePrice.fluctuationRate * amount) + ); + + // Гарантируем, что цена не упадет ниже 50% от базовой цены + resourcePrice.price = Math.max(resourcePrice.price, Math.round(resourcePrice.basePrice * 0.5)); + + await resourcePrice.save(); + + await ctx.reply(`Вы успешно продали ${currentResources} ед. ${resourceType} за ${totalPrice} руб.`); +}); + +// Найм грузовика bot.action('hire_truck', async (ctx) => { - let user = await UserModel.findByPk(ctx.from.id) - let warehouse = await WarehouseModel.findOne({ where: { playerId: user.telegram_id } }) + const user = await UserModel.findByPk(ctx.from.id); - if (!warehouse) return await ctx.reply(`У вас нет склада для найма грузовиков.`) + // Получаем список предприятий пользователя + const enterprises = await EnterpriseModel.findAll({ + where: { playerId: user.telegram_id } + }); - // Логика найма грузовика - let truck = await TruckModel.create({ - warehouseId: warehouse.id, + if (enterprises.length === 0) { + return await ctx.reply('У вас нет предприятий для найма грузовиков.'); + } + + // Создаем кнопки для выбора предприятия + const buttons = enterprises.map((enterprise) => [ + { text: `Нанять для ${enterprise.name}`, callback_data: `hire_truck_${enterprise.id}` } + ]); + + return await ctx.reply( + 'Выберите предприятие для найма грузовика:', + Markup.inlineKeyboard(buttons).resize() + ); +}); + +// Обработка найма грузовика для выбранного предприятия +bot.action(/hire_truck_(\d+)/, async (ctx) => { + const user = await UserModel.findByPk(ctx.from.id); + const enterpriseId = ctx.match[1]; + + // Находим предприятие + const enterprise = await EnterpriseModel.findByPk(enterpriseId); + + if (!enterprise || enterprise.playerId != user.telegram_id) { + return await ctx.reply('Предприятие не найдено или вам не принадлежит.'); + } + + // Проверяем, есть ли уже грузовик для этого предприятия + const existingTruck = await TruckModel.findOne({ + where: { enterpriseId: enterprise.id } + }); + + if (existingTruck) { + return await ctx.reply(`У вас уже есть грузовик для предприятия ${enterprise.name}.`); + } + + // Создаем новый грузовик + const newTruck = await TruckModel.create({ + enterpriseId: enterprise.id, capacity: 10, // Начальная вместимость efficiency: 1 // Начальная эффективность - }) + }); + + return await ctx.reply( + `Вы успешно наняли грузовик для предприятия ${enterprise.name}!` + ); +}); - return await ctx.reply(`Вы наняли грузовик для транспортировки ресурсов!`) -}) async function initializePrices() { const resources = [ @@ -810,91 +978,82 @@ setInterval(async () => { } }, 60000) // Обновляем цены каждые 60 секунд +// Функция добычи ресурсов const resourceProduction = async () => { try { - // Получаем все предприятия без фильтрации по пользователю + // Получаем все предприятия const enterprises = await EnterpriseModel.findAll(); for (const enterprise of enterprises) { - const { id, efficiency, warehouseCapacity, resourceType, playerId, level, currentResources } = enterprise; + const { + id, + efficiency, + warehouseCapacity, + resourceType, + currentResources, + level, + playerId + } = enterprise; - // Рассчитываем добычу ресурсов + // Расчёт добытых ресурсов const producedResources = efficiency * level; - // Проверяем, не превышает ли общая сумма ресурсов ёмкость склада + // Проверяем ёмкость склада предприятия if (currentResources + producedResources <= warehouseCapacity) { - enterprise.currentResources = currentResources + producedResources; + enterprise.currentResources += producedResources; await enterprise.save(); - console.log(`Предприятие ${id}: добыто ${producedResources} единиц ${resourceType}.`); + console.log( + `Предприятие ${id}: добыто ${producedResources} единиц ${resourceType}.` + ); } else { console.log(`Предприятие ${id}: склад заполнен.`); } - - // Получаем универсальный склад, если он есть - const Warehouse = await WarehouseModel.findOne({ - where: { playerId: playerId } // Привязка к игроку через playerId - }); - - if (Warehouse) { - const transportableAmount = Math.min( - enterprise.currentResources, - universalWarehouse.remainingCapacity - ); - - if (transportableAmount > 0) { - // Перемещаем ресурсы - enterprise.currentResources -= transportableAmount; - Warehouse.resources[resourceType] = - (Warehouse.resources[resourceType] || 0) + transportableAmount; - - await enterprise.save(); - await Warehouse.save(); - - console.log(`Предприятие ${id}: перевезено ${transportableAmount} единиц ${resourceType} на универсальный склад.`); - } else { - console.log(`Универсальный склад игрока ${playerId} заполнен.`); - } - } } } catch (error) { console.error('Ошибка добычи ресурсов:', error); } }; - -// Запуск функции каждый час -setInterval(resourceProduction, 60 * 60 * 1000); - -// Функция добычи ресурсов +// Функция транспортировки ресурсов const resourceTransportation = async () => { try { + // Получаем все предприятия const enterprises = await EnterpriseModel.findAll(); for (const enterprise of enterprises) { - const { id, resourceType, currentResources, playerId } = enterprise; + const { + id, + resourceType, + currentResources, + playerId + } = enterprise; if (currentResources > 0) { - // Получаем склад игрока + // Получаем грузовики, привязанные к предприятию + const trucks = await TruckModel.findAll({ + where: { enterpriseId: id } + }); + + if (trucks.length === 0) { + console.log( + `Предприятие ${id}: нет доступных грузовиков для транспортировки.` + ); + continue; + } + + // Получаем универсальный склад игрока const warehouse = await WarehouseModel.findOne({ where: { playerId } }); if (!warehouse) { - console.log(`У игрока ${playerId} нет склада.`); + console.log( + `Предприятие ${id}: у игрока ${playerId} нет универсального склада.` + ); continue; } - // Получаем грузовики, привязанные к этому складу - const trucks = await Truck.findAll({ - where: { warehouseId: warehouse.id } - }); - - if (trucks.length === 0) { - console.log(`На складе ${warehouse.id} нет доступных грузовиков.`); - continue; - } - - // Транспортируем ресурсы + // Транспортировка ресурсов let totalTransported = 0; for (const truck of trucks) { const transportableAmount = Math.min( @@ -905,7 +1064,8 @@ const resourceTransportation = async () => { if (transportableAmount > 0) { // Обновляем данные склада - warehouse[resourceType] = (warehouse[resourceType] || 0) + transportableAmount; + warehouse[resourceType] = + (warehouse[resourceType] || 0) + transportableAmount; // Уменьшаем ресурсы на предприятии enterprise.currentResources -= transportableAmount; @@ -915,18 +1075,22 @@ const resourceTransportation = async () => { totalTransported += transportableAmount; - console.log(`Грузовик ${truck.id} перевёз ${transportableAmount} единиц ${resourceType}.`); + console.log( + `Грузовик ${truck.id} перевёз ${transportableAmount} единиц ${resourceType}.` + ); } - // Прекращаем, если на предприятии закончились ресурсы + // Прекращаем, если ресурсы закончились if (enterprise.currentResources === 0) break; } if (totalTransported === 0) { - console.log(`Склад ${warehouse.id} заполнен. Ресурсы не перевезены.`); + console.log( + `Предприятие ${id}: склад заполнен, транспортировка невозможна.` + ); } } else { - console.log(`Предприятие ${id} не имеет доступных ресурсов для транспортировки.`); + console.log(`Предприятие ${id}: нет ресурсов для транспортировки.`); } } } catch (error) { @@ -940,7 +1104,8 @@ const getWarehouseUsedCapacity = (warehouse) => { return resources.reduce((sum, resource) => sum + (warehouse[resource] || 0), 0); }; -// Запускаем процесс транспортировки каждый час +// Запускаем процессы каждый час +setInterval(resourceProduction, 60 * 60 * 1000); setInterval(resourceTransportation, 60 * 60 * 1000); diff --git a/config/index.js b/config/index.js index 8a1b13b..073e4ed 100644 --- a/config/index.js +++ b/config/index.js @@ -21,6 +21,7 @@ module.exports = { LobbyModel: require('../models/lobby.model'), EnterpriseModel: require('../models/enterprise.model'), WarehouseModel: require('../models/warehouse.model'), + TruckModel: require('../models/truck.model'), ResourcePriceModel: require('../models/resourceprice.model'), SaleModel: require('../models/sales.model'), mainChat : -1001895132127, diff --git a/models/truck.model.js b/models/truck.model.js new file mode 100644 index 0000000..fbb7b49 --- /dev/null +++ b/models/truck.model.js @@ -0,0 +1,10 @@ +const sequelize = require('../db'); +const {DataTypes} = require('sequelize'); + +const Truck = sequelize.define('truck', { + enterpriseId: DataTypes.INTEGER, + capacity: DataTypes.INTEGER, // Сколько может перевезти за час + efficiency: DataTypes.INTEGER // Частота транспортировки (количество операций в час) +}) + +module.exports = Truck; diff --git a/models/warehouse.model.js b/models/warehouse.model.js index c997fa5..86f4c5a 100644 --- a/models/warehouse.model.js +++ b/models/warehouse.model.js @@ -12,10 +12,4 @@ const Warehouse = sequelize.define('warehouse', { diamond: DataTypes.INTEGER, }) -const Truck = sequelize.define('truck', { - warehouseId: DataTypes.INTEGER, - capacity: DataTypes.INTEGER, // Сколько может перевезти за час - efficiency: DataTypes.INTEGER // Частота транспортировки (количество операций в час) -}) - -module.exports = Warehouse, Truck; +module.exports = Warehouse;