From bf41216a9b5982792fd38877cdb9ea91d2f73c86 Mon Sep 17 00:00:00 2001 From: Brandon McPhail Date: Fri, 13 Dec 2013 15:48:18 -0800 Subject: [PATCH 01/19] Fixed challenge prize logic to avoid double paying --- src/controllers/challenges.js | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/controllers/challenges.js b/src/controllers/challenges.js index dc11abe5df..7f5014ec8e 100644 --- a/src/controllers/challenges.js +++ b/src/controllers/challenges.js @@ -106,20 +106,22 @@ api.create = function(req, res){ if (+req.body.prize > 0) { waterfall.push(function(cb){ var groupBalance = ((group.balance && group.leader==user._id) ? group.balance : 0); - if (req.body.prize > (user.balance*4 + groupBalance*4)) - return cb("Challenge.prize > (your gems + group balance). Purchase more gems or lower prize amount.s") + var prizeCost = req.body.prize/4; // I really should have stored user.balance as gems rather than dollars... stupid... + if (prizeCost > user.balance + groupBalance) + return cb("You can't afford this prize. Purchase more gems or lower the prize amount.") - var net = req.body.prize/4; // I really should have stored user.balance as gems rather than dollars... stupid... - - // user is group leader, and group has balance. Subtract from that first, then take the rest from user - if (groupBalance > 0) { - group.balance -= net; - if (group.balance < 0) { - net = Math.abs(group.balance); - group.balance = 0; - } - } - user.balance -= net; + if (groupBalance >= prizeCost) { + // Group pays for all of prize + group.balance -= prizeCost; + } else if (groupBalance > 0) { + // User pays remainder of prize cost after group + var remainder = prizeCost - group.balance; + group.balance = 0; + user.balance -= remainder; + } else { + // User pays for all of prize + user.balance -= prizeCost; + } cb(null) }); } @@ -344,4 +346,4 @@ api.unlink = function(req, res, next) { if (err) return res.json(500,{err:err}); res.send(200); }); -} \ No newline at end of file +} From 376e97723807eb88eaa0157b7e59698b9b9f2e48 Mon Sep 17 00:00:00 2001 From: Magnilucent Date: Sat, 14 Dec 2013 02:38:08 -0500 Subject: [PATCH 02/19] Fixes #1959: petCount was only being updated... in groupsCtrl.js. This made profile.petCount blank unless the current user viewed himself in chat. I put the code to set profile.petCount into userCtrl.js. Note: petCount (not profile.petCount) is still always equal to the current user's pet count. There is probably a better solution than this, I just don't know enough about AngularJS to make it. --- public/js/controllers/userCtrl.js | 1 + 1 file changed, 1 insertion(+) diff --git a/public/js/controllers/userCtrl.js b/public/js/controllers/userCtrl.js index 351f57688f..e2324c171f 100644 --- a/public/js/controllers/userCtrl.js +++ b/public/js/controllers/userCtrl.js @@ -3,6 +3,7 @@ habitrpg.controller("UserCtrl", ['$rootScope', '$scope', '$location', 'User', '$http', '$state', function($rootScope, $scope, $location, User, $http, $state) { $scope.profile = User.user; + $scope.profile.petCount = $rootScope.Shared.countPets(null, $scope.profile.items.pets); $scope.hideUserAvatar = function() { $(".userAvatar").hide(); }; From 485209ed74c743dee88a5bb7a65e344293b17a12 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 22 Dec 2013 12:20:35 -0600 Subject: [PATCH 03/19] Remove sliding effect on avatar mouseover --- public/css/avatar.styl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/css/avatar.styl b/public/css/avatar.styl index bb81bb6327..3a774d4f65 100644 --- a/public/css/avatar.styl +++ b/public/css/avatar.styl @@ -15,12 +15,12 @@ future re: pets and whatnot, this is just temporary. width: 10em max-width: 10em margin: 0 // need this b/c of bootstrap, remove or reset later - padding: 0 // push down the sprite position: relative cursor: pointer background: #f5f5f5 - transition: padding 0.13s ease-out, border 0.25s ease-out, background 0.25s ease-out + transition: border 0.25s ease-out, background 0.25s ease-out//, padding 0.13s ease-out outline: 1px solid rgba(0,0,0,0.1) +// padding: 0 // push down the sprite // the hero's info frame .herobox:after @@ -74,12 +74,12 @@ future re: pets and whatnot, this is just temporary. background: desaturate(lighten($better, 30%), 10%) &:after opacity: 1 -.herobox.hasPet +/* .herobox.hasPet &:hover, &:focus padding-top: 3.25em .herobox:not(.hasPet) &:hover, &:focus - padding-top: 2.5em + padding-top: 2.5em */ // positioning the sprites, etc From 47c4b1dbe53daecdbfe3befdd2b97f6a25c8eb5a Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sat, 28 Dec 2013 17:46:18 -0600 Subject: [PATCH 04/19] Don't need angle brackets anymore for party cast notifications --- src/controllers/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/user.js b/src/controllers/user.js index aac874bc83..c0b2d84ad1 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -370,7 +370,7 @@ api.cast = function(req, res) { if (group) { series.push(function(cb2){ - var message = '`<'+user.profile.name+'> casts '+spell.text + (type=='user' ? ' on @'+found.profile.name : ' for the party')+'.`'; + var message = '`'+user.profile.name+' casts '+spell.text + (type=='user' ? ' on @'+found.profile.name : ' for the party')+'.`'; group.sendChat(message); group.save(cb2); }) From d3a4d35d5dfe2fb17c0aeca8f23b0d4d3ae96e3d Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sat, 28 Dec 2013 21:50:27 -0700 Subject: [PATCH 05/19] quest: when new user joins party, invite to pending-invite quests. When user in quest leaves party, knock user from group.quest.members & quest from user.party.quest --- src/controllers/groups.js | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/controllers/groups.js b/src/controllers/groups.js index e219dd8b15..94a4b6e343 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -248,6 +248,11 @@ api.join = function(req, res) { if (group.type == 'party' && group._id == (user.invitations && user.invitations.party && user.invitations.party.id)) { user.invitations.party = undefined; user.save(); + // invite new user to pending quest + if (group.quest.key && !group.quest.active) { + group.quest.members[user._id] = undefined; + group.markModified('quest.members'); + } } else if (group.type == 'guild' && user.invitations && user.invitations.guilds) { var i = _.findIndex(user.invitations.guilds, {id:group._id}); @@ -278,11 +283,25 @@ api.join = function(req, res) { api.leave = function(req, res, next) { var user = res.locals.user, group = res.locals.group; - - Group.update({_id:group._id},{$pull:{members:user._id}}, function(err, saved){ - if (err) return res.json(500,{err:err}); + async.parallel([ + // Remove active quest from user if they're leaving the party + function(cb){ + if (group.type != 'party') return cb(null,{},1); + user.party.quest = Group.cleanQuestProgress(); + user.save(cb); + }, + function(cb){ + var update = {$pull:{members:user._id}}; + if (group.type == 'party' && group.quest.key){ + update['$unset'] = {}; + update['$unset']['quest.members.' + user._id] = 1; + } + Group.update({_id:group._id},update,cb); + } + ],function(err){ + if (err) return next(err); return res.send(204); - }); + }) } api.invite = function(req, res, next) { @@ -351,7 +370,7 @@ api.removeMember = function(req, res, next){ } if(_.contains(group.members, uuid)){ - Group.update({_id:group._id},{$pull:{members:uuid}}, function(err, saved){ + Group.update({_id:group._id},{$pull:{members:uuid},$inc:{memberCount:-1}}, function(err, saved){ if (err) return res.json(500,{err:err}); // Sending an empty 204 because Group.update doesn't return the group From 57cd53cfc98ba4fcd0ac6b9a97386635c9b061b6 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sat, 28 Dec 2013 21:54:25 -0700 Subject: [PATCH 06/19] fix #2155 . Since quest.leader is new, doesn't apply to quests started before today. Show abort/begin for all quest members if quest.leader doesn't exist, otherwise only show those options to the leader --- views/options/social/group.jade | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/views/options/social/group.jade b/views/options/social/group.jade index de224d432f..b1217e0006 100644 --- a/views/options/social/group.jade +++ b/views/options/social/group.jade @@ -21,7 +21,7 @@ a.pull-right.gem-wallet(popover-trigger='mouseenter', popover-title='Guild Bank' hr .npc_ian.pull-left p Once all members have either accepted or rejected, the quest begins. Only those that clicked "accept" will be able to participate in the quest and recieve the drops. If members are pending too long (inactive?), you can start without them by clicking "Begin". - button.btn.btn-small.btn-warning(ng-if='group.quest.leader==user._id || group.leader==user._id', ng-click='party.$questAccept({"force":true})') Begin + button.btn.btn-small.btn-warning(ng-if='!group.quest.leader || group.quest.leader==user._id', ng-click='party.$questAccept({"force":true})') Begin //-TODO Cancel button //-TODO Both force-start & cancel should only be available to quest-initiator @@ -71,7 +71,7 @@ a.pull-right.gem-wallet(popover-trigger='mouseenter', popover-title='Guild Bank' p Only participants can collect items and share in the quest loot. If you die during a quest, you get booted from the quest. If everyone dies once, the quest fails. - button.btn.btn-mini.btn-danger(ng-if='group.quest.leader==user._id || group.leader==user._id', ng-click='questAbort()') Abort + button.btn.btn-mini.btn-danger(ng-if='!group.quest.leader || group.quest.leader==user._id', ng-click='questAbort()') Abort // ------ Information ------- .modal.inline-modal From b20a3b73815ef5a4f4930ad263ab292c411a7102 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 29 Dec 2013 00:12:15 -0700 Subject: [PATCH 07/19] #1499 add list filter on tasks to not display completed todos at all until "completed" tab clicked --- public/js/filters/filters.js | 6 ++++++ views/shared/tasks/task.jade | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/public/js/filters/filters.js b/public/js/filters/filters.js index 9d2255d9e2..222a8bb30a 100644 --- a/public/js/filters/filters.js +++ b/public/js/filters/filters.js @@ -8,4 +8,10 @@ angular.module('habitrpg') return function (gp) { return Math.floor((gp - Math.floor(gp))*100); } + }) + .filter('completedFilter', function(){ + return function(tasks,_showCompleted) { + if (tasks[0].type != 'todo') return tasks; + return _.where(tasks, {completed:!!_showCompleted}); + } }) \ No newline at end of file diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index c032d8f794..19f34d7205 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -1,4 +1,4 @@ -li(bindonce='list', ng-repeat='task in obj[list.type+"s"]', class='task {{Shared.taskClasses(task, user.filters, user.preferences.dayStart, user.lastCron, list.showCompleted, main)}}', ng-click='spell && castEnd(task, "task", $event)', ng-class='{"cast-target":spell}') +li(bindonce='list', ng-repeat='task in obj[list.type+"s"] | completedFilter: list.showCompleted', class='task {{Shared.taskClasses(task, user.filters, user.preferences.dayStart, user.lastCron, list.showCompleted, main)}}', ng-click='spell && castEnd(task, "task", $event)', ng-class='{"cast-target":spell}') // right-hand side control buttons .task-meta-controls From d96683378212d14c2ba7acf489388b1b93f12f06 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sat, 28 Dec 2013 23:31:28 -0700 Subject: [PATCH 08/19] #1499 ng-if to hide/show task editing (fewer watchers) --- views/shared/tasks/task.jade | 195 ++++++++++++++++++----------------- 1 file changed, 98 insertions(+), 97 deletions(-) diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index 19f34d7205..b9840263f1 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -64,113 +64,114 @@ li(bindonce='list', ng-repeat='task in obj[list.type+"s"] | completedFilter: lis | {{task.text}} // edit/options dialog - .task-options(ng-show='task._editing') + div(ng-if='task._editing') + .task-options - // Broken Challenge - .well(ng-if='task.challenge.broken') - div(ng-if='task.challenge.broken=="TASK_DELETED"') - p Broken Challenge Link: this task was part of a challenge, but has been removed from it. What would you like to do? - p - a(ng-click='unlink(task, "keep")') Keep It - |  |  - a(ng-click="removeTask(obj[list.type+'s'], $index)") Remove It - div(ng-if='task.challenge.broken=="CHALLENGE_DELETED"') - p Broken Challenge Link: this task was part of a challenge, but the challenge (or group) has been deleted. What to do with the orphan tasks? - p - a(ng-click='unlink(task, "keep-all")') Keep Them - |  |  - a(ng-click='unlink(task, "remove-all")') Remove Them - div(ng-if='task.challenge.broken=="CHALLENGE_CLOSED"') - p. - This challenge has been completed, and the winner was {{task.challenge.winner}}! What to do with the orphan tasks? - p - a(ng-click='unlink(task, "keep-all")') Keep Them - |  |  - a(ng-click='unlink(task, "remove-all")') Remove Them - //div(ng-if='task.challenge.broken=="UNSUBSCRIBED"') - p Broken Challenge Link: this task was part of a challenge, but you have unsubscribed from the challenge. What to do with the orphan tasks? - p - a(ng-click="unlink(task, 'keep-all')") Keep Them - |  |  - a(ng-click="unlink(task, 'remove-all')") Remove Them + // Broken Challenge + .well(ng-if='task.challenge.broken') + div(ng-if='task.challenge.broken=="TASK_DELETED"') + p Broken Challenge Link: this task was part of a challenge, but has been removed from it. What would you like to do? + p + a(ng-click='unlink(task, "keep")') Keep It + |  |  + a(ng-click="removeTask(obj[list.type+'s'], $index)") Remove It + div(ng-if='task.challenge.broken=="CHALLENGE_DELETED"') + p Broken Challenge Link: this task was part of a challenge, but the challenge (or group) has been deleted. What to do with the orphan tasks? + p + a(ng-click='unlink(task, "keep-all")') Keep Them + |  |  + a(ng-click='unlink(task, "remove-all")') Remove Them + div(ng-if='task.challenge.broken=="CHALLENGE_CLOSED"') + p. + This challenge has been completed, and the winner was {{task.challenge.winner}}! What to do with the orphan tasks? + p + a(ng-click='unlink(task, "keep-all")') Keep Them + |  |  + a(ng-click='unlink(task, "remove-all")') Remove Them + //div(ng-if='task.challenge.broken=="UNSUBSCRIBED"') + p Broken Challenge Link: this task was part of a challenge, but you have unsubscribed from the challenge. What to do with the orphan tasks? + p + a(ng-click="unlink(task, 'keep-all')") Keep Them + |  |  + a(ng-click="unlink(task, 'remove-all')") Remove Them - form(ng-submit='saveTask(task)') - // text & notes - fieldset.option-group - label.option-title Text - input.option-content(type='text', ng-model='task.text', required, ng-disabled='task.challenge.id') + form(ng-submit='saveTask(task)') + // text & notes + fieldset.option-group + label.option-title Text + input.option-content(type='text', ng-model='task.text', required, ng-disabled='task.challenge.id') - label.option-title Extra Notes - textarea.option-content(rows='3', ng-model='task.notes', ng-disabled='task.challenge.id') + label.option-title Extra Notes + textarea.option-content(rows='3', ng-model='task.notes', ng-disabled='task.challenge.id') - // if Habit, plus/minus command options - fieldset.option-group(ng-if='task.type=="habit" && !task.challenge.id') - legend.option-title Direction/Actions - span.task-checker.action-plusminus.select-toggle - input.visuallyhidden.focusable(id='{{obj._id}}_{{task.id}}-option-plus', type='checkbox', ng-model='task.up') - label(for='{{obj._id}}_{{task.id}}-option-plus') - span.task-checker.action-plusminus.select-toggle - input.visuallyhidden.focusable(id='{{obj._id}}_{{task.id}}-option-minus', type='checkbox', ng-model='task.down') - label(for='{{obj._id}}_{{task.id}}-option-minus') + // if Habit, plus/minus command options + fieldset.option-group(ng-if='task.type=="habit" && !task.challenge.id') + legend.option-title Direction/Actions + span.task-checker.action-plusminus.select-toggle + input.visuallyhidden.focusable(id='{{obj._id}}_{{task.id}}-option-plus', type='checkbox', ng-model='task.up') + label(for='{{obj._id}}_{{task.id}}-option-plus') + span.task-checker.action-plusminus.select-toggle + input.visuallyhidden.focusable(id='{{obj._id}}_{{task.id}}-option-minus', type='checkbox', ng-model='task.down') + label(for='{{obj._id}}_{{task.id}}-option-minus') - // if Daily, calendar - fieldset(bo-if='task.type=="daily"', class="option-group") - legend.option-title Repeat - .task-controls.tile-group.repeat-days(bindonce) - // note, does not use data-toggle="buttons-checkbox" - it would interfere with our own click binding - button.task-action-btn.tile(ng-class='{active: task.repeat.su}', type='button', ng-click='task.challenge.id || (task.repeat.su = !task.repeat.su)', bo-text='moment.weekdaysMin(0)') - button.task-action-btn.tile(ng-class='{active: task.repeat.m}', type='button', ng-click='task.challenge.id || (task.repeat.m = !task.repeat.m)', bo-text='moment.weekdaysMin(1)') - button.task-action-btn.tile(ng-class='{active: task.repeat.t}', type='button', ng-click='task.challenge.id || (task.repeat.t = !task.repeat.t)', bo-text='moment.weekdaysMin(2)') - button.task-action-btn.tile(ng-class='{active: task.repeat.w}', type='button', ng-click='task.challenge.id || (task.repeat.w = !task.repeat.w)', bo-text='moment.weekdaysMin(3)') - button.task-action-btn.tile(ng-class='{active: task.repeat.th}', type='button', ng-click='task.challenge.id || (task.repeat.th = !task.repeat.th)', bo-text='moment.weekdaysMin(4)') - button.task-action-btn.tile(ng-class='{active: task.repeat.f}', type='button', ng-click='task.challenge.id || (task.repeat.f= !task.repeat.f)', bo-text='moment.weekdaysMin(5)') - button.task-action-btn.tile(ng-class='{active: task.repeat.s}', type='button', ng-click='task.challenge.id || (task.repeat.s = !task.repeat.s)', bo-text='moment.weekdaysMin(6)') + // if Daily, calendar + fieldset(bo-if='task.type=="daily"', class="option-group") + legend.option-title Repeat + .task-controls.tile-group.repeat-days(bindonce) + // note, does not use data-toggle="buttons-checkbox" - it would interfere with our own click binding + button.task-action-btn.tile(ng-class='{active: task.repeat.su}', type='button', ng-click='task.challenge.id || (task.repeat.su = !task.repeat.su)', bo-text='moment.weekdaysMin(0)') + button.task-action-btn.tile(ng-class='{active: task.repeat.m}', type='button', ng-click='task.challenge.id || (task.repeat.m = !task.repeat.m)', bo-text='moment.weekdaysMin(1)') + button.task-action-btn.tile(ng-class='{active: task.repeat.t}', type='button', ng-click='task.challenge.id || (task.repeat.t = !task.repeat.t)', bo-text='moment.weekdaysMin(2)') + button.task-action-btn.tile(ng-class='{active: task.repeat.w}', type='button', ng-click='task.challenge.id || (task.repeat.w = !task.repeat.w)', bo-text='moment.weekdaysMin(3)') + button.task-action-btn.tile(ng-class='{active: task.repeat.th}', type='button', ng-click='task.challenge.id || (task.repeat.th = !task.repeat.th)', bo-text='moment.weekdaysMin(4)') + button.task-action-btn.tile(ng-class='{active: task.repeat.f}', type='button', ng-click='task.challenge.id || (task.repeat.f= !task.repeat.f)', bo-text='moment.weekdaysMin(5)') + button.task-action-btn.tile(ng-class='{active: task.repeat.s}', type='button', ng-click='task.challenge.id || (task.repeat.s = !task.repeat.s)', bo-text='moment.weekdaysMin(6)') - // if Reward, pricing - fieldset.option-group.option-short(ng-if='task.type=="reward" && !task.challenge.id') - legend.option-title Price - input.option-content(type='number', size='16', min='0', step="any", ng-model='task.value') - .money.input-suffix - span.shop_gold + // if Reward, pricing + fieldset.option-group.option-short(ng-if='task.type=="reward" && !task.challenge.id') + legend.option-title Price + input.option-content(type='number', size='16', min='0', step="any", ng-model='task.value') + .money.input-suffix + span.shop_gold - // if Todos, the due date - fieldset.option-group(ng-if='task.type=="todo" && !task.challenge.id') - legend.option-title Due Date - input.option-content.datepicker(type='text', data-date-format='mm/dd/yyyy', ng-model='task.date') + // if Todos, the due date + fieldset.option-group(ng-if='task.type=="todo" && !task.challenge.id') + legend.option-title Due Date + input.option-content.datepicker(type='text', data-date-format='mm/dd/yyyy', ng-model='task.date') - fieldset.option-group(ng-if='!$state.includes("options.social.challenges")') - legend.option-title Tags - label.checkbox(ng-repeat='tag in user.tags') - input(type='checkbox', ng-model='task.tags[tag.id]') - | {{tag.name}} + fieldset.option-group(ng-if='!$state.includes("options.social.challenges")') + legend.option-title Tags + label.checkbox(ng-repeat='tag in user.tags') + input(type='checkbox', ng-model='task.tags[tag.id]') + | {{tag.name}} - // Advanced Options - span(bo-if='task.type!="reward"') - p.option-title.mega(ng-click='task._advanced = !task._advanced') Advanced Options - fieldset.option-group.advanced-option(ng-class="{visuallyhidden: !task._advanced}") - legend.option-title - a.priority-multiplier-help(href='https://trello.com/card/priority-multiplier/50e5d3684fe3a7266b0036d6/17', target='_blank', popover-title='How difficult is this task?', popover-trigger='mouseenter', popover="This multiplies its point value. Use sparingly, rely instead on our organic value-adjustment algorithms. But some tasks are grossly more valuable (Write Thesis vs Floss Teeth). Click for more info.") - i.icon-question-sign - | Difficulty - .task-controls.tile-group.priority-multiplier - button.task-action-btn.tile(type='button', ng-class='{active: task.priority==1 || !task.priority}', ng-click='task.challenge.id || (task.priority=1)') Easy - button.task-action-btn.tile(type='button', ng-class='{active: task.priority==1.5}', ng-click='task.challenge.id || (task.priority=1.5)') Medium - button.task-action-btn.tile(type='button', ng-class='{active: task.priority==2}', ng-click='task.challenge.id || (task.priority=2)') Hard - //span(ng-if='task.type=="daily" && !task.challenge.id') - br - span(ng-if='task.type=="daily"') - legend.option-title Restore Streak - input.option-content(type='number', ng-model='task.streak') + // Advanced Options + span(bo-if='task.type!="reward"') + p.option-title.mega(ng-click='task._advanced = !task._advanced') Advanced Options + fieldset.option-group.advanced-option(ng-class="{visuallyhidden: !task._advanced}") + legend.option-title + a.priority-multiplier-help(href='https://trello.com/card/priority-multiplier/50e5d3684fe3a7266b0036d6/17', target='_blank', popover-title='How difficult is this task?', popover-trigger='mouseenter', popover="This multiplies its point value. Use sparingly, rely instead on our organic value-adjustment algorithms. But some tasks are grossly more valuable (Write Thesis vs Floss Teeth). Click for more info.") + i.icon-question-sign + | Difficulty + .task-controls.tile-group.priority-multiplier + button.task-action-btn.tile(type='button', ng-class='{active: task.priority==1 || !task.priority}', ng-click='task.challenge.id || (task.priority=1)') Easy + button.task-action-btn.tile(type='button', ng-class='{active: task.priority==1.5}', ng-click='task.challenge.id || (task.priority=1.5)') Medium + button.task-action-btn.tile(type='button', ng-class='{active: task.priority==2}', ng-click='task.challenge.id || (task.priority=2)') Hard + //span(ng-if='task.type=="daily" && !task.challenge.id') + br + span(ng-if='task.type=="daily"') + legend.option-title Restore Streak + input.option-content(type='number', ng-model='task.streak') - legend.option-title Attributes - .task-controls.tile-group - button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="str"}', ng-click='task.attribute="str"') Physical - button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="int"}', ng-click='task.attribute="int"') Mental - button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="con"}', ng-click='task.attribute="con"') Social - button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="per"}', ng-click='task.attribute="per"') - | Other  - i.icon-question-sign(popover='Eg, professional pursuits, hobbies, financial, etc.', popover-trigger='mouseenter', popover-placement='top') + legend.option-title Attributes + .task-controls.tile-group + button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="str"}', ng-click='task.attribute="str"') Physical + button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="int"}', ng-click='task.attribute="int"') Mental + button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="con"}', ng-click='task.attribute="con"') Social + button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="per"}', ng-click='task.attribute="per"') + | Other  + i.icon-question-sign(popover='Eg, professional pursuits, hobbies, financial, etc.', popover-trigger='mouseenter', popover-placement='top') - button.task-action-btn.tile.spacious(type='submit') Save & Close + button.task-action-btn.tile.spacious(type='submit') Save & Close div(class='{{obj._id}}{{task.id}}-chart', ng-show='charts[obj._id+task.id]') From b4ff7d4be4cfdb39afc7e9786989049b1a6039f1 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 29 Dec 2013 00:33:58 -0700 Subject: [PATCH 09/19] #1499 check that task[0] exists first --- public/js/filters/filters.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/filters/filters.js b/public/js/filters/filters.js index 222a8bb30a..e20db4aadf 100644 --- a/public/js/filters/filters.js +++ b/public/js/filters/filters.js @@ -11,7 +11,7 @@ angular.module('habitrpg') }) .filter('completedFilter', function(){ return function(tasks,_showCompleted) { - if (tasks[0].type != 'todo') return tasks; + if (!tasks[0] || tasks[0].type != 'todo') return tasks; return _.where(tasks, {completed:!!_showCompleted}); } }) \ No newline at end of file From 585f73630598f62757f6676f502bb7c093dc045a Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 29 Dec 2013 00:46:08 -0700 Subject: [PATCH 10/19] add markdown support for task.text so we can use icons in task titles. @wc8 does this satisfy https://trello.com/c/FCVdjdUd/102-task-reward-icons sufficiently? @lemoness --- views/shared/tasks/task.jade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index b9840263f1..62e23966d1 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -61,7 +61,7 @@ li(bindonce='list', ng-repeat='task in obj[list.type+"s"] | completedFilter: lis label(for='box-{{obj._id}}_{{task.id}}') // main content p.task-text - | {{task.text}} + markdown(ng-model='task.text',target='_blank') // edit/options dialog div(ng-if='task._editing') From c14e4c8fec62032bb068ae804a784f6f04428047 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 29 Dec 2013 00:47:39 -0700 Subject: [PATCH 11/19] display profile.stats.class in modal --- views/shared/modals/members.jade | 1 + 1 file changed, 1 insertion(+) diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 380254a13b..f0d71c8d93 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -14,6 +14,7 @@ div(ng-controller='MemberModalCtrl') li(ng-show='profile.auth.timestamps.created') - Member since {{timestamp(profile.auth.timestamps.created)}} - li(ng-show='profile.auth.timestamps.loggedin') - Last logged in {{timestamp(profile.auth.timestamps.loggedin)}} - h3 Stats + .label.label-info {{profile.stats.class}} p.alert.alert-info Coming back soon! //-include ../profiles/stats .span6 From e9cb9b8a9fbc795539378e4ca89c5c723eb37452 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 29 Dec 2013 01:24:02 -0700 Subject: [PATCH 12/19] task-icons: larger emoji in task text than chat --- public/css/tasks.styl | 5 +++++ views/shared/tasks/task.jade | 1 + 2 files changed, 6 insertions(+) diff --git a/public/css/tasks.styl b/public/css/tasks.styl index 1cd7709df9..78a02d007e 100644 --- a/public/css/tasks.styl +++ b/public/css/tasks.styl @@ -119,6 +119,11 @@ for $stage in $stages line-height: 1.4 word-wrap: break-word + span.emoji + width:1.5em + height:1.5em + background-size:1.5em + // task due date (for to-dos) .task-date font-size: 70% diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index 62e23966d1..5f4922267d 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -62,6 +62,7 @@ li(bindonce='list', ng-repeat='task in obj[list.type+"s"] | completedFilter: lis // main content p.task-text markdown(ng-model='task.text',target='_blank') + //-| {{task.text}} // edit/options dialog div(ng-if='task._editing') From 2f794724393fb9645196e9114222af9e721a84e6 Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 29 Dec 2013 11:04:31 -0600 Subject: [PATCH 13/19] Some work on demystification of RPG elements --- views/options/profile.jade | 12 ++++++++---- views/shared/modals/classes.jade | 6 +++--- views/shared/profiles/stats.jade | 31 +++++++++++++++++++++---------- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/views/options/profile.jade b/views/options/profile.jade index bbf97f5c93..41e76ebfd9 100644 --- a/views/options/profile.jade +++ b/views/options/profile.jade @@ -138,13 +138,17 @@ script(id='partials/options.profile.stats.html', type='text/ng-template') .border-right(ng-class='user.flags.classSelected && !user.preferences.disableClasses ? "span4" : "span6"') include ../shared/profiles/stats .span4.border-right.allocate-stats(ng-if='user.flags.classSelected && !user.preferences.disableClasses') - h4 - | {{user.stats.class}}  + h3 Character Build + h4 Class:  + span(ng-show="user.stats.class == 'warrior'") Warrior  + span(ng-show="user.stats.class == 'wizard'") Mage  + span(ng-show="user.stats.class == 'healer'") Healer  + span(ng-show="user.stats.class == 'rogue'") Rogue  a.btn.btn-danger.btn-mini(ng-click='changeClass(null)') - | Change & Re-Roll  + | Change Class, Refund Attribute Points  small 3 - h6 Points: {{user.stats.points}} + h5 Unallocated Attribute Points: {{user.stats.points}} fieldset.auto-allocate label.checkbox input(type='checkbox', ng-model='user.preferences.automaticAllocation', ng-change='set({"preferences.automaticAllocation": user.preferences.automaticAllocation?true: false})') diff --git a/views/shared/modals/classes.jade b/views/shared/modals/classes.jade index c4a4f898c9..6d36aba06e 100644 --- a/views/shared/modals/classes.jade +++ b/views/shared/modals/classes.jade @@ -1,6 +1,6 @@ .modal(ng-if='!user.flags.classSelected && user.stats.lvl >= 10', data-backdrop=true, ng-controller='UserCtrl') .modal-header - h3 Class System Unlocked! + h3 Choose Your Class! .modal-body.select-class p Select your class. Click each class for more information. See Wikia for thorough details. .row-fluid @@ -19,7 +19,7 @@ span(class='shield_warrior_5') span(class='weapon_warrior_6') .span3(ng-click='selectedClass = "wizard"') - h5 Wizard + h5 Mage figure.herobox(ng-class='{"selected-class": selectedClass=="wizard"}') .character-sprites span(class='skin_{{user.preferences.skin}}') @@ -62,7 +62,7 @@ span(class='weapon_healer_6') br .well(ng-show='selectedClass=="warrior"') Warriors deal damage to tasks (reducing redness), have moderate defense, and improved critical hits. - .well(ng-show='selectedClass=="wizard"') Wizards deal damage to tasks (reducing redness), can debuff tasks, and they gain experience rapidly. + .well(ng-show='selectedClass=="wizard"') Mages deal damage to tasks (reducing redness), can debuff tasks, and they gain experience rapidly. .well(ng-show='selectedClass=="rogue"') Rogues finds more drops and gold. They can also "go stealth" to avoid damage at the end of a day. .well(ng-show='selectedClass=="healer"') Healers have high defense against damage, and can heal themselves and other players in the party, as well as buff players. diff --git a/views/shared/profiles/stats.jade b/views/shared/profiles/stats.jade index 63b85944ae..e6c25bd009 100644 --- a/views/shared/profiles/stats.jade +++ b/views/shared/profiles/stats.jade @@ -3,7 +3,7 @@ table.table.table-striped tr(ng-repeat='(k,v) in user.items.gear.equipped', ng-init='piece=Content.gear.flat[v]', ng-show='piece') td strong {{piece.text}}:  - span(ng-repeat='stat in ["str","con","per","int"]', ng-show='piece[stat]') {{piece[stat]}}{{stat}}  + span(ng-repeat='stat in ["str","con","per","int"]', ng-show='piece[stat]') {{piece[stat]}} {{stat.toUpperCase()}}  h4 Stats table.table.table-striped @@ -31,19 +31,25 @@ table.table.table-striped strong {{v}}: {{profile._statsComputed[k]}} td ul - li Allocated: {{profile.stats[k] || 0}} - li - | Gear: {{Content.gear.flat[profile.items.gear.equipped.weapon][k] + Content.gear.flat[profile.items.gear.equipped.armor][k] + Content.gear.flat[profile.items.gear.equipped.head][k] + Content.gear.flat[profile.items.gear.equipped.shield][k] || 0}}  - i.icon-question-sign(popover-title='Class Gear', popover-trigger='mouseenter', popover-placement='right', popover="Wearing your class's gear gives that stat a 1.5% bonus, in addition to this number.") - li Level: {{(profile.stats.lvl - 1) / 2}} - li Buffs: {{profile.stats.buffs[k] || 0}} + li Level: {{(profile.stats.lvl - 1) / 2}}  + i.icon-question-sign(popover-title='Level Bonus', popover-trigger='mouseenter', popover-placement='right', popover="Each attribute gets a bonus equal to half your Level.") + li Equipment: {{Content.gear.flat[profile.items.gear.equipped.weapon][k] + Content.gear.flat[profile.items.gear.equipped.armor][k] + Content.gear.flat[profile.items.gear.equipped.head][k] + Content.gear.flat[profile.items.gear.equipped.shield][k] || 0}}  + i.icon-question-sign(popover-title='Equipment', popover-trigger='mouseenter', popover-placement='right', popover="Attribute bonuses provided by your equipped battle gear. See the Equipment tab under Inventory to select your battle gear.") + li Class Equip Bonus: {{profile._statsComputed[k] - profile.stats.buffs[k] - ((profile.stats.lvl - 1) / 2) - Content.gear.flat[profile.items.gear.equipped.weapon][k] - Content.gear.flat[profile.items.gear.equipped.armor][k] - Content.gear.flat[profile.items.gear.equipped.head][k] - Content.gear.flat[profile.items.gear.equipped.shield][k]}}  + i.icon-question-sign(popover-title='Class Equipment Bonus', popover-trigger='mouseenter', popover-placement='right', popover="Your class uses its own equipment more effectively than gear from other classes. Equipped gear from your current class gets a 50% boost to the attribute bonus it grants.") + li Allocated: {{profile.stats[k] || 0}}  + i.icon-question-sign(popover-title='Allocated Points', popover-trigger='mouseenter', popover-placement='right', popover="Attribute points you've earned and assigned. Assign points using the Character Build column.") + li Buffs: {{profile.stats.buffs[k] || 0}}  + i.icon-question-sign(popover-title='Buffs', popover-trigger='mouseenter', popover-placement='right', popover="Attribute bonuses provided by abilities you or your party members have used. The abilities you can use are found in the Rewards column on your Tasks page.") tr(ng-if='profile.stats.buffs.stealth') td - strong Stealth: {{profile.stats.buffs.stealth}} + strong Stealth: {{profile.stats.buffs.stealth}}  + i.icon-question-sign(popover-title='Stealth', popover-trigger='mouseenter', popover-placement='right', popover="When a new day begins, you will avoid damage from this many missed Dailies.") td tr(ng-if='profile.stats.buffs.streaks') td - strong Frozen Streaks: true + strong Streaks Frozen  + i.icon-question-sign(popover-title='Streaks Frozen', popover-trigger='mouseenter', popover-placement='right', popover="Streaks on missed Dailies will not reset at the end of the day.") td h4 Pets @@ -51,4 +57,9 @@ table.table.table-striped tr td strong Pets Found - | : {{profile.petCount}} \ No newline at end of file + | : {{profile.petCount}} +// Dunno why this doesn't work // +// tr + td + strong Mounts Tamed + | : {{profile.mountCount}} // From fd324fc220faa218ea0a5b4ee4861d48bd028a93 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 29 Dec 2013 12:18:29 -0700 Subject: [PATCH 14/19] classes: fix class-modal heads. Also, little class title display trick @sabrecat (tiny perf bonus) --- views/options/profile.jade | 5 +---- views/shared/modals/classes.jade | 8 ++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/views/options/profile.jade b/views/options/profile.jade index 41e76ebfd9..872b7d529f 100644 --- a/views/options/profile.jade +++ b/views/options/profile.jade @@ -140,10 +140,7 @@ script(id='partials/options.profile.stats.html', type='text/ng-template') .span4.border-right.allocate-stats(ng-if='user.flags.classSelected && !user.preferences.disableClasses') h3 Character Build h4 Class:  - span(ng-show="user.stats.class == 'warrior'") Warrior  - span(ng-show="user.stats.class == 'wizard'") Mage  - span(ng-show="user.stats.class == 'healer'") Healer  - span(ng-show="user.stats.class == 'rogue'") Rogue  + span {{ {warrior:'Warrior',wizard:'Mage',healer:'Healer',rogue:'Rogue'}[user.stats.class] }}  a.btn.btn-danger.btn-mini(ng-click='changeClass(null)') | Change Class, Refund Attribute Points  small 3 diff --git a/views/shared/modals/classes.jade b/views/shared/modals/classes.jade index 6d36aba06e..cc598ecac6 100644 --- a/views/shared/modals/classes.jade +++ b/views/shared/modals/classes.jade @@ -10,7 +10,7 @@ .character-sprites span(class='skin_{{user.preferences.skin}}') span(class='{{user.preferences.size}}_armor_warrior_5') - span(class='head_base_0') + span(class='head_0') span(class='hair_base_{{user.preferences.hair.base}}_{{user.preferences.hair.color}}') span(class='hair_bangs_{{user.preferences.hair.bangs}}_{{user.preferences.hair.color}}') span(class='hair_beard_{{user.preferences.hair.beard}}_{{user.preferences.hair.color}}') @@ -24,7 +24,7 @@ .character-sprites span(class='skin_{{user.preferences.skin}}') span(class='{{user.preferences.size}}_armor_wizard_5') - span(class='head_base_0') + span(class='head_0') span(class='hair_base_{{user.preferences.hair.base}}_{{user.preferences.hair.color}}') span(class='hair_bangs_{{user.preferences.hair.bangs}}_{{user.preferences.hair.color}}') span(class='hair_beard_{{user.preferences.hair.beard}}_{{user.preferences.hair.color}}') @@ -38,7 +38,7 @@ .character-sprites span(class='skin_{{user.preferences.skin}}') span(class='{{user.preferences.size}}_armor_rogue_5') - span(class='head_base_0') + span(class='head_0') span(class='hair_base_{{user.preferences.hair.base}}_{{user.preferences.hair.color}}') span(class='hair_bangs_{{user.preferences.hair.bangs}}_{{user.preferences.hair.color}}') span(class='hair_beard_{{user.preferences.hair.beard}}_{{user.preferences.hair.color}}') @@ -52,7 +52,7 @@ .character-sprites span(class='skin_{{user.preferences.skin}}') span(class='{{user.preferences.size}}_armor_healer_5') - span(class='head_base_0') + span(class='head_0') span(class='hair_base_{{user.preferences.hair.base}}_{{user.preferences.hair.color}}') span(class='hair_bangs_{{user.preferences.hair.bangs}}_{{user.preferences.hair.color}}') span(class='hair_beard_{{user.preferences.hair.beard}}_{{user.preferences.hair.color}}') From 2f4fd2facacb7b9a69151af7d6049d5546a199f7 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sun, 29 Dec 2013 17:13:39 -0700 Subject: [PATCH 15/19] fix #2163 add stats.maxMP in GET /api/v2/user. @russtaylor let me know if you need it in other locations --- src/controllers/user.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/user.js b/src/controllers/user.js index c0b2d84ad1..baf8a37055 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -144,6 +144,7 @@ api.getUser = function(req, res, next) { var user = res.locals.user.toJSON(); user.stats.toNextLevel = shared.tnl(user.stats.lvl); user.stats.maxHealth = 50; + user.stats.maxMP = res.locals.user._statsComputed.maxMP; delete user.apiToken; if (user.auth) { delete user.auth.hashed_password; From 1036f0549e738be60b21589238ee173313043c8d Mon Sep 17 00:00:00 2001 From: Sabe Jones Date: Sun, 29 Dec 2013 19:11:32 -0600 Subject: [PATCH 16/19] Add .idea and .git to nodemon ignore list, to reduce unnecessary server bounces during dev --- Gruntfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 3ea64fbf1f..6294aed2dc 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -57,7 +57,7 @@ module.exports = function(grunt) { nodemon: { dev: { - ignoredFiles: ['public/*', 'Gruntfile.js', 'views/*', 'build/*'] + ignoredFiles: ['public/*', 'Gruntfile.js', 'views/*', 'build/*', '.idea*', '.git*'] } }, From cb07bc9229f48810b5fd30ccca2d9f5526be3146 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 30 Dec 2013 12:16:42 +0100 Subject: [PATCH 17/19] allow multiple files for translations --- {locales => locales-old}/bg/app.json | 0 {locales => locales-old}/bg/app.original.json | 0 {locales => locales-old}/cs/app.json | 0 {locales => locales-old}/cu/app.json | 0 {locales => locales-old}/de/app.json | 0 {locales => locales-old}/el/app.json | 0 {locales => locales-old}/en/app.json | 0 {locales => locales-old}/es/app.json | 0 {locales => locales-old}/fr/app.json | 0 {locales => locales-old}/he/app.json | 0 {locales => locales-old}/id/app.json | 0 {locales => locales-old}/it/app.json | 0 {locales => locales-old}/ja/app.json | 0 {locales => locales-old}/nl/app.json | 0 {locales => locales-old}/no/app.json | 0 {locales => locales-old}/pl/app.json | 0 {locales => locales-old}/pt/app.json | 0 {locales => locales-old}/ru/app.json | 0 {locales => locales-old}/sl/app.json | 0 {locales => locales-old}/uk/app.json | 0 {locales => locales-old}/ur/app.json | 0 locales/en/app.js | 13 + locales/en/main.json | 417 ++++++++++++++++++ locales/en/secondary.json | 3 + src/middleware.js | 2 +- 25 files changed, 434 insertions(+), 1 deletion(-) rename {locales => locales-old}/bg/app.json (100%) rename {locales => locales-old}/bg/app.original.json (100%) rename {locales => locales-old}/cs/app.json (100%) rename {locales => locales-old}/cu/app.json (100%) rename {locales => locales-old}/de/app.json (100%) rename {locales => locales-old}/el/app.json (100%) rename {locales => locales-old}/en/app.json (100%) rename {locales => locales-old}/es/app.json (100%) rename {locales => locales-old}/fr/app.json (100%) rename {locales => locales-old}/he/app.json (100%) rename {locales => locales-old}/id/app.json (100%) rename {locales => locales-old}/it/app.json (100%) rename {locales => locales-old}/ja/app.json (100%) rename {locales => locales-old}/nl/app.json (100%) rename {locales => locales-old}/no/app.json (100%) rename {locales => locales-old}/pl/app.json (100%) rename {locales => locales-old}/pt/app.json (100%) rename {locales => locales-old}/ru/app.json (100%) rename {locales => locales-old}/sl/app.json (100%) rename {locales => locales-old}/uk/app.json (100%) rename {locales => locales-old}/ur/app.json (100%) create mode 100644 locales/en/app.js create mode 100644 locales/en/main.json create mode 100644 locales/en/secondary.json diff --git a/locales/bg/app.json b/locales-old/bg/app.json similarity index 100% rename from locales/bg/app.json rename to locales-old/bg/app.json diff --git a/locales/bg/app.original.json b/locales-old/bg/app.original.json similarity index 100% rename from locales/bg/app.original.json rename to locales-old/bg/app.original.json diff --git a/locales/cs/app.json b/locales-old/cs/app.json similarity index 100% rename from locales/cs/app.json rename to locales-old/cs/app.json diff --git a/locales/cu/app.json b/locales-old/cu/app.json similarity index 100% rename from locales/cu/app.json rename to locales-old/cu/app.json diff --git a/locales/de/app.json b/locales-old/de/app.json similarity index 100% rename from locales/de/app.json rename to locales-old/de/app.json diff --git a/locales/el/app.json b/locales-old/el/app.json similarity index 100% rename from locales/el/app.json rename to locales-old/el/app.json diff --git a/locales/en/app.json b/locales-old/en/app.json similarity index 100% rename from locales/en/app.json rename to locales-old/en/app.json diff --git a/locales/es/app.json b/locales-old/es/app.json similarity index 100% rename from locales/es/app.json rename to locales-old/es/app.json diff --git a/locales/fr/app.json b/locales-old/fr/app.json similarity index 100% rename from locales/fr/app.json rename to locales-old/fr/app.json diff --git a/locales/he/app.json b/locales-old/he/app.json similarity index 100% rename from locales/he/app.json rename to locales-old/he/app.json diff --git a/locales/id/app.json b/locales-old/id/app.json similarity index 100% rename from locales/id/app.json rename to locales-old/id/app.json diff --git a/locales/it/app.json b/locales-old/it/app.json similarity index 100% rename from locales/it/app.json rename to locales-old/it/app.json diff --git a/locales/ja/app.json b/locales-old/ja/app.json similarity index 100% rename from locales/ja/app.json rename to locales-old/ja/app.json diff --git a/locales/nl/app.json b/locales-old/nl/app.json similarity index 100% rename from locales/nl/app.json rename to locales-old/nl/app.json diff --git a/locales/no/app.json b/locales-old/no/app.json similarity index 100% rename from locales/no/app.json rename to locales-old/no/app.json diff --git a/locales/pl/app.json b/locales-old/pl/app.json similarity index 100% rename from locales/pl/app.json rename to locales-old/pl/app.json diff --git a/locales/pt/app.json b/locales-old/pt/app.json similarity index 100% rename from locales/pt/app.json rename to locales-old/pt/app.json diff --git a/locales/ru/app.json b/locales-old/ru/app.json similarity index 100% rename from locales/ru/app.json rename to locales-old/ru/app.json diff --git a/locales/sl/app.json b/locales-old/sl/app.json similarity index 100% rename from locales/sl/app.json rename to locales-old/sl/app.json diff --git a/locales/uk/app.json b/locales-old/uk/app.json similarity index 100% rename from locales/uk/app.json rename to locales-old/uk/app.json diff --git a/locales/ur/app.json b/locales-old/ur/app.json similarity index 100% rename from locales/ur/app.json rename to locales-old/ur/app.json diff --git a/locales/en/app.js b/locales/en/app.js new file mode 100644 index 0000000000..944ffc2cf7 --- /dev/null +++ b/locales/en/app.js @@ -0,0 +1,13 @@ +var _ = require('lodash'); + +var files = [ + // List of files containing translations + require('./main.json'), + require('./secondary.json') +]; + +module.exports = {}; + +_.each(files, function(file){ + _.merge(module.exports, file); +}); \ No newline at end of file diff --git a/locales/en/main.json b/locales/en/main.json new file mode 100644 index 0000000000..0c0fa858bb --- /dev/null +++ b/locales/en/main.json @@ -0,0 +1,417 @@ +{ + + "languageName": "English", + "stringNotFound": "String not found.", + + +"_commentfrontpage":"HABITRPG FRONT PAGE", + "synopsis" : "A habit building program which treats your life like a Role Playing Game. Level up as you succeed, lose HP as you fail, earn money to buy weapons and armor.", + "playButton" : "Play", + + + +"_commenttut": "TUTORIAL/TOUR", + "endTourButton": "End Tour", + "nextButton" : "Next", + "prevButton" : "Prev", + "tour1Title" : "Welcome to HabitRPG", + "tour1Text" : "Welcome to HabitRPG, a habit-tracker which treats your goals like a Role Playing Game.", + "tour2Title" : "Habits", + "tour2Text" : "Habits are good or bad goals that you constantly track.", + "tour3Title" : "Dailies", + "tour3Text" : "Dailies are goals that you want to complete once a day.", + "tour4Title" : "Todos", + "tour4Text" : "Todos are one-off goals which need to be completed eventually. ", + "tour5Title" : "Rewards", + "tour5Text" : "As you complete goals, you earn gold to buy rewards. Buy them liberally - rewards are integral in forming good habits. ", + "tour6Title" : "Hover over comments", + "tour6Text" : "Different task-types have special properties. Hover over each task's comment for more information. When you're ready to get started, delete the existing tasks and add your own.", + + + +"_commentdefaulttasks":"DEFAULT TASKS", + "habit1" : "1h Productive Work", + "habit1comment": "-- Habits: Constantly Track -- For some habits, it only makes sense to *gain* points (like this one)", + "habit2" : "Eat Junk Food", + "habit2comment" : "For others, it only makes sense to *lose* points", + "habit3" : "Take The Stairs", + "habit3comment" : "For the rest, both + and - make sense (stairs = gain, elevator = lose)", + "daily1" : "1h Personal Project", + "daily1comment" : "-- Dailies: Complete Once a Day -- At the end of each day, non-completed Dailies dock you points.", + "daily2" : "Exercise", + "daily2comment" : "If you are doing well, they turn green and are less valuable (experience, gold) and less damaging (HP). This means you can ease up on them for a bit.", + "daily3" : "45m Reading", + "daily3comment" : "But if you are doing poorly, they turn red. The worse you do, the more valuable (exp, gold) and more damaging (HP) these goals become. This encourages you to focus on your shortcomings, the reds.", + "todo1" : "Call Mom", + "todo1comment" : "-- Todos: Complete Eventually -- Non-completed Todos won't hurt you, but they will become more valuable over time. This will encourage you to wrap up stale Todos.", + "reward1" : "1 Episode of Game of Thrones", + "reward1comment": "-- Rewards: Treat Yourself! -- As you complete goals, you earn gold to buy rewards. Buy them liberally - rewards are integral in forming good habits.", + "reward2" : "Cake", + "reward2comment" : "But only buy if you have enough gold!", + +"_commentdefaulttags" : "DEFAULT TAGS", + "morning" : "morning", + "afternoon" : "afternoon", + "evening" : "evening", + +"_commenthead": "HEADER", + "health": "Health", + "experience": "Experience", + "history": "History", + "anonymous": "Anonymous", + "level": "Level", + "tasks": "Tasks", + "loginAndReg" : "Login / Register", + "loginFacebookAlt" : "Login / Register with Facebook", + "login" : "Login", + "register" : "Register", + "options": "Options", + "logout": "Logout", + +"_commentnotifcations" : "NOTIFICATIONS", + "partyNotification" : "New Party Messages", + + + +"_commenttaskview": "TASK VIEW", + "habits": "Habits", + "Habits": "Habits", + "newHabit": "New Habit", + "edit": "Edit", + "text": "Text", + "extraNotes": "Extra Notes", + "directions/Actions": "Directions/Actions", + "advancedOptions": "Advanced Options", + "difficulty": "Difficulty", + "difficultyHelpTitle": "How difficult is this task?", + "difficultyHelpContent": "This multiplies its point value. Use sparingly, rely instead on our organic value-adjustment algorithms. But some tasks are grossly more valuable (Write Thesis vs Floss Teeth). Click for more info.", + "easy": "Easy", + "medium": "Medium", + "hard": "Hard", + "delete": "Delete", + "progress": "Progress", + "score": "Score", + "dailies": "Dailies", + "Dailies": "Dailies", + "newDaily": "New Daily", + "repeat": "Repeat", + "Su": "Su", + "M": "M", + "T": "T", + "We": "W", + "Th": "Th", + "F": "F", + "S": "S", + "todos": "To-Dos", + "Todos": "To-Dos", + "newTodo": "New To-Do", + "dueDate": "Due Date", + "remaining": "Remaining", + "complete": "Complete", + "rewards": "Rewards", + "Rewards": "Rewards", + "gold": "Gold", + "silver": "Silver", + "newReward": "New Reward", + "price": "Price", + "tags" : "Tags", + "editTags" : "Edit Tags", + "newTag" : "New Tag", + "clearFilters" : "Clear Filters", + + +"_commentdrops" : "DROP SYSTEM", + "dropsEnabled" : "Drops Enabled!", + "dropsEnabledText1" : "You've unlocked the Drop System! Now when you complete tasks, you have a small chance of finding an item. And guess what, you just found a", + "dropsEnabledText2" : "egg", + "itemDropped" : "An item has dropped", + + + +"_commentoptionsview": "OPTIONS VIEW", + "profile": "Profile", + "avatar": "Avatar", + + "head": "Head", + "showHelm": "Show Helm", + "hair": "Hair", + "skin": "Skin", + "clothing" : "Clothing", + "showWeapon": "Show Weapon", + "other" : "Other", + "showShield": "Show Shield", + "showArmor": "Show Armor", + "photoUrl": "Photo Url", + "fullName": "Full Name", + "blurb": "Blurb", + "items" : "Items", + "weapon" : "Weapon", + "armor" : "armor", + "helm" : "Helm", + "shield" : "shield", + + "stats" : "Stats", + "strength" : "Strength", + "defense" : "Defense", + "totalStrength" : "Total Strength", + "totalDefense" : "Total Defense", + + + "party": "Party", + "createAParty":"Create A Party", + "noPartyText": "You are not in a party. You can either create one and invite friends, or if you want to join an existing party, have them enter:", + "partyName": "Party Name", + "create":"Create", + "userId":"User Id", + "invite":"Invite", + "leave": "Leave", + "invitedTo" : "You're Invited To", + "chat": "Chat", + + "inventory": "Inventory", + "eggs": "Eggs", + "noEggs": "You don't have any eggs yet.", + "hatchingPotions": "Hatching Potions", + "noHatchingPotions": "You don't have any hatching potions yet.", + "hatchYourEgg" : "Hatch Your Egg", + "whichHatchingPotion1" : "Which hatching potion will you pour on your", + "whichHatchingPotion2" : "egg?", + "pour" : "pour", + "rarePets" : "Rare Pets", + "market": "Market", + + "stable": "Stable", + "petsFound":"Pets Found", + "rarePets": "Rare Pets", + + "tavern": "Tavern", + "restButton":"Rest In The Inn", + "checkoutButton": "Check Out Of Inn", + "tavernTalkTitle":"Tavern Talk & LFG", + "tavernRestingInfo" : "Whilst resting your dailies are saved and aren't effected by day turn-over. Whether you check out tomorrow or in a weeks time you'll continue in the same state as when you checked in.", + "resources" : "Resources", + "LFGPosts" : "LGF Posts", + "tutorials" : "Tutorials", + + "achievements":"Achievements", + "achievementUnlocked" : "Achievement Unlocked!", + "npcText" : "Backed the Kickstarter project at the maximum level!", + "contribName" : "Contributor", + "contribText" : "Has contributed to HabitRPG (code, design, pixel art, legal advice, docs, etc). Want this badge? Fix a bug", + "kickstartName1" : "Kickstarter Backer - ", + "kickstartName2" : "Tier", + "kickstartText" : "Backed the Kickstarter Project", + "streakName" : " Streak Achievement(s)", + "streakText1" : "Has performed ", + "stureakText2" : " 21-day streaks on Dailies", + "origUserName" : "Original User", + "origUserText" : "Goes way back to the Habit's days of yore (bless them for soldiering through bugs!)", + "ultimGearName" : "Ultimate Gear", + "ultimGearText" : "Has attained the maximum weapon and armor set", + "ultimGearUnlocked" : "You have earned the 'Ultimate Gear' Achievement for upgrading to the maximum gear set!", + "beastMastName" : "Beast Master", + "beastMastText" : "Has found all 90 pets (insanely difficult, give this user props!)", + "beastMastUnlocked" : "You have earned the 'Beast Master' Achievement for collecting all the pets!", + + "settings":"Settings", + "customDayStart":"Custom Day Start", + "24HrClock": "24Hr Clock", + "clockInfo":"HabitRPG defaults to check and reset your dailies at midnight each day. You can customize that here (Enter number between 0 and 24).", + "misc":"Misc", + "hideHeader":"Hide Header", + "showHeader":"Show Header", + "changePass":"Change Password", + "oldPass":"Old Password", + "newPass":"New Password", + "confirmPass":"Confirm New Password", + "dangerZone": "Danger Zone", + "reset":"Reset", + "resetAltText":"Resets your entire account (dangerous)", + "resetText1":"This resets your entire account - your tasks will be deleted and your character will start over.", + "resetText2":"This is highly discouraged because you'll lose historical data, which is useful for graphing your progress over time. However, some people find it useful in the beginning after playing with the app for a while.", + "restore":"Restore", + "restoreAltText":"Restores attributes to your character", + "restoreText1":"HabitRPG is quite Beta-quality at present, and many find they need to restore character attributes as a result. Enter your numbers here and it will be applied automatically to your character. This will be removed once Habit is more stable.", + "delete":"Delete", + "deleteAltText":"Delete your account", + "deleteHeader" : "Delete Account", + "deleteText1":"Woa woa woa! Are you sure? This will seriously delete your account forever, and it can never be restored. If you're absolutely certain, type", + "deleteText2":" DELETE ", + "deleteText3":"into the text-box", + "API":"API", + "APIText":"Copy these for use in third party applications.", + "APIToken":"Api Token", + + + +"_commentitems": "ITEMS", + "_commentitemsweps": "WEAPONS", + "sword0name" : "Training Sword", + "sword1Name" : "Sword", + "sword1Text" : "Increases experience gain by 3%.", + "sword2Name" : "Axe", + "sword2Text" : "Increases experience gain by 6%.", + "sword3Name" : "Morningstar", + "sword3Text" : "Increases experience gain by 9%.", + "sword4Name" : "Blue Sword", + "sword4Text" : "Increases experience gain by 12%.", + "sword5Name" : "Red Sword", + "sword5Text" : "Increases experience gain by 15%.", + "sword6Name" : "Golden Sword", + "sword6Text" : "Increases experience gain by 18%.", + + "_commentitemsarmor": "ARMOR", + "armor0Name" : "Cloth Armor", + "armor1Name" : "Leather Armor" , + "armor1Text": "Decreases HP Loss by 4%", + "armor2Name" : "Chain Mail", + "armor2Text": "Decreases HP Loss by 6%", + "armor3Name" : "Plate Mail", + "armor3Text": "Decreases HP Loss by 7%", + "armor4Name" : "Red Armor", + "armor4Text": "Decreases HP Loss by 8%", + "armor5Name" : "Golden Armor", + "armor5Text": "Decreases HP Loss by 10%", + + "_commentitemshead" : "HEADGEAR", + "head0Name" : "No Helm", + "head1Name" : "Leather Helm" , + "head1Text": "Decreases HP loss by 2%", + "head2Name" : "Chain Coif", + "head2Text": "Decreases HP loss by 3%", + "head3Name" : "Plate Helm", + "head3Text": "Decreases HP loss by 4%", + "head4Name" : "Red Helm", + "head4Text": "Decreases HP loss by 5%", + "head5Name" : "Golden Helm", + "head5Text": "Decreases HP loss by 6%", + + "_commentitemsshield" : "SHIELDS", + "sheild0Name" : "No Shield", + "sheild1Name" : "Wooden Shield", + "sheild1Text": "Decreases HP loss by 3%", + "sheild2Name" : "Buckler", + "sheild2Text": "Decreases HP loss by 4%", + "sheild3Name" : "Reinforced Shield", + "sheild3Text": "Decreases HP loss by 5%", + "sheild4Name" : "Red Shield", + "sheild4Text": "Decreases HP loss by 7%", + "sheild5Name" : "Golden Shield", + "sheild5Text": "Decreases HP loss by 8%", + + "_commentitemsother" : "OTHER ITEMS", + "healthPotionName" : "Health Potion", + "healthPotionNotes" : "Recover 15 HP Instantly", + "rerollName" : "Fortify Potion", + "rerollNotes" : "Resets your task values back to yellow. Useful when everything's red and it's hard to stay alive.", + "rerollModelHeader" : "Reset Your Tasks", + "rerollModelText1" : "Highly discouraged because red tasks provide good incentive to improve", + "rerollModelText2" : "read more", + "rerollModelText3" : "However, this becomes necessary after long bouts of bad habits.", + + "_commentitemspeteggs": "PET EGGS", + "wolfEgg": "Wolf Cub", + "tigerEgg": "Tiger Cub", + "pandaEgg": "Panda Cub", + "lionEgg": "Lion Cub", + "foxEgg": "Fox", + "pigEgg": "Flying Pig", + "dragonEgg": "Dragon", + "cactusEgg": "Cactus", + "bearEgg": "Bear Cub", + + "_commentitemshatchingpotions": "HATCHING POTIONS", + "basePotName": "Base", + "basePotText" : "Hatches your pet into its base form.", + "whitePotName": "White", + "whitePotText" : "Hatches your pet into its white form.", + "desertPotName": "Desert", + "desertPotText" : "Hatches your pet into its desert form.", + "redPotName": "Red", + "redPotText" : "Hatches your pet into its red form.", + "shadePotName": "Shadepot", + "shadePotText" : "Hatches your pet into its shade form.", + "skeletonPotName": "Skeleton", + "skeletonPotText" : "Hatches your pet into its skeleton form.", + "zombiePotName": "Zombie", + "zombiePotText" : "Hatches your pet into its zombie form.", + "cottonPinkPotName": "Cotton Candy Pink", + "cottonPinkPotText" : "Hatches your pet into its cotton candy pink form.", + "cottonBluePotName": "Cotton Candy Blue", + "cottonBluePotText" : "Hatches your pet into its cotton candy blue form.", + "goldenPotName": "Golden", + "goldenPotText" : "Hatches your pet into its golden form.", + + + +"_commentnpcsandchars": "NPCS & CHARACTERS", + "_commentNPCS" : "NPCS", + "NPCBaileyText1" : "the Town Crier here! Announcing new stuff!", + "NPCAugustinText1" : "Welcome to the Market! I'm the merchant;", + "NPCAugustinText2" : "Dying to get that particular pet you're after, but don't want to wait for it to drop? Buy it here!", + "NPCJohanssonText1" : "Welcome to the Tavern! I'm", + "NPCJohanssonText2": "the bar keep. If you want to rest a while (going on vacation? sudden illness?), I'll set you up at the inn - dailies won't hurt you while you're resting. Stay a while & meet the locals.", + "NPCMelchiorText1" : "", + "NPCBowenText1" : "", + "NPCBochText1" : "", + +"_commentdeathstuff" : "DEATH", + "deathTitle" : "You Died!", + "deathText" : "You've lost your Gold, 1 Level, and 1 piece of Equipment. Be sure to complete your Dailies to prevent this from happening again!", + +"_commentgems" : "GEMS", + "gems" : "Gems", + "outOfGems" : "Out Of Gems", + "buyMoreGems" : "Buy More Gems", + "notEnoughGems" : "Not enough Gems", + "petsOutOfGems" : "Oops, out of Gems, which are used to buy special items! Habit is an open source project, and can use all the help it can get - buy more Gems to receive this pet, and consider it a donation to the contributors", + "gemsWhatFor" : "Used for buying special items (reroll, eggs, hatching potions, etc). You'll need to unlock those features before being able to use Gems.", + +"_commentfooter": "FOOTER", + "footerCompany" : "Company", + "companyAbout" : "About", + "companyBlog" : "Blog", + "companyTeam" : "Team", + "companyExtensions" : "Extensions", + "companyFAQ" : "FAQ", + "footerLegal" : "Legal", + "legalPrivacy" : "Privacy", + "legalTerms" : "Terms", + "footerCommunity" : "Community", + "communityBugs" : "Submit Bugs", + "communityFeatures" : "Request Features", + "communityExtensions" : "Add-ons / Extensions", + "communityForum" : "Community Forum", + "footerSocial" : "Social", + + + +"_commentmisc": "MISC & GLOBAL", + "removeAds": "Remove Ads", + "whyAds": "Why Ads?", + "whyAdsContent1": "Habit is an open source project, and can use all the help it can get - consider this a donation to the contributors. You also get 20 Gems from the purchase, which you can use to buy special items.", + + "whyAdsContent2": "'Hey, I backed the Kickstarter!' - follow", + "whyAdsContent3": "these instructions", + "_commentbuttons": "BUTTONS", + "submit":"Submit", + "close":"Close", + "saveAndClose": "Save & Close", + "cancel":"Cancel", + "ok" : "Ok", + "add" : "Add", + "undo" : "Undo", + "continue" : "Continue", + "accept" : "Accept", + "reject" : "Reject", + "or" : "Or", + "_commenttimestamps":"TIME STAMPS", + "justNow":"Just now", + "aMinuteAgo":"A minute ago", + "minutesAgo":"minutes ago", + "anHourAgo": "An hour ago", + "hoursAgo": "hours ago", + "yesterday":"Yesterday", + "daysAgo":"days ago" + +} diff --git a/locales/en/secondary.json b/locales/en/secondary.json new file mode 100644 index 0000000000..7ce0a73edb --- /dev/null +++ b/locales/en/secondary.json @@ -0,0 +1,3 @@ +{ + "secondaryString": "an example" +} \ No newline at end of file diff --git a/src/middleware.js b/src/middleware.js index 68fa4be14f..25df855d22 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -94,7 +94,7 @@ var getManifestFiles = function(page){ var translations = {}; var loadTranslations = function(locale){ - translations[locale] = require(path.join(__dirname, "/../locales/", locale, 'app.json')); + translations[locale] = require(path.join(__dirname, "/../locales/", locale, 'app.js')); } // First fetch english so we can merge with missing strings in other languages From 34319bdcd3c138c34b10f60a9e916dd679c56264 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 30 Dec 2013 12:23:23 +0100 Subject: [PATCH 18/19] improved multiple files translations --- locales/en/app.js | 13 ------------- locales/en/app.json | 6 ++++++ src/middleware.js | 8 +++++++- 3 files changed, 13 insertions(+), 14 deletions(-) delete mode 100644 locales/en/app.js create mode 100644 locales/en/app.json diff --git a/locales/en/app.js b/locales/en/app.js deleted file mode 100644 index 944ffc2cf7..0000000000 --- a/locales/en/app.js +++ /dev/null @@ -1,13 +0,0 @@ -var _ = require('lodash'); - -var files = [ - // List of files containing translations - require('./main.json'), - require('./secondary.json') -]; - -module.exports = {}; - -_.each(files, function(file){ - _.merge(module.exports, file); -}); \ No newline at end of file diff --git a/locales/en/app.json b/locales/en/app.json new file mode 100644 index 0000000000..5c889549fb --- /dev/null +++ b/locales/en/app.json @@ -0,0 +1,6 @@ +{ + "files": [ + "main.json", + "secondary.json" + ] +} \ No newline at end of file diff --git a/src/middleware.js b/src/middleware.js index 25df855d22..dc23e72161 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -94,7 +94,11 @@ var getManifestFiles = function(page){ var translations = {}; var loadTranslations = function(locale){ - translations[locale] = require(path.join(__dirname, "/../locales/", locale, 'app.js')); + var files = require(path.join(__dirname, "/../locales/", locale, 'app.json')).files; + translations[locale] = {}; + _.each(files, function(file){ + _.merge(translations[locale], require(path.join(__dirname, "/../locales/", locale, file))); + }); } // First fetch english so we can merge with missing strings in other languages @@ -107,6 +111,8 @@ fs.readdirSync(path.join(__dirname, "/../locales")).forEach(function(file) { _.defaults(translations[file], translations.en); }); +console.log(translations) + var langCodes = Object.keys(translations); var avalaibleLanguages = _.map(langCodes, function(langCode){ From c7e93f77931f7ad73ed5eb285b174f3155dfaaee Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 30 Dec 2013 12:28:30 +0100 Subject: [PATCH 19/19] remove console.log --- src/middleware.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/middleware.js b/src/middleware.js index dc23e72161..d959e3a87c 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -111,8 +111,6 @@ fs.readdirSync(path.join(__dirname, "/../locales")).forEach(function(file) { _.defaults(translations[file], translations.en); }); -console.log(translations) - var langCodes = Object.keys(translations); var avalaibleLanguages = _.map(langCodes, function(langCode){