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-datepicker": "~1.2.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",
"github-buttons": "git://github.com/mdo/github-buttons.git",
"marked": "~0.2.9",

View File

@@ -4,7 +4,7 @@
"version": "0.0.0-152",
"main": "./src/server.js",
"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",
"connect-mongo": "*",
"passport-facebook": "~1.0.0",

View File

@@ -32,10 +32,10 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
}, true);
$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
}
var eggData = _.findWhere(Content.eggs, {name:egg});
var eggData = _.findWhere(Content.eggs, {key:egg});
if (!$scope.selectedPotion) {
$scope.selectedEgg = eggData;
} else {
@@ -44,11 +44,11 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
}
$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
}
// 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) {
$scope.selectedPotion = potionData;
} else {
@@ -57,7 +57,7 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
}
$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];
}
@@ -65,8 +65,8 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
var selected = $scope.selectedEgg ? 'selectedEgg' : $scope.selectedPotion ? 'selectedPotion' : $scope.selectedFood ? 'selectedFood' : undefined;
if (selected) {
var type = $scope.selectedEgg ? 'eggs' : $scope.selectedPotion ? 'hatchingPotions' : $scope.selectedFood ? 'food' : undefined;
user.ops.sell({params:{type:type, key: $scope[selected].name}});
if (user.items[type][$scope[selected].name] < 1) {
user.ops.sell({params:{type:type, key: $scope[selected].key}});
if (user.items[type][$scope[selected].key] < 1) {
$scope[selected] = null;
}
}
@@ -77,8 +77,8 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
}
$scope.hatch = function(egg, potion){
if (!confirm('Hatch a ' + potion.name + ' ' + egg.name + '?')) return;
user.ops.hatch({params:{egg:egg.name, hatchingPotion:potion.name}});
if (!confirm('Hatch a ' + potion.key + ' ' + egg.key + '?')) return;
user.ops.hatch({params:{egg:egg.key, hatchingPotion:potion.key}});
$scope.selectedEgg = 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 message = "Buy this " + string + " with " + item.value + " of your " + gems + " Gems?"
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){
@@ -98,12 +98,12 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
// Feeding Pet
if ($scope.selectedFood) {
var food = $scope.selectedFood
if (food.name == 'Saddle') {
if (food.key == 'Saddle') {
if (!confirm('Saddle ' + pet + '?')) return;
} else if (!confirm('Feed ' + pet + ' a ' + food.name + '?')) {
} else if (!confirm('Feed ' + pet + ' a ' + food.key + '?')) {
return;
}
User.user.ops.feed({params:{pet: pet, food: food.name}});
User.user.ops.feed({params:{pet: pet, food: food.key}});
$scope.selectedFood = null;
// Selecting Pet
@@ -125,7 +125,9 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', 'User',
$rootScope.modals.showQuest = false;
}
$scope.questInit = function(){
$rootScope.party.$questAccept({key:$scope.selectedQuest.name});
$rootScope.party.$questAccept({key:$scope.selectedQuest.key}, function(){
$rootScope.party.$get();
});
$scope.closeQuest();
}
}

View File

@@ -37,10 +37,10 @@ habitrpg.controller('NotificationCtrl',
var type = (after.type == 'Food') ? 'food' :
(after.type == 'HatchingPotion') ? 'hatchingPotions' : // can we use camelcase and remove this line?
(after.type.toLowerCase() + 's');
if(!User.user.items[type][after.name]){
User.user.items[type][after.name] = 0;
if(!User.user.items[type][after.key]){
User.user.items[type][after.key] = 0;
}
User.user.items[type][after.name]++;
User.user.items[type][after.key]++;
$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");
$scope.spell.cast(User.user, target);
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;
switch (type) {
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) {
var user = res.locals.user
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 chatUpdated = (lastClientMsg && group.chat && group.chat[0] && group.chat[0].id !== lastClientMsg) ? true : false;
group.chat.unshift(message);
group.chat.splice(200);
group.sendChat(req.query.message, user); // FIXME this should be body, but ngResource is funky
if (group.type === 'party') {
user.party.lastMessageSeen = message.id;
user.party.lastMessageSeen = group.chat[0].id;
user.save();
}
group.save(function(err, saved){
if (err) return res.json(500, {err:err});
return chatUpdated ? res.json({chat: group.chat}) : res.json({message: saved.chat[0]});
});
}
@@ -431,20 +419,22 @@ questStart = function(req, res) {
}
var parallel = [];
var key = group.quest.key;
// TODO will this handle appropriately when people leave/join party between quest invite?
_.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) {
m.party.quest = group.quest.key;
m.party.quest.key = key;
} else {
m.party.quest = undefined;
_.merge(m.party.quest, {key:undefined,collection:{}});
delete group.quest.members[m._id];
}
m._v++;
parallel.push(function(cb2){m.save(cb2);});
})
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);});
async.parallel(parallel,function(err, results){
@@ -502,5 +492,4 @@ api.questReject = function(req, res, next) {
//TODO
function questEnd(){}
function questAbort(){}

View File

@@ -82,11 +82,11 @@ api.score = function(req, res, next) {
task = user.ops.addTask({body:task});
}
var delta = user.ops.score({params:{id:task.id, direction:direction}});
//user.markModified('flags');
user.save(function(err,saved){
if (err) return res.json(500, {err: err});
res.json(200, _.extend({
delta: delta
delta: delta,
}, saved.toJSON().stats));
});
@@ -194,10 +194,43 @@ api.update = function(req, res, next) {
api.cron = function(req, res, next) {
var user = res.locals.user;
user.fns.cron();
if (user.isModified())
res.locals.wasModified = true;
tally = user.fns.cron();
if (user.isModified()) res.locals.wasModified = true;
// 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
@@ -347,15 +380,8 @@ api.cast = function(req, res) {
if (group) {
series.push(function(cb2){
group.chat.unshift({
id: shared.uuid(),
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
});
var message = '`<'+user.profile.name+'> casts '+spell.text + (type=='user' ? ' on @'+found.profile.name : ' for the party')+'.`';
group.sendChat(message);
group.save(cb2);
})
}

View File

@@ -2,6 +2,7 @@ var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var shared = require('habitrpg-shared');
var _ = require('lodash');
var async = require('async');
var GroupSchema = new Schema({
_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}))
quest: {
key: String,
hp: Number,
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
'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
*/
//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.
//TODO when booting user, remove from .joined and check again if we can now start the quest
members: Schema.Types.Mixed
}
}, {
@@ -83,5 +85,88 @@ GroupSchema.methods.toJSON = function(){
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.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){
_.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,
snowball: Number,
streak: Number,
challenges: Array
challenges: Array,
quests: Schema.Types.Mixed
},
auth: {
facebook: Schema.Types.Mixed,
@@ -200,13 +201,17 @@ var UserSchema = new Schema({
},
party: {
//party._id // FIXME make these populate docs?
current: String, // party._id
invitation: String, // party._id
// id // FIXME can we use a populated doc instead of fetching party separate from user?
lastMessageSeen: String,
leader: Boolean,
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: {
armorSet: String,
@@ -250,13 +255,13 @@ var UserSchema = new Schema({
int: {type: Number, 'default': 0},
per: {type: Number, 'default': 0},
buffs: {
str: Number,
def: Number,
per: Number,
con: Number,
stealth: Number,
streaks: Boolean,
snowball: Boolean
str: {type: Number, 'default': 0},
def: {type: Number, 'default': 0},
per: {type: Number, 'default': 0},
con: {type: Number, 'default': 0},
stealth: {type: Number, 'default': 0},
streaks: {type: Boolean, 'default': false},
snowball: {type: Boolean, 'default': false}
}
},
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.
div(ng-repeat='(egg,points) in ownedItems(user.items.eggs)')
//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}}
//-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}})')
p(ng-show='potCount < 1') You don't have any hatching potions.
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}}
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.
p
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()')
| Sell {{selectedPotion.name}} for {{selectedPotion.value}} Gold
| Sell {{selectedPotion.key}} for {{selectedPotion.value}} Gold
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')
li.customize-menu
menu.pets-menu(label='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
| {{egg.value}}
span.Pet_Currency_Gem1x.inline-gems
@@ -97,15 +97,15 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html')
li.customize-menu
menu.pets-menu(label='Hatching Potions')
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
| {{pot.value}}
span.Pet_Currency_Gem1x.inline-gems
li.customize-menu
menu.pets-menu(label='Food')
div(ng-repeat='food in Content.food', ng-show='food.name !== "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}}')
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.key}}')
p
| {{food.value}}
span.Pet_Currency_Gem1x.inline-gems
@@ -121,7 +121,7 @@ script(type='text/ng-template', id='partials/options.inventory.drops.html')
li.customize-menu
menu.pets-menu(label='Special')
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
| {{Content.food.Saddle.value}}
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')
li.customize-menu(ng-repeat='egg in Content.eggs')
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')
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)')
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.key, potion.key)')
//div(class='Mount_Head_{{mount}}')
button(class="pet-button pet-not-owned", ng-hide='user.items.mounts[mount]')
.PixelPaw
@@ -49,8 +49,8 @@ script(type='text/ng-template', id='partials/options.inventory.pets.html')
menu.pets(type='list')
li.customize-menu(ng-repeat='egg in Content.eggs')
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')
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)')
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.key, potion.key)')
.progress(ng-class='{"progress-success": user.items.pets[pet]<50}')
.bar(style="width: {{user.items.pets[pet]/.5}}%;")
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
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["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["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')
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

View File

@@ -14,5 +14,3 @@ form.chat-form(ng-submit='postChat(group,message.content)')
td
button.btn(type="button", ng-click='sync(group)', tooltip='Fetch Recent Messages')
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)')
|
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;

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}}
.hero-stats
.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
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}}
// ------ Information -------

View File

@@ -3,7 +3,7 @@ div(modal='modals.dropsEnabled')
h3 Drops Enabled!
.modal-body
//-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
strong {{user.items.eggs.0.text}} Egg
| ! {{user.items.eggs.0.notes}}.
@@ -22,7 +22,7 @@ div(modal='modals.drop')
h3 An item has dropped!
.modal-body
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}}
.modal-footer
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
td
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
div(class='quest_{{selectedQuest.name}}')
div(class='quest_{{selectedQuest.key}}')
hr
div(style='clear:left;clear:right')
.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')
.modal-header
h3 Quest Invitation
h3 Quest Invitation: {{Content.quests[party.quest.key].text}}
.modal-body
p You have been invited to {{Content.quests[party.quest.key].text}}!
p (TODO list rewards)
p You have been invited to "{{Content.quests[party.quest.key].text}}"!
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
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