Merge branch 'develop' into sabe/attributes
796
dist/customizer.css
vendored
2
dist/habitrpg-shared.css
vendored
214
dist/habitrpg-shared.js
vendored
@@ -9167,7 +9167,7 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
|
||||
|
||||
},{}],5:[function(require,module,exports){
|
||||
(function() {
|
||||
var api, gear, moment, repeat, _;
|
||||
var api, diminishingReturns, gear, moment, repeat, _;
|
||||
|
||||
_ = require('lodash');
|
||||
|
||||
@@ -9363,19 +9363,19 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
|
||||
value: 45
|
||||
},
|
||||
4: {
|
||||
text: "Priest Rod",
|
||||
text: "Physician Rod",
|
||||
notes: 'As much a badge of office as a healing tool. Increases INT by 7.',
|
||||
int: 7,
|
||||
value: 65
|
||||
},
|
||||
5: {
|
||||
text: "Royal Crosier",
|
||||
notes: 'Shines with the pure light of blessings. Increases INT by 9.',
|
||||
text: "Royal Scepter",
|
||||
notes: 'Fit to grace the hand of a monarch, or of one who stands at a monarch\'s right hand. Increases INT by 9.',
|
||||
int: 9,
|
||||
value: 90
|
||||
},
|
||||
6: {
|
||||
text: "Golden Crosier",
|
||||
text: "Golden Scepter",
|
||||
notes: 'Soothes the pain of all who look upon it. Increases INT by 11.',
|
||||
int: 11,
|
||||
value: 120,
|
||||
@@ -9552,19 +9552,19 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
|
||||
value: 45
|
||||
},
|
||||
3: {
|
||||
text: "Defender Vestment",
|
||||
text: "Defender Mantle",
|
||||
notes: 'Turns the healer\'s own magics inward to fend off harm. Increases CON by 12.',
|
||||
con: 12,
|
||||
value: 65
|
||||
},
|
||||
4: {
|
||||
text: "Priest Vestment",
|
||||
text: "Physician Mantle",
|
||||
notes: 'Projects authority and dissipates curses. Increases CON by 15.',
|
||||
con: 15,
|
||||
value: 90
|
||||
},
|
||||
5: {
|
||||
text: "Royal Vestment",
|
||||
text: "Royal Mantle",
|
||||
notes: 'Attire of those who have saved the lives of kings. Increases CON by 18.',
|
||||
con: 18,
|
||||
value: 120,
|
||||
@@ -9886,7 +9886,7 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
|
||||
value: 35
|
||||
},
|
||||
3: {
|
||||
text: "Hospitaler Shield",
|
||||
text: "Protector Shield",
|
||||
notes: 'Traditional shield of defender knights. Increases CON by 6.',
|
||||
con: 6,
|
||||
value: 50
|
||||
@@ -10001,6 +10001,13 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
|
||||
*/
|
||||
|
||||
|
||||
diminishingReturns = function(bonus, max, halfway) {
|
||||
if (halfway == null) {
|
||||
halfway = max / 2;
|
||||
}
|
||||
return max * (bonus / (bonus + halfway));
|
||||
};
|
||||
|
||||
api.spells = {
|
||||
wizard: {
|
||||
fireball: {
|
||||
@@ -10011,11 +10018,12 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
|
||||
notes: 'With a crack, flames burst from your staff, scorching a task. You deal high damage to the task, and gain additional experience (more experience for greens).',
|
||||
cast: function(user, target) {
|
||||
var bonus;
|
||||
target.value += user._statsComputed.int * .0075 * user.fns.crit('per');
|
||||
bonus = (target.value < 0 ? 1 : target.value + 1) * 2.5;
|
||||
user.stats.exp += bonus;
|
||||
bonus = user._statsComputed.int * user.fns.crit('per');
|
||||
target.value += diminishingReturns(bonus * .02, 4);
|
||||
bonus *= Math.ceil((target.value < 0 ? 1 : target.value + 1) * .075);
|
||||
user.stats.exp += diminishingReturns(bonus, 75);
|
||||
if (user.party.quest.key) {
|
||||
return user.party.quest.progress.up += bonus;
|
||||
return user.party.quest.progress.up += diminishingReturns(bonus * .1, 50, 30);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -10177,7 +10185,7 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
|
||||
if ((_base = user.stats.buffs).stealth == null) {
|
||||
_base.stealth = 0;
|
||||
}
|
||||
return user.stats.buffs.stealth = Math.ceil(user._statsComputed.per * .03);
|
||||
return user.stats.buffs.stealth += Math.ceil(user._statsComputed.per * .03);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -10408,43 +10416,53 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
|
||||
api.food = {
|
||||
Meat: {
|
||||
text: 'Meat',
|
||||
target: 'Base'
|
||||
target: 'Base',
|
||||
article: ''
|
||||
},
|
||||
Milk: {
|
||||
text: 'Milk',
|
||||
target: 'White'
|
||||
target: 'White',
|
||||
article: ''
|
||||
},
|
||||
Potatoe: {
|
||||
text: 'Potato',
|
||||
target: 'Desert'
|
||||
target: 'Desert',
|
||||
article: 'a '
|
||||
},
|
||||
Strawberry: {
|
||||
text: 'Strawberry',
|
||||
target: 'Red'
|
||||
target: 'Red',
|
||||
article: 'a '
|
||||
},
|
||||
Chocolate: {
|
||||
text: 'Chocolate',
|
||||
target: 'Shade'
|
||||
target: 'Shade',
|
||||
article: ''
|
||||
},
|
||||
Fish: {
|
||||
text: 'Fish',
|
||||
target: 'Skeleton'
|
||||
target: 'Skeleton',
|
||||
article: 'a '
|
||||
},
|
||||
RottenMeat: {
|
||||
text: 'Rotten Meat',
|
||||
target: 'Zombie'
|
||||
target: 'Zombie',
|
||||
article: ''
|
||||
},
|
||||
CottonCandyPink: {
|
||||
text: 'Pink Cotton Candy',
|
||||
target: 'CottonCandyPink'
|
||||
target: 'CottonCandyPink',
|
||||
article: ''
|
||||
},
|
||||
CottonCandyBlue: {
|
||||
text: 'Blue Cotton Candy',
|
||||
target: 'CottonCandyBlue'
|
||||
target: 'CottonCandyBlue',
|
||||
article: ''
|
||||
},
|
||||
Honey: {
|
||||
text: 'Honey',
|
||||
target: 'Golden'
|
||||
target: 'Golden',
|
||||
article: ''
|
||||
},
|
||||
Saddle: {
|
||||
text: 'Saddle',
|
||||
@@ -10609,7 +10627,7 @@ var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ?
|
||||
|
||||
},{"lodash":3,"moment":4}],6:[function(require,module,exports){
|
||||
var process=require("__browserify_process");(function() {
|
||||
var api, content, dayMapping, moment, preenHistory, sanitizeOptions, _,
|
||||
var $w, api, content, dayMapping, moment, preenHistory, sanitizeOptions, _,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
||||
|
||||
moment = require('moment');
|
||||
@@ -10620,6 +10638,10 @@ var process=require("__browserify_process");(function() {
|
||||
|
||||
api = module.exports = {};
|
||||
|
||||
$w = function(s) {
|
||||
return s.split(' ');
|
||||
};
|
||||
|
||||
/*
|
||||
------------------------------------------------------
|
||||
Time / Day
|
||||
@@ -10743,7 +10765,7 @@ var process=require("__browserify_process");(function() {
|
||||
|
||||
api.diminishingReturns = function(bonus, max, halfway) {
|
||||
if (halfway == null) {
|
||||
halfway = bonus / 2;
|
||||
halfway = max / 2;
|
||||
}
|
||||
return max * (bonus / (bonus + halfway));
|
||||
};
|
||||
@@ -10870,6 +10892,9 @@ var process=require("__browserify_process");(function() {
|
||||
|
||||
api.taskDefaults = function(task) {
|
||||
var defaults, _ref, _ref1, _ref2;
|
||||
if (task == null) {
|
||||
task = {};
|
||||
}
|
||||
if (!(task.type && ((_ref = task.type) === 'habit' || _ref === 'daily' || _ref === 'todo' || _ref === 'reward'))) {
|
||||
task.type = 'habit';
|
||||
}
|
||||
@@ -11158,11 +11183,11 @@ var process=require("__browserify_process");(function() {
|
||||
user.fns.dotSet(k, v);
|
||||
return true;
|
||||
});
|
||||
return typeof cb === "function" ? cb(null, req) : void 0;
|
||||
return typeof cb === "function" ? cb(null, user) : void 0;
|
||||
},
|
||||
sleep: function(req, cb) {
|
||||
user.preferences.sleep = !user.preferences.sleep;
|
||||
return cb(null, req);
|
||||
return typeof cb === "function" ? cb(null, {}) : void 0;
|
||||
},
|
||||
revive: function(req, cb) {
|
||||
var item, lostItem, lostStat;
|
||||
@@ -11204,7 +11229,7 @@ var process=require("__browserify_process");(function() {
|
||||
return typeof cb === "function" ? cb((item ? {
|
||||
code: 200,
|
||||
message: "Your " + item.text + " broke."
|
||||
} : null), req) : void 0;
|
||||
} : null), user) : void 0;
|
||||
},
|
||||
reset: function(req, cb) {
|
||||
var gear;
|
||||
@@ -11230,77 +11255,78 @@ var process=require("__browserify_process");(function() {
|
||||
user.markModified('items.gear.owned');
|
||||
}
|
||||
user.preferences.costume = false;
|
||||
return cb(null, req);
|
||||
return typeof cb === "function" ? cb(null, user) : void 0;
|
||||
},
|
||||
reroll: function(req, cb) {
|
||||
if (user.balance < 1) {
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 401,
|
||||
message: "Not enough gems."
|
||||
}, req);
|
||||
}, req) : void 0;
|
||||
}
|
||||
user.balance--;
|
||||
_.each(user.tasks, function(task) {
|
||||
return task.value = 0;
|
||||
});
|
||||
user.stats.hp = 50;
|
||||
return cb(null, req);
|
||||
return typeof cb === "function" ? cb(null, user) : void 0;
|
||||
},
|
||||
clearCompleted: function(req, cb) {
|
||||
user.todos = _.where(user.todos, {
|
||||
completed: false
|
||||
});
|
||||
return cb(null, req);
|
||||
return typeof cb === "function" ? cb(null, user.todos) : void 0;
|
||||
},
|
||||
sortTask: function(req, cb) {
|
||||
var from, id, task, to, _ref;
|
||||
var from, id, task, tasks, to, _ref;
|
||||
id = req.params.id;
|
||||
_ref = req.query, to = _ref.to, from = _ref.from;
|
||||
task = user.tasks[id];
|
||||
if (!task) {
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: "Task not found."
|
||||
});
|
||||
}) : void 0;
|
||||
}
|
||||
if (!((to != null) && (from != null))) {
|
||||
return cb('?to=__&from=__ are required');
|
||||
return typeof cb === "function" ? cb('?to=__&from=__ are required') : void 0;
|
||||
}
|
||||
user["" + task.type + "s"].splice(to, 0, user["" + task.type + "s"].splice(from, 1)[0]);
|
||||
return cb(null, req);
|
||||
tasks = user["" + task.type + "s"];
|
||||
tasks.splice(to, 0, tasks.splice(from, 1)[0]);
|
||||
return typeof cb === "function" ? cb(null, tasks) : void 0;
|
||||
},
|
||||
updateTask: function(req, cb) {
|
||||
var _base, _ref;
|
||||
if (!user.tasks[(_ref = req.params) != null ? _ref.id : void 0]) {
|
||||
var task, _ref;
|
||||
if (!(task = user.tasks[(_ref = req.params) != null ? _ref.id : void 0])) {
|
||||
return typeof cb === "function" ? cb("Task not found") : void 0;
|
||||
}
|
||||
_.merge(user.tasks[req.params.id], req.body);
|
||||
if (typeof (_base = user.tasks[req.params.id]).markModified === "function") {
|
||||
_base.markModified('tags');
|
||||
_.merge(task, req.body);
|
||||
if (typeof task.markModified === "function") {
|
||||
task.markModified('tags');
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, req) : void 0;
|
||||
return typeof cb === "function" ? cb(null, task) : void 0;
|
||||
},
|
||||
deleteTask: function(req, cb) {
|
||||
var i, task, _ref;
|
||||
task = user.tasks[(_ref = req.params) != null ? _ref.id : void 0];
|
||||
if (!task) {
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: 'Task not found'
|
||||
});
|
||||
}) : void 0;
|
||||
}
|
||||
i = user[task.type + "s"].indexOf(task);
|
||||
if (~i) {
|
||||
user[task.type + "s"].splice(i, 1);
|
||||
}
|
||||
return cb(null, req);
|
||||
return typeof cb === "function" ? cb(null, {}) : void 0;
|
||||
},
|
||||
addTask: function(req, cb) {
|
||||
var task;
|
||||
task = api.taskDefaults(req.body);
|
||||
user["" + task.type + "s"].unshift(task);
|
||||
if (typeof cb === "function") {
|
||||
cb(null, req);
|
||||
cb(null, task);
|
||||
}
|
||||
return task;
|
||||
},
|
||||
@@ -11313,7 +11339,7 @@ var process=require("__browserify_process");(function() {
|
||||
user.tags.push({
|
||||
name: name
|
||||
});
|
||||
return typeof cb === "function" ? cb(null, req) : void 0;
|
||||
return typeof cb === "function" ? cb(null, user.tags) : void 0;
|
||||
},
|
||||
updateTag: function(req, cb) {
|
||||
var i, tid;
|
||||
@@ -11322,10 +11348,10 @@ var process=require("__browserify_process");(function() {
|
||||
id: tid
|
||||
});
|
||||
if (!~i) {
|
||||
return cb('Tag not found', req);
|
||||
return typeof cb === "function" ? cb('Tag not found', req) : void 0;
|
||||
}
|
||||
user.tags[i].name = req.body.name;
|
||||
return typeof cb === "function" ? cb(null, req) : void 0;
|
||||
return typeof cb === "function" ? cb(null, user.tags[i]) : void 0;
|
||||
},
|
||||
deleteTag: function(req, cb) {
|
||||
var i, tag, tid;
|
||||
@@ -11334,7 +11360,7 @@ var process=require("__browserify_process");(function() {
|
||||
id: tid
|
||||
});
|
||||
if (!~i) {
|
||||
return cb('Tag not found', req);
|
||||
return typeof cb === "function" ? cb('Tag not found', req) : void 0;
|
||||
}
|
||||
tag = user.tags[i];
|
||||
delete user.filters[tag.id];
|
||||
@@ -11345,7 +11371,7 @@ var process=require("__browserify_process");(function() {
|
||||
_.each(['habits', 'dailys', 'todos', 'rewards'], function(type) {
|
||||
return typeof user.markModified === "function" ? user.markModified(type) : void 0;
|
||||
});
|
||||
return cb(null, req);
|
||||
return typeof cb === "function" ? cb(null, user.tags) : void 0;
|
||||
},
|
||||
feed: function(req, cb) {
|
||||
var egg, evolve, food, message, pet, potion, userPets, _ref, _ref1, _ref2;
|
||||
@@ -11354,28 +11380,28 @@ var process=require("__browserify_process");(function() {
|
||||
_ref1 = pet.split('-'), egg = _ref1[0], potion = _ref1[1];
|
||||
userPets = user.items.pets;
|
||||
if (!userPets[pet]) {
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: ":pet not found in user.items.pets"
|
||||
});
|
||||
}) : void 0;
|
||||
}
|
||||
if (!((_ref2 = user.items.food) != null ? _ref2[food.key] : void 0)) {
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: ":food not found in user.items.food"
|
||||
});
|
||||
}) : void 0;
|
||||
}
|
||||
if (content.specialPets[pet]) {
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 401,
|
||||
message: "Can't feed this pet."
|
||||
});
|
||||
}) : void 0;
|
||||
}
|
||||
if (user.items.mounts[pet] && (userPets[pet] >= 50 || food.key === 'Saddle')) {
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 401,
|
||||
message: "You already have that mount"
|
||||
});
|
||||
}) : void 0;
|
||||
}
|
||||
message = '';
|
||||
evolve = function() {
|
||||
@@ -11401,33 +11427,33 @@ var process=require("__browserify_process");(function() {
|
||||
}
|
||||
}
|
||||
user.items.food[food.key]--;
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 200,
|
||||
message: message
|
||||
}, req);
|
||||
}, userPets[pet]) : void 0;
|
||||
},
|
||||
purchase: function(req, cb) {
|
||||
var item, key, type, _ref;
|
||||
_ref = req.params, type = _ref.type, key = _ref.key;
|
||||
if (type !== 'eggs' && type !== 'hatchingPotions' && type !== 'food' && type !== 'quests' && type !== 'special') {
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: ":type must be in [hatchingPotions,eggs,food,quests,special]"
|
||||
}, req);
|
||||
}, req) : void 0;
|
||||
}
|
||||
item = content[type][key];
|
||||
if (!item) {
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: ":key not found for Content." + type
|
||||
}, req);
|
||||
}, req) : void 0;
|
||||
}
|
||||
if (!user.items[type][key]) {
|
||||
user.items[type][key] = 0;
|
||||
}
|
||||
user.items[type][key]++;
|
||||
user.balance -= item.value / 4;
|
||||
return cb(null, req);
|
||||
return typeof cb === "function" ? cb(null, _.pick(user, $w('items balance'))) : void 0;
|
||||
},
|
||||
buy: function(req, cb) {
|
||||
var item, key, message, _ref;
|
||||
@@ -11465,26 +11491,26 @@ var process=require("__browserify_process");(function() {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 200,
|
||||
message: message
|
||||
}, req) : void 0;
|
||||
}, _.pick(user, $w('items achievements stats'))) : void 0;
|
||||
},
|
||||
sell: function(req, cb) {
|
||||
var key, type, _ref;
|
||||
_ref = req.params, key = _ref.key, type = _ref.type;
|
||||
if (type !== 'eggs' && type !== 'hatchingPotions' && type !== 'food') {
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: ":type not found. Must bes in [eggs, hatchingPotions, food]"
|
||||
});
|
||||
}) : void 0;
|
||||
}
|
||||
if (!user.items[type][key]) {
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: ":key not found for user.items." + type
|
||||
});
|
||||
}) : void 0;
|
||||
}
|
||||
user.items[type][key]--;
|
||||
user.stats.gp += content[type][key].value;
|
||||
return typeof cb === "function" ? cb(null, req) : void 0;
|
||||
return typeof cb === "function" ? cb(null, _.pick(user, $w('stats items'))) : void 0;
|
||||
},
|
||||
equip: function(req, cb) {
|
||||
var item, key, message, type, _ref;
|
||||
@@ -11502,29 +11528,29 @@ var process=require("__browserify_process");(function() {
|
||||
user.items.gear[type][item.type] = item.key;
|
||||
message = user.fns.handleTwoHanded(item, type);
|
||||
}
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 200,
|
||||
message: message
|
||||
}, req);
|
||||
}, user.items) : void 0;
|
||||
},
|
||||
hatch: function(req, cb) {
|
||||
var egg, hatchingPotion, pet, _ref;
|
||||
_ref = req.params, egg = _ref.egg, hatchingPotion = _ref.hatchingPotion;
|
||||
if (!(egg && hatchingPotion)) {
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 404,
|
||||
message: "Please specify query.egg & query.hatchingPotion"
|
||||
});
|
||||
}) : void 0;
|
||||
}
|
||||
if (!(user.items.eggs[egg] > 0 && user.items.hatchingPotions[hatchingPotion] > 0)) {
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 401,
|
||||
message: "You're missing either that egg or that potion"
|
||||
});
|
||||
}) : void 0;
|
||||
}
|
||||
pet = "" + egg + "-" + hatchingPotion;
|
||||
if (user.items.pets[pet]) {
|
||||
return cb("You already have that pet. Try hatching a different combination!");
|
||||
return typeof cb === "function" ? cb("You already have that pet. Try hatching a different combination!") : void 0;
|
||||
}
|
||||
user.items.pets[pet] = 5;
|
||||
user.items.eggs[egg]--;
|
||||
@@ -11532,7 +11558,7 @@ var process=require("__browserify_process");(function() {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 200,
|
||||
message: "Your egg hatched! Visit your stable to equip your pet."
|
||||
}, req) : void 0;
|
||||
}, user.items) : void 0;
|
||||
},
|
||||
unlock: function(req, cb) {
|
||||
var alreadyOwns, cost, fullSet, k, path, split, v;
|
||||
@@ -11565,7 +11591,7 @@ var process=require("__browserify_process");(function() {
|
||||
if (typeof user.markModified === "function") {
|
||||
user.markModified('purchased');
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, req) : void 0;
|
||||
return typeof cb === "function" ? cb(null, _.pick(user, $w('purchased preferences'))) : void 0;
|
||||
},
|
||||
changeClass: function(req, cb) {
|
||||
var klass, _ref;
|
||||
@@ -11590,13 +11616,13 @@ var process=require("__browserify_process");(function() {
|
||||
} else {
|
||||
if (user.preferences.disableClasses) {
|
||||
user.preferences.disableClasses = false;
|
||||
user.autoAllocate = false;
|
||||
user.preferences.autoAllocate = false;
|
||||
} else {
|
||||
if (!(user.balance >= .75)) {
|
||||
return cb({
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 401,
|
||||
message: "Not enough gems"
|
||||
});
|
||||
}) : void 0;
|
||||
}
|
||||
user.balance -= .75;
|
||||
}
|
||||
@@ -11609,7 +11635,7 @@ var process=require("__browserify_process");(function() {
|
||||
});
|
||||
user.flags.classSelected = false;
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, req) : void 0;
|
||||
return typeof cb === "function" ? cb(null, _.pick(user, $w('stats flags items preferences'))) : void 0;
|
||||
},
|
||||
disableClasses: function(req, cb) {
|
||||
user.stats["class"] = 'warrior';
|
||||
@@ -11618,7 +11644,7 @@ var process=require("__browserify_process");(function() {
|
||||
user.preferences.autoAllocate = true;
|
||||
user.stats.str = user.stats.lvl;
|
||||
user.stats.points = 0;
|
||||
return cb(null, req);
|
||||
return typeof cb === "function" ? cb(null, _.pick(user, $w('stats flags preferences'))) : void 0;
|
||||
},
|
||||
allocate: function(req, cb) {
|
||||
var stat;
|
||||
@@ -11630,7 +11656,7 @@ var process=require("__browserify_process");(function() {
|
||||
user.stats.mp++;
|
||||
}
|
||||
}
|
||||
return typeof cb === "function" ? cb(null, req) : void 0;
|
||||
return typeof cb === "function" ? cb(null, _.pick(user, $w('stats'))) : void 0;
|
||||
},
|
||||
score: function(req, cb) {
|
||||
var addPoints, calculateDelta, delta, direction, id, num, options, stats, subtractPoints, task, th, _ref;
|
||||
@@ -11653,7 +11679,7 @@ var process=require("__browserify_process");(function() {
|
||||
task.priority = 1;
|
||||
}
|
||||
if (task.value > stats.gp && task.type === 'reward') {
|
||||
return cb('Not enough Gold');
|
||||
return typeof cb === "function" ? cb('Not enough Gold') : void 0;
|
||||
}
|
||||
delta = 0;
|
||||
calculateDelta = function() {
|
||||
@@ -11769,7 +11795,7 @@ var process=require("__browserify_process");(function() {
|
||||
}
|
||||
}
|
||||
if (typeof cb === "function") {
|
||||
cb(null, req);
|
||||
cb(null, user);
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
@@ -11904,7 +11930,7 @@ var process=require("__browserify_process");(function() {
|
||||
}
|
||||
user.items.food[drop.key] += 1;
|
||||
drop.type = 'Food';
|
||||
drop.dialog = "You've found a " + drop.text + " Food! " + drop.notes;
|
||||
drop.dialog = "You've found " + drop.article + drop.text + "! " + drop.notes;
|
||||
} else if (rarity > .3) {
|
||||
drop = user.fns.randomVal(content.eggs);
|
||||
if ((_base1 = user.items.eggs)[_name1 = drop.key] == null) {
|
||||
|
||||
796
dist/spritesmith.css
vendored
BIN
dist/spritesmith.png
vendored
|
Before Width: | Height: | Size: 248 KiB After Width: | Height: | Size: 246 KiB |
BIN
img/project_files/hair_color_wips/hair_bangs_1_blue.png
Normal file
|
After Width: | Height: | Size: 474 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_bone.png
Normal file
|
After Width: | Height: | Size: 423 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_ccblue.png
Normal file
|
After Width: | Height: | Size: 451 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_ccgreen.png
Normal file
|
After Width: | Height: | Size: 465 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_ccorange.png
Normal file
|
After Width: | Height: | Size: 461 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_ccpink.png
Normal file
|
After Width: | Height: | Size: 465 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_ccpurple.png
Normal file
|
After Width: | Height: | Size: 458 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_ccyellow.png
Normal file
|
After Width: | Height: | Size: 428 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_darkred.png
Normal file
|
After Width: | Height: | Size: 452 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_desert.png
Normal file
|
After Width: | Height: | Size: 465 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_fire.png
Normal file
|
After Width: | Height: | Size: 464 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_green.png
Normal file
|
After Width: | Height: | Size: 477 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_ice.png
Normal file
|
After Width: | Height: | Size: 455 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_orange.png
Normal file
|
After Width: | Height: | Size: 453 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_purple.png
Normal file
|
After Width: | Height: | Size: 464 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_rot.png
Normal file
|
After Width: | Height: | Size: 479 B |
BIN
img/project_files/hair_color_wips/hair_bangs_1_yellow.png
Normal file
|
After Width: | Height: | Size: 451 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_blue.png
Normal file
|
After Width: | Height: | Size: 444 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_bone.png
Normal file
|
After Width: | Height: | Size: 392 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_ccblue.png
Normal file
|
After Width: | Height: | Size: 421 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_ccgreen.png
Normal file
|
After Width: | Height: | Size: 430 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_ccorange.png
Normal file
|
After Width: | Height: | Size: 432 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_ccpink.png
Normal file
|
After Width: | Height: | Size: 432 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_ccpurple.png
Normal file
|
After Width: | Height: | Size: 427 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_ccyellow.png
Normal file
|
After Width: | Height: | Size: 400 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_darkred.png
Normal file
|
After Width: | Height: | Size: 421 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_desert.png
Normal file
|
After Width: | Height: | Size: 435 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_fire.png
Normal file
|
After Width: | Height: | Size: 434 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_green.png
Normal file
|
After Width: | Height: | Size: 441 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_ice.png
Normal file
|
After Width: | Height: | Size: 426 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_orange.png
Normal file
|
After Width: | Height: | Size: 420 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_purple.png
Normal file
|
After Width: | Height: | Size: 427 B |
BIN
img/project_files/hair_color_wips/hair_bangs_2_rot.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
img/project_files/hair_color_wips/hair_bangs_2_yellow.png
Normal file
|
After Width: | Height: | Size: 420 B |
BIN
img/sprites/spritesmith/achievements/achievement-snowball.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 828 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 827 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 386 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 496 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 359 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 520 B |
|
Before Width: | Height: | Size: 484 B After Width: | Height: | Size: 484 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 511 B After Width: | Height: | Size: 511 B |
@@ -51,9 +51,9 @@ gear =
|
||||
1: text: "Acolyte Rod", notes:'Crafted during a healer\'s initiation. Increases INT by 2.', int: 2, value:20
|
||||
2: text: "Quartz Rod", notes:'Topped with a gem bearing curative properties. Increases INT by 3.', int: 3, value:30
|
||||
3: text: "Amethyst Rod", notes:'Purifies poison at a touch. Increases INT by 5.', int: 5, value:45
|
||||
4: text: "Priest Rod", notes:'As much a badge of office as a healing tool. Increases INT by 7.', int:7, value:65
|
||||
5: text: "Royal Crosier", notes:'Shines with the pure light of blessings. Increases INT by 9.', int: 9, value:90
|
||||
6: text: "Golden Crosier", notes:'Soothes the pain of all who look upon it. Increases INT by 11.', int: 11, value:120, last: true
|
||||
4: text: "Physician Rod", notes:'As much a badge of office as a healing tool. Increases INT by 7.', int:7, value:65
|
||||
5: text: "Royal Scepter", notes:'Fit to grace the hand of a monarch, or of one who stands at a monarch\'s right hand. Increases INT by 9.', int: 9, value:90
|
||||
6: text: "Golden Scepter", notes:'Soothes the pain of all who look upon it. Increases INT by 11.', int: 11, value:120, last: true
|
||||
special:
|
||||
0: text: "Dark Souls Blade", notes:'Feasts upon foes\' life essence to power its wicked strokes. Increases STR by 20.', str: 20, value:150, canOwn: ((u)-> +u.backer?.tier >= 70)
|
||||
1: text: "Crystal Blade", notes:'Its glittering facets tell the tale of a hero. Increases all attributes by 6.', str: 6, per: 6, con: 6, int: 6, value:170, canOwn: ((u)-> +u.contributor?.level >= 4)
|
||||
@@ -88,9 +88,9 @@ gear =
|
||||
#0: text: "Novice Robe", notes:'For healers in training. Confers no benefit.', value:0
|
||||
1: text: "Acolyte Robe", notes:'Garment showing humility and purpose. Increases CON by 6.', con: 6, value:30
|
||||
2: text: "Medic Robe", notes:'Worn by those dedicated to tending the wounded in battle. Increases CON by 9.', con: 9, value:45
|
||||
3: text: "Defender Vestment", notes:'Turns the healer\'s own magics inward to fend off harm. Increases CON by 12.', con: 12, value:65
|
||||
4: text: "Priest Vestment", notes:'Projects authority and dissipates curses. Increases CON by 15.', con: 15, value:90
|
||||
5: text: "Royal Vestment", notes:'Attire of those who have saved the lives of kings. Increases CON by 18.', con: 18, value:120, last: true
|
||||
3: text: "Defender Mantle", notes:'Turns the healer\'s own magics inward to fend off harm. Increases CON by 12.', con: 12, value:65
|
||||
4: text: "Physician Mantle", notes:'Projects authority and dissipates curses. Increases CON by 15.', con: 15, value:90
|
||||
5: text: "Royal Mantle", notes:'Attire of those who have saved the lives of kings. Increases CON by 18.', con: 18, value:120, last: true
|
||||
special:
|
||||
0: text: "Shade Armor", notes:'Screams when struck, for it feels pain in its wearer\'s place. Increases CON by 20.', con: 20, value:150, canOwn: ((u)-> +u.backer?.tier >= 45)
|
||||
1: text: "Crystal Armor", notes:'Its tireless power inures the wearer to mundane discomfort. Increases all attributes by 6.', con: 6, str: 6, per: 6, int: 6, value:170, canOwn: ((u)-> +u.contributor?.level >= 2)
|
||||
@@ -157,7 +157,7 @@ gear =
|
||||
#0: text: "No Shield", notes:'No shield.', def: 0, value:0
|
||||
1: text: "Medic Buckler", notes:'Easy to disengage, freeing a hand for bandaging. Increases CON by 2.', con: 2, value:20
|
||||
2: text: "Kite Shield", notes:'Tapered shield with the symbol of healing. Increases CON by 4.', con: 4, value:35
|
||||
3: text: "Hospitaler Shield", notes:'Traditional shield of defender knights. Increases CON by 6.', con: 6, value:50
|
||||
3: text: "Protector Shield", notes:'Traditional shield of defender knights. Increases CON by 6.', con: 6, value:50
|
||||
4: text: "Savior Shield", notes:'Stops blows aimed at nearby innocents as well as those aimed at you. Increases CON by 9.', con: 9, value:70
|
||||
5: text: "Royal Shield", notes:'Bestowed upon those most dedicated to the kingdom\'s defense. Increases CON by 12.', con: 12, value:90, last: true
|
||||
special:
|
||||
@@ -209,6 +209,9 @@ api.potion = type: 'potion', text: "Health Potion", notes: "Recover 15 Health (I
|
||||
Note, user.stats.mp is docked after automatically (it's appended to functions automatically down below in an _.each)
|
||||
###
|
||||
|
||||
#
|
||||
diminishingReturns = (bonus, max, halfway=max/2) -> max*(bonus/(bonus+halfway))
|
||||
|
||||
api.spells =
|
||||
|
||||
wizard:
|
||||
@@ -219,10 +222,13 @@ api.spells =
|
||||
target: 'task'
|
||||
notes: 'With a crack, flames burst from your staff, scorching a task. You deal high damage to the task, and gain additional experience (more experience for greens).'
|
||||
cast: (user, target) ->
|
||||
target.value += user._statsComputed.int * .0075 * user.fns.crit('per')
|
||||
bonus = (if target.value < 0 then 1 else target.value+1) * 2.5
|
||||
user.stats.exp += bonus
|
||||
user.party.quest.progress.up += bonus if user.party.quest.key
|
||||
# I seriously have no idea what I'm doing here. I'm just mashing buttons until numbers seem right-ish. Anyone know math?
|
||||
bonus = user._statsComputed.int * user.fns.crit('per')
|
||||
target.value += diminishingReturns(bonus*.02, 4)
|
||||
bonus *= Math.ceil ((if target.value < 0 then 1 else target.value+1) *.075)
|
||||
#console.log {bonus, expBonus:bonus,upBonus:bonus*.1}
|
||||
user.stats.exp += diminishingReturns(bonus,75)
|
||||
user.party.quest.progress.up += diminishingReturns(bonus*.1,50,30) if user.party.quest.key
|
||||
|
||||
mpheal:
|
||||
text: 'Ethereal Surge'
|
||||
@@ -337,7 +343,7 @@ api.spells =
|
||||
notes: "You duck into the shadows, pulling up your hood. Many dailies won't find you this night; fewer yet the higher your Perception."
|
||||
cast: (user, target) ->
|
||||
user.stats.buffs.stealth ?= 0
|
||||
user.stats.buffs.stealth = Math.ceil(user._statsComputed.per * .03)
|
||||
user.stats.buffs.stealth += Math.ceil(user._statsComputed.per * .03)
|
||||
|
||||
healer:
|
||||
heal:
|
||||
@@ -462,16 +468,16 @@ _.each api.hatchingPotions, (pot,key) ->
|
||||
_.defaults pot, {key, value: 2, notes: "Pour this on an egg, and it will hatch as a #{pot.text} pet."}
|
||||
|
||||
api.food =
|
||||
Meat: text: 'Meat', target: 'Base'
|
||||
Milk: text: 'Milk', target: 'White'
|
||||
Potatoe: text: 'Potato', target: 'Desert'
|
||||
Strawberry: text: 'Strawberry', target: 'Red'
|
||||
Chocolate: text: 'Chocolate', target: 'Shade'
|
||||
Fish: text: 'Fish', target: 'Skeleton'
|
||||
RottenMeat: text: 'Rotten Meat', target: 'Zombie'
|
||||
CottonCandyPink: text: 'Pink Cotton Candy', target: 'CottonCandyPink'
|
||||
CottonCandyBlue: text: 'Blue Cotton Candy', target: 'CottonCandyBlue'
|
||||
Honey: text: 'Honey', target: 'Golden'
|
||||
Meat: text: 'Meat', target: 'Base', article: ''
|
||||
Milk: text: 'Milk', target: 'White', article: ''
|
||||
Potatoe: text: 'Potato', target: 'Desert', article: 'a '
|
||||
Strawberry: text: 'Strawberry', target: 'Red', article: 'a '
|
||||
Chocolate: text: 'Chocolate', target: 'Shade', article: ''
|
||||
Fish: text: 'Fish', target: 'Skeleton', article: 'a '
|
||||
RottenMeat: text: 'Rotten Meat', target: 'Zombie', article: ''
|
||||
CottonCandyPink: text: 'Pink Cotton Candy', target: 'CottonCandyPink', article: ''
|
||||
CottonCandyBlue: text: 'Blue Cotton Candy', target: 'CottonCandyBlue', article: ''
|
||||
Honey: text: 'Honey', target: 'Golden', article: ''
|
||||
# FIXME what to do with these extra items? Should we add "targets" (plural) for food instead of singular, so we don't have awkward extras?
|
||||
#Cheese: text: 'Cheese', target: 'Golden'
|
||||
#Watermelon: text: 'Watermelon', target: 'Golden'
|
||||
|
||||
@@ -4,6 +4,9 @@ content = require('./content.coffee')
|
||||
|
||||
api = module.exports = {}
|
||||
|
||||
# little helper for large arrays of strings. %w"this that another" equivalent from Rails, I really miss that function
|
||||
$w = (s)->s.split(' ')
|
||||
|
||||
###
|
||||
------------------------------------------------------
|
||||
Time / Day
|
||||
@@ -71,7 +74,7 @@ api.tnl = (lvl) ->
|
||||
{bonus} All the numbers combined for your point bonus (eg, task.value * user.stats.int * critChance, etc)
|
||||
{halfway} (optional) the point at which the graph starts bending
|
||||
###
|
||||
api.diminishingReturns = (bonus, max, halfway=bonus/2) ->
|
||||
api.diminishingReturns = (bonus, max, halfway=max/2) ->
|
||||
max*(bonus/(bonus+halfway))
|
||||
|
||||
api.monod = (bonus, rateOfIncrease, max) ->
|
||||
@@ -163,7 +166,7 @@ api.uuid = ->
|
||||
Even though Mongoose handles task defaults, we want to make sure defaults are set on the client-side before
|
||||
sending up to the server for performance
|
||||
###
|
||||
api.taskDefaults = (task) ->
|
||||
api.taskDefaults = (task={}) ->
|
||||
task.type = 'habit' unless task.type and task.type in ['habit','daily','todo','reward']
|
||||
defaults =
|
||||
id: api.uuid()
|
||||
@@ -347,11 +350,11 @@ api.wrap = (user) ->
|
||||
update: (req, cb) ->
|
||||
_.each req.body, (v,k) ->
|
||||
user.fns.dotSet(k,v);true
|
||||
cb? null, req
|
||||
cb? null, user
|
||||
|
||||
sleep: (req, cb) ->
|
||||
user.preferences.sleep = !user.preferences.sleep
|
||||
cb null, req
|
||||
cb? null, {}
|
||||
|
||||
revive: (req, cb) ->
|
||||
# Reset stats
|
||||
@@ -371,7 +374,7 @@ api.wrap = (user) ->
|
||||
user.items.gear.equipped[item.type] = "#{item.type}_base_0" if user.items.gear.equipped[item.type] is lostItem
|
||||
user.items.gear.costume[item.type] = "#{item.type}_base_0" if user.items.gear.costume[item.type] is lostItem
|
||||
user.markModified? 'items.gear'
|
||||
cb? (if item then {code:200,message:"Your #{item.text} broke."} else null), req
|
||||
cb? (if item then {code:200,message:"Your #{item.text} broke."} else null), user
|
||||
|
||||
reset: (req, cb) ->
|
||||
user.habits = []
|
||||
@@ -392,16 +395,16 @@ api.wrap = (user) ->
|
||||
user.items.gear.owned = {weapon_warrior_0:true}
|
||||
user.markModified? 'items.gear.owned'
|
||||
user.preferences.costume = false
|
||||
cb null, req
|
||||
cb? null, user
|
||||
|
||||
reroll: (req, cb) ->
|
||||
if user.balance < 1
|
||||
return cb {code:401,message: "Not enough gems."}, req
|
||||
return cb? {code:401,message: "Not enough gems."}, req
|
||||
user.balance--
|
||||
_.each user.tasks, (task) ->
|
||||
task.value = 0
|
||||
user.stats.hp = 50
|
||||
cb null, req
|
||||
cb? null, user
|
||||
|
||||
# ------
|
||||
# Tasks
|
||||
@@ -409,34 +412,35 @@ api.wrap = (user) ->
|
||||
|
||||
clearCompleted: (req, cb) ->
|
||||
user.todos = _.where(user.todos, {completed: false})
|
||||
cb null, req
|
||||
cb? null, user.todos
|
||||
|
||||
sortTask: (req, cb) ->
|
||||
{id} = req.params
|
||||
{to, from} = req.query
|
||||
task = user.tasks[id]
|
||||
return cb({code:404, message: "Task not found."}) unless task
|
||||
return cb('?to=__&from=__ are required') unless to? and from?
|
||||
user["#{task.type}s"].splice to, 0, user["#{task.type}s"].splice(from, 1)[0]
|
||||
cb null, req
|
||||
return cb?({code:404, message: "Task not found."}) unless task
|
||||
return cb?('?to=__&from=__ are required') unless to? and from?
|
||||
tasks = user["#{task.type}s"]
|
||||
tasks.splice to, 0, tasks.splice(from, 1)[0]
|
||||
cb? null, tasks
|
||||
|
||||
updateTask: (req, cb) ->
|
||||
return cb?("Task not found") unless user.tasks[req.params?.id]
|
||||
_.merge user.tasks[req.params.id], req.body
|
||||
user.tasks[req.params.id].markModified? 'tags'
|
||||
cb? null, req
|
||||
return cb?("Task not found") unless task = user.tasks[req.params?.id]
|
||||
_.merge task, req.body
|
||||
task.markModified? 'tags'
|
||||
cb? null, task
|
||||
|
||||
deleteTask: (req, cb) ->
|
||||
task = user.tasks[req.params?.id]
|
||||
return cb({code:404,message:'Task not found'}) unless task
|
||||
return cb?({code:404,message:'Task not found'}) unless task
|
||||
i = user[task.type + "s"].indexOf(task)
|
||||
user[task.type + "s"].splice(i, 1) if ~i
|
||||
cb null, req
|
||||
cb? null, {}
|
||||
|
||||
addTask: (req, cb) ->
|
||||
task = api.taskDefaults(req.body)
|
||||
user["#{task.type}s"].unshift(task)
|
||||
cb? null, req
|
||||
cb? null, task
|
||||
task
|
||||
|
||||
# ------
|
||||
@@ -447,19 +451,19 @@ api.wrap = (user) ->
|
||||
{name} = req.body
|
||||
user.tags ?= []
|
||||
user.tags.push({name})
|
||||
cb? null, req
|
||||
cb? null, user.tags
|
||||
|
||||
updateTag: (req, cb) ->
|
||||
tid = req.params.id
|
||||
i = _.findIndex user.tags, {id: tid}
|
||||
return cb('Tag not found', req) if !~i
|
||||
return cb?('Tag not found', req) if !~i
|
||||
user.tags[i].name = req.body.name
|
||||
cb? null, req
|
||||
cb? null, user.tags[i]
|
||||
|
||||
deleteTag: (req, cb) ->
|
||||
tid = req.params.id
|
||||
i = _.findIndex user.tags, {id: tid}
|
||||
return cb('Tag not found', req) if !~i
|
||||
return cb?('Tag not found', req) if !~i
|
||||
tag = user.tags[i]
|
||||
delete user.filters[tag.id]
|
||||
user.tags.splice i, 1
|
||||
@@ -470,7 +474,7 @@ api.wrap = (user) ->
|
||||
|
||||
_.each ['habits','dailys','todos','rewards'], (type) ->
|
||||
user.markModified? type
|
||||
cb null, req
|
||||
cb? null, user.tags
|
||||
|
||||
# ------
|
||||
# Inventory
|
||||
@@ -482,10 +486,10 @@ api.wrap = (user) ->
|
||||
[egg, potion] = pet.split('-')
|
||||
userPets = user.items.pets
|
||||
|
||||
return cb({code:404, message:":pet not found in user.items.pets"}) unless userPets[pet]
|
||||
return cb({code:404, message:":food not found in user.items.food"}) unless user.items.food?[food.key]
|
||||
return cb({code:401, message:"Can't feed this pet."}) if content.specialPets[pet]
|
||||
return cb({code:401, message:"You already have that mount"}) if user.items.mounts[pet] and (userPets[pet] >= 50 or food.key is 'Saddle')
|
||||
return cb?({code:404, message:":pet not found in user.items.pets"}) unless userPets[pet]
|
||||
return cb?({code:404, message:":food not found in user.items.food"}) unless user.items.food?[food.key]
|
||||
return cb?({code:401, message:"Can't feed this pet."}) if content.specialPets[pet]
|
||||
return cb?({code:401, message:"You already have that mount"}) if user.items.mounts[pet] and (userPets[pet] >= 50 or food.key is 'Saddle')
|
||||
|
||||
message = ''
|
||||
evolve = ->
|
||||
@@ -506,18 +510,18 @@ api.wrap = (user) ->
|
||||
if userPets[pet] >= 50 and !user.items.mounts[pet]
|
||||
evolve()
|
||||
user.items.food[food.key]--
|
||||
cb {code:200, message}, req
|
||||
cb? {code:200, message}, userPets[pet]
|
||||
|
||||
# buy is for gear, purchase is for gem-purchaseables (i know, I know...)
|
||||
purchase: (req, cb) ->
|
||||
{type,key} = req.params
|
||||
return cb({code:404,message:":type must be in [hatchingPotions,eggs,food,quests,special]"},req) unless type in ['eggs','hatchingPotions','food','quests','special']
|
||||
return cb?({code:404,message:":type must be in [hatchingPotions,eggs,food,quests,special]"},req) unless type in ['eggs','hatchingPotions','food','quests','special']
|
||||
item = content[type][key]
|
||||
return cb({code:404,message:":key not found for Content.#{type}"},req) unless item
|
||||
return cb?({code:404,message:":key not found for Content.#{type}"},req) unless item
|
||||
user.items[type][key] = 0 unless user.items[type][key]
|
||||
user.items[type][key]++
|
||||
user.balance -= (item.value / 4)
|
||||
cb null, req
|
||||
cb? null, _.pick(user,$w 'items balance')
|
||||
|
||||
# buy is for gear, purchase is for gem-purchaseables (i know, I know...)
|
||||
buy: (req, cb) ->
|
||||
@@ -536,15 +540,15 @@ api.wrap = (user) ->
|
||||
if item.klass in ['warrior','wizard','healer','rogue'] and user.fns.getItem('weapon').last and user.fns.getItem('armor').last and user.fns.getItem('head').last and (user.fns.getItem('shield').last or user.fns.getItem('weapon').twoHanded)
|
||||
user.achievements.ultimateGear = true
|
||||
user.stats.gp -= item.value
|
||||
cb? {code:200, message}, req
|
||||
cb? {code:200, message}, _.pick(user,$w 'items achievements stats')
|
||||
|
||||
sell: (req, cb) ->
|
||||
{key, type} = req.params
|
||||
return cb({code:404,message:":type not found. Must bes in [eggs, hatchingPotions, food]"}) unless type in ['eggs','hatchingPotions', 'food']
|
||||
return cb({code:404,message:":key not found for user.items.#{type}"}) unless user.items[type][key]
|
||||
return cb?({code:404,message:":type not found. Must bes in [eggs, hatchingPotions, food]"}) unless type in ['eggs','hatchingPotions', 'food']
|
||||
return cb?({code:404,message:":key not found for user.items.#{type}"}) unless user.items[type][key]
|
||||
user.items[type][key]--
|
||||
user.stats.gp += content[type][key].value
|
||||
cb? null, req
|
||||
cb? null, _.pick(user,$w 'stats items')
|
||||
|
||||
equip: (req, cb) ->
|
||||
[type, key] = [req.params.type || 'equipped', req.params.key]
|
||||
@@ -557,18 +561,18 @@ api.wrap = (user) ->
|
||||
item = content.gear.flat[key]
|
||||
user.items.gear[type][item.type] = item.key
|
||||
message = user.fns.handleTwoHanded(item,type)
|
||||
cb {code:200,message}, req
|
||||
cb? {code:200,message}, user.items
|
||||
|
||||
hatch: (req, cb) ->
|
||||
{egg, hatchingPotion} = req.params
|
||||
return cb({code:404,message:"Please specify query.egg & query.hatchingPotion"}) unless egg and hatchingPotion
|
||||
return cb({code:401,message:"You're missing either that egg or that potion"}) unless user.items.eggs[egg] > 0 and user.items.hatchingPotions[hatchingPotion] > 0
|
||||
return cb?({code:404,message:"Please specify query.egg & query.hatchingPotion"}) unless egg and hatchingPotion
|
||||
return cb?({code:401,message:"You're missing either that egg or that potion"}) unless user.items.eggs[egg] > 0 and user.items.hatchingPotions[hatchingPotion] > 0
|
||||
pet = "#{egg}-#{hatchingPotion}"
|
||||
return cb("You already have that pet. Try hatching a different combination!") if user.items.pets[pet]
|
||||
return cb?("You already have that pet. Try hatching a different combination!") if user.items.pets[pet]
|
||||
user.items.pets[pet] = 5
|
||||
user.items.eggs[egg]--
|
||||
user.items.hatchingPotions[hatchingPotion]--
|
||||
cb? {code:200, message:"Your egg hatched! Visit your stable to equip your pet."}, req
|
||||
cb? {code:200, message:"Your egg hatched! Visit your stable to equip your pet."}, user.items
|
||||
|
||||
unlock: (req, cb) ->
|
||||
{path} = req.query
|
||||
@@ -587,7 +591,7 @@ api.wrap = (user) ->
|
||||
user.fns.dotSet "purchased." + path, true
|
||||
user.balance -= cost
|
||||
user.markModified? 'purchased'
|
||||
cb? null, req
|
||||
cb? null, _.pick(user,$w 'purchased preferences')
|
||||
|
||||
# ------
|
||||
# Classes
|
||||
@@ -620,14 +624,14 @@ api.wrap = (user) ->
|
||||
# Null ?class value means "reset class"
|
||||
if user.preferences.disableClasses
|
||||
user.preferences.disableClasses = false
|
||||
user.autoAllocate = false
|
||||
user.preferences.autoAllocate = false
|
||||
else
|
||||
return cb({code:401,message:"Not enough gems"}) unless user.balance >= .75
|
||||
return cb?({code:401,message:"Not enough gems"}) unless user.balance >= .75
|
||||
user.balance -= .75
|
||||
_.merge user.stats, {str: 0, con: 0, per: 0, int: 0, points: user.stats.lvl}
|
||||
user.flags.classSelected = false
|
||||
#'stats.points': this is handled on the server
|
||||
cb? null, req
|
||||
cb? null, _.pick(user,$w 'stats flags items preferences')
|
||||
|
||||
disableClasses: (req, cb) ->
|
||||
user.stats.class = 'warrior'
|
||||
@@ -636,7 +640,7 @@ api.wrap = (user) ->
|
||||
user.preferences.autoAllocate = true
|
||||
user.stats.str = user.stats.lvl
|
||||
user.stats.points = 0
|
||||
cb null, req
|
||||
cb? null, _.pick(user,$w 'stats flags preferences')
|
||||
|
||||
allocate: (req, cb) ->
|
||||
stat = req.query.stat or 'str'
|
||||
@@ -644,7 +648,7 @@ api.wrap = (user) ->
|
||||
user.stats[stat]++
|
||||
user.stats.points--
|
||||
user.stats.mp++ if stat is 'int' #increase their MP along with their max MP
|
||||
cb? null, req
|
||||
cb? null, _.pick(user,$w 'stats')
|
||||
|
||||
# ------
|
||||
# Score
|
||||
@@ -665,7 +669,7 @@ api.wrap = (user) ->
|
||||
|
||||
# If they're trying to purhcase a too-expensive reward, don't allow them to do that.
|
||||
if task.value > stats.gp and task.type is 'reward'
|
||||
return cb('Not enough Gold');
|
||||
return cb? 'Not enough Gold'
|
||||
|
||||
delta = 0
|
||||
|
||||
@@ -787,7 +791,7 @@ api.wrap = (user) ->
|
||||
if typeof window is 'undefined'
|
||||
user.fns.randomDrop({task, delta}) if direction is 'up'
|
||||
|
||||
cb? null, req
|
||||
cb? null, user
|
||||
return delta
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
@@ -894,7 +898,7 @@ api.wrap = (user) ->
|
||||
user.items.food[drop.key] ?= 0
|
||||
user.items.food[drop.key]+= 1
|
||||
drop.type = 'Food'
|
||||
drop.dialog = "You've found a #{drop.text} Food! #{drop.notes}"
|
||||
drop.dialog = "You've found #{drop.article}#{drop.text}! #{drop.notes}"
|
||||
|
||||
# Eggs: 30% chance
|
||||
else if rarity > .3
|
||||
|
||||
@@ -71,20 +71,26 @@ angular.module('userServices', []).
|
||||
// Update user
|
||||
_.extend(user, data);
|
||||
if (!user._wrapped){
|
||||
|
||||
// This wraps user with `ops`, which are functions shared both on client and mobile. When performed on client,
|
||||
// they update the user in the browser and then send the request to the server, where the same operation is
|
||||
// replicated. We need to wrap each op to provide a callback to send that operation
|
||||
$window.habitrpgShared.wrap(user);
|
||||
_.each(user.ops, function(op,k){
|
||||
user.ops[k] = _.partialRight(op, function(err, req){
|
||||
if (err) {
|
||||
var message = err.code ? err.message : err;
|
||||
console.log(message);
|
||||
if (MOBILE_APP) Notification.push({type:'text',text:message});
|
||||
else Notification.text(message);
|
||||
// In the case of 200s, they're friendly alert messages like "You're pet has hatched!" - still send the op
|
||||
if ((err.code && err.code >= 400) || !err.code) return;
|
||||
|
||||
}
|
||||
userServices.log({op:k, params: req.params, query:req.query, body:req.body});
|
||||
});
|
||||
user.ops[k] = function(req,cb){
|
||||
if (cb) return op(req,cb);
|
||||
op(req,function(err,response){
|
||||
if (err) {
|
||||
var message = err.code ? err.message : err;
|
||||
console.log(message);
|
||||
if (MOBILE_APP) Notification.push({type:'text',text:message});
|
||||
else Notification.text(message);
|
||||
// In the case of 200s, they're friendly alert messages like "You're pet has hatched!" - still send the op
|
||||
if ((err.code && err.code >= 400) || !err.code) return;
|
||||
}
|
||||
userServices.log({op:k, params: req.params, query:req.query, body:req.body});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ _ = require 'lodash'
|
||||
id = shared.uuid()
|
||||
user =
|
||||
stats: {class: 'warrior', buffs: {per:0,int:0,con:0,str:0}}
|
||||
party: quest: tally: {up:0,down:0}
|
||||
party: quest: key:'evilsanta', progress: {up:0,down:0}
|
||||
items:
|
||||
eggs: {}
|
||||
hatchingPotions: {}
|
||||
@@ -42,12 +42,12 @@ console.log "================================================\n\n"
|
||||
clearUser = (lvl=1) ->
|
||||
_.merge user.stats, {exp:0, gp:0, hp:50, lvl:lvl, str:lvl, con:lvl, per:lvl, int:lvl, mp: 100}
|
||||
_.merge s.buffs, {str:0,con:0,int:0,per:0}
|
||||
_.merge user.party.quest.tally, {up:0,down:0}
|
||||
_.merge user.party.quest.progress, {up:0,down:0}
|
||||
user.items.lastDrop = {count:0}
|
||||
|
||||
_.each [1,50,99], (lvl) ->
|
||||
_.each [1,25,50,75,99], (lvl) ->
|
||||
console.log "[LEVEL #{lvl}] (#{lvl} points in every attr)\n\n"
|
||||
_.each {red:-5,yellow:0,green:5}, (taskVal, color) ->
|
||||
_.each {red:-25,yellow:0,green:35}, (taskVal, color) ->
|
||||
console.log "[task.value = #{taskVal} (#{color})]"
|
||||
console.log "direction\texpΔ\t\thpΔ\tgpΔ\ttask.valΔ\ttask.valΔ bonus\t\tboss-hit"
|
||||
_.each ['up','down'], (direction) ->
|
||||
@@ -56,14 +56,14 @@ _.each [1,50,99], (lvl) ->
|
||||
task.value = taskVal
|
||||
task.type = 'daily' if direction is 'up'
|
||||
delta = user.ops.score params:{id, direction}
|
||||
console.log "#{if direction is 'up' then '↑' else '↓'}\t\t#{s.exp}/#{shared.tnl(s.lvl)}\t\t#{(b4.hp-s.hp).toFixed(1)}\t#{s.gp.toFixed(1)}\t#{delta.toFixed(1)}\t\t#{(task.value-b4.taskVal-delta).toFixed(1)}\t\t\t#{user.party.quest.tally.up.toFixed(1)}"
|
||||
console.log "#{if direction is 'up' then '↑' else '↓'}\t\t#{s.exp}/#{shared.tnl(s.lvl)}\t\t#{(b4.hp-s.hp).toFixed(1)}\t#{s.gp.toFixed(1)}\t#{delta.toFixed(1)}\t\t#{(task.value-b4.taskVal-delta).toFixed(1)}\t\t\t#{user.party.quest.progress.up.toFixed(1)}"
|
||||
|
||||
str = '- [Wizard]'
|
||||
|
||||
task.value = taskVal;clearUser(lvl)
|
||||
b4 = {taskVal}
|
||||
shared.content.spells.wizard.fireball.cast(user,task)
|
||||
str += "\tfireball(task.valΔ:#{(task.value-taskVal).toFixed(1)} exp:#{s.exp.toFixed(1)})"
|
||||
str += "\tfireball(task.valΔ:#{(task.value-taskVal).toFixed(1)} exp:#{s.exp.toFixed(1)} bossHit:#{user.party.quest.progress.up.toFixed(2)})"
|
||||
|
||||
task.value = taskVal;clearUser(lvl)
|
||||
_party = [user, {stats:{mp:0}}]
|
||||
|
||||