mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-27 17:45:40 -05:00
Added a bunch of achievements to the script. Restyled big parts.
This commit is contained in:
parent
871149b2bc
commit
26085f9f3e
15 changed files with 243 additions and 80 deletions
|
@ -51,7 +51,7 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
'editor': go('editor/MainEditorView')
|
||||
|
||||
'editor/achievement': go('editor/achievement/AchievementSearchView')
|
||||
'editor/achievement': go('editor/achievement/AchievementEditView')
|
||||
'editor/achievement/:articleID': go('editor/achievement/AchievementEditView')
|
||||
'editor/article': go('editor/article/ArticleSearchView')
|
||||
'editor/article/preview': go('editor/article/ArticlePreviewView')
|
||||
'editor/article/:articleID': go('editor/article/ArticleEditView')
|
||||
|
|
|
@ -37,7 +37,7 @@ module.exports = class LevelSession extends CocoModel
|
|||
return true if c1[thang][spell] isnt c2[thang]?[spell]
|
||||
false
|
||||
|
||||
isMultiPlayer: ->
|
||||
isMultiplayer: ->
|
||||
console.log @get 'levelName'
|
||||
console.log @
|
||||
console.log @get 'team'
|
||||
|
|
|
@ -56,6 +56,9 @@ _.extend AchievementSchema.properties,
|
|||
proportionalTo:
|
||||
type: 'string'
|
||||
description: 'For repeatables only. Denotes the field a repeatable achievement needs for its calculations'
|
||||
recalculable:
|
||||
type: 'boolean'
|
||||
description: 'Needs to be set to true before it is elligible for recalculation.'
|
||||
function:
|
||||
type: 'object'
|
||||
description: 'Function that gives total experience for X amount achieved'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.locked
|
||||
filter: grayscale(100%)
|
||||
-moz-filter: grayscale(100%)
|
||||
-webkit-filter: grayscale(100%)
|
||||
filter: desaturate(gray, 50)
|
||||
-moz-filter: desaturate(gray, 50)
|
||||
-webkit-filter: desaturate(gray, 50)
|
||||
|
||||
.achievement-body
|
||||
.achievement-icon
|
||||
|
@ -37,8 +37,10 @@
|
|||
max-height: 2.6em
|
||||
margin-top: auto
|
||||
margin-bottom: 0px !important
|
||||
padding-left: 8px
|
||||
padding-left: 5px
|
||||
text-overflow: ellipsis
|
||||
|
||||
// Specific to the user stats page
|
||||
#user-achievements-view
|
||||
.row
|
||||
//.col-lg-4, .col-xs-12
|
||||
|
@ -65,7 +67,7 @@
|
|||
margin-right: 5px
|
||||
width: 260px
|
||||
height: 100px
|
||||
padding: 20px 10px 20px 60px
|
||||
padding: 15px 10px 20px 60px
|
||||
|
||||
.achievement-title
|
||||
font-size: 20px
|
||||
|
|
|
@ -288,3 +288,9 @@ body[lang='ja']
|
|||
|
||||
a[data-toggle="coco-modal"]
|
||||
cursor: pointer
|
||||
|
||||
.achievement-corner
|
||||
position: fixed
|
||||
bottom: 0px
|
||||
right: 0px
|
||||
z-index: 1001
|
||||
|
|
|
@ -106,13 +106,20 @@ block content
|
|||
th Last Played
|
||||
th Status
|
||||
each session in recentlyPlayed
|
||||
if session.get('levelName')
|
||||
tr
|
||||
td
|
||||
a(href="/play/level/#{session.get('levelID')}")= session.get('levelName')
|
||||
- var posturl = ''
|
||||
- if (session.get('team')) posturl = '?team=' + session.get('team')
|
||||
a(href="/play/level/#{session.get('levelID') + posturl}")= session.get('levelName') + (session.get('team') ? ' (' + session.get('team') + ')' : '')
|
||||
td= moment(session.get('changed')).fromNow()
|
||||
td
|
||||
if session.get('state').complete === true
|
||||
| Completed
|
||||
td Completed
|
||||
else if ! session.isMultiplayer()
|
||||
td Unfinished
|
||||
else
|
||||
td
|
||||
|
||||
else
|
||||
.panel.panel-default
|
||||
.panel-body
|
||||
|
|
|
@ -73,6 +73,7 @@ body
|
|||
.main-content-area
|
||||
block content
|
||||
p If this is showing, you dun goofed
|
||||
.achievement-corner
|
||||
|
||||
block footer
|
||||
.footer.clearfix
|
||||
|
|
|
@ -69,9 +69,9 @@ block append content
|
|||
th Last Played
|
||||
th Status
|
||||
each session in singlePlayerSessions
|
||||
if session.get('levelName')
|
||||
tr
|
||||
td
|
||||
if session.get('levelName')
|
||||
a(href="/play/level/#{session.get('levelID')}")= session.get('levelName')
|
||||
td= moment(session.get('changed')).fromNow()
|
||||
if session.get('state').complete === true
|
||||
|
|
|
@ -66,26 +66,6 @@ module.exports = class RootView extends CocoView
|
|||
|
||||
barBorder = $('<img src="/images/achievements/bar_border.png" />')
|
||||
|
||||
###
|
||||
barBorder.hover (e) ->
|
||||
#console.debug e
|
||||
x = e.pageX
|
||||
y = e.pageY
|
||||
$actualHover = _.find [$('.progress-bar-warning'), $('.progress-bar-success'), $('.progress-bar-white')], (el) ->
|
||||
offset = el.offset()
|
||||
l = offset.left
|
||||
t = offset.top
|
||||
h = el.height() + 10
|
||||
w = el.width() + 10
|
||||
|
||||
maxx = l + w
|
||||
maxy = t + h
|
||||
|
||||
return (y <= maxy && y >= t) && (x <= maxx && x >= l) ? true : null
|
||||
#console.debug $actualHover
|
||||
$actualHover.trigger e if $actualHover
|
||||
|
||||
###
|
||||
data =
|
||||
title: achievement.get('name')
|
||||
image: $("<img src='#{achievement.getImageURL()}' />")
|
||||
|
@ -99,16 +79,25 @@ module.exports = class RootView extends CocoView
|
|||
|
||||
showNewAchievement: (achievement, earnedAchievement) ->
|
||||
data = @createNotifyData achievement, earnedAchievement
|
||||
console.debug data
|
||||
options =
|
||||
autoHideDelay: 1000000
|
||||
autoHideDelay: 5000
|
||||
elementPosition: 'top left'
|
||||
globalPosition: 'bottom right'
|
||||
showDuration: 400
|
||||
showDuration: 0
|
||||
style: achievement.getNotifyStyle()
|
||||
autoHide: false
|
||||
clickToHide: true
|
||||
|
||||
console.debug 'showing achievement', achievement.get 'name'
|
||||
$.notify( data, options )
|
||||
unless @timeout?
|
||||
$.notify data, options
|
||||
@timeout = 2000
|
||||
else
|
||||
setTimeout ->
|
||||
$.notify data, options
|
||||
@timeout += 2000
|
||||
, @timeout
|
||||
|
||||
handleNewAchievements: (earnedAchievements) ->
|
||||
_.each earnedAchievements.models, (earnedAchievement) =>
|
||||
|
|
|
@ -26,9 +26,8 @@ module.exports = class AchievementsView extends UserView
|
|||
|
||||
onLoaded: ->
|
||||
console.log @earnedAchievements
|
||||
console.log 'onLoaded'
|
||||
console.log @achievements
|
||||
_.each @earnedAchievements.models, (earned) =>
|
||||
console.log earned
|
||||
return unless relatedAchievement = _.find @achievements.models, (achievement) ->
|
||||
achievement.get('_id') is earned.get 'achievement'
|
||||
relatedAchievement.set 'unlocked', true
|
||||
|
|
|
@ -25,7 +25,7 @@ module.exports = class MainUserView extends UserView
|
|||
multiPlayerSessions = []
|
||||
languageCounts = {}
|
||||
for levelSession in @levelSessions.models
|
||||
if levelSession.isMultiPlayer()
|
||||
if levelSession.isMultiplayer()
|
||||
multiPlayerSessions.push levelSession
|
||||
else
|
||||
singlePlayerSessions.push levelSession
|
||||
|
|
|
@ -11,9 +11,21 @@ do (setupLodash = this) ->
|
|||
|
||||
database.connect()
|
||||
|
||||
achievements =
|
||||
|
||||
## Util
|
||||
|
||||
## Types
|
||||
contributor = (obj) ->
|
||||
_.extend obj, # This way we get the name etc on top
|
||||
collection: 'users'
|
||||
userField: '_id'
|
||||
category: 'contributor'
|
||||
|
||||
### UNLOCKABLES ###
|
||||
# Generally ordered according to user.stats schema
|
||||
unlockableAchievements =
|
||||
signup:
|
||||
name: 'Signed up'
|
||||
name: 'Signed Up'
|
||||
description: 'Signed up to the most awesome coding game around.'
|
||||
query: 'anonymous': false
|
||||
worth: 10
|
||||
|
@ -21,27 +33,155 @@ achievements =
|
|||
userField: '_id'
|
||||
category: 'miscellaneous'
|
||||
difficulty: 1
|
||||
recalculable: true
|
||||
|
||||
completedFirstLevel:
|
||||
name: 'Completed one Level'
|
||||
name: 'Completed 1 Level'
|
||||
description: 'Completed your very first level.'
|
||||
query: 'stats.gamesCompleted': $gte: 1
|
||||
worth: 50
|
||||
worth: 20
|
||||
collection: 'users'
|
||||
userField: '_id'
|
||||
category: 'levels'
|
||||
difficulty: 1
|
||||
recalculable: true
|
||||
|
||||
completedFiveLevels:
|
||||
name: 'Completed one Level'
|
||||
description: 'Completed your very first level.'
|
||||
query: 'stats.gamesCompleted': $gte: 1
|
||||
name: 'Completed 5 Levels'
|
||||
description: 'Completed 5 Levels.'
|
||||
query: 'stats.gamesCompleted': $gte: 5
|
||||
worth: 50
|
||||
collection: 'users'
|
||||
userField: '_id'
|
||||
category: 'levels'
|
||||
difficulty: 2
|
||||
recalculable: true
|
||||
|
||||
completedTwentyLevels:
|
||||
name: 'Completed 20 Levels'
|
||||
description: 'Completed 20 Levels.'
|
||||
query: 'stats.gamesCompleted': $gte: 20
|
||||
worth: 500
|
||||
collection: 'users'
|
||||
userField: '_id'
|
||||
category: 'levels'
|
||||
difficulty: 3
|
||||
recalculable: true
|
||||
|
||||
editedOneArticle: contributor
|
||||
name: 'Edited an Article'
|
||||
description: 'Edited your first Article.'
|
||||
query: 'stats.articleEdits': $gte: 1
|
||||
worth: 50
|
||||
difficulty: 1
|
||||
|
||||
editedOneLevel: contributor
|
||||
name: 'Edited a Level'
|
||||
description: 'Edited your first Level.'
|
||||
query: 'stats.levelEdits': $gte: 1
|
||||
worth: 50
|
||||
difficulty: 1
|
||||
recalculable: true
|
||||
|
||||
editedOneLevelSystem: contributor
|
||||
name: 'Edited a Level System'
|
||||
description: 'Edited your first Level System.'
|
||||
query: 'stats.levelSystemEdits': $gte: 1
|
||||
worth: 50
|
||||
difficulty: 1
|
||||
recalculable: true
|
||||
|
||||
editedOneLevelComponent: contributor
|
||||
name: 'Edited a Level Component'
|
||||
description: 'Edited your first Level Component.'
|
||||
query: 'stats.levelComponentEdits': $gte: 1
|
||||
worth: 50
|
||||
difficulty: 1
|
||||
recalculable: true
|
||||
|
||||
editedOneThangType: contributor
|
||||
name: 'Edited a Thang Type'
|
||||
description: 'Edited your first Thang Type.'
|
||||
query: 'stats.thangTypeEdits': $gte: 1
|
||||
worth: 50
|
||||
difficulty: 1
|
||||
recalculable: true
|
||||
|
||||
submittedOnePatch: contributor
|
||||
name: 'Submitted a Patch'
|
||||
description: 'Submitted your very first patch.'
|
||||
query: 'stats.patchesSubmitted': $gte: 1
|
||||
worth: 50
|
||||
difficulty: 1
|
||||
recalculable: true
|
||||
|
||||
contributedOnePatch: contributor
|
||||
name: 'Contributed a Patch'
|
||||
description: 'Got your very first accepted Patch.'
|
||||
query: 'stats.patchesContributed': $gte: 1
|
||||
worth: 50
|
||||
difficulty: 1
|
||||
recalculable: true
|
||||
|
||||
acceptedOnePatch: contributor
|
||||
name: 'Accepted a Patch'
|
||||
description: 'Accepted your very first patch.'
|
||||
query: 'stats.patchesAccepted': $gte: 1
|
||||
worth: 50
|
||||
difficulty: 1
|
||||
recalculable: false
|
||||
|
||||
oneTranslationPatch: contributor
|
||||
name: 'First Translation'
|
||||
description: 'Did your very first translation.'
|
||||
query: 'stats.totalTranslationPatches': $gte: 1
|
||||
worth: 50
|
||||
difficulty: 1
|
||||
recalculable: true
|
||||
|
||||
oneMiscPatch: contributor
|
||||
name: 'First Miscellaneous Patch'
|
||||
description: 'Did your first miscellaneous patch.'
|
||||
query: 'stats.totalMiscPatches': $gte: 1
|
||||
worth: 50
|
||||
difficulty: 1
|
||||
recalculable: true
|
||||
|
||||
oneArticleTranslationPatch: contributor
|
||||
name: 'First Article Translation'
|
||||
description: 'Did your very first Article translation.'
|
||||
query: 'stats.articleTranslationPatches': $gte: 1
|
||||
worth: 50
|
||||
difficulty: 1
|
||||
recalculable: true
|
||||
|
||||
oneArticleMiscPatch: contributor
|
||||
name: 'First Misc Article Patch'
|
||||
description: 'Did your first miscellaneous Article patch.'
|
||||
query: 'stats.totalMiscPatches': $gte: 1
|
||||
worth: 50
|
||||
difficulty: 1
|
||||
recalculable: true
|
||||
|
||||
oneLevelTranslationPatch: contributor
|
||||
name: 'First Level Translation'
|
||||
description: 'Did your very first Level translation.'
|
||||
query: 'stats.levelTranslationPatches': $gte: 1
|
||||
worth: 50
|
||||
difficulty: 1
|
||||
recalculable: true
|
||||
|
||||
oneLevelMiscPatch: contributor
|
||||
name: 'First Misc Level Patch'
|
||||
description: 'Did your first misc Level patch.'
|
||||
query: 'stats.levelMiscPatches': $gte: 1
|
||||
worth: 50
|
||||
difficulty: 1
|
||||
recalculable: true
|
||||
|
||||
|
||||
### REPEATABLES ###
|
||||
repeatableAchievements =
|
||||
simulatedBy:
|
||||
name: 'Simulated ladder game'
|
||||
description: 'Simulated a ladder game.'
|
||||
|
@ -60,16 +200,30 @@ achievements =
|
|||
c: 0
|
||||
|
||||
Achievement = require '../server/achievements/Achievement'
|
||||
EarnedAchievement = require '../server/achievements/EarnedAchievement'
|
||||
|
||||
Achievement.remove {}, (err) ->
|
||||
Achievement.find {}, (err, achievements) ->
|
||||
achievementIDs = (achievement.get('_id') + '' for achievement in achievements)
|
||||
EarnedAchievement.remove {achievement: $in: achievementIDs}, (err, count) ->
|
||||
return log.error err if err?
|
||||
log.info "Removed #{count} earned achievements that were related"
|
||||
|
||||
Achievement.remove {}, (err) ->
|
||||
log.error err if err?
|
||||
log.info 'Removed all achievements.'
|
||||
|
||||
log.info "Got #{Object.keys(unlockableAchievements).length} unlockable achievements"
|
||||
log.info "and #{Object.keys(repeatableAchievements).length} repeatable achievements"
|
||||
achievements = _.extend unlockableAchievements, repeatableAchievements
|
||||
|
||||
async.each Object.keys(achievements), (key, callback) ->
|
||||
achievement = achievements[key]
|
||||
log.info "Setting up '#{achievement.name}'..."
|
||||
achievement = new Achievement achievement
|
||||
achievement.save (err) ->
|
||||
achievementM = new Achievement achievement
|
||||
# What the actual * Mongoose? It automatically converts 'stats.edits' to a nested object
|
||||
achievementM.set 'query', achievement.query
|
||||
log.debug JSON.stringify achievementM.get 'query'
|
||||
achievementM.save (err) ->
|
||||
log.error err if err?
|
||||
callback()
|
||||
, (err) ->
|
||||
|
|
|
@ -29,6 +29,8 @@ AchievementSchema.methods.getExpFunction = ->
|
|||
parameters = @get('function')?.parameters or jsonschema.properties.function.default.parameters
|
||||
return utils.functionCreators[kind](parameters) if kind of utils.functionCreators
|
||||
|
||||
AchievementSchema.methods.isRecalculable = -> @get('recalculable') is true
|
||||
|
||||
AchievementSchema.statics.jsonschema = jsonschema
|
||||
AchievementSchema.statics.earnedAchievements = {}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ class EarnedAchievementHandler extends Handler
|
|||
# Fetch every single user
|
||||
User.find {}, (err, users) ->
|
||||
callback err if err?
|
||||
log.info "... for a total of #{users.length} users."
|
||||
log.info "for a total of #{users.length} users."
|
||||
|
||||
async.each users, ((user, doneWithUser) ->
|
||||
# Keep track of a user's already achieved in order to set the notified values correctly
|
||||
|
@ -68,6 +68,8 @@ class EarnedAchievementHandler extends Handler
|
|||
newTotalPoints = 0
|
||||
|
||||
async.each achievements, ((achievement, doneWithAchievement) ->
|
||||
return doneWithAchievement() unless achievement.isRecalculable()
|
||||
|
||||
isRepeatable = achievement.get('proportionalTo')?
|
||||
model = mongoose.modelNameByCollection(achievement.get('collection'))
|
||||
if not model?
|
||||
|
@ -79,6 +81,8 @@ class EarnedAchievementHandler extends Handler
|
|||
finalQuery.$or[0][achievement.userField] = userID
|
||||
finalQuery.$or[1][achievement.userField] = mongoose.Types.ObjectId userID
|
||||
|
||||
log.debug JSON.stringify finalQuery
|
||||
|
||||
model.findOne finalQuery, (err, something) ->
|
||||
return doneWithAchievement() if _.isEmpty something
|
||||
|
||||
|
@ -105,14 +109,10 @@ class EarnedAchievementHandler extends Handler
|
|||
EarnedAchievement.update {achievement:earned.achievement, user:earned.user}, earned, {upsert: true}, (err) ->
|
||||
doneWithAchievement err
|
||||
), saveUserPoints = ->
|
||||
# In principle it is enough to deduct the old amount of points and add the new amount,
|
||||
# but just to be entirely safe let's start from 0 in case we're updating all of a user's achievements
|
||||
# Since some achievements cannot be recalculated it's important to deduct the old amount of exp
|
||||
# and add the new amount, instead of just setting to the new amount
|
||||
return doneWithUser() unless newTotalPoints
|
||||
log.debug "Matched a total of #{newTotalPoints} new points"
|
||||
if _.isEmpty filter # Completely clean
|
||||
log.debug "Setting this user's score to #{newTotalPoints}"
|
||||
User.update {_id: userID}, {$set: points: newTotalPoints}, {}, doneWithUser
|
||||
else
|
||||
log.debug "Incrementing score for these achievements with #{newTotalPoints - previousPoints}"
|
||||
User.update {_id: userID}, {$inc: points: newTotalPoints - previousPoints}, {}, doneWithUser
|
||||
), onFinished
|
||||
|
|
|
@ -38,9 +38,9 @@ AchievablePlugin = (schema, options) ->
|
|||
isRepeatable = achievement.get('proportionalTo')?
|
||||
alreadyAchieved = if isNew then false else LocalMongo.matchesQuery originalDocObj, query
|
||||
newlyAchieved = LocalMongo.matchesQuery(docObj, query)
|
||||
#log.debug 'isRepeatable: ' + isRepeatable
|
||||
#log.debug 'alreadyAchieved: ' + alreadyAchieved
|
||||
#log.debug 'newlyAchieved: ' + newlyAchieved
|
||||
log.debug 'isRepeatable: ' + isRepeatable
|
||||
log.debug 'alreadyAchieved: ' + alreadyAchieved
|
||||
log.debug 'newlyAchieved: ' + newlyAchieved
|
||||
|
||||
userObjectID = doc.get(achievement.get('userField'))
|
||||
userID = if _.isObject userObjectID then userObjectID.toHexString() else userObjectID # Standardize! Use strings, not ObjectId's
|
||||
|
|
Loading…
Reference in a new issue