Quests: hurt boss via score method, refactor chat function, change all content.*.name to content.*.key for consistency

This commit is contained in:
Tyler Renelle
2013-12-20 15:43:10 -07:00
parent ecb354e2cc
commit 8095397317
16 changed files with 230 additions and 103 deletions

View File

@@ -29,7 +29,7 @@
"bootstrap": "v2.3.2", "bootstrap": "v2.3.2",
"bootstrap-datepicker": "~1.2.0", "bootstrap-datepicker": "~1.2.0",
"bootstrap-growl": "~1.1.0", "bootstrap-growl": "~1.1.0",
"habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared.git#develop", "habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared.git#bosses",
"BrowserQuest": "https://github.com/mozilla/BrowserQuest.git", "BrowserQuest": "https://github.com/mozilla/BrowserQuest.git",
"github-buttons": "git://github.com/mdo/github-buttons.git", "github-buttons": "git://github.com/mdo/github-buttons.git",
"marked": "~0.2.9", "marked": "~0.2.9",

View File

@@ -4,7 +4,7 @@
"version": "0.0.0-152", "version": "0.0.0-152",
"main": "./src/server.js", "main": "./src/server.js",
"dependencies": { "dependencies": {
"habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared#develop", "habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared#bosses",
"derby-auth": "git://github.com/lefnire/derby-auth#master", "derby-auth": "git://github.com/lefnire/derby-auth#master",
"connect-mongo": "*", "connect-mongo": "*",
"passport-facebook": "~1.0.0", "passport-facebook": "~1.0.0",

View File

@@ -32,10 +32,10 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
}, true); }, true);
$scope.chooseEgg = function(egg){ $scope.chooseEgg = function(egg){
if ($scope.selectedEgg && $scope.selectedEgg.name == egg) { if ($scope.selectedEgg && $scope.selectedEgg.key == egg) {
return $scope.selectedEgg = null; // clicked same egg, unselect return $scope.selectedEgg = null; // clicked same egg, unselect
} }
var eggData = _.findWhere(Content.eggs, {name:egg}); var eggData = _.findWhere(Content.eggs, {key:egg});
if (!$scope.selectedPotion) { if (!$scope.selectedPotion) {
$scope.selectedEgg = eggData; $scope.selectedEgg = eggData;
} else { } else {
@@ -44,11 +44,11 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
} }
$scope.choosePotion = function(potion){ $scope.choosePotion = function(potion){
if ($scope.selectedPotion && $scope.selectedPotion.name == potion) { if ($scope.selectedPotion && $scope.selectedPotion.key == potion) {
return $scope.selectedPotion = null; // clicked same egg, unselect return $scope.selectedPotion = null; // clicked same egg, unselect
} }
// we really didn't think through the way these things are stored and getting passed around... // we really didn't think through the way these things are stored and getting passed around...
var potionData = _.findWhere(Content.hatchingPotions, {name:potion}); var potionData = _.findWhere(Content.hatchingPotions, {key:potion});
if (!$scope.selectedEgg) { if (!$scope.selectedEgg) {
$scope.selectedPotion = potionData; $scope.selectedPotion = potionData;
} else { } else {
@@ -57,7 +57,7 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
} }
$scope.chooseFood = function(food){ $scope.chooseFood = function(food){
if ($scope.selectedFood && $scope.selectedFood.name == food) return $scope.selectedFood = null; if ($scope.selectedFood && $scope.selectedFood.key == food) return $scope.selectedFood = null;
$scope.selectedFood = Content.food[food]; $scope.selectedFood = Content.food[food];
} }
@@ -65,8 +65,8 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
var selected = $scope.selectedEgg ? 'selectedEgg' : $scope.selectedPotion ? 'selectedPotion' : $scope.selectedFood ? 'selectedFood' : undefined; var selected = $scope.selectedEgg ? 'selectedEgg' : $scope.selectedPotion ? 'selectedPotion' : $scope.selectedFood ? 'selectedFood' : undefined;
if (selected) { if (selected) {
var type = $scope.selectedEgg ? 'eggs' : $scope.selectedPotion ? 'hatchingPotions' : $scope.selectedFood ? 'food' : undefined; var type = $scope.selectedEgg ? 'eggs' : $scope.selectedPotion ? 'hatchingPotions' : $scope.selectedFood ? 'food' : undefined;
user.ops.sell({params:{type:type, key: $scope[selected].name}}); user.ops.sell({params:{type:type, key: $scope[selected].key}});
if (user.items[type][$scope[selected].name] < 1) { if (user.items[type][$scope[selected].key] < 1) {
$scope[selected] = null; $scope[selected] = null;
} }
} }
@@ -77,8 +77,8 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
} }
$scope.hatch = function(egg, potion){ $scope.hatch = function(egg, potion){
if (!confirm('Hatch a ' + potion.name + ' ' + egg.name + '?')) return; if (!confirm('Hatch a ' + potion.key + ' ' + egg.key + '?')) return;
user.ops.hatch({params:{egg:egg.name, hatchingPotion:potion.name}}); user.ops.hatch({params:{egg:egg.key, hatchingPotion:potion.key}});
$scope.selectedEgg = null; $scope.selectedEgg = null;
$scope.selectedPotion = null; $scope.selectedPotion = null;
} }
@@ -89,7 +89,7 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
var string = (type == 'hatchingPotion') ? 'hatching potion' : type; // give hatchingPotion a space var string = (type == 'hatchingPotion') ? 'hatching potion' : type; // give hatchingPotion a space
var message = "Buy this " + string + " with " + item.value + " of your " + gems + " Gems?" var message = "Buy this " + string + " with " + item.value + " of your " + gems + " Gems?"
if(confirm(message)) if(confirm(message))
User.user.ops.purchase({params:{type:type,key:item.name}}); User.user.ops.purchase({params:{type:type,key:item.key}});
} }
$scope.choosePet = function(egg, potion){ $scope.choosePet = function(egg, potion){
@@ -98,12 +98,12 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
// Feeding Pet // Feeding Pet
if ($scope.selectedFood) { if ($scope.selectedFood) {
var food = $scope.selectedFood var food = $scope.selectedFood
if (food.name == 'Saddle') { if (food.key == 'Saddle') {
if (!confirm('Saddle ' + pet + '?')) return; if (!confirm('Saddle ' + pet + '?')) return;
} else if (!confirm('Feed ' + pet + ' a ' + food.name + '?')) { } else if (!confirm('Feed ' + pet + ' a ' + food.key + '?')) {
return; return;
} }
User.user.ops.feed({params:{pet: pet, food: food.name}}); User.user.ops.feed({params:{pet: pet, food: food.key}});
$scope.selectedFood = null; $scope.selectedFood = null;
// Selecting Pet // Selecting Pet
@@ -125,7 +125,9 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
$rootScope.modals.showQuest = false; $rootScope.modals.showQuest = false;
} }
$scope.questInit = function(){ $scope.questInit = function(){
$rootScope.party.$questAccept({key:$scope.selectedQuest.name}); $rootScope.party.$questAccept({key:$scope.selectedQuest.key}, function(){
$rootScope.party.$get();
});
$scope.closeQuest(); $scope.closeQuest();
} }
} }

