mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-08-03 08:21:07 +00:00
Merge branch 'develop' of github.com:HabitRPG/habitrpg into develop
This commit is contained in:
commit
6ce2f53503
17 changed files with 131 additions and 126 deletions
|
|
@ -8,7 +8,7 @@ import content from './content/index';
|
|||
|
||||
const DROP_ANIMALS = keys(content.pets);
|
||||
|
||||
function beastMasterProgress (pets) {
|
||||
function beastMasterProgress (pets = {}) {
|
||||
let count = 0;
|
||||
|
||||
each(DROP_ANIMALS, (animal) => {
|
||||
|
|
@ -19,7 +19,7 @@ function beastMasterProgress (pets) {
|
|||
return count;
|
||||
}
|
||||
|
||||
function dropPetsCurrentlyOwned (pets) {
|
||||
function dropPetsCurrentlyOwned (pets = {}) {
|
||||
let count = 0;
|
||||
|
||||
each(DROP_ANIMALS, (animal) => {
|
||||
|
|
@ -30,7 +30,7 @@ function dropPetsCurrentlyOwned (pets) {
|
|||
return count;
|
||||
}
|
||||
|
||||
function mountMasterProgress (mounts) {
|
||||
function mountMasterProgress (mounts = {}) {
|
||||
let count = 0;
|
||||
|
||||
each(DROP_ANIMALS, (animal) => {
|
||||
|
|
@ -41,7 +41,7 @@ function mountMasterProgress (mounts) {
|
|||
return count;
|
||||
}
|
||||
|
||||
function remainingGearInSet (userGear, set) {
|
||||
function remainingGearInSet (userGear = {}, set) {
|
||||
let gear = filter(content.gear.flat, (item) => {
|
||||
let setMatches = item.klass === set;
|
||||
let hasItem = userGear[item.key];
|
||||
|
|
@ -54,7 +54,7 @@ function remainingGearInSet (userGear, set) {
|
|||
return count;
|
||||
}
|
||||
|
||||
function questsOfCategory (userQuests, category) {
|
||||
function questsOfCategory (userQuests = {}, category) {
|
||||
let quests = filter(content.quests, (quest) => {
|
||||
let categoryMatches = quest.category === category;
|
||||
let hasQuest = userQuests[quest.key];
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ module.exports = function sortTask (user, req = {}) {
|
|||
if (index === -1) {
|
||||
throw new NotFound(i18n.t('messageTaskNotFound', req.language));
|
||||
}
|
||||
if (!to && !fromParam) {
|
||||
if (to == null && fromParam == null) { // eslint-disable-line eqeqeq
|
||||
throw new BadRequest('?to=__&from=__ are required');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
"TEST_DB_URI":"mongodb://localhost/habitrpg_test",
|
||||
"NODE_ENV":"development",
|
||||
"CRON_SAFE_MODE":"false",
|
||||
"CRON_SEMI_SAFE_MODE":"false",
|
||||
"MAINTENANCE_MODE": "false",
|
||||
"SESSION_SECRET":"YOUR SECRET HERE",
|
||||
"ADMIN_EMAIL": "you@example.com",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
generateUser,
|
||||
} from '../../../../helpers/api-v3-integration.helper';
|
||||
|
||||
xdescribe('POST /debug/make-admin (pended for v3 prod testing)', () => {
|
||||
describe('POST /debug/make-admin (pended for v3 prod testing)', () => {
|
||||
let user;
|
||||
|
||||
before(async () => {
|
||||
|
|
|
|||
|
|
@ -1,47 +1,53 @@
|
|||
"use strict";
|
||||
|
||||
habitrpg.controller("HallHeroesCtrl", ['$scope', '$rootScope', 'User', 'Notification', 'ApiUrl', '$resource',
|
||||
function($scope, $rootScope, User, Notification, ApiUrl, $resource) {
|
||||
var Hero = $resource(ApiUrl.get() + '/api/v3/hall/heroes/:uid', {uid:'@_id'});
|
||||
habitrpg.controller("HallHeroesCtrl", ['$scope', '$rootScope', 'User', 'Notification', 'ApiUrl', 'Hall',
|
||||
function($scope, $rootScope, User, Notification, ApiUrl, Hall) {
|
||||
$scope.hero = undefined;
|
||||
$scope.loadHero = function(uuid){
|
||||
Hero.query({uid:uuid}, function (heroData) {
|
||||
$scope.hero = heroData.data;
|
||||
$scope.currentHeroIndex = undefined;
|
||||
$scope.heroes = [];
|
||||
|
||||
Hall.getHeroes()
|
||||
.then(function (response) {
|
||||
$scope.heroes = response.data.data;
|
||||
});
|
||||
|
||||
$scope.loadHero = function(uuid, heroIndex) {
|
||||
$scope.currentHeroIndex = heroIndex;
|
||||
Hall.getHero(uuid)
|
||||
.then(function (response) {
|
||||
$scope.hero = response.data.data;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.saveHero = function(hero) {
|
||||
$scope.hero.contributor.admin = ($scope.hero.contributor.level > 7) ? true : false;
|
||||
hero.$save(function(){
|
||||
Notification.text("User updated");
|
||||
$scope.hero = undefined;
|
||||
$scope._heroID = undefined;
|
||||
Hero.query({}, function (heroesData) {
|
||||
$scope.heroes = heroesData.data;
|
||||
Hall.updateHero($scope.hero)
|
||||
.then(function (response) {
|
||||
Notification.text("User updated");
|
||||
$scope.hero = undefined;
|
||||
$scope._heroID = undefined;
|
||||
$scope.heroes[$scope.currentHeroIndex] = response.data.data;
|
||||
$scope.currentHeroIndex = undefined;
|
||||
});
|
||||
})
|
||||
}
|
||||
Hero.query({}, function (heroesData) {
|
||||
$scope.heroes = heroesData.data;
|
||||
});
|
||||
|
||||
$scope.populateContributorInput = function(id) {
|
||||
$scope.populateContributorInput = function(id, index) {
|
||||
$scope._heroID = id;
|
||||
window.scrollTo(0,200);
|
||||
$scope.loadHero(id);
|
||||
window.scrollTo(0, 200);
|
||||
$scope.loadHero(id, index);
|
||||
};
|
||||
}]);
|
||||
|
||||
habitrpg.controller("HallPatronsCtrl", ['$scope', '$rootScope', 'User', 'Notification', 'ApiUrl', '$resource',
|
||||
function($scope, $rootScope, User, Notification, ApiUrl, $resource) {
|
||||
var Patron = $resource(ApiUrl.get() + '/api/v3/hall/patrons/:uid', {uid:'@_id'});
|
||||
|
||||
habitrpg.controller("HallPatronsCtrl", ['$scope', '$rootScope', 'User', 'Notification', 'ApiUrl', 'Hall',
|
||||
function($scope, $rootScope, User, Notification, ApiUrl, Hall) {
|
||||
var page = 0;
|
||||
$scope.patrons = [];
|
||||
|
||||
$scope.loadMore = function(){
|
||||
Patron.query({page: page++}, function(patronsData){
|
||||
$scope.patrons = $scope.patrons.concat(patronsData.data);
|
||||
})
|
||||
$scope.loadMore = function() {
|
||||
Hall.getPatrons(page++)
|
||||
.then(function (response) {
|
||||
$scope.patrons = $scope.patrons.concat(response.data.data);
|
||||
});
|
||||
}
|
||||
$scope.loadMore();
|
||||
|
||||
|
|
|
|||
|
|
@ -193,24 +193,27 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
|
|||
// Don't allow creation of an empty checklist item
|
||||
// TODO Provide UI feedback that this item is still blank
|
||||
} else if ($index == task.checklist.length - 1) {
|
||||
Tasks.addChecklistItem(task._id, task.checklist[$index]);
|
||||
task.checklist.push({completed:false,text:''});
|
||||
focusChecklist(task,task.checklist.length-1);
|
||||
Tasks.addChecklistItem(task._id, task.checklist[$index])
|
||||
.then(function (response) {
|
||||
task.checklist[$index] = response.data.data.checklist[$index];
|
||||
});
|
||||
task.checklist.push({completed:false, text:''});
|
||||
focusChecklist(task, task.checklist.length - 1);
|
||||
} else {
|
||||
$scope.saveTask(task, true);
|
||||
focusChecklist(task, $index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.removeChecklistItem = function(task, $event, $index, force){
|
||||
$scope.removeChecklistItem = function(task, $event, $index, force) {
|
||||
// Remove item if clicked on trash icon
|
||||
if (force) {
|
||||
Tasks.removeChecklistItem(task._id, task.checklist[$index].id);
|
||||
if (task.checklist[$index].id) Tasks.removeChecklistItem(task._id, task.checklist[$index].id);
|
||||
task.checklist.splice($index, 1);
|
||||
} else if (!task.checklist[$index].text) {
|
||||
// User deleted all the text and is now wishing to delete the item
|
||||
// saveTask will prune the empty item
|
||||
Tasks.removeChecklistItem(task._id, task.checklist[$index].id);
|
||||
if (task.checklist[$index].id) Tasks.removeChecklistItem(task._id, task.checklist[$index].id);
|
||||
// Move focus if the list is still non-empty
|
||||
if ($index > 0)
|
||||
focusChecklist(task, $index-1);
|
||||
|
|
|
|||
41
website/client/js/services/hallServices.js
Normal file
41
website/client/js/services/hallServices.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('habitrpg')
|
||||
.factory('Hall', [ '$rootScope', 'ApiUrl', '$http',
|
||||
function($rootScope, ApiUrl, $http) {
|
||||
var apiV3Prefix = '/api/v3';
|
||||
var Hall = {};
|
||||
|
||||
Hall.getHeroes = function () {
|
||||
return $http({
|
||||
method: 'GET',
|
||||
url: apiV3Prefix + '/hall/heroes',
|
||||
});
|
||||
}
|
||||
|
||||
Hall.getHero = function (uuid) {
|
||||
return $http({
|
||||
method: 'GET',
|
||||
url: apiV3Prefix + '/hall/heroes/' + uuid,
|
||||
});
|
||||
}
|
||||
|
||||
Hall.updateHero = function (heroDetails) {
|
||||
return $http({
|
||||
method: 'PUT',
|
||||
url: apiV3Prefix + '/hall/heroes/' + heroDetails._id,
|
||||
data: heroDetails,
|
||||
});
|
||||
}
|
||||
|
||||
Hall.getPatrons = function (page) {
|
||||
if (!page) page = 0;
|
||||
|
||||
return $http({
|
||||
method: 'GET',
|
||||
url: apiV3Prefix + '/hall/patrons?page=' + page,
|
||||
});
|
||||
}
|
||||
|
||||
return Hall;
|
||||
}]);
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
var TASK_KEYS_TO_REMOVE = ['_id', 'completed', 'date', 'dateCompleted', 'history', 'id', 'streak', 'createdAt'];
|
||||
var TASK_KEYS_TO_REMOVE = ['_id', 'completed', 'date', 'dateCompleted', 'history', 'id', 'streak', 'createdAt', 'challenge'];
|
||||
|
||||
angular.module('habitrpg')
|
||||
.factory('Tasks', ['$rootScope', 'Shared', '$http',
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@
|
|||
"js/services/socialServices.js",
|
||||
"js/services/statServices.js",
|
||||
"js/services/userServices.js",
|
||||
"js/services/hallServices.js",
|
||||
|
||||
"js/filters/money.js",
|
||||
"js/filters/roundLargeNumbers.js",
|
||||
|
|
|
|||
|
|
@ -88,21 +88,20 @@ api.setCron = {
|
|||
*
|
||||
* @apiSuccess {Object} data An empty Object
|
||||
*/
|
||||
// TODO: Re-enable after v3 prod testing is done
|
||||
// api.makeAdmin = {
|
||||
// method: 'POST',
|
||||
// url: '/debug/make-admin',
|
||||
// middlewares: [ensureDevelpmentMode, authWithHeaders()],
|
||||
// async handler (req, res) {
|
||||
// let user = res.locals.user;
|
||||
//
|
||||
// user.contributor.admin = true;
|
||||
//
|
||||
// await user.save();
|
||||
//
|
||||
// res.respond(200, {});
|
||||
// },
|
||||
// };
|
||||
api.makeAdmin = {
|
||||
method: 'POST',
|
||||
url: '/debug/make-admin',
|
||||
middlewares: [ensureDevelpmentMode, authWithHeaders()],
|
||||
async handler (req, res) {
|
||||
let user = res.locals.user;
|
||||
|
||||
user.contributor.admin = true;
|
||||
|
||||
await user.save();
|
||||
|
||||
res.respond(200, {});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {post} /api/v3/debug/modify-inventory Manipulate user's inventory
|
||||
|
|
|
|||
|
|
@ -357,7 +357,7 @@ function _generateWebhookTaskData (task, direction, delta, stats, user) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @api {put} /api/v3/tasks/:taskId/score/:direction Score a task
|
||||
* @api {post} /api/v3/tasks/:taskId/score/:direction Score a task
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName ScoreTask
|
||||
* @apiGroup Task
|
||||
|
|
|
|||
|
|
@ -48,6 +48,15 @@ _.each(staticPages, (name) => {
|
|||
};
|
||||
});
|
||||
|
||||
api.redirectApi = {
|
||||
method: 'GET',
|
||||
url: '/static/api',
|
||||
runCron: false,
|
||||
async handler (req, res) {
|
||||
res.redirect(301, '/apidoc');
|
||||
},
|
||||
};
|
||||
|
||||
let shareables = ['level-up', 'hatch-pet', 'raise-pet', 'unlock-quest', 'won-challenge', 'achievement'];
|
||||
|
||||
_.each(shareables, (name) => {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import _ from 'lodash';
|
|||
import nconf from 'nconf';
|
||||
|
||||
const CRON_SAFE_MODE = nconf.get('CRON_SAFE_MODE') === 'true';
|
||||
const CRON_SEMI_SAFE_MODE = nconf.get('CRON_SEMI_SAFE_MODE') === 'true';
|
||||
const shouldDo = common.shouldDo;
|
||||
const scoreTask = common.ops.scoreTask;
|
||||
// const maxPMs = 200;
|
||||
|
|
@ -175,13 +176,15 @@ export function cron (options = {}) {
|
|||
cron: true,
|
||||
});
|
||||
|
||||
// Apply damage from a boss, less damage for Trivial priority (difficulty)
|
||||
user.party.quest.progress.down += delta * (task.priority < 1 ? task.priority : 1);
|
||||
// NB: Medium and Hard priorities do not increase damage from boss. This was by accident
|
||||
// initially, and when we realised, we could not fix it because users are used to
|
||||
// their Medium and Hard Dailies doing an Easy amount of damage from boss.
|
||||
// Easy is task.priority = 1. Anything < 1 will be Trivial (0.1) or any future
|
||||
// setting between Trivial and Easy.
|
||||
if (!CRON_SEMI_SAFE_MODE) {
|
||||
// Apply damage from a boss, less damage for Trivial priority (difficulty)
|
||||
user.party.quest.progress.down += delta * (task.priority < 1 ? task.priority : 1);
|
||||
// NB: Medium and Hard priorities do not increase damage from boss. This was by accident
|
||||
// initially, and when we realised, we could not fix it because users are used to
|
||||
// their Medium and Hard Dailies doing an Easy amount of damage from boss.
|
||||
// Easy is task.priority = 1. Anything < 1 will be Trivial (0.1) or any future
|
||||
// setting between Trivial and Easy.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -517,7 +517,8 @@ schema.statics.bossQuest = async function bossQuest (user, progress) {
|
|||
group.quest.progress.hp -= progress.up;
|
||||
// TODO Create a party preferred language option so emits like this can be localized. Suggestion: Always display the English version too. Or, if English is not displayed to the players, at least include it in a new field in the chat object that's visible in the database - essential for admins when troubleshooting quests!
|
||||
let playerAttack = `${user.profile.name} attacks ${quest.boss.name('en')} for ${progress.up.toFixed(1)} damage.`;
|
||||
let bossAttack = nconf.get('CRON_SAFE_MODE') === 'true' ? `${quest.boss.name('en')} did not attack the party because it was asleep while maintenance was happening.` : `${quest.boss.name('en')} attacks party for ${Math.abs(down).toFixed(1)} damage.`;
|
||||
let bossAttack = nconf.get('CRON_SAFE_MODE') === 'true' || nconf.get('CRON_SEMI_SAFE_MODE') === 'true' ? `${quest.boss.name('en')} does not attack, because it respects the fact that there are some bugs\` \`post-maintenance and it doesn't want to hurt anyone unfairly. It will continue its rampage soon!` : `${quest.boss.name('en')} attacks party for ${Math.abs(down).toFixed(1)} damage.`;
|
||||
// TODO Consider putting the safe mode boss attack message in an ENV var
|
||||
group.sendChat(`\`${playerAttack}\` \`${bossAttack}\``);
|
||||
|
||||
// If boss has Rage, increment Rage as well
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
var nconf = require('nconf');
|
||||
var express = require('express');
|
||||
var router = express.Router();
|
||||
var _ = require('lodash');
|
||||
var locals = require('../middlewares/api-v2/locals');
|
||||
var i18n = require('../libs/api-v2/i18n');
|
||||
var md = require('markdown-it')({
|
||||
html: true,
|
||||
});
|
||||
|
||||
const TOTAL_USER_COUNT = '1,100,000';
|
||||
|
||||
// -------- App --------
|
||||
router.get('/', i18n.getUserLanguage, locals, function(req, res) {
|
||||
if (!req.headers['x-api-user'] && !req.headers['x-api-key'] && !(req.session && req.session.userId))
|
||||
return res.redirect('/static/front');
|
||||
|
||||
return res.render('index', {
|
||||
title: 'Habitica | Your Life The Role Playing Game',
|
||||
env: res.locals.habitrpg
|
||||
});
|
||||
});
|
||||
|
||||
// -------- Static Pages --------
|
||||
|
||||
var pages = ['front', 'privacy', 'terms', 'api', 'features', 'videos', 'contact', 'plans', 'new-stuff', 'community-guidelines', 'old-news', 'press-kit', 'faq', 'overview', 'apps', 'clear-browser-data', 'merch', 'maintenance-info'];
|
||||
|
||||
_.each(pages, function(name){
|
||||
router.get('/static/' + name, i18n.getUserLanguage, locals, function(req, res) {
|
||||
res.render( 'static/' + name, {
|
||||
env: res.locals.habitrpg,
|
||||
md: md,
|
||||
userCount: TOTAL_USER_COUNT
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// -------- Social Media Sharing --------
|
||||
|
||||
var shareables = ['level-up','hatch-pet','raise-pet','unlock-quest','won-challenge','achievement'];
|
||||
|
||||
_.each(shareables, function(name){
|
||||
router.get('/social/' + name, i18n.getUserLanguage, locals, function(req, res) {
|
||||
res.render( 'social/' + name, {
|
||||
env: res.locals.habitrpg,
|
||||
md: md,
|
||||
userCount: TOTAL_USER_COUNT
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// --------- Redirects --------
|
||||
|
||||
router.get('/static/extensions', function(req, res) {
|
||||
res.redirect('http://habitica.wikia.com/wiki/App_and_Extension_Integrations');
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
|
@ -88,7 +88,7 @@ script(type='text/ng-template', id='partials/options.social.hall.heroes.html')
|
|||
span(ng-class='userAdminGlyphiconStyle(hero)')
|
||||
span(ng-if='!hero.contributor.admin')
|
||||
a.label.label-default(ng-class='userLevelStyle(hero)', ng-click='clickMember(hero._id, true)') {{hero.profile.name}}
|
||||
td(ng-if='user.contributor.admin', ng-click='populateContributorInput(hero._id)').btn-link {{hero._id}}
|
||||
td(ng-if='user.contributor.admin', ng-click='populateContributorInput(hero._id, $index)').btn-link {{hero._id}}
|
||||
td {{hero.contributor.level}}
|
||||
td {{hero.contributor.text}}
|
||||
td
|
||||
|
|
|
|||
|
|
@ -98,8 +98,7 @@ footer.footer(ng-controller='FooterCtrl')
|
|||
a.btn.btn-default(ng-click='addLevelsAndGold()') +Exp +GP +MP
|
||||
a.btn.btn-default(ng-click='addOneLevel()') +1 Level
|
||||
a.btn.btn-default(ng-click='addQuestProgress()' tooltip="+1000 to boss quests. 300 items to collection quests") Quest Progress Up
|
||||
// TODO Re-enable after v3 prod testing
|
||||
// a.btn.btn-default(ng-click='makeAdmin()') Make Admin
|
||||
a.btn.btn-default(ng-click='makeAdmin()') Make Admin
|
||||
a.btn.btn-default(ng-click='openModifyInventoryModal()') Modify Inventory
|
||||
|
||||
div(ng-init='deferredScripts()')
|
||||
|
|
|
|||
Loading…
Reference in a new issue