fix(ultimateGear): fix bug where ultimateGear isn't awarded

This does several things:
- Moves the calculation of the ultimateGear achievement into its own
function on user.ops.
- Only computes ultimateGear if item is last and the user does not
already have the achievement
- Fixes bug where ultimateGear was not awarded if user wasn't wearing
their class gear at the time of item purchase.
- Adds content.classes and content.gearTypes which we should start using
elsewhere.
- Added tests for ultimateGear

Fixes HabitRPG/habitrpg#2232.
This commit is contained in:
Cole Gleason 2014-01-09 22:33:01 -06:00
parent 5e0bcfd874
commit 2283093845
4 changed files with 141 additions and 14 deletions

View file

@ -27,7 +27,8 @@ process.nextTick = (function () {
if (canPost) {
var queue = [];
window.addEventListener('message', function (ev) {
if (ev.source === window && ev.data === 'process-tick') {
var source = ev.source;
if ((source === window || source === null) && ev.data === 'process-tick') {
ev.stopPropagation();
if (queue.length > 0) {
var fn = queue.shift();
@ -63,7 +64,7 @@ process.chdir = function (dir) {
};
},{}],3:[function(require,module,exports){
var global=self;/**
var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};/**
* @license
* Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
* Build: `lodash modern -o ./dist/lodash.js`
@ -9167,7 +9168,7 @@ var global=self;/**
},{}],5:[function(require,module,exports){
(function() {
var api, diminishingReturns, events, gear, moment, repeat, _;
var api, classes, diminishingReturns, events, gear, gearTypes, moment, repeat, _;
_ = require('lodash');
@ -9183,6 +9184,10 @@ var global=self;/**
*/
classes = ['warrior', 'rogue', 'healer', 'wizard'];
gearTypes = ['armor', 'weapon', 'shield', 'head'];
events = {
winter: {
start: '2013-12-31',
@ -10121,8 +10126,8 @@ var global=self;/**
flat: {}
};
_.each(['weapon', 'armor', 'head', 'shield'], function(type) {
return _.each(['base', 'warrior', 'rogue', 'healer', 'wizard', 'special'], function(klass) {
_.each(gearTypes, function(type) {
return _.each(classes.concat(['base', 'special']), function(klass) {
return _.each(gear[type][klass], function(item, i) {
var key, _canOwn;
@ -10165,6 +10170,24 @@ var global=self;/**
key: 'potion'
};
/*
---------------------------------------------------------------
Classes
---------------------------------------------------------------
*/
api.classes = classes;
/*
---------------------------------------------------------------
Gear Types
---------------------------------------------------------------
*/
api.gearTypes = gearTypes;
/*
---------------------------------------------------------------
Spells
@ -11832,7 +11855,7 @@ var process=require("__browserify_process");(function() {
return typeof cb === "function" ? cb(null, _.pick(user, $w('items balance'))) : void 0;
},
buy: function(req, cb) {
var item, key, message, _ref;
var item, key, message;
key = req.params.key;
item = key === 'potion' ? content.potion : content.gear.flat[key];
@ -11860,8 +11883,12 @@ var process=require("__browserify_process");(function() {
if (message == null) {
message = "Bought " + item.text + ".";
}
if (((_ref = item.klass) === 'warrior' || _ref === 'wizard' || _ref === 'healer' || _ref === 'rogue') && user.fns.getItem('weapon').last && user.fns.getItem('armor').last && user.fns.getItem('head').last && (user.fns.getItem('shield').last || user.fns.getItem('weapon').twoHanded)) {
user.achievements.ultimateGear = true;
if (!user.achievements.ultimateGear && item.last) {
user.ops.ultimateGear({
params: {},
query: {},
body: {}
});
}
}
user.stats.gp -= item.value;
@ -12214,6 +12241,38 @@ var process=require("__browserify_process");(function() {
cb(null, user);
}
return delta;
},
ultimateGear: function(req, cb) {
var gear, lastGearClassTypeMatrix, ownedLastGear, shouldGrant;
gear = typeof window !== "undefined" && window !== null ? user.items.gear.owned : user.items.gear.owned.toObject();
ownedLastGear = _.chain(content.gear.flat).pick(_.keys(gear)).values().filter(function(gear) {
return gear.last;
});
lastGearClassTypeMatrix = {};
_.each(content.classes, function(klass) {
lastGearClassTypeMatrix[klass] = {};
return _.each(content.gearTypes, function(type) {
lastGearClassTypeMatrix[klass][type] = false;
return true;
});
});
ownedLastGear.each(function(gear) {
if (gear.twoHanded) {
lastGearClassTypeMatrix[gear.klass]["shield"] = true;
}
return lastGearClassTypeMatrix[gear.klass][gear.type] = true;
});
shouldGrant = _(lastGearClassTypeMatrix).values().reduce((function(ans, klass) {
return ans || _(klass).values().reduce((function(ans, gearType) {
return ans && gearType;
}), true);
}), false).valueOf();
user.achievements.ultimateGear = shouldGrant;
if (typeof cb === "function") {
cb(null, user);
}
return shouldGrant;
}
};
}
@ -12351,7 +12410,9 @@ var process=require("__browserify_process");(function() {
drop.type = 'Food';
drop.dialog = "You've found " + drop.article + drop.text + "! " + drop.notes;
} else if (rarity > .3) {
drop = user.fns.randomVal(content.eggs);
drop = user.fns.randomVal(_.where(content.eggs, {
canBuy: true
}));
if ((_ref3 = (_base1 = user.items.eggs)[_name1 = drop.key]) == null) {
_base1[_name1] = 0;
}

View file

@ -9,6 +9,9 @@ moment = require 'moment'
---------------------------------------------------------------
###
classes = ['warrior', 'rogue', 'healer', 'wizard']
gearTypes = ['armor', 'weapon', 'shield', 'head']
events =
winter: {start:'2013-12-31',end:'2014-02-01'}
@ -197,8 +200,8 @@ api.gear =
tree: gear
flat: {}
_.each ['weapon', 'armor', 'head', 'shield'], (type) ->
_.each ['base', 'warrior', 'rogue', 'healer', 'wizard', 'special'], (klass) ->
_.each gearTypes, (type) ->
_.each classes.concat(['base', 'special']), (klass) ->
# add "type" to each item, so we can reference that as "weapon" or "armor" in the html
_.each gear[type][klass], (item, i) ->
key = "#{type}_#{klass}_#{i}"
@ -221,6 +224,22 @@ _.each ['weapon', 'armor', 'head', 'shield'], (type) ->
api.potion = type: 'potion', text: "Health Potion", notes: "Recover 15 Health (Instant Use)", value: 25, key: 'potion'
###
---------------------------------------------------------------
Classes
---------------------------------------------------------------
###
api.classes = classes
###
---------------------------------------------------------------
Gear Types
---------------------------------------------------------------
###
api.gearTypes = gearTypes
###
---------------------------------------------------------------
Spells

View file

@ -600,8 +600,8 @@ api.wrap = (user, main=true) ->
user.items.gear.owned[item.key] = true
message = user.fns.handleTwoHanded(item)
message ?= "Bought #{item.text}."
if item.klass in ['warrior','wizard','healer','rogue'] and user.fns.getItem('weapon').last and user.fns.getItem('armor').last and user.fns.getItem('head').last and (user.fns.getItem('shield').last or user.fns.getItem('weapon').twoHanded)
user.achievements.ultimateGear = true
if not user.achievements.ultimateGear and item.last
user.ops.ultimateGear({params:{}, query:{}, body:{}})
user.stats.gp -= item.value
cb? {code:200, message}, _.pick(user,$w 'items achievements stats')
@ -877,6 +877,37 @@ api.wrap = (user, main=true) ->
cb? null, user
return delta
# ----------------------------------------------------------------------
# Achievements
# ----------------------------------------------------------------------
ultimateGear: (req, cb) ->
# on the server this is a LoDash transform, on the client its an object
gear = if window? then user.items.gear.owned else user.items.gear.owned.toObject()
ownedLastGear = _.chain(content.gear.flat)
.pick(_.keys gear)
.values()
.filter (gear) -> gear.last
lastGearClassTypeMatrix = {}
_.each content.classes, (klass) ->
lastGearClassTypeMatrix[klass] = {}
_.each content.gearTypes, (type) ->
lastGearClassTypeMatrix[klass][type] = false
return true # false exits the each loop early
ownedLastGear.each (gear) ->
lastGearClassTypeMatrix[gear.klass]["shield"] = true if gear.twoHanded
lastGearClassTypeMatrix[gear.klass][gear.type] = true
shouldGrant = _(lastGearClassTypeMatrix)
.values()
.reduce(((ans, klass) -> ans or _(klass).values().reduce(((ans, gearType) -> ans and gearType), true)), false)
.valueOf()
user.achievements.ultimateGear = shouldGrant
cb? null, user
shouldGrant
# ----------------------------------------------------------------------
# user.fns helpers
# ----------------------------------------------------------------------

View file

@ -287,6 +287,7 @@ describe 'User', ->
user.fns.randomVal.restore()
user.fns.predictableRandom.restore()
describe 'Quests', ->
_.each shared.content.quests, (quest)->
it "#{quest.text} has valid values", ->
@ -306,6 +307,21 @@ describe 'User', ->
expect(collect.text).to.be.an('string')
expect(collect.count).to.be.greaterThan 0
describe 'Achievements', ->
_.each shared.content.classes, (klass) ->
user = newUser()
user.stats.gp = 10000
_.each shared.content.gearTypes, (type) ->
_.each [1..5], (i) ->
user.ops.buy {params:'#{type}_#{klass}_#{i}'}
it 'does not get ultimateGear ' + klass, ->
expect(user.achievements.ultimateGear).to.not.be.ok
_.each shared.content.gearTypes, (type) ->
user.ops.buy {params:'#{type}_#{klass}_6'}
it 'gets ultimateGear ' + klass, ->
expect(user.achievements.ultimateGear).to.be.ok
describe 'Simple Scoring', ->
beforeEach ->
{@before, @after} = beforeAfter()
@ -556,4 +572,4 @@ describe 'Helper', ->
pets = { "Wolf-Base": 2, "Wolf-Veteran": 1, "Wolf-Cerberus": 1, "Dragon-Hydra": 1}
expect(shared.countPets(null, pets)).to.eql 1
expect(shared.countPets(_.size(pets), pets)).to.eql 1
expect(shared.countPets(_.size(pets), pets)).to.eql 1