View File

@@ -37,10 +37,10 @@ habitrpg.controller('NotificationCtrl',
var type = (after.type == 'Food') ? 'food' : var type = (after.type == 'Food') ? 'food' :
(after.type == 'HatchingPotion') ? 'hatchingPotions' : // can we use camelcase and remove this line? (after.type == 'HatchingPotion') ? 'hatchingPotions' : // can we use camelcase and remove this line?
(after.type.toLowerCase() + 's'); (after.type.toLowerCase() + 's');
if(!User.user.items[type][after.name]){ if(!User.user.items[type][after.key]){
User.user.items[type][after.name] = 0; User.user.items[type][after.key] = 0;
} }
User.user.items[type][after.name]++; User.user.items[type][after.key]++;
$rootScope.modals.drop = true; $rootScope.modals.drop = true;
}); });

View File

@@ -167,7 +167,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$
if ($scope.spell.target != type) return Notification.text("Invalid target"); if ($scope.spell.target != type) return Notification.text("Invalid target");
$scope.spell.cast(User.user, target); $scope.spell.cast(User.user, target);
User.save(); User.save();
$http.post('/api/v2/user/class/cast/' + $scope.spell.name, {target:target, type:type}).success(function(){ $http.post('/api/v2/user/class/cast/' + $scope.spell.key, {target:target, type:type}).success(function(){
var msg = "You cast " + $scope.spell.text; var msg = "You cast " + $scope.spell.text;
switch (type) { switch (type) {
case 'task': msg += ' on ' + target.text;break; case 'task': msg += ' on ' + target.text;break;

View File

@@ -215,30 +215,18 @@ api.attachGroupPopulated = function(req, res, next) {
api.postChat = function(req, res, next) { api.postChat = function(req, res, next) {
var user = res.locals.user var user = res.locals.user
var group = res.locals.group; var group = res.locals.group;
var message = {
id: shared.uuid(),
uuid: user._id,
contributor: user.contributor && user.contributor.toObject(),
backer: user.backer && user.backer.toObject(),
text: req.query.message, // FIXME this should be body, but ngResource is funky
user: user.profile.name,
timestamp: +(new Date)
};
var lastClientMsg = req.query.previousMsg; var lastClientMsg = req.query.previousMsg;
var chatUpdated = (lastClientMsg && group.chat && group.chat[0] && group.chat[0].id !== lastClientMsg) ? true : false; var chatUpdated = (lastClientMsg && group.chat && group.chat[0] && group.chat[0].id !== lastClientMsg) ? true : false;
group.chat.unshift(message); group.sendChat(req.query.message, user); // FIXME this should be body, but ngResource is funky
group.chat.splice(200);
if (group.type === 'party') { if (group.type === 'party') {
user.party.lastMessageSeen = message.id; user.party.lastMessageSeen = group.chat[0].id;
user.save(); user.save();
} }
group.save(function(err, saved){ group.save(function(err, saved){
if (err) return res.json(500, {err:err}); if (err) return res.json(500, {err:err});
return chatUpdated ? res.json({chat: group.chat}) : res.json({message: saved.chat[0]}); return chatUpdated ? res.json({chat: group.chat}) : res.json({message: saved.chat[0]});
}); });
} }
@@ -431,20 +419,22 @@ questStart = function(req, res) {
} }
var parallel = []; var parallel = [];
var key = group.quest.key;
// 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){
if (m._id == user._id) m.items.quests[m.party.quest]--; if (m._id == user._id) m.items.quests[key]--;
if (group.quest.members[m._id] == true) { if (group.quest.members[m._id] == true) {
m.party.quest = group.quest.key; m.party.quest.key = key;
} else { } else {
m.party.quest = undefined; _.merge(m.party.quest, {key:undefined,collection:{}});
delete group.quest.members[m._id]; delete group.quest.members[m._id];
} }
m._v++;
parallel.push(function(cb2){m.save(cb2);}); parallel.push(function(cb2){m.save(cb2);});
}) })
group.quest.active = true; group.quest.active = true;
group.quest.hp = shared.content.quests[group.quest.key].hp; group.quest.progress.hp = shared.content.quests[group.quest.key].stats.hp;
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){
@@ -502,5 +492,4 @@ api.questReject = function(req, res, next) {
//TODO //TODO
function questEnd(){}
function questAbort(){} function questAbort(){}

View File

@@ -82,11 +82,11 @@ api.score = function(req, res, next) {
task = user.ops.addTask({body:task}); task = user.ops.addTask({body:task});
} }
var delta = user.ops.score({params:{id:task.id, direction:direction}}); var delta = user.ops.score({params:{id:task.id, direction:direction}});
//user.markModified('flags');
user.save(function(err, saved) { user.save(function(err,saved){
if (err) return res.json(500, {err: err}); if (err) return res.json(500, {err: err});
res.json(200, _.extend({ res.json(200, _.extend({
delta: delta delta: delta,
}, saved.toJSON().stats)); }, saved.toJSON().stats));
}); });
@@ -194,10 +194,43 @@ 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;
user.fns.cron(); tally = user.fns.cron();
if (user.isModified()) if (user.isModified()) res.locals.wasModified = true;
res.locals.wasModified = true;
user.save(next); // If user is on a quest, roll for boss & player
if (user.party.quest.key && tally && (tally.up || tally.down)) {
async.waterfall([
function(cb){
Group.findOne({type: 'party', members: {'$in': [user._id]}})
.populate({
path: 'members',
match: {'party.quest.key':user.party.quest.key}
})
.exec(cb);
},
function(group, cb){
var quest = shared.content.quests[group.quest.key];
var down = tally.down * quest.stats.str; // multiply by boss strength
var parallel = [];
_.each(group.members, function(m){
parallel.push(function(cb2){
m.stats.hp += down;
m._v++;
m.save(cb2);
})
})
// Use http://js2coffee.org/ for building this string. Man I wish JS had interpolation...
group.sendChat("`<" + user.profile.name + "> attacks <" + quest.name + "> for " + (tally.up.toFixed(1)) + " damage, <" + quest.name + "> attacks party for " + (tally.down.toFixed(1)) + " damage.`");
parallel.push(function(cb2){
group.hurtBoss(tally.up,cb2);
});
async.parallel(parallel,cb);
}
], next);
} else {
user.save(next);
}
}; };
// api.reroll // Shared.ops // api.reroll // Shared.ops
@@ -347,15 +380,8 @@ api.cast = function(req, res) {
if (group) { if (group) {
series.push(function(cb2){ series.push(function(cb2){
group.chat.unshift({ var message = '`<'+user.profile.name+'> casts '+spell.text + (type=='user' ? ' on @'+found.profile.name : ' for the party')+'.`';
id: shared.uuid(), group.sendChat(message);
uuid: user._id,
contributor: user.contributor && user.contributor.toObject(),
backer: user.backer && user.backer.toObject(),
text: '`casts ' + spell.text + (type == 'user' ? ' on @'+found.profile.name : ' for the party') + '.`',
user: '<'+user.profile.name+'>',
timestamp: +new Date
});
group.save(cb2); group.save(cb2);
}) })
} }

View File

@@ -2,6 +2,7 @@ var mongoose = require("mongoose");
var Schema = mongoose.Schema; var Schema = mongoose.Schema;
var shared = require('habitrpg-shared'); var shared = require('habitrpg-shared');
var _ = require('lodash'); var _ = require('lodash');
var async = require('async');
var GroupSchema = new Schema({ var GroupSchema = new Schema({
_id: {type: String, 'default': shared.uuid}, _id: {type: String, 'default': shared.uuid},
@@ -33,14 +34,15 @@ var GroupSchema = new Schema({
challenges: [{type:'String', ref:'Challenge'}], // do we need this? could depend on back-ref instead (Challenge.find({group:GID})) challenges: [{type:'String', ref:'Challenge'}], // do we need this? could depend on back-ref instead (Challenge.find({group:GID}))
quest: { quest: {
key: String, key: String,
hp: Number,
active: {type:Boolean, 'default':false}, active: {type:Boolean, 'default':false},
progress:{
hp: Number,
collected: Schema.Types.Mixed,
},
/* //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: Schema.Types.Mixed
} }
}, { }, {
@@ -83,5 +85,88 @@ GroupSchema.methods.toJSON = function(){
return doc; return doc;
} }
GroupSchema.methods.sendChat = function(message, user){
var group = this;
var message = {
id: shared.uuid(),
text: message,
timestamp: +(new Date)
};
if (user) {
_.defaults(message, {
uuid: user._id,
contributor: user.contributor && user.contributor.toObject(),
backer: user.backer && user.backer.toObject(),
user: user.profile.name,
});
} else {
message.uuid = 'system';
}
group.chat.unshift(message);
group.chat.splice(200);
}
GroupSchema.methods.hurtBoss = function(delta, cb) {
var group = this;
group.quest.progress.hp -= delta;
var hp = group.quest.progress.hp;
if (group.quest.progress.hp <= 0) {
var key = group.quest.key,
quest = shared.content.quests[key];
var parallel = _.reduce(group.members, function(m,v,k){
// Achievement
_.defaults(v.achievements, {quests:{}})
if (!v.achievements.quests[key]) v.achievements.quests[key] = 0;
v.achievements.quests[key]++;
v.markModified('achievements');
// Drops
v.stats.gp += +quest.drop.gp;
v.stats.exp += +quest.drop.exp;
switch (quest.drop.type) {
case 'gear':
// TODO This means they can lose their new gear on death, is that what we want?
v.items.gear.owned[quest.drop.key] = true;
break;
case 'eggs':
case 'food':
case 'hatchingPotions':
if (!v.items.hatchingPotions[quest.drop.key]) v.items.hatchingPotions[quest.drop.key] = 0;
v.items.hatchingPotions[quest.drop.key]++;
break;
case 'pets':
if (!v.items.pets[quest.drop.key]) v.items.pets[quest.drop.key] = 5;
break;
case 'mounts':
v.items.mounts[quest.drop.key] = true;
break;
}
v.party.quest.key = undefined;
v.party.quest.collection = {};
v.markModified('party.quest');
v._v++;
m.push(function(cb3){ v.save(cb3); })
return m;
}, []);
// Finish the quest
group.quest = {};group.markModified('quest');
group.sendChat('`' + quest.name + ' has been slain! Party has received their rewards`');
parallel.push(function(cb3){
group.save(cb3);
})
async.parallel(parallel,cb);
}
else {
group.save(cb);
}
return hp;
}
module.exports.schema = GroupSchema; module.exports.schema = GroupSchema;
module.exports.model = mongoose.model("Group", GroupSchema); module.exports.model = mongoose.model("Group", GroupSchema);

View File

@@ -16,7 +16,7 @@ var Challenge = require('./challenge').model;
var eggPotionMapping = _.transform(shared.content.eggs, function(m, egg){ var eggPotionMapping = _.transform(shared.content.eggs, function(m, egg){
_.defaults(m, _.transform(shared.content.hatchingPotions, function(m2, pot){ _.defaults(m, _.transform(shared.content.hatchingPotions, function(m2, pot){
m2[egg.name + '-' + pot.name] = true; m2[egg.key + '-' + pot.key] = true;
})); }));
}) })
@@ -45,7 +45,8 @@ var UserSchema = new Schema({
veteran: Boolean, veteran: Boolean,
snowball: Number, snowball: Number,
streak: Number, streak: Number,
challenges: Array challenges: Array,
quests: Schema.Types.Mixed
}, },
auth: { auth: {
facebook: Schema.Types.Mixed, facebook: Schema.Types.Mixed,
@@ -200,13 +201,17 @@ var UserSchema = new Schema({
}, },
party: { party: {
//party._id // FIXME make these populate docs? // id // FIXME can we use a populated doc instead of fetching party separate from user?
current: String, // party._id
invitation: String, // party._id
lastMessageSeen: String, lastMessageSeen: String,
leader: Boolean,
order: {type:String, 'default':'level'}, order: {type:String, 'default':'level'},
quest: String quest: {
key: String,
tally: {
up: {type: Number, 'default': 0},
down: {type: Number, 'default': 0},
collection: {type: Schema.Types.Mixed, 'default': {}} // {feather:1, ingot:2}
}
}
}, },
preferences: { preferences: {
armorSet: String, armorSet: String,
@@ -250,13 +255,13 @@ var UserSchema = new Schema({
int: {type: Number, 'default': 0}, int: {type: Number, 'default': 0},
per: {type: Number, 'default': 0}, per: {type: Number, 'default': 0},
buffs: { buffs: {
str: Number, str: {type: Number, 'default': 0},
def: Number, def: {type: Number, 'default': 0},
per: Number, per: {type: Number, 'default': 0},
con: Number, con: {type: Number, 'default': 0},
stealth: Number, stealth: {type: Number, 'default': 0},
streaks: Boolean, streaks: {type: Boolean, 'default': false},
snowball: Boolean snowball: {type: Boolean, 'default': false}
} }
}, },
tags: [ tags: [

View File

@@ -31,7 +31,7 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html')
p(ng-show='eggCount < 1') You don't have any eggs. p(ng-show='eggCount < 1') You don't have any eggs.
div(ng-repeat='(egg,points) in ownedItems(user.items.eggs)') div(ng-repeat='(egg,points) in ownedItems(user.items.eggs)')
//TODO move positioning this styling to css //TODO move positioning this styling to css
button.customize-option(popover='{{Content.eggs[egg].notes}}', popover-title='{{Content.eggs[egg].text}} Egg', popover-trigger='mouseenter', popover-placement='right', ng-click='chooseEgg(egg)', class='Pet_Egg_{{egg}}', ng-class='{selectableInventory: selectedPotion && !user.items.pets[egg+"-"+selectedPotion.name]}') button.customize-option(popover='{{Content.eggs[egg].notes}}', popover-title='{{Content.eggs[egg].text}} Egg', popover-trigger='mouseenter', popover-placement='right', ng-click='chooseEgg(egg)', class='Pet_Egg_{{egg}}', ng-class='{selectableInventory: selectedPotion && !user.items.pets[egg+"-"+selectedPotion.key]}')
.badge.badge-info.stack-count {{points}} .badge.badge-info.stack-count {{points}}
//-p {{Content.eggs[egg].text}} //-p {{Content.eggs[egg].text}}
@@ -39,7 +39,7 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html')
menu.hatchingPotion-menu(label='Hatching Potions ({{potCount}})') menu.hatchingPotion-menu(label='Hatching Potions ({{potCount}})')
p(ng-show='potCount < 1') You don't have any hatching potions. p(ng-show='potCount < 1') You don't have any hatching potions.
div(ng-repeat='(pot,points) in ownedItems(user.items.hatchingPotions)') div(ng-repeat='(pot,points) in ownedItems(user.items.hatchingPotions)')
button.customize-option(popover='{{Content.hatchingPotions[pot].notes}}', popover-title='{{Content.hatchingPotions[pot].text}} Potion', popover-trigger='mouseenter', popover-placement='right', ng-click='choosePotion(pot)', class='Pet_HatchingPotion_{{pot}}', ng-class='{selectableInventory: selectedEgg && !user.items.pets[selectedEgg.name+"-"+pot]}') button.customize-option(popover='{{Content.hatchingPotions[pot].notes}}', popover-title='{{Content.hatchingPotions[pot].text}} Potion', popover-trigger='mouseenter', popover-placement='right', ng-click='choosePotion(pot)', class='Pet_HatchingPotion_{{pot}}', ng-class='{selectableInventory: selectedEgg && !user.items.pets[selectedEgg.key+"-"+pot]}')
.badge.badge-info.stack-count {{points}} .badge.badge-info.stack-count {{points}}
li.customize-menu li.customize-menu
@@ -79,17 +79,17 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html')
| Welcome to the Market! Buy hard-to-find eggs and potions! Sell your extras! Commission useful services! Come see what we have to offer. | Welcome to the Market! Buy hard-to-find eggs and potions! Sell your extras! Commission useful services! Come see what we have to offer.
p p
button.btn.btn-primary(ng-show='selectedEgg', ng-click='sellInventory()') button.btn.btn-primary(ng-show='selectedEgg', ng-click='sellInventory()')
| Sell {{selectedEgg.name}} for {{selectedEgg.value}} Gold | Sell {{selectedEgg.key}} for {{selectedEgg.value}} Gold
button.btn.btn-primary(ng-show='selectedPotion', ng-click='sellInventory()') button.btn.btn-primary(ng-show='selectedPotion', ng-click='sellInventory()')
| Sell {{selectedPotion.name}} for {{selectedPotion.value}} Gold | Sell {{selectedPotion.key}} for {{selectedPotion.value}} Gold
button.btn.btn-primary(ng-show='selectedFood', ng-click='sellInventory()') button.btn.btn-primary(ng-show='selectedFood', ng-click='sellInventory()')
| Sell {{selectedFood.name}} for {{selectedFood.value}} Gold | Sell {{selectedFood.key}} for {{selectedFood.value}} Gold
menu.inventory-list(type='list') menu.inventory-list(type='list')
li.customize-menu li.customize-menu
menu.pets-menu(label='Eggs') menu.pets-menu(label='Eggs')
div(ng-repeat='egg in Content.eggs') div(ng-repeat='egg in Content.eggs')
button.customize-option(popover='{{egg.notes}}', popover-title='{{egg.text}} Egg', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("eggs", egg)', class='Pet_Egg_{{egg.name}}') button.customize-option(popover='{{egg.notes}}', popover-title='{{egg.text}} Egg', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("eggs", egg)', class='Pet_Egg_{{egg.key}}')
p p
| {{egg.value}} | {{egg.value}}
span.Pet_Currency_Gem1x.inline-gems span.Pet_Currency_Gem1x.inline-gems
@@ -97,15 +97,15 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html')
li.customize-menu li.customize-menu
menu.pets-menu(label='Hatching Potions') menu.pets-menu(label='Hatching Potions')
div(ng-repeat='pot in Content.hatchingPotions') div(ng-repeat='pot in Content.hatchingPotions')
button.customize-option(popover='{{pot.notes}}', popover-title='{{pot.text}} Potion', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("hatchingPotions", pot)', class='Pet_HatchingPotion_{{pot.name}}') button.customize-option(popover='{{pot.notes}}', popover-title='{{pot.text}} Potion', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("hatchingPotions", pot)', class='Pet_HatchingPotion_{{pot.key}}')
p p
| {{pot.value}} | {{pot.value}}
span.Pet_Currency_Gem1x.inline-gems span.Pet_Currency_Gem1x.inline-gems
li.customize-menu li.customize-menu
menu.pets-menu(label='Food') menu.pets-menu(label='Food')
div(ng-repeat='food in Content.food', ng-show='food.name !== "Saddle"') div(ng-repeat='food in Content.food', ng-show='food.key !== "Saddle"')
button.customize-option(popover='{{food.notes}}', popover-title='{{food.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("food", food)', class='Pet_Food_{{food.name}}') button.customize-option(popover='{{food.notes}}', popover-title='{{food.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("food", food)', class='Pet_Food_{{food.key}}')
p p
| {{food.value}} | {{food.value}}
span.Pet_Currency_Gem1x.inline-gems span.Pet_Currency_Gem1x.inline-gems
@@ -121,7 +121,7 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html')
li.customize-menu li.customize-menu
menu.pets-menu(label='Special') menu.pets-menu(label='Special')
div div
button.customize-option(popover='{{Content.food.Saddle.notes}}', popover-title='{{Content.food.Saddle.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("food", Content.food.Saddle)', class='Pet_Food_{{Content.food.Saddle.name}}') button.customize-option(popover='{{Content.food.Saddle.notes}}', popover-title='{{Content.food.Saddle.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("food", Content.food.Saddle)', class='Pet_Food_{{Content.food.Saddle.key}}')
p p
| {{Content.food.Saddle.value}} | {{Content.food.Saddle.value}}
span.Pet_Currency_Gem1x.inline-gems span.Pet_Currency_Gem1x.inline-gems

View File

@@ -16,8 +16,8 @@ script(type='text/ng-template', id='partials/options.inventory.mounts.html')
menu.pets(type='list') menu.pets(type='list')
li.customize-menu(ng-repeat='egg in Content.eggs') li.customize-menu(ng-repeat='egg in Content.eggs')
menu menu
div(ng-repeat='potion in Content.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.mountText}}', popover-placement='bottom', ng-init='mount = egg.name+"-"+potion.name') div(ng-repeat='potion in Content.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.mountText}}', popover-placement='bottom', ng-init='mount = egg.key+"-"+potion.key')
button(class="pet-button Mount_Head_{{mount}}", ng-show='user.items.mounts[mount]', ng-class='{active: user.items.currentMount == mount}', ng-click='chooseMount(egg.name, potion.name)') button(class="pet-button Mount_Head_{{mount}}", ng-show='user.items.mounts[mount]', ng-class='{active: user.items.currentMount == mount}', ng-click='chooseMount(egg.key, potion.key)')
//div(class='Mount_Head_{{mount}}') //div(class='Mount_Head_{{mount}}')
button(class="pet-button pet-not-owned", ng-hide='user.items.mounts[mount]') button(class="pet-button pet-not-owned", ng-hide='user.items.mounts[mount]')
.PixelPaw .PixelPaw
@@ -49,8 +49,8 @@ script(type='text/ng-template', id='partials/options.inventory.pets.html')
menu.pets(type='list') menu.pets(type='list')
li.customize-menu(ng-repeat='egg in Content.eggs') li.customize-menu(ng-repeat='egg in Content.eggs')
menu menu
div(ng-repeat='potion in Content.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.text}}', popover-placement='bottom', ng-init='pet = egg.name+"-"+potion.name') div(ng-repeat='potion in Content.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.text}}', popover-placement='bottom', ng-init='pet = egg.key+"-"+potion.key')
button(class="pet-button Pet-{{pet}}", ng-if='user.items.pets[pet]>0', ng-class='{active: user.items.currentPet == pet, selectableInventory: selectedFood}', ng-click='choosePet(egg.name, potion.name)') button(class="pet-button Pet-{{pet}}", ng-if='user.items.pets[pet]>0', ng-class='{active: user.items.currentPet == pet, selectableInventory: selectedFood}', ng-click='choosePet(egg.key, potion.key)')
.progress(ng-class='{"progress-success": user.items.pets[pet]<50}') .progress(ng-class='{"progress-success": user.items.pets[pet]<50}')
.bar(style="width: {{user.items.pets[pet]/.5}}%;") .bar(style="width: {{user.items.pets[pet]/.5}}%;")
button(class="pet-button pet-not-owned", ng-if='!user.items.pets[pet]') button(class="pet-button pet-not-owned", ng-if='!user.items.pets[pet]')
@@ -61,8 +61,9 @@ script(type='text/ng-template', id='partials/options.inventory.pets.html')
div div
button(ng-if='user.items.pets["Wolf-Veteran"]', class="pet-button Pet-Wolf-Veteran", ng-class='{active: user.items.currentPet == "Wolf-Veteran"}', ng-click='choosePet("Wolf", "Veteran")', popover='Veteran Wolf', popover-trigger='mouseenter', popover-placement='bottom') button(ng-if='user.items.pets["Wolf-Veteran"]', class="pet-button Pet-Wolf-Veteran", ng-class='{active: user.items.currentPet == "Wolf-Veteran"}', ng-click='choosePet("Wolf", "Veteran")', popover='Veteran Wolf', popover-trigger='mouseenter', popover-placement='bottom')
button(ng-if='user.items.pets["Wolf-Cerberus"]', class="pet-button Pet-Wolf-Cerberus", ng-class='{active: user.items.currentPet == "Wolf-Cerberus"}', ng-click='choosePet("Wolf", "Cerberus")', popover='Cerberus Pup', popover-trigger='mouseenter', popover-placement='bottom') button(ng-if='user.items.pets["Wolf-Cerberus"]', class="pet-button Pet-Wolf-Cerberus", ng-class='{active: user.items.currentPet == "Wolf-Cerberus"}', ng-click='choosePet("Wolf", "Cerberus")', popover='Cerberus Pup', popover-trigger='mouseenter', popover-placement='bottom')
button(ng-if='user.items.pets["Dragon-Hydra"]', class="pet-button Pet-Dragon-Hydra", ng-class='{active: user.items.currentPet == "Dragon-Hydra"}', ng-click='choosePet("Dragon", "Hydra")', popover='Hydra', popover-trigger='mouseenter', popover-placement='bottom')
button(ng-if='user.items.pets["Turkey-Base"]', class="pet-button Pet-Turkey-Base", ng-class='{active: user.items.currentPet == "Turkey-Base"}', ng-click='choosePet("Turkey", "Base")', popover='Turkey', popover-trigger='mouseenter', popover-placement='bottom') button(ng-if='user.items.pets["Turkey-Base"]', class="pet-button Pet-Turkey-Base", ng-class='{active: user.items.currentPet == "Turkey-Base"}', ng-click='choosePet("Turkey", "Base")', popover='Turkey', popover-trigger='mouseenter', popover-placement='bottom')
button(ng-if='user.items.pets["BearCub-Polar"]', class="pet-button Pet-BearCub-Polar", ng-class='{active: user.items.currentPet == "BearCub-Polar"}', ng-click='choosePet("BearCub", "Polar")', popover='Polar Bear Cub', popover-trigger='mouseenter', popover-placement='bottom')
button(ng-if='user.items.pets["Dragon-Hydra"]', class="pet-button Pet-Dragon-Hydra", ng-class='{active: user.items.currentPet == "Dragon-Hydra"}', ng-click='choosePet("Dragon", "Hydra")', popover='Hydra', popover-trigger='mouseenter', popover-placement='bottom')
a(target='_blank', href='http://habitrpg.wikia.com/wiki/Contributing_to_HabitRPG') a(target='_blank', href='http://habitrpg.wikia.com/wiki/Contributing_to_HabitRPG')
button(ng-if='!user.items.pets["Dragon-Hydra"]', class="pet-button pet-not-owned", popover-trigger='mouseenter', popover-placement='right', popover="Click the gold paw to learn more about how you can obtain this rare pet through contributing to HabitRPG!", popover-title='How to Get this Pet!') button(ng-if='!user.items.pets["Dragon-Hydra"]', class="pet-button pet-not-owned", popover-trigger='mouseenter', popover-placement='right', popover="Click the gold paw to learn more about how you can obtain this rare pet through contributing to HabitRPG!", popover-title='How to Get this Pet!')
.PixelPaw-Gold .PixelPaw-Gold

View File

@@ -13,6 +13,4 @@ form.chat-form(ng-submit='postChat(group,message.content)')
input.btn.chat-btn(type='submit', value='Send Chat', ng-class='{disabled: _sending == true}') input.btn.chat-btn(type='submit', value='Send Chat', ng-class='{disabled: _sending == true}')
td td
button.btn(type="button", ng-click='sync(group)', tooltip='Fetch Recent Messages') button.btn(type="button", ng-click='sync(group)', tooltip='Fetch Recent Messages')
i(class='pull-right icon-refresh') i(class='pull-right icon-refresh')

View File

@@ -9,5 +9,5 @@ li(bindonce='group.chat', ng-repeat='message in group.chat', bo-class='{highligh
a(bo-show='user.contributor.admin || message.uuid == user.id', ng-click='deleteChatMessage(group, message)') a(bo-show='user.contributor.admin || message.uuid == user.id', ng-click='deleteChatMessage(group, message)')
| |
i.icon-remove(tooltip='Delete') i.icon-remove(tooltip='Delete')
a.label.chat-message(class='float-label', bo-class='{"label-npc": message.backer.npc, "label-contributor-{{message.contributor.level}}":message.contributor.level}', ng-click='clickMember(message.uuid, true)') a.label.chat-message(bo-show='message.user', class='float-label', bo-class='{"label-npc": message.backer.npc, "label-contributor-{{message.contributor.level}}":message.contributor.level}', ng-click='clickMember(message.uuid, true)')
span(tooltip='{{contribText(message.contributor, message.backer)}}') {{message.user}}&nbsp; span(tooltip='{{contribText(message.contributor, message.backer)}}') {{message.user}}&nbsp;

View File

@@ -31,10 +31,10 @@ a.pull-right.gem-wallet(popover-trigger='mouseenter', popover-title='Guild Bank'
| {{group.quest.hp | number:0}} / {{Content.quests[group.quest.key].hp}} | {{group.quest.hp | number:0}} / {{Content.quests[group.quest.key].hp}}
.hero-stats .hero-stats
.meter.health(title='Boss Health') .meter.health(title='Boss Health')
.bar(style='width: {{Shared.percent(group.quest.hp, Content.quests[group.quest.key].hp)}}%;') .bar(style='width: {{Shared.percent(group.quest.progress.hp, Content.quests[group.quest.key].stats.hp)}}%;')
span.meter-text span.meter-text
i.icon-heart i.icon-heart
| {{group.quest.hp | number:0}} / {{Content.quests[group.quest.key].hp}} | {{group.quest.progress.hp | number:0}} / {{Content.quests[group.quest.key].stats.hp}}
p {{Content.quests[group.quest.key].notes}} p {{Content.quests[group.quest.key].notes}}
// ------ Information ------- // ------ Information -------

View File

@@ -3,7 +3,7 @@ div(modal='modals.dropsEnabled')
h3 Drops Enabled! h3 Drops Enabled!
.modal-body .modal-body
//-p // TODO how to handle random first drop? //-p // TODO how to handle random first drop?
span.item-drop-icon(class='Pet_Egg_{{user.items.eggs.0.name}}', style='margin-left: 0px') span.item-drop-icon(class='Pet_Egg_{{user.items.eggs.0.key}}', style='margin-left: 0px')
| You've unlocked the Drop System! Now when you complete tasks, you have a small chance of finding an item. You just found a | You've unlocked the Drop System! Now when you complete tasks, you have a small chance of finding an item. You just found a
strong {{user.items.eggs.0.text}} Egg strong {{user.items.eggs.0.text}} Egg
| ! {{user.items.eggs.0.notes}}. | ! {{user.items.eggs.0.notes}}.
@@ -22,7 +22,7 @@ div(modal='modals.drop')
h3 An item has dropped! h3 An item has dropped!
.modal-body .modal-body
p p
span.item-drop-icon(class='Pet_{{user._tmp.drop.type}}_{{user._tmp.drop.name}}') span.item-drop-icon(class='Pet_{{user._tmp.drop.type}}_{{user._tmp.drop.key}}')
| {{user._tmp.drop.dialog}} | {{user._tmp.drop.dialog}}
.modal-footer .modal-footer
button.btn.btn-default.cancel(ng-click='modals.drop = false') Close button.btn.btn-default.cancel(ng-click='modals.drop = false') Close

View File

@@ -6,8 +6,18 @@ div(modal='modals.showQuest', ng-controller='InventoryCtrl')
tr tr
td td
p {{selectedQuest.notes}} p {{selectedQuest.notes}}
br
.well
h5 Rewards
table.table.table-striped
tr
td {{selectedQuest.drop.text}}
tr
td {{selectedQuest.drop.exp}} Experience
tr
td {{selectedQuest.drop.gp}} Gold
td td
div(class='quest_{{selectedQuest.name}}') div(class='quest_{{selectedQuest.key}}')
hr hr
div(style='clear:left;clear:right') div(style='clear:left;clear:right')
.npc_ian.pull-left .npc_ian.pull-left
@@ -18,10 +28,21 @@ div(modal='modals.showQuest', ng-controller='InventoryCtrl')
div(modal='party.quest.key && !questHold && party.quest.members[user._id] == undefined') div(modal='party.quest.key && !questHold && party.quest.members[user._id] == undefined')
.modal-header .modal-header
h3 Quest Invitation h3 Quest Invitation: {{Content.quests[party.quest.key].text}}
.modal-body .modal-body
p You have been invited to {{Content.quests[party.quest.key].text}}! p You have been invited to "{{Content.quests[party.quest.key].text}}"!
p (TODO list rewards) br
p {{Content.quests[party.quest.key].notes}}
br
.well
h5 Rewards
table.table.table-striped
tr
td {{Content.quests[party.quest.key].drop.text}}
tr
td {{Content.quests[party.quest.key].drop.exp}} Experience
tr
td {{Content.quests[party.quest.key].drop.gp}} Gold
.modal-footer .modal-footer
button.btn.btn-default.btn-small.btn-cancel(ng-click='questHold = true') Ask Later button.btn.btn-default.btn-small.btn-cancel(ng-click='questHold = true') Ask Later
button.btn.btn-default.btn-small.btn-cancel(ng-click='party.$questReject()') Reject button.btn.btn-default.btn-small.btn-cancel(ng-click='party.$questReject()') Reject