mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
begins porting group model to es6
This commit is contained in:
@@ -75,7 +75,7 @@
|
|||||||
"comma-style": [2, "last"],
|
"comma-style": [2, "last"],
|
||||||
"comma-dangle": [2, "always-multiline"],
|
"comma-dangle": [2, "always-multiline"],
|
||||||
"computed-property-spacing": [2, "never"],
|
"computed-property-spacing": [2, "never"],
|
||||||
"consistent-this": [2, "self"],
|
"consistent-this": [0, "self"],
|
||||||
"func-names": 2,
|
"func-names": 2,
|
||||||
"func-style": [2, "declaration", { "allowArrowFunctions": true }],
|
"func-style": [2, "declaration", { "allowArrowFunctions": true }],
|
||||||
"block-spacing": [2, "always"],
|
"block-spacing": [2, "always"],
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ const SERVER_FILES = [
|
|||||||
'./website/src/**/api-v3/**/*.js',
|
'./website/src/**/api-v3/**/*.js',
|
||||||
'./website/src/models/user.js',
|
'./website/src/models/user.js',
|
||||||
'./website/src/models/task.js',
|
'./website/src/models/task.js',
|
||||||
|
'./website/src/models/group.js',
|
||||||
|
'./website/src/models/tag.js',
|
||||||
'./website/src/models/emailUnsubscription.js',
|
'./website/src/models/emailUnsubscription.js',
|
||||||
'./website/src/server.js',
|
'./website/src/server.js',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,26 +1,28 @@
|
|||||||
var mongoose = require("mongoose");
|
import mongoose from 'mongoose';
|
||||||
var Schema = mongoose.Schema;
|
import { model as User} from './user';
|
||||||
var User = require('./user').model;
|
import shared from '../../../common';
|
||||||
var shared = require('../../../common');
|
import _ from 'lodash';
|
||||||
var _ = require('lodash');
|
// var async = require('async');
|
||||||
var async = require('async');
|
import logger from '../libs/api-v3/logger';
|
||||||
var logging = require('../libs/api-v2/logging');
|
// var Challenge = require('./../models/challenge').model;
|
||||||
var Challenge = require('./../models/challenge').model;
|
import firebase from '../libs/api-v2/firebase';
|
||||||
var firebase = require('../libs/api-v2/firebase');
|
import baseModel from '../libs/api-v3/baseModel';
|
||||||
|
import Q from 'q';
|
||||||
|
|
||||||
// NOTE any change to groups' members in MongoDB will have to be run through the API
|
let Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
// NOTE once Firebase is enabled any change to groups' members in MongoDB will have to be run through the API
|
||||||
// changes made directly to the db will cause Firebase to get out of sync
|
// changes made directly to the db will cause Firebase to get out of sync
|
||||||
var GroupSchema = new Schema({
|
export let schema = new Schema({
|
||||||
_id: {type: String, 'default': shared.uuid},
|
name: {type: String, required: true},
|
||||||
name: String,
|
|
||||||
description: String,
|
description: String,
|
||||||
leader: {type: String, ref: 'User'},
|
leader: {type: String, ref: 'User'},
|
||||||
members: [{type: String, ref: 'User'}],
|
members: [{type: String, ref: 'User'}], // TODO do we need this? could depend on back-ref instead (User.find({group:GID})
|
||||||
invites: [{type: String, ref: 'User'}],
|
invites: [{type: String, ref: 'User'}], // TODO do we need this? could depend on back-ref instead (User.find({group:GID})
|
||||||
type: {type: String, "enum": ['guild', 'party']},
|
type: {type: String, enum: ['guild', 'party'], required: true},
|
||||||
privacy: {type: String, "enum": ['private', 'public'], 'default':'private'},
|
privacy: {type: String, enum: ['private', 'public'], default: 'private', required: true},
|
||||||
//_v: {type: Number,'default': 0},
|
// _v: {type: Number,'default': 0}, // TODO ?
|
||||||
chat: Array,
|
chat: Array, // TODO ?
|
||||||
/*
|
/*
|
||||||
# [{
|
# [{
|
||||||
# timestamp: Date
|
# timestamp: Date
|
||||||
@@ -32,41 +34,52 @@ var GroupSchema = new Schema({
|
|||||||
# }]
|
# }]
|
||||||
*/
|
*/
|
||||||
leaderOnly: { // restrict group actions to leader (members can't do them)
|
leaderOnly: { // restrict group actions to leader (members can't do them)
|
||||||
challenges: {type:Boolean, 'default':false},
|
challenges: {type: Boolean, default: false, required: true},
|
||||||
//invites: {type:Boolean, 'default':false}
|
// invites: {type:Boolean, 'default':false} // TODO ?
|
||||||
},
|
},
|
||||||
memberCount: {type: Number, 'default': 0},
|
memberCount: {type: Number, default: 0},
|
||||||
challengeCount: {type: Number, 'default': 0},
|
challengeCount: {type: Number, default: 0},
|
||||||
balance: Number,
|
balance: {type: Number, default: 0},
|
||||||
logo: String,
|
logo: String,
|
||||||
leaderMessage: String,
|
leaderMessage: String,
|
||||||
challenges: [{type:'String', ref:'Challenge'}], // do we need this? could depend on back-ref instead (Challenge.find({group:GID}))
|
challenges: [{type: String, ref: 'Challenge'}], // TODO do we need this? could depend on back-ref instead (Challenge.find({group:GID}))
|
||||||
quest: {
|
quest: {
|
||||||
key: String,
|
key: String,
|
||||||
active: {type:Boolean, 'default':false},
|
active: {type: Boolean, default: false},
|
||||||
leader: {type:String, ref:'User'},
|
leader: {type: String, ref: 'User'},
|
||||||
progress:{
|
progress: {
|
||||||
hp: Number,
|
hp: Number,
|
||||||
collect: {type:Schema.Types.Mixed, 'default':{}}, // {feather: 5, ingot: 3}
|
collect: {type: Schema.Types.Mixed, default: () => {
|
||||||
|
return {};
|
||||||
|
}}, // {feather: 5, ingot: 3}
|
||||||
rage: Number, // limit break / "energy stored in shell", for explosion-attacks
|
rage: Number, // limit break / "energy stored in shell", for explosion-attacks
|
||||||
},
|
},
|
||||||
|
|
||||||
//Shows boolean for each party-member who has accepted the quest. Eg {UUID: true, UUID: false}. Once all users click
|
// Shows boolean for each party-member who has accepted the quest. Eg {UUID: true, UUID: false}. Once all users click
|
||||||
//'Accept', the quest begins. If a false user waits too long, probably a good sign to prod them or boot them.
|
// 'Accept', the quest begins. If a false user waits too long, probably a good sign to prod them or boot them.
|
||||||
//TODO when booting user, remove from .joined and check again if we can now start the quest
|
// TODO when booting user, remove from .joined and check again if we can now start the quest
|
||||||
members: Schema.Types.Mixed,
|
members: {type: Schema.Types.Mixed, default: () => {
|
||||||
extra: Schema.Types.Mixed
|
return {};
|
||||||
}
|
}},
|
||||||
|
extra: {type: Schema.Types.Mixed, default: () => {
|
||||||
|
return {};
|
||||||
|
}},
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
strict: 'throw',
|
strict: true,
|
||||||
minimize: false // So empty objects are returned
|
minimize: false, // So empty objects are returned
|
||||||
});
|
});
|
||||||
|
|
||||||
|
schema.plugin(baseModel, {
|
||||||
|
noSet: ['_id'],
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO migration
|
||||||
/**
|
/**
|
||||||
* Derby duplicated stuff. This is a temporary solution, once we're completely off derby we'll run an mongo migration
|
* Derby duplicated stuff. This is a temporary solution, once we're completely off derby we'll run an mongo migration
|
||||||
* to remove duplicates, then take these fucntions out
|
* to remove duplicates, then take these fucntions out
|
||||||
*/
|
*/
|
||||||
function removeDuplicates(doc){
|
/* function removeDuplicates(doc){
|
||||||
// Remove duplicate members
|
// Remove duplicate members
|
||||||
if (doc.members) {
|
if (doc.members) {
|
||||||
var uniqMembers = _.uniq(doc.members);
|
var uniqMembers = _.uniq(doc.members);
|
||||||
@@ -74,219 +87,238 @@ function removeDuplicates(doc){
|
|||||||
doc.members = uniqMembers;
|
doc.members = uniqMembers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// FIXME this isn't always triggered, since we sometimes use update() or findByIdAndUpdate()
|
// FIXME this isn't always triggered, since we sometimes use update() or findByIdAndUpdate()
|
||||||
// @see https://github.com/LearnBoost/mongoose/issues/964
|
// @see https://github.com/LearnBoost/mongoose/issues/964 -> Add update pre?
|
||||||
GroupSchema.pre('save', function(next){
|
// TODO necessary?
|
||||||
removeDuplicates(this);
|
schema.pre('save', function preSaveGroup (next) {
|
||||||
|
// removeDuplicates(this);
|
||||||
this.memberCount = _.size(this.members);
|
this.memberCount = _.size(this.members);
|
||||||
this.challengeCount = _.size(this.challenges);
|
this.challengeCount = _.size(this.challenges);
|
||||||
next();
|
return next();
|
||||||
})
|
|
||||||
|
|
||||||
GroupSchema.pre('remove', function(next) {
|
|
||||||
var group = this;
|
|
||||||
async.waterfall([
|
|
||||||
function(cb) {
|
|
||||||
var invitationQuery = {};
|
|
||||||
var groupType = group.type;
|
|
||||||
//Add an 's' to group type guild because the model has the plural version
|
|
||||||
if (group.type == "guild") groupType += "s";
|
|
||||||
invitationQuery['invitations.' + groupType + '.id'] = group._id;
|
|
||||||
User.find(invitationQuery, cb);
|
|
||||||
},
|
|
||||||
function(users, cb) {
|
|
||||||
if (users) {
|
|
||||||
users.forEach(function (user, index, array) {
|
|
||||||
if ( group.type == "party" ) {
|
|
||||||
user.invitations.party = {};
|
|
||||||
} else {
|
|
||||||
var i = _.findIndex(user.invitations.guilds, {id: group._id});
|
|
||||||
user.invitations.guilds.splice(i, 1);
|
|
||||||
}
|
|
||||||
user.save();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cb();
|
|
||||||
}
|
|
||||||
], next);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
GroupSchema.post('remove', function(group) {
|
schema.pre('remove', true, function preRemoveGroup (next, done) {
|
||||||
|
next();
|
||||||
|
let group = this;
|
||||||
|
|
||||||
|
// Remove invitations when group is deleted
|
||||||
|
// TODO verify it works fir everything
|
||||||
|
User.find({
|
||||||
|
// TODO remove need for guilds s in migration? same for id -> _id
|
||||||
|
[`invitations.${group.type}${group.type === 'guild' ? 's' : ''}.id`]: group._id,
|
||||||
|
}).exec()
|
||||||
|
.then(users => {
|
||||||
|
return Q.all(users.map(user => {
|
||||||
|
if (group.type === 'party') {
|
||||||
|
user.invitations.party = {};
|
||||||
|
} else {
|
||||||
|
let i = _.findIndex(user.invitations.guilds, {id: group._id});
|
||||||
|
user.invitations.guilds.splice(i, 1);
|
||||||
|
}
|
||||||
|
return user.save(); // TODO update?
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
.then(done)
|
||||||
|
.catch(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
schema.post('remove', function postRemoveGroup (group) {
|
||||||
firebase.deleteGroup(group._id);
|
firebase.deleteGroup(group._id);
|
||||||
});
|
});
|
||||||
|
|
||||||
GroupSchema.methods.toJSON = function(){
|
schema.methods.toJSON = function groupToJSON () {
|
||||||
var doc = this.toObject();
|
let doc = this.toObject();
|
||||||
removeDuplicates(doc);
|
// removeDuplicates(doc);
|
||||||
doc._isMember = this._isMember;
|
doc._isMember = this._isMember; // TODO ?
|
||||||
|
|
||||||
//fix(groups): temp fix to remove chat entries stored as strings (not sure why that's happening..).
|
// TODO migration
|
||||||
|
// fix(groups): temp fix to remove chat entries stored as strings (not sure why that's happening..).
|
||||||
// Required as angular 1.3 is strict on dupes, and no message.id to `track by`
|
// Required as angular 1.3 is strict on dupes, and no message.id to `track by`
|
||||||
_.remove(doc.chat,function(msg){return !msg.id});
|
_.remove(doc.chat, msg => !msg.id);
|
||||||
|
|
||||||
|
// TODO should not be needed here
|
||||||
// @see pre('save') comment above
|
// @see pre('save') comment above
|
||||||
this.memberCount = _.size(this.members);
|
this.memberCount = _.size(this.members);
|
||||||
this.challengeCount = _.size(this.challenges);
|
this.challengeCount = _.size(this.challenges);
|
||||||
|
|
||||||
return doc;
|
return doc;
|
||||||
}
|
};
|
||||||
|
|
||||||
var chatDefaults = module.exports.chatDefaults = function(msg,user){
|
// TODO move to its own model
|
||||||
var message = {
|
export function chatDefaults (msg, user) {
|
||||||
|
let message = {
|
||||||
id: shared.uuid(),
|
id: shared.uuid(),
|
||||||
text: msg,
|
text: msg,
|
||||||
timestamp: +new Date,
|
timestamp: Number(new Date()),
|
||||||
likes: {},
|
likes: {},
|
||||||
flags: {},
|
flags: {},
|
||||||
flagCount: 0
|
flagCount: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
_.defaults(message, {
|
_.defaults(message, {
|
||||||
uuid: user._id,
|
uuid: user._id,
|
||||||
contributor: user.contributor && user.contributor.toObject(),
|
contributor: user.contributor && user.contributor.toObject(),
|
||||||
backer: user.backer && user.backer.toObject(),
|
backer: user.backer && user.backer.toObject(),
|
||||||
user: user.profile.name
|
user: user.profile.name,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
message.uuid = 'system';
|
message.uuid = 'system';
|
||||||
}
|
}
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
GroupSchema.methods.sendChat = function(message, user){
|
|
||||||
var group = this;
|
schema.methods.sendChat = function sendChat (message, user) {
|
||||||
group.chat.unshift(chatDefaults(message,user));
|
this.chat.unshift(chatDefaults(message, user));
|
||||||
group.chat.splice(200);
|
this.chat.splice(200);
|
||||||
// Kick off chat notifications in the background.
|
|
||||||
var lastSeenUpdate = {$set:{}, $inc:{_v:1}};
|
// Kick off chat notifications in the background. // TODO refactor
|
||||||
lastSeenUpdate['$set']['newMessages.'+group._id] = {name:group.name,value:true};
|
let lastSeenUpdate = {$set: {}, $inc: {_v: 1}}; // TODO standardize this _v inc at the user level
|
||||||
if (group._id == 'habitrpg') {
|
lastSeenUpdate.$set[`newMessages.${this._id}`] = {name: this.name, value: true};
|
||||||
|
|
||||||
|
if (this._id === 'habitrpg') {
|
||||||
// TODO For Tavern, only notify them if their name was mentioned
|
// 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?
|
// 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();
|
// User.update({'profile.name':{$in:profileNames}},lastSeenUpdate,{multi:true}).exec();
|
||||||
} else {
|
} else {
|
||||||
mongoose.model('User').update({_id:{$in:group.members, $ne: user ? user._id : ''}},lastSeenUpdate,{multi:true}).exec();
|
User.update({
|
||||||
|
_id: {$in: this.members, $ne: user ? user._id : ''},
|
||||||
|
}, lastSeenUpdate, {multi: true}).exec();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
var cleanQuestProgress = function(merge){
|
function _cleanQuestProgress (merge) {
|
||||||
var clean = {
|
// TODO clone? (also in sendChat message)
|
||||||
|
let clean = {
|
||||||
key: null,
|
key: null,
|
||||||
progress: {
|
progress: {
|
||||||
up: 0,
|
up: 0,
|
||||||
down: 0,
|
down: 0,
|
||||||
collect: {}
|
collect: {},
|
||||||
},
|
},
|
||||||
completed: null,
|
completed: null,
|
||||||
RSVPNeeded: false
|
RSVPNeeded: false, // TODO absolutely change this cryptic name
|
||||||
};
|
};
|
||||||
merge = merge || {progress:{}};
|
|
||||||
_.merge(clean, _.omit(merge,'progress'));
|
if (merge) { // TODO why does it do 2 merges?
|
||||||
|
_.merge(clean, _.omit(merge, 'progress'));
|
||||||
_.merge(clean.progress, merge.progress);
|
_.merge(clean.progress, merge.progress);
|
||||||
return clean;
|
|
||||||
}
|
|
||||||
GroupSchema.statics.cleanQuestProgress = cleanQuestProgress;
|
|
||||||
|
|
||||||
// Participants: Grant rewards & achievements, finish quest
|
|
||||||
GroupSchema.methods.finishQuest = function(quest, cb) {
|
|
||||||
var group = this;
|
|
||||||
var questK = quest.key;
|
|
||||||
var updates = {$inc:{},$set:{}};
|
|
||||||
|
|
||||||
updates['$inc']['achievements.quests.' + questK] = 1;
|
|
||||||
updates['$inc']['stats.gp'] = +quest.drop.gp;
|
|
||||||
updates['$inc']['stats.exp'] = +quest.drop.exp;
|
|
||||||
updates['$inc']['_v'] = 1;
|
|
||||||
if (group._id == 'habitrpg') {
|
|
||||||
updates['$set']['party.quest.completed'] = questK; // Just show the notif
|
|
||||||
} else {
|
|
||||||
updates['$set']['party.quest'] = cleanQuestProgress({completed: questK}); // clear quest progress
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_.each(quest.drop.items, function(item){
|
return clean;
|
||||||
var dropK = item.key;
|
}
|
||||||
|
|
||||||
|
schema.statics.cleanQuestProgress = _cleanQuestProgress;
|
||||||
|
|
||||||
|
// Participants: Grant rewards & achievements, finish quest
|
||||||
|
// TODO transform in promise
|
||||||
|
schema.methods.finishQuest = function finishQuest (quest, cb) {
|
||||||
|
let questK = quest.key;
|
||||||
|
let updates = {$inc: {}, $set: {}};
|
||||||
|
|
||||||
|
updates.$inc[`achievements.quests.${questK}`] = 1;
|
||||||
|
updates.$inc['stats.gp'] = Number(quest.drop.gp); // TODO are this castings necessary?
|
||||||
|
updates.$inc['stats.exp'] = Number(quest.drop.exp);
|
||||||
|
updates.$inc._v = 1;
|
||||||
|
|
||||||
|
if (this._id === 'habitrpg') {
|
||||||
|
updates.$set['party.quest.completed'] = questK; // Just show the notif
|
||||||
|
} else {
|
||||||
|
updates.$set['party.quest'] = _cleanQuestProgress({completed: questK}); // clear quest progress
|
||||||
|
}
|
||||||
|
|
||||||
|
_.each(quest.drop.items, (item) => {
|
||||||
|
let dropK = item.key;
|
||||||
|
|
||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
case 'gear':
|
case 'gear':
|
||||||
// TODO This means they can lose their new gear on death, is that what we want?
|
// TODO This means they can lose their new gear on death, is that what we want?
|
||||||
updates['$set']['items.gear.owned.'+dropK] = true;
|
updates.$set[`items.gear.owned.${dropK}`] = true;
|
||||||
break;
|
break;
|
||||||
case 'eggs':
|
case 'eggs':
|
||||||
case 'food':
|
case 'food':
|
||||||
case 'hatchingPotions':
|
case 'hatchingPotions':
|
||||||
case 'quests':
|
case 'quests':
|
||||||
updates['$inc']['items.'+item.type+'.'+dropK] = _.where(quest.drop.items,{type:item.type,key:item.key}).length;
|
updates.$inc[`items.${item.type}.${dropK}`] = _.where(quest.drop.items, {type: item.type, key: item.key}).length;
|
||||||
break;
|
break;
|
||||||
case 'pets':
|
case 'pets':
|
||||||
updates['$set']['items.pets.'+dropK] = 5;
|
updates.$set[`items.pets.${dropK}`] = 5;
|
||||||
break;
|
break;
|
||||||
case 'mounts':
|
case 'mounts':
|
||||||
updates['$set']['items.mounts.'+dropK] = true;
|
updates.$set[`items.mounts.${dropK}`] = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
var q = group._id === 'habitrpg' ? {} : {_id:{$in:_.keys(group.quest.members)}};
|
|
||||||
group.quest = {};group.markModified('quest');
|
|
||||||
mongoose.model('User').update(q, updates, {multi:true}, cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isOnQuest(user,progress,group){
|
let q = this._id === 'habitrpg' ? {} : {_id: {$in: _.keys(this.quest.members)}};
|
||||||
|
this.quest = {};
|
||||||
|
this.markModified('quest');
|
||||||
|
User.update(q, updates, {multi: true}, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
function _isOnQuest (user, progress, group) {
|
||||||
return group && progress && group.quest && group.quest.active && group.quest.members[user._id] === true;
|
return group && progress && group.quest && group.quest.active && group.quest.members[user._id] === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupSchema.statics.collectQuest = function(user, progress, cb) {
|
// TODO use promise
|
||||||
this.findOne({type: 'party', members: {'$in': [user._id]}},function(err, group){
|
schema.statics.collectQuest = function collectQuest (user, progress, cb) {
|
||||||
if (!isOnQuest(user,progress,group)) return cb(null);
|
this.findOne({
|
||||||
var quest = shared.content.quests[group.quest.key];
|
type: 'party',
|
||||||
|
members: {$in: [user._id]},
|
||||||
|
}).then(group => {
|
||||||
|
if (!_isOnQuest(user, progress, group)) return cb();
|
||||||
|
let quest = shared.content.quests[group.quest.key];
|
||||||
|
|
||||||
_.each(progress.collect,function(v,k){
|
_.each(progress.collect, (v, k) => {
|
||||||
group.quest.progress.collect[k] += v;
|
group.quest.progress.collect[k] += v;
|
||||||
});
|
});
|
||||||
|
|
||||||
var foundText = _.reduce(progress.collect, function(m,v,k){
|
let foundText = _.reduce(progress.collect, (m, v, k) => {
|
||||||
m.push(v + ' ' + quest.collect[k].text('en'));
|
m.push(`${v} ${quest.collect[k].text('en')}`);
|
||||||
return m;
|
return m;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
foundText = foundText ? foundText.join(', ') : 'nothing';
|
foundText = foundText ? foundText.join(', ') : 'nothing';
|
||||||
group.sendChat("`" + user.profile.name + " found "+foundText+".`");
|
group.sendChat(`\`${user.profile.name} found ${foundText}.\``);
|
||||||
group.markModified('quest.progress.collect');
|
group.markModified('quest.progress.collect');
|
||||||
|
|
||||||
// Still needs completing
|
// Still needs completing
|
||||||
if (_.find(shared.content.quests[group.quest.key].collect, function(v,k){
|
if (_.find(shared.content.quests[group.quest.key].collect, (v, k) => {
|
||||||
return group.quest.progress.collect[k] < v.count;
|
return group.quest.progress.collect[k] < v.count;
|
||||||
})) return group.save(cb);
|
})) return group.save(cb);
|
||||||
|
|
||||||
async.series([
|
// TODO use promise
|
||||||
function(cb2){
|
group.finishQuest(quest, () => {
|
||||||
group.finishQuest(quest,cb2);
|
|
||||||
},
|
|
||||||
function(cb2){
|
|
||||||
group.sendChat('`All items found! Party has received their rewards.`');
|
group.sendChat('`All items found! Party has received their rewards.`');
|
||||||
group.save(cb2);
|
group.save(cb);
|
||||||
}
|
});
|
||||||
],cb);
|
|
||||||
})
|
})
|
||||||
}
|
.catch(cb);
|
||||||
|
};
|
||||||
|
|
||||||
// to set a boss: `db.groups.update({_id:'habitrpg'},{$set:{quest:{key:'dilatory',active:true,progress:{hp:1000,rage:1500}}}})`
|
// to set a boss: `db.groups.update({_id:'habitrpg'},{$set:{quest:{key:'dilatory',active:true,progress:{hp:1000,rage:1500}}}})`
|
||||||
module.exports.tavernQuest = {};
|
// we export an empty object that is then populated with the query-returned data
|
||||||
var tavernQ = {_id:'habitrpg','quest.key':{$ne:null}};
|
export let tavernQuest = {};
|
||||||
|
|
||||||
process.nextTick(function(){
|
process.nextTick(function(){
|
||||||
mongoose.model('Group').findOne(tavernQ, function(err,tavern){
|
mongoose.model('Group').findOne({_id: 'habitrpg', 'quest.key': {$ne: null}}, (err, tavern) => {
|
||||||
|
// TODO handle error?
|
||||||
if (!tavern) return; // No tavern quest
|
if (!tavern) return; // No tavern quest
|
||||||
|
|
||||||
var quest = tavern.quest.toObject();
|
|
||||||
// Using _assign so we don't lose the reference to the exported tavernQuest
|
// Using _assign so we don't lose the reference to the exported tavernQuest
|
||||||
_.assign(module.exports.tavernQuest, quest);
|
_.assign(tavernQuest, tavern.quest.toObject());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
GroupSchema.statics.tavernBoss = function(user,progress) {
|
schema.statics.tavernBoss = function tavernBoss (user, progress) {
|
||||||
if (!progress) return;
|
if (!progress) return;
|
||||||
|
|
||||||
// hack: prevent crazy damage to world boss
|
// hack: prevent crazy damage to world boss
|
||||||
var dmg = Math.min(900, Math.abs(progress.up||0)),
|
let dmg = Math.min(900, Math.abs(progress.up || 0));
|
||||||
rage = -Math.min(900, Math.abs(progress.down||0));
|
let rage = -Math.min(900, Math.abs(progress.down || 0));
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function(cb){
|
function(cb){
|
||||||
@@ -339,12 +371,12 @@ GroupSchema.statics.tavernBoss = function(user,progress) {
|
|||||||
}
|
}
|
||||||
],function(err,res){
|
],function(err,res){
|
||||||
if (err === true) return; // no current quest
|
if (err === true) return; // no current quest
|
||||||
if (err) return logging.error(err);
|
if (err) return logger.error(err);
|
||||||
dmg = rage = null;
|
dmg = rage = null;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupSchema.statics.bossQuest = function(user, progress, cb) {
|
schema.statics.bossQuest = function bossQuest (user, progress, cb) {
|
||||||
this.findOne({type: 'party', members: {'$in': [user._id]}},function(err, group){
|
this.findOne({type: 'party', members: {'$in': [user._id]}},function(err, group){
|
||||||
if (!isOnQuest(user,progress,group)) return cb(null);
|
if (!isOnQuest(user,progress,group)) return cb(null);
|
||||||
var quest = shared.content.quests[group.quest.key];
|
var quest = shared.content.quests[group.quest.key];
|
||||||
@@ -386,7 +418,7 @@ GroupSchema.statics.bossQuest = function(user, progress, cb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove user from this group
|
// Remove user from this group
|
||||||
GroupSchema.methods.leave = function(user, keep, mainCb){
|
schema.methods.leave = function leaveGroup (user, keep, mainCb){
|
||||||
if(!user) return mainCb(new Error('Missing user.'));
|
if(!user) return mainCb(new Error('Missing user.'));
|
||||||
|
|
||||||
if(keep && typeof keep === 'function'){
|
if(keep && typeof keep === 'function'){
|
||||||
@@ -472,22 +504,14 @@ GroupSchema.methods.leave = function(user, keep, mainCb){
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export let model = mongoose.model('Group', schema);
|
||||||
GroupSchema.methods.toJSON = function() {
|
|
||||||
var doc = this.toObject();
|
|
||||||
|
|
||||||
return doc;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
module.exports.schema = GroupSchema;
|
|
||||||
var Group = module.exports.model = mongoose.model("Group", GroupSchema);
|
|
||||||
|
|
||||||
// initialize tavern if !exists (fresh installs)
|
// initialize tavern if !exists (fresh installs)
|
||||||
Group.count({_id: 'habitrpg'}, function(err, ct){
|
// TODO use promise
|
||||||
|
model.count({_id: 'habitrpg'}, (err, ct) => {
|
||||||
if (ct > 0) return;
|
if (ct > 0) return;
|
||||||
|
|
||||||
new Group({
|
new model({
|
||||||
_id: 'habitrpg',
|
_id: 'habitrpg',
|
||||||
chat: [],
|
chat: [],
|
||||||
leader: '9',
|
leader: '9',
|
||||||
|
|||||||
Reference in New Issue
Block a user