Описание функционала и возможностей классов EventActivationHandler и EventAction | DevBlog_11

События в игровом мире

Управление игровыми событиями в Онлайн-Игре

Классы 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());
            // Можете отправить сообщение без изображения или с другим изображением
        }
    }

}

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *