Управление игровыми событиями в Онлайн-Игре
Классы EventActivationHandler
и EventAction
играют ключевую роль в динамическом управлении событиями в нашей онлайн-игре. Они обеспечивают активацию, управление и оповещение игроков о текущих событиях в игровом мире. Давайте подробно рассмотрим их функционал и возможности.
1. Класс EventActivationHandler
Инициализация и Настройка
- Инициализируется с экземплярами моделей для работы с событиями и биомами.
- Настраивает Telegram API для отправки уведомлений игрокам.
- Часовой пояс установлен на ‘Europe/Kiev’ для соответствия локальному времени.
Метод process()
- Активация событий: Определяет доступные для активации события, исключая уже активные на текущей неделе.
- Фильтрация: Выбирает события с учётом их частоты проведения и последней активации.
- Уведомления: Отправляет информацию об активированных событиях всем игрокам через Telegram.
Управление Статусами
- Обновление статусов истекших событий, автоматически переводя их в состояние «завершено».
2. Класс EventAction
Взаимодействие с Игроками
- Обрабатывает нажатия на кнопку «События» в телеграм-боте, предоставляя игрокам актуальную информацию о текущих событиях.
Информирование об Событиях
- Отображение списка активных событий, их описаний, типов, влияния на персонажа и времени окончания.
- Динамическое сообщение об активных событиях или уведомление об их отсутствии.
Выводы
Классы EventActivationHandler
и EventAction
обеспечивают эффективное управление игровыми событиями, автоматизируя их активацию и информирование игроков. Это позволяет создавать динамичный и вовлекающий игровой процесс, адаптируя игру к текущему времени и предпочтениям игроков. Использование данных классов значительно повышает качество игрового опыта, делая каждый сеанс уникальным и непредсказуемым.
P.S. Код самих классов:
<?php namespace App\Controllers\Telegram\Commands\Actions; use Longman\TelegramBot\Request; use Longman\TelegramBot\Entities\ServerResponse; use App\Models\CharacterModel; use App\Models\ActiveEventModel; use App\Models\EventModel; use DateTime; class EventAction extends BaseAction { protected $characterModel; protected $activeEvents; protected $events; public function __construct($callbackQuery) { parent::__construct($callbackQuery); $this->characterModel = new CharacterModel(); $this->activeEvents = new ActiveEventModel(); $this->events = new EventModel(); } public function handle(): ServerResponse { [$user, $character] = $this->getUserAndCharacter(); if (!$user || !$character) { return Request::sendMessage([ 'chat_id' => $this->callbackQuery->getMessage()->getChat()->getId(), 'text' => 'Пользователь не найден в базе данных или персонаж не определён.', ]); } // Получаем активные события из таблицы `active_events` $activeEvents = $this->activeEvents->where('status', 'active')->findAll(); $text = "🌟 *На текущий момент в мире развернулись следующие события:* 🌟\n\n"; if (!empty($activeEvents)) { foreach ($activeEvents as $index => $event) { // Из таблицы `events` получаем детали события по ID $eventDetails = $this->events->find($event['event_id']); $endTime = new DateTime($event['end_time']); $now = new DateTime(); $interval = $now->diff($endTime); // Формирование строки события $text .= "№" . ($index + 1) . " *{$eventDetails['name']}*\n\n" . "📜 *Описание:* _{$eventDetails['description']}_\n\n" . "🌍 *Тип события:* _" . $this->translateEventType($eventDetails['event_type']) . "_\n\n" . "🚀 *Влияние на персонажа:* _" . $this->translateEffectType($eventDetails['effect_type']) . "_\n\n" . "⏳ *Событие закончится через:* _" . $interval->format('%H чс. %I мин.') . "_\n\n"; } } else { $text = "🎉 На текущий момент активных событий нет. Исследуйте мир и готовьтесь к новым приключениям! 🎉"; } $keyboard = $this->getKeyboard(); Request::answerCallbackQuery(['callback_query_id' => $this->callbackQuery->getId()]); return Request::sendMessage([ 'chat_id' => $this->callbackQuery->getMessage()->getChat()->getId(), 'text' => $text, 'parse_mode' => 'Markdown', 'reply_markup' => json_encode($keyboard), ]); } protected function translateEventType($type) { $translations = [ 'local' => 'Выборочно в указанных биомах', 'global' => 'Повсеместно в указанных биомах' ]; return $translations[$type] ?? $type; } protected function translateEffectType($type) { $translations = [ 'damage' => 'Урон', 'heal' => 'Лечение', 'buff' => 'Усиление', 'debuff' => 'Ослабление', 'none' => 'Без эффекта' ]; return $translations[$type] ?? $type; } protected function getKeyboard() { // Метод для получения клавиатуры, если нужно изменить структуру return [ 'inline_keyboard' => [ [ ['text' => '🎮 Развлечения', 'callback_data' => 'entertainment'], ['text' => '🧑🌾 Действия 🛠️', 'callback_data' => 'characterActions'], ['text' => '🎉 События', 'callback_data' => 'events'] ], [ ['text' => '🎒 Инвентарь', 'callback_data' => 'inventory'], ['text' => '🛒 Магазин', 'callback_data' => 'shop'], ] ] ]; } }
<?php namespace App\TaskHandlers; use App\Models\ActiveEventModel; use App\Models\EventModel; use App\Models\BiomeModel; use CodeIgniter\I18n\Time; use Longman\TelegramBot\Request; use Longman\TelegramBot\Telegram; use Longman\TelegramBot\Exception\TelegramException; use App\Models\TelegramUserModel; class EventActivationHandler { protected $activeEventModel; protected $eventModel; protected $biomeModel; protected $telegramUserModel; private $telegram; protected $startOfWeek; protected $endOfWeek; protected $now; public function __construct() { $this->activeEventModel = new ActiveEventModel(); $this->eventModel = new EventModel(); $this->biomeModel = new BiomeModel(); $this->telegramUserModel = new TelegramUserModel(); $API_KEY = getenv('telegram.API_KEY'); $BOT_USERNAME = getenv('telegram.BOT_USERNAME'); try { $this->telegram = new Telegram($API_KEY, $BOT_USERNAME); Request::initialize($this->telegram); } catch (TelegramException $e) { log_message('error', $e->getMessage()); } $this->now = Time::now('Europe/Kiev'); // День недели: 0 (воскресенье) - 6 (суббота) $dayOfWeek = $this->now->getDayOfWeek(); // CodeIgniter начинает неделю с воскресенья, нам нужно адаптировать логику, если ваша неделя начинается с понедельника $this->startOfWeek = $this->now->subDays($dayOfWeek)->setTime(0, 0, 0); $this->endOfWeek = $this->now->addDays(7 - $dayOfWeek - 1)->setTime(23, 59, 59); } public function process() { // Обновление статусов истекших событий $this->updateExpiredEventsStatus(); // Получаем доступные для активации события, исключая уже активированные на этой неделе $availableEvents = $this->eventModel->whereNotIn('event_id', function($builder) { return $builder->select('event_id') ->from('active_events') ->where('start_time >=', $this->startOfWeek) ->where('start_time <=', $this->endOfWeek); })->findAll(); // Проверяем время последней активации if (!$this->isRecentActivationTooClose()) { die(); } // Фильтрация событий с учетом частоты активации $filteredEvents = array_filter($availableEvents, function($event) { $activationCountThisWeek = $this->getActivationCountThisWeek($event['event_id'], $this->startOfWeek, $this->endOfWeek); return $activationCountThisWeek < $event['frequency_per_week']; }); // Выбор и активация случайного события if (!empty($filteredEvents)) { $randomEvent = $filteredEvents[array_rand($filteredEvents)]; $this->activateEvent($randomEvent); $this->notifyPlayersAboutEvent($randomEvent); } } protected function getActivationCountThisWeek($eventId, $startOfWeek, $endOfWeek) { return $this->activeEventModel ->where('event_id', $eventId) ->where('start_time >=', $startOfWeek) ->where('start_time <=', $endOfWeek) ->countAllResults(); } protected function updateExpiredEventsStatus() { // Получаем все активные события, у которых время окончания уже прошло $expiredEvents = $this->activeEventModel ->where('status', 'active') ->where('end_time <', $this->now) ->findAll(); foreach ($expiredEvents as $event) { // Обновляем статус события на 'completed' $this->activeEventModel->update($event['id'], ['status' => 'completed']); } } protected function getLastActivationDay($eventId) { // Ищем последнее активное событие с данным ID $lastActiveEvent = $this->activeEventModel ->where('event_id', $eventId) ->orderBy('created_at', 'DESC') ->first(); if ($lastActiveEvent) { // Если событие найдено, возвращаем день недели его активации $lastActivationTime = Time::createFromFormat('Y-m-d H:i:s', $lastActiveEvent['created_at'], 'Europe/Kiev'); return $lastActivationTime->getDayOfWeek(); } return 0; } protected function activateEvent($event) { $data = [ 'event_id' => $event['event_id'], 'start_time' => $this->now->toDateTimeString(), 'end_time' => $this->now->addMinutes($event['duration'])->toDateTimeString(), 'status' => 'active', 'effect_applied' => false, ]; $this->activeEventModel->insert($data); } protected function isRecentActivationTooClose() { // Получаем время последнего активированного события $lastActiveEvent = $this->activeEventModel ->select('start_time') ->orderBy('start_time', 'DESC') ->first(); if ($lastActiveEvent) { $lastActiveEventTime = Time::createFromFormat('Y-m-d H:i:s', $lastActiveEvent['start_time'], 'Europe/Kiev'); $timeDifference = $this->now->difference($lastActiveEventTime); // Возвращает true, если последнее событие было активировано более чем 45 минут назад return $timeDifference->getMinutes() > 45; } // Если нет активированных событий, то ограничение по времени не применяется return false; } protected function notifyPlayersAboutEvent($event) { $message = ''; $allBiomes = json_decode($event['biome_ids'], true); // Декодирование строки JSON в массив $biomsStr = ''; $imgPath = $event['img_path']; foreach ($allBiomes as $biom) { $biomsStr .= "🔹 {$this->biomeModel->where('id', $biom)->first()['name']}\n"; } // Форматирование продолжительности $durationFormatted = $this->formatDuration($event['duration']); // Перевод эффекта влияния $effectTypeTranslated = $this->translateEffectType($event['effect_type']); // Перевод типа территории $territoryTranslated = $this->translateTerritory($event['event_type']); $message .= "⚠️ *Внимание, началось событие: ➡️ {$event['name']}.*\n\n"; $message .= "ℹ️ *Описание:*\n"; $message .= "_{$event['description']}_\n\n"; $message .= "⌛️ Продлится : $durationFormatted\n\n"; $message .= "🔔 Эффект влияния: $effectTypeTranslated\n\n"; $message .= "🗾 Территория: $territoryTranslated\n\n"; $message .= $biomsStr; $message .= "\nПринимайте меры, если вы не готовы к таковому!"; $allTelegramUsers = $this->telegramUserModel->findAll(); $counter = 0; // Счетчик отправленных сообщений foreach ($allTelegramUsers as $telegramUser) { if ($counter > 0 && $counter % 50 == 0) { // После каждых 50 сообщений sleep(5); // Пауза на 5 секунд } $this->sendMessageToAllUsers($message, $imgPath, $telegramUser['telegram_id']); $counter++; } } protected function formatDuration($minutes) { $days = floor($minutes / 1440); $hours = floor(($minutes - $days * 1440) / 60); $mins = $minutes % 60; return "{$days} дн. {$hours} чс. {$mins} мин."; } protected function translateEffectType($type) { $translations = [ 'damage' => 'Урон', 'heal' => 'Лечение', 'buff' => 'Усиление', 'debuff' => 'Ослабление', 'none' => 'Без эффекта' ]; return $translations[$type] ?? $type; } protected function translateTerritory($territory) { $translations = [ 'local' => 'Выборочно в указанных биомах', 'global' => 'Повсеместно в указанных биомах' ]; return $translations[$territory] ?? $territory; } protected function sendMessageToAllUsers($message, $imgPath, $chatId) { $keyboard = [ 'inline_keyboard' => [ [ ['text' => '👨🎤 Персонаж', 'callback_data' => 'character'], ['text' => '🧑🌾 Действия 🛠️', 'callback_data' => 'characterActions'], ['text' => '🎉 События', 'callback_data' => 'events'] ] ] ]; Request::answerCallbackQuery(['callback_query_id' => $chatId]); try { $imagePath = base_url($imgPath); // Предполагается, что $imgPath - это локальный путь к файлу $result = Request::sendPhoto([ 'chat_id' => $chatId, 'photo' => Request::encodeFile($imagePath), 'caption' => $message, 'parse_mode' => 'Markdown', 'reply_markup' => json_encode($keyboard), ]); } catch (TelegramException $e) { log_message('error', "Ошибка при отправке фото: " . $e->getMessage()); // Можете отправить сообщение без изображения или с другим изображением } } }