Menu rework
Inventory system
Shop
This commit is contained in:
Degradin 2025-01-10 19:09:05 +03:00
parent 125b782a64
commit 7e6980207f
5 changed files with 156 additions and 43 deletions

View File

@ -1,5 +1,6 @@
const { DataTypes } = require('sequelize'); const { DataTypes } = require('sequelize');
const sequelize = require('../db'); const sequelize = require('../db');
const { faker } = require('@faker-js/faker');
const Inventory = sequelize.define('inventory', { const Inventory = sequelize.define('inventory', {
id: { id: {
@ -18,6 +19,11 @@ const Inventory = sequelize.define('inventory', {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
}, },
text_id: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: faker.string.uuid()
},
description: { description: {
type: DataTypes.TEXT, type: DataTypes.TEXT,
allowNull: false, allowNull: false,
@ -42,6 +48,10 @@ const Inventory = sequelize.define('inventory', {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: true allowNull: true
}, // Длительность эффекта в секундах }, // Длительность эффекта в секундах
canBeEquipped: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
equipped: { equipped: {
type: DataTypes.BOOLEAN, type: DataTypes.BOOLEAN,
defaultValue: false defaultValue: false

View File

@ -1,5 +1,6 @@
const { DataTypes } = require('sequelize'); const { DataTypes } = require('sequelize');
const sequelize = require('../db'); const sequelize = require('../db');
const { faker } = require('@faker-js/faker');
const Item = sequelize.define('item', { const Item = sequelize.define('item', {
id: { id: {
@ -7,6 +8,11 @@ const Item = sequelize.define('item', {
autoIncrement: true, autoIncrement: true,
primaryKey: true, primaryKey: true,
}, },
text_id: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: faker.string.uuid()
},
name: { name: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: false, allowNull: false,
@ -35,6 +41,10 @@ const Item = sequelize.define('item', {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: true allowNull: true
}, // Длительность эффекта в секундах }, // Длительность эффекта в секундах
canBeEquipped: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
}); });
module.exports = Item; module.exports = Item;

View File

@ -5,7 +5,9 @@ const World = sequelize.define('world', {
id: {type: DataTypes.INTEGER, primaryKey: true, unique: true}, id: {type: DataTypes.INTEGER, primaryKey: true, unique: true},
balance: {type: DataTypes.INTEGER, defaultValue: 50000000}, balance: {type: DataTypes.INTEGER, defaultValue: 50000000},
transactionfee: {type: DataTypes.INTEGER, defaultValue: 1}, transactionfee: {type: DataTypes.INTEGER, defaultValue: 1},
matPrice: {type: DataTypes.INTEGER, defaultValue: 100} matPrice: {type: DataTypes.INTEGER, defaultValue: 100},
itemsInCrimeShop: { type: DataTypes.ARRAY(DataTypes.INTEGER), defaultValue: [] }
}) })
module.exports = World; module.exports = World;

160
rpg.js
View File

@ -92,8 +92,10 @@ rpg.action('rpg_profile', async (ctx) => {
`; `;
// Отправляем сообщение // Отправляем сообщение
ctx.reply(profile.trim(), Markup.inlineKeyboard([ ctx.editMessageText(profile.trim(), Markup.inlineKeyboard([
[Markup.button.callback('💼 Задачи', 'crime_missions')], [Markup.button.callback('💳 Карточки', 'view_cards')],
[Markup.button.callback('🎒 Инвентарь', 'inventory')],
[Markup.button.callback('🔙 В меню', 'crime_menu')],
])); ]));
}); });
@ -105,7 +107,7 @@ const usersCrimeNet = new Set();
rpg.hears('CampFireGG.Crime', async (ctx) => { rpg.hears('CampFireGG.Crime', async (ctx) => {
const userId = ctx.from.id; const userId = ctx.from.id;
const character = await CharacterModel.findOne({ where: { telegram_id: userId } }); const character = await CharacterModel.findOne({ where: { telegram_id: userId } });
if (!usersCrimeNet.has(userId) && userId != 275416286){ if (!usersCrimeNet.has(userId) && character.level === 1){
// Если пользователь вводит команду впервые // Если пользователь вводит команду впервые
usersCrimeNet.add(userId); usersCrimeNet.add(userId);
const username = ctx.from.username || 'User'; const username = ctx.from.username || 'User';
@ -171,8 +173,6 @@ rpg.hears('CampFireGG.Crime', async (ctx) => {
Markup.inlineKeyboard([ Markup.inlineKeyboard([
[Markup.button.callback('💼 Задачи', 'crime_missions')], [Markup.button.callback('💼 Задачи', 'crime_missions')],
[Markup.button.callback('📊 Профиль', 'rpg_profile')], [Markup.button.callback('📊 Профиль', 'rpg_profile')],
[Markup.button.callback('💳 Карточки', 'view_cards')],
[Markup.button.callback('🎒 Инвентарь', 'inventory')],
[Markup.button.callback('💰 Магазин', 'shop')], [Markup.button.callback('💰 Магазин', 'shop')],
]) ])
); );
@ -241,6 +241,7 @@ rpg.action('view_cards', async (ctx) => {
buttons.push([ buttons.push([
Markup.button.callback(`💳 *${lastFourDigits}`, `brutecard_${card.id}`), Markup.button.callback(`💳 *${lastFourDigits}`, `brutecard_${card.id}`),
//Markup.button.callback(`💰 *${lastFourDigits}`, `limitbuy_${card.id}`) //Markup.button.callback(`💰 *${lastFourDigits}`, `limitbuy_${card.id}`)
Markup.button.callback('🔙 В меню', 'crime_menu'),
]); ]);
}); });
@ -374,7 +375,7 @@ rpg.action(/brutecard_/, async (ctx) => {
} }
// Проверяем наличие "Эмулятора картридера" в инвентаре // Проверяем наличие "Эмулятора картридера" в инвентаре
const emulator = inventory.find((item) => item.name === 'Эмулятор картридера'); const emulator = inventory.find((item) => item.text_id === 'cardreader_emulator');
if (!emulator) { if (!emulator) {
return ctx.reply('Для запуска брутфорса вам нужен "Эмулятор картридера".'); return ctx.reply('Для запуска брутфорса вам нужен "Эмулятор картридера".');
} }
@ -393,13 +394,15 @@ rpg.action(/brutecard_/, async (ctx) => {
// Генерация кнопок // Генерация кнопок
const buttons = []; const buttons = [];
let fakePin = 1234
for (let i = 1; i <= buttonsCount; i++) { for (let i = 1; i <= buttonsCount; i++) {
if (i === winButton) { if (i === winButton) {
// Если это победная кнопка (правильный ПИН) // Если это победная кнопка (правильный ПИН)
buttons.push({ text: `${card.pin}`, callback_data: `brute_success_${cardId}` }); buttons.push({ text: `${card.pin}`, callback_data: `brute_success_${cardId}` });
} else { } else {
fakePin = faker.finance.pin()
// Если это неудачная кнопка (случайный ПИН) // Если это неудачная кнопка (случайный ПИН)
buttons.push({ text: `${faker.finance.pin()}`, callback_data: `brute_fail_${cardId}` }); buttons.push({ text: `${fakePin}`, callback_data: `brute_fail_${cardId}_${i}` });
} }
} }
@ -462,12 +465,14 @@ rpg.action(/brute_fail_/, async (ctx) => {
const buttonsCount = 10; const buttonsCount = 10;
const winButton = utils.rand(1, buttonsCount); const winButton = utils.rand(1, buttonsCount);
const buttons = []; const buttons = [];
let fakePin = 1234
for (let i = 1; i <= buttonsCount; i++) { for (let i = 1; i <= buttonsCount; i++) {
fakePin = faker.finance.pin()
if (i === winButton) { if (i === winButton) {
buttons.push({ text: `${card.pin}`, callback_data: `brute_success_${cardId}` }); buttons.push({ text: `${card.pin}`, callback_data: `brute_success_${cardId}` });
} else { } else {
buttons.push({ text: `${faker.finance.pin()}`, callback_data: `brute_fail_${cardId}` }); buttons.push({ text: `${fakePin}`, callback_data: `brute_fail_${cardId}_${i}` });
} }
} }
@ -501,9 +506,7 @@ rpg.action(/brute_fail_/, async (ctx) => {
rpg.action(/brute_success_/, async (ctx) => { rpg.action(/brute_success_/, async (ctx) => {
const cardId = ctx.match.input.split('_')[2]; const cardId = ctx.match.input.split('_')[2];
console.log(cardId)
const card = await StolenCardsModel.findByPk(cardId); const card = await StolenCardsModel.findByPk(cardId);
console.log(card)
if (!card) { if (!card) {
return ctx.answerCbQuery('Карточка не найдена.'); return ctx.answerCbQuery('Карточка не найдена.');
} }
@ -549,7 +552,7 @@ rpg.action('crime_missions', async (ctx) => {
//[{text: 'Угон', callback_data: `WIP`}], //[{text: 'Угон', callback_data: `WIP`}],
// [{text: 'Ювелирка', callback_data: `WIP`}], // [{text: 'Ювелирка', callback_data: `WIP`}],
//[{text: 'Банк', callback_data: `WIP`}], //[{text: 'Банк', callback_data: `WIP`}],
[{text: '🔙 Back to Menu', callback_data: `crime_menu`}] [{text: '🔙 В меню', callback_data: `crime_menu`}]
]), ]),
); );
}); });
@ -623,9 +626,7 @@ rpg.action(`PHONE`, async (ctx) => {
} }
let randPhone = utils.rand(1,10) let randPhone = utils.rand(1,10)
if (property.mobile.name) { if (property.mobile.name) {
console.log(Math.round(phones[randPhone].price/100*70))
let dirtyMoney = Math.round(phones[randPhone].price/100*70) let dirtyMoney = Math.round(phones[randPhone].price/100*70)
console.log(character.intelligence + 10)
Exp(ctx, character, character.intelligence + 10) Exp(ctx, character, character.intelligence + 10)
character.dirtymoney += dirtyMoney character.dirtymoney += dirtyMoney
return await ctx.reply(`Вы сбыли украденный ${phones[randPhone].name} за Ð${utils.spaces(dirtyMoney)}`) return await ctx.reply(`Вы сбыли украденный ${phones[randPhone].name} за Ð${utils.spaces(dirtyMoney)}`)
@ -726,8 +727,8 @@ rpg.action('crime_menu', async (ctx) => {
`💻 CampFireGG.Crime Menu`, `💻 CampFireGG.Crime Menu`,
Markup.inlineKeyboard([ Markup.inlineKeyboard([
[Markup.button.callback('💼 Задачи', 'crime_missions')], [Markup.button.callback('💼 Задачи', 'crime_missions')],
[Markup.button.callback('📊 Профиль', 'crime_stats')], [Markup.button.callback('📊 Профиль', 'rpg_profile')],
[Markup.button.callback('💰 ...', 'crime_market')], [Markup.button.callback('💰 Магазин', 'shop')],
]) ])
); );
}); });
@ -885,7 +886,7 @@ rpg.action('inventory', async (ctx) => {
const unequippedItems = inventory.filter((item) => !item.equipped); const unequippedItems = inventory.filter((item) => !item.equipped);
if (equippedItems.length > 0) { if (equippedItems.length > 0) {
message += '🛡️ *Снаряженные предметы:*\n'; message += '🛡️ Снаряженные предметы:\n';
equippedItems.forEach((item) => { equippedItems.forEach((item) => {
message += `- ${item.name} (${item.type})\n`; message += `- ${item.name} (${item.type})\n`;
}); });
@ -893,7 +894,7 @@ rpg.action('inventory', async (ctx) => {
} }
if (unequippedItems.length > 0) { if (unequippedItems.length > 0) {
message += '🎒 *Предметы в инвентаре:*\n'; message += '🎒 Предметы в инвентаре:\n';
unequippedItems.forEach((item) => { unequippedItems.forEach((item) => {
message += `- ${item.name} (${item.type})\n`; message += `- ${item.name} (${item.type})\n`;
}); });
@ -903,20 +904,50 @@ rpg.action('inventory', async (ctx) => {
// Кнопки для взаимодействия // Кнопки для взаимодействия
const buttons = [ const buttons = [
...unequippedItems.map((item) => ...unequippedItems.map((item) =>
Markup.button.callback(`🎯 Использовать ${item.name}`, `use_item_${item.id}`) Markup.button.callback(`🔎 ${item.name}`, `view_item_${item.id}`)
), ),
...equippedItems.map((item) => ...equippedItems.map((item) =>
Markup.button.callback(`🚫 Снять ${item.name}`, `unequip_item_${item.id}`) Markup.button.callback(`🚫 Снять ${item.name}`, `unequip_item_${item.id}`)
), ),
]; ];
buttons.push(Markup.button.callback('🔙 В меню', 'crime_menu'));
await ctx.replyWithMarkdown(message, Markup.inlineKeyboard(buttons, { columns: 2 })); await ctx.editMessageText(message, Markup.inlineKeyboard(buttons, { columns: 2 }));
} catch (error) { } catch (error) {
console.error('Ошибка при выводе инвентаря:', error); console.error('Ошибка при выводе инвентаря:', error);
await ctx.reply('Произошла ошибка при отображении вашего инвентаря.'); 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 = []
let message = ` ${item.name}\n`
message += ` 📜 ${item.description}\n\n 〰️Редкость: ${item.rarity}\n 🔤Тип: ${item.type}`
if (!item.equipped){
buttons.push([Markup.button.callback(`🎯 Использовать ${item.name}`, `use_item_${item.id}`)]);
}
if (item.equipped && item.canBeEquipped) {
buttons.push([Markup.button.callback(`🚫 Снять ${item.name}`, `unequip_item_${item.id}`)]);
}
await ctx.replyWithMarkdown(message, Markup.inlineKeyboard(buttons, { columns: 2 }));
});
rpg.action(/use_item_(\d+)/, async (ctx) => { rpg.action(/use_item_(\d+)/, async (ctx) => {
const itemId = parseInt(ctx.match[1], 10); const itemId = parseInt(ctx.match[1], 10);
@ -936,17 +967,25 @@ rpg.action(/use_item_(\d+)/, async (ctx) => {
return ctx.reply(`${item.name} уже снаряжен.`); return ctx.reply(`${item.name} уже снаряжен.`);
} }
if (!item.canBeEquipped) {
return ctx.answerCbQuery(`🚫 ${item.name} нельзя сейчас использовать.`);
}
// Применяем эффекты предмета // Применяем эффекты предмета
if (item.effectData) { if (item.effectData) {
const resultMessages = processEffects(character, item.effectData, true); const resultMessages = processEffects(character, item.effectData, true);
await ctx.reply(resultMessages); await ctx.answerCbQuery(resultMessages, {show_alert: true});
} }
// Снаряжаем предмет if (item.canBeEquipped && item.type != 'consumable') {
item.equipped = true; item.equipped = true;
await item.save(); await item.save();
ctx.reply(`Вы снарядили ${item.name}.`);
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) => { rpg.action(/unequip_item_(\d+)/, async (ctx) => {
@ -955,47 +994,65 @@ rpg.action(/unequip_item_(\d+)/, async (ctx) => {
const item = await InventoryModel.findByPk(itemId); const item = await InventoryModel.findByPk(itemId);
if (!item || item.telegram_id != ctx.from.id) { if (!item || item.telegram_id != ctx.from.id) {
return ctx.reply('Этот предмет не найден в вашем инвентаре.'); return ctx.answerCbQuery('Этот предмет не найден в вашем инвентаре.');
} }
if (!item.equipped) { if (!item.equipped) {
return ctx.reply(`${item.name} не снаряжен.`); return ctx.answerCbQuery(`${item.name} не снаряжен.`);
} }
if (item.effectData) { if (item.effectData) {
const resultMessages = processEffects(character, item.effectData, false); const resultMessages = processEffects(character, item.effectData, false);
await ctx.reply(resultMessages); await ctx.editMessageText(resultMessages);
} }
item.equipped = false; item.equipped = false;
await item.save(); await item.save();
ctx.reply(`Вы успешно сняли ${item.name}.`); ctx.answerCbQuery(`Вы успешно сняли ${item.name}.`);
}); });
rpg.action('shop', async (ctx) => { rpg.action('shop', async (ctx) => {
const items = await ItemsModel.findAll(); // Получаем текущую запись мира
const world = await WorldModel.findOne({ where: { id: 1 } });
if (!world) {
return ctx.reply('Магазин недоступен.');
}
// Получаем ID предметов, доступных в магазине
const { itemsInCrimeShop } = world;
if (!itemsInCrimeShop || itemsInCrimeShop.length === 0) {
return ctx.reply('Магазин пуст!');
}
// Загружаем только те предметы, ID которых есть в массиве
const items = await ItemsModel.findAll({ where: { id: itemsInCrimeShop } });
if (items.length === 0) { if (items.length === 0) {
return ctx.reply('Магазин пуст!'); return ctx.reply('Магазин пуст!');
} }
let message = 'Добро пожаловать в магазин! Здесь вы можете купить предметы:\n\n'; let message = 'Добро пожаловать в магазин! Здесь вы можете купить предметы:\n\n';
// Генерируем кнопки, каждая кнопка будет в отдельной строке // Генерируем кнопки, каждая кнопка будет в отдельной строке
const buttons = items.map((item) => [ const buttons = items.map((item) => [
Markup.button.callback(`${item.name} - ${item.price}`, `buy_item_${item.id}`) Markup.button.callback(`${item.name} - ${item.price}`, `buy_item_${item.id}`)
]); ]);
buttons.push([Markup.button.callback('🔙 В меню', 'crime_menu')]);
// Отправляем сообщение с клавиатурой // Отправляем сообщение с клавиатурой
await ctx.reply(message, Markup.inlineKeyboard(buttons)); await ctx.editMessageText(message, Markup.inlineKeyboard(buttons));
}); });
rpg.action(/buy_item_(\d+)/, async (ctx) => { rpg.action(/buy_item_(\d+)/, async (ctx) => {
const itemId = parseInt(ctx.match[1], 10); const itemId = parseInt(ctx.match[1], 10);
const item = await ItemsModel.findByPk(itemId); 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) { if (!item) {
return ctx.reply('Предмет не найден.'); return ctx.reply('Предмет не найден.');
@ -1008,27 +1065,29 @@ rpg.action(/buy_item_(\d+)/, async (ctx) => {
} }
if (character.balance < item.price) { if (character.balance < item.price) {
return ctx.reply('У вас недостаточно средств для покупки этого предмета.'); return ctx.answerCbQuery('У вас недостаточно средств для покупки этого предмета.', {show_alert: true});
} }
// Снимаем деньги с баланса // Снимаем деньги с баланса
character.balance -= item.price; character.dirtymoney -= item.price;
// Добавляем предмет в инвентарь // Добавляем предмет в инвентарь
await InventoryModel.create({ await InventoryModel.create({
telegram_id: ctx.from.id, telegram_id: ctx.from.id,
name: item.name, name: item.name,
text_id: item.text_id,
description: item.description, description: item.description,
effectData: item.effectData, effectData: item.effectData,
price: item.price, price: item.price,
rarity: item.rarity, rarity: item.rarity,
type: item.type, type: item.type,
duration: item.duration, duration: item.duration,
equipped: false, canBeEquipped: item.canBeEquipped,
equipped: false
}); });
await character.save(); await character.save();
ctx.reply(`Вы успешно купили ${item.name} за ${item.price}₽!`); ctx.answerCbQuery(`Вы успешно купили ${item.name} за ${item.price}₽!`, {show_alert: true});
}); });
//////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////
@ -1111,6 +1170,12 @@ const processEffects = (character, effects, isEquipping) => {
messages.push(`Ваша выносливость увеличена на ${effect.amount}.`); messages.push(`Ваша выносливость увеличена на ${effect.amount}.`);
break; break;
case 'intelligence_boost':
character.intelligence -= Math.max(0, character.intelligence - effect.amount);
character.save()
messages.push(`Ваш интеллект уменьшена на ${effect.amount}.`);
break;
default: default:
messages.push('Неизвестный эффект при снятии.'); messages.push('Неизвестный эффект при снятии.');
} }
@ -1126,7 +1191,7 @@ const processEffects = (character, effects, isEquipping) => {
case 'damage_boost': case 'damage_boost':
character.force += effect.amount; character.force += effect.amount;
character.save() character.save()
messages.push(`Ваш урон увеличен на ${effect.amount}.`); messages.push(`Ваша сила увеличена на ${effect.amount}.`);
break; break;
case 'max_health_boost': case 'max_health_boost':
@ -1141,6 +1206,18 @@ const processEffects = (character, effects, isEquipping) => {
messages.push(`Ваша выносливость уменьшена на ${effect.amount}.`); messages.push(`Ваша выносливость уменьшена на ${effect.amount}.`);
break; 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;
default: default:
messages.push('Неизвестный эффект при снаряжении.'); messages.push('Неизвестный эффект при снаряжении.');
} }
@ -1248,7 +1325,7 @@ const reduceStealedCards = async () => {
await card.destroy(); // Удаляем карточки await card.destroy(); // Удаляем карточки
} }
console.log(`Карточки для персонажа ${character.id} успешно обновлены.`); console.log(`Карточки для персонажа ${character.name} успешно обновлены.`);
} }
} }
@ -1260,7 +1337,6 @@ const reduceStealedCards = async () => {
async function generateCard(userId, balance) { async function generateCard(userId, balance) {
console.log(balance)
if(!Number(balance)){ if(!Number(balance)){
balance = faker.finance.amount({dec: 0})*100 balance = faker.finance.amount({dec: 0})*100
console.log('Random balance: ' + balance) console.log('Random balance: ' + balance)

15
signs Normal file
View File

@ -0,0 +1,15 @@
Ð
💳
💼
📊
💰
📛
⏏️
🛡️
🎒
🎯
🚫
📜
〰️
🔤