Quarters system, generalized modal dialogs, bug fixes

This commit is contained in:
Tyler Renelle 2012-09-18 21:37:49 -04:00
parent 694691f109
commit be9d419313
11 changed files with 169 additions and 70 deletions

View file

@ -208,11 +208,7 @@ module.exports = {
type: 'reroll',
text: "Re-Roll",
icon: 'favicon',
notes: "Reset your tasks. When you're struggling and everything's red, use for a clean slate. Use sparingly, it costs real money.",
rerollWarning: {
title: "This item costs real money",
content: "Each use is $1, can use 5 times before checkout."
},
notes: "Resets your tasks. When you're struggling and everything's red, use for a clean slate.",
value: 0
}
}

View file

@ -68,11 +68,24 @@ module.exports.viewHelpers = function(view) {
return "0";
}
});
return view.fn("silver", function(num) {
view.fn("silver", function(num) {
if (num) {
return num.toFixed(1).split('.')[1];
} else {
return "0";
}
});
view.fn("money", function(num) {
if (num) {
return num.toFixed(2);
} else {
return "0.00";
}
});
view.fn("lessThan", function(a, b) {
return a < b;
});
return view.fn("quarters", function(money) {
return money / 0.25;
});
};

View file

@ -32,6 +32,7 @@ get('/:uidParam?', function(page, model, _arg, next) {
model.set('_userId', sess.userId);
return model.subscribe("users." + sess.userId, function(err, user) {
model.ref('_user', user);
user.setNull('balance', 2);
model.set('_items', {
armor: content.items.armor[parseInt(user.get('items.armor')) + 1],
weapon: content.items.weapon[parseInt(user.get('items.weapon')) + 1],
@ -51,9 +52,7 @@ get('/:uidParam?', function(page, model, _arg, next) {
});
ready(function(model) {
var pathParts, poormanscron, setupSortable, step, tour, type, _i, _j, _len, _len1, _ref1, _ref2;
pathParts = window.location.toString().split('/');
$('#purl').val("" + pathParts[0] + "//" + pathParts[2] + "/" + (model.get('_userId')));
var poormanscron, setupSortable, step, tour, type, _i, _j, _len, _len1, _ref1, _ref2;
$('[rel=tooltip]').tooltip();
$('[rel=popover]').popover();
model.on('set', '*', function() {
@ -309,7 +308,8 @@ ready(function(model) {
model.set('_user.items.armor', 0);
model.set('_user.items.weapon', 0);
model.set('_items.armor', content.items.armor[1]);
return model.set('_items.weapon', content.items.weapon[1]);
model.set('_items.weapon', content.items.weapon[1]);
return model.set('_user.balance', model.get('_user.balance') - 0.50);
};
exports.poormanscron = poormanscron = function() {
var daysPassed, lastCron, today;

View file

@ -120,7 +120,7 @@ module.exports.score = score = function(spec) {
_ref2 = [user.get('stats.money'), user.get('stats.hp'), user.get('stats.exp'), user.get('stats.lvl')], money = _ref2[0], hp = _ref2[1], exp = _ref2[2], lvl = _ref2[3];
if (type === 'reward') {
money -= task.get('value');
num = task.get('value').toFixed(2);
num = parseFloat(task.get('value')).toFixed(2);
statsNotification("<i class='icon-star'></i>GP -" + num, 'success');
if (money < 0) {
hp += money;

View file

@ -42,8 +42,8 @@ module.exports.newUserAndPurl = function() {
}
model.set("users." + sess.userId, newUser);
}
acceptableUid = require('guid').isGuid(uidParam) || (uidParam === '3' || uidParam === '9');
if (acceptableUid && sess.userId !== uidParam) {
acceptableUid = require('guid').isGuid(uidParam) || (uidParam === '3');
if (acceptableUid && sess.userId !== uidParam && !(sess.habitRpgAuth && sess.habitRpgAuth.facebook)) {
return sess.userId = uidParam;
}
};
@ -53,7 +53,7 @@ module.exports.setupEveryauth = function(everyauth) {
everyauth.everymodule.findUserById(function(id, callback) {
return callback(null, null);
});
return everyauth.facebook.appId(process.env.FACEBOOK_KEY).appSecret(process.env.FACEBOOK_SECRET).findOrCreateUser(function(session, accessToken, accessTokenExtra, fbUserMetadata) {
everyauth.facebook.appId(process.env.FACEBOOK_KEY).appSecret(process.env.FACEBOOK_SECRET).findOrCreateUser(function(session, accessToken, accessTokenExtra, fbUserMetadata) {
var model, q;
session.habitRpgAuth || (session.habitRpgAuth = {});
session.habitRpgAuth.facebook = fbUserMetadata.id;
@ -78,6 +78,14 @@ module.exports.setupEveryauth = function(everyauth) {
});
return fbUserMetadata;
}).redirectPath("/");
return everyauth.everymodule.handleLogout(function(req, res) {
if (req.session.habitRpgAuth && req.session.habitRpgAuth.facebook) {
req.session.habitRpgAuth.facebook = void 0;
}
req.session.userId = void 0;
req.logout();
return this.redirect(res, this.logoutRedirectPath());
});
};
module.exports.setupQueries = function(store) {

View file

@ -81,10 +81,7 @@ module.exports = {
type: 'reroll'
text: "Re-Roll"
icon: 'favicon'
notes: "Reset your tasks. When you're struggling and everything's red, use for a clean slate. Use sparingly, it costs real money."
rerollWarning:
title: "This item costs real money"
content: "Each use is $1, can use 5 times before checkout."
notes: "Resets your tasks. When you're struggling and everything's red, use for a clean slate."
value:0
}

View file

@ -46,3 +46,15 @@ module.exports.viewHelpers = (view) ->
num.toFixed(1).split('.')[1]
else
return "0"
view.fn "money", (num) ->
if num
return num.toFixed(2)
else
return "0.00"
view.fn "lessThan", (a, b) ->
a < b
view.fn "quarters", (money) ->
return money/0.25

View file

@ -25,6 +25,8 @@ get '/:uidParam?', (page, model, {uidParam}, next) ->
model.subscribe "users.#{sess.userId}", (err, user) ->
model.ref '_user', user
user.setNull 'balance', 2
# Store
model.set '_items'
armor: content.items.armor[parseInt(user.get('items.armor')) + 1]
@ -234,7 +236,6 @@ ready (model) ->
direction = 'down' if direction == 'false/'
user = model.at('_user')
task = model.at $(el).parents('li')[0]
scoring.score({user:user, task:task, direction:direction})
exports.revive = (e, el) ->
@ -244,6 +245,7 @@ ready (model) ->
model.set '_user.items.weapon', 0
model.set '_items.armor', content.items.armor[1]
model.set '_items.weapon', content.items.weapon[1]
model.set '_user.balance', (model.get('_user.balance') - 0.50)
# ========== CRON ==========

View file

@ -107,7 +107,7 @@ module.exports.score = score = (spec = {user:null, task:null, direction:null, cr
if type == 'reward'
# purchase item
money -= task.get('value')
num = task.get('value').toFixed(2)
num = parseFloat(task.get('value')).toFixed(2)
statsNotification "<i class='icon-star'></i>GP -#{num}", 'success'
# if too expensive, reduce health & zero money
if money < 0

View file

@ -1,22 +1,45 @@
url = 'http://localhost:3000'
utils = require('utils')
casper = require("casper").create()
_ = require('../public/js/underscore-min.js')
# ---------- Main Stuff ------------
casper.start url, ->
@test.assertTitle "HabitRPG", "homepage title is the one expected"
# ---------- Todos gain delta on cron ------------
# ---------- Setup Tasks ------------
casper.then ->
todoBefore = @evaluate -> window.DERBY.model.get('_todoList')[0]
@then ->
@evaluate ->
window.DERBY.model.set('_user.lastCron', new Date('09/17/2012'))
@then -> @reload()
@then ->
todoAfter = @evaluate -> window.DERBY.model.get('_todoList')[0]
@then -> @test.assert(todoBefore.value > todoAfter.value, "Incomplete TODO gained value on cron")
_.each ['habit', 'daily', 'todo', 'reward'], (type) ->
# Add 15 of each task type
casper.repeat 15, ->
casper.fill "form#new-#{type}", {'new-task': type } # why can't I use true here?
casper.click "form#new-#{type} input[type=submit]"
# @then ->
# utils.dump @evaluate -> _.pluck(window.DERBY.model.get('_user.tasks'), 'text')
# ---------- Run Cron ------------
casper.then ->
@evaluate -> window.DERBY.model.set('_user.lastCron', new Date('09/01/2012'));
# @then -> @reload ->
# @echo 'Refreshing pag (running cron)'
# @then -> @reload ->
# @echo 'Refreshing page (trying to trigger the database-spaz bug)'
# ---------- Todos gain delta on cron ------------
# casper.then ->
# todoBefore = @evaluate -> window.DERBY.model.get('_todoList')[0]
# @then ->
# @evaluate ->
# window.DERBY.model.set('_user.lastCron', new Date('09/17/2012'))
# @then -> @reload()
# @then ->
# @wait 2000, ->
# todoAfter = @evaluate -> window.DERBY.model.get('_todoList')[0]
# @then -> @test.assert(todoBefore.value > todoAfter.value, "Incomplete TODO gained value on cron")
# utils.dump todoBefore
# utils.dump todoAfter
# ---------- User Death ------------
# casper.then ->
@ -35,11 +58,11 @@ casper.then ->
# ---------- Misc Pages ------------
casper.thenOpen "#{url}/terms", ->
@test.assertTitle "Terms Of Use", "terms page works"
casper.thenOpen "#{url}/privacy", ->
@test.assertTitle "Privacy Policy", "privacy page works"
# casper.thenOpen "#{url}/terms", ->
# @test.assertTitle "Terms Of Use", "terms page works"
#
# casper.thenOpen "#{url}/privacy", ->
# @test.assertTitle "Privacy Policy", "privacy page works"
casper.run ->
@test.renderResults true

View file

@ -3,7 +3,8 @@
<Head:>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="https://js.stripe.com/v1/"></script>
<Header:>
<!-- SEO text, else Google gets strange copy -->
<h2 style="display:none;">A habit tracker app which treats your goals like a Role Playing Game. As you accomplish goals, you level up. If you fail your goals, you lose hit points. Lose all your HP and you die.</h2>
@ -25,24 +26,14 @@
<li><a onclick="window.location='/logout'">Logout</a></li>
</ul>
</div>
<!-- User Settings Modal -->
<div class="modal hide" id="settings-modal" role="dialog">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Settings</h3>
</div>
<div class="modal-body">
<strong>User ID</strong><br/>
<small>Copy this ID for use in third party applications.</small>
<pre class=prettyprint>{_user.id}</pre><br/>
</div>
</div>
{/}
<app:myModal modalId="settings-modal" header="Settings">
<strong>User ID</strong><br/>
<small>Copy this ID for use in third party applications.</small>
<pre class=prettyprint>{_user.id}</pre><br/>
</app:myModal>
{/}
</div>
<div class='container-fluid'>
<div class='row-fluid'>
<div id=character class='span4'>
@ -82,16 +73,26 @@
<Body:>
<br/>
<!-- Revive Modal -->
<!-- Game Over Modal -->
<div style="{#unless equal(_user.stats.lvl,0)}display:none;{/}">
<div data-action="backdrop" class="modal-backdrop"></div>
<div class="modal" id="dead-modal">
<div class="modal-body">
<img src="/img/BrowserQuest/habitrpg_mods/dead.png" />
<h3>You're Dead</h3>
<p><a x-bind=click:revive class="btn btn-danger btn-large">Revive</a></p>
</div>
</div>
<app:myModal noDismiss=true modalId='dead-modal'>
<span class="well pull-right">Quarters: {quarters(_user.balance)}</span>
<table>
<tr>
<td><img src="/img/BrowserQuest/habitrpg_mods/dead.png" /></td>
<td>
<h3>Game Over</h3>
{#if lessThan(_user.balance,0.50)}
<a data-toggle="modal" data-target="#stripe-modal" class="btn btn-danger btn-large">Buy More Quarters</a> <span class='label'>Not enough quarters</span>
{else}
<p>
<a x-bind=click:revive class="btn btn-danger btn-large">Continue</a> <span class='label'>2 Quarters</span>
</p>
{/}
</td>
</tr>
</table>
</app:myModal>
</div>
<div id=wrap class=container-fluid>
@ -111,12 +112,12 @@
<!--helpTitle & helpContent moved to tour -->
<app:taskColumn title="Habits">
<ul class='habits'>{#each _habitList as :task}<app:task />{/}</ul>
<app:newTask type=habit><input value={_newHabit} type="text" placeholder="New Habit"/></app:newTask>
<app:newTask type=habit><input value={_newHabit} type="text" name=new-task placeholder="New Habit"/></app:newTask>
</app:taskColumn>
<app:taskColumn title="Daily">
<ul class='dailys'>{#each _dailyList as :task}<app:task />{/}</ul>
<app:newTask type=daily><input value={_newDaily} type="text" placeholder="New Daily"/></app:newTask>
<app:newTask type=daily><input value={_newDaily} type="text" name=new-task placeholder="New Daily"/></app:newTask>
</app:taskColumn>
<app:taskColumn title="Todos" type="todo">
@ -130,7 +131,7 @@
<ul class='todos'>
{#each _todoList as :task}<app:task />{/}
</ul>
<app:newTask type=todo><input value={_newTodo} type="text" placeholder="New Todo"/></app:newTask>
<app:newTask type=todo><input value={_newTodo} type="text" name=new-task placeholder="New Todo"/></app:newTask>
</div>
<div class="tab-pane" id="tab2">
<ul class='completeds'>
@ -161,13 +162,46 @@
</ul>
{/}
<app:newTask type=reward><input value={_newReward} type="text" placeholder="New Reward"/></app:newTask>
<app:newTask type=reward><input value={_newReward} type="text" name=new-task placeholder="New Reward"/></app:newTask>
</app:taskColumn>
<!-- Re-Roll modal -->
<app:myModal modalId='reroll-modal' header="Reset Your Tasks">
<span class="well pull-right">Quarters: {quarters(_user.balance)}</span>
<p>Highly discouraged because red tasks provide good incentive to improve (<a target="_blank" href="https://github.com/lefnire/habitrpg#all-my-tasks-are-red-im-dying-too-fast">read more</a>). However, this becomes necessary after long bouts of bad habits.</p>
{#if lessThan(_user.balance,1)}
<a data-toggle="modal" data-target="#stripe-modal" class="btn btn-danger btn-large">Buy More Quarters</a> <span class='label'>Not enough quarters</span>
{else}
<button data-dismiss="modal" x-bind=click:buyReroll class="btn btn-danger btn-large">Re-Roll</button> <span class='label'>4 Quarters</span>
{/}
</app:myModal>
</div>
</div>
<app:myFooter/>
<myFooter:>
<!-- Stripe Modal -->
<app:myModal modalId="stripe-modal" header='Buy More Quarters'>
<div class='payment-errors'></div>
<!-- Stripe form from https://stripe.com/docs/tutorials/forms -->
<form action="/" method="POST" id="payment-form" x-bind=submit:submitPayment>
<div class="alert alert-info pull-right"><h2>$5</h2></div>
<div class="form-row">
<label>Card Number</label>
<input type="text" size="20" autocomplete="off" class="card-number"/>
</div>
<div class="form-row">
<label>CVC</label>
<input type="text" size="4" autocomplete="off" class="card-cvc"/>
</div>
<div class="form-row">
<label>Expiration (MM/YYYY)</label>
<input type="text" size="2" class="card-expiry-month"/>
<span> / </span>
<input type="text" size="4" class="card-expiry-year"/>
</div>
<button type="submit" class="submit-button">Submit Payment</button>
</form>
</app:myModal>
<!-- Footer -->
<footer class=footer>
<div class=container>
{#unless _mobileDevice}
@ -209,6 +243,18 @@
</div>
</footer>
<myModal: nonvoid>
{{{#if noDismiss}}}<div data-action="backdrop" class="modal-backdrop"></div>{{{/}}}
<div class="modal {{{#unless noDismiss}}}hide{{{/}}}" id={{{modalId}}} role="dialog">
{{{#if header}}}
<div class="modal-header">
{{{#unless noDismiss}}}<button type="button" class="close" data-dismiss="modal">×</button>{{{/}}}
<h3>{{{unescaped header}}}</h3>
</div>
{{{/}}}
<div class="modal-body">{{{content}}}</div>
</div>
<taskColumn: nonvoid>
<div class="span3 well {{{type}}}s">
<!--removing for now, help-info in the tour instead-->
@ -218,7 +264,7 @@
</div>
<newTask: nonvoid>
<form class="form-inline" data-task-type={{{type}}} x-bind=submit:addTask>
<form class="form-inline" id=new-{{{type}}} data-task-type={{{type}}} x-bind=submit:addTask>
{{{content}}}
<input class="btn" type=submit value=Add>
</form>
@ -315,14 +361,16 @@
<span rel="popover" data-placement="left" data-content="{:item.notes}" data-original-title="{:item.text}" class='task-notes'><i class="icon-comment"></i></span>
</div>
<div class="task-controls">
{{{#unless disabled}}}
{{{#if reroll}}}
<a data-toggle="modal" data-target="#reroll-modal" class="item-buy-link" style='cursor:pointer'><i class=icon-retweet></i></a>
{{{else}}}
<a x-bind=click:buyItem class="item-buy-link" data-type={:item.type} data-value={:item.value} data-index={:item.index}>{:item.value}<img src="/img/coin_single_gold.png"/></a>
{{{/}}}
</div>
<div class="task-text"><img src="/img/BrowserQuest/habitrpg_mods/{:item.icon}.png" /> {:item.text}</div>
</pre>
</li>
<!-- Required scripts, needed by ready() -->
<Scripts:>
<script src=/js/jquery.min.js></script>