Got repeatables working flawlessly and improved the achievement popup by a great deal

This commit is contained in:
Ruben Vereecken 2014-05-31 23:19:55 +02:00
parent bb57756cd9
commit 8d2c62ba84
11 changed files with 80 additions and 39 deletions

View file

@ -273,7 +273,7 @@ class CocoModel extends Backbone.Model
achievements = new NewAchievementCollection achievements = new NewAchievementCollection
achievements.fetch( achievements.fetch(
success: (collection) -> success: (collection) ->
Backbone.Mediator.publish('achievements:new', collection) unless _.isEmpty(collection.models) me.fetch (success: -> Backbone.Mediator.publish('achievements:new', collection)) unless _.isEmpty(collection.models)
) )

View file

@ -78,13 +78,17 @@ module.exports = class User extends CocoModel
a = 5 a = 5
b = 40 b = 40
# y = a * ln(1/b * (x + b)) # y = a * ln(1/b * (x + b)) + 1
@levelFromExp: (xp) -> @levelFromExp: (xp) ->
if xp > 0 then Math.floor(a * Math.log((1/b) * (xp + b))) else 0 if xp > 0 then Math.floor(a * Math.log((1/b) * (xp + b))) + 1 else 1
# x = (e^(y/a) - 1) * b # x = (e^((y-1)/a) - 1) * b
@expForLevel: (level) -> @expForLevel: (level) ->
Math.ceil((Math.exp(level / a) - 1) * b) Math.ceil((Math.exp((level - 1)/ a) - 1) * b)
level: -> level: ->
User.levelFromExp(@get('points')) User.levelFromExp(@get('points'))
levelFromExp: (xp) -> User.levelFromExp(xp)
expForLevel: (level) -> User.expForLevel(level)

View file

@ -25,6 +25,7 @@ MongoFindQuerySchema =
oneOf: [ oneOf: [
#{ $ref: '#/definitions/' + MongoQueryOperatorSchema.id}, #{ $ref: '#/definitions/' + MongoQueryOperatorSchema.id},
{ type: 'string' } { type: 'string' }
{ type: 'object' }
] ]
additionalProperties: true # TODO make Treema accept new pattern matched keys additionalProperties: true # TODO make Treema accept new pattern matched keys
definitions: {} definitions: {}
@ -49,6 +50,24 @@ _.extend(AchievementSchema.properties,
proportionalTo: proportionalTo:
type: 'string' type: 'string'
description: 'For repeatables only. Denotes the field a repeatable achievement needs for its calculations' description: 'For repeatables only. Denotes the field a repeatable achievement needs for its calculations'
function:
type: 'object'
oneOf: [
linear:
type: 'object'
properties:
a: {type: 'number', default: 1},
required: ['a']
description: 'f(x) = a * x'
logarithmic:
type:'object'
properties:
a: {type: 'number', default: 1}
b: {type: 'number', default: 1}
required: ['a', 'b']
description: 'f(x) = a * ln(1/b * (x + b))'
]
default: linear: a: 1
) )
AchievementSchema.definitions = {} AchievementSchema.definitions = {}

View file

@ -1,10 +1,12 @@
.notifyjs-achievement-base .notifyjs-achievement-base
background: url("/images/pages/base/notify_mockup.png") //background: url("/images/pages/base/notify_mockup.png")
background-image: url("/images/pages/base/modal_background.png")
background-size: 100% 100% background-size: 100% 100%
width: 500px width: 500px
height: 200px height: 200px
padding: 35px 20px 15px 15px padding: 35px 35px 15px 15px
text-align: center text-align: center
cursor: auto
.achievement-body .achievement-body
.achievement-image .achievement-image
@ -13,7 +15,10 @@
width: 100px width: 100px
height: 100px height: 100px
border-radius: 50% border-radius: 50%
margin: 30px 40px 20px 30px margin: 20px 30px 20px 30px
-webkit-box-shadow: 0px 0px 36px 0px white
-moz-box-shadow: 0px 0px 36px 0px white
box-shadow: 0px 0px 36px 0px white
.achievement-title .achievement-title
font-family: Bangers font-family: Bangers
@ -23,13 +28,14 @@
margin-top: 10px margin-top: 10px
font-size: 16px font-size: 16px
.achievement-message
&:empty
display: none
.achievement-progress .achievement-progress
padding: 25px 0px 0px 0px padding: 15px 0px 0px 0px
.achievement-message
font-family: Bangers
font-size: 18px
&:empty
display: none
.progress-wrapper .progress-wrapper
.progress-bar-wrapper .progress-bar-wrapper
@ -38,4 +44,4 @@
padding-left: 5px padding-left: 5px
font-family: Bangers font-family: Bangers
font-size: 16px font-size: 16px
float: right float: right

