diff --git a/common/dist/scripts/habitrpg-shared.js b/common/dist/scripts/habitrpg-shared.js
index ff9863e7f3..4f5ac0ac59 100644
--- a/common/dist/scripts/habitrpg-shared.js
+++ b/common/dist/scripts/habitrpg-shared.js
@@ -176,7 +176,7 @@ gear = {
0: {
text: t('weaponWarrior0Text'),
notes: t('weaponWarrior0Notes'),
- value: 0
+ value: 1
},
1: {
text: t('weaponWarrior1Text'),
@@ -4574,7 +4574,7 @@ api.userDefaults = {
value: 0,
up: true,
down: false,
- attribute: 'per'
+ attribute: 'str'
}, {
type: 'habit',
text: t('defaultHabit2Text'),
@@ -4582,7 +4582,7 @@ api.userDefaults = {
value: 0,
up: false,
down: true,
- attribute: 'con'
+ attribute: 'str'
}, {
type: 'habit',
text: t('defaultHabit3Text'),
@@ -4593,52 +4593,7 @@ api.userDefaults = {
attribute: 'str'
}
],
- dailys: [
- {
- type: 'daily',
- text: t('defaultDaily1Text'),
- notes: t('defaultDaily1Notes'),
- value: 0,
- completed: false,
- repeat: repeat,
- attribute: 'per'
- }, {
- type: 'daily',
- text: t('defaultDaily2Text'),
- notes: t('defaultDaily2Notes'),
- value: 3,
- completed: false,
- repeat: repeat,
- attribute: 'con'
- }, {
- type: 'daily',
- text: t('defaultDaily3Text'),
- notes: t('defaultDaily3Notes'),
- value: -10,
- completed: false,
- repeat: repeat,
- attribute: 'int'
- }, {
- type: 'daily',
- text: t('defaultDaily4Text'),
- notes: t('defaultDaily4Notes'),
- checklist: [
- {
- completed: true,
- text: t('defaultDaily4Checklist1')
- }, {
- completed: false,
- text: t('defaultDaily4Checklist2')
- }, {
- completed: false,
- text: t('defaultDaily4Checklist3')
- }
- ],
- completed: false,
- repeat: repeat,
- attribute: 'str'
- }
- ],
+ dailys: [],
todos: [
{
type: 'todo',
@@ -4646,81 +4601,9 @@ api.userDefaults = {
notes: t('defaultTodoNotes'),
completed: false,
attribute: 'int'
- }, {
- type: 'todo',
- text: t('defaultTodo2Text'),
- notes: t('defaultTodoNotes'),
- checklist: [
- {
- completed: false,
- text: t('defaultTodo2Checklist1')
- }, {
- completed: false,
- text: t('defaultTodo2Checklist2')
- }, {
- completed: false,
- text: t('defaultTodo2Checklist3')
- }
- ],
- completed: false,
- attribute: 'per'
- }, {
- type: 'todo',
- text: t('defaultTodo3Text'),
- notes: t('defaultTodoNotes'),
- checklist: [
- {
- completed: false,
- text: t('defaultTodo3Checklist1')
- }, {
- completed: false,
- text: t('defaultTodo3Checklist2')
- }, {
- completed: false,
- text: t('defaultTodo3Checklist3')
- }
- ],
- completed: false,
- attribute: 'per'
- }, {
- type: 'todo',
- text: t('defaultTodo4Text'),
- notes: t('defaultTodoNotes'),
- checklist: [
- {
- completed: false,
- text: t('defaultTodo4Checklist1')
- }, {
- completed: false,
- text: t('defaultTodo4Checklist2')
- }, {
- completed: false,
- text: t('defaultTodo4Checklist3')
- }
- ],
- completed: false,
- attribute: 'per'
- }, {
- type: 'todo',
- text: t('defaultTodo5Text'),
- notes: t('defaultTodoNotes'),
- completed: false,
- attribute: 'per'
- }
- ],
- rewards: [
- {
- type: 'reward',
- text: t('defaultReward1Text'),
- notes: t('defaultReward1Notes'),
- value: 20
- }, {
- type: 'reward',
- text: t('defaultReward2Text'),
- notes: t('defaultReward2Notes'),
- value: 10
}
],
+ rewards: [],
tags: [
{
name: t('defaultTag1')
diff --git a/common/locales/en/defaultTasks.json b/common/locales/en/defaultTasks.json
index 239c9b645a..e10440d5f1 100644
--- a/common/locales/en/defaultTasks.json
+++ b/common/locales/en/defaultTasks.json
@@ -1,12 +1,12 @@
{
- "defaultHabit1Text": "1h Productive Work",
- "defaultHabit1Notes": "When you create a new Habit, you can click the Edit icon and choose for it to represent a positive habit, a negative habit, or both. For some Habits, like this one, it only makes sense to gain points.",
+ "defaultHabit1Text": "Good Habit",
+ "defaultHabit1Notes": " ",
- "defaultHabit2Text": "Eat Junk Food",
- "defaultHabit2Notes": "For others, it only makes sense to *lose* points.",
-
- "defaultHabit3Text": "Take The Stairs",
- "defaultHabit3Notes": "For the rest, both + and - make sense (stairs = gain, elevator = lose).",
+ "defaultHabit2Text": "Bad Habit",
+ "defaultHabit2Notes": " ",
+
+ "defaultHabit3Text": "Good or Bad Habit",
+ "defaultHabit3Notes": " ",
"defaultDaily1Text": "1h Personal Project",
"defaultDaily1Notes": "All tasks default to yellow when they are created. This means you will take only moderate damage when they are missed and will gain only a moderate reward when they are completed.",
diff --git a/common/locales/en/npc.json b/common/locales/en/npc.json
index 3ee492cf72..deb5166311 100644
--- a/common/locales/en/npc.json
+++ b/common/locales/en/npc.json
@@ -24,6 +24,7 @@
"payNote": "Note: PayPal sometimes takes a long time to clear. We recommend paying with card.",
"card": "Card",
"paymentMethods": "Payment Methods:",
+
"welcomeHabit": "Welcome to HabitRPG",
"welcomeHabitT1": "Welcome to HabitRPG, a habit tracker that treats your tasks like a Role Playing Game. I'm",
"welcomeHabitT2": "your guide!",
@@ -61,5 +62,17 @@
"spells": "Spells",
"spellsText": "You can now unlock class-specific spells. You'll see your first at level 11. Your mana replenishes 10 points per day, plus 1 point per completed ",
"toDo": "To-Do",
- "moreClass": "For more information on the class-system, see"
+ "moreClass": "For more information on the class-system, see",
+
+ "tourWelcome": "Welcome to Habitica! This is your To-Do list. Check off a task!",
+ "tourExp": "Great job! Checking off a task gives you Experience and Gold!",
+ "tourDailies": "This column is for Daily tasks. Enter a task you should do every day! Sample Dailies : Make Bed , Floss , Check Work Email ",
+ "tourCron": "Splendid! Your Dailies will reset every day.",
+ "tourHP": "Watch out! If you don't complete a Daily by midnight, it will hurt you!",
+ "tourHabits": "This column is for good and bad Habits that you do many times a day! Click the pencil to edit the names.
Sample Good Habits : Eat a vegetable , 15 minutes productive work Sample Bad Habits : Smoke , Procrastinate Sample Good or Bad Habits : Took Stairs/Elevator , Drank Water/Soda ",
+ "tourStats": "Good Habits add Experience and Gold! Bad Habits remove health.",
+ "tourGP": "Buy the Training Sword with the gold you just earned!",
+ "tourAvatar": "Now your avatar has the Training Sword. Click on your avatar to customize it!",
+ "tourScrollDown": "Be sure to scroll all the way down to see all the options! Click on your avatar again to return to the tasks page.",
+ "tourMuchMore": "When you're done with tasks, you can form a Party with friends, chat in the shared-interest Guilds, join Challenges, and more!"
}
diff --git a/common/script/content.coffee b/common/script/content.coffee
index 394ab2461b..08b41e97bb 100644
--- a/common/script/content.coffee
+++ b/common/script/content.coffee
@@ -56,7 +56,7 @@ gear =
0:
text: t('weaponBase0Text'), notes: t('weaponBase0Notes'), value:0
warrior:
- 0: text: t('weaponWarrior0Text'), notes: t('weaponWarrior0Notes'), value:0
+ 0: text: t('weaponWarrior0Text'), notes: t('weaponWarrior0Notes'), value:1
1: text: t('weaponWarrior1Text'), notes: t('weaponWarrior1Notes', {str: 3}), str: 3, value:20
2: text: t('weaponWarrior2Text'), notes: t('weaponWarrior2Notes', {str: 6}), str: 6, value:30
3: text: t('weaponWarrior3Text'), notes: t('weaponWarrior3Notes', {str: 9}), str: 9, value:45
@@ -1654,29 +1654,29 @@ _.each api.subscriptionBlocks, (b,k)->b.key = k
repeat = {m:true,t:true,w:true,th:true,f:true,s:true,su:true}
api.userDefaults =
habits: [
- {type: 'habit', text: t('defaultHabit1Text'), notes: t('defaultHabit1Notes'), value: 0, up: true, down: false, attribute: 'per' }
- {type: 'habit', text: t('defaultHabit2Text'), notes: t('defaultHabit2Notes'), value: 0, up: false, down: true, attribute: 'con'}
+ {type: 'habit', text: t('defaultHabit1Text'), notes: t('defaultHabit1Notes'), value: 0, up: true, down: false, attribute: 'str' }
+ {type: 'habit', text: t('defaultHabit2Text'), notes: t('defaultHabit2Notes'), value: 0, up: false, down: true, attribute: 'str'}
{type: 'habit', text: t('defaultHabit3Text'), notes: t('defaultHabit3Notes'), value: 0, up: true, down: true, attribute: 'str'}
]
dailys: [
- {type: 'daily', text: t('defaultDaily1Text'), notes: t('defaultDaily1Notes'), value: 0, completed: false, repeat: repeat, attribute: 'per' }
- {type: 'daily', text: t('defaultDaily2Text'), notes: t('defaultDaily2Notes'), value: 3, completed: false, repeat: repeat, attribute: 'con' }
- {type: 'daily', text: t('defaultDaily3Text'), notes: t('defaultDaily3Notes'), value: -10, completed: false, repeat: repeat, attribute: 'int' }
- {type: 'daily', text: t('defaultDaily4Text'), notes: t('defaultDaily4Notes'), checklist: [{completed: true, text: t('defaultDaily4Checklist1') }, {completed: false, text: t('defaultDaily4Checklist2')}, {completed: false, text: t('defaultDaily4Checklist3')}], completed: false, repeat: repeat, attribute: 'str' }
+# {type: 'daily', text: t('defaultDaily1Text'), notes: t('defaultDaily1Notes'), value: 0, completed: false, repeat: repeat, attribute: 'per' }
+# {type: 'daily', text: t('defaultDaily2Text'), notes: t('defaultDaily2Notes'), value: 3, completed: false, repeat: repeat, attribute: 'con' }
+# {type: 'daily', text: t('defaultDaily3Text'), notes: t('defaultDaily3Notes'), value: -10, completed: false, repeat: repeat, attribute: 'int' }
+# {type: 'daily', text: t('defaultDaily4Text'), notes: t('defaultDaily4Notes'), checklist: [{completed: true, text: t('defaultDaily4Checklist1') }, {completed: false, text: t('defaultDaily4Checklist2')}, {completed: false, text: t('defaultDaily4Checklist3')}], completed: false, repeat: repeat, attribute: 'str' }
]
todos: [
{type: 'todo', text: t('defaultTodo1Text'), notes: t('defaultTodoNotes'), completed: false, attribute: 'int' }
- {type: 'todo', text: t('defaultTodo2Text'), notes: t('defaultTodoNotes'), checklist: [{completed: false, text: t('defaultTodo2Checklist1') }, {completed: false, text: t('defaultTodo2Checklist2')}, {completed: false, text: t('defaultTodo2Checklist3')}], completed: false, attribute: 'per' }
- {type: 'todo', text: t('defaultTodo3Text'), notes: t('defaultTodoNotes'), checklist: [{completed: false, text: t('defaultTodo3Checklist1') }, {completed: false, text: t('defaultTodo3Checklist2')}, {completed: false, text: t('defaultTodo3Checklist3')}], completed: false, attribute: 'per' }
- {type: 'todo', text: t('defaultTodo4Text'), notes: t('defaultTodoNotes'), checklist: [{completed: false, text: t('defaultTodo4Checklist1') }, {completed: false, text: t('defaultTodo4Checklist2')}, {completed: false, text: t('defaultTodo4Checklist3')}], completed: false, attribute: 'per' }
- {type: 'todo', text: t('defaultTodo5Text'), notes: t('defaultTodoNotes'), completed: false, attribute: 'per' }
+# {type: 'todo', text: t('defaultTodo2Text'), notes: t('defaultTodoNotes'), checklist: [{completed: false, text: t('defaultTodo2Checklist1') }, {completed: false, text: t('defaultTodo2Checklist2')}, {completed: false, text: t('defaultTodo2Checklist3')}], completed: false, attribute: 'per' }
+# {type: 'todo', text: t('defaultTodo3Text'), notes: t('defaultTodoNotes'), checklist: [{completed: false, text: t('defaultTodo3Checklist1') }, {completed: false, text: t('defaultTodo3Checklist2')}, {completed: false, text: t('defaultTodo3Checklist3')}], completed: false, attribute: 'per' }
+# {type: 'todo', text: t('defaultTodo4Text'), notes: t('defaultTodoNotes'), checklist: [{completed: false, text: t('defaultTodo4Checklist1') }, {completed: false, text: t('defaultTodo4Checklist2')}, {completed: false, text: t('defaultTodo4Checklist3')}], completed: false, attribute: 'per' }
+# {type: 'todo', text: t('defaultTodo5Text'), notes: t('defaultTodoNotes'), completed: false, attribute: 'per' }
]
rewards: [
- {type: 'reward', text: t('defaultReward1Text'), notes: t('defaultReward1Notes'), value: 20 }
- {type: 'reward', text: t('defaultReward2Text'), notes: t('defaultReward2Notes'), value: 10 }
+# {type: 'reward', text: t('defaultReward1Text'), notes: t('defaultReward1Notes'), value: 20 }
+# {type: 'reward', text: t('defaultReward2Text'), notes: t('defaultReward2Notes'), value: 10 }
]
tags: [
diff --git a/migrations/20150218_interactive_tour.js b/migrations/20150218_interactive_tour.js
new file mode 100644
index 0000000000..50e633b1c4
--- /dev/null
+++ b/migrations/20150218_interactive_tour.js
@@ -0,0 +1,3 @@
+//db.users.update({'flags.showTour':{$ne:false}},{$set:{'flags.tour.intro':0}},{multi:1})
+//db.users.update({'flags.showTour':false},{$set:{'flags.tour.intro':-1}},{multi:1})
+db.users.update({},{$set:{'flags.tour.intro':-1}},{multi:1})
\ No newline at end of file
diff --git a/website/public/js/controllers/settingsCtrl.js b/website/public/js/controllers/settingsCtrl.js
index 9bd419998e..6ba382bb44 100644
--- a/website/public/js/controllers/settingsCtrl.js
+++ b/website/public/js/controllers/settingsCtrl.js
@@ -55,12 +55,11 @@ habitrpg.controller('SettingsCtrl',
$scope.showTour = function(){
User.set({'flags.showTour':true});
- $location.path('/tasks');
- $timeout(Guide.initTour);
+ Guide.goto('intro', 0, true);
}
$scope.showClassesTour = function(){
- Guide.classesTour();
+ Guide.goto('classes', 0, true);
}
$scope.showBailey = function(){
diff --git a/website/public/js/controllers/tasksCtrl.js b/website/public/js/controllers/tasksCtrl.js
index 7294d5c2f7..c1604721c0 100644
--- a/website/public/js/controllers/tasksCtrl.js
+++ b/website/public/js/controllers/tasksCtrl.js
@@ -1,7 +1,7 @@
"use strict";
-habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','Notification', '$http', 'ApiUrl', '$timeout', 'Shared',
- function($scope, $rootScope, $location, User, Notification, $http, ApiUrl, $timeout, Shared) {
+habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','Notification', '$http', 'ApiUrl', '$timeout', 'Shared', 'Guide',
+ function($scope, $rootScope, $location, User, Notification, $http, ApiUrl, $timeout, Shared, Guide) {
$scope.obj = User.user; // used for task-lists
$scope.user = User.user;
@@ -15,6 +15,7 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
break;
case 'todo':
$rootScope.playSound('ToDo');
+ Guide.goto('intro', 1);
break;
default:
if (direction === 'down') $rootScope.playSound('Minus_Habit');
@@ -46,6 +47,7 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
}
delete listDef.newTask;
delete listDef.focus;
+ if (listDef.type=='daily') Guide.goto('intro', 2);
};
$scope.toggleBulk = function(list) {
@@ -92,6 +94,7 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
if (!stayOpen) task._editing = false;
if (isSaveAndClose)
$("#task-" + task.id).parent().children('.popover').removeClass('in');
+ if (task.type == 'habit') Guide.goto('intro', 3);
};
/**
@@ -194,6 +197,7 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
$scope.buy = function(item) {
User.user.ops.buy({params:{key:item.key}});
$rootScope.playSound('Reward');
+ Guide.goto('intro', 4);
};
diff --git a/website/public/js/controllers/userCtrl.js b/website/public/js/controllers/userCtrl.js
index cf5336f4c7..81c1958953 100644
--- a/website/public/js/controllers/userCtrl.js
+++ b/website/public/js/controllers/userCtrl.js
@@ -27,8 +27,7 @@ habitrpg.controller("UserCtrl", ['$rootScope', '$scope', '$location', 'User', '$
User.user.ops.changeClass({query:{class:klass}});
$scope.selectedClass = undefined;
Shared.updateStore(User.user);
- $state.go('options.profile.stats');
- window.setTimeout(Guide.classesTour, 10);
+ Guide.goto('classes', 0,true);
}
$scope.save = function(){
diff --git a/website/public/js/services/guideServices.js b/website/public/js/services/guideServices.js
index 45f02161e0..614f51af16 100644
--- a/website/public/js/services/guideServices.js
+++ b/website/public/js/services/guideServices.js
@@ -7,214 +7,205 @@
angular.module('habitrpg').factory('Guide',
['$rootScope', 'User', '$timeout', '$state',
function($rootScope, User, $timeout, $state) {
- /**
- * Init and show the welcome tour. Note we do it listening to a $rootScope broadcasted 'userLoaded' message,
- * this because we need to determine whether to show the tour *after* the user has been pulled from the server,
- * otherwise it's always start off as true, and then get set to false later
- */
- var tourRunning = false;
- $rootScope.$on('userUpdated', initTour);
- function initTour(){
- if (User.user.flags.showTour === false || tourRunning) return;
- tourRunning = true;
- var tourSteps = [
- {
- orphan:true,
- title: window.env.t('welcomeHabit'),
- content: window.env.t('welcomeHabitT1') + " Justin , " + window.env.t('welcomeHabitT2'),
- }, {
- element: ".main-herobox",
- title: window.env.t('yourAvatar'),
- content: window.env.t('yourAvatarText'),
- }, {
- element: ".main-herobox",
- title: window.env.t('avatarCustom'),
- content: window.env.t('avatarCustomText'),
- }, {
- element: ".hero-stats",
- title: window.env.t('hitPoints'),
- content: window.env.t('hitPointsText'),
- }, {
- element: ".hero-stats",
- title: window.env.t('expPoints'),
- content: window.env.t('expPointsText'),
- }, {
- element: "ul.habits",
- title: window.env.t('typeGoals'),
- content: window.env.t('typeGoalsText'),
- placement: "top"
- }, {
- element: "ul.habits",
- title: window.env.t('habits'),
- content: window.env.t('tourHabits'),
- placement: "top"
- }, {
- element: "ul.dailys",
- title: window.env.t('dailies'),
- content: window.env.t('tourDailies'),
- placement: "top"
- }, {
- element: "ul.todos",
- title: window.env.t('todos'),
- content: window.env.t('tourTodos'),
- placement: "top",
- }, {
- element: "ul.main-list.rewards",
- title: window.env.t('rewards'),
- content: window.env.t('tourRewards'),
- placement: "top"
- }, {
- element: "ul.habits li:first-child",
- title: window.env.t('hoverOver'),
- content: window.env.t('hoverOverText'),
- placement: "right"
- }, {
- orphan:true,
- title: window.env.t('unlockFeatures'),
- content: window.env.t('unlockFeaturesT1') + " " + window.env.t('habitWiki') + " " + window.env.t('unlockFeaturesT2'),
- placement: "right"
- }
- ];
- $('.main-herobox').popover('destroy');
- var tour = new Tour({
- backdrop: true,
- //orphan: true,
- //keyboard: false,
- template: '
« Prev Next » Pause
' + window.env.t('endTour') + ' ',
- onEnd: function(){
- User.set({'flags.showTour': false});
- }
- });
- _.each(tourSteps, function(step) {
+
+ var chapters = {
+ intro: [
+ [ //0
+ {
+ state: 'tasks',
+ element: ".task-column.todos",
+ content: window.env.t('tourWelcome'),
+ placement: "top"
+ }
+ ], [ //1
+ {
+ state: 'tasks',
+ element: '.sticky-wrapper',
+ content: window.env.t('tourExp'),
+ placement: 'bottom'
+ }, {
+ state: 'tasks',
+ element: ".task-column.dailys",
+ content: window.env.t('tourDailies'),
+ placement: "top"
+ }
+ ], [ //2
+ {
+ orphan: true,
+ content: window.env.t('tourCron'),
+ placement: 'bottom'
+ }, {
+ state: 'tasks',
+ element: '.meter.health',
+ content: window.env.t('tourHP'),
+ placement: 'bottom'
+ }, {
+ state: 'tasks',
+ element: ".task-column.habits",
+ content: window.env.t('tourHabits'),
+ placement: "right"
+ }
+ ], [ //3
+ {
+ state: 'tasks',
+ element: ".hero-stats",
+ content: window.env.t('tourStats')
+ }, {
+ state: 'tasks',
+ element: ".task-column.rewards",
+ content: window.env.t('tourGP'),
+ placement: 'left'
+ }
+ ], [ //4
+ {
+ state: 'tasks',
+ element: '.main-herobox',
+ content: window.env.t('tourAvatar'),
+ placement: 'bottom'
+ }
+ ], [ //5
+ {
+ state: 'options.profile.avatar',
+ orphan: true,
+ content: window.env.t('tourScrollDown')
+ }, {
+ element: "ul.toolbar-nav",
+ backdrop:false,
+ content: window.env.t('tourMuchMore'),
+ placement: "bottom",
+ final: true,
+ //onHidden: function(){
+ // $rootScope.$watch('user.flags.customizationsNotification', _.partial(goto, 'intro', 4));
+ //}
+ }
+ ]
+ ],
+ classes: [
+ [
+ {
+ state: 'options.inventory.equipment',
+ element: '.equipment-tab',
+ title: window.env.t('classGear'),
+ content: window.env.t('classGearText', {klass: User.user.stats.class})
+ }, {
+ state: 'options.profile.stats',
+ element: ".allocate-stats",
+ title: window.env.t('stats'),
+ content: window.env.t('classStats')
+ }, {
+ state: 'options.profile.stats',
+ element: ".auto-allocate",
+ title: window.env.t('autoAllocate'),
+ placement: 'left',
+ content: window.env.t('autoAllocateText')
+ }, {
+ element: ".meter.mana",
+ title: window.env.t('spells'),
+ content: window.env.t('spellsText') + " " + window.env.t('toDo') + " ."
+ }, {
+ orphan: true,
+ title: window.env.t('readMore'),
+ content: window.env.t('moreClass') + " Wikia .",
+ final: true
+ }
+ ]
+ ]
+ }
+ _.each(chapters, function(chapter, k){
+ _(chapter).flatten().each(function(step) {
step.content = "";
+ $(step.element).popover('destroy'); // destroy existing hover popovers so we can add our own
step.onShow = function(){
- // Since all the steps are currently on the tasks page, ensure we go back there for each step in case they
- // clicked elsewhere during the tour. FIXME: $state.go() returns a promise, necessary for async tour steps;
- // however, that's not working here - have to use timeout instead :/
- if (!$state.is('tasks')) return $timeout(function(){$state.go('tasks');}, 0)
+ // step.path doesn't work in Angular do to async ui-router. Our custom solution:
+ if (step.state && !$state.is(step.state)) {
+ // $state.go() returns a promise, necessary for async tour steps; however, that's not working here - have to use timeout instead :/
+ $state.go(step.state);
+ return $timeout(function(){});
+ }
}
- step.html = true;
- tour.addStep(step);
+ step.onHide = function(){
+ if (step.final) { // -1 indicates complete
+ var ups={};ups['flags.tour.'+k] = -1;
+ User.set(ups);
+ }
+ }
+ })
+ })
+
+ var tour = {};
+ _.each(chapters, function(v,k){
+ tour[k] = new Tour({
+ backdrop: true,
+ template: function(i,step){
+ return ' ' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ //'
' + (step.final ? 'Finish Tour' : 'Hide') + ' '+
+ (step.final ? '
Finish Tour ' : '')+
+ '
' +
+ '« Prev ' +
+ 'Next » ' +
+ 'Pause ' +
+ '
' +
+ '
' +
+ '
';
+ },
+ storage: false,
+ //onEnd: function(){
+ // User.set({'flags.showTour': false});
+ //}
});
- tour.restart(); // Tour doesn't quite mesh with our handling of flags.showTour, just restart it on page load
- //tour.start(true);
- };
+ });
- var alreadyShown = function(before, after) {
- return !(!before && after === true);
- };
-
- var showPopover = function(selector, title, html, placement) {
- if (!placement) placement = 'bottom';
- $(selector).popover('destroy');
- var button = "" + window.env.t('close') + " ";
- if (env.worldDmg.guide) {
- html = "
" + html + '
' + button + '
';
+ var goto = function(chapter, page, force) {
+ var curr = User.user.flags.tour[chapter];
+ if ((page != curr+1 || curr > page) && !force) return;
+ var updates = {};updates['flags.tour.'+chapter] = page;
+ User.set(updates);
+ var chap = tour[chapter], opts = chap._options;
+ opts.steps = [];
+ _.times(page, function(p){
+ opts.steps = opts.steps.concat(chapters[chapter][p]);
+ })
+ var end = opts.steps.length;
+ opts.steps = opts.steps.concat(chapters[chapter][page]);
+ chap._removeState('end');
+ if (chap._inited) {
+ chap.goTo(end);
} else {
- html = "
" + html + '
' + button + '
';
+ chap.setCurrentStep(end);
+ chap.start();
}
- $(selector).popover({
- title: title,
- placement: placement,
- trigger: 'manual',
- html: true,
- content: html
- }).popover('show');
- };
+ }
- $rootScope.$watch('user.flags.customizationsNotification', function(after, before) {
- if (alreadyShown(before, after)) return;
- showPopover('.main-herobox', window.env.t('customAvatar'), window.env.t('customAvatarText'), 'bottom');
- });
+ //Init and show the welcome tour (only after user is pulled from server & wrapped).
+ var watcher = $rootScope.$watch('User.user.ops.update', function(updateFn){
+ if (!updateFn) return; // only run after user has been wrapped
+ watcher(); // deregister watcher
+ if (window.env.IS_MOBILE) return; // Don't show tour immediately on mobile devices
+ goto('intro', User.user.flags.tour.intro, true);
- $rootScope.$watch('user.flags.itemsEnabled', function(after, before) {
- if (alreadyShown(before, after)) return;
- var html = window.env.t('storeUnlockedText');
- showPopover('div.rewards', window.env.t('storeUnlocked'), html, 'left');
- });
-
- $rootScope.$watch('user.flags.partyEnabled', function(after, before) {
- if (alreadyShown(before, after)) return;
- var html = window.env.t('partySysText');
- showPopover('.user-menu', window.env.t('partySys'), html, 'bottom');
- });
-
- $rootScope.$watch('user.flags.dropsEnabled', function(after, before) {
- if (alreadyShown(before, after)) return;
- var eggs = User.user.items.eggs || {};
- if (!eggs) {
- eggs['Wolf'] = 1; // This is also set on the server
- }
- $rootScope.openModal('dropsEnabled');
- });
-
- $rootScope.$watch('user.flags.rebirthEnabled', function(after, before) {
+ var alreadyShown = function(before, after) { return !(!before && after === true) };
+ //$rootScope.$watch('user.flags.dropsEnabled', _.flow(alreadyShown, function(already) { //FIXME requires lodash@~3.2.0
+ $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams){
+ if (toState.name == 'options.profile.avatar') goto('intro', 5);
+ })
+ $rootScope.$watch('user.flags.dropsEnabled', function(after, before) {
+ if (alreadyShown(before,after)) return;
+ var eggs = User.user.items.eggs || {};
+ if (!eggs) eggs['Wolf'] = 1; // This is also set on the server
+ $rootScope.openModal('dropsEnabled');
+ });
+ $rootScope.$watch('user.flags.rebirthEnabled', function(after, before) {
if (alreadyShown(before, after)) return;
$rootScope.openModal('rebirthEnabled');
+ });
});
-
- /**
- * Classes Tour
- */
- function classesTour(){
-
- // TODO notice my hack-job `onShow: _.once()` functions. Without these, the syncronous path redirects won't properly handle showing tour
- var tourSteps = [
- {
- path: '/#/options/inventory/equipment',
- onShow: _.once(function(tour){
- $timeout(function(){tour.goTo(0)});
- }),
- element: '.equipment-tab',
- title: window.env.t('classGear'),
- content: window.env.t('classGearText', {klass: User.user.stats.class})
- },
- {
- path: '/#/options/profile/stats',
- onShow: _.once(function(tour){
- $timeout(function(){tour.goTo(1)});
- }),
- element: ".allocate-stats",
- title: window.env.t('stats'),
- content: window.env.t('classStats'),
- }, {
- element: ".auto-allocate",
- title: window.env.t('autoAllocate'),
- placement: 'left',
- content: window.env.t('autoAllocateText'),
- }, {
- element: ".meter.mana",
- title: window.env.t('spells'),
- content: window.env.t('spellsText') + " " + window.env.t('toDo') + " ."
- }, {
- orphan: true,
- title: window.env.t('readMore'),
- content: window.env.t('moreClass') + " Wikia ."
- }
- ];
- _.each(tourSteps, function(step){
- if (env.worldDmg.guide) {
- step.content = "";
- } else {
- step.content = "";
- }
- });
- $('.allocate-stats').popover('destroy');
- var tour = new Tour({
-// onEnd: function(){
-// User.set({'flags.showTour': false});
-// }
- });
- tourSteps.forEach(function(step) {
- tour.addStep(_.defaults(step, {html: true}));
- });
- tour.restart(); // Tour doesn't quite mesh with our handling of flags.showTour, just restart it on page load
- //tour.start(true);
- };
-
return {
- initTour: initTour,
- classesTour: classesTour
+ goto: goto
};
+
}]);
diff --git a/website/public/js/services/notificationServices.js b/website/public/js/services/notificationServices.js
index a0dde36fbb..9b4ccb8182 100644
--- a/website/public/js/services/notificationServices.js
+++ b/website/public/js/services/notificationServices.js
@@ -7,7 +7,7 @@ angular.module("habitrpg").factory("Notification",
function notify(html, type, icon) {
var notice = $.pnotify({
type: type || 'warning', //('info', 'text', 'warning', 'success', 'gp', 'xp', 'hp', 'lvl', 'death', 'mp', 'crit')
- text: html,
+ text: html,
opacity: 1,
addclass: 'alert-' + type,
delay: 7000,
diff --git a/website/src/models/user.js b/website/src/models/user.js
index d07b9e5bc5..21a4330c8c 100644
--- a/website/src/models/user.js
+++ b/website/src/models/user.js
@@ -119,6 +119,10 @@ var UserSchema = new Schema({
flags: {
customizationsNotification: {type: Boolean, 'default': false},
showTour: {type: Boolean, 'default': true},
+ tour: {
+ intro: {type: Number, 'default': 0},
+ classes: {type: Number, 'default': 0}
+ },
dropsEnabled: {type: Boolean, 'default': false},
itemsEnabled: {type: Boolean, 'default': false},
newStuff: {type: Boolean, 'default': false},
@@ -150,12 +154,12 @@ var UserSchema = new Schema({
gear: {
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/))
+ if (v.key.match(/[armor|head|shield]_warrior_0/))
m[v.key]['default'] = true;
}),
equipped: {
- weapon: {type: String, 'default': 'weapon_warrior_0'},
+ weapon: String,
armor: {type: String, 'default': 'armor_base_0'},
head: {type: String, 'default': 'head_base_0'},
shield: {type: String, 'default': 'shield_base_0'},
@@ -165,7 +169,7 @@ var UserSchema = new Schema({
body: String
},
costume: {
- weapon: {type: String, 'default': 'weapon_base_0'},
+ weapon: String,
armor: {type: String, 'default': 'armor_base_0'},
head: {type: String, 'default': 'head_base_0'},
shield: {type: String, 'default': 'shield_base_0'},
@@ -173,7 +177,7 @@ var UserSchema = new Schema({
headAccessory: String,
eyewear: String,
body: String
- },
+ }
},
special:{
diff --git a/website/views/shared/header/menu.jade b/website/views/shared/header/menu.jade
index cd7a08b00f..ddb78f3e64 100644
--- a/website/views/shared/header/menu.jade
+++ b/website/views/shared/header/menu.jade
@@ -166,7 +166,7 @@ nav.toolbar(ng-controller='AuthCtrl', ng-class='{active: isToolbarHidden}')
a(target="_blank" href='http://habitrpg.wikia.com/wiki/Contributing_to_HabitRPG')=env.t('contributeToHRPG')
li
a(target="_blank" href='http://habitrpg.wikia.com/wiki/')=env.t('overview')
- li(ng-controller='SettingsCtrl')
+ //-li(ng-controller='SettingsCtrl')
a(ng-click='showTour()', popover-placement='right', popover-trigger='mouseenter', popover=env.t('restartTour'))= env.t('showTour')
ul.toolbar-subscribe(ng-if='!user.purchased.plan.customerId')
li.toolbar-subscribe-button
diff --git a/website/views/shared/tasks/lists.jade b/website/views/shared/tasks/lists.jade
index cd37d2962c..6925c1da03 100644
--- a/website/views/shared/tasks/lists.jade
+++ b/website/views/shared/tasks/lists.jade
@@ -78,12 +78,8 @@ script(id='templates/habitrpg-tasks.html', type="text/ng-template")
+taskColumnTabs('top')
- // Actual List
- ul(class='{{list.type}}s main-list', ng-show='obj[list.type + "s"].length > 0', hrpg-sort-tasks)
- include ./task
-
// Static Rewards
- ul.items.rewards(ng-if='main && list.type=="reward" && user.flags.itemsEnabled')
+ ul.items.rewards(ng-if='main && list.type=="reward"')
li.task.reward-item(ng-repeat='item in itemStore',popover-trigger='mouseenter', popover-placement='top', popover='{{item.notes()}}')
// right-hand side control buttons
.task-meta-controls
@@ -124,6 +120,10 @@ script(id='templates/habitrpg-tasks.html', type="text/ng-template")
+specialSpell('salt','snowball')
+specialSpell('opaquePotion','spookDust')
+ // Actual List
+ ul(class='{{list.type}}s main-list', ng-show='obj[list.type + "s"].length > 0', hrpg-sort-tasks)
+ include ./task
+
// Spells
ul.items(ng-if='main && list.type=="reward" && user.stats.class && !user.preferences.disableClasses')
li.task.reward-item(ng-repeat='(k,skill) in Content.spells[user.stats.class]', ng-if='user.stats.lvl >= skill.lvl',popover-trigger='mouseenter', popover-placement='top', popover='{{skill.notes()}}')
@@ -143,7 +143,7 @@ script(id='templates/habitrpg-tasks.html', type="text/ng-template")
br
// Ads
- div(ng-if='::main && !user.purchased.ads && !user.purchased.plan.customerId && list.type!="reward"')
+ div(ng-if='::main && !user.purchased.ads && !user.purchased.plan.customerId && list.type!="reward" && user.stats.lvl>1')
span.pull-right
a(ui-sref='options.settings.subscription', popover=env.t('removeAds'), popover-trigger='mouseenter')
span.glyphicon.glyphicon-remove