gstt / laravel-achievements

Achievements for Laravel 5.3+
MIT License
296 stars 37 forks source link

Achievement Chain #30

Open gabs-simon opened 6 years ago

gabs-simon commented 6 years ago

Sometimes, there are multiple achievements that track the exact same thing, but with different points to obtain.

For example, an user can obtain a "Newcomer" achievement when creating their first comment on the site, a "Regular Commenter" achievement when they reached 10 comments and a "Regular Commenter" achievement when they reached 100 comments.

Currently, these achievements need to be tracked separately, something like this:

<?php
use App\Achievements\Newcomer;
use App\Achievements\RegularCommenter;
use App\Achievements\VeteranCommenter;

public function createComment($user, $comment){
     // Some logic to be processed when the user creates a comment...
    $user->addProgress(Newcomer, 1);
    $user->addProgress(RegularCommenter, 1);
    $user->addProgress(VeteranCommenter, 1);
}

And this would quickly grow out of hand when there are many achievements tracking the same thing. In order to solve this problem, I propose a feature called "Achievement Chain". It would work in the following way:

1) A new abstract class, AchievementChain will be created. This class will be defined only by a list of achievements. 2) All methods that can be used to add, remove, set reset progress to Achievements can also receive instances of AchievementChain. When one of this methods receive an instance of AchievementChain, it will call the same method for every Achievement described in the definition of the AchievementChain instance.

swirlingskirts commented 6 years ago

YES! I came here looking for exactly this functionality. I have some workarounds going in the meanwhile, but I think this is a grand solution.

Another potential thing to look at is whether an achievement is "replaceable" - that is, if an achievement higher in the chain has been unlocked, the lower ones stop displaying. Thereby "transforming" my "newcomer" achievement into a "regular commenter" achievement.

gabs-simon commented 6 years ago

In the way you put it, "Replaceable" seems like a good idea. I agree that sometimes it's necessary to retrieve the highest achievement unlocked in the chain. Maybe the AchievementChain could return an instance of AchievementProgress of the higher achievement unlocked in the chain? I'm thinking of something like:

<?php
use App\Achievements\Newcomer; // Create 1 comment
use App\Achievements\RegularCommenter; // Create 10 comments
use App\Achievements\VeteranCommenter; // Create 100 comments

class CommentAchievements implements AchievementChain{
    public function chain(){
        return [Newcomer::class, RegularCommenter::class, VeteranCommenter::class];
    }
}
// $user1 has unlocked RegularCommenter
// $user2 has unlocked VeteranCommenter
$user1->highestOnAchievementChain(CommentAchievements); // returns instance of AchievementProgress for RegularCommenter
$user2->highestOnAchievementChain(CommentAchievements); // returns instance of AchievementProgress for VeteranCommenter
swirlingskirts commented 6 years ago

Aha, now that I understand the philosophy the package was designed on, your method of returning the highest unlocked in the chain is indeed a better method of implementing, and would work exactly as needed for this situation.

It does bring up the need for a method like getAchievementsOrHighest() or something like that - for when you want to get all the achievements someone has earned, but only the highest if they're in a chain?

gabs-simon commented 6 years ago

The feature for AchievementChain has been pushed to the master branch. It's still on development since there is no release version for it.

JoNMii commented 3 years ago

The feature for AchievementChain has been pushed to the master branch. It's still on development since there is no release version for it.

Is the command "php artisan achievements:load" working with chains for you when it process the chain?