View file

@ -48,6 +48,7 @@ module.exports = class AchievementEditView extends View
@treema = @$el.find('#achievement-treema').treema(options) @treema = @$el.find('#achievement-treema').treema(options)
@treema.build() @treema.build()
console.log @treema
pushChangesToPreview: => pushChangesToPreview: =>
'TODO' # TODO might want some intrinsic preview thing 'TODO' # TODO might want some intrinsic preview thing
@ -73,4 +74,4 @@ module.exports = class AchievementEditView extends View
res.success => res.success =>
url = "/editor/achievement/#{@achievement.get('slug') or @achievement.id}" url = "/editor/achievement/#{@achievement.get('slug') or @achievement.id}"
document.location.href = url document.location.href = url

View file

@ -8,11 +8,18 @@ module.exports = class AchievementSearchView extends SearchView
tableTemplate: require 'templates/editor/achievement/table' tableTemplate: require 'templates/editor/achievement/table'
projection: ['name', 'description', 'collection', 'slug'] projection: ['name', 'description', 'collection', 'slug']
initialize: ->
console.log me.isAdmin()
unless me.isAdmin()
NotFoundView = require '../../not_found'
return new NotFoundView
else super()
getRenderData: -> getRenderData: ->
context = super() context = super()
context.currentEditor = 'editor.achievement_title' context.currentEditor = 'editor.achievement_title'
context.currentNew = 'editor.new_achievement_title' context.currentNew = 'editor.new_achievement_title'
context.currentNewSignup = 'editor.new_achievement_title_signup' context.currentNewSignup = 'editor.new_achievement_title_login'
context.currentSearch = 'editor.achievement_search_title' context.currentSearch = 'editor.achievement_search_title'
@$el.i18n() @$el.i18n()
context context

View file

@ -39,17 +39,24 @@ module.exports = class RootView extends CocoView
totalExpNeeded = nextLevelExp - currentLevelExp totalExpNeeded = nextLevelExp - currentLevelExp
currentExp = me.get('points') currentExp = me.get('points')
worth = achievement.get('worth') worth = achievement.get('worth')
alreadyAchievedPercentage = 100 * (currentExp - currentLevelExp - achievement.get('worth')) / totalExpNeeded leveledUp = currentExp - worth < currentLevelExp
newlyAchievedPercentage = 100 * achievement.get('worth') / totalExpNeeded alreadyAchievedPercentage = 100 * (currentExp - currentLevelExp - worth) / totalExpNeeded
newlyAchievedPercentage = if currentLevelExp is currentExp then 0 else 100 * worth / totalExpNeeded
console.debug "Current level is #{currentLevel} (#{currentLevelExp} xp), next level is #{nextLevel} (#{nextLevelExp} xp)." console.debug "Current level is #{currentLevel} (#{currentLevelExp} xp), next level is #{nextLevel} (#{nextLevelExp} xp)."
console.debug "Need a total of #{nextLevelExp - currentLevelExp}, already had #{currentExp - currentLevelExp - worth} and just now earned #{worth}" console.debug "Need a total of #{nextLevelExp - currentLevelExp}, already had #{currentExp - currentLevelExp - worth} and just now earned #{worth} totalling on #{currentExp}"
alreadyAchievedBar = $("<div class='progress-bar progress-bar-warning' style='width:#{alreadyAchievedPercentage}%'></div>") alreadyAchievedBar = $("<div class='progress-bar progress-bar-warning' style='width:#{alreadyAchievedPercentage}%'></div>")
newlyAchievedBar = $("<div class='progress-bar progress-bar-success' style='width:#{newlyAchievedPercentage}%'></div>") newlyAchievedBar = $("<div data-toggle='tooltip' class='progress-bar progress-bar-success' style='width:#{newlyAchievedPercentage}%'></div>")
progressBar = $('<div class="progress"></div>').append(alreadyAchievedBar).append(newlyAchievedBar) emptyBar = $("<div data-toggle='tooltip' class='progress-bar progress-bar-white' style='width:#{100 - newlyAchievedPercentage - alreadyAchievedPercentage}%'></div>")
message = "Reached level #{currentLevel}!" if currentExp - worth < currentLevelExp progressBar = $('<div class="progress" data-toggle="tooltip"></div>').append(alreadyAchievedBar).append(newlyAchievedBar).append(emptyBar)
message = if (currentLevel isnt 1) and leveledUp then "Reached level #{currentLevel}!" else null
alreadyAchievedBar.tooltip(title: "#{currentExp} XP in total")
newlyAchievedBar.tooltip(title: "#{worth} XP earned")
emptyBar.tooltip(title: "#{nextLevelExp - currentExp} XP until level #{nextLevel}")
# TODO a default should be linked here
imageURL = '/file/' + achievement.get('icon') imageURL = '/file/' + achievement.get('icon')
data = data =
title: achievement.get('name') title: achievement.get('name')
@ -60,11 +67,11 @@ module.exports = class RootView extends CocoView
message: message message: message
options = options =
autoHideDelay: 15000 autoHideDelay: 10000
globalPosition: 'bottom right' globalPosition: 'bottom right'
showDuration: 400 showDuration: 400
style: 'achievement' style: 'achievement'
autoHide: false autoHide: true
clickToHide: true clickToHide: true
$.notify( data, options ) $.notify( data, options )

