Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jirachi #78

Draft
wants to merge 11 commits into
base: 1.2.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ TODO:
- Make party add/remove easier
- Add Pokemon Emojis wherever possible
- Fix spawn type bug
- Fix search with apostrophe

**Stretch**

Expand Down
11 changes: 11 additions & 0 deletions src/battle/data/abilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,17 @@ const abilitiesToRegister = Object.freeze({
battle.unregisterListener(properties.listenerId);
},
}),
[abilityIdEnum.SERENE_GRACE]: new Ability({
id: abilityIdEnum.SERENE_GRACE,
name: "Serene Grace",
description:
"Most moves have twice the chance to apply effects and status.",
// effect is hard-coded in moves.js > genericApplySingleStatus and genericApplySingleEffect
abilityAdd() {
return {};
},
abilityRemove() {},
}),
[abilityIdEnum.ANGER_POINT]: new Ability({
id: abilityIdEnum.ANGER_POINT,
name: "Anger Point",
Expand Down
40 changes: 39 additions & 1 deletion src/battle/data/effects.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
/* eslint-disable no-param-reassign */
const { effectTypes, statToBattleStat } = require("../../config/battleConfig");
const { effectIdEnum, battleEventEnum } = require("../../enums/battleEnums");
const {
effectIdEnum,
battleEventEnum,
moveIdEnum,
} = require("../../enums/battleEnums");
const { getIsTargetPokemonCallback } = require("../engine/eventConditions");
const { getEffect } = require("./effectRegistry");
const { getMove } = require("./moveRegistry");

/**
* @template T
Expand Down Expand Up @@ -157,6 +162,39 @@ const effectsToRegister = Object.freeze({
target[statToIncrease] -= baseStatValue;
},
}),
[effectIdEnum.DOOM_DESIRE]: new Effect({
id: effectIdEnum.DOOM_DESIRE,
name: "Doom Desire",
description: "The target will take damage when the effect is removed.",
type: effectTypes.DEBUFF,
dispellable: false,
/**
* @param {EffectAddBasicArgs & {initialArgs: any}} args
*/
effectAdd({ battle, target, source }) {
battle.addToLog(
`${source.name} is foreseeing an attack against ${target.name}!`
);
return {};
},
effectRemove({ battle, target, source }) {
battle.addToLog(
`${target.name} was hit by ${source.name}'s Doom Desire!`
);
const damageToDeal = source.calculateMoveDamage({
move: getMove(moveIdEnum.DOOM_DESIRE),
target,
primaryTarget: target,
allTargets: [target],
offTargetDamageMultiplier: 1,
backTargetDamageMultiplier: 1,
});
source.dealDamage(damageToDeal, target, {
type: "move",
moveId: moveIdEnum.DOOM_DESIRE,
});
},
}),
});

module.exports = {
Expand Down
194 changes: 192 additions & 2 deletions src/battle/data/moves.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ const {
statusConditions,
} = require("../../config/battleConfig");
const { getMove } = require("./moveRegistry");
const { moveIdEnum } = require("../../enums/battleEnums");
const {
moveIdEnum,
abilityIdEnum,
effectIdEnum,
} = require("../../enums/battleEnums");
const { drawIterable } = require("../../utils/gachaUtils");

class Move {
Expand Down Expand Up @@ -128,7 +132,21 @@ class Move {
options,
probablity = 1,
}) {
if (!missedTargets.includes(target) && Math.random() < probablity) {
let shouldApplyStatus = false;
if (!missedTargets.includes(target)) {
const roll = Math.random();
if (roll < probablity) {
shouldApplyStatus = true;
} else if (
source.hasAbility(abilityIdEnum.SERENE_GRACE) &&
roll < 2 * probablity
) {
source.battle.addToLog(`${source.name}'s Serene Grace activates!`);
shouldApplyStatus = true;
}
}

if (shouldApplyStatus) {
return target.applyStatus(statusId, source, options);
}
return false;
Expand Down Expand Up @@ -166,6 +184,78 @@ class Move {
});
}
}

// eslint-disable-next-line class-methods-use-this
genericApplySingleEffect({
source,
target,
// eslint-disable-next-line no-unused-vars
primaryTarget,
// eslint-disable-next-line no-unused-vars
allTargets,
missedTargets = [],
effectId,
duration,
initialArgs = {},
probablity = 1,
}) {
let shouldApplyEffect = false;
if (!missedTargets.includes(target)) {
const roll = Math.random();
if (roll < probablity) {
shouldApplyEffect = true;
} else if (
source.hasAbility(abilityIdEnum.SERENE_GRACE) &&
roll < 2 * probablity
) {
source.battle.addToLog(`${source.name}'s Serene Grace activates!`);
shouldApplyEffect = true;
}
}

if (shouldApplyEffect) {
return target.applyEffect(effectId, duration, source, initialArgs);
}
return false;
}

/**
* @template {EffectIdEnum} K
* @param {object} param0
* @param {BattlePokemon} param0.source
* @param {BattlePokemon} param0.primaryTarget
* @param {Array<BattlePokemon>} param0.allTargets
* @param {Array<BattlePokemon>=} param0.missedTargets
* @param {K} param0.effectId
* @param {number} param0.duration
* @param {EffectInitialArgsTypeFromId<K>=} param0.initialArgs
* @param {number=} param0.probablity
*/
genericApplyAllEffects({
source,
primaryTarget,
allTargets,
missedTargets = [],
effectId,
duration,
// @ts-ignore
initialArgs = {},
probablity = 1,
}) {
for (const target of allTargets) {
this.genericApplySingleEffect({
source,
target,
primaryTarget,
allTargets,
missedTargets,
effectId,
duration,
initialArgs,
probablity,
});
}
}
}

