Moved scoring functionality into score.coffee, separate it out so i

can start implementing services
This commit is contained in:
Tyler Renelle 2012-07-20 21:39:29 -04:00
parent 2f008533bc
commit 0b58c24e58
5 changed files with 239 additions and 247 deletions

View file

@ -1,5 +1,5 @@
// Generated by CoffeeScript 1.3.3
var Guid, content, derby, get, getHabits, ready, view, _ref;
var Guid, content, derby, get, getHabits, ready, score, view, _ref;
derby = require('derby');
@ -13,6 +13,8 @@ content = require('./content');
Guid = require('guid');
score = require('./score');
view.fn('taskClasses', function(type, completed, value, hideCompleted) {
var classes;
classes = type;
@ -128,15 +130,6 @@ get('/:uidParam?', function(page, model, _arg) {
}
users.set(userId, newUser);
}
model.store.accessControl = true;
model.store.readPathAccess('users.*', function(id, accept) {
console.log("model.writeAccess called with id:" + id);
return accept(id === userId);
});
model.store.writeAccess('*', 'users.*', function(id, accept) {
console.log("model.writeAccess called with id:" + id);
return accept(id === userId);
});
return getHabits(page, model, userId);
});
});
@ -152,7 +145,7 @@ getHabits = function(page, model, userId) {
potion: content.items.potion,
reroll: content.items.reroll
});
model.fn('_tnl', '_user.stats.lvl', function(lvl) {
model.fn('_user._tnl', '_user.stats.lvl', function(lvl) {
return 50 * Math.pow(lvl, 2) - 150 * lvl + 200;
});
model.refList("_habitList", "_user.tasks", "_user.habitIds");
@ -164,7 +157,7 @@ getHabits = function(page, model, userId) {
};
ready(function(model) {
var endOfDayTally, expModifier, hpModifier, poormanscron, setupSortable, step, tour, type, updateStats, _i, _j, _len, _len1, _ref1, _ref2;
var endOfDayTally, poormanscron, setupSortable, step, tour, type, _i, _j, _len, _len1, _ref1, _ref2;
model.set('_purl', window.location.origin + '/' + model.get('_userId'));
$('[rel=popover]').popover();
model.on('set', '*', function() {
@ -323,59 +316,8 @@ ready(function(model) {
return _results;
}
};
exports.updateStats = updateStats = function(user, stats) {
var money, tnl;
if (stats.hp != null) {
if (stats.hp < 0) {
user.set('stats.lvl', 0);
} else {
user.set('stats.hp', stats.hp);
}
}
if (stats.exp != null) {
tnl = model.get('_tnl');
if (stats.exp >= tnl) {
stats.exp -= tnl;
user.set('stats.lvl', user.get('stats.lvl') + 1);
}
if (!user.get('items.itemsEnabled') && stats.exp >= 50) {
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);
}
};
expModifier = function(value) {
var dmg, modified, user;
user = model.at('_user');
dmg = user.get('items.weapon') * .03;
dmg += user.get('stats.lvl') * .03;
modified = value + (value * dmg);
return modified;
};
hpModifier = function(value) {
var ac, modified, user;
user = model.at('_user');
ac = user.get('items.armor') * .03;
ac += user.get('stats.lvl') * .03;
modified = value - (value * ac);
return modified;
};
exports.vote = function(e, el, next) {
var adjustvalue, completed, delta, direction, exp, hp, lvl, money, sign, task, user, value, _ref3;
var direction, task, user;
direction = $(el).attr('data-direction');
if (direction === 'true/') {
direction = 'up';
@ -383,63 +325,12 @@ ready(function(model) {
if (direction === 'false/') {
direction = 'down';
}
task = model.at($(el).parents('li')[0]);
user = model.at('_user');
sign = direction === "up" ? 1 : -1;
value = task.get('value');
delta = 0;
if (value < 0) {
delta = (-0.1 * value + 1) * sign;
} else {
delta = (Math.pow(0.9, value)) * sign;
}
adjustvalue = task.get('type') !== 'reward';
if ((task.get('type') === 'habit') && (task.get("up") === false || task.get("down") === false)) {
adjustvalue = false;
}
if (adjustvalue) {
value += delta;
}
completed = task.get("completed");
if (task.get('type') !== 'habit') {
if (direction === "up") {
completed = true;
}
if (direction === "down") {
completed = false;
}
} else {
if (task.get('value') !== value) {
task.push('history', {
date: new Date(),
value: value
});
}
}
task.set('value', value);
task.set('completed', completed);
_ref3 = [user.get('stats.money'), user.get('stats.hp'), user.get('stats.exp'), user.get('stats.lvl')], money = _ref3[0], hp = _ref3[1], exp = _ref3[2], lvl = _ref3[3];
if (task.get('type') === 'reward') {
money -= task.get('value');
if (money < 0) {
hp += money;
money = 0;
}
}
if (delta > 0 || (task.get('type') === 'daily' || task.get('type') === 'todo')) {
exp += expModifier(delta);
money += delta;
} else if (task.get('type') !== 'reward') {
hp += hpModifier(delta);
}
return updateStats(user, {
hp: hp,
exp: exp,
money: money
});
task = model.at($(el).parents('li')[0]);
return score(user, task, direction);
};
exports.endOfDayTally = endOfDayTally = function(e, el) {
var absVal, completed, expTally, hp, key, lvl, task, todoTally, user, value, _ref3;
var absVal, completed, expTally, key, lvl, task, todoTally, user, value, _ref3;
user = model.at('_user');
todoTally = 0;
for (key in model.get('_user.tasks')) {
@ -447,14 +338,7 @@ ready(function(model) {
_ref3 = [task.get('type'), task.get('value'), task.get('completed')], type = _ref3[0], value = _ref3[1], completed = _ref3[2];
if (type === 'todo' || type === 'daily') {
if (!completed) {
value += value < 0 ? (-0.1 * value + 1) * -1 : (Math.pow(0.9, value)) * -1;
task.set('value', value);
if (type === 'daily') {
hp = user.get('stats.hp') + hpModifier(value);
updateStats(user, {
hp: hp
});
}
score(user, task, 'down', true);
}
if (type === 'daily') {
task.push("history", {

109
lib/app/score.js Normal file
View file

@ -0,0 +1,109 @@
// Generated by CoffeeScript 1.3.3
var content, expModifier, hpModifier, updateStats;
content = require('./content');
expModifier = function(user, value) {
var dmg, modified;
dmg = user.get('items.weapon') * .03;
dmg += user.get('stats.lvl') * .03;
modified = value + (value * dmg);
return modified;
};
hpModifier = function(user, value) {
var ac, modified;
ac = user.get('items.armor') * .03;
ac += user.get('stats.lvl') * .03;
modified = value - (value * ac);
return modified;
};
updateStats = function(user, stats) {
var money, tnl;
if (stats.hp != null) {
if (stats.hp < 0) {
user.set('stats.lvl', 0);
} 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);
}
if (!user.get('items.itemsEnabled') && stats.exp >= 50) {
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);
}
};
module.exports = function(user, task, direction, cron) {
var adjustvalue, completed, delta, exp, hp, lvl, money, sign, type, value, _ref;
sign = direction === "up" ? 1 : -1;
value = task.get('value');
delta = value < 0 ? (-0.1 * value + 1) * sign : (Math.pow(0.9, value)) * sign;
type = task.get('type');
adjustvalue = type !== 'reward';
if ((type === 'habit') && (task.get("up") === false || task.get("down") === false)) {
adjustvalue = false;
}
if (adjustvalue) {
value += delta;
}
completed = task.get("completed");
if (type !== 'habit') {
if (direction === "up") {
completed = true;
}
if (direction === "down") {
completed = false;
}
} else {
if (task.get('value') !== value) {
task.push('history', {
date: new Date(),
value: value
});
}
}
task.set('value', value);
task.set('completed', completed);
_ref = [user.get('stats.money'), user.get('stats.hp'), user.get('stats.exp'), user.get('stats.lvl')], money = _ref[0], hp = _ref[1], exp = _ref[2], lvl = _ref[3];
if (type === 'reward') {
money -= task.get('value');
if (money < 0) {
hp += money;
money = 0;
}
}
if (delta > 0 || ((type === 'daily' || type === 'todo') && !cron)) {
exp += expModifier(user, delta);
money += delta;
} else if (type !== 'reward' && (type !== 'todo' && cron)) {
hp += hpModifier(user, delta);
}
updateStats(user, {
hp: hp,
exp: exp,
money: money
});
return delta;
};

View file

@ -4,6 +4,7 @@ derby.use require('derby-ui-boot')
derby.use(require('../../ui'))
content = require('./content')
Guid = require('guid')
score = require('./score')
## VIEW HELPERS ##
view.fn 'taskClasses', (type, completed, value, hideCompleted) ->
@ -76,14 +77,14 @@ get '/:uidParam?', (page, model, {uidParam}) ->
when 'reward' then newUser.rewardIds.push guid
users.set userId, newUser
#TODO these *Access functions aren't being called, why?
model.store.accessControl = true
model.store.readPathAccess 'users.*', (id, accept) ->
console.log "model.writeAccess called with id:#{id}" # never called
accept(id == userId)
model.store.writeAccess '*', 'users.*', (id, accept) ->
console.log "model.writeAccess called with id:#{id}" # never called
accept(id == userId)
# #TODO these *Access functions aren't being called, why?
# model.store.accessControl = true
# model.store.readPathAccess 'users.*', (id, accept) ->
# console.log "model.writeAccess called with id:#{id}" # never called
# accept(id == userId)
# model.store.writeAccess '*', 'users.*', (id, accept) ->
# console.log "model.writeAccess called with id:#{id}" # never called
# accept(id == userId)
getHabits(page, model, userId)
@ -104,7 +105,7 @@ getHabits = (page, model, userId) ->
reroll: content.items.reroll
# http://tibia.wikia.com/wiki/Formula
model.fn '_tnl', '_user.stats.lvl', (lvl) -> 50 * Math.pow(lvl, 2) - 150 * lvl + 200
model.fn '_user._tnl', '_user.stats.lvl', (lvl) -> 50 * Math.pow(lvl, 2) - 150 * lvl + 200
# Default Tasks
model.refList "_habitList", "_user.tasks", "_user.habitIds"
@ -255,113 +256,15 @@ ready (model) ->
task = model.at('_user.tasks.'+taskId)
task.set('value', 0) unless task.get('type')=='reward'
# Setter for user.stats: handles death, leveling up, etc
exports.updateStats = updateStats = (user, stats) ->
if stats.hp?
# game over
if stats.hp < 0
user.set 'stats.lvl', 0 # this signifies dead
else
user.set 'stats.hp', stats.hp
if stats.exp?
# level up & carry-over exp
tnl = model.get '_tnl'
if stats.exp >= tnl
stats.exp -= tnl
user.set 'stats.lvl', user.get('stats.lvl') + 1
if !user.get('items.itemsEnabled') and stats.exp >=50
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?
money = 0.0 if (!money? or money<0)
user.set 'stats.money', stats.money
# Calculates Exp modification based on weapon & lvl
expModifier = (value) ->
user = model.at '_user'
dmg = user.get('items.weapon') * .03 # each new weapon adds an additional 3% experience
dmg += user.get('stats.lvl') * .03 # same for lvls
modified = value + (value * dmg)
return modified
# Calculates HP-loss modification based on armor & lvl
hpModifier = (value) ->
user = model.at '_user'
ac = user.get('items.armor') * .03 # each new armor blocks an additional 3% damage
ac += user.get('stats.lvl') * .03 # same for lvls
modified = value - (value * ac)
return modified
exports.vote = (e, el, next) ->
direction = $(el).attr('data-direction')
direction = 'up' if direction == 'true/'
direction = 'down' if direction == 'false/'
#TODO this should be model.at(el), shouldn't have to find parent
user = model.at('_user')
task = model.at $(el).parents('li')[0]
user = model.at '_user'
# 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
sign = if (direction == "up") then 1 else -1
value = task.get('value')
delta = 0
if value < 0
delta = (( -0.1 * value + 1 ) * sign)
else
delta = (( Math.pow(0.9, value) ) * sign)
# Don't adjust values for rewards, or for habits that don't have both + and -
adjustvalue = (task.get('type') != 'reward')
if (task.get('type') == 'habit') and (task.get("up")==false or task.get("down")==false)
adjustvalue = false
value += delta if adjustvalue
# up/down -voting as checkbox & assigning as completed, 2 birds one stone
completed = task.get("completed")
if task.get('type') != 'habit'
completed = true if direction=="up"
completed = false if direction=="down"
else
# Add habit value to habit-history (if different)
task.push 'history', { date: new Date(), value: value } if task.get('value') != value
task.set('value', value)
task.set('completed', completed)
# Update the user's status
[money, hp, exp, lvl] = [user.get('stats.money'), user.get('stats.hp'), user.get('stats.exp'), user.get('stats.lvl')]
if task.get('type') == 'reward'
# purchase item
money -= task.get('value')
# if too expensive, reduce health & zero money
if money < 0
hp += money # hp - money difference
money = 0
# If positive delta, add points to exp & money
# Only take away mony if it was a mistake (aka, a checkbox)
if delta > 0 or (task.get('type') == 'daily' or task.get('type') == 'todo')
exp += expModifier(delta)
money += delta
# Deduct from health (rewards case handled above)
else if task.get('type') != 'reward'
hp += hpModifier(delta)
updateStats(user, {hp: hp, exp: exp, money: money})
score(user, task, direction)
# Note: Set 12am daily cron for this
# At end of day, add value to all incomplete Daily & Todo tasks (further incentive)
@ -375,15 +278,10 @@ ready (model) ->
for key of model.get '_user.tasks'
task = model.at "_user.tasks.#{key}"
[type, value, completed] = [task.get('type'), task.get('value'), task.get('completed')]
if type == 'todo' or type == 'daily'
unless completed
value += if (value < 0) then (( -0.1 * value + 1 ) * -1) else (( Math.pow(0.9,value) ) * -1)
task.set('value', value)
# Deduct experience for missed Daily tasks,
# but not for Todos (just increase todo's value)
if (type == 'daily')
hp = user.get('stats.hp') + hpModifier(value)
updateStats user, { hp: hp }
if type in ['todo', 'daily']
# Deduct experience for missed Daily tasks,
# but not for Todos (just increase todo's value)
score(user, task, 'down', true) unless completed
if type == 'daily'
task.push "history", { date: new Date(), value: value }
else

101
src/app/score.coffee Normal file
View file

@ -0,0 +1,101 @@
content = require('./content')
# Calculates Exp modification based on weapon & lvl
expModifier = (user, value) ->
dmg = user.get('items.weapon') * .03 # each new weapon adds an additional 3% experience
dmg += user.get('stats.lvl') * .03 # same for lvls
modified = value + (value * dmg)
return modified
# Calculates HP-loss modification based on armor & lvl
hpModifier = (user, value) ->
ac = user.get('items.armor') * .03 # each new armor blocks an additional 3% damage
ac += user.get('stats.lvl') * .03 # same for lvls
modified = value - (value * ac)
return modified
# Setter for user.stats: handles death, leveling up, etc
updateStats = (user, stats) ->
if stats.hp?
# game over
if stats.hp < 0
user.set 'stats.lvl', 0 # this signifies dead
else
user.set 'stats.hp', stats.hp
if stats.exp?
# level up & carry-over exp
tnl = user.get '_tnl'
if stats.exp >= tnl
stats.exp -= tnl
user.set 'stats.lvl', user.get('stats.lvl') + 1
if !user.get('items.itemsEnabled') and stats.exp >=50
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?
money = 0.0 if (!money? or money<0)
user.set 'stats.money', stats.money
module.exports = (user, task, direction, cron) ->
# 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
sign = if (direction == "up") then 1 else -1
value = task.get('value')
delta = if (value < 0) then (( -0.1 * value + 1 ) * sign) else (( Math.pow(0.9,value) ) * sign)
type = task.get('type')
# Don't adjust values for rewards, or for habits that don't have both + and -
adjustvalue = (type != 'reward')
if (type == 'habit') and (task.get("up")==false or task.get("down")==false)
adjustvalue = false
value += delta if adjustvalue
# up/down -voting as checkbox & assigning as completed, 2 birds one stone
completed = task.get("completed")
if type != 'habit'
completed = true if direction=="up"
completed = false if direction=="down"
else
# Add habit value to habit-history (if different)
task.push 'history', { date: new Date(), value: value } if task.get('value') != value
task.set('value', value)
task.set('completed', completed)
# Update the user's status
[money, hp, exp, lvl] = [user.get('stats.money'), user.get('stats.hp'), user.get('stats.exp'), user.get('stats.lvl')]
if type == 'reward'
# purchase item
money -= task.get('value')
# if too expensive, reduce health & zero money
if money < 0
hp += money # hp - money difference
money = 0
# Add points to exp & money if positive delta
# Only take away mony if it was a mistake (aka, a checkbox)
if delta > 0 or ( type in ['daily', 'todo'] and !cron )
exp += expModifier(user, delta)
money += delta
# Deduct from health (rewards case handled above)
else if type != 'reward' and ( type!='todo' and cron )
hp += hpModifier(user, delta)
updateStats(user, {hp: hp, exp: exp, money: money})
return delta

View file

@ -38,9 +38,9 @@
{#if _user.history.exp}
<a x-bind=click:toggleChart data-toggle-id="exp-chart" data-history-path="_user.history.exp"><i class=icon-signal></i></a>&nbsp;
{/}
Exp: {round(_user.stats.exp)} / {_tnl}
Exp: {round(_user.stats.exp)} / {_user._tnl}
</span>
<div class="bar" style="width: {percent(_user.stats.exp,_tnl)}%;"></div>
<div class="bar" style="width: {percent(_user.stats.exp,_user._tnl)}%;"></div>
</div>
</td>
</tr>