diff --git a/public/js/app.js b/public/js/app.js index ded963b44e..fcdcc1a5d3 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -17,7 +17,7 @@ window.env.t = function(string){ window.habitrpg = angular.module('habitrpg', ['ngResource', 'ngSanitize', 'userServices', 'groupServices', 'memberServices', 'challengeServices', - 'sharedServices', 'authServices', 'notificationServices', 'guideServices', + 'authServices', 'notificationServices', 'guideServices', 'ui.bootstrap', 'ui.keypress', 'ui.router', 'chieffancypants.loadingBar', 'At', 'pasvaz.bindonce']) // @see https://github.com/angular-ui/ui-router/issues/110 and https://github.com/HabitRPG/habitrpg/issues/1705 diff --git a/public/js/controllers/challengesCtrl.js b/public/js/controllers/challengesCtrl.js index 82854bade3..93ef91e1d3 100644 --- a/public/js/controllers/challengesCtrl.js +++ b/public/js/controllers/challengesCtrl.js @@ -132,7 +132,7 @@ habitrpg.controller("ChallengesCtrl", ['$scope', 'User', 'Challenges', 'Notifica //------------------------------------------------------------ $scope.addTask = function(addTo, listDef) { - var task = window.habitrpgShared.helpers.taskDefaults({text: listDef.newTask, type: listDef.type}); + var task = $rootScope.Shared.taskDefaults({text: listDef.newTask, type: listDef.type}); addTo.unshift(task); //User.log({op: "addTask", data: task}); //TODO persist delete listDef.newTask; diff --git a/public/js/controllers/filtersCtrl.js b/public/js/controllers/filtersCtrl.js index 3839074a6c..9ec425d568 100644 --- a/public/js/controllers/filtersCtrl.js +++ b/public/js/controllers/filtersCtrl.js @@ -22,7 +22,7 @@ habitrpg.controller("FiltersCtrl", ['$scope', '$rootScope', 'User', 'API_URL', ' $scope.createTag = function(name) { user.tags = user.tags || []; user.tags.push({ - id: window.habitrpgShared.helpers.uuid(), + id: $rootScope.Shared.uuid(), name: name }); User.log({op:'set',data:{'tags':user.tags}}); diff --git a/public/js/controllers/footerCtrl.js b/public/js/controllers/footerCtrl.js index 401c40b9fc..4f15ba55b0 100644 --- a/public/js/controllers/footerCtrl.js +++ b/public/js/controllers/footerCtrl.js @@ -47,17 +47,16 @@ habitrpg.controller("FooterCtrl", ['$scope', '$rootScope', 'User', '$http', 'Not $scope.addMissedDay = function(){ if (!confirm("Are you sure you want to reset the day?")) return; var dayBefore = moment(User.user.lastCron).subtract('days', 1).toDate(); - User.set('lastCron', dayBefore); + User.set({'lastCron': dayBefore}); Notification.text('-1 day, remember to refresh'); } $scope.addTenGems = function(){ - console.log(API_URL); $http.post(API_URL + '/api/v1/user/addTenGems').success(function(){ User.log({}); }) } $scope.addLevelsAndGold = function(){ - User.setMultiple({ + User.set({ 'stats.exp': User.user.stats.exp + 10000, 'stats.gp': User.user.stats.gp + 10000 }); diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index 09133c3738..793698d465 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -66,7 +66,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Groups', '$http', 'A // We watch Members.selectedMember because it's asynchronously set, so would be a hassle to handle updates here $scope.$watch( function() { return Members.selectedMember; }, function (member) { if(member) - member.petCount = window.habitrpgShared.helpers.countPets(null, member.items.pets); + member.petCount = $rootScope.Shared.countPets(null, member.items.pets); $scope.profile = member; }); } diff --git a/public/js/controllers/inventoryCtrl.js b/public/js/controllers/inventoryCtrl.js index 3e94a7f001..71dd6271df 100644 --- a/public/js/controllers/inventoryCtrl.js +++ b/public/js/controllers/inventoryCtrl.js @@ -2,13 +2,13 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User', 'API_URL', function($rootScope, $scope, User, API_URL, $http, Notification) { var user = User.user; - var Items = window.habitrpgShared.items; + var Content = $rootScope.Shared.content; // convenience vars since these are accessed frequently $scope.selectedEgg = null; // {index: 1, name: "Tiger", value: 5} $scope.selectedPotion = null; // {index: 5, name: "Red", value: 3} - $scope.totalPets = _.size($scope.Items.eggs) * _.size($scope.Items.hatchingPotions); + $scope.totalPets = _.size(Content.eggs) * _.size(Content.hatchingPotions); // count egg, food, hatchingPotion stack totals var countStacks = function(items) { return _.reduce(items,function(m,v){return m+v;},0);} @@ -20,10 +20,10 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User', 'API_URL', $scope.$watch('user.items.gear', function(gear){ $scope.gear = { - base: _.where(Items.items.gear.flat, {klass: 'base'}) + base: _.where(shared.content.gear.flat, {klass: 'base'}) }; _.each(gear.owned, function(bool,key){ - var item = Items.items.gear.flat[key]; + var item = shared.content.gear.flat[key]; if (!$scope.gear[item.klass]) $scope.gear[item.klass] = []; $scope.gear[item.klass].push(item); }) @@ -33,7 +33,7 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User', 'API_URL', if ($scope.selectedEgg && $scope.selectedEgg.name == egg) { return $scope.selectedEgg = null; // clicked same egg, unselect } - var eggData = _.findWhere(Items.items.eggs, {name:egg}); + var eggData = _.findWhere(shared.content.eggs, {name:egg}); if (!$scope.selectedPotion) { $scope.selectedEgg = eggData; } else { @@ -46,7 +46,7 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User', 'API_URL', return $scope.selectedPotion = null; // clicked same egg, unselect } // we really didn't think through the way these things are stored and getting passed around... - var potionData = _.findWhere(Items.items.hatchingPotions, {name:potion}); + var potionData = _.findWhere(shared.content.hatchingPotions, {name:potion}); if (!$scope.selectedEgg) { $scope.selectedPotion = potionData; } else { @@ -56,42 +56,18 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User', 'API_URL', $scope.chooseFood = function(food){ if ($scope.selectedFood && $scope.selectedFood.name == food) return $scope.selectedFood = null; - $scope.selectedFood = $scope.Items.food[food]; + $scope.selectedFood = Content.food[food]; } $scope.sellInventory = function() { - // TODO DRY this - if ($scope.selectedEgg) { - user.items.eggs[$scope.selectedEgg.name]--; - User.setMultiple({ - 'items.eggs': user.items.eggs, - 'stats.gp': User.user.stats.gp + $scope.selectedEgg.value - }); - if (user.items.eggs[$scope.selectedEgg.name] < 1) { - $scope.selectedEgg = null; - } - - } else if ($scope.selectedPotion) { - user.items.hatchingPotions[$scope.selectedPotion.name]--; - User.setMultiple({ - 'items.hatchingPotions': user.items.hatchingPotions, - 'stats.gp': User.user.stats.gp + $scope.selectedPotion.value - }); - if (user.items.hatchingPotions[$scope.selectedPotion.name] < 1) { - $scope.selectedPotion = null; - } - - } else if ($scope.selectedFood) { - user.items.food[$scope.selectedFood.name]--; - User.setMultiple({ - 'items.food': user.items.food, - 'stats.gp': User.user.stats.gp + $scope.selectedFood.value - }); - if (user.items.food[$scope.selectedFood.name] < 1) { - $scope.selectedFood = null; + var selected = $scope.selectedEgg ? 'selectedEgg' : $scope.selectedPotion ? 'selectedPotion' : $scope.selectedFood ? 'selectedFood' : undefined; + if (selected) { + var type = $scope.selectedEgg ? 'eggs' : $scope.selectedPotion ? 'hatchingPotions' : $scope.selectedFood ? 'food' : undefined; + user.ops.sell({query:{type:type, key: $scope[selected].name}}); + if (user.items[type][$scope[selected].name] < 1) { + $scope[selected] = null; } } - } $scope.ownedItems = function(inventory){ @@ -99,21 +75,15 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User', 'API_URL', } $scope.hatch = function(egg, potion){ - var pet = egg.name+"-"+potion.name; - if (user.items.pets[pet]) - return alert("You already have that pet. Try hatching a different combination!"); - - var setObj = {}; - setObj['items.pets.' + pet] = 5; - setObj['items.eggs.' + egg.name] = user.items.eggs[egg.name] - 1; - setObj['items.hatchingPotions.' + potion.name] = user.items.hatchingPotions[potion.name] - 1; - - User.setMultiple(setObj); - - alert("Your egg hatched! Visit your stable to equip your pet."); - - $scope.selectedEgg = null; - $scope.selectedPotion = null; + user.ops.hatch({query:{egg:egg.name, hatchingPotion:potion.name}}, function(err, req){ + // Bypassing the UserServices-injected callback so we can only show alert on success. It's safe, since this means + // UserServices callback will be 3rd param and never get called + if (err) return Notification.text(err); + User.log({op:'hatch', query:req.query}); + Notification.text("Your egg hatched! Visit your stable to equip your pet."); + $scope.selectedEgg = null; + $scope.selectedPotion = null; + }); } $scope.buy = function(type, item){ @@ -134,7 +104,7 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User', 'API_URL', // Feeding Pet if ($scope.selectedFood) { - if (window.habitrpgShared.items.items.specialPets[pet]) return Notification.text("Can't feed this pet."); + if (window.habitrpgShared.shared.content.specialPets[pet]) return Notification.text("Can't feed this pet."); var setObj = {}; var userPets = user.items.pets; if (user.items.mounts[pet] && (userPets[pet] >= 50 || $scope.selectedFood.name == 'Saddle')) @@ -163,7 +133,7 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User', 'API_URL', } setObj['items.pets.' + pet] = userPets[pet]; setObj['items.food.' + $scope.selectedFood.name] = user.items.food[$scope.selectedFood.name] - 1; - User.setMultiple(setObj); + User.set(setObj); $scope.selectedFood = null; // Selecting Pet @@ -182,19 +152,5 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User', 'API_URL', var mount = egg + '-' + potion; User.set('items.currentMount', (user.items.currentMount == mount) ? '' : mount); } - - $scope.equip = function(user, item, costume) { - var equipTo = costume ? 'costume' : 'equipped'; - if (item.type == 'shield') { - var weapon = Items.items.gear.flat[user.items.gear[equipTo].weapon]; - if (weapon && weapon.twoHanded) return Notification.text(weapon.text + ' is two-handed'); - } - var setVars = {}; - setVars['items.gear.' + equipTo + '.' + item.type] = item.key; - if (item.twoHanded) - setVars['items.gear.' + equipTo + '.shield'] = 'warrior_shield_0'; - User.setMultiple(setVars); - } - } ]); \ No newline at end of file diff --git a/public/js/controllers/notificationCtrl.js b/public/js/controllers/notificationCtrl.js index d1ddbda530..4a87b7129b 100644 --- a/public/js/controllers/notificationCtrl.js +++ b/public/js/controllers/notificationCtrl.js @@ -53,7 +53,7 @@ habitrpg.controller('NotificationCtrl', $rootScope.$watch('user.items.pets', function(after, before){ if(_.size(after) === _.size(before) || - window.habitrpgShared.helpers.countPets(null, after) < 90) return; + $rootScope.Shared.countPets(null, after) < 90) return; User.user.achievements.beastMaster = true; $rootScope.modals.achievements.beastMaster = true; }, true); diff --git a/public/js/controllers/rootCtrl.js b/public/js/controllers/rootCtrl.js index 17525266ab..ee95a757f0 100644 --- a/public/js/controllers/rootCtrl.js +++ b/public/js/controllers/rootCtrl.js @@ -5,12 +5,15 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$http', '$state', '$stateParams', 'Notification', 'Groups', function($scope, $rootScope, $location, User, $http, $state, $stateParams, Notification, Groups) { + var user = User.user; + $rootScope.modals = {}; $rootScope.modals.achievements = {}; $rootScope.User = User; - $rootScope.user = User.user; + $rootScope.user = user; $rootScope.settings = User.settings; - $rootScope.Items = window.habitrpgShared.items.items; + $rootScope.Shared = window.habitrpgShared; + $rootScope.Content = window.habitrpgShared.content; // Angular UI Router $rootScope.$state = $state; @@ -34,10 +37,10 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ // count pets, mounts collected totals, etc $rootScope.countExists = function(items) {return _.reduce(items,function(m,v){return m+(v?1:0)},0)} - $rootScope.petCount = window.habitrpgShared.helpers.countPets(null, User.user.items.pets); + $rootScope.petCount = $rootScope.Shared.countPets(null, User.user.items.pets); $rootScope.$watch('user.items.pets', function(pets){ - $rootScope.petCount = window.habitrpgShared.helpers.countPets($rootScope.countExists(pets), User.user.items.pets); + $rootScope.petCount = $rootScope.Shared.countPets($rootScope.countExists(pets), User.user.items.pets); }, true); $scope.safeApply = function(fn) { @@ -51,13 +54,6 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ } }; - /* - FIXME this is dangerous, organize helpers.coffee better, so we can group them by which controller needs them, - and then simply _.defaults($scope, Helpers.user) kinda thing - */ - _.defaults($rootScope, window.habitrpgShared.algos); - _.defaults($rootScope, window.habitrpgShared.helpers); - $rootScope.set = User.set; $rootScope.authenticated = User.authenticated; @@ -150,8 +146,6 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ $rootScope.applyingAction = true; $scope.spell = spell; if (spell.target == 'self') { - var tasks = User.user.habits.concat(User.user.dailys).concat(User.user.todos); - User.user.tasks = _.object(_.pluck(tasks,'id'), tasks); $scope.castEnd(null, 'self'); } else if (spell.target == 'party') { var party = Groups.party(); diff --git a/public/js/controllers/settingsCtrl.js b/public/js/controllers/settingsCtrl.js index 6d10e0d758..2925e15372 100644 --- a/public/js/controllers/settingsCtrl.js +++ b/public/js/controllers/settingsCtrl.js @@ -16,13 +16,13 @@ habitrpg.controller('SettingsCtrl', // } $scope.showTour = function(){ - User.set('flags.showTour',true); + User.set({'flags.showTour':true}); Guide.initTour(); $location.path('/tasks'); } $scope.showBailey = function(){ - User.set('flags.newStuff',true); + User.set({'flags.newStuff':true}); } $scope.saveDayStart = function(){ @@ -31,7 +31,7 @@ habitrpg.controller('SettingsCtrl', dayStart = 0; return alert('Please enter a number between 0 and 24'); } - User.set('preferences.dayStart', dayStart); + User.set({'preferences.dayStart': dayStart}); } $scope.language = window.env.language; @@ -41,7 +41,7 @@ habitrpg.controller('SettingsCtrl', $rootScope.$on('userSynced', function(){ location.reload(); }); - User.set('preferences.language', $scope.language.code); + User.set({'preferences.language': $scope.language.code}); } $scope.reroll = function(){ @@ -74,24 +74,18 @@ habitrpg.controller('SettingsCtrl', $rootScope.$watch('modals.restore', function(value){ if(value === true){ $scope.restoreValues.stats = angular.copy(User.user.stats); -// $scope.restoreValues.items = angular.copy(User.user.items); $scope.restoreValues.achievements = {streak: User.user.achievements.streak || 0}; } }) $scope.restore = function(){ var stats = $scope.restoreValues.stats, -// items = $scope.restoreValues.items, achievements = $scope.restoreValues.achievements; - User.setMultiple({ + User.set({ "stats.hp": stats.hp, "stats.exp": stats.exp, "stats.gp": stats.gp, "stats.lvl": stats.lvl, -// "items.weapon": items.weapon, -// "items.armor": items.armor, -// "items.head": items.head, -// "items.shield": items.shield, "achievements.streak": achievements.streak }); $rootScope.modals.restore = false; diff --git a/public/js/controllers/tasksCtrl.js b/public/js/controllers/tasksCtrl.js index 72fb1dcb59..81759ce21f 100644 --- a/public/js/controllers/tasksCtrl.js +++ b/public/js/controllers/tasksCtrl.js @@ -1,22 +1,22 @@ "use strict"; -habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User', 'Algos', 'Helpers', 'Notification', '$http', 'API_URL', - function($scope, $rootScope, $location, User, Algos, Helpers, Notification, $http, API_URL) { +habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','Notification', '$http', 'API_URL', + function($scope, $rootScope, $location, User, Notification, $http, API_URL) { $scope.obj = User.user; // used for task-lists $scope.score = function(task, direction) { - if (task.type === "reward" && User.user.stats.gp < task.value){ - return Notification.text('Not enough Gold!'); - } - Algos.score(User.user, task, direction); - User.log({op: "score",data: task, dir: direction}); - + User.user.ops.score({params:{id: task.id, direction:direction}}) }; $scope.addTask = function(addTo, listDef) { - var task = window.habitrpgShared.helpers.taskDefaults({text: listDef.newTask, type: listDef.type}, User.user.filters); - addTo.unshift(task); - User.log({op: "addTask", data: task}); + var newTask = { + text: listDef.newTask, + type: listDef.type, + tags: _.transform(User.user.filters, function(m,v,k){ + if (v) m[k]=v; + }) + } + User.user.ops.addTask({body:newTask}); delete listDef.newTask; }; @@ -40,40 +40,12 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User', ' $scope.removeTask = function(list, $index) { if (!confirm("Are you sure you want to delete this task?")) return; - User.log({ op: "delTask", data: list[$index] }); + User.user.ops.deleteTask({params:{id:list[$index].id}}) list.splice($index, 1); }; $scope.saveTask = function(task) { - var setVal = function(k, v) { - var op; - if (typeof v !== "undefined") { - op = { op: "set", data: {} }; - op.data["tasks." + task.id + "." + k] = v; - return log.push(op); - } - }; - var log = []; - setVal("text", task.text); - setVal("notes", task.notes); - setVal("priority", task.priority); - setVal("tags", task.tags); - if (task.type === "habit") { - setVal("up", task.up); - setVal("down", task.down); - } else if (task.type === "daily") { - setVal("repeat", task.repeat); - // TODO we'll remove this once rewrite's running for a while. This was a patch for derby issues - setVal("streak", task.streak); - - } else if (task.type === "todo") { - setVal("date", task.date); - } else { - if (task.type === "reward") { - setVal("value", task.value); - } - } - User.log(log); + User.user.ops.updateTask({params:{id:task.id},body:task}); task._editing = false; }; @@ -104,16 +76,18 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User', ' ------------------------ */ - $scope.itemStore = window.habitrpgShared.items.updateStore(User.user); + $rootScope.$on('userSynced', function(){ + $scope.itemStore = User.user.fns.updateStore(); + }) $scope.buy = function(item) { - var hasEnough = window.habitrpgShared.items.buyItem(User.user, item); + var hasEnough = User.user.ops.buy({query:{key:item.key}}); if (hasEnough) { - User.log({op: "buy", key: item.key}); Notification.text("Item purchased."); - $scope.itemStore = window.habitrpgShared.items.updateStore(User.user); + $scope.itemStore = User.user.fns.updateStore(); } else { - Notification.text("Not enough Gold!"); +// Notification.text("Not enough Gold!"); + // handled by userServices interceptor } }; diff --git a/public/js/controllers/userCtrl.js b/public/js/controllers/userCtrl.js index e0c52fdba2..8fa9ec1bff 100644 --- a/public/js/controllers/userCtrl.js +++ b/public/js/controllers/userCtrl.js @@ -12,56 +12,20 @@ habitrpg.controller("UserCtrl", ['$rootScope', '$scope', '$location', 'User', '$ }); $scope.allocate = function(stat){ - var setObj = {} - setObj['stats.' + stat] = User.user.stats[stat] + 1; - setObj['stats.points'] = User.user.stats.points - 1; - User.setMultiple(setObj); + User.user.ops.allocate({query:{stat:stat}}); } - $scope.rerollClass = function(){ - if (!confirm("Are you sure you want to re-roll? This will reset your character's class and allocated points (you'll get them all back to re-allocate)")) - return; - User.setMultiple({ - 'flags.classSelected': false, - //'stats.points': this is handled on the server - 'stats.str': 0, - 'stats.def': 0, - 'stats.per': 0, - 'stats.int': 0 - }) - } - $scope.rerollSubmit = function(){ - var klass = $scope.selectedClass; - var setVars = { - "stats.class": klass, - "flags.classSelected": true - }; + $scope.changeClass = function(klass){ + if (!klass) { + if (!confirm("Are you sure you want to re-roll? This will reset your character's class and allocated points (you'll get them all back to re-allocate)")) + return; + return User.user.ops.changeClass({}); + } - // Clear their gear and equip their new class's gear (can still equip old gear from inventory) - // If they've rolled this class before, restore their progress - _.each(['weapon', 'armor','shield','head'], function(type){ - var foundKey = false; - _.findLast(User.user.items.gear.owned, function(v,k){ - if (~k.indexOf(type + '_' + klass)) { - foundKey = k; - return true; - } - }); - setVars['items.gear.equipped.' + type] = - foundKey ? foundKey : // restore progress from when they last rolled this class - (type == 'weapon') ? 'weapon_' + klass + '_0' : // weapon_0 is significant, don't reset to base_0 - (type == 'shield' && klass == 'rogue') ? 'shield_rogue_0' : // rogues start with an off-hand weapon - type + '_base_0'; // naked for the rest! - - // Grant them their new class's gear - if (type == 'weapon' || (type == 'shield' && klass == 'rogue')) - setVars['items.gear.owned.' + type + '_' + klass + '_0'] = true; - }); - - User.setMultiple(setVars); + User.user.ops.changeClass({query:{class:klass}}); $scope.selectedClass = undefined; - //FIXME run updateStore (we need to access a different scope) + User.user.fns.updateStore(); } $scope.save = function(){ @@ -72,11 +36,28 @@ habitrpg.controller("UserCtrl", ['$rootScope', '$scope', '$location', 'User', '$ if(!curVal || $scope.editingProfile[key].toString() !== curVal.toString()) values['profile.' + key] = value; }); - User.setMultiple(values); + User.set(values); $scope._editing.profile = false; } - $scope.unlock = User.unlock; + /** + * For gem-unlockable preferences, (a) if owned, select preference (b) else, purchase + * @param path: User.preferences <-> User.purchased maps like User.preferences.skin=abc <-> User.purchased.skin.abc. + * Pass in this paramater as "skin.abc". Alternatively, pass as an array ["skin.abc", "skin.xyz"] to unlock sets + */ + $scope.unlock = function(path){ + var fullSet = ~path.indexOf(','); + var cost = fullSet ? 1.25 : 0.5; // 5G per set, 2G per individual + + if (fullSet) { + if (confirm("Purchase for 5 Gems?") !== true) return; + if (User.user.balance < cost) return $rootScope.modals.buyGems = true; + } else if (!User.user.fns.dotGet('purchased.' + path)) { + if (confirm("Purchase for 2 Gems?") !== true) return; + if (User.user.balance < cost) return $rootScope.modals.buyGems = true; + } + User.user.ops.unlock({query:{path:path}}) + } } ]); diff --git a/public/js/services/guideServices.js b/public/js/services/guideServices.js index cdb3fc034c..ddb43467cb 100644 --- a/public/js/services/guideServices.js +++ b/public/js/services/guideServices.js @@ -5,7 +5,7 @@ */ angular.module('guideServices', []). - factory('Guide', ['$rootScope', 'User', 'Items', 'Helpers', function($rootScope, User, Items, Helpers) { + factory('Guide', ['$rootScope', 'User', function($rootScope, User) { /** * Init and show the welcome tour. Note we do it listening to a $rootScope broadcasted 'userLoaded' message, @@ -79,7 +79,7 @@ angular.module('guideServices', []). $('.main-herobox').popover('destroy'); var tour = new Tour({ onEnd: function(){ - User.set('flags.showTour', false); + User.set({'flags.showTour': false}); } }); tourSteps.forEach(function(step) { @@ -134,7 +134,7 @@ angular.module('guideServices', []). $rootScope.$watch('user.items.pets', function(after, before) { if (User.user.achievements && User.user.achievements.beastMaster) return; if (before >= 90) { - User.set('achievements.beastMaster', true); + User.set({'achievements.beastMaster': true}); $('#beastmaster-achievement-modal').modal('show'); // FIXME } }); diff --git a/public/js/services/sharedServices.js b/public/js/services/sharedServices.js deleted file mode 100644 index 50c3fdb72d..0000000000 --- a/public/js/services/sharedServices.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -/** - * Services that persists and retrieves user from localStorage. - */ - -angular.module('sharedServices', [] ). - factory("Items", ['$rootScope', function($rootScope){ - return window.habitrpgShared.items; - }]). - factory("Algos", ['$rootScope', function($rootScope){ - return window.habitrpgShared.algos; - }]). - factory("Helpers", ['$rootScope', function($rootScope){ - return window.habitrpgShared.helpers; - }]); \ No newline at end of file diff --git a/public/js/services/userServices.js b/public/js/services/userServices.js index 26308ee955..12cc2f6d30 100644 --- a/public/js/services/userServices.js +++ b/public/js/services/userServices.js @@ -21,14 +21,13 @@ angular.module('userServices', []). user = {}; // this is stored as a reference accessible to all controllers, that way updates propagate //first we populate user with schema - _.extend(user, $window.habitrpgShared.helpers.newUser()); user.apiToken = user._id = ''; // we use id / apitoken to determine if registered - $window.habitrpgShared.algos.defineComputed(user); //than we try to load localStorage if (localStorage.getItem(STORAGE_USER_ID)) { _.extend(user, JSON.parse(localStorage.getItem(STORAGE_USER_ID))); } + user._wrapped = false; var syncQueue = function (cb) { if (!authenticated) { @@ -71,6 +70,16 @@ angular.module('userServices', []). // Update user _.extend(user, data); + if (!user._wrapped){ + $rootScope.Shared.wrap(user); + _.each(user.ops, function(op,k){ + user.ops[k] = _.partialRight(op, function(err, req){ + //if (err) return Notification.text(err); // FIXME Circular dependency found: Notification <- User + if (err) return alert(err); + userServices.log({op:k, params: req.params, query:req.query, body:req.body}); + }); + }); + } // Emit event when user is synced $rootScope.$emit('userSynced'); @@ -108,6 +117,9 @@ angular.module('userServices', []). }; var userServices = { user: user, + set: function(updates) { + user.ops.update({body:updates}); + }, online: function (status) { if (status===true) { settings.online = true; @@ -130,7 +142,7 @@ angular.module('userServices', []). // If they don't have timezone, set it var offset = moment().zone(); // eg, 240 - this will be converted on server as -(offset/60) if (user.preferences.timezoneOffset !== offset) - userServices.set('preferences.timezoneOffset', offset); + userServices.set({'preferences.timezoneOffset': offset}); cb && cb(); }); } else { @@ -161,62 +173,6 @@ angular.module('userServices', []). userServices.log({}); }, - /* - Very simple path-set. `set('preferences.gender','m')` for example. We'll deprecate this once we have a complete API - */ - set: function(k, v) { - var log = { op: 'set', data: {} }; - $window.habitrpgShared.helpers.dotSet(k, v, this.user); - log.data[k] = v; - userServices.log(log); - }, - - setMultiple: function(obj){ - var log = { op: 'set', data: {} }; - _.each(obj, function(v,k){ - $window.habitrpgShared.helpers.dotSet(k, v, userServices.user); - log.data[k] = v; - }); - userServices.log(log); - }, - - revive: function(){ - $window.habitrpgShared.algos.revive(user); - userServices.log({ op: "revive" }); - }, - - /** - * For gem-unlockable preferences, (a) if owned, select preference (b) else, purchase - * @param path: User.preferences <-> User.purchased maps like User.preferences.skin=abc <-> User.purchased.skin.abc. - * Pass in this paramater as "skin.abc". Alternatively, pass as an array ["skin.abc", "skin.xyz"] to unlock sets - */ - unlock: function(path){ - var self = userServices; //this; // why isn't this working? - - if (_.isArray(path)) { - if (confirm("Purchase for 5 Gems?") !== true) return; - if (user.balance < 1.25) return $rootScope.modals.buyGems = true; - path = path.join(','); - } else { - if ($window.habitrpgShared.helpers.dotGet('purchased.' + path, user)) { - var pref = path.split('.')[0], - val = path.split('.')[1]; - return self.set('preferences.' + pref, val); - } else { - if (confirm("Purchase for 2 Gems?") !== true) return; - if (user.balance < 0.5) return $rootScope.modals.buyGems = true; - } - } - - $http.post(API_URL + '/api/v1/user/unlock?path=' + path) - .success(function(data, status, headers, config){ - self.log({}); // sync new unlocked & preferences - }).error(function(data, status, headers, config){ - alert(status + ': ' + data); - //FIXME use method used elsewhere for handling this error, this is temp while developing - }) - }, - save: save, settings: settings diff --git a/public/manifest.json b/public/manifest.json index f14e5b3d97..a17b2846a9 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -32,7 +32,6 @@ "js/app.js", "js/services/authServices.js", "js/services/notificationServices.js", - "js/services/sharedServices.js", "js/services/userServices.js", "js/services/groupServices.js", "js/services/memberServices.js", diff --git a/src/controllers/admin.js b/src/controllers/admin.js index 3d38243c25..7d8be48150 100644 --- a/src/controllers/admin.js +++ b/src/controllers/admin.js @@ -1,9 +1,7 @@ var _ = require('lodash'); var nconf = require('nconf'); var async = require('async'); -var algos = require('habitrpg-shared/script/algos'); -var helpers = require('habitrpg-shared/script/helpers'); -var items = require('habitrpg-shared/script/items'); +var shared = require('habitrpg-shared'); var User = require('./../models/user').model; var Group = require('./../models/group').model; var api = module.exports; diff --git a/src/controllers/auth.js b/src/controllers/auth.js index 4db8cd2768..0d3a8304d5 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -3,7 +3,7 @@ var validator = require('validator'); var check = validator.check; var sanitize = validator.sanitize; var passport = require('passport'); -var helpers = require('habitrpg-shared/script/helpers'); +var shared = require('habitrpg-shared'); var async = require('async'); var utils = require('../utils'); var nconf = require('nconf'); @@ -83,17 +83,18 @@ api.registerUser = function(req, res, next) { if (found) { return cb("Username already taken"); } - newUser = helpers.newUser(true); salt = utils.makeSalt(); - newUser.auth = { - local: { - username: username, - email: email, - salt: salt - }, - timestamps: {created: +new Date(), loggedIn: +new Date()} + var newUser = { + auth: { + local: { + username: username, + email: email, + salt: salt, + hashed_password: utils.encryptPassword(password, salt) + }, + timestamps: {created: +new Date(), loggedIn: +new Date()} + } }; - newUser.auth.local.hashed_password = utils.encryptPassword(password, salt); user = new User(newUser); user.save(cb); } @@ -251,12 +252,12 @@ api.setupPassport = function(router) { }, function(user, cb){ if (user) return cb(null, user); - var newUser = helpers.newUser(true); - newUser.auth = { - facebook: req.user, - timestamps: {created: +new Date(), loggedIn: +new Date()} - }; - user = new User(newUser); + user = new User({ + auth: { + facebook: req.user, + timestamps: {created: +new Date(), loggedIn: +new Date()} + } + }); user.save(cb); diff --git a/src/controllers/challenges.js b/src/controllers/challenges.js index 9481d5ce49..dc11abe5df 100644 --- a/src/controllers/challenges.js +++ b/src/controllers/challenges.js @@ -3,9 +3,7 @@ var _ = require('lodash'); var nconf = require('nconf'); var async = require('async'); -var algos = require('habitrpg-shared/script/algos'); -var helpers = require('habitrpg-shared/script/helpers'); -var items = require('habitrpg-shared/script/items'); +var shared = require('habitrpg-shared'); var User = require('./../models/user').model; var Group = require('./../models/group').model; var Challenge = require('./../models/challenge').model; diff --git a/src/controllers/deprecated.js b/src/controllers/deprecated.js index d2df8fe934..e38e0a5f7c 100644 --- a/src/controllers/deprecated.js +++ b/src/controllers/deprecated.js @@ -31,7 +31,7 @@ var initDeprecated = function(req, res, next) { return next(); }; -router.post('/v1/users/:uid/tasks/:taskId/:direction', initDeprecated, auth.auth, api.scoreTask); +router.post('/v1/users/:uid/tasks/:taskId/:direction', initDeprecated, auth.auth, api.score); router.get('/v1/users/:uid/calendar.ics', function(req, res, next) { return next() //disable for now diff --git a/src/controllers/groups.js b/src/controllers/groups.js index d45adceca3..4efd8c65bd 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -3,9 +3,7 @@ var _ = require('lodash'); var nconf = require('nconf'); var async = require('async'); -var algos = require('habitrpg-shared/script/algos'); -var helpers = require('habitrpg-shared/script/helpers'); -var items = require('habitrpg-shared/script/items'); +var shared = require('habitrpg-shared'); var User = require('./../models/user').model; var Group = require('./../models/group').model; var api = module.exports; @@ -208,7 +206,7 @@ api.postChat = function(req, res, next) { var user = res.locals.user var group = res.locals.group; var message = { - id: helpers.uuid(), + id: shared.uuid(), uuid: user._id, contributor: user.contributor && user.contributor.toObject(), backer: user.backer && user.backer.toObject(), diff --git a/src/controllers/user.js b/src/controllers/user.js index a2478e0929..039238e9e0 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -5,9 +5,7 @@ var ipn = require('paypal-ipn'); var _ = require('lodash'); var nconf = require('nconf'); var async = require('async'); -var algos = require('habitrpg-shared/script/algos'); -var helpers = require('habitrpg-shared/script/helpers'); -var items = require('habitrpg-shared/script/items'); +var shared = require('habitrpg-shared'); var validator = require('validator'); var check = validator.check; var sanitize = validator.sanitize; @@ -58,12 +56,6 @@ api.verifyTaskExists = function(req, res, next) { return next(); }; -function addTask(user, task) { - task = helpers.taskDefaults(task); - user[task.type+'s'].unshift(task); - return task; -} - /* API Routes --------------- @@ -73,7 +65,7 @@ function addTask(user, task) { This is called form deprecated.coffee's score function, and the req.headers are setup properly to handle the login Export it also so we can call it from deprecated.coffee */ -api.scoreTask = function(req, res, next) { +api.score = function(req, res, next) { var id = req.params.id, direction = req.params.direction, user = res.locals.user, @@ -106,9 +98,9 @@ api.scoreTask = function(req, res, next) { if (task.type === 'daily' || task.type === 'todo') { task.completed = direction === 'up'; } - task = addTask(user, task); + task = user.ops.addTask({body:task}); } - var delta = algos.score(user, task, direction); + var delta = user.ops.score({params:{id:task.id, direction:direction}}); //user.markModified('flags'); user.save(function(err, saved) { if (err) return res.json(500, {err: err}); @@ -146,46 +138,35 @@ api.getTask = function(req, res, next) { * Delete Task */ api.deleteTask = function(req, res, next) { - var user = res.locals.user; - user.deleteTask(res.locals.task.id); - user.save(function(err) { - if (err) return res.json(500, {err: err}); - res.send(204); - }); + api.verifyTaskExists(req, res, function(){ + var user = res.locals.user; + user.deleteTask(res.locals.task.id); + user.save(function(err) { + if (err) return res.json(500, {err: err}); + res.send(204); + }); + }) }; /* Update Task */ -api.updateTask = function(req, res, next) { - var user = res.locals.user; - var tid = req.params.id; - var task = user.tasks[req.params.id]; - _.merge(task, req.body); - user.save(function(err, saved) { - if (err) return res.json(500, {err: err}) - return res.json(200, task); - }); -}; -api.createTask = function(req, res, next) { - var user = res.locals.user; - var task = addTask(user, req.body); - user.save(function(err, saved) { - if (err) return res.json(500, {err: err}); - return res.json(201, task); - }); -}; +// api.updateTask // handled in Shared.ops + +// api.addTask // handled in Shared.ops api.sortTask = function(req, res, next) { - var id = req.params.id; - var to = req.body.to, from = req.body.from, type = req.body.type; - var user = res.locals.user; - user[type+'s'].splice(to, 0, user[type+'s'].splice(from, 1)[0]); - user.save(function(err, saved) { - if (err) return res.json(500, {err: err}); - return res.json(200, saved.toJSON()[type+'s']); - }); + api.verifyTaskExists(req, res, function(){ + var id = req.params.id; + var to = req.body.to, from = req.body.from, type = req.body.type; + var user = res.locals.user; + user[type+'s'].splice(to, 0, user[type+'s'].splice(from, 1)[0]); + user.save(function(err, saved) { + if (err) return res.json(500, {err: err}); + return res.json(200, saved.toJSON()[type+'s']); + }); + }) }; api.clearCompleted = function(req, res, next) { @@ -202,22 +183,7 @@ api.clearCompleted = function(req, res, next) { Items ------------------------------------------------------------------------ */ -api.buy = function(req, res, next) { - var user = res.locals.user; - var key = req.params.key; - if (key !== 'potion' && !items.items.gear.flat[key]) { - return res.json(400, {err: ":item must be a supported key, see https://github.com/HabitRPG/habitrpg-shared/blob/master/script/items.coffee"}); - } - var hasEnough = items.buyItem(user, items.items.gear.flat[key]); - if (hasEnough) { - return user.save(function(err, saved) { - if (err) return res.json(500, {err: err}); - return res.json(200, saved.toJSON().items); - }); - } else { - return res.json(200, {err: "Not enough GP"}); - } -}; +// api.buy // handled in Shard.ops /* ------------------------------------------------------------------------ @@ -230,7 +196,7 @@ api.buy = function(req, res, next) { */ api.getUser = function(req, res, next) { var user = res.locals.user.toJSON(); - user.stats.toNextLevel = algos.tnl(user.stats.lvl); + user.stats.toNextLevel = shared.tnl(user.stats.lvl); user.stats.maxHealth = 50; delete user.apiToken; if (user.auth) { @@ -249,7 +215,7 @@ api.getUser = function(req, res, next) { * Note: custom is for 3rd party apps */ acceptablePUTPaths = _.reduce(require('./../models/user').schema.paths, function(m,v,leaf){ - var found= _.find('tasks achievements filters flags invitations items lastCron party preferences profile stats tags custom'.split(' '), function(root){ + var found= _.find('tasks achievements filters flags invitations lastCron party preferences profile stats tags'.split(' '), function(root){ return leaf.indexOf(root) == 0; }); if (found) m[leaf]=true; @@ -263,14 +229,14 @@ acceptablePUTPaths['tasks']=true; // and for now, let them fully set tasks.* (fo * PUT /user {'stats.hp':50, 'tasks.TASK_ID.repeat.m':false} * See acceptablePUTPaths for which user paths are supported */ -api.updateUser = function(req, res, next) { +api.update = function(req, res, next) { var user = res.locals.user; var errors = []; if (_.isEmpty(req.body)) return res.json(200, user); _.each(req.body, function(v, k) { if (acceptablePUTPaths[k]) - helpers.dotSet(k, v, user); + user.fns.dotSet(k, v); else errors.push("path `" + k + "` was not saved, as it's a protected path. Make sure to send `PUT /api/v1/user` request bodies as `{'set.this.path':value}` instead of `{set:{this:{path:value}}}`"); return true; @@ -284,7 +250,7 @@ api.updateUser = function(req, res, next) { api.cron = function(req, res, next) { var user = res.locals.user; - algos.cron(user); + shared.cron(user); if (user.isModified()) { res.locals.wasModified = true; user.auth.timestamps.loggedin = new Date(); @@ -292,15 +258,6 @@ api.cron = function(req, res, next) { user.save(next); }; -api.revive = function(req, res, next) { - var user = res.locals.user; - algos.revive(user); - user.save(function(err, saved) { - if (err) return res.json(500, {err: err}); - return res.json(200, saved); - }); -}; - api.reroll = function(req, res, next) { var user = res.locals.user; if (user.balance < 1) return res.json(401, {err: "Not enough tokens."}); @@ -353,36 +310,7 @@ api['delete'] = function(req, res) { ------------------------------------------------------------------------ */ -api.unlock = function(req, res) { - var user = res.locals.user; - var path = req.query.path; - var fullSet = ~path.indexOf(','); - - // 5G per set, 2G per individual - cost = fullSet ? 1.25 : 0.5; - - if (user.balance < cost) - return res.json(401, {err: 'Not enough gems'}); - - if (fullSet) { - var paths = path.split(','); - _.each(paths, function(p){ - helpers.dotSet('purchased.' + p, true, user); - }); - } else { - if (helpers.dotGet('purchased.' + path, user) === true) - return res.json(401, {err: 'User already purchased that'}); - helpers.dotSet('purchased.' + path, true, user); - } - - user.balance -= cost; - user._v++; - user.markModified('purchased'); - user.save(function(err, saved){ - if (err) res.json(500, {err:err}); - res.send(200); - }) -} +// api.unlock // see Shared.ops /* ------------------------------------------------------------------------ @@ -492,7 +420,7 @@ api.deleteTag = function(req, res){ api.cast = function(req, res) { var user = res.locals.user; var type = req.body.type, target = req.body.target; - var spell = items.items.spells[user.stats.class][req.params.spell]; + var spell = shared.content.spells[user.stats.class][req.params.spell]; var done = function(){ var err = arguments[0]; @@ -548,6 +476,28 @@ api.cast = function(req, res) { } } +/** + * All other user.ops which can easily be mapped to habitrpg-shared/index.coffee, not requiring custom API-wrapping + */ +_.each(shared.wrap({}).ops, function(op,k){ + if (!api[k]) { + api[k] = function(req, res, next) { + var user = res.locals.user; + async.series([ + function(cb){ user.ops[k](req, cb) }, + function(cb){ user.save(cb) }, + ], function(err){ + if (err) { + // If we want to send something other than 500, pass err as {code: 200, message: "Not enough GP"} + if (err.code) return res.json(err.code, err.message); + return res.json(500,{err:err}); + } + return res.send(200); + }) + } + } +}) + /* ------------------------------------------------------------------------ Batch Update @@ -558,78 +508,28 @@ api.batchUpdate = function(req, res, next) { var user = res.locals.user; var oldSend = res.send; var oldJson = res.json; - var performAction = function(action, cb) { - // TODO come up with a more consistent approach here. like: - // req.body=action.data; delete action.data; _.defaults(req.params, action) - // Would require changing action.dir on mobile app - req.params.id = action.data && action.data.id; - req.params.direction = action.dir; - req.params.type = action.type; - req.params.key = action.key; - req.body = action.data; + var callOp = function(_req, cb) { res.send = res.json = function(code, data) { - if (_.isNumber(code) && code >= 400) { - console.error({ - code: code, - data: data - }); - } + if (_.isNumber(code) && code >= 400) + console.error({code: code, data: data}); //FIXME send error messages down return cb(); }; - switch (action.op) { - case "score": - api.scoreTask(req, res); - break; - case "buy": - api.buy(req, res); - break; - case "sortTask": - api.verifyTaskExists(req, res, function() { - api.sortTask(req, res); - }); - break; - case "addTask": - api.createTask(req, res); - break; - case "delTask": - api.verifyTaskExists(req, res, function() { - api.deleteTask(req, res); - }); - break; - case "set": - api.updateUser(req, res); - break; - case "delTag": - api.deleteTag(req, res); - break; - case "revive": - api.revive(req, res); - break; - case "clear-completed": - api.clearCompleted(req, res); - break; - case "reroll": - api.reroll(req, res); - break; - default: - cb(); - break; - } + api[_req.op](_req, res); }; // Setup the array of functions we're going to call in parallel with async - var actions = _.transform(req.body || [], function(result, action) { - if (!_.isEmpty(action)) { + var ops = _.transform(req.body || [], function(result, _req) { + if (!_.isEmpty(_req)) { result.push(function(cb) { - performAction(action, cb); + callOp(_req, cb); }); } }); // call all the operations, then return the user object to the requester - async.series(actions, function(err) { + async.series(ops, function(err) { res.json = oldJson; res.send = oldSend; if (err) return res.json(500, {err: err}); diff --git a/src/models/challenge.js b/src/models/challenge.js index 40f2ddd0c9..aab2833bc4 100644 --- a/src/models/challenge.js +++ b/src/models/challenge.js @@ -1,12 +1,12 @@ var mongoose = require("mongoose"); var Schema = mongoose.Schema; -var helpers = require('habitrpg-shared/script/helpers'); +var shared = require('habitrpg-shared'); var _ = require('lodash'); var TaskSchemas = require('./task'); var Group = require('./group').model; var ChallengeSchema = new Schema({ - _id: {type: String, 'default': helpers.uuid}, + _id: {type: String, 'default': shared.uuid}, name: String, shortName: String, description: String, diff --git a/src/models/group.js b/src/models/group.js index 093ca0ae83..b8a7a428b3 100644 --- a/src/models/group.js +++ b/src/models/group.js @@ -1,10 +1,10 @@ var mongoose = require("mongoose"); var Schema = mongoose.Schema; -var helpers = require('habitrpg-shared/script/helpers'); +var shared = require('habitrpg-shared'); var _ = require('lodash'); var GroupSchema = new Schema({ - _id: {type: String, 'default': helpers.uuid}, + _id: {type: String, 'default': shared.uuid}, name: String, description: String, leader: {type: String, ref: 'User'}, diff --git a/src/models/task.js b/src/models/task.js index 67d32929e9..f56c8fb63b 100644 --- a/src/models/task.js +++ b/src/models/task.js @@ -6,7 +6,7 @@ // ------------ var mongoose = require("mongoose"); var Schema = mongoose.Schema; -var helpers = require('habitrpg-shared/script/helpers'); +var shared = require('habitrpg-shared'); var _ = require('lodash'); // Task Schema @@ -14,7 +14,7 @@ var _ = require('lodash'); var TaskSchema = { //_id:{type: String,'default': helpers.uuid}, - id: {type: String,'default': helpers.uuid}, + id: {type: String,'default': shared.uuid}, text: String, notes: {type: String, 'default': ''}, tags: {type: Schema.Types.Mixed, 'default': {}}, //{ "4ddf03d9-54bd-41a3-b011-ca1f1d2e9371" : true }, diff --git a/src/models/user.js b/src/models/user.js index 10e548d11e..152f00d285 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -6,8 +6,7 @@ // ------------ var mongoose = require("mongoose"); var Schema = mongoose.Schema; -var helpers = require('habitrpg-shared/script/helpers'); -var items = require('habitrpg-shared/script/items'); +var shared = require('habitrpg-shared'); var _ = require('lodash'); var TaskSchemas = require('./task'); var Challenge = require('./challenge').model; @@ -15,23 +14,23 @@ var Challenge = require('./challenge').model; // User Schema // ----------- -var eggPotionMapping = _.transform(items.items.eggs, function(m, egg){ - _.defaults(m, _.transform(items.items.hatchingPotions, function(m2, pot){ +var eggPotionMapping = _.transform(shared.content.eggs, function(m, egg){ + _.defaults(m, _.transform(shared.content.hatchingPotions, function(m2, pot){ m2[egg.name + '-' + pot.name] = true; })); }) -var specialPetsMapping = items.items.specialPets; // may need to revisit if we add additional information about the special pets +var specialPetsMapping = shared.content.specialPets; // may need to revisit if we add additional information about the special pets var UserSchema = new Schema({ // ### UUID and API Token _id: { type: String, - 'default': helpers.uuid + 'default': shared.uuid }, apiToken: { type: String, - 'default': helpers.uuid + 'default': shared.uuid }, // ### Mongoose Update Object @@ -109,7 +108,7 @@ var UserSchema = new Schema({ }, items: { gear: { - owned: _.transform(items.items.gear.flat, function(m,v,k){ + owned: _.transform(shared.content.gear.flat, function(m,v,k){ m[v.key] = {type: Boolean}; if (v.key.match(/[weapon|armor|head|shield]_warrior_0/)) m[v.key]['default'] = true; @@ -149,19 +148,19 @@ var UserSchema = new Schema({ // 'PandaCub': 0, // 0 indicates "doesn't own" // 'Wolf': 5 // Number indicates "stacking" // } - eggs: _.transform(items.items.eggs, function(m,v,k){ m[k] = Number; }), + eggs: _.transform(shared.content.eggs, function(m,v,k){ m[k] = Number; }), // hatchingPotions: { // 'Desert': 0, // 0 indicates "doesn't own" // 'CottonCandyBlue': 5 // Number indicates "stacking" // } - hatchingPotions: _.transform(items.items.hatchingPotions, function(m,v,k){ m[k] = Number; }), + hatchingPotions: _.transform(shared.content.hatchingPotions, function(m,v,k){ m[k] = Number; }), // Food: { // 'Watermelon': 0, // 0 indicates "doesn't own" // 'RottenMeat': 5 // Number indicates "stacking" // } - food: _.transform(items.items.food, function(m,v,k){ m[k] = Number; }), + food: _.transform(shared.content.food, function(m,v,k){ m[k] = Number; }), // mounts: { // 'Wolf-Desert': true, @@ -281,16 +280,30 @@ UserSchema.methods.toJSON = function() { return doc; }; -UserSchema.virtual('tasks').get(function () { - var tasks = this.habits.concat(this.dailys).concat(this.todos).concat(this.rewards); - var tasks = _.object(_.pluck(tasks,'id'), tasks); - return tasks; -}); +//UserSchema.virtual('tasks').get(function () { +// var tasks = this.habits.concat(this.dailys).concat(this.todos).concat(this.rewards); +// var tasks = _.object(_.pluck(tasks,'id'), tasks); +// return tasks; +//}); - // FIXME - since we're using special @post('init') above, we need to flag when the original path was modified. - // Custom setter/getter virtuals? +UserSchema.post('init', function(doc){ + shared.wrap(doc); +}) UserSchema.pre('save', function(next) { + + // Populate new users with default content + if (this.isNew){ + //TODO for some reason this doesn't work here: `_.merge(this, shared.content.userDefaults);` + this.habits = shared.content.userDefaults.habits; + this.dailys = shared.content.userDefaults.dailys; + this.todos = shared.content.userDefaults.todos; + this.rewards = shared.content.userDefaults.rewards; + this.tags = shared.content.userDefaults.tags; + // tasks automatically get id=helpers.uuid() from TaskSchema id.default, but tags are Schema.Types.Mixed - so we need to manually invoke here + _.each(this.tags, function(tag){tag.id = shared.uuid();}) + } + //this.markModified('tasks'); if (_.isNaN(this.preferences.dayStart) || this.preferences.dayStart < 0 || this.preferences.dayStart > 24) { this.preferences.dayStart = 0; @@ -308,7 +321,7 @@ UserSchema.pre('save', function(next) { // Actually, can this be used as an attr default? (schema {type: ..., 'default': function(){}}) this.stats.points = this.stats.lvl - (this.stats.con + this.stats.str + this.stats.per + this.stats.int); - var petCount = helpers.countPets(_.reduce(this.items.pets,function(m,v){ + var petCount = shared.countPets(_.reduce(this.items.pets,function(m,v){ //HOTFIX - Remove when solution is found, the first argument passed to reduce is a function if(_.isFunction(v)) return m; return m+(v?1:0)},0), this.items.pets); diff --git a/src/routes/api.js b/src/routes/api.js index f63b8b6fe5..5bee5207fd 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -19,7 +19,6 @@ var middleware = require('../middleware'); $ mocha test/user.mocha.coffee */ -var verifyTaskExists = user.verifyTaskExists var cron = user.cron; router.get('/status', function(req, res) { @@ -32,16 +31,16 @@ router.get('/status', function(req, res) { router.get('/export/history',auth.auth,dataexport.history); //[todo] encode data output options in the data controller and use these to build routes /* Scoring*/ -router.post('/user/task/:id/:direction', auth.auth, cron, user.scoreTask); -router.post('/user/tasks/:id/:direction', auth.auth, cron, user.scoreTask); +router.post('/user/task/:id/:direction', auth.auth, cron, user.score); +router.post('/user/tasks/:id/:direction', auth.auth, cron, user.score); /* Tasks*/ router.get('/user/tasks', auth.auth, cron, user.getTasks); router.get('/user/task/:id', auth.auth, cron, user.getTask); -router.put('/user/task/:id', auth.auth, cron, verifyTaskExists, user.updateTask); -router["delete"]('/user/task/:id', auth.auth, cron, verifyTaskExists, user.deleteTask); -router.post('/user/task', auth.auth, cron, user.createTask); -router.put('/user/task/:id/sort', auth.auth, cron, verifyTaskExists, user.sortTask); +router.put('/user/task/:id', auth.auth, cron, user.updateTask); +router["delete"]('/user/task/:id', auth.auth, cron, user.deleteTask); +router.post('/user/task', auth.auth, cron, user.addTask); +router.put('/user/task/:id/sort', auth.auth, cron, user.sortTask); router.post('/user/clear-completed', auth.auth, cron, user.clearCompleted); router.post('/user/task/:id/unlink', auth.auth, challenges.unlink); // removing cron since they may want to remove task first if (nconf.get('NODE_ENV') == 'development') { @@ -53,7 +52,7 @@ router.post('/user/buy/:key', auth.auth, cron, user.buy); /* User*/ router.get('/user', auth.auth, cron, user.getUser); -router.put('/user', auth.auth, cron, user.updateUser); +router.put('/user', auth.auth, cron, user.update); router.post('/user/revive', auth.auth, cron, user.revive); router.post('/user/batch-update', middleware.forceRefresh, auth.auth, cron, user.batchUpdate); router.post('/user/reroll', auth.auth, cron, user.reroll); diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index c1fee38b2c..da2a33522c 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -9,7 +9,7 @@ script(type='text/ng-template', id='partials/options.inventory.inventory.html') li.customize-menu menu.pets-menu(label='{{label}}', ng-repeat='(klass,label) in {base:"Base", warrior:"Warrior", wizard:"Wizard", rogue:"Rogue", special:"Special"}', ng-show='gear[klass]') div(ng-repeat='item in gear[klass]') - button.customize-option(popover='{{item.notes}}', popover-title='{{item.text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='equip(user,item)', class='shop_{{item.key}}', ng-class='{selectableInventory: user.items.gear.equipped[item.type] == item.key}') + button.customize-option(popover='{{item.notes}}', popover-title='{{item.text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='user.ops.equip({query:{key:item.key}})', class='shop_{{item.key}}', ng-class='{selectableInventory: user.items.gear.equipped[item.type] == item.key}') label.checkbox.inline input(type="checkbox", ng-model="user.preferences.costume") | Use Costume  @@ -17,22 +17,22 @@ script(type='text/ng-template', id='partials/options.inventory.inventory.html') li.customize-menu(ng-if='user.preferences.costume') menu.pets-menu(label='{{label}}', ng-repeat='(klass,label) in {base:"Base", warrior:"Warrior", wizard:"Wizard", rogue:"Rogue", special:"Special"}', ng-show='gear[klass]') div(ng-repeat='item in gear[klass]') - button.customize-option(popover='{{item.notes}}', popover-title='{{item.text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='equip(user,item, true)', class='shop_{{item.key}}', ng-class='{selectableInventory: user.items.gear.costume[item.type] == item.key}') + button.customize-option(popover='{{item.notes}}', popover-title='{{item.text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='user.ops.equip({query:{type:"costume", key:item.key}})', class='shop_{{item.key}}', ng-class='{selectableInventory: user.items.gear.costume[item.type] == item.key}') li.customize-menu menu.pets-menu(label='Eggs ({{eggCount}})') p(ng-show='eggCount < 1') You don't have any eggs. div(ng-repeat='(egg,points) in ownedItems(user.items.eggs)') //TODO move positioning this styling to css - button.customize-option(popover='{{Items.eggs[egg].notes}}', popover-title='{{Items.eggs[egg].text}} Egg', popover-trigger='mouseenter', popover-placement='right', ng-click='chooseEgg(egg)', class='Pet_Egg_{{egg}}', ng-class='{selectableInventory: selectedPotion && !user.items.pets[egg+"-"+selectedPotion.name]}') + button.customize-option(popover='{{Content.eggs[egg].notes}}', popover-title='{{Content.eggs[egg].text}} Egg', popover-trigger='mouseenter', popover-placement='right', ng-click='chooseEgg(egg)', class='Pet_Egg_{{egg}}', ng-class='{selectableInventory: selectedPotion && !user.items.pets[egg+"-"+selectedPotion.name]}') .badge.badge-info.stack-count {{points}} - //-p {{Items.eggs[egg].text}} + //-p {{Content.eggs[egg].text}} li.customize-menu menu.hatchingPotion-menu(label='Hatching Potions ({{potCount}})') p(ng-show='potCount < 1') You don't have any hatching potions. div(ng-repeat='(pot,points) in ownedItems(user.items.hatchingPotions)') - button.customize-option(popover='{{Items.hatchingPotions[pot].notes}}', popover-title='{{Items.hatchingPotions[pot].text}} Potion', popover-trigger='mouseenter', popover-placement='right', ng-click='choosePotion(pot)', class='Pet_HatchingPotion_{{pot}}', ng-class='{selectableInventory: selectedEgg && !user.items.pets[selectedEgg.name+"-"+pot]}') + button.customize-option(popover='{{Content.hatchingPotions[pot].notes}}', popover-title='{{Content.hatchingPotions[pot].text}} Potion', popover-trigger='mouseenter', popover-placement='right', ng-click='choosePotion(pot)', class='Pet_HatchingPotion_{{pot}}', ng-class='{selectableInventory: selectedEgg && !user.items.pets[selectedEgg.name+"-"+pot]}') .badge.badge-info.stack-count {{points}} //-p {{pot}} @@ -40,7 +40,7 @@ script(type='text/ng-template', id='partials/options.inventory.inventory.html') menu.pets-menu(label='Food ({{foodCount}})') p(ng-show='foodCount < 1') You don't have any food. div(ng-repeat='(food,points) in ownedItems(user.items.food)') - button.customize-option(popover='{{Items.food[food].notes}}', popover-title='{{Items.food[food].text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='chooseFood(food)', class='Pet_Food_{{food}}') + button.customize-option(popover='{{Content.food[food].notes}}', popover-title='{{Content.food[food].text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='chooseFood(food)', class='Pet_Food_{{food}}') .badge.badge-info.stack-count {{points}} //-p {{food}} @@ -70,7 +70,7 @@ script(type='text/ng-template', id='partials/options.inventory.inventory.html') menu.inventory-list(type='list') li.customize-menu menu.pets-menu(label='Eggs') - div(ng-repeat='egg in Items.eggs') + div(ng-repeat='egg in Content.eggs') button.customize-option(popover='{{egg.notes}}', popover-title='{{egg.text}} Egg', popover-trigger='mouseenter', popover-placement='left', ng-click='buy("egg", egg)', class='Pet_Egg_{{egg.name}}') p | {{egg.value}} @@ -78,7 +78,7 @@ script(type='text/ng-template', id='partials/options.inventory.inventory.html') li.customize-menu menu.pets-menu(label='Hatching Potions') - div(ng-repeat='pot in Items.hatchingPotions') + div(ng-repeat='pot in Content.hatchingPotions') button.customize-option(popover='{{pot.notes}}', popover-title='{{pot.text}} Potion', popover-trigger='mouseenter', popover-placement='left', ng-click='buy("hatchingPotion", pot)', class='Pet_HatchingPotion_{{pot.name}}') p | {{pot.value}} @@ -86,7 +86,7 @@ script(type='text/ng-template', id='partials/options.inventory.inventory.html') li.customize-menu menu.pets-menu(label='Food') - div(ng-repeat='food in Items.food') + div(ng-repeat='food in Content.food') button.customize-option(popover='{{food.notes}}', popover-title='{{food.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='buy("food", food)', class='Pet_Food_{{food.name}}') p | {{food.value}} diff --git a/views/options/inventory/stable.jade b/views/options/inventory/stable.jade index bc90d34ba0..e706713c63 100644 --- a/views/options/inventory/stable.jade +++ b/views/options/inventory/stable.jade @@ -26,9 +26,9 @@ script(type='text/ng-template', id='partials/options.inventory.stable.mounts.htm Shall I bring you your steed, {{user.profile.name}}? Click a mount to saddle up. h4 {{mountCount}} / {{totalPets}} Mounts Tamed menu.pets(type='list') - li.customize-menu(ng-repeat='egg in Items.eggs') + li.customize-menu(ng-repeat='egg in Content.eggs') menu - div(ng-repeat='potion in Items.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.mountText}}', popover-placement='bottom', ng-init='mount = egg.name+"-"+potion.name') + div(ng-repeat='potion in Content.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.mountText}}', popover-placement='bottom', ng-init='mount = egg.name+"-"+potion.name') button(class="pet-button Mount_Head_{{mount}}", ng-show='user.items.mounts[mount]', ng-class='{active: user.items.currentMount == mount}', ng-click='chooseMount(egg.name, potion.name)') //div(class='Mount_Head_{{mount}}') button(class="pet-button pet-not-owned", ng-hide='user.items.mounts[mount]') @@ -59,9 +59,9 @@ script(type='text/ng-template', id='partials/options.inventory.stable.pets.html' h4 {{petCount}} / {{totalPets}} Pets Found menu.pets(type='list') - li.customize-menu(ng-repeat='egg in Items.eggs') + li.customize-menu(ng-repeat='egg in Content.eggs') menu - div(ng-repeat='potion in Items.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.text}}', popover-placement='bottom', ng-init='pet = egg.name+"-"+potion.name') + div(ng-repeat='potion in Content.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.text}}', popover-placement='bottom', ng-init='pet = egg.name+"-"+potion.name') button(class="pet-button Pet-{{pet}}", ng-if='user.items.pets[pet]>0', ng-class='{active: user.items.currentPet == pet, selectableInventory: selectedFood}', ng-click='choosePet(egg.name, potion.name)') .progress(ng-class='{"progress-success": user.items.pets[pet]<50}') .bar(style="width: {{user.items.pets[pet]/.5}}%;") @@ -85,7 +85,7 @@ script(type='text/ng-template', id='partials/options.inventory.stable.pets.html' li.customize-menu menu.pets-menu(label='Food') div(ng-repeat='(food,points) in ownedItems(user.items.food)') - button.customize-option(popover-append-to-body='true', popover='{{Items.food[food].notes}}', popover-title='{{Items.food[food].text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='chooseFood(food)', class='Pet_Food_{{food}}') + button.customize-option(popover-append-to-body='true', popover='{{Content.food[food].notes}}', popover-title='{{Content.food[food].text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='chooseFood(food)', class='Pet_Food_{{food}}') .badge.badge-info.stack-count {{points}} // Remove this once we have images in - p {{Items.food[food].text}} + p {{Content.food[food].text}} diff --git a/views/options/profile.jade b/views/options/profile.jade index b0d32c24a0..c0dba4bceb 100644 --- a/views/options/profile.jade +++ b/views/options/profile.jade @@ -6,8 +6,8 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template') li.customize-menu menu(label='Head') menu - button.broad_armor_base_0.customize-option(type='button', ng-click='set("preferences.size","broad")') - button.slim_armor_base_0.customize-option(type='button', ng-click='set("preferences.size","slim")') + button.broad_armor_base_0.customize-option(type='button', ng-click='set({"preferences.size":"broad"})') + button.slim_armor_base_0.customize-option(type='button', ng-click='set({"preferences.size":"slim"})') .span4 h3 Hair @@ -15,60 +15,60 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template') // Color li.customize-menu menu(label='Color') - button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#c8c8c8;', ng-click='set("preferences.hair.color", "white")') - button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#903a00;', ng-click='set("preferences.hair.color", "brown")') - button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#cfb853;', ng-click='set("preferences.hair.color", "blond")') - button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#ec720f;', ng-click='set("preferences.hair.color", "red")') - button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#2e2e2e;', ng-click='set("preferences.hair.color", "black")') + button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#c8c8c8;', ng-click='set({"preferences.hair.color": "white"})') + button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#903a00;', ng-click='set({"preferences.hair.color": "brown"})') + button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#cfb853;', ng-click='set({"preferences.hair.color": "blond"})') + button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#ec720f;', ng-click='set({"preferences.hair.color": "red" })') + button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#2e2e2e;', ng-click='set({"preferences.hair.color": "black"})') // Bangs li.customize-menu menu(label='Bangs') button(class='head_base_0 customize-option', type='button', ng-click='set("preferences.hair.bangs",0)') - button(class='hair_bangs_1_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.bangs",1)') - button(class='hair_bangs_2_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.bangs",2)') - button(class='hair_bangs_3_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.bangs",3)') + button(class='hair_bangs_1_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.bangs":1})') + button(class='hair_bangs_2_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.bangs":2})') + button(class='hair_bangs_3_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.bangs":3})') // Beard li.customize-menu menu(label='Beard') - button(class='head_base_0 customize-option', type='button', ng-click='set("preferences.hair.beard",0)') - button(class='hair_beard_1_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.beard",1)') - button(class='hair_beard_2_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.beard",2)') - button(class='hair_beard_3_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.beard",3)') + button(class='head_base_0 customize-option', type='button', ng-click='set({"preferences.hair.beard":0})') + button(class='hair_beard_1_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.beard":1})') + button(class='hair_beard_2_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.beard":2})') + button(class='hair_beard_3_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.beard":3})') // Mustache li.customize-menu menu(label='Mustache') - button(class='head_base_0 customize-option', type='button', ng-click='set("preferences.hair.mustache",0)') - button(class='hair_mustache_1_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.mustache",1)') - button(class='hair_mustache_2_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.mustache",2)') + button(class='head_base_0 customize-option', type='button', ng-click='set({"preferences.hair.mustache":0})') + button(class='hair_mustache_1_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.mustache":1})') + button(class='hair_mustache_2_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.mustache":2})') // Base li.customize-menu menu(label='Base') - button(class='head_base_0 customize-option', type='button', ng-click='set("preferences.hair.base",0)') - button(class='hair_base_1_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",1)') - button(class='hair_base_2_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",2)') - button(class='hair_base_3_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",3)') - button(class='hair_base_4_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",4)') - button(class='hair_base_5_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",5)') - button(class='hair_base_6_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",6)') - button(class='hair_base_7_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",7)') - button(class='hair_base_8_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",8)') + button(class='head_base_0 customize-option', type='button', ng-click='set({"preferences.hair.base":0})') + button(class='hair_base_1_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.base":1})') + button(class='hair_base_2_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.base":2})') + button(class='hair_base_3_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.base":3})') + button(class='hair_base_4_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.base":4})') + button(class='hair_base_5_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.base":5})') + button(class='hair_base_6_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.base":6})') + button(class='hair_base_7_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.base":7})') + button(class='hair_base_8_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.base":8})') .span4 // skin li.customize-menu menu(label='Basic Skins') - button.customize-option(type='button', class='skin_asian', ng-click='set("preferences.skin","asian")') - button.customize-option(type='button', class='skin_white', ng-click='set("preferences.skin","white")') - button.customize-option(type='button', class='skin_ea8349', ng-click='set("preferences.skin","ea8349")') - button.customize-option(type='button', class='skin_c06534', ng-click='set("preferences.skin","c06534")') - button.customize-option(type='button', class='skin_98461a', ng-click='set("preferences.skin","98461a")') - button.customize-option(type='button', class='skin_black', ng-click='set("preferences.skin","black")') - button.customize-option(type='button', class='skin_dead', ng-click='set("preferences.skin","dead")') - button.customize-option(type='button', class='skin_orc', ng-click='set("preferences.skin","orc")') + button.customize-option(type='button', class='skin_asian', ng-click='set({"preferences.skin":"asian" })') + button.customize-option(type='button', class='skin_white', ng-click='set({"preferences.skin":"white" })') + button.customize-option(type='button', class='skin_ea8349', ng-click='set({"preferences.skin":"ea8349"})') + button.customize-option(type='button', class='skin_c06534', ng-click='set({"preferences.skin":"c06534"})') + button.customize-option(type='button', class='skin_98461a', ng-click='set({"preferences.skin":"98461a"})') + button.customize-option(type='button', class='skin_black', ng-click='set({"preferences.skin":"black" })') + button.customize-option(type='button', class='skin_dead', ng-click='set({"preferences.skin":"dead" })') + button.customize-option(type='button', class='skin_orc', ng-click='set({"preferences.skin":"orc" })') // Rainbow Skin h5. @@ -83,7 +83,7 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template') button.customize-option(type='button', class='skin_d7a9f7', ng-class='{locked: !user.purchased.skin.d7a9f7}', ng-click='unlock("skin.d7a9f7")') button.customize-option(type='button', class='skin_800ed0', ng-class='{locked: !user.purchased.skin.800ed0}', ng-click='unlock("skin.800ed0")') button.customize-option(type='button', class='skin_rainbow', ng-class='{locked: !user.purchased.skin.rainbow}', ng-click='unlock("skin.rainbow")') - button.btn.btn-small.btn-primary(ng-hide='user.purchased.skin.eb052b && user.purchased.skin.f69922 && user.purchased.skin.f5d70f && user.purchased.skin.0ff591 && user.purchased.skin.2b43f6 && user.purchased.skin.d7a9f7 && user.purchased.skin.800ed0 && user.purchased.skin.rainbow', ng-click='unlock(["skin.eb052b", "skin.f69922", "skin.f5d70f", "skin.0ff591", "skin.2b43f6", "skin.d7a9f7", "skin.800ed0", "skin.rainbow"])') Unlock Set - 5 + button.btn.btn-small.btn-primary(ng-hide='user.purchased.skin.eb052b && user.purchased.skin.f69922 && user.purchased.skin.f5d70f && user.purchased.skin.0ff591 && user.purchased.skin.2b43f6 && user.purchased.skin.d7a9f7 && user.purchased.skin.800ed0 && user.purchased.skin.rainbow', ng-click='unlock("skin.eb052b,skin.f69922,skin.f5d70f,skin.0ff591,skin.2b43f6,skin.d7a9f7,skin.800ed0,skin.rainbow")') Unlock Set - 5 // Special Events // restore to d4df481 to see purchasing + "limited edition" code @@ -104,11 +104,11 @@ script(id='partials/options.profile.stats.html', type='text/ng-template') .span4.border-right(ng-show='user.stats.lvl >= 5') h4 | {{user.stats.class}}  - a.btn.btn-danger.btn-mini(ng-click='rerollClass()') Re-roll + a.btn.btn-danger.btn-mini(ng-click='changeClass(null)') Re-roll h6 Points: {{user.stats.points}} fieldset label.checkbox - input(type='checkbox', ng-model='user.preferences.automaticAllocation', ng-change='set("preferences.automaticAllocation", user.preferences.automaticAllocation?true: false)') + input(type='checkbox', ng-model='user.preferences.automaticAllocation', ng-change='set({"preferences.automaticAllocation": user.preferences.automaticAllocation?true: false})') | Automatic Allocation i.icon-question-sign(popover-trigger='mouseenter', popover-placement='bottom', popover="When 'automatic' is checked, your points will be allocated to the stat representing your task focus (see Task > Edit). When unchecked, you'll have one point to allocate each level. The system makes a suggestion, but you can ignore the suggestion.") table.table.table-striped diff --git a/views/options/settings.jade b/views/options/settings.jade index 50f876c201..46cf6f327e 100644 --- a/views/options/settings.jade +++ b/views/options/settings.jade @@ -30,8 +30,8 @@ script(type='text/ng-template', id='partials/options.settings.settings.html') select(ng-model='language.code', ng-options='lang.code as lang.name for lang in avalaibleLanguages', ng-change='changeLanguage()') hr h4 Misc - button.btn(ng-hide='user.preferences.hideHeader', ng-click='set("preferences.hideHeader",true)', popover-trigger='mouseenter', popover-placement='right', popover='Hide your avatar, Health/Experience bars, and party.') Hide Header - button.btn(ng-show='user.preferences.hideHeader', ng-click='set("preferences.hideHeader",false)', popover-trigger='mouseenter', popover='Display your avatar, Health/Experience bars, and party.') Show Header + button.btn(ng-hide='user.preferences.hideHeader', ng-click='set({"preferences.hideHeader":true})', popover-trigger='mouseenter', popover-placement='right', popover='Hide your avatar, Health/Experience bars, and party.') Hide Header + button.btn(ng-show='user.preferences.hideHeader', ng-click='set({"preferences.hideHeader":false})', popover-trigger='mouseenter', popover='Display your avatar, Health/Experience bars, and party.') Show Header button.btn(ng-click='showTour()', popover-trigger='mouseenter', popover='Restart the introductory tour from when you first joined HabitRPG.') Show Tour button.btn(ng-click='showBailey()', popover-trigger='mouseenter', popover='Bring Bailey the Town Crier out of hiding so you can review past news.') Show Bailey button.btn(ng-click='modals.restore = true', popover-trigger='mouseenter', popover='Manually change values like Health, Level, and Gold.') Fix Character Values diff --git a/views/options/social/group.jade b/views/options/social/group.jade index 17ef4cbde3..62f54a5a7e 100644 --- a/views/options/social/group.jade +++ b/views/options/social/group.jade @@ -46,7 +46,7 @@ a.pull-right.gem-wallet(popover-trigger='mouseenter', popover-title='Guild Bank' ng-model='user.party.order', ng-controller='ChatCtrl', ng-options='k as v for (k , v) in partyOrderChoices', - ng-change='set("party.order", user.party.order)' + ng-change='set({"party.order": user.party.order})' ) table.table.table-striped(bindonce='group') tr(ng-repeat='member in group.members') diff --git a/views/shared/header/header.jade b/views/shared/header/header.jade index ee8fde65d9..7ea8f6315f 100644 --- a/views/shared/header/header.jade +++ b/views/shared/header/header.jade @@ -11,15 +11,15 @@ // stat bars .hero-stats .meter.health(title='Health') - .bar(style='width: {{percent(user.stats.hp, 50)}}%;') + .bar(style='width: {{Shared.percent(user.stats.hp, 50)}}%;') span.meter-text i.icon-heart | {{user.stats.hp | number:0}} / 50 .meter.experience(title='Experience') - .bar(style='width: {{percent(user.stats.exp,tnl(user.stats.lvl))}}%;') + .bar(style='width: {{Shared.percent(user.stats.exp,Shared.tnl(user.stats.lvl))}}%;') span.meter-text i.icon-star - | {{user.stats.exp | number:0}} / {{tnl(user.stats.lvl) | number:0}} + | {{user.stats.exp | number:0}} / {{Shared.tnl(user.stats.lvl) | number:0}} // FIXME doesn't look great here, but the "Experience" CSS title rollover covers it where it was before span(ng-show='user.history.exp') a(ng-click='toggleChart("exp")', tooltip='Progress') diff --git a/views/shared/modals/achievements.jade b/views/shared/modals/achievements.jade index c599d910e1..22cdd540c2 100644 --- a/views/shared/modals/achievements.jade +++ b/views/shared/modals/achievements.jade @@ -40,7 +40,7 @@ div(modal='user.flags.contributor') {{user.profile.name}}, you awesome person! You're now a level {{user.contributor.level}} contributor for helping HabitRPG. See what prizes you've earned for your contribution! .modal-footer - button.btn.btn-default.cancel(ng-click='set("flags.contributor",false)') Ok + button.btn.btn-default.cancel(ng-click='set("{flags.contributor":false})') Ok diff --git a/views/shared/modals/classes.jade b/views/shared/modals/classes.jade index ae5c5c06a3..b26bcf8668 100644 --- a/views/shared/modals/classes.jade +++ b/views/shared/modals/classes.jade @@ -66,4 +66,4 @@ span(class='weapon_healer_6') .modal-footer - button.btn.btn-default.cancel(ng-click='rerollSubmit()') Select \ No newline at end of file + button.btn.btn-default.cancel(ng-click='selectedClass && changeClass(selectedClass)') Select \ No newline at end of file diff --git a/views/shared/modals/death.jade b/views/shared/modals/death.jade index 7069de2135..5b8c7326ed 100644 --- a/views/shared/modals/death.jade +++ b/views/shared/modals/death.jade @@ -9,7 +9,7 @@ div(modal='user.stats.hp <= 0', options='{backdrop:true, keyboard:false, backdro .row-fluid .span4 p - a.btn.btn-danger.btn-large.notification-action(ng-click='User.revive()') Continue + a.btn.btn-danger.btn-large.notification-action(ng-click='user.ops.revive({})') Continue .span8 p | You've lost a Level, all your Gold, and a random piece of Equipment. Arise, Habiteer, and try again! Curb those negative Habits, be vigilant in completion of Dailies, and hold death at arm's length with a Health Potion if you falter! diff --git a/views/shared/modals/pets.jade b/views/shared/modals/pets.jade index 0a2f152d08..ccac0bd490 100644 --- a/views/shared/modals/pets.jade +++ b/views/shared/modals/pets.jade @@ -10,7 +10,7 @@ div(modal='modals.dropsEnabled') p span.item-drop-icon(class='Pet_Egg_Wolf', style='margin-left: 0px') span. - You've unlocked the Drop System! Now when you complete tasks, you have a small chance of finding an item. You just found a {{Items.eggs.Wolf.text}} Egg! {{Items.eggs.Wolf.notes}} + You've unlocked the Drop System! Now when you complete tasks, you have a small chance of finding an item. You just found a {{Content.eggs.Wolf.text}} Egg! {{Content.eggs.Wolf.notes}} br p. If you've got your eye on a pet, but can't wait any longer for it to drop, use Gems in Options > Inventory to buy one! diff --git a/views/shared/profiles/stats.jade b/views/shared/profiles/stats.jade index 1552d4cc02..e0ff5465ae 100644 --- a/views/shared/profiles/stats.jade +++ b/views/shared/profiles/stats.jade @@ -23,7 +23,7 @@ p | : {{profile.stats.lvl}} p strong Experience - | : {{profile.stats.exp | number:0}} / {{tnl(profile.stats.lvl)}} + | : {{profile.stats.exp | number:0}} / {{Shared.tnl(profile.stats.lvl)}} p strong Strength | :  diff --git a/views/shared/tasks/lists.jade b/views/shared/tasks/lists.jade index ec09f0a17b..82ed6e93a7 100644 --- a/views/shared/tasks/lists.jade +++ b/views/shared/tasks/lists.jade @@ -18,10 +18,10 @@ script(id='templates/habitrpg-tasks.html', type="text/ng-template") // Gold & Gems span.option-box.pull-right.wallet(bo-if='main && list.type=="reward"') .money - | {{gold(user.stats.gp)}} + | {{Shared.gold(user.stats.gp)}} span.shop_gold(tooltip='Gold') .money - | {{silver(user.stats.gp)}} + | {{Shared.silver(user.stats.gp)}} span.shop_silver(tooltip='Silver') // Header @@ -62,7 +62,7 @@ script(id='templates/habitrpg-tasks.html', type="text/ng-template") // Spells ul.items(ng-if='main && list.type=="reward" && user.stats.class') - li.task.reward-item(ng-repeat='(k,spell) in Items.spells[user.stats.class]', ng-show='user.stats.lvl >= spell.lvl') + li.task.reward-item(ng-repeat='(k,spell) in Content.spells[user.stats.class]', ng-show='user.stats.lvl >= spell.lvl') .task-meta-controls span.task-notes(popover-trigger='mouseenter', popover-placement='left', popover='{{spell.notes}}', popover-title='{{spell.text}}') i.icon-comment diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index 55c87fbaaa..770c643a1a 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 {{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"]', 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 @@ -118,13 +118,13 @@ li(bindonce='list', ng-repeat='task in obj[list.type+"s"]', class='task {{taskCl 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)') + 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')