v5.9.7
Item Drop Function Images for Items
16637
json/logs.json
BIN
media/items/armor_cloth.png
Normal file
After Width: | Height: | Size: 242 KiB |
BIN
media/items/armor_leather.png
Normal file
After Width: | Height: | Size: 281 KiB |
BIN
media/items/armor_neiroshield.png
Normal file
After Width: | Height: | Size: 284 KiB |
BIN
media/items/baseball_bat.png
Normal file
After Width: | Height: | Size: 154 KiB |
BIN
media/items/big_aid_kit.png
Normal file
After Width: | Height: | Size: 256 KiB |
BIN
media/items/boots_army.png
Normal file
After Width: | Height: | Size: 335 KiB |
BIN
media/items/cardreader_emulator.png
Normal file
After Width: | Height: | Size: 204 KiB |
BIN
media/items/drink_litenergy.png
Normal file
After Width: | Height: | Size: 261 KiB |
BIN
media/items/drink_redbull.png
Normal file
After Width: | Height: | Size: 256 KiB |
BIN
media/items/drink_tornadoenergy.png
Normal file
After Width: | Height: | Size: 215 KiB |
BIN
media/items/glasses_botanic.png
Normal file
After Width: | Height: | Size: 187 KiB |
BIN
media/items/grenade_low.png
Normal file
After Width: | Height: | Size: 181 KiB |
BIN
media/items/helmet_moto.png
Normal file
After Width: | Height: | Size: 225 KiB |
BIN
media/items/image (12).png
Normal file
After Width: | Height: | Size: 291 KiB |
BIN
media/items/implant_vaultone.png
Normal file
After Width: | Height: | Size: 261 KiB |
BIN
media/items/knife.png
Normal file
After Width: | Height: | Size: 166 KiB |
BIN
media/items/knuckle_basic.png
Normal file
After Width: | Height: | Size: 201 KiB |
BIN
media/items/medium_aid_kit.png
Normal file
After Width: | Height: | Size: 168 KiB |
BIN
media/items/pistol_pioneer.png
Normal file
After Width: | Height: | Size: 230 KiB |
BIN
media/items/pistol_regular.png
Normal file
After Width: | Height: | Size: 232 KiB |
BIN
media/items/simcard.png
Normal file
After Width: | Height: | Size: 152 KiB |
BIN
media/items/small_aid_kit.png
Normal file
After Width: | Height: | Size: 107 KiB |
BIN
media/items/stimulator_25.png
Normal file
After Width: | Height: | Size: 186 KiB |
BIN
media/items/template.png
Normal file
After Width: | Height: | Size: 133 KiB |
@ -56,6 +56,11 @@ const Inventory = sequelize.define('inventory', {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false
|
||||
},
|
||||
img: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
defaultValue: faker.image.url()
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Inventory;
|
||||
|
@ -33,6 +33,11 @@ const Item = sequelize.define('item', {
|
||||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
},
|
||||
dropChance: {
|
||||
type: DataTypes.FLOAT,
|
||||
allowNull: false,
|
||||
defaultValue: 1,
|
||||
},
|
||||
type: {
|
||||
type: DataTypes.STRING, // Тип предмета (например, "инструмент", "ресурс")
|
||||
allowNull: false,
|
||||
@ -45,6 +50,11 @@ const Item = sequelize.define('item', {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: false
|
||||
},
|
||||
img: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
defaultValue: faker.image.url()
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = Item;
|
||||
|
445
rpg.js
@ -1,5 +1,7 @@
|
||||
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');
|
||||
@ -8,6 +10,7 @@ const sequelize = require('./db'); // Подключение базы данны
|
||||
const utils = require('./utils');
|
||||
const handlers = require('./handlers');
|
||||
const { keyboard } = require('telegraf/markup');
|
||||
const { parse } = require('error-stack-parser');
|
||||
const {
|
||||
phones,
|
||||
expToUp,
|
||||
@ -973,6 +976,29 @@ rpg.action(/lock_*/, async (ctx) => {
|
||||
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);
|
||||
@ -997,7 +1023,7 @@ rpg.action('inventory', async (ctx) => {
|
||||
if (equippedItems.length > 0) {
|
||||
message += '🛡️ Снаряженные предметы:\n';
|
||||
equippedItems.forEach((item) => {
|
||||
message += `- ${item.name} (${item.type})\n`;
|
||||
message += `- ${item.name} (${getItemType(item.type)})\n`;
|
||||
});
|
||||
message += '\n';
|
||||
}
|
||||
@ -1005,7 +1031,7 @@ rpg.action('inventory', async (ctx) => {
|
||||
if (unequippedItems.length > 0) {
|
||||
message += '🎒 Предметы в инвентаре:\n';
|
||||
unequippedItems.forEach((item) => {
|
||||
message += `- ${item.name} (${item.type})\n`;
|
||||
message += `- ${item.name} (${getItemType(item.type)})\n`;
|
||||
});
|
||||
message += '\n';
|
||||
}
|
||||
@ -1044,17 +1070,46 @@ rpg.action(/view_item_(\d+)/, async (ctx) => {
|
||||
if (item.equipped) {
|
||||
return ctx.reply(`${item.name} уже снаряжен.`);
|
||||
}
|
||||
const buttons = []
|
||||
let message = `ℹ️ ${item.name}\n`
|
||||
message += ` 📜 ${item.description}\n\n 〰️Редкость: ${item.rarity}\n 🔤Тип: ${item.type}`
|
||||
|
||||
// Подготовка кнопок
|
||||
const buttons = [];
|
||||
if (!item.equipped) {
|
||||
buttons.push([Markup.button.callback(`🎯 Использовать ${item.name}`, `use_item_${item.id}`)]);
|
||||
buttons.push({ text: `🎯 Использовать ${item.name}`, callback_data: `use_item_${item.id}` });
|
||||
}
|
||||
if (item.equipped && item.canBeEquipped) {
|
||||
buttons.push([Markup.button.callback(`🚫 Снять ${item.name}`, `unequip_item_${item.id}`)]);
|
||||
buttons.push({ text: `🚫 Снять ${item.name}`, callback_data: `unequip_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'); // Путь к картинке
|
||||
console.log(imagePath);
|
||||
|
||||
// Проверяем существует ли файл
|
||||
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('Произошла ошибка при отправке сообщения.');
|
||||
}
|
||||
logs(ctx, "Просмотр предмета", {item: item});
|
||||
await ctx.reply(message, Markup.inlineKeyboard(buttons, { columns: 2 }));
|
||||
});
|
||||
|
||||
rpg.action(/use_item_(\d+)/, async (ctx) => {
|
||||
@ -1079,6 +1134,15 @@ rpg.action(/use_item_(\d+)/, async (ctx) => {
|
||||
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}, пока надеты другие предметы того же типа.`);
|
||||
}
|
||||
|
||||
// Применяем эффекты предмета
|
||||
if (item.effectData) {
|
||||
const resultMessages = processEffects(character, item.effectData, true);
|
||||
@ -1191,13 +1255,50 @@ rpg.action(/buy_item_(\d+)/, async (ctx) => {
|
||||
type: item.type,
|
||||
duration: item.duration,
|
||||
canBeEquipped: item.canBeEquipped,
|
||||
equipped: false
|
||||
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);
|
||||
console.log(itemId)
|
||||
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) {
|
||||
@ -1275,14 +1376,50 @@ const processEffects = (character, effects, isEquipping) => {
|
||||
case 'stamina_penalty':
|
||||
character.max_stamina += effect.amount;
|
||||
character.save()
|
||||
messages.push(`Ваша выносливость увеличена на ${effect.amount}.`);
|
||||
messages.push(`Ваша стамина увеличена на ${effect.amount}.`);
|
||||
break;
|
||||
|
||||
case 'intelligence_boost':
|
||||
character.intelligence -= Math.max(0, character.intelligence - effect.amount);
|
||||
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('Неизвестный эффект при снятии.');
|
||||
@ -1311,7 +1448,7 @@ const processEffects = (character, effects, isEquipping) => {
|
||||
case 'stamina_penalty':
|
||||
character.max_stamina -= effect.amount;
|
||||
character.save()
|
||||
messages.push(`Ваша выносливость уменьшена на ${effect.amount}.`);
|
||||
messages.push(`Ваша стамина уменьшена на ${effect.amount}.`);
|
||||
break;
|
||||
|
||||
case 'stamina_recover':
|
||||
@ -1326,6 +1463,42 @@ const processEffects = (character, effects, isEquipping) => {
|
||||
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('Неизвестный эффект при снаряжении.');
|
||||
}
|
||||
@ -1335,6 +1508,70 @@ const processEffects = (character, effects, isEquipping) => {
|
||||
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 = [];
|
||||
@ -1787,14 +2024,28 @@ const startBattle = async (ctx, character, enemy, battle) => {
|
||||
const enemyTurn = async (ctx, character, battle) => {
|
||||
const enemy = await Enemy.findByPk(battle.enemy);
|
||||
|
||||
const dodgeChance = Math.min(character.resistance * 0.05, 0.5);
|
||||
const dodgeChance = Math.min(character.resilience * 0.05, 0.5);
|
||||
const isDodged = Math.random() < dodgeChance;
|
||||
// Генерация кнопок
|
||||
const buttons = generateBattleButtons(character, battle);
|
||||
const keyboard = Markup.inlineKeyboard(buttons);
|
||||
// Логи битвы
|
||||
const battlelogs = battle.logs || [];
|
||||
const logMessage = battlelogs.slice(-5).map((log) => `• ${log}`).join("\n");
|
||||
|
||||
if (isDodged) {
|
||||
// Сообщение с информацией
|
||||
battle.logs.push(`💨 ${character.name} уклонились от атаки противника!`);
|
||||
await battle.save({ fields: ["logs"] });
|
||||
logs(ctx, "Уклонение от атаки", { enemy, battle });
|
||||
return;
|
||||
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;
|
||||
@ -1805,6 +2056,23 @@ const enemyTurn = async (ctx, character, battle) => {
|
||||
battle.logs.push(
|
||||
`💔 Противник нанес ${character.name} ${damage} урона!\n\n${character.name} потерпел поражение от ${enemy.name}.`
|
||||
);
|
||||
// При проигрыше игрок потеряет деньги или экипированные предметы
|
||||
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)];
|
||||
// Снимаем предмет и снимает эффекты с персонажа
|
||||
processEffects(character, randomItem.effectData, false);
|
||||
randomItem.equipped = false;
|
||||
await randomItem.destroy();
|
||||
battle.logs.push(`💼 ${character.name} потерял ${randomItem.name}.`);
|
||||
} else {
|
||||
// Теряет 10% от денег
|
||||
const lostMoney = Math.floor(character.dirtymoney * 0.1);
|
||||
character.dirtymoney -= lostMoney;
|
||||
battle.logs.push(`💰 ${character.name} потерял ${lostMoney} монет.`);
|
||||
}
|
||||
await character.save();
|
||||
await battle.save({ fields: ["logs", "status"] });
|
||||
logs(ctx, "Поражение персонажа", { enemy, battle });
|
||||
return;
|
||||
@ -1814,6 +2082,14 @@ const enemyTurn = async (ctx, character, battle) => {
|
||||
battle.logs.push(`💔 Противник нанес ${character.name} ${damage} урона. У ${character.name} осталось ${character.hp} HP.`);
|
||||
await battle.save({ fields: ["logs"] });
|
||||
logs(ctx, "Атака врага", { enemy, battle, damage });
|
||||
return await ctx.editMessageText(
|
||||
`⚔️ Сражение с ${enemy.name}\n\n` +
|
||||
`❤️ Здоровье врага: ${battle.enemy_hp}/${enemy.hp}\n` +
|
||||
`⚔️ Урон врага: ${enemy.damage}\n\n` +
|
||||
`📜 Логи битвы:\n${logMessage || "Пока ничего не произошло."}\n\n` +
|
||||
`🎯 Выберите цель для атаки:`,
|
||||
keyboard
|
||||
);
|
||||
};
|
||||
|
||||
rpg.action(/attack_\d+/, async (ctx) => {
|
||||
@ -1960,6 +2236,143 @@ function generateBattleButtons(character, battle) {
|
||||
return rows;
|
||||
}
|
||||
|
||||
// Функция выпадения предмета с врага или локации с учетом аксессуаров и управляемого шанса
|
||||
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) {
|
||||
console.log('enemyid')
|
||||
source = await Enemy.findByPk(enemyId);
|
||||
if (!source) return null;
|
||||
items = source.loot; // Лут из врага
|
||||
} else if (locationId) {
|
||||
console.log('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}%`);
|
||||
}
|
||||
}
|
||||
|
||||
// Если предметы выпали, возвращаем их, иначе 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) {
|
||||
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);
|
||||
|
||||
|
||||
|
||||
// Обработка критического удара
|
||||
@ -2050,7 +2463,7 @@ rpg.action(/miss_\d+/, async (ctx) => {
|
||||
if (character.hp <= 0) {
|
||||
logs(ctx, "Поражение персонажа", { enemy, battle });
|
||||
return ctx.editMessageText(
|
||||
`💔 ${character.name} потерпел поражение от ${enemy.name}!\n\n📜 Логи битвы:\n${logs}`
|
||||
`💔 ${character.name} потерпел поражение от ${enemy.name}!\n\n📜 Логи битвы:\n${logMessage}`
|
||||
);
|
||||
}
|
||||
logs(ctx, "Промах", { enemy, battle });
|
||||
|