#
# TODO should update the achievement?
fun add_achievement(achievement: Achievement) do
- var key = self.key / achievement.key
- if game.store.has_key(key) then return
stats.inc("achievements")
- achievement.save_in(self)
- save
+ achievement.owner = self
+ achievement.save
end
+ # Is `a` unlocked for this `Player`?
+ fun has_achievement(a: Achievement): Bool do return load_achievement(a.id) != null
+
# Load the event from its `id`.
#
# Looks for the event save file in game data.
# Returns `null` if the event cannot be found.
fun load_achievement(id: String): nullable Achievement do
- var key = self.key / "achievements" / id
- if not game.store.has_key(key) then return null
- var json = game.store.load_object(key)
- return new Achievement.from_json(game, json)
+ var req = new JsonObject
+ req["id"] = id
+ req["game"] = game.key
+ req["owner"] = key
+ var obj = game.db.collection("achievements").find(req)
+ if obj isa JsonObject then
+ return new Achievement.from_json(game, obj)
+ end
+ return null
end
# List all events registered in this entity.
#
# To add events see `add_event`.
fun load_achievements: MapRead[String, Achievement] do
+ var req = new JsonObject
+ req["game"] = game.key
+ req["owner"] = key
var res = new HashMap[String, Achievement]
- var key = self.key / "achievements"
- if not game.store.has_collection(key) then return res
- var coll = game.store.list_collection(key)
- for id in coll do
- res[id.to_s] = load_achievement(id.to_s).as(not null)
+ for obj in game.db.collection("achievements").find_all(req) do
+ var achievement = new Achievement.from_json(game, obj)
+ res[achievement.id] = achievement
end
return res
end
class Achievement
super GameEntity
- redef var key is lazy do return "achievements" / id
+ redef var collection_name = "achievements"
redef var game
+ redef fun key do
+ var owner = self.owner
+ if owner == null then return id
+ return "{owner.key}-{id}"
+ end
+
# Uniq ID for this achievement.
var id: String
# Is this achievement unlocked by somebody?
var is_unlocked: Bool is lazy do return not load_events.is_empty
+ # Game entity this achievement is about.
+ var owner: nullable GameEntity = null
+
# Init `self` from a `json` object.
#
# Used to load achievements from storage.
init from_json(game: Game, json: JsonObject) do
- self.game = game
- id = json["id"].to_s
- name = json["name"].to_s
- desc = json["desc"].to_s
- reward = json["reward"].as(Int)
+ init(game,
+ json["id"].as(String),
+ json["name"].as(String),
+ json["desc"].as(String),
+ json["reward"].as(Int))
end
- redef fun to_json do
+ redef fun to_json_object do
var json = super
json["id"] = id
json["name"] = name
json["desc"] = desc
json["reward"] = reward
+ json["game"] = game.key
+ var owner = self.owner
+ if owner != null then json["owner"] = owner.key
return json
end
end
redef class Player
-
- # Is `a` unlocked for this `Player`?
- fun has_achievement(a: Achievement): Bool do
- return load_achievement(a.id) != null
- end
-
# Unlocks an achievement for this Player based on a GithubEvent.
#
# Register the achievement and adds the achievement reward to the player
nitcoins += a.reward
add_achievement(a)
trigger_unlock_event(a, event)
+ save
end
# Create a new event that marks the achievement unlocking.
obj["player"] = name
obj["reward"] = achievement.reward
obj["achievement"] = achievement.id
- obj["github_event"] = event.json
+ obj["github_event"] = event
var ge = new GameEvent(game, "achievement_unlocked", obj)
add_event(ge)
game.add_event(ge)
if not event.action == "closed" then return
if not event.pull.merged then return
var player = event.pull.user.player(game)
- if player.stats["commits"] == threshold then
+ if player.stats["commits"] >= threshold then
var a = new_achievement(game)
player.unlock_achievement(a, event)
end
redef var reward = 10000
redef var threshold = 10000
end
+
+#####################
+### Issue Comments
+#####################
+
+# Unlock achievement after X issue comments.
+#
+# Used to factorize behavior.
+abstract class PlayerXComments
+ super AchievementReactor
+
+ # Number of comments required to unlock the achievement.
+ var threshold: Int is noinit
+
+ redef fun react_event(game, event) do
+ if not event isa IssueCommentEvent then return
+ if not event.action == "created" then return
+ var player = event.comment.user.player(game)
+ if player.stats["comments"] == threshold then
+ var a = new_achievement(game)
+ player.unlock_achievement(a, event)
+ end
+ end
+end
+
+# Player author his first comment in issues.
+class Player1Comment
+ super PlayerXComments
+
+ redef var id = "player_1_comment"
+ redef var name = "From lurker to member"
+ redef var desc = "Comment on an issue."
+ redef var reward = 10
+ redef var threshold = 1
+end
+
+# Player author 100 issue comments.
+class Player100Comments
+ super PlayerXComments
+
+ redef var id = "player_100_comments"
+ redef var name = "Chatter"
+ redef var desc = "Comment 100 times on issues."
+ redef var reward = 100
+ redef var threshold = 100
+end
+
+# Player author 1000 issue comments.
+class Player1KComments
+ super PlayerXComments
+
+ redef var id = "player_1000_comments"
+ redef var name = "You sir, talk a lot!"
+ redef var desc = "Comment 1000 times on issues."
+ redef var reward = 1000
+ redef var threshold = 1000
+end
+
+# Ping @privat in a comment.
+class PlayerPingGod
+ super AchievementReactor
+
+ redef var id = "player_ping_god"
+ redef var name = "Ping god"
+ redef var desc = "Ping the owner of the repo for the first time."
+ redef var reward = 50
+
+ redef fun react_event(game, event) do
+ if not event isa IssueCommentEvent then return
+ var owner = game.repo.owner.login
+ if event.comment.body.has("@{owner}".to_re) then
+ var player = event.comment.user.player(game)
+ var a = new_achievement(game)
+ player.unlock_achievement(a, event)
+ end
+ end
+end
+
+# Give your first +1
+class PlayerFirstReview
+ super AchievementReactor
+
+ redef var id = "player_first_review"
+ redef var name = "First +1"
+ redef var desc = "Give a +1 for the first time."
+ redef var reward = 10
+
+ redef fun react_event(game, event) do
+ if not event isa IssueCommentEvent then return
+ # FIXME use a more precise way to locate reviews
+ if event.comment.is_ack then
+ var player = event.comment.user.player(game)
+ var a = new_achievement(game)
+ player.unlock_achievement(a, event)
+ end
+ end
+end
+
+# Talk about nitcoin in issue comments.
+class PlayerSaysNitcoin
+ super AchievementReactor
+
+ redef var id = "player_says_nitcoin"
+ redef var name = "Talking about money"
+ redef var desc = "Say something about nitcoins in a comment."
+ redef var reward = 10
+
+ redef fun react_event(game, event) do
+ if not event isa IssueCommentEvent then return
+ if event.comment.body.has("(n|N)itcoin".to_re) then
+ var player = event.comment.user.player(game)
+ var a = new_achievement(game)
+ player.unlock_achievement(a, event)
+ end
+ end
+end