mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-08-01 15:31:16 +00:00
Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
commit
977aeec27f
37 changed files with 458 additions and 215 deletions
|
|
@ -14,5 +14,4 @@ HabitRPG uses [Trello](https://trello.com/b/EpoYEYod/habitrpg) to track feature
|
|||
|
||||
# Contributing Code
|
||||
|
||||
* See [Contributing to HabitRPG](http://habitrpg.wikia.com/wiki/Contributing_to_HabitRPG)
|
||||
* See [Tips](http://habitrpg.wikia.com/wiki/Tips)
|
||||
* See [Contributing to HabitRPG](http://habitrpg.wikia.com/wiki/Contributing_to_HabitRPG#Coders_.28Web_.26_Mobile.29)
|
||||
|
|
|
|||
41
Gruntfile.js
41
Gruntfile.js
|
|
@ -4,6 +4,8 @@ module.exports = function(grunt) {
|
|||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
|
||||
git_changelog: {
|
||||
minimal: {
|
||||
options: {
|
||||
|
|
@ -56,7 +58,10 @@ module.exports = function(grunt) {
|
|||
build: {
|
||||
files: [
|
||||
{expand: true, cwd: 'public/', src: 'favicon.ico', dest: 'build/'},
|
||||
{expand: true, cwd: 'public/', src: 'bower_components/habitrpg-shared/dist/spritesmith-*.png', dest: 'build/'}
|
||||
{expand: true, cwd: 'public/', src: 'bower_components/habitrpg-shared/dist/spritesmith.png', dest: 'build/'},
|
||||
{expand: true, cwd: 'public/', src: 'bower_components/habitrpg-shared/img/sprites/backer-only/*.gif', dest: 'build/'},
|
||||
{expand: true, cwd: 'public/', src: 'bower_components/habitrpg-shared/img/sprites/npc_ian.gif', dest: 'build/'},
|
||||
{expand: true, cwd: 'public/', src: 'bower_components/bootstrap/dist/fonts/*', dest: 'build/'}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
@ -69,16 +74,18 @@ module.exports = function(grunt) {
|
|||
},
|
||||
src: [
|
||||
'build/*.js', 'build/*.css', 'build/favicon.ico',
|
||||
'build/bower_components/bootstrap/docs/assets/css/*.css',
|
||||
'build/bower_components/habitrpg-shared/dist/*.css'
|
||||
'build/bower_components/habitrpg-shared/dist/*.png',
|
||||
'build/bower_components/habitrpg-shared/img/sprites/backer-only/*.gif',
|
||||
'build/bower_components/habitrpg-shared/img/sprites/npc_ian.gif',
|
||||
'build/bower_components/bootstrap/dist/fonts/*'
|
||||
],
|
||||
dest: 'make-sure-i-do-not-exist'
|
||||
dest: 'build/*.css'
|
||||
}
|
||||
},
|
||||
|
||||
nodemon: {
|
||||
nodemon: {
|
||||
dev: {
|
||||
ignoredFiles: ['public/*', 'Gruntfile.js', 'CHANGELOG.md', 'views/*', 'build/*', '.idea*', '.git*', '*.log']
|
||||
script: '<%= pkg.main %>'
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -106,27 +113,35 @@ module.exports = function(grunt) {
|
|||
var files = grunt.file.readJSON('./public/manifest.json');
|
||||
var uglify = {};
|
||||
var cssmin = {};
|
||||
|
||||
_.each(files, function(val, key){
|
||||
|
||||
var js = uglify['build/' + key + '.js'] = [];
|
||||
|
||||
_.each(files[key]['js'], function(val){
|
||||
js.push('public/' + val);
|
||||
});
|
||||
|
||||
var css = cssmin['build/' + key + '.css'] = [];
|
||||
|
||||
_.each(files[key]['css'], function(val){
|
||||
if(val == 'app.css' || val == 'static.css'){
|
||||
cssmin['build/' + val] = ['build/' + val]
|
||||
}else{
|
||||
cssmin['build/' + val] = ['public/' + val]
|
||||
}
|
||||
var path = (val == 'app.css' || val == 'static.css') ? 'build/' : 'public/';
|
||||
css.push(path + val)
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
grunt.config.set('uglify.build.files', uglify);
|
||||
grunt.config.set('uglify.build.options', {compress: false})
|
||||
grunt.config.set('uglify.build.options', {compress: false});
|
||||
|
||||
grunt.config.set('cssmin.build.files', cssmin);
|
||||
// Rewrite urls to relative path
|
||||
grunt.config.set('cssmin.build.options', {'target': 'public/css/whatever-css.css'});
|
||||
});
|
||||
|
||||
// Register tasks.
|
||||
grunt.registerTask('build:prod', ['loadManifestFiles', 'clean:build', 'uglify', 'stylus', 'cssmin', 'copy:build', 'hashres']);
|
||||
grunt.registerTask('build:dev', ['loadManifestFiles', 'clean:build', 'stylus', 'cssmin', 'copy:build']);
|
||||
grunt.registerTask('build:dev', ['stylus']);
|
||||
|
||||
grunt.registerTask('run:dev', [ 'build:dev', 'concurrent' ]);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@
|
|||
"jquery": "~2.1.0",
|
||||
"jquery.cookie": "~1.4.0",
|
||||
"jquery-ui": "~1.10.3",
|
||||
"angular": "~1.2.11",
|
||||
"angular-sanitize": "~1.2.11",
|
||||
"angular-resource": "~1.2.11",
|
||||
"angular": "~1.2.12",
|
||||
"angular-sanitize": "~1.2.12",
|
||||
"angular-resource": "~1.2.12",
|
||||
"angular-ui-utils": "~0.1.0",
|
||||
"angular-ui-select2": "https://github.com/angular-ui/ui-select2.git#4469fd7a32eba2e28d5e3dd319b49e1ad090b167",
|
||||
"angular-bootstrap": "~0.10.0",
|
||||
|
|
@ -36,13 +36,12 @@
|
|||
"JavaScriptButtons": "git://github.com/paypal/JavaScriptButtons.git#master",
|
||||
"Angular-At-Directive": "git://github.com/snicker/Angular-At-Directive#master",
|
||||
"js-emoji": "git://github.com/snicker/js-emoji#master",
|
||||
"gemoji": "git://github.com/github/gemoji",
|
||||
"sticky": "*",
|
||||
"swagger-ui": "https://github.com/wordnik/swagger-ui.git#105c516f5f055b140e935e9cfe0c36f841921dff",
|
||||
"ngInfiniteScroll": "~1.0.0",
|
||||
"jquery-colorbox": "~1.4.36"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-mocks": "~1.2.11"
|
||||
"angular-mocks": "~1.2.12"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
23
migrations/contribs_plan.js
Normal file
23
migrations/contribs_plan.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Give contrib.level 7+ free subscription for life
|
||||
db.users.update(
|
||||
|
||||
{
|
||||
'contributor.level':{$gte:7},
|
||||
'purchased.plan.customerId':null
|
||||
},
|
||||
|
||||
{
|
||||
$set: {
|
||||
'purchased.plan':{
|
||||
planId: 'basic',
|
||||
customerId: 'habitrpg',
|
||||
dateCreated: new Date,
|
||||
dateUpdated: new Date,
|
||||
gemsBought: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{multi:true}
|
||||
|
||||
)
|
||||
|
|
@ -33,6 +33,10 @@ alert-sm-mixin()
|
|||
.alert
|
||||
alert-sm-mixin()
|
||||
|
||||
.notification-icon
|
||||
transform: scale(0.8)
|
||||
|
||||
|
||||
// variables
|
||||
|
||||
gpColor = #FBEFC0
|
||||
|
|
@ -95,35 +99,6 @@ borderDarken = 20%
|
|||
border-color: darken(dropColor,borderDarken)
|
||||
color: dropText
|
||||
|
||||
// alert icons
|
||||
|
||||
.icon-gold
|
||||
background: url("/bower_components/habitrpg-shared/img/coin_single_gold.png") no-repeat
|
||||
background-position: center center
|
||||
background-size: 18px
|
||||
width: 14px
|
||||
height: 14px
|
||||
display: inline-block
|
||||
vertical-align: middle
|
||||
|
||||
.icon-silver
|
||||
background: url("/bower_components/habitrpg-shared/img/coin_single_silver.png") no-repeat
|
||||
background-position: center center
|
||||
background-size: 18px
|
||||
width: 14px
|
||||
height: 14px
|
||||
display: inline-block
|
||||
vertical-align: middle
|
||||
|
||||
.icon-death
|
||||
background: url("/bower_components/habitrpg-shared/img/sprites/dead.png") no-repeat
|
||||
background-position: center center
|
||||
background-size: 14px
|
||||
width: 14px
|
||||
height: 14px
|
||||
display: inline-block
|
||||
vertical-align: middle
|
||||
|
||||
.undo-button
|
||||
z-index: 3000
|
||||
position:absolute
|
||||
|
|
|
|||
|
|
@ -118,6 +118,10 @@ html, body
|
|||
float: left
|
||||
padding: 15px
|
||||
|
||||
&.herobox
|
||||
padding-right: 0px
|
||||
padding-left: 0px
|
||||
|
||||
.death-modal // a .modal-body
|
||||
margin: 10px
|
||||
|
||||
|
|
@ -163,3 +167,19 @@ a.label
|
|||
background-color: #428bca
|
||||
.badge-success
|
||||
background-color: #47a447
|
||||
|
||||
.white, .white a
|
||||
color: #fff !important
|
||||
|
||||
.collapse-toolbar
|
||||
position:absolute
|
||||
right:0px
|
||||
top:0px
|
||||
width:30px
|
||||
height:25px
|
||||
background-color:#222
|
||||
z-index:999
|
||||
.glyphicon
|
||||
color:white
|
||||
padding-left:7px
|
||||
padding-top:5px
|
||||
|
|
|
|||
|
|
@ -4,15 +4,15 @@
|
|||
.npc_matt
|
||||
margin-bottom: 20px
|
||||
|
||||
.toolbar-bailey-container
|
||||
width:55px
|
||||
position:relative
|
||||
.npc_bailey
|
||||
float:left
|
||||
.npc_bailey_head
|
||||
//height: 30px !important
|
||||
height: 50px !important
|
||||
position: absolute
|
||||
//top: 117px // 147 (header-height) - 30 (bailey height)
|
||||
top: 97px
|
||||
right: 50px
|
||||
top: 4px
|
||||
height:45px !important
|
||||
cursor: pointer
|
||||
|
||||
// Tour (Justin)
|
||||
|
|
|
|||
|
|
@ -114,8 +114,12 @@ window.habitrpg = angular.module('habitrpg',
|
|||
.state('options.social.guilds.detail', {
|
||||
url: '/:gid',
|
||||
templateUrl: 'partials/options.social.guilds.detail.html',
|
||||
controller: ['$scope', 'Groups', '$stateParams', function($scope, Groups, $stateParams){
|
||||
$scope.group = Groups.Group.get({gid:$stateParams.gid});
|
||||
controller: ['$scope', 'Groups', '$stateParams',
|
||||
function($scope, Groups, $stateParams){
|
||||
Groups.Group.get({gid:$stateParams.gid}, function(group){
|
||||
$scope.group = group;
|
||||
Groups.seenMessage(group._id);
|
||||
});
|
||||
}]
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -319,6 +319,9 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '
|
|||
$scope.text = 'Party';
|
||||
$scope.group = $rootScope.party = Groups.party();
|
||||
$scope.newGroup = new Groups.Group({type:'party'});
|
||||
|
||||
Groups.seenMessage($scope.group._id);
|
||||
|
||||
$scope.create = function(group){
|
||||
group.$save(function(newGroup){
|
||||
$scope.group = newGroup;
|
||||
|
|
|
|||
|
|
@ -94,7 +94,6 @@ habitrpg.controller('NotificationCtrl',
|
|||
$rootScope.$watch('user.items.' + watched, function(before, after){
|
||||
if (after == before) return;
|
||||
if (+after < +before) {
|
||||
Notification.death();
|
||||
//don't want to day "lost a head"
|
||||
if (watched === 'head') watched = 'helm';
|
||||
Notification.text('Lost GP, 1 LVL, ' + watched);
|
||||
|
|
@ -116,12 +115,6 @@ habitrpg.controller('NotificationCtrl',
|
|||
}
|
||||
});
|
||||
|
||||
// Math updates modal
|
||||
$rootScope.$watch('!user.flags.mathUpdates', function(after, before){
|
||||
if (after == before || after != true) return;
|
||||
$rootScope.openModal('mathUpdates');
|
||||
});
|
||||
|
||||
// Completed quest modal
|
||||
$rootScope.$watch('user.party.quest.completed', function(after, before){
|
||||
if (after == before || after != true) return;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$
|
|||
$rootScope.Shared = Shared;
|
||||
$rootScope.Content = Content;
|
||||
$rootScope.env = window.env;
|
||||
$rootScope.Math = Math;
|
||||
|
||||
// Angular UI Router
|
||||
$rootScope.$state = $state;
|
||||
|
|
@ -198,6 +199,11 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$
|
|||
*/
|
||||
$scope.castStart = function(spell) {
|
||||
if (User.user.stats.mp < spell.mana) return Notification.text(window.env.t('notEnoughMana'));
|
||||
|
||||
// Temporary for valentine's day, remove after event
|
||||
if (spell.key == 'valentine' && User.user.stats.gp < spell.value)
|
||||
return Notification.text('Not enough gold.');
|
||||
|
||||
$rootScope.applyingAction = true;
|
||||
$scope.spell = spell;
|
||||
if (spell.target == 'self') {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
*/
|
||||
|
||||
angular.module('groupServices', ['ngResource']).
|
||||
factory('Groups', ['API_URL', '$resource', '$q',
|
||||
function(API_URL, $resource, $q) {
|
||||
factory('Groups', ['API_URL', '$resource', '$q', '$http', 'User',
|
||||
function(API_URL, $resource, $q, $http, User) {
|
||||
var Group = $resource(API_URL + '/api/v2/groups/:gid',
|
||||
{gid:'@_id', messageId: '@_messageId'},
|
||||
{
|
||||
|
|
@ -44,6 +44,12 @@ angular.module('groupServices', ['ngResource']).
|
|||
return tavern;
|
||||
},
|
||||
|
||||
// On enter, set chat message to "seen"
|
||||
seenMessage: function(gid){
|
||||
$http.post('/api/v2/groups/'+gid+'/chat/seen');
|
||||
if (User.user.newMessages) User.user.newMessages[gid] = false;
|
||||
},
|
||||
|
||||
Group: Group
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,11 +25,11 @@ angular.module("notificationServices", [])
|
|||
gold = Math.floor(absolute);
|
||||
silver = Math.floor((absolute - gold) * 100);
|
||||
if (gold && silver > 0) {
|
||||
return "" + gold + " <span class='icon-gold'></span> " + silver + " <span class='icon-silver'></span>";
|
||||
return "" + gold + " <span class='notification-icon shop_gold'></span> " + silver + " <span class='notification-icon shop_silver'></span>";
|
||||
} else if (gold > 0) {
|
||||
return "" + gold + " <span class='icon-gold'></span>";
|
||||
return "" + gold + " <span class='notification-icon shop_gold'></span>";
|
||||
} else if (silver > 0) {
|
||||
return "" + silver + " <span class='icon-silver'></span>";
|
||||
return "" + silver + " <span class='notification-icon shop_silver'></span>";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -60,9 +60,6 @@ angular.module("notificationServices", [])
|
|||
lvl: function(){
|
||||
growl('<span class="glyphicon glyphicon-chevron-up"></span> ' + window.env.t('levelUp'), 'lvl');
|
||||
},
|
||||
death: function(){
|
||||
growl("<span class='glyphicon glyphicon-death'></span> " + window.env.t('respawn'), "death");
|
||||
},
|
||||
error: function(error){
|
||||
growl("<span class='glyphicon glyphicon-exclamation-sign'></span> " + error, "danger");
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"app": {
|
||||
"js": [
|
||||
"bower_components/jquery/jquery.js",
|
||||
"bower_components/jquery/dist/jquery.min.js",
|
||||
"bower_components/jquery.cookie/jquery.cookie.js",
|
||||
"bower_components/bootstrap-growl/jquery.bootstrap-growl.js",
|
||||
"bower_components/bootstrap-tour/build/js/bootstrap-tour.js",
|
||||
|
|
@ -72,7 +72,7 @@
|
|||
},
|
||||
"static": {
|
||||
"js": [
|
||||
"bower_components/jquery/jquery.js",
|
||||
"bower_components/jquery/dist/jquery.min.js",
|
||||
"bower_components/habitrpg-shared/dist/habitrpg-shared.js",
|
||||
"bower_components/angular/angular.js",
|
||||
"bower_components/angular-ui/build/angular-ui.js",
|
||||
|
|
|
|||
|
|
@ -249,6 +249,14 @@ api.deleteChatMessage = function(req, res){
|
|||
});
|
||||
}
|
||||
|
||||
api.seenMessage = function(req,res,next){
|
||||
// Skip the auth step, we want this to be fast. If !found with uuid/token, then it just doesn't save
|
||||
var update = {$set:{}};
|
||||
update['$set']['newMessages.'+req.params.gid+'.value'] = false;
|
||||
User.update({_id:req.headers['x-api-user'], apiToken:req.headers['x-api-key']},update).exec();
|
||||
res.send(200);
|
||||
}
|
||||
|
||||
api.likeChatMessage = function(req, res, next) {
|
||||
var user = res.locals.user;
|
||||
var group = res.locals.group;
|
||||
|
|
|
|||
|
|
@ -89,10 +89,25 @@ api.score = function(req, res, next) {
|
|||
delta: delta,
|
||||
_tmp: user._tmp
|
||||
}, saved.toJSON().stats));
|
||||
});
|
||||
|
||||
// if it's a challenge task, sync the score
|
||||
user.syncScoreToChallenge(task, delta);
|
||||
// If it's a challenge task, sync the score. Do it in the background, we've already sent down a response
|
||||
// and the user doesn't care what happens back there
|
||||
if (!task.challenge || !task.challenge.id || task.challenge.broken) return;
|
||||
if (task.type == 'reward') return; // we don't want to update the reward GP cost
|
||||
Challenge.findById(task.challenge.id, 'habits dailys todos rewards', function(err, chal){
|
||||
if (err) return next(err);
|
||||
if (!chal) {
|
||||
task.challenge.broken = 'CHALLENGE_DELETED';
|
||||
return user.save();
|
||||
}
|
||||
var t = chal.tasks[task.id];
|
||||
if (!t) return chal.syncToUser(user); // this task was removed from the challenge, notify user
|
||||
t.value += delta;
|
||||
if (t.type == 'habit' || t.type == 'daily')
|
||||
t.history.push({value: t.value, date: +new Date});
|
||||
chal.save();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -409,7 +424,7 @@ api.cast = function(req, res, next) {
|
|||
case 'user':
|
||||
async.waterfall([
|
||||
function(cb){
|
||||
Group.findOne({type: 'party', members: {'$in': [user._id]}}).populate('members', 'profile.name stats achievements').exec(cb);
|
||||
Group.findOne({type: 'party', members: {'$in': [user._id]}}).populate('members', 'profile.name stats achievements items.special').exec(cb);
|
||||
},
|
||||
function(group, cb) {
|
||||
// Solo player? let's just create a faux group for simpler code
|
||||
|
|
|
|||
|
|
@ -122,22 +122,21 @@ var getManifestFiles = function(page){
|
|||
|
||||
if(!files) throw new Error("Page not found!");
|
||||
|
||||
var css = '';
|
||||
|
||||
_.each(files.css, function(file){
|
||||
css += '<link rel="stylesheet" type="text/css" href="' + getBuildUrl(file) + '">';
|
||||
});
|
||||
var code = '';
|
||||
|
||||
if(nconf.get('NODE_ENV') === 'production'){
|
||||
return css + '<script type="text/javascript" src="' + getBuildUrl(page + '.js') + '"></script>';
|
||||
code += '<link rel="stylesheet" type="text/css" href="' + getBuildUrl(page + '.css') + '">';
|
||||
code += '<script type="text/javascript" src="' + getBuildUrl(page + '.js') + '"></script>';
|
||||
}else{
|
||||
var results = css;
|
||||
_.each(files.js, function(file){
|
||||
results += '<script type="text/javascript" src="' + getBuildUrl(file) + '"></script>';
|
||||
_.each(files.css, function(file){
|
||||
code += '<link rel="stylesheet" type="text/css" href="' + getBuildUrl(file) + '">';
|
||||
});
|
||||
_.each(files.js, function(file){
|
||||
code += '<script type="text/javascript" src="' + getBuildUrl(file) + '"></script>';
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// Translations
|
||||
|
|
|
|||
|
|
@ -106,6 +106,17 @@ GroupSchema.methods.sendChat = function(message, user){
|
|||
}
|
||||
group.chat.unshift(message);
|
||||
group.chat.splice(200);
|
||||
|
||||
// Kick off chat notifications in the background.
|
||||
var lastSeenUpdate = {$set:{}, $inc:{_v:1}};
|
||||
lastSeenUpdate['$set']['newMessages.'+group._id] = {name:group.name,value:true};
|
||||
if (group._id == 'habitrpg') {
|
||||
// TODO For Tavern, only notify them if their name was mentioned
|
||||
// var profileNames = [] // get usernames from regex of @xyz. how to handle space-delimited profile names?
|
||||
// User.update({'profile.name':{$in:profileNames}},lastSeenUpdate,{multi:true}).exec();
|
||||
} else {
|
||||
mongoose.model('User').update({_id:{$in:group.members, $ne: user ? user._id : ''}},lastSeenUpdate,{multi:true}).exec();
|
||||
}
|
||||
}
|
||||
|
||||
var cleanQuestProgress = function(merge){
|
||||
|
|
@ -164,13 +175,13 @@ GroupSchema.methods.finishQuest = function(quest, cb) {
|
|||
}
|
||||
|
||||
// FIXME this is a temporary measure, we need to remove quests from users when they traverse parties
|
||||
function isOnQuest(user,group){
|
||||
return group && user.party.quest.key && user.party.quest.key == group.quest.key;
|
||||
function isOnQuest(user,progress,group){
|
||||
return group && progress && user.party.quest.key && user.party.quest.key == group.quest.key;
|
||||
}
|
||||
|
||||
GroupSchema.statics.collectQuest = function(user, progress, cb) {
|
||||
this.findOne({type: 'party', members: {'$in': [user._id]}},function(err, group){
|
||||
if (!isOnQuest(user,group)) return cb(null);
|
||||
if (!isOnQuest(user,progress,group)) return cb(null);
|
||||
var quest = shared.content.quests[group.quest.key];
|
||||
|
||||
_.each(progress.collect,function(v,k){
|
||||
|
|
@ -204,7 +215,7 @@ GroupSchema.statics.collectQuest = function(user, progress, cb) {
|
|||
|
||||
GroupSchema.statics.bossQuest = function(user, progress, cb) {
|
||||
this.findOne({type: 'party', members: {'$in': [user._id]}},function(err, group){
|
||||
if (!isOnQuest(user,group)) return cb(null);
|
||||
if (!isOnQuest(user,progress,group)) return cb(null);
|
||||
var quest = shared.content.quests[group.quest.key];
|
||||
if (!progress || !quest) return cb(null); // FIXME why is this ever happening, progress should be defined at this point
|
||||
var down = progress.down * quest.boss.str; // multiply by boss strength
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ var UserSchema = new Schema({
|
|||
rebirths: Number,
|
||||
rebirthLevel: Number,
|
||||
perfect: Number,
|
||||
habitBirthday: Boolean
|
||||
habitBirthday: Boolean,
|
||||
valentine: Number
|
||||
},
|
||||
auth: {
|
||||
facebook: Schema.Types.Mixed,
|
||||
|
|
@ -136,7 +137,9 @@ var UserSchema = new Schema({
|
|||
},
|
||||
|
||||
special:{
|
||||
snowball: {type: Number, 'default': 0}
|
||||
snowball: {type: Number, 'default': 0},
|
||||
valentine: Number,
|
||||
valentineReceived: Array // array of strings, by sender name
|
||||
},
|
||||
|
||||
// -------------- Animals -------------------
|
||||
|
|
@ -207,9 +210,11 @@ var UserSchema = new Schema({
|
|||
|
||||
lastCron: {type: Date, 'default': Date.now},
|
||||
|
||||
// {GROUP_ID: Boolean}, represents whether they have unseen chat messages
|
||||
newMessages: {type: Schema.Types.Mixed, 'default': {}},
|
||||
|
||||
party: {
|
||||
// id // FIXME can we use a populated doc instead of fetching party separate from user?
|
||||
lastMessageSeen: String,
|
||||
order: {type:String, 'default':'level'},
|
||||
quest: {
|
||||
key: String,
|
||||
|
|
@ -245,7 +250,8 @@ var UserSchema = new Schema({
|
|||
disableClasses: {type: Boolean, 'default': false},
|
||||
newTaskEdit: {type: Boolean, 'default': false},
|
||||
tagsCollapsed: {type: Boolean, 'default': false},
|
||||
advancedCollapsed: {type: Boolean, 'default': false}
|
||||
advancedCollapsed: {type: Boolean, 'default': false},
|
||||
toolbarCollapsed: {type:Boolean, 'default':false}
|
||||
},
|
||||
profile: {
|
||||
blurb: String,
|
||||
|
|
@ -367,22 +373,6 @@ UserSchema.pre('save', function(next) {
|
|||
next();
|
||||
});
|
||||
|
||||
UserSchema.methods.syncScoreToChallenge = function(task, delta){
|
||||
if (!task.challenge || !task.challenge.id || task.challenge.broken) return;
|
||||
if (task.type == 'reward') return; // we don't want to update the reward GP cost
|
||||
var self = this;
|
||||
Challenge.findById(task.challenge.id, function(err, chal){
|
||||
if (err) throw err;
|
||||
var t = chal.tasks[task.id];
|
||||
if (!t) return chal.syncToUser(self); // this task was removed from the challenge, notify user
|
||||
t.value += delta;
|
||||
if (t.type == 'habit' || t.type == 'daily') {
|
||||
t.history.push({value: t.value, date: +new Date});
|
||||
}
|
||||
chal.save();
|
||||
});
|
||||
}
|
||||
|
||||
UserSchema.methods.unlink = function(options, cb) {
|
||||
var cid = options.cid, keep = options.keep, tid = options.tid;
|
||||
var self = this;
|
||||
|
|
|
|||
|
|
@ -488,6 +488,17 @@ module.exports = (swagger, v2) ->
|
|||
middleware: [auth.auth, groups.attachGroup]
|
||||
action: groups.postChat
|
||||
|
||||
# placing before route below, so that if !=='seen' it goes to next()
|
||||
"/groups/{gid}/chat/seen":
|
||||
spec:
|
||||
method: 'POST'
|
||||
description: "Flag chat messages for a particular group as seen"
|
||||
parameters: [
|
||||
path 'gid','Group id','string'
|
||||
]
|
||||
middleware: []
|
||||
action: groups.seenMessage
|
||||
|
||||
"/groups/{gid}/chat/{messageId}":
|
||||
spec:
|
||||
method: 'DELETE'
|
||||
|
|
|
|||
|
|
@ -116,7 +116,9 @@ if (cluster.isMaster && (isDev || isProd)) {
|
|||
app.use(app.router);
|
||||
|
||||
var maxAge = isProd ? 31536000000 : 0;
|
||||
// Cache emojis without copying them to build, they are too many
|
||||
app.use(express['static'](path.join(__dirname, "/../build"), { maxAge: maxAge }));
|
||||
app.use('/bower_components/habitrpg-shared/img/emoji/unicode', express['static'](path.join(__dirname, "/../public/bower_components/habitrpg-shared/img/emoji/unicode"), { maxAge: maxAge }));
|
||||
app.use(express['static'](path.join(__dirname, "/../public")));
|
||||
|
||||
// Custom Directives
|
||||
|
|
|
|||
|
|
@ -253,6 +253,33 @@ describe('API', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('Challenge deleted, breaks task link', function (done) {
|
||||
var itThis = this;
|
||||
request.del(baseURL + "/challenges/" + challenge._id).end(function (res) {
|
||||
User.findById(user._id, function(err,user){
|
||||
var len = user.dailys.length- 1,
|
||||
daily = user.dailys[user.dailys.length-1];
|
||||
expect(daily.challenge.broken).to.be('CHALLENGE_DELETED');
|
||||
|
||||
// Now let's handle if challenge was deleted, but didn't get to update all the users (an error)
|
||||
var unset = {$unset:{}};
|
||||
unset['$unset']['dailys.' + len + '.challenge.broken'] = 1;
|
||||
User.findByIdAndUpdate(user._id,unset,function(err,user){
|
||||
expect(err).to.not.be.ok();
|
||||
expect(user.dailys[len].challenge.broken).to.not.be.ok();
|
||||
request.post(baseURL + "/user/tasks/" + daily.id + "/up").end(function (res) {
|
||||
setTimeout(function(){
|
||||
User.findById(user._id,function(err,user){
|
||||
expect(user.dailys[len].challenge.broken).to.be('CHALLENGE_DELETED');
|
||||
done()
|
||||
})
|
||||
}, 100); // we need to wait for challenge to update user, it's a background job for perf reasons
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
it('Admin creates a challenge', function (done) {
|
||||
User.findByIdAndUpdate(_id, {$set:{'contributor.admin':true}}, function (err,_user) {
|
||||
expect(err).to.not.be.ok();
|
||||
|
|
|
|||
|
|
@ -20,19 +20,15 @@ html(ng-app="habitrpg", ng-controller="RootCtrl", ng-class='{"applying-action":a
|
|||
|
||||
|
||||
body(ng-cloak, ng-controller='GroupsCtrl')
|
||||
include ./shared/header/menu
|
||||
include ./shared/modals/index
|
||||
include ./shared/header/header
|
||||
|
||||
include ./shared/tasks/lists
|
||||
include ./main/index
|
||||
include ./options/index
|
||||
|
||||
#notification-area(ng-controller='NotificationCtrl')
|
||||
#wrap.container-fluid
|
||||
//if they hide the header, we still need user-menu visible
|
||||
div(ng-if='user.preferences.hideHeader')
|
||||
include ./shared/header/menu
|
||||
|
||||
.row
|
||||
.col-md-12.exp-chart(ng-show='charts.exp')
|
||||
#main(ui-view)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
form.form-inline(ng-show='_editing', ng-submit='createTag(_newTag)')
|
||||
.input-group
|
||||
input.form-control.input-sm(type='text', ng-model='_newTag', placeholder=env.t('newTag'))
|
||||
span.input-group-addon(type='submit')
|
||||
span.input-group-addon(ng-click='createTag(_newTag)')
|
||||
=env.t('add')
|
||||
// <li class="{#unless activeFilters(users[_userId].filters)}hidden{/}">
|
||||
li
|
||||
|
|
|
|||
|
|
@ -68,6 +68,12 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html')
|
|||
button.customize-option(popover='{{Content.special.snowball.notes}}', popover-title='{{Content.special.snowball.text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='castStart(Content.special.snowball)', class='inventory_special_snowball')
|
||||
.badge.badge-info.stack-count {{user.items.special.snowball}}
|
||||
|
||||
li.customize-menu(ng-if='user.items.special.valentineReceived[0]')
|
||||
menu.pets-menu(label=env.t('valentineCard'))
|
||||
div
|
||||
button.customize-option(popover="Valentine's Day Card from {{User.user.items.special.valentineReceived[0]}}", popover-trigger='mouseenter', popover-placement='right', ng-click='openModal("valentine")', class='inventory_special_valentine')
|
||||
.badge.badge-info.stack-count {{user.items.special.valentineReceived.length}}
|
||||
|
||||
li.customize-menu(ng-if='user.purchased.plan.customerId')
|
||||
menu.pets-menu(label=env.t('subscriberItem'))
|
||||
div
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template')
|
|||
.col-md-4
|
||||
h3=env.t('bodyBody')
|
||||
small
|
||||
| 2 <span class="Pet_Currency_Gem1x inline-gems"/> /
|
||||
| 2 <span class="Pet_Currency_Gem1x inline-gems"></span> /
|
||||
= ' ' + env.t('locked')
|
||||
|
||||
h5=env.t('bodySize')
|
||||
|
|
@ -27,7 +27,7 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template')
|
|||
.col-md-4
|
||||
h3=env.t('bodyHead')
|
||||
small
|
||||
| 2 <span class="Pet_Currency_Gem1x inline-gems"/> /
|
||||
| 2 <span class="Pet_Currency_Gem1x inline-gems"></span> /
|
||||
= ' ' + env.t('locked')
|
||||
menu(type='list')
|
||||
// For special events code, see commit dfa27b3
|
||||
|
|
@ -86,7 +86,7 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template')
|
|||
.col-md-4
|
||||
h3=env.t('bodySkin')
|
||||
small
|
||||
| 2 <span class="Pet_Currency_Gem1x inline-gems"/> /
|
||||
| 2 <span class="Pet_Currency_Gem1x inline-gems"></span> /
|
||||
= ' ' + env.t('locked')
|
||||
// skin
|
||||
li.customize-menu
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@
|
|||
p
|
||||
|
|
||||
=env.t('noChallenges')
|
||||
|
|
||||
a.label.label-primary(ui-sref='options.social.challenges')
|
||||
|
|
||||
span.glyphicon.glyphicon-bullhorn
|
||||
|
|
||||
=env.t('challenges')
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
label.control-label(for='new-group-name')
|
||||
| {{text}}
|
||||
=env.t('name')
|
||||
input.form-control#new-group-name.input-medium.option-content(required, type='text', placeholder='{{text}} Name', ng-model='newGroup.name')
|
||||
input.form-control#new-group-name.input-medium.option-content(required, type='text', placeholder=('{{text}} ' + env.t('name')), ng-model='newGroup.name')
|
||||
.form-group
|
||||
label(for='new-group-description')=env.t('description')
|
||||
textarea.form-control#new-group-description.option-content(cols='3', placeholder=env.t('description'), ng-model='newGroup.description')
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ script(type='text/ng-template', id='partials/options.social.party.html')
|
|||
include ./group
|
||||
div(ng-hide='group._id')
|
||||
div(ng-show='user.invitations.party.id')
|
||||
h2=env.t('invitedTo') + ' {{user.invitations.party.name}}'
|
||||
h2=env.t('invitedTo', {name: '{{user.invitations.party.name}}'})
|
||||
a.btn.btn-success(data-type='party', ng-click='join(user.invitations.party)')=env.t('accept')
|
||||
a.btn.btn-danger(ng-click='reject()')=env.t('reject')
|
||||
div(ng-hide='user.invitations.party.id')
|
||||
|
|
@ -25,7 +25,7 @@ script(type='text/ng-template', id='partials/options.social.party.html')
|
|||
|
||||
script(type='text/ng-template', id='partials/options.social.guilds.public.html')
|
||||
div(ng-repeat='invitation in user.invitations.guilds')
|
||||
h3=env.t('invitedTo') + ' {{invitation.name}}'
|
||||
h3=env.t('invitedTo', {name: '{{invitation.name}}'})
|
||||
a.btn.btn-success(data-type='guild', ng-click='join(invitation)')=env.t('accept')
|
||||
a.btn.btn-danger(ng-click='reject(invitation)')=env.t('reject')
|
||||
// Public Groups
|
||||
|
|
|
|||
|
|
@ -48,4 +48,5 @@ figure.herobox(ng-click='spell ? castEnd(profile, "user", $event) : clickMember(
|
|||
|
|
||||
=env.t('lvl')
|
||||
| {{profile.stats.lvl}}
|
||||
|
|
||||
span.glyphicon.glyphicon-plus-sign(ng-show='profile.achievements.rebirths', tooltip=env.t('reborn', {reLevel: "{{profile.achievements.rebirthLevel}}"}))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
.header-wrap(ng-controller='HeaderCtrl')
|
||||
a.label.label-default.undo-button(x-bind='click:undo', ng-show='_undo')=env.t('undo')
|
||||
div(ng-if='!user.preferences.hideHeader')
|
||||
include menu
|
||||
|
||||
header.site-header(ng-hide='user.preferences.hideHeader', role='banner', data-partysize='{{party.members.length>1 ? truarr(party.members.length) : 0}}')
|
||||
// avatar
|
||||
|
|
@ -38,6 +36,3 @@
|
|||
span(ng-controller='PartyCtrl')
|
||||
.herobox-wrap(ng-repeat='profile in partyMinusSelf')
|
||||
include avatar
|
||||
|
||||
.npc_bailey.npc_bailey_head(ng-show='user.flags.newStuff', tooltip=env.t('psst'), tooltip-placement='top', ng-click='openModal("newStuff")')
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +1,124 @@
|
|||
.user-menu(ng-controller='AuthCtrl')
|
||||
ul.nav.site-nav(ng-show='authenticated()')
|
||||
li.flyout
|
||||
h1.task-action-btn.tile.solid.user-reporter {{user.profile.name}}
|
||||
ul.flyout-content.nav.stacked
|
||||
li
|
||||
a.task-action-btn.tile.solid
|
||||
span(ng-show='$state.includes("options")', ui-sref='tasks')
|
||||
span.glyphicon.glyphicon-ok
|
||||
a.collapse-toolbar(popover=env.t('expandToolbar'),popover-placement='left',popover-trigger='mouseenter',ng-if='user.preferences.toolbarCollapsed',ng-click='set({"preferences.toolbarCollapsed":false})')
|
||||
span.glyphicon.glyphicon-chevron-down
|
||||
|
||||
nav.navbar.navbar-inverse(ng-controller='AuthCtrl',style='min-height:0px;margin-bottom:0px;border:0;border-radius:0px',ng-if='!user.preferences.toolbarCollapsed')
|
||||
|
||||
ul.nav.navbar-nav.pull-right
|
||||
li
|
||||
a(popover=env.t('collapseToolbar'),popover-placement='left',popover-trigger='mouseenter',,ng-if='!user.preferences.toolbarCollapsed',ng-click='set({"preferences.toolbarCollapsed":true})')
|
||||
span.glyphicon.glyphicon-chevron-up.white
|
||||
|
||||
.navbar-header
|
||||
button.navbar-toggle(type='button', ng-click='isNavbarCollapsed = !isNavbarCollapsed', ng-init='isNavbarCollapsed = true')
|
||||
span.sr-only Toggle navigation
|
||||
span.icon-bar
|
||||
span.icon-bar
|
||||
span.icon-bar
|
||||
.collapse.navbar-collapse(collapse="isNavbarCollapsed")
|
||||
ul.nav.navbar-nav
|
||||
li
|
||||
a(ui-sref='tasks',popover=env.t('tasks'),popover-placement='right',popover-trigger='mouseenter')
|
||||
span.glyphicon.glyphicon-check.white
|
||||
li.dropdown
|
||||
a.dropdown-toggle(popover='User',popover-placement='right',popover-trigger='mouseenter')
|
||||
span.glyphicon.glyphicon-user.white
|
||||
span.caret
|
||||
ul.dropdown-menu
|
||||
li
|
||||
a(ui-sref='options.profile.avatar')=env.t('avatar')
|
||||
li
|
||||
a(ui-sref='options.profile.stats')=env.t('stats')
|
||||
li
|
||||
a(ui-sref='options.profile.profile')=env.t('profile')
|
||||
li
|
||||
a(ng-click='logout()')
|
||||
span.glyphicon.glyphicon-share-alt
|
||||
|
|
||||
=env.t('tasks')
|
||||
span(ng-show='$state.includes("tasks")', ui-sref='options')
|
||||
span.glyphicon.glyphicon-wrench
|
||||
|
|
||||
=env.t('options')
|
||||
li
|
||||
a.task-action-btn.tile.solid(href="http://habitrpg.wikia.com/wiki/FAQ", target='_blank')
|
||||
span.glyphicon.glyphicon-book
|
||||
|
|
||||
=env.t('FAQ')
|
||||
li
|
||||
a.task-action-btn.tile.solid(href="https://vimeo.com/57654086", target='_blank')
|
||||
span.glyphicon.glyphicon-film
|
||||
//| Tutorials is too long to stay in the menu if we add a space
|
||||
=env.t('tutorials')
|
||||
li
|
||||
a.task-action-btn.tile.solid(ng-click='User.sync()')
|
||||
span.glyphicon.glyphicon-refresh
|
||||
|
|
||||
=env.t('sync')
|
||||
li
|
||||
a.task-action-btn.tile.solid(ng-click='logout()')
|
||||
span.glyphicon.glyphicon-share-alt
|
||||
|
|
||||
=env.t('logout')
|
||||
// party invitation notification
|
||||
a(ng-show='user.party.invitation', x-bind='click:gotoPartyChat')
|
||||
span.glyphicon.glyphicon-user
|
||||
// party chat notification
|
||||
a(ng-show='newChatMessages(_party.chat,user.party.lastMessageSeen)', x-bind='click:gotoPartyChat')
|
||||
span.glyphicon.glyphicon-comment(tooltip=env.t('partyNotification'))
|
||||
=env.t('logout')
|
||||
li.dropdown
|
||||
a.dropdown-toggle(popover=env.t('social'),popover-placement='right',popover-trigger='mouseenter')
|
||||
span.glyphicon.glyphicon-heart.white
|
||||
span.caret
|
||||
ul.dropdown-menu
|
||||
li
|
||||
a(ui-sref='options.social.tavern')=env.t('tavern')
|
||||
li
|
||||
a(ui-sref='options.social.party')=env.t('party')
|
||||
li
|
||||
a(ui-sref='options.social.guilds')=env.t('guilds')
|
||||
li
|
||||
a(ui-sref='options.social.challenges')=env.t('challenges')
|
||||
li
|
||||
a(ui-sref='options.social.hall')=env.t('hall')
|
||||
li.dropdown
|
||||
a.dropdown-toggle(popover=env.t('inventory'), popover-placement='right',popover-trigger='mouseenter')
|
||||
span.glyphicon.glyphicon-gift.white
|
||||
span.caret
|
||||
ul.dropdown-menu
|
||||
li
|
||||
a(ui-sref='options.inventory.drops')=env.t('market')
|
||||
li
|
||||
a(ui-sref='options.inventory.pets')=env.t('pets')
|
||||
li
|
||||
a(ui-sref='options.inventory.mounts')=env.t('mounts')
|
||||
li
|
||||
a(ui-sref='options.inventory.equipment')=env.t('equipment')
|
||||
li.dropdown
|
||||
a.dropdown-toggle(popover=env.t('settings'),popover-placement='right',popover-trigger='mouseenter')
|
||||
span.glyphicon.glyphicon-wrench.white
|
||||
span.caret
|
||||
ul.dropdown-menu
|
||||
li
|
||||
a(ui-sref='options.settings.settings')=env.t('site')
|
||||
li
|
||||
a(ui-sref='options.settings.api')=env.t('API')
|
||||
li
|
||||
a(ui-sref='options.settings.export')=env.t('export')
|
||||
li
|
||||
a(ui-sref='options.settings.subscription')=env.t('subscription')
|
||||
li.dropdown
|
||||
a.dropdown-toggle(popover=env.t('help'),popover-placement='right',popover-trigger='mouseenter')
|
||||
span.glyphicon.glyphicon-question-sign.white
|
||||
span.caret
|
||||
ul.dropdown-menu
|
||||
li
|
||||
a(href="http://habitrpg.wikia.com/wiki/FAQ", target='_blank')
|
||||
span.glyphicon.glyphicon-book
|
||||
|
|
||||
=env.t('FAQ')
|
||||
li
|
||||
a(href="https://vimeo.com/57654086", target='_blank')
|
||||
span.glyphicon.glyphicon-film
|
||||
|
|
||||
=env.t('tutorials')
|
||||
li
|
||||
a(ng-click='User.sync()',popover=env.t('sync'),popover-placement='right',popover-trigger='mouseenter')
|
||||
span.glyphicon.glyphicon-refresh.white
|
||||
li
|
||||
a.pull-right.gem-wallet(style='padding-top:8px;padding-bottom:0px;',ng-click='openModal("buyGems",{track:"Gems > Toolbar"})', popover-trigger='mouseenter', popover-title=env.t('gems'), popover=env.t('gemsWhatFor'), popover-placement='bottom',popover-append-to-body='true')
|
||||
//span.task-action-btn.tile.flush.bright.add-gems-btn +
|
||||
span.task-action-btn.tile.flush.neutral
|
||||
.Pet_Currency_Gem2x.Gems
|
||||
| {{user.balance * 4 | number:0}}
|
||||
li
|
||||
button.btn.btn-primary.navbar-btn(ng-if='!user.purchased.plan.customerId',ui-sref='options.settings.subscription',popover-trigger='mouseenter',popover-placement='bottom',popover-title=env.t('subscriptions'),popover=env.t('subDescription'),popover-append-to-body='true')=env.t('subscribe')
|
||||
button.btn.btn-default.navbar-btn(ng-if='user.purchased.plan.customerId',ui-sref='options.settings.subscription',popover-trigger='mouseenter',popover-placement='bottom',popover=env.t('manageSub'),popover-append-to-body='true')=env.t('subscribed')
|
||||
|
||||
li(ng-if='user.flags.newStuff')
|
||||
div.toolbar-bailey-container
|
||||
|
|
||||
.npc_bailey.npc_bailey_head(popover=env.t('psst'), popover-trigger='mouseenter', popover-placement='right', ng-click='openModal("newStuff")')
|
||||
|
||||
// Invitations
|
||||
li(ng-if='user.invitations.party.id')
|
||||
a(ui-sref='options.social.party')
|
||||
span.glyphicon.glyphicon-user.white(popover=env.t('invitedTo', {name: '{{user.invitations.party.name}}'}),popover-placement='bottom',popover-trigger='mouseenter',popover-append-to-body='true')
|
||||
li(ng-repeat='guild in user.invitations.guilds')
|
||||
a(ui-sref='options.social.guilds')
|
||||
span.glyphicon.glyphicon-user.white(popover=env.t('invitedTo', {name: '{{guild.name}}'}),popover-placement='bottom',popover-trigger='mouseenter',popover-append-to-body='true')
|
||||
// Chat notifs
|
||||
li(ng-repeat='(k,v) in user.newMessages', ng-if='v.value')
|
||||
a(ng-click='k==party._id ? $state.go("options.social.party") : $state.go("options.social.guilds.detail",{gid:k})')
|
||||
span.glyphicon.glyphicon-comment.white(popover='New message in "{{v.name}}"',popover-placement='bottom',popover-trigger='mouseenter',popover-append-to-body='true')
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,4 +8,5 @@ include ./settings
|
|||
include ./drops
|
||||
include ./classes
|
||||
include ./quests
|
||||
include ./rebirth
|
||||
include ./rebirth
|
||||
include ./limited
|
||||
30
views/shared/modals/limited.jade
Normal file
30
views/shared/modals/limited.jade
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//Valentine
|
||||
script(id='modals/valentine.html', type='text/ng-template')
|
||||
.modal-header
|
||||
h4
|
||||
.inventory_special_valentine.pull-right
|
||||
=env.t('valentineCard')
|
||||
.modal-body
|
||||
.bg-info(style='padding:10px')
|
||||
p To: {{user.profile.name}}, From: {{user.items.special.valentineReceived[0]}}
|
||||
hr
|
||||
div(ng-switch='Math.floor(Math.random()*3)')
|
||||
ul.list-unstyled(ng-switch-when='0')
|
||||
li "Roses are red
|
||||
li My Dailies are blue
|
||||
li I'm happy that I'm
|
||||
li In a Party with you!"
|
||||
ul.list-unstyled(ng-switch-when='1')
|
||||
li "Roses are red
|
||||
li Violets are nice
|
||||
li Let's get together
|
||||
li And fight against Vice!"
|
||||
ul.list-unstyled(ng-switch-default)
|
||||
li "Roses are red
|
||||
li This poem style is old
|
||||
li I hope that you like this
|
||||
li Cuz it cost ten gold."
|
||||
p
|
||||
small For enduring such a saccharine poem, you both receive the "Adoring Friends" badge!
|
||||
.modal-footer
|
||||
button.btn.btn-default(ng-click='user.ops.readValentine({});$close()')=env.t('ok')
|
||||
|
|
@ -1,17 +1,3 @@
|
|||
//-div(modal='!user.flags.mathUpdates')
|
||||
script(type='text/ng-template', id='modals/mathUpdates.html')
|
||||
.modal-header
|
||||
h4 Math Updates
|
||||
.modal-body
|
||||
.npc_tyler.pull-left
|
||||
p Hey all, Tyler here. Sorry for the rocky numbers (Exp, GP, etc) with the Class System roll-out! We're tweaking the math today, and things should be mostly finalized by tomorrow. <a target='_blank' href='https://trello.com/c/L1H6LiZ3/110-character-stats-attributes-the-class-system'>Follow our progress here</a>, and please do chime in!
|
||||
br
|
||||
p We've also changed classes to unlock at lvl 10 instead of 5, to prevent overwhelming our new users. And since users have expressed confusion, we'll be improving UI and clarifification. Stay tuned.
|
||||
br
|
||||
p iOS users, iTunes is on vacation until 12/28. Until we can push the updated app, you'll need to sync periodically to get the corrected numbers (app has old math, server saves the correct numbers). Android goes out ASAP. More info on <a target='_blank' href='https://trello.com/c/L1H6LiZ3/110-character-stats-attributes-the-class-system'>Trello</a>.
|
||||
.modal-footer
|
||||
button.btn.btn-default(ng-click='User.set({"flags.mathUpdates":true}); $close()') Dismiss
|
||||
|
||||
script(type='text/ng-template', id='modals/newStuff.html')
|
||||
.modal-header
|
||||
h4 New Stuff!
|
||||
|
|
@ -26,37 +12,58 @@ script(type='text/ng-template', id='modals/newStuff.html')
|
|||
h3.popover-title
|
||||
a(target='_blank', href='https://twitter.com/Mihakuu') Bailey
|
||||
.popover-content
|
||||
table.table.table-striped
|
||||
tr
|
||||
td
|
||||
h5 February Mystery Item
|
||||
p
|
||||
.pull-right.inventory_present
|
||||
| We're excited to announce a new feature a s a big thank-you to the awesome people who <a href='https://habitrpg.com/#/options/settings/subscription' target='_blank'>subscribe</a> to HabitRPG! Every month, all subscribers will now receive a limited-edition mystery item! The mystery item will be a stats-free costume piece (like the Absurd Party Robes) that will <strong>only</strong> be available to the people who are subscribers each month. The February 2014 item will be revealed on the 23rd to everyone, but all people who are subscribers during the month of February will receive it. <a href='https://habitrpg.com/#/options/settings/subscription' target='_blank'>Subscribe now</a>, get excited, and thank you so much for helping to support HabitRPG! We love you.
|
||||
tr
|
||||
td
|
||||
h5 Critical Hammer Of Bug-Crushing
|
||||
p
|
||||
.pull-right.weapon_special_critical
|
||||
| Some of you may have noticed that we periodically have some bugs that are nastier than the norm - the dreaded critical bugs. These monstrous apparitions have been snapping at the heels of many a player. For updates on what we're currently working on to improve site stability, read <a href='https://github.com/HabitRPG/habitrpg/issues/milestones' target='_blank'>this link</a> - and then jump in to help! Not only will programming assistance reward you with the usual contributor levels, but if you actually manage to fix a bug marked <a href='http://goo.gl/v4DnzB' target='_blank'>"critical,"</a> you will now receive the <em>Critical Hammer of Bug-Crushing</em> as your reward!
|
||||
tr
|
||||
td
|
||||
|
||||
h5 Rainbow Hair Colors
|
||||
p
|
||||
.pull-right.customize-option.hair_bangs_1_rainbow
|
||||
| Want to spruce up your avatar? Rainbow hair colors are now available! Dye your luscious locks purple, green, or even rainbow-striped, and passersby will look at you with envy.
|
||||
tr
|
||||
td
|
||||
h5 Stability Update
|
||||
p We've stabilized the site a lot (we're still working out kinks, but we're way better now). Follow the <a href='https://github.com/HabitRPG/habitrpg/issues/milestones' target='_blank'>progress here</a>, but here are some workarounds for now:
|
||||
ul
|
||||
li Click slower. <a href='https://github.com/HabitRPG/habitrpg/issues/2301#issuecomment-34398206' target='_blank'>VersionError</a> is caused by clicking things off too fast (we're working on a fix).
|
||||
li If you see an error, refresh before proceeding.
|
||||
|
||||
h5 Happy Valentine's Day!
|
||||
p Help motivate all of the lovely people in your life by sending them a caring valentine. Valentines can be purchased for 10 gold from the Item Store. For spreading love and joy throughout the community, both the giver AND the receiver get a coveted "adoring friends" badge. Hooray!
|
||||
p
|
||||
small.muted By Lemoness, mariahm, crystalphoenix, aiseant, zoebeagle, cole, lefnire
|
||||
small.muted By Lemoness and zoebeagle
|
||||
hr
|
||||
p
|
||||
small.muted 02/13/2014
|
||||
|
||||
hr
|
||||
h5 02/12/2014
|
||||
table.table.table-striped
|
||||
tr
|
||||
td
|
||||
h5 Chat & Invite Notifications
|
||||
p Chat & group-invitation notifications are back! Miss them? They currently work for all chat updates in parties & guilds. Any devs willing to jump into @tagging in Tavern, <a href='http://goo.gl/uhcjkg' target='_blank'>see here</a>.
|
||||
tr
|
||||
td
|
||||
h5 Toolbar
|
||||
p In order to make room for these notifs, we added a toolbar above the header. You can collapse the toolbar (far-right icon), but take care as Bailey notifs are inside the toolbar!
|
||||
hr
|
||||
h5 02/07/2014
|
||||
table.table.table-striped
|
||||
tr
|
||||
td
|
||||
h5 February Mystery Item
|
||||
p
|
||||
.pull-right.inventory_present
|
||||
| We're excited to announce a new feature a s a big thank-you to the awesome people who <a href='https://habitrpg.com/#/options/settings/subscription' target='_blank'>subscribe</a> to HabitRPG! Every month, all subscribers will now receive a limited-edition mystery item! The mystery item will be a stats-free costume piece (like the Absurd Party Robes) that will <strong>only</strong> be available to the people who are subscribers each month. The February 2014 item will be revealed on the 23rd to everyone, but all people who are subscribers during the month of February will receive it. <a href='https://habitrpg.com/#/options/settings/subscription' target='_blank'>Subscribe now</a>, get excited, and thank you so much for helping to support HabitRPG! We love you.
|
||||
tr
|
||||
td
|
||||
h5 Critical Hammer Of Bug-Crushing
|
||||
p
|
||||
.pull-right.weapon_special_critical
|
||||
| Some of you may have noticed that we periodically have some bugs that are nastier than the norm - the dreaded critical bugs. These monstrous apparitions have been snapping at the heels of many a player. For updates on what we're currently working on to improve site stability, read <a href='https://github.com/HabitRPG/habitrpg/issues/milestones' target='_blank'>this link</a> - and then jump in to help! Not only will programming assistance reward you with the usual contributor levels, but if you actually manage to fix a bug marked <a href='http://goo.gl/v4DnzB' target='_blank'>"critical,"</a> you will now receive the <em>Critical Hammer of Bug-Crushing</em> as your reward!
|
||||
tr
|
||||
td
|
||||
|
||||
h5 Rainbow Hair Colors
|
||||
p
|
||||
.pull-right.customize-option.hair_bangs_1_rainbow
|
||||
| Want to spruce up your avatar? Rainbow hair colors are now available! Dye your luscious locks purple, green, or even rainbow-striped, and passersby will look at you with envy.
|
||||
tr
|
||||
td
|
||||
h5 Stability Update
|
||||
p We've stabilized the site a lot (we're still working out kinks, but we're way better now). Follow the <a href='https://github.com/HabitRPG/habitrpg/issues/milestones' target='_blank'>progress here</a>, but here are some workarounds for now:
|
||||
ul
|
||||
li Click slower. <a href='https://github.com/HabitRPG/habitrpg/issues/2301#issuecomment-34398206' target='_blank'>VersionError</a> is caused by clicking things off too fast (we're working on a fix).
|
||||
li If you see an error, refresh before proceeding.
|
||||
|
||||
p
|
||||
small.muted By Lemoness, mariahm, crystalphoenix, aiseant, zoebeagle, cole, lefnire
|
||||
|
||||
hr
|
||||
h5 02/01/2014
|
||||
|
|
@ -68,6 +75,7 @@ script(type='text/ng-template', id='modals/newStuff.html')
|
|||
p
|
||||
small.muted by @baconsaur & @DanielTheBard
|
||||
|
||||
hr
|
||||
h5 01/30/2014
|
||||
table.table.table-striped
|
||||
tr
|
||||
|
|
|
|||
|
|
@ -133,3 +133,9 @@ div(ng-if='profile.achievements.habitBirthday')
|
|||
=env.t('habitBirthdayText')
|
||||
hr
|
||||
|
||||
div(ng-if='profile.achievements.valentine')
|
||||
.achievement.achievement-valentine
|
||||
h5=env.t('adoringFriends')
|
||||
small
|
||||
=env.t('adoringFriendsText', {cards: "{{profile.achievements.valentine}}"})
|
||||
hr
|
||||
|
|
|
|||
|
|
@ -58,8 +58,9 @@ script(id='templates/habitrpg-tasks.html', type="text/ng-template")
|
|||
span(bo-class='{"shop_{{item.key}} shop-sprite item-img": true}')
|
||||
p.task-text {{item.text}}
|
||||
|
||||
// Winter Event
|
||||
ul.items.rewards(ng-if='main && list.type=="reward" && (user.items.special.snowball>0 || user.stats.buffs.snowball)')
|
||||
// Events
|
||||
ul.items.rewards(ng-if='main && list.type=="reward"')
|
||||
|
||||
li.task.reward-item(ng-if='user.items.special.snowball>0',popover-trigger='mouseenter', popover-placement='top', popover='{{Content.spells.special.snowball.notes}}')
|
||||
.task-meta-controls
|
||||
span.task-notes
|
||||
|
|
@ -71,6 +72,7 @@ script(id='templates/habitrpg-tasks.html', type="text/ng-template")
|
|||
span.shop_snowball(style='display:inline-block;vertical-align:top;')
|
||||
// main content
|
||||
p.task-text {{Content.spells.special.snowball.text}}
|
||||
|
||||
li.task.reward-item(ng-if='user.stats.buffs.snowball',popover-trigger='mouseenter', popover-placement='top', popover='{{Content.spells.special.salt.notes}}')
|
||||
.task-meta-controls
|
||||
span.task-notes
|
||||
|
|
@ -84,6 +86,18 @@ script(id='templates/habitrpg-tasks.html', type="text/ng-template")
|
|||
span.shop_salt.shop-sprite.item-img
|
||||
p.task-text {{Content.spells.special.salt.text}}
|
||||
|
||||
li.task.reward-item(popover-trigger='mouseenter', popover-placement='top', popover='{{Content.spells.special.valentine.notes}}')
|
||||
.task-meta-controls
|
||||
span.task-notes
|
||||
span.glyphicon.glyphicon-comment
|
||||
//left-hand size commands
|
||||
.task-controls
|
||||
a.money.btn-buy.item-btn(ng-click='castStart(Content.spells.special.valentine)')
|
||||
span.reward-cost {{Content.spells.special.valentine.value}}
|
||||
span.shop_gold
|
||||
span.inventory_special_valentine.shop-sprite.item-img
|
||||
p.task-text {{Content.spells.special.valentine.text}}
|
||||
|
||||
// Spells
|
||||
ul.items(ng-if='main && list.type=="reward" && user.stats.class && !user.preferences.disableClasses')
|
||||
li.task.reward-item(ng-repeat='(k,spell) in Content.spells[user.stats.class]', ng-if='user.stats.lvl >= spell.lvl',popover-trigger='mouseenter', popover-placement='top', popover='{{spell.notes}}')
|
||||
|
|
|
|||
Loading…
Reference in a new issue