From 83e3a6aff293cdc3efb2b18982a5b0fd0e303646 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Sat, 16 Nov 2013 02:22:12 -0800 Subject: [PATCH] classes WIP --- public/css/header.styl | 2 + public/css/index.styl | 11 +++- public/css/tasks.styl | 2 +- public/js/controllers/rootCtrl.js | 49 ++++++++++++++++- public/js/controllers/tasksCtrl.js | 7 +++ public/js/controllers/userCtrl.js | 21 +++++++ src/controllers/user.js | 61 +++++++++++++++++++++ src/models/user.js | 30 +++++++++- src/routes/api.js | 1 + views/index.jade | 5 +- views/options/profile.jade | 88 +++++++++++++++++++++++++----- views/shared/header/avatar.jade | 28 ++++++---- views/shared/header/header.jade | 9 ++- views/shared/modals/classes.jade | 45 +++++++++++++++ views/shared/modals/index.jade | 3 +- views/shared/tasks/lists.jade | 18 +++++- views/shared/tasks/task.jade | 10 +++- 17 files changed, 351 insertions(+), 39 deletions(-) create mode 100644 views/shared/modals/classes.jade diff --git a/public/css/header.styl b/public/css/header.styl index a12f1b1408..f5013ec182 100644 --- a/public/css/header.styl +++ b/public/css/header.styl @@ -150,6 +150,8 @@ background: darken($worse, 38%) .experience .bar background: darken($neutral, 30%) + .mana .bar + background: darken($best, 30%) .meter-text position: absolute diff --git a/public/css/index.styl b/public/css/index.styl index 62c4b06f1a..c8af436a16 100644 --- a/public/css/index.styl +++ b/public/css/index.styl @@ -173,4 +173,13 @@ a .modal-indented-list margin-left: 10px; - padding-left: 10px; \ No newline at end of file + padding-left: 10px; + +// Spells + +html.applying-action, html.applying-action * + cursor: copy !important +.cast-target:hover + border: 5px solid green !important +.selected-class + background-color: green \ No newline at end of file diff --git a/public/css/tasks.styl b/public/css/tasks.styl index 680fd1ce65..1cd7709df9 100644 --- a/public/css/tasks.styl +++ b/public/css/tasks.styl @@ -402,4 +402,4 @@ form color: #333 &:hover border-top: 0 - margin-top: 2px + margin-top: 2px \ No newline at end of file diff --git a/public/js/controllers/rootCtrl.js b/public/js/controllers/rootCtrl.js index 607bd333e7..bbf43d1a84 100644 --- a/public/js/controllers/rootCtrl.js +++ b/public/js/controllers/rootCtrl.js @@ -3,8 +3,8 @@ /* Make user and settings available for everyone through root scope. */ -habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$http', '$state', '$stateParams', - function($scope, $rootScope, $location, User, $http, $state, $stateParams) { +habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$http', '$state', '$stateParams', 'Notification', + function($scope, $rootScope, $location, User, $http, $state, $stateParams, Notification) { $rootScope.modals = {}; $rootScope.modals.achievements = {}; $rootScope.User = User; @@ -121,5 +121,50 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ chart = new google.visualization.LineChart($("." + id + "-chart")[0]); chart.draw(data, options); }; + + + + /* + ------------------------ + Spells + ------------------------ + */ + $scope.castStart = function(spell) { + if (User.user.stats.mp < spell.mana) return Notification.text("Not enough mana."); + $rootScope.applyingAction = true; + $scope.spell = spell; + if (spell.target == 'self') { + debugger + var tasks = User.user.habits.concat(User.user.dailys).concat(User.user.todos); + User.user.tasks = _.object(_.pluck(tasks,'id'), tasks); + $scope.castEnd(null, 'self'); + } else if (spell.target == 'party') { + //TODO $scope.castEnd() + } + } + + $scope.castEnd = function(target, type, $event){ + if ($scope.spell.target != type) return Notification.text("Invalid target"); + $scope.spell.cast(User.user, target); + $http.post('/api/v1/user/cast/' + $scope.spell.name, {target:target, type:type}).success(function(){ + var msg = "You cast " + $scope.spell.text; + switch (type) { + case 'task': msg += ' on ' + target.text;break; + case 'user': msg += ' on ' + target.profile.name;break; + case 'party': msg += ' on the Party';break; + } + Notification.text(msg); + $rootScope.applyingAction = false; + $scope.spell = null; + //User.sync(); // FIXME push a lot of the server code to also in client, so we can run updates in browser without requiring sync + }) + $event && $event.stopPropagation(); + } + +// $rootScope.castCancel = function(){ +// debugger +// $rootScope.applyingAction = false; +// $scope.spell = null; +// } } ]); diff --git a/public/js/controllers/tasksCtrl.js b/public/js/controllers/tasksCtrl.js index 5b599e9473..323f944f28 100644 --- a/public/js/controllers/tasksCtrl.js +++ b/public/js/controllers/tasksCtrl.js @@ -132,6 +132,13 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User', ' User.log({op: 'clear-completed'}); } + + /* + ------------------------ + Ads + ------------------------ + */ + /** * See conversation on http://productforums.google.com/forum/#!topic/adsense/WYkC_VzKwbA, * Adsense is very sensitive. It must be called once-and-only-once for every , else things break. diff --git a/public/js/controllers/userCtrl.js b/public/js/controllers/userCtrl.js index 793f54c9f7..ab5f3bea1c 100644 --- a/public/js/controllers/userCtrl.js +++ b/public/js/controllers/userCtrl.js @@ -14,6 +14,27 @@ habitrpg.controller("UserCtrl", ['$rootScope', '$scope', '$location', 'User', '$ if(value === true) $scope.editingProfile = angular.copy(User.user.profile); }); + $scope.allocate = function(stat){ + var setObj = {} + setObj['stats.' + stat] = User.user.stats[stat] + 1; + setObj['stats.points'] = User.user.stats.points - 1; + User.setMultiple(setObj); + } + + $scope.rerollClass = function(){ + if (!confirm("Are you sure you want to re-roll? This will reset your character's class and allocated points (you'll get them all back to re-allocate)")) + return; + User.setMultiple({ + 'stats.class': '', + //'stats.points': this is handled on the server + 'stats.str': 0, + 'stats.def': 0, + 'stats.per': 0, + 'stats.int': 0 + }) + + } + $scope.save = function(){ var values = {}; _.each($scope.editingProfile, function(value, key){ diff --git a/src/controllers/user.js b/src/controllers/user.js index 560b5a7776..915ed348ae 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -482,6 +482,67 @@ api.deleteTag = function(req, res){ } } +/* + ------------------------------------------------------------------------ + Spells + ------------------------------------------------------------------------ + */ +api.cast = function(req, res) { + var user = res.locals.user; + var type = req.body.type, target = req.body.target; + var spell = items.items.spells[user.stats.class][req.params.spell]; + + var done = function(){ + var err = arguments[0]; + var saved = _.size(arguments == 3) ? arguments[2] : arguments[1]; + if (err) return res.json(500, {err:err}); + res.json(saved); + } + + switch (type) { + case 'task': + spell.cast(user, user.tasks[target.id]); + user.save(done); + break; + + case 'self': + spell.cast(user); + user.save(done); + break; + + case 'party': + async.waterfall([ + function(cb){ + Group.findOne({type: 'party', members: {'$in': [user._id]}}).populate('members').exec(cb); + }, + function(group, cb) { + spell.cast(user, group.members); + var series = _.reduce(group.members, function(m,v){ + m.push(function(cb2){v.save(cb2);}) + return m; + }, []); + async.series(series, cb); + } + ], done); + break; + + case 'user': + async.waterfall([ + function(cb) { + User.findById(target._id, cb); + }, + function(member, cb) { + spell.cast(user, member); + member.save(cb); // not parallel because target could be user, which causes race condition when saving + }, + function(saved, num, cb) { + user.save(cb); + } + ], done); + break; + } +} + /* ------------------------------------------------------------------------ Batch Update diff --git a/src/models/user.js b/src/models/user.js index 94dd5cb9f9..80a11b878d 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -188,12 +188,17 @@ var UserSchema = new Schema({ armorSet: String, dayStart: {type:Number, 'default': 0}, gender: {type:String, 'default': 'm'}, - hair: {type:String, 'default':'blond'}, + hair: { + color: {type: String, 'default': 'blonde'}, + base: {type: Number, 'default': 0}, + bangs: {type: Number, 'default': 0} + }, hideHeader: {type:Boolean, 'default':false}, showHelm: {type:Boolean, 'default':true}, skin: {type:String, 'default':'white'}, timezoneOffset: Number, - language: String + language: String, + automaticAllocation: Boolean }, profile: { blurb: String, @@ -202,9 +207,24 @@ var UserSchema = new Schema({ }, stats: { hp: Number, + mp: Number, exp: Number, gp: Number, - lvl: Number + lvl: Number, + + // Class System + 'class': String, + points: {type: Number, 'default': 0}, + str: {type: Number, 'default': 0}, + def: {type: Number, 'default': 0}, + int: {type: Number, 'default': 0}, + per: {type: Number, 'default': 0}, + buffs: { + str: Number, + def: Number, + per: Number, + stealth: Number + } }, tags: [ { @@ -264,6 +284,10 @@ UserSchema.pre('save', function(next) { 'Anonymous'; } + // FIXME handle this on level-up instead, and come up with how we're going to handle retroactively + // Actually, can this be used as an attr default? (schema {type: ..., 'default': function(){}}) + this.stats.points = this.stats.lvl - (this.stats.def + this.stats.str + this.stats.per + this.stats.int); + var petCount = _.reduce(this.items.pets,function(m,v){return m+(v ? 1 : 0);},0); this.achievements.beastMaster = petCount >= 90; diff --git a/src/routes/api.js b/src/routes/api.js index cdee266826..34c3c3327d 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -60,6 +60,7 @@ router.post('/user/buy-gems', auth.auth, user.buyGems); router.post('/user/buy-gems/paypal-ipn', user.buyGemsPaypalIPN); router.post('/user/unlock', auth.auth, cron, user.unlock); router.post('/user/reset', auth.auth, user.reset); +router.post('/user/cast/:spell', auth.auth, user.cast); router['delete']('/user', auth.auth, user['delete']); /* Tags */ diff --git a/views/index.jade b/views/index.jade index 733bdca2ae..b28e87b716 100644 --- a/views/index.jade +++ b/views/index.jade @@ -1,5 +1,6 @@ doctype 5 -html +//html(ng-app="habitrpg", ng-controller="RootCtrl", ng-class='{"applying-action":applyingAction}', ui-keypress="{27:'castCancel()'}") +html(ng-app="habitrpg", ng-controller="RootCtrl", ng-class='{"applying-action":applyingAction}', ui-keypress="{27:'castCancel()'}") head title HabitRPG | Your Life The Role Playing Game @@ -21,7 +22,7 @@ html meta(name='viewport', content='width=device-width') meta(name='apple-mobile-web-app-capable', content='yes') - body(ng-app="habitrpg", ng-controller="RootCtrl", ng-cloak) + body(ng-cloak) div(ng-controller='GroupsCtrl') include ./shared/modals/index include ./shared/header/header diff --git a/views/options/profile.jade b/views/options/profile.jade index 27c5baa885..470cf8758d 100644 --- a/views/options/profile.jade +++ b/views/options/profile.jade @@ -12,21 +12,44 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template') input(type='checkbox', ng-model='user.preferences.showHelm', ng-change='toggleHelm(user.preferences.showHelm)') | Show Helm - menu(ng-show='user.preferences.gender=="f"', type='list') - li.customize-menu - menu(label='Clothing') - button.f_armor_0_v1.customize-option(type='button', ng-click='set("preferences.armorSet","v1")') - button.f_armor_0_v2.customize-option(type='button', ng-click='set("preferences.armorSet","v2")') + menu(ng-show='user.preferences.gender=="f"', type='list') + li.customize-menu + menu(label='Clothing') + button.f_armor_0_v1.customize-option(type='button', ng-click='set("preferences.armorSet","v1")') + button.f_armor_0_v2.customize-option(type='button', ng-click='set("preferences.armorSet","v2")') .span4 - // hair - li.customize-menu - menu(label='Hair') - button(class='{{user.preferences.gender}}_hair_blond customize-option', type='button', ng-click='set("preferences.hair","blond")') - button(class='{{user.preferences.gender}}_hair_black customize-option', type='button', ng-click='set("preferences.hair","black")') - button(class='{{user.preferences.gender}}_hair_brown customize-option', type='button', ng-click='set("preferences.hair","brown")') - button(class='{{user.preferences.gender}}_hair_white customize-option', type='button', ng-click='set("preferences.hair","white")') - button(class='{{user.preferences.gender}}_hair_red customize-option', type='button', ng-click='set("preferences.hair","red")') + h3 Hair + menu(type='list') + // Color + li.customize-menu + menu(label='Color') + button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#c8c8c8;', ng-click='set("preferences.hair.color", "white")') + button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#903a00;', ng-click='set("preferences.hair.color", "brown")') + button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#cfb853;', ng-click='set("preferences.hair.color", "blond")') + button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#ec720f;', ng-click='set("preferences.hair.color", "red")') + button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:#2e2e2e;', ng-click='set("preferences.hair.color", "black")') + + // Bangs + li.customize-menu + menu(label='Bangs') + button(class='{{user.preferences.gender}}_hair_bangs_0_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.bangs",0)') + button(class='{{user.preferences.gender}}_hair_bangs_1_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.bangs",1)') + button(class='{{user.preferences.gender}}_hair_bangs_2_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.bangs",2)') + button(class='{{user.preferences.gender}}_hair_bangs_3_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.bangs",3)') + + // Base + li.customize-menu + menu(label='Base') + button(class='{{user.preferences.gender}}_hair_base_0_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",0)') + button(class='{{user.preferences.gender}}_hair_base_1_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",1)') + button(class='{{user.preferences.gender}}_hair_base_2_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",2)') + button(class='{{user.preferences.gender}}_hair_base_3_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",3)') + button(class='{{user.preferences.gender}}_hair_base_4_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",4)') + button(class='{{user.preferences.gender}}_hair_base_5_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",5)') + button(class='{{user.preferences.gender}}_hair_base_6_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",6)') + button(class='{{user.preferences.gender}}_hair_base_7_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",7)') + button(class='{{user.preferences.gender}}_hair_base_8_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set("preferences.hair.base",8)') .span4 // skin @@ -70,9 +93,44 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template') script(id='partials/options.profile.stats.html', type='text/ng-template') .row-fluid - .span6.border-right + .border-right(ng-class='user.stats.lvl < 5 ? "span6" : "span4"') include ../shared/profiles/stats - .span6.border-right + .span4.border-right(ng-show='user.stats.lvl >= 5') + h4 + | {{user.stats.class}}  + a.btn.btn-danger.btn-mini(ng-click='rerollClass()') Re-roll + h6 Points: {{user.stats.points}} + fieldset + label.checkbox + input(type='checkbox', ng-model='user.preferences.automaticAllocation', ng-change='set("preferences.automaticAllocation", user.preferences.automaticAllocation?true: false)') + | Automatic Allocation + i.icon-question-sign(popover-trigger='mouseenter', popover-placement='bottom', popover="When 'automatic' is checked, your points will be allocated to the stat representing your task focus (see Task > Edit). When unchecked, you'll have one point to allocate each level. The system makes a suggestion, but you can ignore the suggestion.") + table.table.table-striped + tr + td + i.icon-question-sign(popover-title='Strength', popover='Strength increases damage you inflict to tasks, reducing their redness', popover-trigger='mouseenter') + |  STR: {{user.stats.str}} + td + a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("str")') + + tr + td + i.icon-question-sign(popover-title='Defense', popover='Defense decreases damage inflicted by tasks, and increases the effectiveness of healing and buff spells', popover-trigger='mouseenter') + |  DEF: {{user.stats.def}} + td + a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("def")') + + tr + td + i.icon-question-sign(popover-title='Perception', popover='Perception increases the likelihood of drops, Gold bonuses, and critical hit chances', popover-trigger='mouseenter') + |  PER: {{user.stats.per}} + td + a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("per")') + + tr + td + i.icon-question-sign(popover-title='Intelligence', popover='Intelligence increases your overall mana, allowing you to cast more spells', popover-trigger='mouseenter') + |  INT: {{user.stats.int}} + td + a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("int")') + + .border-right(ng-class='user.stats.lvl < 5 ? "span6" : "span4"') include ../shared/profiles/achievements script(id='partials/options.profile.profile.html', type='text/ng-template') diff --git a/views/shared/header/avatar.jade b/views/shared/header/avatar.jade index 3154f5f92d..7e0ac9b371 100644 --- a/views/shared/header/avatar.jade +++ b/views/shared/header/avatar.jade @@ -3,27 +3,35 @@ Removing it will remove the user's name, but will allow more room for mounts & helms. IMO this is the lesser of two evils, revisit //-figure.herobox(ng-click='clickMember(profile._id)', data-name='{{profile.profile.name}}', ng-class='{isUser: profile.id==user.id, hasPet: profile.items.currentPet}', data-level='{{profile.stats.lvl}}', data-uid='{{profile.id}}', rel='popover', data-placement='bottom', data-trigger='hover', data-html='true', data-content="
Level: {{profile.stats.lvl}}
GP: {{profile.stats.gp | number:0}}
{{count(profile.items.pets)}} / 90 Pets Found
") -figure.herobox(ng-click='clickMember(profile._id)', data-name='{{profile.profile.name}}', ng-class='{isUser: user._id==profile._id && !(user.items.currentMount && user.items.currentPet), hasPet: profile.items.currentPet && profile.items.currentMount}') +figure.herobox(ng-click='spell ? castEnd(profile, "user", $event) : clickMember(profile._id)', data-name='{{profile.profile.name}}', ng-class='{isUser: user._id==profile._id && !(user.items.currentMount && user.items.currentPet), hasPet: profile.items.currentPet && profile.items.currentMount, "cast-target": applyingAction}') .character-sprites // Mount Body span(ng-if='profile.items.currentMount', class='Mount_Body_{{profile.items.currentMount}}') // Avatar - span(class='{{profile.preferences.gender}}_skin_{{profile.preferences.skin}}') - span(class='{{profile.preferences.gender}}_hair_{{profile.preferences.hair}}') - span(class='{{equipped("armor", profile.items.armor, profile.preferences, profile.backer, profile.contributor)}}') - span(class='{{profile.preferences.gender}}_head_0', ng-hide='profile.preferences.showHelm') - span(class='{{equipped("head", profile.items.head, profile.preferences, profile.backer, profile.contributor)}}', ng-show='profile.preferences.showHelm') - span(class='{{equipped("shield",profile.items.shield,profile.preferences, profile.backer, profile.contributor)}}') - span(class='{{equipped("weapon",profile.items.weapon,profile.preferences, profile.backer, profile.contributor)}}') + //span(class='{{profile.preferences.gender}}_skin_{{profile.preferences.skin}}') + //span(class='{{profile.preferences.gender}}_head_0') + //span(class='{{profile.preferences.gender}}_hair_base_{{profile.preferences.hair.base}}_{{profile.preferences.hair.color}}') + //span(class='{{profile.preferences.gender}}_hair_bangs_{{profile.preferences.hair.bangs}}_{{profile.preferences.hair.color}}') + //span(class='{{equipped("armor", profile.items.armor, profile.preferences, profile.backer, profile.contributor)}}-{{profile.stats.class}}') + //span(class='{{equipped("head", profile.items.head, profile.preferences, profile.backer, profile.contributor)}}-{{profile.stats.class}}', ng-show='profile.preferences.showHelm') + //span(class='{{equipped("shield",profile.items.shield,profile.preferences, profile.backer, profile.contributor)}}-{{profile.stats.class}}') + //span(class='{{equipped("weapon",profile.items.weapon,profile.preferences, profile.backer, profile.contributor)}}-{{profile.stats.class}}') + + span(class='m_skin_{{profile.preferences.skin}}') + span(class='m_head_0') + span(class='m_hair_base_{{profile.preferences.hair.base}}_{{profile.preferences.hair.color}}') + span(class='m_hair_bangs_{{profile.preferences.hair.bangs}}_{{profile.preferences.hair.color}}') + span(class='m_armor_{{profile.items.armor}}-{{profile.stats.class}}') + span(class='m_head_{{profile.items.head}}-{{profile.stats.class}}', ng-show='profile.preferences.showHelm') + span(class='m_shield_{{profile.items.shield}}-{{profile.stats.class}}') + span(class='m_weapon_{{profile.items.weapon}}-{{profile.stats.class}}') // Mount Head span(ng-if='profile.items.currentMount', class='Mount_Head_{{profile.items.currentMount}}') // Resting span(ng-class='{zzz:profile.flags.rest}') - - // Pet // FIXME handle @minimal, this might have to be a directive span.current-pet(class='Pet-{{profile.items.currentPet}}', ng-show='profile.items.currentPet && !minimal') .avatar-level Lvl {{profile.stats.lvl}} diff --git a/views/shared/header/header.jade b/views/shared/header/header.jade index 626d295786..b37f2be6eb 100644 --- a/views/shared/header/header.jade +++ b/views/shared/header/header.jade @@ -1,5 +1,4 @@ -//.header-wrap(ng-controller='HeaderCtrl', data-spy="affix", data-offset-top="148") -.header-wrap(ng-controller='HeaderCtrl') +.header-wrap(ng-controller='HeaderCtrl', data-spy="affix", data-offset-top="148") a.label.undo-button(x-bind='click:undo', ng-show='_undo') Undo div(ng-if='!user.preferences.hideHeader') include menu @@ -25,6 +24,12 @@ span(ng-show='user.history.exp') a(ng-click='toggleChart("exp")', tooltip='Progress') i.icon-signal + .meter.mana(title='Mana', ng-if='user.stats.class') + .bar(style='width: {{percent(user.stats.mp, user.stats.int+10)}}%;') + span.meter-text + i.icon-fire + | {{user.stats.mp || 0 | number:0}} / {{user.stats.int+10}} + // party span(ng-controller='PartyCtrl') .herobox-wrap(ng-repeat='profile in partyMinusSelf') diff --git a/views/shared/modals/classes.jade b/views/shared/modals/classes.jade new file mode 100644 index 0000000000..985352462b --- /dev/null +++ b/views/shared/modals/classes.jade @@ -0,0 +1,45 @@ +.modal(ng-if='user.stats.lvl >= 10 && !user.stats.class', data-backdrop=true) + .modal-header + h3 Class System Unlocked! + .modal-body + .well Select your class. Upon level-up, you will now be able to allocate points to various stats. Alternatively, you can choose which stat your tasks represent (physical, mental, social, or "other") and drive your class automatically. + .well(ng-show='selectedClass=="warrior"') Warriors deal moderate damage to tasks and have moderate defense against tasks. + .well(ng-show='selectedClass=="wizard"') Wizards deal high damage to task, and can cast debuff spells on multiple tasks. + .well(ng-show='selectedClass=="rogue"') Rogues finds more drops, Gold, and have a high chance of dealing "critical hits," which grant large GP & Exp bonuses. Rogues can also "go stealth" to avoid and entire day of dailies. + .well(ng-show='selectedClass=="healer"') Healers have high defense against damage, and can heal themselves and other players in the party, as well as buff players. + .row-fluid + .span3(ng-click='selectedClass = "warrior"') + h5 Warrior + figure.herobox(ng-class='{"selected-class": selectedClass=="warrior"}') + .character-sprites + span(class='{{user.preferences.gender}}_skin_{{user.preferences.skin}}') + span(class='{{user.preferences.gender}}_hair_{{user.preferences.hair}}') + span(class='{{user.preferences.gender}}_armor_5-warrior') + span(class='{{user.preferences.gender}}_head_5-warrior') + .span3(ng-click='selectedClass = "wizard"') + h5 Wizard + figure.herobox(ng-class='{"selected-class": selectedClass=="wizard"}') + .character-sprites + span(class='{{user.preferences.gender}}_skin_{{user.preferences.skin}}') + span(class='{{user.preferences.gender}}_hair_{{user.preferences.hair}}') + span(class='{{user.preferences.gender}}_armor_5-wizard') + span(class='{{user.preferences.gender}}_head_5-wizard') + .span3(ng-click='selectedClass = "rogue"') + h5 Rogue + figure.herobox(ng-class='{"selected-class": selectedClass=="rogue"}') + .character-sprites + span(class='{{user.preferences.gender}}_skin_{{user.preferences.skin}}') + span(class='{{user.preferences.gender}}_hair_{{user.preferences.hair}}') + span(class='{{user.preferences.gender}}_armor_5-rogue') + span(class='{{user.preferences.gender}}_head_5-rogue') + .span3(ng-click='selectedClass = "healer"') + h5 Healer + figure.herobox(ng-class='{"selected-class": selectedClass=="healer"}') + .character-sprites + span(class='{{user.preferences.gender}}_skin_{{user.preferences.skin}}') + span(class='{{user.preferences.gender}}_hair_{{user.preferences.hair}}') + span(class='{{user.preferences.gender}}_armor_5-healer') + span(class='{{user.preferences.gender}}_head_5-healer') + + .modal-footer + button.btn.btn-default.cancel(ng-click='set("stats.class", selectedClass)') Select \ No newline at end of file diff --git a/views/shared/modals/index.jade b/views/shared/modals/index.jade index 6d15f517b7..339424984e 100644 --- a/views/shared/modals/index.jade +++ b/views/shared/modals/index.jade @@ -6,4 +6,5 @@ include ./new-stuff include ./buy-gems include ./members include ./settings -include ./pets \ No newline at end of file +include ./pets +include ./classes \ No newline at end of file diff --git a/views/shared/tasks/lists.jade b/views/shared/tasks/lists.jade index df20e2d6ee..7d1f8fca55 100644 --- a/views/shared/tasks/lists.jade +++ b/views/shared/tasks/lists.jade @@ -45,7 +45,7 @@ script(id='templates/habitrpg-tasks.html', type="text/ng-template") include ./task // Static Rewards - ul.items(ng-show='main && list.type=="reward" && user.flags.itemsEnabled') + ul.items.rewards(ng-if='main && list.type=="reward" && user.flags.itemsEnabled') li.task.reward-item(ng-hide='item.hide', ng-repeat='item in itemStore') // right-hand side control buttons .task-meta-controls @@ -62,6 +62,22 @@ script(id='templates/habitrpg-tasks.html', type="text/ng-template") span(ng-class='{"shop_{{item.classes}} shop-sprite item-img": true}') p.task-text {{item.text}} + // Spells + ul.items(ng-if='main && list.type=="reward" && user.stats.class') + li.task.reward-item(ng-repeat='(k,spell) in Items.spells[user.stats.class]') + .task-meta-controls + span.task-notes(popover-trigger='mouseenter', popover-placement='left', popover='{{spell.notes}}', popover-title='{{spell.text}}') + i.icon-comment + //left-hand size commands + .task-controls + a.money.btn-buy.item-btn(ng-click='castStart(spell)') + span.reward-cost + strong {{spell.mana}} + |  MP + // main content + span(ng-class='{"shop_{{spell.classes}} shop-sprite item-img": true}') + p.task-text {{spell.text}} + br // Ads diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index c20f601c58..cdd677a504 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -1,4 +1,4 @@ -li(ng-repeat='task in obj[list.type+"s"]', class='task {{taskClasses(task, user.filters, user.preferences.dayStart, user.lastCron, list.showCompleted, main)}}', data-id='{{task.id}}') +li(ng-repeat='task in obj[list.type+"s"]', class='task {{taskClasses(task, user.filters, user.preferences.dayStart, user.lastCron, list.showCompleted, main)}}', ng-click='spell && castEnd(task, "task", $event)', ng-class='{"cast-target":spell}') // right-hand side control buttons .task-meta-controls @@ -159,6 +159,14 @@ li(ng-repeat='task in obj[list.type+"s"]', class='task {{taskClasses(task, user. span(ng-if='task.type=="daily" && !task.challenge.id') legend.option-title Restore Streak input.option-content(type='number', ng-model='task.streak') + + legend.option-title Attributes + .task-controls.tile-group + button.task-action-btn.tile(type='button') Physical + button.task-action-btn.tile(type='button') Mental + button.task-action-btn.tile(type='button') Social + button.task-action-btn.tile(type='button') Other + button.task-action-btn.tile.spacious(type='submit') Save & Close div(class='{{obj._id}}{{task.id}}-chart', ng-show='charts[obj._id+task.id]')