Merge branch 'develop' of github.com:HabitRPG/habitrpg into rebalancing

This commit is contained in:
Blade Barringer
2015-03-06 07:47:10 -06:00
5379 changed files with 56072 additions and 25993 deletions

3
.bowerrc Normal file
View File

@@ -0,0 +1,3 @@
{
"directory": "website/public/bower_components"
}

2
.buildpacks Normal file
View File

@@ -0,0 +1,2 @@
https://github.com/heroku/heroku-buildpack-nodejs.git
https://github.com/stomita/heroku-buildpack-phantomjs.git

27
.gitignore vendored
View File

@@ -1,6 +1,25 @@
coverage.html
.DS_Store
website/public/gen
website/public/common
node_modules
*.swp
.idea*
config.json
npm-debug.log
.idea
.swo
.swp
lib
website/public/bower_components
website/build
newrelic_agent.log
.bower-tmp
.bower-registry
.bower-cache
.vagrant
*.log
src/*/*.map
src/*/*/*.map
test/*.js
test/*.map
website/public/docs
*.sublime-workspace
coverage.html

12
.jshintrc Normal file
View File

@@ -0,0 +1,12 @@
{
"globals": {
"confirm": false
},
"browser": true,
"node": true,
"asi": true,
"boss": true,
"newcap": false
}

15
.nodemonignore Normal file
View File

@@ -0,0 +1,15 @@
node_modules/**
.bower-cache/**
.bower-tmp/**
.bower-registry/**
website/public/**
website/views/**
website/build/**
.git/**
Gruntfile.js
CHANGELOG.md
.idea*
*.log
newrelic_agent.log
*.swp
*.swx

View File

@@ -1,3 +1,9 @@
language: node_js
node_js:
- "0.10"
- '0.10'
- mongodb
before_script:
- 'npm install -g grunt-cli mocha'
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- cp config.json.example config.json

282
CHANGELOG.md Normal file
View File

@@ -0,0 +1,282 @@
<a name="">My app - Changelog</a>
# (2014-02-15)
## Documentation
- **rebirth:** Bullet point about repurchase of limited ed gear after Rebirth
([d3f4a561](watch/commits/d3f4a561fdf137e5d8f406bae03be4fef1caff22))
## Bug Fixes
- **#2003:** healer gear not showing
([949cd97b](watch/commits/949cd97b91b42e9450eba559bbfea17e239ab100))
- **#2375:** merge in @SabreCat's stats.jade changes "More elegant show/hide setup for attribute bonuses"
([518f200a](watch/commits/518f200a8fc7373b44ed7d7b5f016d921b0746bd))
- **allocateNow:** Send empty object to user.ops per @colegleason suggestion
([f6e12fa2](watch/commits/f6e12fa25e4366622db3e6f1b6ab03e848b49e10))
- **batch-update:** send errors to client if batch-update found an error, crash pid.
([f9679629](watch/commits/f967962996be69a5335454610af76d10e1db08b8))
- **beastmaster:** fixes #2557, adds opacity to previously-owned pets after they're mounted. You can earn them back again
([5caaff1c](watch/commits/5caaff1cea1a68fe572e7ddf4aac50248b13df5d))
- **bosses:** don't reset progress.up when starting a new quest. We want to be able to carry over damage from the same day a boss battle begins, even if the dailies were completed before battle-start. Fixes #2168
([4efd0f5e](watch/commits/4efd0f5ed8708f2491dd483f93e3d7a268a6337d))
- **bower:** updated jquery directory
([191b789d](watch/commits/191b789d760a7bdc7d1b53727f6127b677c78c94))
- **bs3:**
- fix to MemberModalCtrl parameter
([ebd1df93](watch/commits/ebd1df932e28263e5cc01e8a35f545ab26f1e8bd))
- port pet feeding bar
([5db96ebc](watch/commits/5db96ebca2fbd5b64f49af03a5137ea80f6b1673))
- **buffs:** Move help bubble to left of special buffs
([4f911a68](watch/commits/4f911a68d805742e6744383948eea6f224f2b0ea))
- **challenges:**
- better handling of deleted challenges. If !chal, break the task.challenge. Move the function into userController#score so we have access to next, etc. fixes #1883
([33b326b5](watch/commits/33b326b59685ea6e50f9950094d009460ce80094))
- challenge csv export now has proper filename
([36f21196](watch/commits/36f21196f466260b7cd52b283c50b9e16943f668),
[#2689](watch/issues/2689))
- **classes:**
- misc fixes
([d2121a85](watch/commits/d2121a858716cb5a532a53ee9c5a1adaa74a7f69))
- misc class fixes (not @snicker, ng-if on item store since we dynamically swap it sometimes)
([478be611](watch/commits/478be6111337cd200374f7f31b959725c6a0b945))
- **css:**
- temp fix for bailey height
([c8faffcc](watch/commits/c8faffcc7289090990c3a17ab8c07a00069f5ce4))
- menu and gems wallet margin
([975b5165](watch/commits/975b5165730477310aa64bac27ddc07a34ea6c1d))
- lighter columns title
([a22e2814](watch/commits/a22e28143f74302c8340c3d33b01af9714875523))
- better food tray
([1c41c4dd](watch/commits/1c41c4ddb9a5b04297a371bc4d6aba013ce33f17))
- **errors:**
- `return next(err)` when experiencing errors, instead of res.json(500,{err:err}). Let the top-level error handler handle this (needed for upcoming versionerror discarding)
([bf5e9016](watch/commits/bf5e9016a4cb7889b3a9e39b90eb35cb8f7f9ec8))
- handle if err.message == undefined, send err
([b42dacf2](watch/commits/b42dacf2035d62453b585cfcf453829a423b59de))
- **event-tracking:**
- typo
([ff9d4b88](watch/commits/ff9d4b886ef7a98da0514975441a8bb845496c31))
- stripe sub, not pp
([0c99976b](watch/commits/0c99976bf5a3c7f04f031d62a8b07c862c85a0a9))
- **find_uniq_user:** fix
([ecbe780e](watch/commits/ecbe780e70549b1470504efe052f238c89a9db14))
- **footer:** ensure window.env is accessible from static pages, so we can get deferred scripts on frontpage (esp google analytics)
([67ee011a](watch/commits/67ee011aa35969db93e2d7dc1cd1e1f587f146de))
- **groups:**
- pass missing next into Like function
([afee0968](watch/commits/afee0968f8f6923847e186d3e11b9745ced9606e))
- send error if +1 errored
([5b6c4427](watch/commits/5b6c4427b504b6143f24bfee314f562b9803c5a4))
- **hall:** let's try $gt instead of $ne:null, the query is still slow
([a72b0131](watch/commits/a72b013131cfc7fa5d3affdbfe59b5b3cb15ae89))
- **i18n:** do not save user language for now
([094a4be0](watch/commits/094a4be0015f0f0deaaf94a0734193eb40a8beae))
- **misc:**
- some styles & translations
([8f19f225](watch/commits/8f19f225f104960b3cf27e229a5571e014be697c))
- isStaticPage and debug buttons
([19139f56](watch/commits/19139f562b8e68ed43f4cab748920f1e0634e86e))
- **missing-gems:** remove ad-removal from script, since ads are part of subscription
([e1240dde](watch/commits/e1240dde1d3dcaca4235fad384fea5c07a3706bf))
- **mongoose:** typo
([2786b362](watch/commits/2786b362067efdd245c3efa3a4891021fcfaab2d))
- **mounts:**
- fix pets & mounts css to position the user based on pet/mount equip
([37340d23](watch/commits/37340d23180da02d3742dc9be40a5fb780ecb13b))
- Move avatar upward when mounted regardless of pet
([bc1adeb1](watch/commits/bc1adeb1277103a5ca1f756e175ed68bbe837a2f))
- **nodemon:**
- Add another ignore for weirdsauce Windoze dev environments
([3fda08c3](watch/commits/3fda08c366793c8fbcbf701a9594ae3b2fd8bbea))
- ignore CHANGELOG.md on watch
([d6c55952](watch/commits/d6c55952da8b49f36e9d8e4570d80931d081343d))
- **party:** Round boss health up instead of to nearest integer
([626da568](watch/commits/626da5681f5ea95700f8ddf40587c7184926971c),
[#2504](watch/issues/2504))
- **paypal:** fixes #2492, remove environment check for now, only have production-mode option. revisit
([1dc68112](watch/commits/1dc68112d131e4ebdec32ddff938eb6311d6565f))
- **performance:** cache spritesmith image, fix #2633
([f03d7d7d](watch/commits/f03d7d7dde4f8cb39babd2b982d77e7f88f349b7))
- **pets:** add questPets to UserSchema.items.mounts too fixes #2814
([42766125](watch/commits/42766125d5c8870f25c3a0a001473f700b8f6cc1))
- **profile:** fix bug where empty profile displayed on username click
([0579c432](watch/commits/0579c432489c4a038e8c9f95ea3b285f5abc146f),
[#2465](watch/issues/2465))
- **quests:**
- quests with a level cap cannot be bought before that level.
([dab9ddbd](watch/commits/dab9ddbda27f5e10e4545fea703deebfe2dd9975),
[#2707](watch/issues/2707))
- bug fix to multi-drop
([f478d10c](watch/commits/f478d10c20f816cd104b3f0da814c189957f45f5))
- list multiple rewards in dialog
([e48c7277](watch/commits/e48c7277f8256cf827790aece51e897fe0439374))
- **readme:** remove text about translations wip
([f2bb1fd2](watch/commits/f2bb1fd26e44a9eb0ba325776bf335e021beeece))
- **settings:**
- remove unnecessary code
([5f0cf657](watch/commits/5f0cf6575c0dc4cfc041956e3dc27898d8b4242d))
- reintroduce space between captions and help bubbles stripped during localization
([5ddf09fe](watch/commits/5ddf09fe13c7f8d844c8c47be0fb8f8b2fd1df33))
- **spells:**
- temp workaround for spell & task being undefined. #2649 #2640
([241d0414](watch/commits/241d04140f5db77929d9f597d232f55843bb0f5d))
- more $rootScope spell-casting bug fixes
([47bd6dcb](watch/commits/47bd6dcb79778d90d6f3ddeb003c3d8e45433333))
- add some spells tests, don't send up body to spell paths
([e0646bb9](watch/commits/e0646bb98d44b6874b5259107c9be5fa34c58933))
- some $rootScope.applying action fixes so cast-ending is immediate instead of waiting on response. Also, slim down party population to the essentials to avoid RequestEntityTooLarge
([c6f7ab8a](watch/commits/c6f7ab8a5c6f4e382208a928b90ba5f4eba9cd37))
- <ESC> to cancel spell-casting
([a1df41ad](watch/commits/a1df41ad8165cd9eb6d2d5d59c7fe404edde716c))
- **stable:** show hatchable combo when petOwned>0 (fyi @deilann)
([51bff238](watch/commits/51bff23885ca0080e7e71ff752daa0950ae923ae))
- **stats:** Better layout for attribute point allocation
([d782fc6b](watch/commits/d782fc6b6a3cd7e90d327c93a5764626b2990c74))
- **swagger:** fix jade script warning in swagger
([2e2fcfcf](watch/commits/2e2fcfcf464fbae21bff9e1be1ca915f071b976b))
- **tests:**
- include select2 in test manifest
([38b4cea7](watch/commits/38b4cea73299f51c4db7f6b2eb12533d219745f8))
- don't use cluster in tests, else we get "connection refused"
([7a479098](watch/commits/7a479098dc6535654e322c737d80813790967941))
- **todos:**
- add migration for dateCreated & dateCompleted #2478
([4cc39f16](watch/commits/4cc39f16a13f5fb9f0e3ddde7d274c0f224f4a0e))
- add dateCompleted to todos so they're archived 3 days after completion, not 3 days after creation. Fixes #2478
([b1afc177](watch/commits/b1afc177aa4bfc4cbd9b847e40431db91666d9c3))
- **toolbar:**
- Tweak Settings drop-down
([e241429c](watch/commits/e241429cc3d2eca18d2f5a9726f6caa6270a1b02))
- Tweak icon popovers
([4454204f](watch/commits/4454204f47f80e64119f7896bf246259173d115b))
- tweaks
([5501d57e](watch/commits/5501d57e107c0bc7085847b0c808f027360fa405))
- **translation:** Fix #2585.
([06200acc](watch/commits/06200accada462c3234ab407cfb0f6b684e5effe))
- **translations:**
- fix #2564 and similar ones
([42740902](watch/commits/42740902055a3807532028a5dfb39eff905c104f))
- add env.t to rootScope
([13131087](watch/commits/13131087ff9563d2d174b2c978102f0dc2b87387))
- remove translations for privacy & terms
([a9095f34](watch/commits/a9095f346479336be13b2bf341666b908fa30b3d))
- merge @luveluen pull request, fix some syntax
([a6c67f17](watch/commits/a6c67f17815558f19895b8f67d29c40c14689f09))
- @lefnire now everything is ok
([52decb7e](watch/commits/52decb7edeefb4755ea832b0cf63eaeea5e93259))
- correct some variables
([fba73953](watch/commits/fba739535bc1b630d73eb469448e9c3706043efd))
- revert some views
([d000c706](watch/commits/d000c70679ae0e13d9bec749295e42cc8e299c95))
- **user:**
- make sure next is passed to all routes, and is available in err-back of batch updates
([0c21f54c](watch/commits/0c21f54c67b52b07c417fd8216c6b04bce59d0ab))
- if need to upgrade site, send 501, not 400
([ab86ba11](watch/commits/ab86ba11bdb3379a8d8fa1814879640d61c57227))
- PUT user retricted path errors are 401, not 500
([0aec4caa](watch/commits/0aec4caa785c3b12e15f1c2e19c5b67b20d1a6e1))
- **winston:** typo
([83b3739f](watch/commits/83b3739f4671a08466e057242f936140d5c739ef))
## Features
- **administrators:** start adding features page for admin accounts
([f7f4a0c1](watch/commits/f7f4a0c166558ba7e5461732f7bb6d7bcac25f88))
- **attributes:** Add backfill button in flat and classbased allocation modes
([76a7ab5b](watch/commits/76a7ab5bcce2d486dab3f447f0659ba870d1ff7e))
- **bailey:** notif about STWC updates + scroll-purchase deadlines (@colegleason)
([90176444](watch/commits/90176444e9c7a318040829e8b71d1493b5d58e9e))
- **bug-crushing:** add the critical hammer of bug-crushing
([00af5f7d](watch/commits/00af5f7d0258b0f7dddef8ede40bd825b057748a))
- **challenges:**
- add angular-ui-select2 for simpler find/select challenge winner.
([9fa45217](watch/commits/9fa452173989889c48ed696a45cf4a1dc16294a4))
- add button for csv export
([ae0d758d](watch/commits/ae0d758d8fc751219a693fee7f3e3ebcfbd67590))
- add csv export for challenge progress. WIP, will refine this over time - but we need it something like this for the STWC come 1/31.
([16a602f9](watch/commits/16a602f94c3b7c99d49e42b47b4835b65a243690))
- markdown in challenge-descriptions
([41233c7b](watch/commits/41233c7b167905eeccfdff5589789e002ec23f97))
- **cheating:** prevent +habit spamming with a 10s timer
([ad4ca665](watch/commits/ad4ca6655a3bdd870bb08173530372f81fdc9102))
- **event-tracking:**
- better page-view tracking via ui-router
([b093717b](watch/commits/b093717b8d54b61e5d4b44b0d56a1f43308f078c))
- track registration count
([72b6c9bc](watch/commits/72b6c9bc9189275909804f9ecab18e9fe1f69d27))
- pass ga to server user.ops
([9217b517](watch/commits/9217b5174ab9ab4754269263b214f6bfe45d4f1d))
- track ecommerce events
([d89fb17b](watch/commits/d89fb17b03b2e2c0fb1da77fb13cc660a5b6c9d1))
- add server-side GA tracking for ecommerce events
([f7b4a04a](watch/commits/f7b4a04a590ade26871abc726ade2c666176488e))
- start adding some client-side GA event-tracking
([ffb42906](watch/commits/ffb42906e1d7c6bd8f01e715d98d96426bc6d0de))
- **groups:** add group chat notifications
([ce82be63](watch/commits/ce82be637d1d707e899aeee5f315da69367fa367))
- **habitBirthday:** add habitrpg birthday event. includes cakes for all pets, absurd party robes, npc swap, badge, etc. @lemoness
([aff885c0](watch/commits/aff885c05c03bd70beeb0db8d68922671fc46309))
- **homepage:**
- start cleaning up homepage, add navbar for play button & upcoming links
([0ddaae4d](watch/commits/0ddaae4d7525277e696a57d20234e49cd6fd1cbc))
- use .marketing for centering, add playbutton as static in footer. This is pretty ugly (http://gyazo.com/215e20729569689ab48cf56c71c1fe28), let's iterate / prettify. @deilann
([47bcaf83](watch/commits/47bcaf83e760dbb266ae7ff2f7299c2a1cdf3712))
- **marketing:**
- more copy for mobile
([cbb44847](watch/commits/cbb448478edfd0003c43d20ed216bab20d25dadd))
- start fleshing out the about page with images, content, etc. Create separate videos page
([cb079977](watch/commits/cb079977e6f35f9308ab28158373dd3e1de9f798))
- add video tuts on "learn more" page until we have some copy
([5028707c](watch/commits/5028707c7b174b5e050c7c1662155e781a6b415b))
- some frontpage updates, a screenshot, & "contact us" button mods
([a582a054](watch/commits/a582a0546d680d36a21c507deff725a6c38fdb28))
- **premium:**
- subscriber mystery item (doesn't yet do anything)
([d0342628](watch/commits/d0342628340ce7dce95fa20177ccbcfe1ebf93e6))
- backport server code for premium subs (it's just ccard handling & uer model stuff)
([3660f1a8](watch/commits/3660f1a85c1447de118f334a145d0d7698b93981))
- updates to group plans info page
([66f95cdd](watch/commits/66f95cdd4cfb698fddc765a77b66d29e31eb1361))
- backport client-side premium code to public repo, it's client-side anyway (@colegleason @paglias)
([2e18f0eb](watch/commits/2e18f0eb82f5efc77544d33d1db3fbb9cc583124))
- **quests:**
- add flags.levelDrops for dropping items at certain levels
([78315d82](watch/commits/78315d828ba9a1033526b9a72b7c385281e6ad0a))
- allow dropping scrolls in quests
([54064deb](watch/commits/54064debf3c95390b5507acd826f9db3339b9f09))
- allow html in quest notes
([800231cf](watch/commits/800231cf6481351032d4e5143edd54f5e7e3a179))
- add level requirement for quests
([9e69d795](watch/commits/9e69d7959f174955f44429a94f22ce40fc5f7861))
- add canBuy so we can exclude certain items from the market (if you can only find them on quest-drop, etc). This isn't the prettiest, change?
([f16654d2](watch/commits/f16654d2354dc86cc7c52e1cf0562f850cf203be))
- allow quests to drop multiple items
([d9e5725e](watch/commits/d9e5725ee13f7e9ad329fc548537d5265cf483ca))
- **rainbow-hair:** add rainbow hair colors
([82d9233d](watch/commits/82d9233d99167d6704c878884dcc49a55cc7d884))
- **restore:** add restore-gp back in. Parly to end the winter event, partly due to the convo at #2681 regarding subscriptions. Fixes #2681
([179316e1](watch/commits/179316e10fa7597b08573d94721861baa3dbbb1c))
- **toolbar:**
- try with icons instead of text, test against prod / beta & get a vote.
([7456f00d](watch/commits/7456f00dc6122ad293652b7a32fb4ce671f75241))
- add toolbar featuring navigation, gems / subs, bailey, & chat / invite notifications
([f72cb213](watch/commits/f72cb21300c078b439b3334bfa3e205ba04dc949))
- **tracking:** gems > toolbar separately from wallet
([f6abfc67](watch/commits/f6abfc67b31808c0e2d325c235747260855338c9))
- **valentine:** valentine event
([fd6eb872](watch/commits/fd6eb8724eae38d02849ffccb09f1f9c7d8e490d))
- **winter:**
- remove purchasable winter hair colors, keep available if they purchased during event
([f8796e90](watch/commits/f8796e9028d4f4cd2b5c5ede1734d2876d174dc9))
- remove winter scrolls & snowballs
([52f8f0d5](watch/commits/52f8f0d5b0fdf4271fcb5f7d497ad3bf544c24e8))
## Docs
- **rebirth:** Bullet point about repurchase of limited ed gear after Rebirth

14
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,14 @@
# Reporting Bugs
[Please see these instructions for reporting bugs](https://github.com/HabitRPG/habitrpg/issues/2760)
## Frequently Asked Questions
You might find help with your issue on the [Frequently Asked Questions](http://habitrpg.wikia.com/wiki/FAQ) page.
# Requesting a feature
HabitRPG uses [Trello](https://trello.com/b/EpoYEYod/habitrpg) to track feature requests. [Read more](https://trello.com/c/odmhIqyW/440-read-first-table-of-contents).
# Contributing Code
See [Contributing to HabitRPG](http://habitrpg.wikia.com/wiki/Contributing_to_HabitRPG#Coders_.28Web_.26_Mobile.29)

77
DOCS-README.md Normal file
View File

@@ -0,0 +1,77 @@
# HabitRPG Docs Project
Generated documentation for all of HabitRPG's source files will be kept in the folder and subfolders. If you would like to use the existing documentation, or contribute to the documentation efforts, read on.
## Viewing Docs
You're looking at it!
Unless you are viewing this file directly from GitHub, you should see a list of files and folders to the left of this readme.
If you are working locally, you can goto `localhost:3000/docs/` and view the Docs.
All documentation is generated from comments in the code, into HTML files in the `public/docs/` folder. After you have cloned the HabitRPG repo locally, and done all the `npm install` goodness, the Docs should generate automagickly when you run `grunt run:dev`
## What I do now?
Well if you know Markdown, simply add detailed comments in the code using Markdown syntax.
````
/*
User.js
=======
Defines the user data model (schema) for use via the API.
*/
// Dependencies
// ------------
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var helpers = require('habitrpg-shared/script/helpers');
var _ = require('lodash');....
````
As you can see, you can use both multiline style comments `/* fancy stuff */` and inline comments `// Ooooh my`.
The exception being end of line comments
`text: String, // example: Wolf `
The above will not be on the "pretty print" side of the Docs, but will stay in the code. An example use case for end of line comments would be for FIXME notes.
Add anything that would be helpful to a developer regarding how to use the functions, variables, and objects associated with HabitRPG.
**All documentation should be committed as pull request to the `docs-project` branch of HabitRPG.** Since we are adding comments directly to the code, I don't want to be editing files used for beta or master. We can merge in the docs after we're sure we didn't break anything.
### jsDoc Syntax
Yes, the generator also supports jsDoc-style comments such as
````
@param {Array} files Array of file paths relative to the `inDir` to generate documentation for.
````
**Important Note:** If you use the `@param` syntax, you must use multiline comment blocks (ie `/* stuff */`), otherwise they won't be parsed like parameters.
This may or may not be useful for HabitRPG. Example use cases:
- Documenting the API
- Javascript Models
## Okay, I added great comments. Now what?
If you're running locally, just re-run `grunt run:dev`. Any changed docs will be automagickly updated.
Once you're satisfied with the output, push your changes to your fork of HabitRPG and issue a Pull Request on the `docs-project` branch.
It's that easy!
## Tech Info
The generator we are using is [Docker](https://github.com/jbt/docker), which is a fork of [Docco](http://jashkenas.github.io/docco/). Docker supports the same wide-range of filetypes, including being able to generate documentation for a whole project, including an index.
We also use the [Grunt-Docker](https://github.com/Prevole/grunt-docker) node module for automatic processing.
## Road Map
- Customize CSS with HabitRPG specific Styling
- Explore possibilities of importing Wiki content
- Specify style guide for consistency of comments

41
Dockerfile Normal file
View File

@@ -0,0 +1,41 @@
FROM ubuntu:trusty
MAINTAINER Thibault Cohen <titilambert@gmail.com>
ENV DEBIAN_FRONTEND noninteractive
### Init
RUN apt-get update
### Utils
RUN apt-get install -y git vim graphicsmagick nodejs phantomjs npm pkgconf libcairo2-dev libjpeg8-dev
### Installation
RUN cd /opt && git clone https://github.com/HabitRPG/habitrpg.git
#RUN cd /opt/habitrpg && git checkout -t origin/develop
RUN cd /opt/habitrpg && git pull
RUN cd /opt/habitrpg && npm install -g grunt-cli bower nodemon
RUN ln -s /usr/bin/nodejs /usr/bin/node
RUN cd /opt/habitrpg && npm install
# Add config file
ADD ./config.json /opt/habitrpg/
RUN mkdir -p /opt/habitrpg/build
RUN cd /opt/habitrpg && bower install --allow-root
# Run server
RUN cd /opt/habitrpg && grunt build:prod
CMD cd /opt/habitrpg && grunt nodemon

64
EXTENDEDCHANGELOG.md Normal file
View File

@@ -0,0 +1,64 @@
<a name="">HabitRPG</a>
# (2014-01-28)
## Documentation
- **rebirth:** Bullet point about repurchase of limited ed gear after Rebirth
([d3f4a561](https://github.com/habitrpg/habitrpg/commits/d3f4a561fdf137e5d8f406bae03be4fef1caff22))
## Bug Fixes
- **#2003:** healer gear not showing
([949cd97b](https://github.com/habitrpg/habitrpg/commits/949cd97b91b42e9450eba559bbfea17e239ab100))
- **#2375:** merge in @SabreCat's stats.jade changes "More elegant show/hide setup for attribute bonuses"
([518f200a](https://github.com/habitrpg/habitrpg/commits/518f200a8fc7373b44ed7d7b5f016d921b0746bd))
- **beastmaster:** fixes #2557, adds opacity to previously-owned pets after they're mounted. You can earn them back again
([5caaff1c](https://github.com/habitrpg/habitrpg/commits/5caaff1cea1a68fe572e7ddf4aac50248b13df5d))
- **bosses:** don't reset progress.up when starting a new quest. We want to be able to carry over damage from the same day a boss battle begins, even if the dailies were completed before battle-start. Fixes #2168
([4efd0f5e](https://github.com/habitrpg/habitrpg/commits/4efd0f5ed8708f2491dd483f93e3d7a268a6337d))
- **classes:**
- misc fixes
([d2121a85](https://github.com/habitrpg/habitrpg/commits/d2121a858716cb5a532a53ee9c5a1adaa74a7f69))
- misc class fixes (not @snicker, ng-if on item store since we dynamically swap it sometimes)
([478be611](https://github.com/habitrpg/habitrpg/commits/478be6111337cd200374f7f31b959725c6a0b945))
- **find_uniq_user:** fix
([ecbe780e](https://github.com/habitrpg/habitrpg/commits/ecbe780e70549b1470504efe052f238c89a9db14))
- **mounts:** Move avatar upward when mounted regardless of pet
([bc1adeb1](https://github.com/habitrpg/habitrpg/commits/bc1adeb1277103a5ca1f756e175ed68bbe837a2f))
- **nodemon:** ignore CHANGELOG.md on watch
([d6c55952](https://github.com/habitrpg/habitrpg/commits/d6c55952da8b49f36e9d8e4570d80931d081343d))
- **party:** Round boss health up instead of to nearest integer
([626da568](https://github.com/habitrpg/habitrpg/commits/626da5681f5ea95700f8ddf40587c7184926971c),
[#2504](https://github.com/habitrpg/habitrpg/issues/2504))
- **paypal:** fixes #2492, remove environment check for now, only have production-mode option. revisit
([1dc68112](https://github.com/habitrpg/habitrpg/commits/1dc68112d131e4ebdec32ddff938eb6311d6565f))
- **profile:** fix bug where empty profile displayed on username click
([0579c432](https://github.com/habitrpg/habitrpg/commits/0579c432489c4a038e8c9f95ea3b285f5abc146f),
[#2465](https://github.com/habitrpg/habitrpg/issues/2465))
- **quests:**
- bug fix to multi-drop
([f478d10c](https://github.com/habitrpg/habitrpg/commits/f478d10c20f816cd104b3f0da814c189957f45f5))
- list multiple rewards in dialog
([e48c7277](https://github.com/habitrpg/habitrpg/commits/e48c7277f8256cf827790aece51e897fe0439374))
- **settings:** reintroduce space between captions and help bubbles stripped during localization
([5ddf09fe](https://github.com/habitrpg/habitrpg/commits/5ddf09fe13c7f8d844c8c47be0fb8f8b2fd1df33))
- **spells:**
- more $rootScope spell-casting bug fixes
([47bd6dcb](https://github.com/habitrpg/habitrpg/commits/47bd6dcb79778d90d6f3ddeb003c3d8e45433333))
- add some spells tests, don't send up body to spell paths
([e0646bb9](https://github.com/habitrpg/habitrpg/commits/e0646bb98d44b6874b5259107c9be5fa34c58933))
- some $rootScope.applying action fixes so cast-ending is immediate instead of waiting on response. Also, slim down party population to the essentials to avoid RequestEntityTooLarge
([c6f7ab8a](https://github.com/habitrpg/habitrpg/commits/c6f7ab8a5c6f4e382208a928b90ba5f4eba9cd37))
- <ESC> to cancel spell-casting
([a1df41ad](https://github.com/habitrpg/habitrpg/commits/a1df41ad8165cd9eb6d2d5d59c7fe404edde716c))
- **stable:** show hatchable combo when petOwned>0 (fyi @deilann)
([51bff238](https://github.com/habitrpg/habitrpg/commits/51bff23885ca0080e7e71ff752daa0950ae923ae))
- **stats:** Better layout for attribute point allocation
([d782fc6b](https://github.com/habitrpg/habitrpg/commits/d782fc6b6a3cd7e90d327c93a5764626b2990c74))
- **tests:**
- include select2 in test manifest
([38b4cea7](https://github.com/habitrpg/habitrpg/commits/38b4cea73299f51c4db7f6b2eb12533d219745f8))
- don't use cluster in tests, else we get "connection refused"
([7a479098](https://github.com/habitrpg/habitrpg/commits/7a479098dc6535654e322c737d80813790967941)

View File

@@ -1,13 +1,11 @@
//var sizeOf = require('image-size');
/*global module:false*/
var _ = require('lodash');
module.exports = function(grunt) {
var timestamp = +new Date;
// Ported from shared
// So this sucks. Mobile Safari can't render image files > 1024x1024*3, so we have to break it down to multiple
// files in this hack approach. See https://github.com/Ensighten/grunt-spritesmith/issues/67#issuecomment-34786248
var images = grunt.file.expand('img/sprites/spritesmith/**/*.png');
var images = grunt.file.expand('common/img/sprites/spritesmith/**/*.png');
// var totalDims = {width:0,height:0};
// _.each(images, function(img){
// var dims = sizeOf(img);
@@ -23,12 +21,12 @@ module.exports = function(grunt) {
var sliced = images.slice(i * (images.length/COUNT), (i+1) * images.length/COUNT)
sprite[''+i] = {
src: sliced,
dest: 'dist/spritesmith'+i+'.png',
destCss: 'dist/spritesmith'+i+'.css',
dest: 'common/dist/sprites/spritesmith'+i+'.png',
destCss: 'common/dist/sprites/spritesmith'+i+'.css',
engine: 'phantomjssmith',
algorithm: 'binary-tree',
padding:1,
cssTemplate: 'css/css.template.mustache',
cssTemplate: 'common/css/css.template.mustache',
cssVarMap: function (sprite) {
// For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class, which works as a
// 60x60 image pointing at the proper part of the 90x90 sprite.
@@ -52,18 +50,48 @@ module.exports = function(grunt) {
}
}*/
}
})
});
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// Cleanup previous spritesmith files
clean: {
main: ['dist/spritesmith*.*']
git_changelog: {
minimal: {
options: {
repo_url: 'https://github.com/habitrpg/habitrpg',
appName : 'HabitRPG',
branch_name: 'develop'
}
},
extended: {
options: {
file: 'EXTENDEDCHANGELOG.md',
repo_url: 'https://github.com/habitrpg/habitrpg',
appName : 'HabitRPG',
branch_name: 'develop',
grep_commits: '^perf|^style|^fix|^feat|^docs|^refactor|^chore|BREAKING'
}
}
},
karma: {
unit: {
configFile: 'karma.conf.js'
},
continuous: {
configFile: 'karma.conf.js',
singleRun: true,
autoWatch: false
}
},
clean: {
build: ['website/build'],
sprite: ['common/dist/sprites']
},
/**
* Converts our individual image files in img/spritesmith into a unified spritesheet.
*/
sprite: sprite,
cssmin: {
@@ -72,36 +100,158 @@ module.exports = function(grunt) {
report: 'gzip'
},
files:{
"dist/habitrpg-shared.css": [
"dist/spritesmith*.css",
"css/backer.css",
"css/Mounts.css",
"css/index.css"
"common/dist/sprites/habitrpg-shared.css": [
"common/dist/sprites/spritesmith*.css",
"common/css/backer.css",
"common/css/Mounts.css",
"common/css/index.css"
]
}
}
},
stylus: {
build: {
options: {
compress: false, // AFTER
'include css': true,
paths: ['website/public']
},
files: {
'website/build/app.css': ['website/public/css/index.styl'],
'website/build/static.css': ['website/public/css/static.styl']
}
}
},
browserify: {
dist: {
src: ["index.js"],
dest: "dist/habitrpg-shared.js"
src: ["common/index.js"],
dest: "common/dist/scripts/habitrpg-shared.js"
},
options: {
transform: ['coffeeify']
//debug: true Huge data uri source map (400kb!)
}
}
},
copy: {
build: {
files: [
{expand: true, cwd: 'website/public/', src: 'favicon.ico', dest: 'website/build/'},
{expand: true, cwd: '', src: 'common/dist/sprites/spritesmith*.png', dest: 'website/build/'},
{expand: true, cwd: '', src: 'common/img/sprites/backer-only/*.gif', dest: 'website/build/'},
{expand: true, cwd: '', src: 'common/img/sprites/npc_ian.gif', dest: 'website/build/'},
{expand: true, cwd: 'website/public/', src: 'bower_components/bootstrap/dist/fonts/*', dest: 'website/build/'}
]
}
},
// UPDATE IT WHEN YOU ADD SOME FILES NOT ALREADY MATCHED!
hashres: {
build: {
options: {
fileNameFormat: '${name}-${hash}.${ext}'
},
src: [
'website/build/*.js',
'website/build/*.css',
'website/build/favicon.ico',
'website/build/common/dist/sprites/*.png',
'website/build/common/img/sprites/backer-only/*.gif',
'website/build/common/img/sprites/npc_ian.gif',
'website/build/bower_components/bootstrap/dist/fonts/*'
],
dest: 'website/build/*.css'
}
},
nodemon: {
dev: {
script: '<%= pkg.main %>'
}
},
watch: {
dev: {
files: ['website/public/**/*.styl'], // 'public/**/*.js' Not needed because not in production
tasks: [ 'build:dev' ],
options: {
nospawn: true
}
}
},
concurrent: {
dev: ['nodemon', 'watch'],
options: {
logConcurrentOutput: true
}
}
});
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-spritesmith');
grunt.loadNpmTasks('grunt-contrib-cssmin'); // Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-browserify');
//Load build files from public/manifest.json
grunt.registerTask('loadManifestFiles', 'Load all build files from public/manifest.json', function(){
var files = grunt.file.readJSON('./website/public/manifest.json');
var uglify = {};
var cssmin = {};
// Default task(s).
grunt.registerTask('default', ['cssmin', 'browserify']);
grunt.registerTask('full', ['clean', 'sprite', 'cssmin', 'browserify']);
_.each(files, function(val, key){
var js = uglify['website/build/' + key + '.js'] = [];
_.each(files[key].js, function(val){
var path = "./";
if( val.indexOf('common/') == -1)
path = './website/public/';
js.push(path + val);
});
var css = cssmin['website/build/' + key + '.css'] = [];
_.each(files[key].css, function(val){
var path = "./";
if( val.indexOf('common/') == -1) {
path = (val == 'app.css' || val == 'static.css') ? './website/build/' : './website/public/';
}
css.push(path + val)
});
});
grunt.config.set('uglify.build.files', uglify);
grunt.config.set('uglify.build.options', {compress: false});
grunt.config.set('cssmin.build.files', cssmin);
// Rewrite urls to relative path
grunt.config.set('cssmin.build.options', {'target': 'website/public/css/whatever-css.css'});
});
// Register tasks.
grunt.registerTask('compile:sprites', ['clean:sprite', 'sprite', 'cssmin']);
grunt.registerTask('build:prod', ['loadManifestFiles', 'clean:build', 'browserify', 'uglify', 'stylus', 'cssmin', 'copy:build', 'hashres']);
grunt.registerTask('build:dev', ['browserify', 'stylus']);
grunt.registerTask('run:dev', [ 'build:dev', 'concurrent' ]);
if(process.env.NODE_ENV == 'production')
grunt.registerTask('default', ['build:prod']);
else
grunt.registerTask('default', ['build:dev']);
// Load tasks
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-stylus');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-nodemon');
grunt.loadNpmTasks('grunt-concurrent');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-spritesmith');
grunt.loadNpmTasks('grunt-hashres');
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('git-changelog');
};

10
LICENSE
View File

@@ -1 +1,9 @@
This project's license can be obtained at https://raw2.github.com/HabitRPG/habitrpg/develop/LICENSE
* Code is GPL v3 licensed:
This Source Code is subject to the terms of the GNU General Public License, v. 3.0.
If a copy of the GPL was not distributed with this file, You can obtain one at http://www.gnu.org/licenses/gpl-3.0.txt
* Assets and content designed for Mozilla BrowserQuest is licensed under CC-BY-SA 3.0:
http://creativecommons.org/licenses/by-sa/3.0/
* Assets and content designed for HabitRPG is licensed under CC-BY-NC-SA 3.0:
http://creativecommons.org/licenses/by-nc-sa/3.0/

1
Procfile Normal file
View File

@@ -0,0 +1 @@
web: ./node_modules/.bin/grunt nodemon;

View File

@@ -1,36 +1,12 @@
[![Build Status](https://travis-ci.org/HabitRPG/habitrpg-shared.png?branch=master)](https://travis-ci.org/HabitRPG/habitrpg-shared)
HabitRPG [![Build Status](https://travis-ci.org/HabitRPG/habitrpg.png?branch=develop)](https://travis-ci.org/HabitRPG/habitrpg) [![Code Climate](https://codeclimate.com/github/HabitRPG/habitrpg.png)](https://codeclimate.com/github/HabitRPG/habitrpg) [![Bountysource](https://api.bountysource.com/badge/tracker?tracker_id=68393)](https://www.bountysource.com/trackers/68393-habitrpg?utm_source=68393&utm_medium=shield&utm_campaign=TRACKER_BADGE) [![Dependency Status](https://gemnasium.com/HabitRPG/habitrpg.svg)](https://gemnasium.com/HabitRPG/habitrpg)
===============
## We're in the process of migrating this repository to the main HabitRPG repository, you can report any issue [here](https://github.com/HabitRPG/habitrpg).
Shared resources useful for the multiple HabitRPG repositories, that way all the repositories remain in-sync with common characteristics. Includes things like:
* Assets - sprites, images, etc
* CSS - especially, esp. sprite-sheet mapping
* Algorithms - level up algorithm, scoring functions, etc
* View helper functions that may come in handy for multiple client MVCs
* Item definitions - weapons, armor, pets
[HabitRPG](https://habitrpg.com) is an open source habit building program which treats your life like a Role Playing Game. Level up as you succeed, lose HP as you fail, earn money to buy weapons and armor.
##Installation
* `npm install`
* `grunt` - after you've made modifications and want to compile the dist files for browser
We need more programmers! Your assistance will be greatly appreciated.
* Node.js
* `require ('coffee-script')`
* `require('./script/algos.coffee')`, `require('./script/helpers.coffee')`, etc.
* Browser
* Use `<script/>` tag to include ./dist/habitrpg-shared.js it will export `window.habitrpgShared` object.
* // Use `browser.debug.js' if you want to have sourcemaps. - EDIT: Only one file now, and it has sourcemaps. Fix this
For an introduction to the technologies used and how the software is organised, refer to [Contributing to HabitRPG](http://habitrpg.wikia.com/wiki/Contributing_to_HabitRPG#Coders_.28Web_.26_Mobile.29) - "Coders (Web & Mobile)" section.
* Note how to invoke scoring function:
* `algos.score(user, task, direction)`, etc
* TODO document all the functions
To set up a local install of HabitRPG for development and testing, see [Setting up HabitRPG locally](http://habitrpg.wikia.com/wiki/Setting_up_HabitRPG_locally), which contains instructions for Windows, *nix / Mac OS, and Vagrant.
##Tests
* `npm test`
##CSS
Shared CSS between the website and the mobile app is a fuzzy area. Spritesheets definitely go in habitrpg-shared (since mobile
uses them too). Other things, like customizer buttons, *may* want to go here? As you find sharable components, (1) move them
from the website into habitrpg-shared, (2) remove from website & make sure all html/css references are updated.
Currently, all or most spritesheets are available. They're in css/*.css, but that's not what you want. You want `/spritesheets.css`
which is the concat'd file (using `grunt`) which includes all the spritesheets. I'd prefer this be in /dist/spritesheets.css
for consistency, but it's having image referencing weirdness
Then read [Guidance for Blacksmiths](http://habitrpg.wikia.com/wiki/Guidance_for_Blacksmiths) for additional instructions and useful tips.

11
VAGRANT.md Normal file
View File

@@ -0,0 +1,11 @@
# Vagrant #
Vagrant is a system to create reproducible and portable development
environments. Because of the variety of systems used for HabitRPG
development and the various issues developers may encounter setting up
HabitRPG on them, vagrant provides a single development enviroment with
minimal dependencies on the developer's local platform. It can be used
on a variety of systems including Windows, Mac OS X, and Linux.
Instructions for using the HabitRPG Vagrant environment are in
[Setting up HabitRPG locally](http://habitrpg.wikia.com/wiki/Setting_up_HabitRPG_locally).

18
Vagrantfile vendored Normal file
View File

@@ -0,0 +1,18 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provider "virtualbox" do |v|
v.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/vagrant", "1"]
end
config.vm.box = "thepeopleseason/habitrpg"
config.ssh.forward_agent = true
config.vm.hostname = "habitrpg"
config.vm.network "forwarded_port", guest: 3000, host: 3000, auto_correct: true
config.vm.usable_port_range = (3000..3050)
config.vm.provision :shell, :path => "vagrant.sh"
end

8
archive/README.md Normal file
View File

@@ -0,0 +1,8 @@
# What's This?
I'm consolidating @litenull's rewrite branch with the old code, and removing files from both sources once they've been
successfully merged into the new platform. While @litenull's "from scratch" approach was really clean, it will take
us longer to implemente all the original features. This approach will (1) let us leverage code we already have, (2) merge
in litenull's hard work from the last few weeks.
Once this archive/ directory is completely empty, we should be fully merged and ready to deploy the rewrite!

View File

@@ -0,0 +1,46 @@
_ = require 'lodash'
moment = require 'moment'
###
Loads JavaScript files from public/vendor/*
Use require() to min / concatinate for faster page load
###
loadJavaScripts = (model) ->
# Turns out you can't have expressions in browserify require() statements
#vendor = '../../public/vendor'
#require "#{vendor}/jquery-ui-1.10.2/jquery-1.9.1"
###
Internal Scripts
###
require "../vendor/jquery.cookie.min.js"
require "../vendor/bootstrap/js/bootstrap.min.js"
require "../vendor/datepicker/js/bootstrap-datepicker"
require "../vendor/bootstrap-tour/bootstrap-tour"
unless (model.get('_mobileDevice') is true)
require "../vendor/sticky"
# note: external script loading is handled in app.on('render') near the bottom of this file (see https://groups.google.com/forum/?fromgroups=#!topic/derbyjs/x8FwdTLEuXo)
# jquery sticky header on scroll, no need for position fixed
initStickyHeader = (model) ->
$('.header-wrap').sticky({topSpacing:0})
module.exports.app = (appExports, model, app) ->
app.on 'render', (ctx) ->
#restoreRefs(model)
unless model.get('_mobileDevice')
setupTooltips(model)
initStickyHeader(model)
setupSortable(model)
$('.datepicker').datepicker({autoclose:true, todayBtn:true})
.on 'changeDate', (ev) ->
#for some reason selecting a date doesn't fire a change event on the field, meaning our changes aren't saved
model.at(ev.target).set 'date', moment(ev.date).format('MM/DD/YYYY')

View File

@@ -0,0 +1,239 @@
_ = require 'lodash'
{helpers} = require 'habitrpg-shared'
async = require 'async'
module.exports.app = (app) ->
###
Sync any updates to challenges since last refresh. Do it after cron, so we don't punish them for new tasks
This is challenge->user sync. user->challenge happens when user interacts with their tasks
###
app.on 'ready', (model) ->
window.setTimeout ->
_.each model.get('groups'), (g) ->
if (@uid in g.members) and g.challenges
_.each(g.challenges, ->app.challenges.syncChalToUser g)
true
, 500
###
Sync user to challenge (when they score, add to statistics)
###
app.model.on "change", "_page.user.priv.tasks.*.value", (id, value, previous, passed) ->
### Sync to challenge, but do it later ###
async.nextTick =>
model = app.model
ctx = {model: model}
task = model.at "_page.user.priv.tasks.#{id}"
tobj = task.get()
pub = model.get "_page.user.pub"
if (chalTask = helpers.taskInChallenge.call ctx, tobj)? and chalTask.get()
chalTask.increment "value", value - previous
chal = model.at "groups.#{tobj.group.id}.challenges.#{tobj.challenge}"
chalUser = -> helpers.indexedAt.call(ctx, chal.path(), 'members', {id:pub.id})
cu = chalUser()
unless cu?.get()
chal.push "members", {id: pub.id, name: model.get(pub.profile.name)}
cu = model.at chalUser()
else
cu.set 'name', pub.profile.name # update their name incase it changed
cu.set "#{tobj.type}s.#{tobj.id}",
value: tobj.value
history: tobj.history
###
Render graphs for user scores when the "Challenges" tab is clicked
###
###
TODO
1) on main tab click or party
* sort & render graphs for party
2) guild -> all guilds
3) public -> all public
###
###
$('#profile-challenges-tab-link').on 'shown', ->
async.each _.toArray(model.get('groups')), (g) ->
async.each _.toArray(g.challenges), (chal) ->
async.each _.toArray(chal.tasks), (task) ->
async.each _.toArray(chal.members), (member) ->
if (history = member?["#{task.type}s"]?[task.id]?.history) and !!history
data = google.visualization.arrayToDataTable _.map(history, (h)-> [h.date,h.value])
options =
backgroundColor: { fill:'transparent' }
width: 150
height: 50
chartArea: width: '80%', height: '80%'
axisTitlePosition: 'none'
legend: position: 'bottom'
hAxis: gridlines: color: 'transparent' # since you can't seem to *remove* gridlines...
vAxis: gridlines: color: 'transparent'
chart = new google.visualization.LineChart $(".challenge-#{chal.id}-member-#{member.id}-history-#{task.id}")[0]
chart.draw(data, options)
###
app.fn
challenges:
###
Create
###
create: (e,el) ->
[type, gid] = [$(el).attr('data-type'), $(el).attr('data-gid')]
cid = @model.id()
@model.set '_page.new.challenge',
id: cid
name: ''
habits: []
dailys: []
todos: []
rewards: []
user:
uid: @uid
name: @pub.get('profile.name')
group: {type, id:gid}
timestamp: +new Date
###
Save
###
save: ->
newChal = @model.get('_page.new.challenge')
[gid, cid] = [newChal.group.id, newChal.id]
@model.push "_page.lists.challenges.#{gid}", newChal, ->
app.browser.growlNotification('Challenge Created','success')
app.challenges.discard()
app.browser.resetDom() # something is going absolutely haywire here, all model data at end of reflist after chal created
###
Toggle Edit
###
toggleEdit: (e, el) ->
path = "_page.editing.challenges.#{$(el).attr('data-id')}"
@model.set path, !@model.get(path)
###
Discard
###
discard: ->
@model.del '_page.new.challenge'
###
Delete
###
delete: (e) ->
return unless confirm("Delete challenge, are you sure?") is true
e.at().remove()
###
Add challenge name as a tag for user
###
syncChalToUser: (chal) ->
return unless chal
### Sync tags ###
tags = @priv.get('tags') or []
idx = _.findIndex tags, {id: chal.id}
if ~idx and (tags[idx].name isnt chal.name)
### update the name - it's been changed since ###
@priv.set "tags.#{idx}.name", chal.name
else
@priv.push 'tags', {id: chal.id, name: chal.name, challenge: true}
tags = {}; tags[chal.id] = true
_.each chal.habits.concat(chal.dailys.concat(chal.todos.concat(chal.rewards))), (task) =>
_.defaults task, { tags, challenge: chal.id, group: {id: chal.group.id, type: chal.group.type} }
path = "tasks.#{task.id}"
if @priv.get path
@priv.set path, _.defaults(@priv.get(path), task)
else
@model.push "_page.lists.tasks.#{@uid}.#{task.type}s", task
true
###
Subscribe
###
subscribe: (e) ->
chal = e.get()
### Add all challenge's tasks to user's tasks ###
currChallenges = @pub.get('challenges')
@pub.unshift('challenges', chal.id) unless currChallenges and ~currChallenges.indexOf(chal.id)
e.at().push "members",
id: @uid
name: @pub.get('profile.name')
app.challenges.syncChalToUser(chal)
###
--------------------------
Unsubscribe functions
--------------------------
###
unsubscribe: (chal, keep=true) ->
### Remove challenge from user ###
i = @pub.get('challenges')?.indexOf(chal.id)
if i? and ~i
@pub.remove("challenges", i, 1)
### Remove user from challenge ###
if ~(i = _.findIndex chal.members, {id: @uid})
@model.remove "groups.#{chal.group.id}.challenges.#{chal.id}.members", i, 1
### Remove tasks from user ###
async.each chal.habits.concat(chal.dailys.concat(chal.todos.concat(chal.rewards))), (task) =>
if keep is true
@priv.del "tasks.#{task.id}.challenge"
else
path = "_page.lists.tasks.#{@uid}.#{task.type}s"
if ~(i = _.findIndex(@model.get(path), {id:task.id}))
@model.remove(path, i, 1)
true
taskUnsubscribe: (e, el) ->
###
since the challenge was deleted, we don't have its data to unsubscribe from - but we have the vestiges on the task
FIXME this is a really dumb way of doing this
###
tasks = @priv.get('tasks')
tobj = tasks[$(el).attr("data-tid")]
deletedChal =
id: tobj.challenge
members: [@uid]
habits: _.where tasks, {type: 'habit', challenge: tobj.challenge}
dailys: _.where tasks, {type: 'daily', challenge: tobj.challenge}
todos: _.where tasks, {type: 'todo', challenge: tobj.challenge}
rewards: _.where tasks, {type: 'reward', challenge: tobj.challenge}
switch $(el).attr('data-action')
when 'keep'
@priv.del "tasks.#{tobj.id}.challenge"
@priv.del "tasks.#{tobj.id}.group"
when 'keep-all'
app.challenges.unsubscribe.call @, deletedChal, true
when 'remove'
path = "_page.lists.tasks.#{@uid}.#{tobj.type}s"
if ~(i = _.findIndex @model.get(path), {id: tobj.id})
@model.remove path, i
when 'remove-all'
app.challenges.unsubscribe.call @, deletedChal, false
challengeUnsubscribe: (e, el) ->
$(el).popover('destroy').popover({
html: true
placement: 'top'
trigger: 'manual'
title: 'Unsubscribe From Challenge And:'
content: """
<a class=challenge-unsubscribe-and-remove>Remove Tasks</a><br/>
<a class=challenge-unsubscribe-and-keep>Keep Tasks</a><br/>
<a class=challenge-unsubscribe-cancel>Cancel</a><br/>
"""
}).popover('show')
$('.challenge-unsubscribe-and-remove').click => app.challenges.unsubscribe.call @, e.get(), false
$('.challenge-unsubscribe-and-keep').click => app.challenges.unsubscribe.call @, e.get(), true
$('[class^=challenge-unsubscribe]').click => $(el).popover('destroy')

View File

@@ -0,0 +1,22 @@
_ = require 'lodash'
module.exports.app = (appExports, model) ->
user = model.at('_user')
appExports.filtersDeleteTag = (e, el) ->
tags = user.get('tags')
tag = e.at "_user.tags." + $(el).attr('data-index')
tagId = tag.get('id')
###something got corrupted, let's clear the corrupt tags###
unless tagId
user.set 'tags', _.filter( tags, ((t)-> t?.id) )
user.set 'filters', {}
return
model.del "_user.filters.#{tagId}"
tag.remove()
### remove tag from all tasks###
_.each user.get("tasks"), (task) -> user.del "tasks.#{task.id}.tags.#{tagId}"; true

View File

@@ -0,0 +1,139 @@
_ = require('lodash')
helpers = require('habitrpg-shared/script/helpers')
module.exports.app = (appExports, model, app) ->
browser = require './browser'
_currentTime = model.at '_currentTime'
_currentTime.setNull +new Date
# Every 60 seconds, reset the current time so that the chat can update relative times
setInterval (->_currentTime.set +new Date), 60000
appExports.groupCreate = (e,el) ->
type = $(el).attr('data-type')
newGroup =
name: model.get("_new.group.name")
description: model.get("_new.group.description")
leader: user.get('id')
members: [user.get('id')]
type: type
# parties - free
if type is 'party'
return model.add 'groups', newGroup, ->location.reload()
# guilds - 4G
unless user.get('balance') >= 1
return $('#more-gems-modal').modal 'show'
if confirm "Create Guild for 4 Gems?"
newGroup.privacy = (model.get("_new.group.privacy") || 'public') if type is 'guild'
newGroup.balance = 1 # they spent $ to open the guild, it goes into their guild bank
model.add 'groups', newGroup, ->
user.incr 'balance', -1, ->location.reload()
appExports.toggleGroupEdit = (e, el) ->
path = "_editing.groups.#{$(el).attr('data-gid')}"
model.set path, !model.get(path)
appExports.toggleLeaderMessageEdit = (e, el) ->
path = "_editing.leaderMessage.#{$(el).attr('data-gid')}"
model.set path, !model.get(path)
appExports.groupAddWebsite = (e, el) ->
test = e.get()
e.at().unshift 'websites', model.get('_newGroupWebsite')
model.del '_newGroupWebsite'
appExports.groupInvite = (e,el) ->
uid = model.get('_groupInvitee').replace(/[\s"]/g, '')
model.set '_groupInvitee', ''
return if _.isEmpty(uid)
model.query('users').publicInfo([uid]).fetch (err, profiles) ->
throw err if err
profile = profiles.at(0).get()
return model.set("_groupError", "User with id #{uid} not found.") unless profile
model.query('groups').withMember(uid).fetch (err, g) ->
throw err if err
group = e.get(); groups = g.get()
{type, name} = group; gid = group.id
groupError = (msg) -> model.set("_groupError", msg)
invite = ->
$.bootstrapGrowl "Invitation Sent."
switch type
when 'guild' then model.push "users.#{uid}.invitations.guilds", {id:gid, name}, ->location.reload()
when 'party' then model.set "users.#{uid}.invitations.party", {id:gid, name}, ->location.reload()
switch type
when 'guild'
if profile.invitations?.guilds and _.find(profile.invitations.guilds, {id:gid})
return groupError("User already invited to that group")
else if uid in group.members
return groupError("User already in that group")
else invite()
when 'party'
if profile.invitations?.party
return groupError("User already pending invitation.")
else if _.find(groups, {type:'party'})
return groupError("User already in a party.")
else invite()
appExports.acceptInvitation = (e,el) ->
gid = e.get('id')
if $(el).attr('data-type') is 'party'
user.set 'invitations.party', null, ->joinGroup(gid)
else
e.at().remove ->joinGroup(gid)
appExports.rejectInvitation = (e, el) ->
clear = -> browser.resetDom(model)
if e.at().path().indexOf('party') != -1
model.del e.at().path(), clear
else e.at().remove clear
appExports.groupLeave = (e,el) ->
if confirm("Leave this group, are you sure?") is true
uid = user.get('id')
group = model.at "groups.#{$(el).attr('data-id')}"
index = group.get('members').indexOf(uid)
if index != -1
group.remove 'members', index, 1, ->
updated = group.get()
# last member out, delete the party
if _.isEmpty(updated.members) and (updated.type is 'party')
group.del ->location.reload()
# assign new leader, so the party is editable #TODO allow old leader to assign new leader, this is just random
else if (updated.leader is uid)
group.set "leader", updated.members[0], ->location.reload()
else location.reload()
###
Chat Functionality
###
model.on 'unshift', '_party.chat', -> $('.chat-message').tooltip()
model.on 'unshift', '_habitrpg.chat', -> $('.chat-message').tooltip()
appExports.chatKeyup = (e, el, next) ->
return next() unless e.keyCode is 13
appExports.sendChat(e, el)
appExports.deleteChatMessage = (e) ->
if confirm("Delete chat message?") is true
e.at().remove() #requires the {#with}
app.on 'render', (ctx) ->
$('#party-tab-link').on 'shown', (e) ->
messages = model.get('_party.chat')
return false unless messages?.length > 0
model.set '_user.party.lastMessageSeen', messages[0].id
appExports.gotoPartyChat = ->
model.set '_gamePane', true, ->
$('#party-tab-link').tab('show')
appExports.assignGroupLeader = (e, el) ->
newLeader = model.get('_new.groupLeader')
if newLeader and (confirm("Assign new leader, you sure?") is true)
e.at().set('leader', newLeader, ->browser.resetDom(model)) if newLeader

View File

@@ -0,0 +1,7 @@
i18n = require 'derby-i18n'
i18n.plurals.add 'he', (n) -> n
i18n.plurals.add 'bg', (n) -> n
i18n.plurals.add 'nl', (n) -> n
module.exports = i18n

View File

@@ -0,0 +1,20 @@
# Translations
i18n = require './i18n'
i18n.localize app,
availableLocales: ['en', 'he', 'bg', 'nl']
defaultLocale: 'en'
urlScheme: false
checkHeader: true
# ========== CONTROLLER FUNCTIONS ==========
ready (model) ->
misc.fixCorruptUser(model) # https://github.com/lefnire/habitrpg/issues/634
# used for things like remove website, chat, etc
exports.removeAt = (e, el) ->
if (confirmMessage = $(el).attr 'data-confirm')?
return unless confirm(confirmMessage) is true
e.at().remove()
browser.resetDom(model) if $(el).attr('data-refresh')

View File

@@ -0,0 +1,39 @@
items = require 'habitrpg-shared/script/items'
_ = require 'lodash'
updateStore = (model) ->
nextItems = items.updateStore(model.get('_user'))
_.each nextItems, (v,k) -> model.set("_items.next.#{k}",v); true
###
server exports
###
module.exports.server = (model) ->
model.set '_items', items.items
updateStore(model)
###
app exports
###
module.exports.app = (appExports, model) ->
misc = require './misc'
model.on "set", "_user.items.*", -> updateStore(model)
appExports.buyItem = (e, el) ->
misc.batchTxn model, (uObj, paths) ->
ret = items.buyItem uObj, $(el).attr('data-type'), {paths}
alert("Not enough GP") if ret is false
appExports.activateRewardsTab = ->
model.set '_activeTabRewards', true
model.set '_activeTabPets', false
appExports.activatePetsTab = ->
model.set '_activeTabPets', true
model.set '_activeTabRewards', false

View File

@@ -0,0 +1,152 @@
_ = require 'lodash'
algos = require 'habitrpg-shared/script/algos'
items = require('habitrpg-shared/script/items').items
helpers = require('habitrpg-shared/script/helpers')
#TODO put this in habitrpg-shared
###
We can't always use refLists, but we often still need to get a positional path by id: eg, users.1234.tasks.5678.value
For arrays (which use indexes, not id-paths), here's a helper function so we can run indexedPath('users',:user.id,'tasks',:task.id,'value)
###
indexedPath = ->
_.reduce arguments, (m,v) =>
return v if !m #first iteration
return "#{m}.#{v}" if _.isString v #string paths
return "#{m}." + _.findIndex(@model.get(m),v)
, ''
taskInChallenge = (task) ->
return undefined unless task?.challenge
@model.at indexedPath.call(@, "groups.#{task.group.id}.challenges", {id:task.challenge}, "#{task.type}s", {id:task.id})
###
algos.score wrapper for habitrpg-helpers to work in Derby. We need to do model.set() instead of simply setting the
object properties, and it's very difficult to diff the two objects and find dot-separated paths to set. So we to first
clone our user object (if we don't do that, it screws with model.on() listeners, ping Tyler for an explaination),
perform the updates while tracking paths, then all the values at those paths
###
module.exports.score = (model, taskId, direction, allowUndo=false) ->
drop = undefined
delta = batchTxn model, (uObj, paths) ->
tObj = uObj.tasks[taskId]
# Stuff for undo
if allowUndo
tObjBefore = _.cloneDeep tObj
tObjBefore.completed = !tObjBefore.completed if tObjBefore.type in ['daily', 'todo']
previousUndo = model.get('_undo')
clearTimeout(previousUndo.timeoutId) if previousUndo?.timeoutId
timeoutId = setTimeout (-> model.del('_undo')), 20000
model.set '_undo', {stats:_.cloneDeep(uObj.stats), task:tObjBefore, timeoutId: timeoutId}
delta = algos.score(uObj, tObj, direction, {paths})
model.set('_streakBonus', uObj._tmp.streakBonus) if uObj._tmp?.streakBonus
drop = uObj._tmp?.drop
# Update challenge statistics
# FIXME put this in it's own batchTxn, make batchTxn model.at() ref aware (not just _user)
# FIXME use reflists for users & challenges
if (chalTask = taskInChallenge.call({model}, tObj)) and chalTask?.get()
model._dontPersist = false
chalTask.incr "value", delta
chal = model.at indexedPath.call({model}, "groups.#{tObj.group.id}.challenges", {id:tObj.challenge})
chalUser = -> indexedPath.call({model}, chal.path(), 'users', {id:uObj.id})
cu = model.at chalUser()
unless cu?.get()
chal.push "users", {id: uObj.id, name: helpers.username(uObj.auth, uObj.profile?.name)}
cu = model.at chalUser()
else
cu.set 'name', helpers.username(uObj.auth, uObj.profile?.name) # update their name incase it changed
cu.set "#{tObj.type}s.#{tObj.id}",
value: tObj.value
history: tObj.history
model._dontPersist = true
, done:->
if drop and $?
model.set '_drop', drop
$('#item-dropped-modal').modal 'show'
delta
###
Cleanup task-corruption (null tasks, rogue/invisible tasks, etc)
Obviously none of this should be happening, but we'll stop-gap until we can find & fix
Gotta love refLists! see https://github.com/lefnire/habitrpg/issues/803 & https://github.com/lefnire/habitrpg/issues/6343
###
module.exports.fixCorruptUser = (model) ->
user = model.at('_user')
tasks = user.get('tasks')
## Remove corrupted tasks
_.each tasks, (task, key) ->
unless task?.id? and task?.type?
user.del("tasks.#{key}")
delete tasks[key]
true
resetDom = false
batchTxn model, (uObj, paths, batch) ->
## fix https://github.com/lefnire/habitrpg/issues/1086
uniqPets = _.uniq(uObj.items.pets)
batch.set('items.pets', uniqPets) if !_.isEqual(uniqPets, uObj.items.pets)
if uObj.invitations?.guilds
uniqInvites = _.uniq(uObj.invitations.guilds)
batch.set('invitations.guilds', uniqInvites) if !_.isEqual(uniqInvites, uObj.invitations.guilds)
## Task List Cleanup
['habit','daily','todo','reward'].forEach (type) ->
# 1. remove duplicates
# 2. restore missing zombie tasks back into list
idList = uObj["#{type}Ids"]
taskIds = _.pluck( _.where(tasks, {type}), 'id')
union = _.union idList, taskIds
# 2. remove empty (grey) tasks
preened = _.filter union, (id) -> id and _.contains(taskIds, id)
# There were indeed issues found, set the new list
if !_.isEqual(idList, preened)
batch.set("#{type}Ids", preened)
console.error uObj.id + "'s #{type}s were corrupt."
true
resetDom = !_.isEmpty(paths)
require('./browser').resetDom(model) if resetDom
module.exports.viewHelpers = (view) ->
#misc
view.fn "percent", (x, y) ->
x=1 if x==0
Math.round(x/y*100)
view.fn 'indexOf', (str1, str2) ->
return false unless str1 && str2
str1.indexOf(str2) != -1
view.fn "round", Math.round
view.fn "floor", Math.floor
view.fn "ceil", Math.ceil
view.fn "truarr", (num) -> num-1
view.fn 'count', (arr) -> arr?.length or 0
view.fn 'int',
get: (num) -> num
set: (num) -> [parseInt(num)]
view.fn 'indexedPath', indexedPath
#iCal
view.fn "encodeiCalLink", helpers.encodeiCalLink
#User
view.fn "gems", (balance) -> balance * 4
#Challenges
view.fn 'taskInChallenge', (task) ->
taskInChallenge.call(@,task)?.get()
view.fn 'taskAttrFromChallenge', (task, attr) ->
taskInChallenge.call(@,task)?.get(attr)
view.fn 'brokenChallengeLink', (task) ->
task?.challenge and !(taskInChallenge.call(@,task)?.get())
view.fn 'challengeMemberScore', (member, tType, tid) ->
Math.round(member["#{tType}s"]?[tid]?.value)

View File

@@ -0,0 +1,73 @@
_ = require 'lodash'
{ randomVal } = require 'habitrpg-shared/script/helpers'
{ pets, hatchingPotions } = require('habitrpg-shared/script/items').items
###
app exports
###
module.exports.app = (appExports, model) ->
user = model.at '_user'
appExports.chooseEgg = (e, el) ->
model.ref '_hatchEgg', e.at()
appExports.hatchEgg = (e, el) ->
hatchingPotionName = $(el).children('select').val()
myHatchingPotion = user.get 'items.hatchingPotions'
egg = model.get '_hatchEgg'
eggs = user.get 'items.eggs'
myPets = user.get 'items.pets'
hatchingPotionIdx = myHatchingPotion.indexOf hatchingPotionName
eggIdx = eggs.indexOf egg
return alert "You don't own that hatching potion yet, complete more tasks!" if hatchingPotionIdx is -1
return alert "You don't own that egg yet, complete more tasks!" if eggIdx is -1
return alert "You already have that pet, hatch a different combo." if myPets and myPets.indexOf("#{egg.name}-#{hatchingPotionName}") != -1
user.push 'items.pets', egg.name + '-' + hatchingPotionName, ->
eggs.splice eggIdx, 1
myHatchingPotion.splice hatchingPotionIdx, 1
user.set 'items.eggs', eggs
user.set 'items.hatchingPotions', myHatchingPotion
alert 'Your egg hatched! Visit your stable to equip your pet.'
#FIXME Bug: this removes from the array properly in the browser, but on refresh is has removed all items from the arrays
# user.remove 'items.hatchingPotions', hatchingPotionIdx, 1
# user.remove 'items.eggs', eggIdx, 1
appExports.choosePet = (e, el, next) ->
petStr = $(el).attr('data-pet')
return next() if user.get('items.pets').indexOf(petStr) == -1
# If user's pet is already active, deselect it
return user.set 'items.currentPet', {} if user.get('items.currentPet.str') is petStr
[name, modifier] = petStr.split('-')
pet = _.find pets, {name: name}
pet.modifier = modifier
pet.str = petStr
user.set 'items.currentPet', pet
appExports.buyHatchingPotion = (e, el) ->
name = $(el).attr 'data-hatchingPotion'
newHatchingPotion = _.find hatchingPotions, {name: name}
gems = user.get('balance') * 4
if gems >= newHatchingPotion.value
if confirm "Buy this hatching potion with #{newHatchingPotion.value} of your #{gems} Gems?"
user.push 'items.hatchingPotions', newHatchingPotion.name
user.set 'balance', (gems - newHatchingPotion.value) / 4
else
$('#more-gems-modal').modal 'show'
appExports.buyEgg = (e, el) ->
name = $(el).attr 'data-egg'
newEgg = _.find pets, {name: name}
gems = user.get('balance') * 4
if gems >= newEgg.value
if confirm "Buy this egg with #{newEgg.value} of your #{gems} Gems?"
user.push 'items.eggs', newEgg
user.set 'balance', (gems - newEgg.value) / 4
else
$('#more-gems-modal').modal 'show'

View File

@@ -0,0 +1,29 @@
algos = require 'habitrpg-shared/script/algos'
helpers = require 'habitrpg-shared/script/helpers'
_ = require 'lodash'
moment = require 'moment'
misc = require './misc'
appExports.clearCompleted = (e, el) ->
completedIds = _.pluck( _.where(model.get('_todoList'), {completed:true}), 'id')
todoIds = user.get('todoIds')
_.each completedIds, (id) -> user.del "tasks.#{id}"; true
user.set 'todoIds', _.difference(todoIds, completedIds)
###
Undo
###
appExports.undo = () ->
undo = model.get '_undo'
clearTimeout(undo.timeoutId) if undo?.timeoutId
model.del '_undo'
_.each undo.stats, (val, key) -> user.set "stats.#{key}", val; true
taskPath = "tasks.#{undo.task.id}"
_.each undo.task, (val, key) ->
return true if key in ['id', 'type'] # strange bugs in this world: https://workflowy.com/shared/a53582ea-43d6-bcce-c719-e134f9bf71fd/
if key is 'completed'
user.pass({cron:true}).set("#{taskPath}.completed",val)
else
user.set "#{taskPath}.#{key}", val
true

View File

@@ -0,0 +1,95 @@
_ = require 'lodash'
{ randomVal } = require 'habitrpg-shared/script/helpers'
{ pets, hatchingPotions } = require('habitrpg-shared/script/items').items
###
Listeners to enabled flags, set notifications to the user when they've unlocked features
###
module.exports.app = (appExports, model) ->
user = model.at('_user')
alreadyShown = (before, after) -> !(!before and after is true)
showPopover = (selector, title, html, placement='bottom') ->
$(selector).popover('destroy')
html += " <a href='#' onClick=\"$('#{selector}').popover('hide');return false;\">[Close]</a>"
$(selector).popover({
title: title
placement: placement
trigger: 'manual'
html: true
content: html
}).popover 'show'
user.on 'set', 'flags.customizationsNotification', (after, before) ->
return if alreadyShown(before,after)
$('.main-herobox').popover('destroy') #remove previous popovers
html = "Click your avatar to customize your appearance."
showPopover '.main-herobox', 'Customize Your Avatar', html, 'bottom'
user.on 'set', 'flags.itemsEnabled', (after, before) ->
return if alreadyShown(before,after)
html = """
<img src='/vendor/BrowserQuest/client/img/1/chest.png' />
Congratulations, you have unlocked the Item Store! You can now buy weapons, armor, potions, etc. Read each item's comment for more information.
"""
showPopover 'div.rewards', 'Item Store Unlocked', html, 'left'
user.on 'set', 'flags.petsEnabled', (after, before) ->
return if alreadyShown(before,after)
html = """
<img src='/img/sprites/wolf_border.png' style='width:30px;height:30px;float:left;padding-right:5px' />
You have unlocked Pets! You can now buy pets with Gems (note, you replenish Gems with real-life money - so chose your pets wisely!)
"""
showPopover '#rewardsTabs', 'Pets Unlocked', html, 'left'
user.on 'set', 'flags.partyEnabled', (after, before) ->
return if user.get('party.current') or alreadyShown(before,after)
html = """
Be social, join a party and play Habit with your friends! You'll be better at your habits with accountability partners. Click User -> Options -> Party, and follow the instructions. LFG anyone?
"""
showPopover '.user-menu', 'Party System', html, 'bottom'
user.on 'set', 'flags.dropsEnabled', (after, before) ->
return if alreadyShown(before,after)
egg = randomVal pets
dontPersist = model._dontPersist
model._dontPersist = false
user.push 'items.eggs', egg
model._dontPersist = dontPersist
$('#drops-enabled-modal').modal 'show'
user.on 'push', 'items.pets', (after, before) ->
return if user.get('achievements.beastMaster')
if before >= 90 # evidently before is the count?
dontPersist = model._dontPersist; model._dontPersist = false
user.set 'achievements.beastMaster', true, (-> model._dontPersist = dontPersist)
$('#beastmaster-achievement-modal').modal('show')
user.on 'set', 'items.*', (after, before) ->
return if user.get('achievements.ultimateGear')
items = user.get('items')
if parseInt(items.weapon) >= 6 and parseInt(items.armor) >= 5 and parseInt(items.head) >= 5 and parseInt(items.shield) >= 5
dontPersist = model._dontPersist; model._dontPersist = false
user.set 'achievements.ultimateGear', true, (->model._dontPersist = dontPersist)
$('#max-gear-achievement-modal').modal('show')
user.on 'set', 'tasks.*.streak', (id, after, before) ->
if after > 0
# 21-day streak, as per the old philosophy of doign a thing 21-days in a row makes a habit
if (after % 21) is 0
dontPersist = model._dontPersist; model._dontPersist = false
user.incr 'achievements.streak', 1, (-> model._dontPersist = dontPersist)
$('#streak-achievement-modal').modal('show')
# they're undoing a task at the 21 mark, take back their badge
else if (before - after is 1) and (before % 21 is 0)
dontPersist = model._dontPersist; model._dontPersist = false
user.incr 'achievements.streak', -1, (-> model._dontPersist = dontPersist)

53
bower.json Normal file
View File

@@ -0,0 +1,53 @@
{
"name": "HabitRPG",
"version": "0.1.1",
"homepage": "https://github.com/lefnire/habitrpg",
"authors": [
"Tyler Renelle <tylerrenelle@gmail.com>"
],
"private": true,
"ignore": [
"**/.*",
"node_modules",
"public/bower_components",
"test",
"tests"
],
"dependencies": {
"jquery": "~2.1.0",
"jquery.cookie": "~1.4.0",
"angular": "1.3.9",
"angular-ui": "~0.4.0",
"angular-sanitize": "1.3.9",
"angular-resource": "1.3.9",
"angular-ui-utils": "~0.1.0",
"angular-ui-select2": "git://github.com/angular-ui/ui-select2.git",
"angular-bootstrap": "~0.12.0",
"angular-loading-bar": "~0.6.0",
"bootstrap": "~3.1.0",
"bootstrap-growl": "git://github.com/ifightcrime/bootstrap-growl.git#master",
"bootstrap-tour": "~0.10.1",
"BrowserQuest": "git://github.com/browserquest/BrowserQuest.git",
"github-buttons": "git://github.com/mdo/github-buttons.git",
"marked": "~0.2.9",
"Angular-At-Directive": "git://github.com/snicker/Angular-At-Directive#master",
"js-emoji": "https://github.com/iamcal/js-emoji.git",
"sticky": "*",
"swagger-ui": "git://github.com/wordnik/swagger-ui.git",
"ngInfiniteScroll": "1.0.0",
"jquery-colorbox": "~1.4.36",
"pnotify": "~1.3.1",
"jquery-ui": "~1.10.3",
"hello": "~1.3.1",
"css-social-buttons": "https://github.com/samcollins/css-social-buttons.git",
"angular-filter": "~0.5.1",
"angular-ui-router": "~0.2.13"
},
"devDependencies": {
"angular-mocks": "1.3.9"
},
"resolutions": {
"angular": "1.3.9",
"jquery": ">=1.9.0"
}
}

16
common/README.md Normal file
View File

@@ -0,0 +1,16 @@
# Common
Shared resources useful for the multiple HabitRPG repositories, that way all the repositories remain in-sync with common characteristics. Includes things like:
* Assets - sprites, images, etc
* CSS - especially, esp. sprite-sheet mapping
* Algorithms - level up algorithm, scoring functions, etc
* View helper functions that may come in handy for multiple client MVCs
* Item definitions - weapons, armor, pets
## Compiling spritesheets
Because of some wonkiness with Heroku, the spritesheet compilation is not part of the production build process and must be done manually when new images are added by running:
``` bash
grunt compile:sprites
```

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

View File

BIN
common/audio/wattsTheme/Daily.mp3 Executable file

Binary file not shown.

BIN
common/audio/wattsTheme/Daily.ogg Executable file

Binary file not shown.

BIN
common/audio/wattsTheme/Death.mp3 Executable file

Binary file not shown.

BIN
common/audio/wattsTheme/Death.ogg Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
common/audio/wattsTheme/ToDo.mp3 Executable file

Binary file not shown.

BIN
common/audio/wattsTheme/ToDo.ogg Executable file

Binary file not shown.

View File

@@ -14,7 +14,7 @@
/* Critical */
.weapon_special_critical {
background: url("../img/sprites/backer-only/weapon_special_critical.gif") no-repeat;
background: url("/common/img/sprites/backer-only/weapon_special_critical.gif") no-repeat;
width: 90px;
height: 90px;
margin-left:-12px;
@@ -31,30 +31,30 @@
}
.head_special_0 {
background: url("../img/sprites/backer-only/BackerOnly-Equip-ShadeHelmet.gif") no-repeat;
background: url("/common/img/sprites/backer-only/BackerOnly-Equip-ShadeHelmet.gif") no-repeat;
}
.head_special_1 {
background: url("../img/sprites/backer-only/ContributorOnly-Equip-CrystalHelmet.gif") no-repeat;
background: url("/common/img/sprites/backer-only/ContributorOnly-Equip-CrystalHelmet.gif") no-repeat;
margin-top: 3px;
}
.broad_armor_special_0,.slim_armor_special_0 {
background: url("../img/sprites/backer-only/BackerOnly-Equip-ShadeArmor.gif") no-repeat;
background: url("/common/img/sprites/backer-only/BackerOnly-Equip-ShadeArmor.gif") no-repeat;
}
.broad_armor_special_1,.slim_armor_special_1 {
background: url("../img/sprites/backer-only/ContributorOnly-Equip-CrystalArmor.gif") no-repeat;
background: url("/common/img/sprites/backer-only/ContributorOnly-Equip-CrystalArmor.gif") no-repeat;
}
.shield_special_0 {
background: url("../img/sprites/backer-only/BackerOnly-Shield-TormentedSkull.gif") no-repeat;
background: url("/common/img/sprites/backer-only/BackerOnly-Shield-TormentedSkull.gif") no-repeat;
}
.weapon_special_0 {
background: url("../img/sprites/backer-only/BackerOnly-Weapon-DarkSoulsBlade.gif") no-repeat;
background: url("/common/img/sprites/backer-only/BackerOnly-Weapon-DarkSoulsBlade.gif") no-repeat;
}
.Pet-Wolf-Cerberus {
width: 105px;
height: 72px;
background: url("../img/sprites/backer-only/BackerOnly-Pet-CerberusPup.gif") no-repeat;
}
background: url("/common/img/sprites/backer-only/BackerOnly-Pet-CerberusPup.gif") no-repeat;
}

View File

@@ -2,12 +2,12 @@
margin-left: 1.25em;
}*/
/* Comment out for holiday events
/* Comment out for holiday events */
.npc_ian {
background: url("../img/sprites/npc_ian.gif") no-repeat;
background: url("/common/img/sprites/npc_ian.gif") no-repeat;
width: 78px;
height: 135px;
} */
}
.Gems {
display:inline-block;

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

BIN
common/dist/sprites/spritesmith0.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

File diff suppressed because it is too large Load Diff

BIN
common/dist/sprites/spritesmith1.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

File diff suppressed because it is too large Load Diff

BIN
common/dist/sprites/spritesmith2.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

File diff suppressed because it is too large Load Diff

BIN
common/dist/sprites/spritesmith3.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

File diff suppressed because it is too large Load Diff

BIN
common/dist/sprites/spritesmith4.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

File diff suppressed because it is too large Load Diff

BIN
common/dist/sprites/spritesmith5.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

Before

Width:  |  Height:  |  Size: 387 B

After

Width:  |  Height:  |  Size: 387 B

View File

Before

Width:  |  Height:  |  Size: 729 B

After

Width:  |  Height:  |  Size: 729 B

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Some files were not shown because too many files have changed in this diff Show More