mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-19 15:48:04 +01:00
quests: add collection quests, incl. polar bear pt 2, and quest arks, and achievements
This commit is contained in:
@@ -84,6 +84,8 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
|
|||||||
}
|
}
|
||||||
|
|
||||||
$scope.purchase = function(type, item){
|
$scope.purchase = function(type, item){
|
||||||
|
if (item.previous && !User.user.achievements.quests[item.previous])
|
||||||
|
return alert("You must first complete " + $rootScope.Content.quests[item.previous].text + '.');
|
||||||
var gems = User.user.balance * 4;
|
var gems = User.user.balance * 4;
|
||||||
if(gems < item.value) return $rootScope.modals.buyGems = true;
|
if(gems < item.value) return $rootScope.modals.buyGems = true;
|
||||||
var string = (type == 'hatchingPotion') ? 'hatching potion' : type; // give hatchingPotion a space
|
var string = (type == 'hatchingPotion') ? 'hatching potion' : type; // give hatchingPotion a space
|
||||||
|
|||||||
@@ -28,12 +28,6 @@ api.auth = function(req, res, next) {
|
|||||||
if (err) return res.json(500, {err: err});
|
if (err) return res.json(500, {err: err});
|
||||||
if (_.isEmpty(user)) return res.json(401, NO_USER_FOUND);
|
if (_.isEmpty(user)) return res.json(401, NO_USER_FOUND);
|
||||||
|
|
||||||
// Remove this after a few days. Users aren't refreshing after the pets roll out, which is required
|
|
||||||
if (_.find(req.body, function(v){return v && v.data && _.isArray(v.data['items.pets'])})) {
|
|
||||||
// simply discard the update. Unfortunately, sending an error will keep their set ops in the sync queue.
|
|
||||||
return res.json(200, {_v: user._v-1});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.locals.wasModified = req.query._v ? +user._v !== +req.query._v : true;
|
res.locals.wasModified = req.query._v ? +user._v !== +req.query._v : true;
|
||||||
res.locals.user = user;
|
res.locals.user = user;
|
||||||
req.session.userId = user._id;
|
req.session.userId = user._id;
|
||||||
@@ -42,16 +36,14 @@ api.auth = function(req, res, next) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
api.authWithSession = function(req, res, next) { //[todo] there is probably a more elegant way of doing this...
|
api.authWithSession = function(req, res, next) { //[todo] there is probably a more elegant way of doing this...
|
||||||
var uid;
|
var uid = req.session.userId;
|
||||||
uid = req.session.userId;
|
if (!(req.session && req.session.userId))
|
||||||
if (!(req.session && req.session.userId)) {
|
|
||||||
return res.json(401, NO_SESSION_FOUND);
|
return res.json(401, NO_SESSION_FOUND);
|
||||||
}
|
User.findOne({_id: uid}, function(err, user) {
|
||||||
return User.findOne({_id: uid,}, function(err, user) {
|
|
||||||
if (err) return res.json(500, {err: err});
|
if (err) return res.json(500, {err: err});
|
||||||
if (_.isEmpty(user)) return res.json(401, NO_USER_FOUND);
|
if (_.isEmpty(user)) return res.json(401, NO_USER_FOUND);
|
||||||
res.locals.user = user;
|
res.locals.user = user;
|
||||||
return next();
|
next();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -409,8 +409,12 @@ questStart = function(req, res) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var parallel = [], questMembers = {};
|
var parallel = [],
|
||||||
var key = group.quest.key;
|
questMembers = {},
|
||||||
|
key = group.quest.key,
|
||||||
|
quest = shared.content.quests[key],
|
||||||
|
collectTally = quest.collect ? _.transform(quest.collect, function(m,v,k){m[k]=0}) : {};
|
||||||
|
|
||||||
// TODO will this handle appropriately when people leave/join party between quest invite?
|
// TODO will this handle appropriately when people leave/join party between quest invite?
|
||||||
_.each(group.members, function(m){
|
_.each(group.members, function(m){
|
||||||
var updates = {$set:{},$inc:{'_v':1}};
|
var updates = {$set:{},$inc:{'_v':1}};
|
||||||
@@ -418,10 +422,11 @@ questStart = function(req, res) {
|
|||||||
updates['$inc']['items.quests.'+key] = -1;
|
updates['$inc']['items.quests.'+key] = -1;
|
||||||
if (group.quest.members[m] == true) {
|
if (group.quest.members[m] == true) {
|
||||||
updates['$set']['party.quest.key'] = key;
|
updates['$set']['party.quest.key'] = key;
|
||||||
|
updates['$set']['party.quest.tally'] = {up:0,down:0,collect:collectTally};
|
||||||
questMembers[m] = true;
|
questMembers[m] = true;
|
||||||
} else {
|
} else {
|
||||||
updates['$unset'] = {'party.quest.key':undefined};
|
updates['$unset'] = {'party.quest.key':1};
|
||||||
updates['$set']['party.quest.collection'] = {};
|
updates['$set']['party.quest.tally'] = {};
|
||||||
}
|
}
|
||||||
parallel.push(function(cb2){
|
parallel.push(function(cb2){
|
||||||
User.update({_id:m},updates,cb2);
|
User.update({_id:m},updates,cb2);
|
||||||
@@ -429,8 +434,12 @@ questStart = function(req, res) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
group.quest.active = true;
|
group.quest.active = true;
|
||||||
group.quest.progress.hp = shared.content.quests[group.quest.key].stats.hp;
|
if (quest.boss)
|
||||||
|
group.quest.progress.hp = quest.boss.hp;
|
||||||
|
else
|
||||||
|
group.quest.progress.collect = collectTally;
|
||||||
group.quest.members = questMembers;
|
group.quest.members = questMembers;
|
||||||
|
group.markModified('quest'); // members & progress.collect are both Mixed types
|
||||||
parallel.push(function(cb2){group.save(cb2)});
|
parallel.push(function(cb2){group.save(cb2)});
|
||||||
|
|
||||||
async.parallel(parallel,function(err, results){
|
async.parallel(parallel,function(err, results){
|
||||||
@@ -491,12 +500,14 @@ api.questAbort = function(req, res, next){
|
|||||||
async.parallel([
|
async.parallel([
|
||||||
function(cb){
|
function(cb){
|
||||||
User.update({_id:{$in: _.keys(group.quest.members)}},{
|
User.update({_id:{$in: _.keys(group.quest.members)}},{
|
||||||
$set:{'party.quest.key':undefined,'party.quest.tally.collection':{}},
|
$unset: {'party.quest.key':1},
|
||||||
$inc:{_v:1}
|
$set: {'party.quest.tally.collect':{}},
|
||||||
|
$inc: {_v:1}
|
||||||
},cb);
|
},cb);
|
||||||
},
|
},
|
||||||
function(cb) {
|
function(cb) {
|
||||||
group.quest = {};
|
group.quest = {};
|
||||||
|
group.markModified('quest');
|
||||||
group.save(cb);
|
group.save(cb);
|
||||||
}
|
}
|
||||||
], function(err){
|
], function(err){
|
||||||
|
|||||||
@@ -193,32 +193,38 @@ api.update = function(req, res, next) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
api.cron = function(req, res, next) {
|
api.cron = function(req, res, next) {
|
||||||
var user = res.locals.user;
|
var user = res.locals.user,
|
||||||
tally = user.fns.cron();
|
tally = user.fns.cron(),
|
||||||
if (user.isModified()) res.locals.wasModified = true;
|
ranCron = user.isModified(),
|
||||||
|
quest = shared.content.quests[user.party.quest.key];
|
||||||
|
|
||||||
// If user is on a quest, roll for boss & player
|
if (ranCron) res.locals.wasModified = true;
|
||||||
if (user.party.quest.key && tally && (tally.up || tally.down)) {
|
if (!ranCron) return next(null,user);
|
||||||
async.waterfall([
|
if (!quest) return user.save(next);
|
||||||
function(cb){user.save(cb)}, // make sure to save the cron effects
|
|
||||||
function(saved, count, cb) {
|
// If user is on a quest, roll for boss & player, or handle collections
|
||||||
Group.findOne({type: 'party', members: {'$in': [user._id]}}, cb);
|
// FIXME this saves user, runs db updates, loads user. Is there a better way to handle this?
|
||||||
},
|
async.waterfall([
|
||||||
function(group, cb){
|
function(cb){
|
||||||
group.bossAttack(user,tally,cb);
|
user.save(cb); // make sure to save the cron effects
|
||||||
},
|
},
|
||||||
function(updated,cb){
|
function(saved, count, cb) {
|
||||||
// User has been updated in boss-grapple, reload
|
Group.findOne({type: 'party', members: {'$in': [user._id]}}, cb);
|
||||||
User.findById(user._id,cb);
|
},
|
||||||
}
|
function(group, cb){
|
||||||
], function(err, saved) {
|
var type = quest.boss ? 'boss' : 'collect';
|
||||||
user = res.locals.user = saved;
|
group[type+'Quest'](user,tally,cb);
|
||||||
next(err,saved);
|
},
|
||||||
});
|
function(){
|
||||||
|
var cb = arguments[arguments.length-1];
|
||||||
|
// User has been updated in boss-grapple, reload
|
||||||
|
User.findById(user._id, cb);
|
||||||
|
}
|
||||||
|
], function(err, saved) {
|
||||||
|
user = res.locals.user = saved;
|
||||||
|
next(err,saved);
|
||||||
|
});
|
||||||
|
|
||||||
} else {
|
|
||||||
user.save(next);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// api.reroll // Shared.ops
|
// api.reroll // Shared.ops
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ var GroupSchema = new Schema({
|
|||||||
active: {type:Boolean, 'default':false},
|
active: {type:Boolean, 'default':false},
|
||||||
progress:{
|
progress:{
|
||||||
hp: Number,
|
hp: Number,
|
||||||
collected: Schema.Types.Mixed,
|
collect: {type:Schema.Types.Mixed, 'default':{}} // {feather: 5, ingot: 3}
|
||||||
},
|
},
|
||||||
|
|
||||||
//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
|
||||||
@@ -107,15 +107,84 @@ GroupSchema.methods.sendChat = function(message, user){
|
|||||||
group.chat.splice(200);
|
group.chat.splice(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupSchema.methods.bossAttack = function(user, tally, cb) {
|
// Participants: Grant rewards & achievements, finish quest
|
||||||
|
GroupSchema.methods.finishQuest = function(quest, cb) {
|
||||||
var group = this;
|
var group = this;
|
||||||
var questK = group.quest.key;
|
|
||||||
var quest = shared.content.quests[questK];
|
var questK = quest.key;
|
||||||
var dropK = quest.drop.key;
|
var dropK = quest.drop.key;
|
||||||
var down = tally.down * quest.stats.str; // multiply by boss strength
|
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;
|
||||||
|
updates['$unset'] = {'party.quest.key':undefined};
|
||||||
|
updates['$set']['party.quest.collect'] = {};
|
||||||
|
|
||||||
|
switch (quest.drop.type) {
|
||||||
|
case 'gear':
|
||||||
|
// TODO This means they can lose their new gear on death, is that what we want?
|
||||||
|
updates['$set']['items.gear.owned.'+dropK] = true;
|
||||||
|
break;
|
||||||
|
case 'eggs':
|
||||||
|
case 'food':
|
||||||
|
case 'hatchingPotions':
|
||||||
|
updates['$inc']['items.'+quest.drop.type+'.'+dropK] = 1;
|
||||||
|
break;
|
||||||
|
case 'pets':
|
||||||
|
updates['$set']['items.pets.'+dropK] = 5;
|
||||||
|
break;
|
||||||
|
case 'mounts':
|
||||||
|
updates['$set']['items.mounts.'+dropK] = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// FIXME this is TERRIBLE practice. Looks like there are circular dependencies in the models, such that `var User` at
|
||||||
|
// this point is undefined. So we get around that by loading from mongoose only once we get to this point
|
||||||
|
var members = _.keys(group.quest.members);
|
||||||
|
group.quest = {};group.markModified('quest');
|
||||||
|
mongoose.models.User.update({_id:{$in:members}}, updates, {multi:true}, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupSchema.methods.collectQuest = function(user, tally, cb) {
|
||||||
|
var group = this,
|
||||||
|
quest = shared.content.quests[group.quest.key];
|
||||||
|
|
||||||
|
_.each(tally.collect,function(v,k){
|
||||||
|
group.quest.progress.collect[k] += v;
|
||||||
|
});
|
||||||
|
|
||||||
|
var foundText = _.reduce(tally.collect, function(m,v,k){
|
||||||
|
m.push(v + ' ' + quest.collect[k].text);
|
||||||
|
return m;
|
||||||
|
}, []);
|
||||||
|
foundText = foundText ? foundText.join(', ') : 'nothing';
|
||||||
|
group.sendChat("`<" + user.profile.name + "> found "+foundText+".`");
|
||||||
|
group.markModified('quest.progress.collect');
|
||||||
|
|
||||||
|
// Still needs completing
|
||||||
|
if (_.find(shared.content.quests[group.quest.key].collect, function(v,k){
|
||||||
|
return group.quest.progress.collect[k] < v.count;
|
||||||
|
})) return group.save(cb);
|
||||||
|
|
||||||
|
async.series([
|
||||||
|
function(cb2){
|
||||||
|
group.finishQuest(quest,cb2);
|
||||||
|
},
|
||||||
|
function(cb2){
|
||||||
|
group.sendChat('`All items found! Party has received their rewards.`');
|
||||||
|
group.save(cb2);
|
||||||
|
}
|
||||||
|
],cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupSchema.methods.bossQuest = function(user, tally, cb) {
|
||||||
|
var group = this;
|
||||||
|
var quest = shared.content.quests[group.quest.key];
|
||||||
|
var down = tally.down * quest.boss.str; // multiply by boss strength
|
||||||
|
|
||||||
group.quest.progress.hp -= tally.up;
|
group.quest.progress.hp -= tally.up;
|
||||||
group.sendChat("`<" + user.profile.name + "> attacks <" + quest.name + "> for " + (tally.up.toFixed(1)) + " damage, <" + quest.name + "> attacks party for " + (down.toFixed(1)) + " damage.`");
|
group.sendChat("`<" + user.profile.name + "> attacks <" + quest.boss.name + "> for " + (tally.up.toFixed(1)) + " damage, <" + quest.boss.name + "> attacks party for " + (down.toFixed(1)) + " damage.`");
|
||||||
//var hp = group.quest.progress.hp;
|
//var hp = group.quest.progress.hp;
|
||||||
|
|
||||||
// Everyone takes damage
|
// Everyone takes damage
|
||||||
@@ -127,47 +196,11 @@ GroupSchema.methods.bossAttack = function(user, tally, cb) {
|
|||||||
|
|
||||||
// Boss slain, finish quest
|
// Boss slain, finish quest
|
||||||
if (group.quest.progress.hp <= 0) {
|
if (group.quest.progress.hp <= 0) {
|
||||||
|
group.sendChat('`' + quest.boss.name + ' has been slain! Party has received their rewards.`');
|
||||||
|
// Participants: Grant rewards & achievements, finish quest
|
||||||
series.push(function(cb2){
|
series.push(function(cb2){
|
||||||
async.parallel([
|
group.finishQuest(quest,cb2);
|
||||||
// Participants: Grant rewards & achievements, finish quest
|
});
|
||||||
function(cb3){
|
|
||||||
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;
|
|
||||||
updates['$unset'] = {'party.quest.key':undefined};
|
|
||||||
updates['$set']['party.quest.collection'] = {};
|
|
||||||
|
|
||||||
switch (quest.drop.type) {
|
|
||||||
case 'gear':
|
|
||||||
// TODO This means they can lose their new gear on death, is that what we want?
|
|
||||||
updates['$set']['items.gear.owned.'+dropK] = true;
|
|
||||||
break;
|
|
||||||
case 'eggs':
|
|
||||||
case 'food':
|
|
||||||
case 'hatchingPotions':
|
|
||||||
updates['$inc']['items.'+quest.drop.type+'.'+dropK] = 1;
|
|
||||||
break;
|
|
||||||
case 'pets':
|
|
||||||
updates['$set']['items.pets.'+dropK] = 5;
|
|
||||||
break;
|
|
||||||
case 'mounts':
|
|
||||||
updates['$set']['items.mounts.'+dropK] = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// FIXME this is TERRIBLE practice. Looks like there are circular dependencies in the models, such that `var User` at
|
|
||||||
// this point is undefined. So we get around that by loading from mongoose only once we get to this point
|
|
||||||
mongoose.models.User.update({_id:{$in: _.keys(group.quest.members)}},updates,{multi:true},cb3);
|
|
||||||
},
|
|
||||||
// Group: finish quest
|
|
||||||
function(cb3){
|
|
||||||
group.quest = {};group.markModified('quest');
|
|
||||||
group.sendChat('`' + quest.name + ' has been slain! Party has received their rewards`');
|
|
||||||
group.save(cb3);
|
|
||||||
}
|
|
||||||
],cb2);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
series.push(function(cb2){group.save(cb2)});
|
series.push(function(cb2){group.save(cb2)});
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ var UserSchema = new Schema({
|
|||||||
tally: {
|
tally: {
|
||||||
up: {type: Number, 'default': 0},
|
up: {type: Number, 'default': 0},
|
||||||
down: {type: Number, 'default': 0},
|
down: {type: Number, 'default': 0},
|
||||||
collection: {type: Schema.Types.Mixed, 'default': {}} // {feather:1, ingot:2}
|
collect: {type: Schema.Types.Mixed, 'default': {}} // {feather:1, ingot:2}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html')
|
|||||||
li.customize-menu
|
li.customize-menu
|
||||||
menu.pets-menu(label='Quests')
|
menu.pets-menu(label='Quests')
|
||||||
div(ng-repeat='quest in Content.quests')
|
div(ng-repeat='quest in Content.quests')
|
||||||
button.customize-option(popover='{{quest.notes}}', popover-title='{{quest.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("quests", quest)', class='inventory_quest_scroll')
|
button.customize-option(popover='{{quest.notes}}', popover-title='{{quest.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("quests", quest)', class='inventory_quest_scroll', ng-class='{locked: quest.previous && !user.achievements.quests[quest.previous]}')
|
||||||
p
|
p
|
||||||
| {{quest.value}}
|
| {{quest.value}}
|
||||||
span.Pet_Currency_Gem1x.inline-gems
|
span.Pet_Currency_Gem1x.inline-gems
|
||||||
|
|||||||
@@ -8,35 +8,45 @@ a.pull-right.gem-wallet(popover-trigger='mouseenter', popover-title='Guild Bank'
|
|||||||
|
|
||||||
|
|
||||||
// ------ Bosses -------
|
// ------ Bosses -------
|
||||||
.modal.inline-modal(ng-if='group.type==="party" && group.quest.key && group.quest.active==false')
|
.modal.inline-modal(bindonce='group', ng-if='group.type==="party" && group.quest.key')
|
||||||
.modal-header(bindonce='group')
|
.modal-header
|
||||||
h3 Quest: {{Content.quests[group.quest.key].text}}
|
h3(ng-if='group.quest.active==false') Quest Invite: {{Content.quests[group.quest.key].text}}
|
||||||
|
h3(ng-if='group.quest.active==true') {{Content.quests[group.quest.key].text}}
|
||||||
.modal-body
|
.modal-body
|
||||||
table.table.table-striped
|
div(ng-if='group.quest.active==false')
|
||||||
tr(ng-repeat='member in group.members')
|
table.table.table-striped
|
||||||
td {{member.profile.name}}
|
tr(ng-repeat='member in group.members')
|
||||||
td {{group.quest.members[member._id] == undefined ? 'Pending' : k ? 'Rejected' : 'Accepted'}}
|
td {{member.profile.name}}
|
||||||
button.btn.btn-warning(ng-click='party.$questAccept({"force":true})') Force Start
|
td {{group.quest.members[member._id] == undefined ? 'Pending' : k ? 'Rejected' : 'Accepted'}}
|
||||||
|
button.btn.btn-warning(ng-click='party.$questAccept({"force":true})') Force Start
|
||||||
|
|
||||||
.modal.inline-modal(ng-if='group.type=="party" && group.quest.key && group.quest.active==true')
|
div(ng-if='group.quest.active==true')
|
||||||
.modal-header(bindonce='group')
|
div(ng-if='Content.quests[group.quest.key].boss')
|
||||||
h3 {{Content.quests[group.quest.key].text}}
|
div(class="quest_{{group.quest.key}}")
|
||||||
.modal-body
|
//-
|
||||||
div(class="quest_{{group.quest.key}}")
|
.progress(style="height:10px")
|
||||||
//-
|
.bar(style='width: {{Shared.percent(group.quest.hp, Content.quests[group.quest.key].hp)}}%;')
|
||||||
.progress(style="height:10px")
|
span.meter-text
|
||||||
.bar(style='width: {{Shared.percent(group.quest.hp, Content.quests[group.quest.key].hp)}}%;')
|
i.icon-heart
|
||||||
span.meter-text
|
| {{group.quest.hp | number:0}} / {{Content.quests[group.quest.key].hp}}
|
||||||
i.icon-heart
|
.hero-stats
|
||||||
| {{group.quest.hp | number:0}} / {{Content.quests[group.quest.key].hp}}
|
.meter.health(title='Boss Health')
|
||||||
.hero-stats
|
.bar(style='width: {{Shared.percent(group.quest.progress.hp, Content.quests[group.quest.key].boss.hp)}}%;')
|
||||||
.meter.health(title='Boss Health')
|
span.meter-text
|
||||||
.bar(style='width: {{Shared.percent(group.quest.progress.hp, Content.quests[group.quest.key].stats.hp)}}%;')
|
i.icon-heart
|
||||||
span.meter-text
|
| {{group.quest.progress.hp | number:0}} / {{Content.quests[group.quest.key].boss.hp}}
|
||||||
i.icon-heart
|
|
||||||
| {{group.quest.progress.hp | number:0}} / {{Content.quests[group.quest.key].stats.hp}}
|
div(ng-if='Content.quests[group.quest.key].collect')
|
||||||
p {{Content.quests[group.quest.key].notes}}
|
h4 Collected:
|
||||||
button.btn.btn-mini.btn-danger(ng-click='questAbort()') Abort
|
table.table.table-striped
|
||||||
|
tr(ng-repeat='(k,v) in group.quest.progress.collect')
|
||||||
|
td
|
||||||
|
div(class='{{group.quest.key}}_k') {{Content.quests[group.quest.key].collect[k].text}}
|
||||||
|
td
|
||||||
|
{{v}} / {{Content.quests[group.quest.key].collect[k].count}}
|
||||||
|
|
||||||
|
p {{Content.quests[group.quest.key].notes}}
|
||||||
|
button.btn.btn-mini.btn-danger(ng-click='questAbort()') Abort
|
||||||
|
|
||||||
// ------ Information -------
|
// ------ Information -------
|
||||||
.modal.inline-modal
|
.modal.inline-modal
|
||||||
|
|||||||
@@ -81,7 +81,18 @@
|
|||||||
.achievement.achievement-karaoke(ng-show='profile.achievements.challenges')
|
.achievement.achievement-karaoke(ng-show='profile.achievements.challenges')
|
||||||
div(ng-class='{muted: !profile.achievements.challenges}')
|
div(ng-class='{muted: !profile.achievements.challenges}')
|
||||||
h5 Was the winner in the following challenges
|
h5 Was the winner in the following challenges
|
||||||
ul
|
table.table.table-striped
|
||||||
li(ng-repeat='chal in profile.achievements.challenges') {{chal}}
|
tr(ng-repeat='chal in profile.achievements.challenges')
|
||||||
|
td {{chal}}
|
||||||
|
hr
|
||||||
|
|
||||||
|
div(ng-if='profile.achievements.quests || user._id == profile._id')
|
||||||
|
.achievement.achievement-alien(ng-show='profile.achievements.quests')
|
||||||
|
div(ng-class='{muted: !profile.achievements.quests}')
|
||||||
|
h5 Completed the following quests
|
||||||
|
table.table.table-striped
|
||||||
|
tr(ng-repeat='(k,v) in profile.achievements.quests')
|
||||||
|
td {{Content.quests[k].text}}
|
||||||
|
td x{{v}}
|
||||||
hr
|
hr
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user