View file

@ -29,7 +29,7 @@ AchievementSchema.pre('save', (next) ->
next() next()
) )
AchievementSchema.plugin(plugins.SearchablePlugin, {searchable: ['name']})
AchievementSchema.plugin(plugins.NamedPlugin) AchievementSchema.plugin(plugins.NamedPlugin)
AchievementSchema.plugin(plugins.SearchablePlugin, {searchable: ['name']})
module.exports = Achievement = mongoose.model('Achievement', AchievementSchema) module.exports = Achievement = mongoose.model('Achievement', AchievementSchema)

View file

@ -159,7 +159,7 @@ module.exports = class Handler
return @sendNotFoundError(res) return @sendNotFoundError(res)
term = req.query.term term = req.query.term
matchedObjects = [] matchedObjects = []
filters = [{filter: {index: true}}] filters = if @modelClass.schema.uses_coco_versions or @modelClass.schema.uses_coco_permissions then [filter: {index: true}] else [filter: {}]
if @modelClass.schema.uses_coco_permissions and req.user if @modelClass.schema.uses_coco_permissions and req.user
filters.push {filter: {index: req.user.get('id')}} filters.push {filter: {index: req.user.get('id')}}
projection = null projection = null

View file

@ -15,7 +15,6 @@ loadAchievements = ->
category = achievement.get 'collection' category = achievement.get 'collection'
achievements[category] = [] unless category of achievements achievements[category] = [] unless category of achievements
achievements[category].push achievement achievements[category].push achievement
loadAchievements() loadAchievements()
module.exports = AchievablePlugin = (schema, options) -> module.exports = AchievablePlugin = (schema, options) ->
@ -62,11 +61,11 @@ module.exports = AchievablePlugin = (schema, options) ->
console.error err if err? console.error err if err?
) )
if isRepeatable # TODO test more thoroughly if isRepeatable
console.log 'Upserting repeatable achievement called \'' + (achievement.get 'name') + '\' for ' + userID console.log 'Upserting repeatable achievement called \'' + (achievement.get 'name') + '\' for ' + userID
proportionalTo = achievement.get 'proportionalTo' proportionalTo = achievement.get 'proportionalTo'
originalAmount = util.getByPath(originalDocObj, proportionalTo) or 0 originalAmount = util.getByPath(originalDocObj, proportionalTo) or 0
newAmount = docObj.get proportionalTo newAmount = docObj[proportionalTo]
if originalAmount isnt newAmount if originalAmount isnt newAmount
earned.notified = false earned.notified = false
@ -87,10 +86,5 @@ module.exports = AchievablePlugin = (schema, options) ->
earnedPoints = achievement.get('worth') earnedPoints = achievement.get('worth')
wrapUp() wrapUp()
delete before[doc.id] unless isNew # This assumes everything we patch has a _id delete before[doc.id] unless isNew # This assumes everything we patch has a _id
return return

View file

@ -127,3 +127,6 @@ UserSchema.statics.hashPassword = (password) ->
shasum.digest('hex') shasum.digest('hex')
module.exports = User = mongoose.model('User', UserSchema) module.exports = User = mongoose.model('User', UserSchema)
AchievablePlugin = require '../plugins/achievements'
UserSchema.plugin(AchievablePlugin)