AchievementsManager::setCriteriaProgress()   B
last analyzed

Complexity

Conditions 6
Paths 12

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 8.7537
c 0
b 0
f 0
cc 6
nc 12
nop 4
1
<?php
2
3
namespace Zurbaev\Achievements;
4
5
use Zurbaev\Achievements\Contracts\AchievementsStorageInterface;
6
use Zurbaev\Achievements\Contracts\CriteriaHandler;
7
use Zurbaev\Achievements\Contracts\CriteriaHandlersManager as CriteriaHandlersManagerContract;
8
9
class AchievementsManager
10
{
11
    /**
12
     * @var AchievementsStorageInterface
13
     */
14
    protected $storage;
15
16
    /**
17
     * Contains list of achievement IDs that
18
     * should be checked for completeness
19
     * in current criterias update.
20
     *
21
     * @var array
22
     */
23
    protected $achievementsToCheck = [];
24
25
    /**
26
     * Criteria handlers manager.
27
     *
28
     * @var CriteriaHandlersManagerContract
29
     */
30
    protected $handlers;
31
32
    /**
33
     * AchievementsManager constructor.
34
     *
35
     * @param AchievementsStorageInterface    $storage
36
     * @param CriteriaHandlersManagerContract $handlers
37
     */
38
    public function __construct(AchievementsStorageInterface $storage, CriteriaHandlersManagerContract $handlers)
39
    {
40
        $this->storage = $storage;
41
        $this->handlers = $handlers;
42
    }
43
44
    /**
45
     * Updates criterias progress by given type & checks
46
     * referred achievements for completeness state.
47
     *
48
     * Returns number of updated criterias.
49
     *
50
     * @param mixed  $owner
51
     * @param string $type
52
     * @param mixed  $data  = null
53
     *
54
     * @return int
55
     */
56
    public function updateAchievementCriterias($owner, string $type, $data = null): int
57
    {
58
        $criterias = $this->storage->getOwnerCriteriasByType($owner, $type, $data);
59
60
        if (!count($criterias)) {
61
            return 0;
62
        }
63
64
        $this->achievementsToCheck = [];
65
66
        $achievements = $this->storage->getAchievementsByCriterias($criterias);
67
        $updatedCriteriasCount = 0;
68
69
        foreach ($criterias as $criteria) {
70
            /** @var AchievementCriteria $criteria*/
71
72
            if ($criteria->completed()) {
73
                continue;
74
            }
75
76
            $achievement = $this->storage->getAchievementForCriteria($criteria, $achievements);
77
78
            if (is_null($achievement)) {
79
                continue;
80
            }
81
82
            $change = $this->getCriteriaChange($owner, $criteria, $achievement, $data);
83
84
            if (is_null($change)) {
85
                continue;
86
            }
87
88
            $this->setCriteriaProgress($owner, $criteria, $achievement, $change);
89
            $updatedCriteriasCount++;
90
        }
91
92
        if (count($this->achievementsToCheck) > 0) {
93
            $this->checkCompletedAchievements($owner);
94
        }
95
96
        return $updatedCriteriasCount;
97
    }
98
99
    /**
100
     * Requests new progress value for given owner & criteria.
101
     *
102
     * @param mixed               $owner
103
     * @param AchievementCriteria $criteria
104
     * @param Achievement         $achievement
105
     * @param mixed               $data        = null
106
     *
107
     * @return AchievementCriteriaChange|null
108
     */
109
    public function getCriteriaChange($owner, AchievementCriteria $criteria, Achievement $achievement, $data = null)
110
    {
111
        $handler = $this->handlers->getHandlerFor($criteria->type());
112
113
        if (!$handler instanceof CriteriaHandler) {
114
            return null;
115
        }
116
117
        return $handler->handle($owner, $criteria, $achievement, $data);
118
    }
119
120
    /**
121
     * Updates criteria progress & saves achievement for completeness check (if eligible for).
122
     *
123
     * @param mixed                     $owner
124
     * @param AchievementCriteria       $criteria
125
     * @param Achievement               $achievement
126
     * @param AchievementCriteriaChange $change
127
     *
128
     * @return bool
129
     */
130
    protected function setCriteriaProgress($owner, AchievementCriteria $criteria, Achievement $achievement, AchievementCriteriaChange $change)
131
    {
132
        $maxValue = $criteria->maxValue();
133
        $changeValue = $change->value;
134
        $oldValue = null;
135
        $newValue = $changeValue;
136
        $progress = new AchievementCriteriaProgress(0, false);
137
138
        if ($maxValue > 0 && $changeValue > $maxValue) {
139
            $changeValue = $maxValue;
140
        }
141
142
        if ($criteria->hasProgress()) {
143
            $progress = $criteria->progress();
144
            $oldValue = $progress->value;
145
            $newValue = $progress->getNewValue($maxValue, $changeValue, $change->progressType);
146
        }
147
148
        if ($oldValue === $newValue) {
149
            return false;
150
        }
151
152
        $progress->value = $newValue;
153
        $progress->changed = true;
154
        $progress->data = $change->progressData;
155
156
        $this->storage->setCriteriaProgressUpdated($owner, $criteria, $achievement, $progress);
157
158
        if ($this->isCompletedCriteria($criteria, $progress)) {
159
            $this->completedCriteriaFor($achievement);
160
        }
161
162
        return true;
163
    }
164
165
    /**
166
     * Saves achievement to completeness check list.
167
     *
168
     * @param Achievement $achievement
169
     */
170
    protected function completedCriteriaFor(Achievement $achievement)
171
    {
172
        if (!in_array($achievement->id(), $this->achievementsToCheck)) {
173
            $this->achievementsToCheck[] = $achievement->id();
174
        }
175
    }
176
177
    /**
178
     * Checks all saved achievements for completeness state.
179
     *
180
     * Returns number of completed achievements.
181
     *
182
     * @param mixed $owner
183
     *
184
     * @return int
185
     */
186
    protected function checkCompletedAchievements($owner): int
187
    {
188
        $achievements = $this->storage->getAchievementsWithProgressFor($owner, $this->achievementsToCheck);
189
        $this->achievementsToCheck = [];
190
191
        if (!$achievements || !count($achievements)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $achievements of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
192
            return 0;
193
        }
194
195
        $completedAchievements = array_filter($achievements, function (Achievement $achievement) {
196
            return !$achievement->completed() && $this->isCompletedAchievement($achievement);
197
        });
198
199
        if (count($completedAchievements) > 0) {
200
            $this->storage->setAchievementsCompleted($owner, $completedAchievements);
201
        }
202
203
        return count($completedAchievements);
204
    }
205
206
    /**
207
     * Determines if given criteria was completed.
208
     *
209
     * @param AchievementCriteria         $criteria
210
     * @param AchievementCriteriaProgress $progress
211
     *
212
     * @return bool
213
     */
214
    protected function isCompletedCriteria(AchievementCriteria $criteria, AchievementCriteriaProgress $progress): bool
215
    {
216
        $progress->completed = $progress->value >= $criteria->maxValue();
217
218
        return $progress->completed;
219
    }
220
221
    /**
222
     * Determines if given achievement was completed.
223
     *
224
     * @param Achievement $achievement
225
     *
226
     * @return bool
227
     */
228
    protected function isCompletedAchievement(Achievement $achievement): bool
229
    {
230
        $completedCriterias = array_filter($achievement->criterias(), function (AchievementCriteria $criteria) {
231
            return $this->isCompletedCriteria($criteria, $criteria->progress());
232
        });
233
234
        return count($achievement->criterias()) === count($completedCriterias);
235
    }
236
}
237