const movesToRegister = Object.freeze({
Expand Down Expand Up @@ -222,6 +312,106 @@ const movesToRegister = Object.freeze({
});
},
}),
[moveIdEnum.CONFUSION]: new Move({
id: moveIdEnum.CONFUSION,
name: "Confusion",
type: pokemonTypes.PSYCHIC,
power: 50,
accuracy: 100,
cooldown: 0,
targetType: targetTypes.ENEMY,
targetPosition: targetPositions.FRONT,
targetPattern: targetPatterns.SINGLE,
tier: moveTiers.BASIC,
damageType: damageTypes.SPECIAL,
description:
"The target is hit by a weak telekinetic force. This has a 25% chance to confuse the target for 1 turn.",
execute(args) {
this.genericDealAllDamage(args);
this.genericApplyAllEffects({
...args,
effectId: "confused",
duration: 1,
probablity: 0.25,
});
},
}),
[moveIdEnum.PSYCHIC]: new Move({
id: moveIdEnum.PSYCHIC,
name: "Psychic",
type: pokemonTypes.PSYCHIC,
power: 65,
accuracy: 90,
cooldown: 3,
targetType: targetTypes.ENEMY,
targetPosition: targetPositions.FRONT,
targetPattern: targetPatterns.ROW,
tier: moveTiers.POWER,
damageType: damageTypes.SPECIAL,
description:
"The target is hit by a strong telekinetic force. This has a 60% chance to lower the targets' Special Defense for 2 turns.",
execute(args) {
this.genericDealAllDamage(args);
this.genericApplyAllEffects({
...args,
effectId: "spdDown",
duration: 2,
probablity: 0.6,
});
},
}),
[moveIdEnum.DOOM_DESIRE]: new Move({
id: moveIdEnum.DOOM_DESIRE,
name: "Doom Desire",
type: pokemonTypes.STEEL,
power: 120,
accuracy: 100,
cooldown: 5,
targetType: targetTypes.ENEMY,
targetPosition: targetPositions.ANY,
targetPattern: targetPatterns.SQUARE,
tier: moveTiers.ULTIMATE,
damageType: damageTypes.SPECIAL,
description:
"Two turns after this move is used, the user's strikes the target with a concentrated bundle of light (undispellable). This move also has a 10% chance to apply Perish Song.",
execute(args) {
this.genericApplyAllEffects({
...args,
effectId: effectIdEnum.DOOM_DESIRE,
duration: 2,
});
this.genericApplyAllEffects({
...args,
effectId: "perishSong",
duration: 3,
probablity: 0.1,
});
},
}),
[moveIdEnum.IRON_HEAD]: new Move({
id: moveIdEnum.IRON_HEAD,
name: "Iron Head",
type: pokemonTypes.STEEL,
power: 90,
accuracy: 100,
cooldown: 3,
targetType: targetTypes.ENEMY,
targetPosition: targetPositions.FRONT,
targetPattern: targetPatterns.SINGLE,
tier: moveTiers.POWER,
damageType: damageTypes.PHYSICAL,
description:
"The target is struck with a hard head made of iron. This has a 50% chance to flinch.",
execute(args) {
this.genericDealAllDamage(args);
this.genericApplyAllEffects({
...args,
effectId: "flinched",
duration: 1,
probablity: 0.5,
});
},
}),
[moveIdEnum.AQUA_IMPACT]: new Move({
id: moveIdEnum.AQUA_IMPACT,
name: "Aqua Impact",
Expand Down
25 changes: 16 additions & 9 deletions src/battle/engine/BattlePokemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,10 @@ class BattlePokemon {
}
}

hasAbility(abilityId) {
return this.ability?.abilityId === abilityId;
}

/**
* @param {number} heal
* @param {BattlePokemon} target
Expand Down Expand Up @@ -1506,7 +1510,8 @@ class BattlePokemon {
*/
removeEffect(effectId) {
// if effect doesn't exist, do nothing
if (!this.effectIds[effectId]) {
const effectInstance = this.getEffectInstance(effectId);
if (!effectInstance) {
return false;
}
const effect = getEffect(effectId);
Expand All @@ -1519,28 +1524,30 @@ class BattlePokemon {
// @ts-ignore
effect.effectRemove({
battle: this.battle,
source: effectInstance.source,
duration: effectInstance.duration,
target: this,
properties: this.effectIds[effectId].args,
initialArgs: this.effectIds[effectId].initialArgs,
properties: effectInstance.args,
initialArgs: effectInstance.initialArgs,
});
} else {
const legacyEffect = /** @type {any} */ (effect);
legacyEffect.effectRemove(
this.battle,
this,
this.effectIds[effectId].args,
this.effectIds[effectId].initialArgs
effectInstance.args,
effectInstance.initialArgs
);
}

if (this.effectIds[effectId] !== undefined) {
const afterRemoveArgs = {
target: this,
source: this.effectIds[effectId].source,
source: effectInstance.source,
effectId,
duration: this.effectIds[effectId].duration,
initialArgs: this.effectIds[effectId].initialArgs,
args: this.effectIds[effectId].args,
duration: effectInstance.duration,
initialArgs: effectInstance.initialArgs,
args: effectInstance.args,
};
this.battle.eventHandler.emit(
battleEventEnum.AFTER_EFFECT_REMOVE,
Expand Down
2 changes: 2 additions & 0 deletions src/battle/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@
* @param {object} param0
* @param {Battle} param0.battle
* @param {BattlePokemon} param0.target
* @param {BattlePokemon} param0.source
* @param {number} param0.duration
* @param {T} param0.initialArgs
* @param {U} param0.properties
*/
Expand Down
Loading
Loading