mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-05-23 06:07:07 +00:00
Casting Chilling Frost and Stealth skill again will not be processed and return an error instead. Fixes #12361. (#12404)
* Added logic for a repeating Chilling Frost skill. Added test case for redundant chilling frost skill cast. Added comments for the logic of repeating Stealth skill because of an error. * Added logic for a repeating Stealth skill. Avoiding MP reduction still pending because of console error. Test cases pending. * Completed the logic for a repeated Stealth skill. Added repeated frost skill cast check in common. Removed exclusive test. Test cases are pending. * Added test case for Stealth skill recast. Fixed lint errors. Fixed a flaw in if statement which led to test case failure. * Fixed lint errors in test case. * Added a common JSON entry for skil recasts in three files. Other files remaining. Added Chilling Frost recast check in common code. Modified test cases. * Added spellDisabled condition in client code. * Reverted JSON messages for three languages. Added spellAlreadyCast attribute to JSON file in locales/en. Made changes for showing appropriate message in client code. * Added an import for throwing BadRequest in common code. Modified test case accordingly. * Update website/common/script/content/spells.js Co-authored-by: Matteo Pagliazzi <matteopagliazzi@gmail.com> * Added target and req attributes in cast() method arguments. * Changed common code test case because of increased function parameters. Moved chilling frost test casse to common tests instead of server tests. * Changed the test case format in common tests. * Added a missing done statement. * Fixed a minor error which led to failing test case. Removed the exclusive test which led to lint error. * Fixed lint errors. * Added a class named 'disabled' for the frontend change. * fix(skills): style cleanup * fix(skills): unfix Co-authored-by: Matteo Pagliazzi <matteopagliazzi@gmail.com> Co-authored-by: Sabe Jones <sabrecat@gmail.com>
This commit is contained in:
parent
d7d7f82723
commit
c0bf2cffea
6 changed files with 72 additions and 17 deletions
|
|
@ -161,6 +161,23 @@ describe('POST /user/class/cast/:spellId', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('Issue #12361: returns an error if stealth has already been cast', async () => {
|
||||
await user.update({
|
||||
'stats.class': 'rogue',
|
||||
'stats.lvl': 15,
|
||||
'stats.mp': 400,
|
||||
'stats.buffs.stealth': 1,
|
||||
});
|
||||
await user.sync();
|
||||
await expect(user.post('/user/class/cast/stealth'))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: t('spellAlreadyCast'),
|
||||
});
|
||||
expect(user.stats.mp).to.equal(400);
|
||||
});
|
||||
|
||||
it('returns an error if targeted party member doesn\'t exist', async () => {
|
||||
const { groupLeader } = await createAndPopulateGroup({
|
||||
groupDetails: { type: 'party', privacy: 'private' },
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import {
|
|||
import spells from '../../../website/common/script/content/spells';
|
||||
import {
|
||||
NotAuthorized,
|
||||
BadRequest,
|
||||
} from '../../../website/common/script/libs/errors';
|
||||
import i18n from '../../../website/common/script/i18n';
|
||||
|
||||
|
|
@ -25,7 +26,7 @@ describe('shared.ops.spells', () => {
|
|||
const spell = spells.healer.heal;
|
||||
|
||||
try {
|
||||
spell.cast(user);
|
||||
spell.cast(user, null, { language: 'en' });
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.equal(i18n.t('messageHealthAlreadyMax'));
|
||||
|
|
@ -35,4 +36,22 @@ describe('shared.ops.spells', () => {
|
|||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('Issue #12361: returns an error if chilling frost has already been cast', done => {
|
||||
user.stats.class = 'wizard';
|
||||
user.stats.lvl = 15;
|
||||
user.stats.mp = 400;
|
||||
user.stats.buffs.streaks = true;
|
||||
|
||||
const spell = spells.wizard.frost;
|
||||
try {
|
||||
spell.cast(user, null, { language: 'en' });
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(BadRequest);
|
||||
expect(err.message).to.equal(i18n.t('spellAlreadyCast'));
|
||||
expect(user.stats.mp).to.eql(400);
|
||||
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -42,12 +42,14 @@
|
|||
:key="key"
|
||||
v-b-popover.hover.auto="skillNotes(skill)"
|
||||
class="col-12 col-md-3"
|
||||
@click="castStart(skill)"
|
||||
@click="!spellDisabled(key) ? castStart(skill) : null"
|
||||
>
|
||||
<!-- eslint-enable vue/no-use-v-if-with-v-for -->
|
||||
<div class="spell col-12 row">
|
||||
<div
|
||||
class="spell col-12 row"
|
||||
:class="{'disabled': spellDisabled(key)}"
|
||||
>
|
||||
<div class="col-8 details">
|
||||
<a :class="{'disabled': spellDisabled(key)}"></a>
|
||||
<div
|
||||
class="img"
|
||||
:class="`shop_${skill.key} shop-sprite item-img`"
|
||||
|
|
@ -88,9 +90,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.drawer-container {
|
||||
}
|
||||
|
||||
.drawer-slider {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
|
@ -100,7 +99,7 @@
|
|||
white-space: initial;
|
||||
}
|
||||
|
||||
.spell:hover {
|
||||
.spell:hover:not(.disabled) {
|
||||
cursor: pointer;
|
||||
border: solid 2px #50b5e9;
|
||||
}
|
||||
|
|
@ -116,6 +115,10 @@
|
|||
padding-left: 0;
|
||||
overflow: hidden;
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.details {
|
||||
text-align: left;
|
||||
padding-top: .5em;
|
||||
|
|
@ -280,9 +283,9 @@ export default {
|
|||
let notes = skill.notes();
|
||||
|
||||
if (skill.key === 'frost' && this.spellDisabled(skill.key)) {
|
||||
notes = this.$t('spellWizardFrostAlreadyCast');
|
||||
notes = this.$t('spellAlreadyCast');
|
||||
} else if (skill.key === 'stealth' && this.spellDisabled(skill.key)) {
|
||||
notes = this.$t('spellRogueStealthMaxedOut');
|
||||
notes = this.$t('spellAlreadyCast');
|
||||
} else if (skill.key === 'stealth') {
|
||||
notes = this.$t('spellRogueStealthDaliesAvoided', {
|
||||
originalText: notes,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
"spellWizardFrostText": "Chilling Frost",
|
||||
"spellWizardFrostNotes": "With one cast, ice freezes all your streaks so they won't reset to zero tomorrow! ",
|
||||
"spellWizardFrostAlreadyCast": "You have already cast this today. Your streaks are frozen, and there's no need to cast this again.",
|
||||
|
||||
|
||||
"spellWarriorSmashText": "Brutal Smash",
|
||||
|
|
@ -37,7 +36,6 @@
|
|||
"spellRogueStealthText": "Stealth",
|
||||
"spellRogueStealthNotes": "With each cast, a few of your undone Dailies won't cause damage tonight. Their streaks and colors won't change. (Based on: PER)",
|
||||
"spellRogueStealthDaliesAvoided": "<%= originalText %> Number of dailies that will be avoided: <%= number %>.",
|
||||
"spellRogueStealthMaxedOut": "You have already avoided all your dailies; there's no need to cast this again.",
|
||||
|
||||
"spellHealerHealText": "Healing Light",
|
||||
"spellHealerHealNotes": "Shining light restores your health! (Based on: CON and INT)",
|
||||
|
|
@ -76,5 +74,6 @@
|
|||
"challengeTasksNoCast": "Casting a skill on challenge tasks is not allowed.",
|
||||
"groupTasksNoCast": "Casting a skill on group tasks is not allowed.",
|
||||
"spellNotOwned": "You don't own this skill.",
|
||||
"spellLevelTooHigh": "You must be level <%= level %> to use this skill."
|
||||
"spellLevelTooHigh": "You must be level <%= level %> to use this skill.",
|
||||
"spellAlreadyCast": "You've cast this skill already. It won't have any additional effect."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import each from 'lodash/each';
|
||||
import t from './translation';
|
||||
import { NotAuthorized } from '../libs/errors';
|
||||
import { NotAuthorized, BadRequest } from '../libs/errors';
|
||||
import statsComputed from '../libs/statsComputed'; // eslint-disable-line import/no-cycle
|
||||
import setDebuffPotionItems from '../libs/setDebuffPotionItems'; // eslint-disable-line import/no-cycle
|
||||
import crit from '../fns/crit'; // eslint-disable-line import/no-cycle
|
||||
|
|
@ -104,7 +104,10 @@ spells.wizard = {
|
|||
lvl: 14,
|
||||
target: 'self',
|
||||
notes: t('spellWizardFrostNotes'),
|
||||
cast (user) {
|
||||
cast (user, target, req) {
|
||||
// Check if chilling frost skill has been previously casted or not.
|
||||
// See #12361 for more details.
|
||||
if (user.stats.buffs.streaks === true) throw new BadRequest(t('spellAlreadyCast')(req.language));
|
||||
user.stats.buffs.streaks = true;
|
||||
},
|
||||
},
|
||||
|
|
@ -226,8 +229,8 @@ spells.healer = {
|
|||
lvl: 11,
|
||||
target: 'self',
|
||||
notes: t('spellHealerHealNotes'),
|
||||
cast (user) {
|
||||
if (user.stats.hp >= 50) throw new NotAuthorized(t('messageHealthAlreadyMax')(user.language));
|
||||
cast (user, target, req) {
|
||||
if (user.stats.hp >= 50) throw new NotAuthorized(t('messageHealthAlreadyMax')(req.language));
|
||||
user.stats.hp += (statsComputed(user).con + statsComputed(user).int + 5) * 0.075;
|
||||
if (user.stats.hp > 50) user.stats.hp = 50;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -163,6 +163,20 @@ async function castSpell (req, res, { isV3 = false }) {
|
|||
task: results[1],
|
||||
});
|
||||
} else if (targetType === 'self') {
|
||||
const spellName = spell.key;
|
||||
// Check if stealth skill has been previously casted or not.
|
||||
// See #12361 for more details.
|
||||
if (spellName === 'stealth') {
|
||||
const incompleteDailiesDue = await Tasks.Task.countDocuments({
|
||||
userId: user._id,
|
||||
type: 'daily',
|
||||
completed: false,
|
||||
isDue: true,
|
||||
}).exec();
|
||||
if (user.stats.buffs.stealth >= incompleteDailiesDue) {
|
||||
throw new BadRequest(res.t('spellAlreadyCast'));
|
||||
}
|
||||
}
|
||||
await castSelfSpell(req, user, spell, quantity);
|
||||
|
||||
let userToJson = user;
|
||||
|
|
|
|||
Loading…
Reference in a new issue