Merge pull request #4713 from HabitRPG/lefnire/interactive-tour

Interactive Tour
This commit is contained in:
Tyler Renelle 2015-03-10 19:23:01 -06:00
commit de67f44e1f
13 changed files with 253 additions and 357 deletions

View file

@ -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')

View file

@ -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.",

View file

@ -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! <b>Sample Dailies</b>: <b>Make Bed</b>, <b>Floss</b>, <b>Check Work Email</b>",
"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.<ul><li><b>Sample Good Habits</b>: <b>Eat a vegetable</b>, <b>15 minutes productive work</b></li><li><b>Sample Bad Habits</b>: <b>Smoke</b>, <b>Procrastinate</b></li><li><b>Sample Good or Bad Habits</b>: <b>Took Stairs/Elevator</b>, <b>Drank Water/Soda</b>",
"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!"
}

View file

@ -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: [

View file

@ -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})

View file

@ -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(){

View file

@ -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);
};

View file

@ -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(){

View file

@ -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') + " <a href='http://www.kickstarter.com/profile/1823740484' target='_blank'>Justin</a>, " + 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') + " <a href='http://habitrpg.wikia.com' target='_blank'>" + window.env.t('habitWiki') + "</a> " + window.env.t('unlockFeaturesT2'),
placement: "right"
}
];
$('.main-herobox').popover('destroy');
var tour = new Tour({
backdrop: true,
//orphan: true,
//keyboard: false,
template: '<div class="popover" role="tooltip"> <div class="arrow"></div> <h3 class="popover-title"></h3> <div class="popover-content"></div> <div class="popover-navigation"> <div class="btn-group"> <button class="btn btn-sm btn-default" data-role="prev">&laquo; Prev</button> <button class="btn btn-sm btn-default" data-role="next">Next &raquo;</button> <button class="btn btn-sm btn-default" data-role="pause-resume" data-pause-text="Pause" data-resume-text="Resume">Pause</button> </div> <button class="btn btn-sm btn-default" data-role="end">' + window.env.t('endTour') + '</button> </div> </div>',
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') + " <a target='_blank' href='http://habitrpg.wikia.com/wiki/Todos'>" + window.env.t('toDo') + "</a>."
}, {
orphan: true,
title: window.env.t('readMore'),
content: window.env.t('moreClass') + " <a href='http://habitrpg.wikia.com/wiki/Class_System' target='_blank'>Wikia</a>.",
final: true
}
]
]
}
_.each(chapters, function(chapter, k){
_(chapter).flatten().each(function(step) {
step.content = "<div><div class='" + (env.worldDmg.guide ? "npc_justin_broken" : "npc_justin") + " float-left'></div>" + step.content + "</div>";
$(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 '<div class="popover" role="tooltip"> ' +
'<div class="arrow"></div> ' +
'<h3 class="popover-title"></h3> ' +
'<div class="popover-content"></div> ' +
'<div class="popover-navigation"> ' +
//'<button class="btn btn-sm btn-default" data-role="end" style="float:none;">' + (step.final ? 'Finish Tour' : 'Hide') + '</button>'+
(step.final ? '<button class="btn btn-sm btn-default" data-role="end" style="float:none;">Finish Tour</button>' : '')+
'<div class="btn-group"> ' +
'<button class="btn btn-sm btn-default" data-role="prev">&laquo; Prev</button> ' +
'<button class="btn btn-sm btn-default" data-role="next">Next &raquo;</button> ' +
'<button class="btn btn-sm btn-default" data-role="pause-resume" data-pause-text="Pause" data-resume-text="Resume">Pause</button> ' +
'</div> ' +
'</div>' +
'</div>';
},
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 = "<button class='btn btn-sm btn-default' onClick=\"$('" + selector + "').popover('hide');return false;\">" + window.env.t('close') + "</button>";
if (env.worldDmg.guide) {
html = "<div><div class='npc_justin_broken float-left'></div>" + html + '<br/>' + button + '</div>';
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 = "<div><div class='npc_justin float-left'></div>" + html + '<br/>' + button + '</div>';
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') + " <a target='_blank' href='http://habitrpg.wikia.com/wiki/Todos'>" + window.env.t('toDo') + "</a>."
}, {
orphan: true,
title: window.env.t('readMore'),
content: window.env.t('moreClass') + " <a href='http://habitrpg.wikia.com/wiki/Class_System' target='_blank'>Wikia</a>."
}
];
_.each(tourSteps, function(step){
if (env.worldDmg.guide) {
step.content = "<div><div class='npc_justin_broken float-left'></div>" + step.content + "</div>";
} else {
step.content = "<div><div class='npc_justin float-left'></div>" + step.content + "</div>";
}
});
$('.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
};
}]);

View file

@ -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,

View file

@ -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:{

View file

@ -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

View file

@ -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