mirror of
https://github.com/sudoxnym/habitica-self-host.git
synced 2026-08-01 11:40:25 +00:00
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:
parent
5e0bcfd874
commit
2283093845
4 changed files with 141 additions and 14 deletions
79
dist/habitrpg-shared.js
vendored
79
dist/habitrpg-shared.js
vendored
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
# ----------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue