mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-19 15:48:04 +01:00
341 lines
9.2 KiB
JavaScript
341 lines
9.2 KiB
JavaScript
// Generated by CoffeeScript 1.4.0
|
|
var MODIFIER, async, content, cron, expModifier, helpers, hpModifier, model, moment, score, setModel, setupNotifications, taskDeltaFormula, updateStats, user, _;
|
|
|
|
async = require('async');
|
|
|
|
moment = require('moment');
|
|
|
|
_ = require('underscore');
|
|
|
|
content = require('./content');
|
|
|
|
helpers = require('./helpers');
|
|
|
|
MODIFIER = .03;
|
|
|
|
user = void 0;
|
|
|
|
model = void 0;
|
|
|
|
setModel = function(m) {
|
|
model = m;
|
|
user = model.at('_user');
|
|
if (!model.get('_mobileDevice')) {
|
|
return setupNotifications();
|
|
}
|
|
};
|
|
|
|
setupNotifications = function() {
|
|
var statsNotification;
|
|
if (typeof jQuery === "undefined" || jQuery === null) {
|
|
return;
|
|
}
|
|
statsNotification = function(html, type) {
|
|
if (user.get('stats.lvl') === 0) {
|
|
return;
|
|
}
|
|
return $.bootstrapGrowl(html, {
|
|
type: type,
|
|
top_offset: 20,
|
|
align: 'right',
|
|
width: 250,
|
|
delay: 3000,
|
|
allow_dismiss: true,
|
|
stackup_spacing: 10
|
|
});
|
|
};
|
|
user.on('set', 'stats.hp', function(captures, args) {
|
|
var num, rounded;
|
|
num = captures - args;
|
|
rounded = Math.abs(num.toFixed(1));
|
|
if (num < 0) {
|
|
return statsNotification("<i class='icon-heart'></i>HP -" + rounded, 'error');
|
|
}
|
|
});
|
|
user.on('set', 'stats.money', function(captures, args) {
|
|
var num, rounded;
|
|
num = captures - args;
|
|
rounded = Math.abs(num.toFixed(1));
|
|
if (num < 0) {
|
|
return statsNotification("<i class='icon-star'></i>GP -" + rounded, 'success');
|
|
} else if (num > 0) {
|
|
num = Math.abs(num);
|
|
return statsNotification("<i class='icon-star'></i>Exp,GP +" + rounded, 'success');
|
|
}
|
|
});
|
|
return user.on('set', 'stats.lvl', function(captures, args) {
|
|
if (captures > args) {
|
|
return statsNotification('<i class="icon-chevron-up"></i> Level Up!', 'info');
|
|
}
|
|
});
|
|
};
|
|
|
|
/*
|
|
Calculates Exp & GP modification based on weapon & lvl
|
|
{value} task.value for gain
|
|
{modifiers} may manually pass in stats as {weapon, exp}. This is used for testing
|
|
*/
|
|
|
|
|
|
expModifier = function(value, modifiers) {
|
|
var dmg, lvl, modified, weapon;
|
|
if (modifiers == null) {
|
|
modifiers = {};
|
|
}
|
|
weapon = modifiers.weapon || user.get('items.weapon');
|
|
lvl = modifiers.lvl || user.get('stats.lvl');
|
|
dmg = weapon * MODIFIER;
|
|
dmg += (lvl - 1) * MODIFIER;
|
|
modified = value + (value * dmg);
|
|
return modified;
|
|
};
|
|
|
|
/*
|
|
Calculates HP-loss modification based on armor & lvl
|
|
{value} task.value which is hurting us
|
|
{modifiers} may manually pass in modifier as {armor, lvl}. This is used for testing
|
|
*/
|
|
|
|
|
|
hpModifier = function(value, modifiers) {
|
|
var ac, armor, lvl, modified;
|
|
if (modifiers == null) {
|
|
modifiers = {};
|
|
}
|
|
armor = modifiers.armor || user.get('items.armor');
|
|
lvl = modifiers.lvl || user.get('stats.lvl');
|
|
ac = armor * MODIFIER;
|
|
ac += (lvl - 1) * MODIFIER;
|
|
modified = value - (value * ac);
|
|
return modified;
|
|
};
|
|
|
|
/*
|
|
Calculates the next task.value based on direction
|
|
For negative values, use a line: something like y=-.1x+1
|
|
For positibe values, taper off with inverse log: y=.9^x
|
|
Would love to use inverse log for the whole thing, but after 13 fails it hits infinity. Revisit this formula later
|
|
{currentValue} the current value of the task, determines it's next value
|
|
{direction} 'up' or 'down'
|
|
*/
|
|
|
|
|
|
taskDeltaFormula = function(currentValue, direction) {
|
|
var delta, sign;
|
|
sign = direction === "up" ? 1 : -1;
|
|
delta = currentValue < 0 ? (-0.1 * currentValue + 1) * sign : (Math.pow(0.9, currentValue)) * sign;
|
|
return delta;
|
|
};
|
|
|
|
updateStats = function(stats) {
|
|
var money, tnl;
|
|
if (user.get('stats.lvl') === 0) {
|
|
return;
|
|
}
|
|
if (stats.hp != null) {
|
|
if (stats.hp <= 0) {
|
|
user.set('stats.lvl', 0);
|
|
user.set('stast.hp', 0);
|
|
return;
|
|
} else {
|
|
user.set('stats.hp', stats.hp);
|
|
}
|
|
}
|
|
if (stats.exp != null) {
|
|
tnl = user.get('_tnl');
|
|
if (stats.exp >= tnl) {
|
|
stats.exp -= tnl;
|
|
user.set('stats.lvl', user.get('stats.lvl') + 1);
|
|
user.set('stats.hp', 50);
|
|
}
|
|
if (!user.get('items.itemsEnabled') && stats.exp >= 15) {
|
|
user.set('items.itemsEnabled', true);
|
|
$('ul.items').popover({
|
|
title: content.items.unlockedMessage.title,
|
|
placement: 'left',
|
|
trigger: 'manual',
|
|
html: true,
|
|
content: "<div class='item-store-popover'> <img src='/img/BrowserQuest/chest.png' /> " + content.items.unlockedMessage.content + " <a href='#' onClick=\"$('ul.items').popover('hide');return false;\">[Close]</a> </div>"
|
|
});
|
|
$('ul.items').popover('show');
|
|
}
|
|
user.set('stats.exp', stats.exp);
|
|
}
|
|
if (stats.money != null) {
|
|
if (!(typeof money !== "undefined" && money !== null) || money < 0) {
|
|
money = 0.0;
|
|
}
|
|
return user.set('stats.money', stats.money);
|
|
}
|
|
};
|
|
|
|
score = function(taskId, direction, options) {
|
|
var adjustvalue, delta, exp, hp, lvl, modified, money, num, task, taskObj, taskPath, type, userObj, value, _ref, _ref1, _ref2;
|
|
if (options == null) {
|
|
options = {
|
|
cron: false,
|
|
times: 1
|
|
};
|
|
}
|
|
taskPath = "_user.tasks." + taskId;
|
|
_ref = [model.at(taskPath), model.get(taskPath)], task = _ref[0], taskObj = _ref[1];
|
|
type = taskObj.type, value = taskObj.value;
|
|
userObj = user.get();
|
|
if (!task) {
|
|
_ref1 = userObj.stats, money = _ref1.money, hp = _ref1.hp, exp = _ref1.exp;
|
|
if (direction === "up") {
|
|
modified = expModifier(1);
|
|
money += modified;
|
|
exp += modified;
|
|
} else {
|
|
modified = hpModifier(1);
|
|
hp -= modified;
|
|
}
|
|
updateStats({
|
|
hp: hp,
|
|
exp: exp,
|
|
money: money
|
|
});
|
|
return;
|
|
}
|
|
adjustvalue = type !== 'reward';
|
|
if ((type === 'habit') && (taskObj.up === false || taskObj.down === false)) {
|
|
adjustvalue = false;
|
|
}
|
|
delta = 0;
|
|
_.times(options.times, function(n) {
|
|
var nextDelta;
|
|
nextDelta = taskDeltaFormula(value, direction);
|
|
if (adjustvalue) {
|
|
value += nextDelta;
|
|
}
|
|
return delta += nextDelta;
|
|
});
|
|
if (type === 'habit') {
|
|
if (taskObj.value !== value) {
|
|
task.push('history', {
|
|
date: new Date(),
|
|
value: value
|
|
});
|
|
}
|
|
}
|
|
task.set('value', value);
|
|
if (options.cron) {
|
|
if (type === 'daily') {
|
|
return delta;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
_ref2 = userObj.stats, money = _ref2.money, hp = _ref2.hp, exp = _ref2.exp, lvl = _ref2.lvl;
|
|
if (type === 'reward') {
|
|
money -= task.get('value');
|
|
num = parseFloat(task.get('value')).toFixed(2);
|
|
if (money < 0) {
|
|
hp += money;
|
|
money = 0;
|
|
}
|
|
}
|
|
if ((delta > 0 || (type === 'daily' || type === 'todo')) && !options.cron) {
|
|
modified = expModifier(delta);
|
|
exp += modified;
|
|
money += modified;
|
|
} else if (type !== 'reward' && type !== 'todo') {
|
|
modified = hpModifier(delta);
|
|
hp += modified;
|
|
}
|
|
updateStats({
|
|
hp: hp,
|
|
exp: exp,
|
|
money: money
|
|
});
|
|
return delta;
|
|
};
|
|
|
|
cron = function() {
|
|
var daysPassed, hpTally, lastCron, tallyTask, tasks, today, todoTally;
|
|
today = new Date();
|
|
user.setNull('lastCron', today);
|
|
lastCron = user.get('lastCron');
|
|
daysPassed = helpers.daysBetween(today, lastCron);
|
|
if (daysPassed > 0) {
|
|
todoTally = 0;
|
|
hpTally = 0;
|
|
tallyTask = function(taskObj, callback) {
|
|
var absVal, completed, daysFailed, id, repeat, task, type, value;
|
|
id = taskObj.id, type = taskObj.type, completed = taskObj.completed, repeat = taskObj.repeat;
|
|
if (id == null) {
|
|
return callback('a task had a null id during cron, this should not be happening');
|
|
}
|
|
task = user.at("tasks." + id);
|
|
if (type === 'todo' || type === 'daily') {
|
|
if (!completed) {
|
|
daysFailed = daysPassed;
|
|
if (type === 'daily' && repeat) {
|
|
daysFailed = 0;
|
|
_.times(daysPassed, function(n) {
|
|
var thatDay;
|
|
thatDay = moment().subtract('days', n + 1);
|
|
if (repeat[helpers.dayMapping[thatDay.day()]] === true) {
|
|
return daysFailed++;
|
|
}
|
|
});
|
|
}
|
|
hpTally += score(id, 'down', {
|
|
cron: true,
|
|
times: daysFailed
|
|
});
|
|
}
|
|
value = task.get('value');
|
|
if (type === 'daily') {
|
|
task.push("history", {
|
|
date: today,
|
|
value: value
|
|
});
|
|
} else {
|
|
absVal = completed ? Math.abs(value) : value;
|
|
todoTally += absVal;
|
|
}
|
|
if (type === 'daily') {
|
|
task.pass({
|
|
cron: true
|
|
}).set('completed', false);
|
|
}
|
|
}
|
|
return callback();
|
|
};
|
|
tasks = _.toArray(user.get('tasks'));
|
|
return async.forEach(tasks, tallyTask, function(err) {
|
|
var expTally, lvl;
|
|
user.push('history.todos', {
|
|
date: today,
|
|
value: todoTally
|
|
});
|
|
expTally = user.get('stats.exp');
|
|
lvl = 0;
|
|
while (lvl < (user.get('stats.lvl') - 1)) {
|
|
lvl++;
|
|
expTally += (lvl * 100) / 5;
|
|
}
|
|
user.push('history.exp', {
|
|
date: today,
|
|
value: expTally
|
|
});
|
|
updateStats({
|
|
hp: user.get('stats.hp') + hpTally
|
|
});
|
|
return user.set('lastCron', today);
|
|
});
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
setModel: setModel,
|
|
MODIFIER: MODIFIER,
|
|
score: score,
|
|
cron: cron,
|
|
expModifier: expModifier,
|
|
hpModifier: hpModifier,
|
|
taskDeltaFormula: taskDeltaFormula
|
|
};
|