Completed
Push — master ( 1bb7d4...9323b7 )
by Petr
02:58
created

UserService::giveStarterItems()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 16
rs 9.4286
cc 2
eloc 9
nc 2
nop 1
1
<?php
2
3
namespace Rottenwood\KingdomBundle\Service;
4
5
use Monolog\Logger;
6
use Rottenwood\KingdomBundle\Entity\Human;
7
use Rottenwood\KingdomBundle\Entity\Infrastructure\InventoryItemRepository;
8
use Rottenwood\KingdomBundle\Entity\Infrastructure\Item;
9
use Rottenwood\KingdomBundle\Entity\Infrastructure\ItemRepository;
10
use Rottenwood\KingdomBundle\Entity\Infrastructure\RoomRepository;
11
use Rottenwood\KingdomBundle\Entity\InventoryItem;
12
use Rottenwood\KingdomBundle\Entity\Room;
13
use Rottenwood\KingdomBundle\Entity\Infrastructure\User;
14
use Rottenwood\KingdomBundle\Entity\Infrastructure\UserRepository;
15
use Rottenwood\KingdomBundle\Exception\ItemNotFound;
16
use Rottenwood\KingdomBundle\Exception\NotEnoughItems;
17
use Rottenwood\KingdomBundle\Exception\RoomNotFound;
18
use Rottenwood\KingdomBundle\Redis\RedisClientInterface;
19
use Snc\RedisBundle\Client\Phpredis\Client;
20
use Symfony\Component\Finder\Finder;
21
use Symfony\Component\Finder\SplFileInfo;
22
use Symfony\Component\HttpKernel\KernelInterface;
23
24
class UserService
25
{
26
27
    /** @var KernelInterface */
28
    private $kernel;
29
    /** @var \Redis */
30
    private $redis;
31
    /** @var UserRepository */
32
    private $userRepository;
33
    /** @var InventoryItemRepository */
34
    private $inventoryItemRepository;
35
    /** @var Logger */
36
    private $logger;
37
    /** @var RoomRepository */
38
    private $roomRepository;
39
    /** @var ItemRepository */
40
    private $itemRepository;
41
42
    /**
43
     * @param KernelInterface         $kernel
44
     * @param Client                  $redis
45
     * @param Logger                  $logger
46
     * @param UserRepository          $userRepository
47
     * @param InventoryItemRepository $inventoryItemRepository
48
     * @param RoomRepository          $roomRepository
49
     * @param ItemRepository          $itemRepository
50
     */
51
    public function __construct(
52
        KernelInterface $kernel,
53
        Client $redis,
54
        Logger $logger,
55
        UserRepository $userRepository,
56
        InventoryItemRepository $inventoryItemRepository,
57
        RoomRepository $roomRepository,
58
        ItemRepository $itemRepository
59
    ) {
60
        $this->redis = $redis;
0 ignored issues
show
Documentation Bug introduced by
It seems like $redis of type object<Snc\RedisBundle\Client\Phpredis\Client> is incompatible with the declared type object<Redis> of property $redis.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
61
        $this->logger = $logger;
62
        $this->userRepository = $userRepository;
63
        $this->inventoryItemRepository = $inventoryItemRepository;
64
        $this->kernel = $kernel;
65
        $this->roomRepository = $roomRepository;
66
        $this->itemRepository = $itemRepository;
67
    }
68
69
    /**
70
     * Запрос ID всех онлайн игроков в комнате
71
     * @param Room  $room
72
     * @param array $excludePlayerIds
73
     * @return int[]
74
     */
75
    public function getOnlineUsersIdsInRoom(Room $room, $excludePlayerIds = [])
76
    {
77
        return array_map(
78
            function (User $user) {
79
                return $user->getId();
80
            },
81
            $this->getOnlineUsersInRoom($room, $excludePlayerIds)
82
        );
83
    }
84
85
    /**
86
     * Запрос всех онлайн игроков в комнате
87
     * @param Room      $room
88
     * @param int|array $excludePlayerIds
89
     * @return Human[]
90
     */
91
    public function getOnlineUsersInRoom(Room $room, $excludePlayerIds = [])
92
    {
93
        if (!is_array($excludePlayerIds)) {
94
            $excludePlayerIds = [$excludePlayerIds];
95
        }
96
97
        return $this->userRepository->findOnlineByRoom($room, $this->getOnlineUsersIds(), $excludePlayerIds);
98
    }
99
100
    /**
101
     * Запрос id всех игроков онлайн из redis
102
     * @return int[]
103
     */
104
    public function getOnlineUsersIds()
105
    {
106
        return $this->redis->smembers(RedisClientInterface::ONLINE_LIST);
107
    }
108
109
    /**
110
     * @param array $userIds
111
     * @return array
112
     */
113
    public function getSessionsByUserIds(array $userIds)
114
    {
115
        return array_values($this->redis->hmget(RedisClientInterface::ID_SESSION_HASH, $userIds));
116
    }
117
118
    /**
119
     * Передать предмет другому персонажу
120
     * @param User $userFrom
121
     * @param User $userTo
122
     * @param Item $item
123
     * @param int  $quantityToGive Сколько предметов передать
124
     * @return bool
125
     * @throws \Exception
126
     */
127
    public function giveItem(User $userFrom, User $userTo, Item $item, $quantityToGive = 1)
128
    {
129
        try {
130
            $this->dropItem($userFrom, $item, $quantityToGive);
131
        } catch (\Exception $exception) {
132
            if ($exception instanceof ItemNotFound || $exception instanceof NotEnoughItems) {
133
                return false;
134
            } else {
135
                throw $exception;
136
            }
137
        }
138
139
        $this->takeItem($userTo, $item, $quantityToGive);
140
141
        $this->logger->info(
142
            sprintf(
143
                '[%d]%s передал [%d]%s предмет: [%d]%s x %d шт.',
144
                $userFrom->getId(),
145
                $userFrom->getName(),
146
                $userTo->getId(),
147
                $userTo->getName(),
148
                $item->getId(),
149
                $item->getName(),
150
                $quantityToGive
151
            )
152
        );
153
154
        return true;
155
    }
156
157
    /**
158
     * Выбросить предмет
159
     * @param User $user
160
     * @param Item $item
161
     * @param int  $quantityToDrop Сколько предметов выбросить
162
     * @return int Количество оставшихся предметов
163
     * @throws ItemNotFound
164
     * @throws NotEnoughItems
165
     */
166
    public function dropItem(User $user, Item $item, $quantityToDrop)
167
    {
168
        $inventoryItem = $this->inventoryItemRepository->findOneByUserAndItemId($user, $item->getId());
169
170
        if (!$inventoryItem) {
171
            throw new ItemNotFound;
172
        }
173
174
        $itemQuantity = $inventoryItem->getQuantity();
175
        $itemQuantityAfterDrop = $itemQuantity - $quantityToDrop;
176
177
        if ($itemQuantityAfterDrop == 0) {
178
            $this->inventoryItemRepository->remove($inventoryItem);
179
        } elseif ($itemQuantityAfterDrop > 0) {
180
            $inventoryItem->setQuantity($itemQuantityAfterDrop);
181
        } else {
182
            throw new NotEnoughItems;
183
        }
184
185
        $this->inventoryItemRepository->flush($inventoryItem);
186
187
        $this->logger->info(
188
            sprintf(
189
                '[%d]%s выбросил предмет: [%d]%s x %d шт. (осталось %d)',
190
                $user->getId(),
191
                $user->getName(),
192
                $item->getId(),
193
                $item->getName(),
194
                $quantityToDrop,
195
                $itemQuantityAfterDrop
196
            )
197
        );
198
199
        return $itemQuantityAfterDrop;
200
    }
201
202
    /**
203
     * Взять предмет
204
     * @param User $user
205
     * @param Item $item
206
     * @param int  $quantityToTake Сколько предметов взять
207
     */
208
    public function takeItem(User $user, Item $item, $quantityToTake = 1)
209
    {
210
        $inventoryItem = $this->inventoryItemRepository->findOneByUserAndItemId($user, $item->getId());
211
212
        if ($inventoryItem) {
213
            $quantity = $inventoryItem->getQuantity() + $quantityToTake;
214
            $inventoryItem->setQuantity($quantity);
215
        } else {
216
            $inventoryItem = new InventoryItem($user, $item, $quantityToTake);
217
            $this->inventoryItemRepository->persist($inventoryItem);
218
        }
219
220
        $this->inventoryItemRepository->flush($inventoryItem);
221
222
        $this->logger->info(
223
            sprintf(
224
                '[%d]%s взял предмет: [%d]%s x %d шт. (всего %d)',
225
                $user->getId(),
226
                $user->getName(),
227
                $item->getId(),
228
                $item->getName(),
229
                $quantityToTake,
230
                isset($quantity) ? $quantity : $quantityToTake
231
            )
232
        );
233
    }
234
235
    /**
236
     * Установка рэндомного аватара
237
     * @return string
238
     */
239
    public function pickAvatar()
240
    {
241
        $finder = new Finder();
242
243
        $prefix = 'male';
244
        $avatarPath = $this->kernel->getRootDir() . '/../web/img/avatars/' . $prefix;
245
246
        $files = $finder->files()->in($avatarPath);
247
248
        $avatars = [];
249
        /** @var SplFileInfo $file */
250
        foreach ($files as $file) {
251
            $avatars[] = $file->getBasename('.jpg');
252
        }
253
254
        $avatar = $prefix . '/' . $avatars[array_rand($avatars)];
255
256
        return $avatar;
257
    }
258
259
    /**
260
     * Транслитерация и конвертация строки, удаление цифр
261
     * @param string $string
262
     * @return string
263
     */
264
    public function transliterate($string)
265
    {
266
        $englishLetters = implode('', array_keys($this->getAlphabet()));
267
        $cyrillicLetters = 'абвгдеёжзиклмнопрстуфхцчшщьыъэюяАБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ';
268
        $pattern = '[^' . preg_quote($englishLetters . $cyrillicLetters, '/') . ']';
269
270
        $stringWithoutSpecialChars = mb_ereg_replace($pattern, '', $string);
271
272
        $cyrillicString = mb_convert_case(
273
            strtr($stringWithoutSpecialChars, $this->getAlphabet()),
274
            MB_CASE_TITLE,
275
            'UTF-8'
276
        );
277
278
        return $cyrillicString;
279
    }
280
281
    /**
282
     * Массив соответствия русских букв латинским
283
     * @return string[]
284
     */
285
    private function getAlphabet()
286
    {
287
        return [
288
            'a' => 'а', 'b' => 'б', 'c' => 'ц', 'd' => 'д', 'e' => 'е',
289
            'f' => 'ф', 'g' => 'г', 'h' => 'х', 'i' => 'ай', 'j' => 'дж',
290
            'k' => 'к', 'l' => 'л', 'm' => 'м', 'n' => 'н', 'o' => 'о',
291
            'p' => 'п', 'q' => 'к', 'r' => 'р', 's' => 'с', 't' => 'т',
292
            'u' => 'у', 'v' => 'в', 'w' => 'в', 'x' => 'кс', 'y' => 'й',
293
            'z' => 'з', 'A' => 'А', 'B' => 'Б', 'C' => 'Ц', 'D' => 'Д',
294
            'E' => 'Е', 'F' => 'Ф', 'G' => 'Г', 'H' => 'Х', 'I' => 'Ай',
295
            'J' => 'Дж', 'K' => 'К', 'L' => 'Л', 'M' => 'М', 'N' => 'Н',
296
            'O' => 'О', 'P' => 'П', 'Q' => 'К', 'R' => 'Р', 'S' => 'С',
297
            'T' => 'Т', 'U' => 'Ю', 'V' => 'В', 'W' => 'В', 'X' => 'Кс',
298
            'Y' => 'Й', 'Z' => 'З',
299
        ];
300
    }
301
302
    /**
303
     * Стартовая комната при создании персонажа
304
     * @return Room
305
     * @throws RoomNotFound
306
     */
307
    public function getStartRoom()
308
    {
309
        $startRoom = $this->roomRepository->findOneByXandY(0, 0);
310
311
        if (!$startRoom) {
312
            throw new RoomNotFound();
313
        }
314
315
        return $startRoom;
316
    }
317
318
    /**
319
     * Стартовые предметы при создании персонажа
320
     * @param User $user
321
     */
322
    public function giveStarterItems(User $user)
323
    {
324
        $starterItemsIds = [
325
            'newbie-boots',
326
            'newbie-legs',
327
            'newbie-shirt',
328
            'tester-sword',
329
        ];
330
331
        $items = $this->itemRepository->findSeveralByIds($starterItemsIds);
332
333
        //TODO[Rottenwood]: Заменить на метод принимающий массив предметов
334
        foreach ($items as $item) {
335
            $this->takeItem($user, $item);
336
        }
337
    }
338
}
339