Skip to content

Commit

Permalink
Misc QOL (#73)
Browse files Browse the repository at this point in the history
* force update

* state update improvements for tutorial

* update upsell logic

* realtime tutorial completion and upsell

* more helpful tutorial descriptions

* pokemon info improvements

* list improvements

* improve early exp gain

* improve party auto

* remove log + display active pokemon info

* move targeting fix

* update restrict description

* evolve command displays requirements

* improve shop labels and emojis

* small fix

* add more battle tab emojis

* update changelog
  • Loading branch information
ewei068 authored Dec 31, 2024
1 parent 087f0d4 commit fefceab
Show file tree
Hide file tree
Showing 25 changed files with 421 additions and 141 deletions.
15 changes: 14 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
- Team colors
- Target indicators
- Improved mobile display
- Fix help command
- Add more battle tab emojis
- Display current active Pokemon
- Fix `/help` command
- Add bulk buy to shop
- Added user settings
- Profile privacy
Expand All @@ -30,6 +32,17 @@
- Super effective attacks deal more damage
- Add X target pattern shape
- You can now view other users' profiles with `/trainerinfo`! You can make your profile private in `/settings`.
- Improve pokemon info
- Add some emojis
- Add a training button
- Rebalance Pokemon EXP gain
- Gain much more EXP in early game (low trainer level)
- Gain slightly more EXP at high trainer level
- Gain slightly more EXP when beating low-level PVE NPCs
- Added experimental smart positioning to `/party auto`
- `/evolve` will display evolution requirements if not met
- Improve `/pokemart` with emojis and better button descriptions
- Fixed a bunch of bugs

TODO:

Expand Down
2 changes: 1 addition & 1 deletion src/battle/engine/Battle.js
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ class Battle {
Math.floor(
Object.values(this.allPokemon).reduce((acc, pokemon) => {
if (pokemon.isFainted) {
return acc + (this.minLevel || pokemon.level);
return acc + Math.max(12, this.minLevel || pokemon.level);
}
return acc;
}, 0) * this.pokemonExpMultiplier
Expand Down
1 change: 1 addition & 0 deletions src/battle/engine/BattlePokemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ class BattlePokemon {
this.hittable = true;
this.incapacitated = false;
this.restricted = false;
this.shiny = pokemonData.shiny;
}

/**
Expand Down
24 changes: 18 additions & 6 deletions src/commands/battle/partyAuto.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/**
* @file
* @author Elvis Wei
* @date 2023
* @section Description
*
* partyAuto.js is used to automatically make a party.
*/
Expand All @@ -16,7 +14,6 @@ const { getUserSelectedDevice } = require("../../utils/trainerUtils");
* creates an automatic party for the given user, uses dependencies to get other relevant data.
* @param {*} user the user given to get the relevant data from.
* @param {*} option the option type for making the automatic party.
* @returns Error or message to send.
*/
const partyAuto = async (user, option) => {
// get trainer
Expand Down Expand Up @@ -55,13 +52,28 @@ const partyAuto = async (user, option) => {
};
}

// sort pokemon by defensive stats
bestPokemons.data.sort((a, b) => {
const [hpA, , defA, , spdA] = a.stats;
const [hpB, , defB, , spdB] = b.stats;
return hpB + defB + spdB - b.level - (hpA + defA + spdA - a.level);
});

// add best pokemons to party in random positions
for (const pokemon of bestPokemons.data) {
for (const [index, pokemon] of bestPokemons.data.entries()) {
// if position is already taken, get new position
// eslint-disable-next-line no-constant-condition
while (true) {
// get random position
const position = Math.floor(Math.random() * length);
let position;
if (index < 2) {
// get random position in first row
position = Math.floor(Math.random() * party.cols);
} else {
// get random position not in first row
position =
Math.floor(Math.random() * (party.cols * (party.rows - 1))) +
party.cols;
}
// if position is empty, add pokemon
if (!party.pokemonIds[position]) {
party.pokemonIds[position] = pokemon._id.toString();
Expand Down
17 changes: 11 additions & 6 deletions src/commands/pokemon/evolve.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/**
* @file
* @author Elvis Wei
* @date 2023
* @section Description
*
* evolve.js looks up a pokemon, returning an embed with the pokemon's valid evolution options.
*/
Expand All @@ -15,13 +13,13 @@ const {
} = require("../../components/idConfigSelectRow");
const { buildPokemonEmbed } = require("../../embeds/pokemonEmbeds");
const { eventNames } = require("../../config/eventConfig");
const { buildSpeciesEvolutionString } = require("../../utils/pokemonUtils");

/**
* Looks up a Pokemon, returning an embed with the Pokemon's valid
* evolution options.
* @param {Object} user User who initiated the command.
* @param {String} pokemonId ID of the Pokemon to evolve.
* @returns Embed with Pokemon's valid evolution options.
* @param {object} user User who initiated the command.
* @param {string} pokemonId ID of the Pokemon to evolve.
*/
const evolve = async (user, pokemonId) => {
// get trainer
Expand Down Expand Up @@ -55,7 +53,14 @@ const evolve = async (user, pokemonId) => {

// if empty, pokemon cannot evolve
if (evolutionSpeciesIds.length === 0) {
return { send: null, err: `${pokemon.data.name} cannot evolve yet!` };
return {
send: null,
err: `${
pokemon.data.name
} cannot evolve yet! It evolves with the following requirements:\n${buildSpeciesEvolutionString(
speciesData
)}`,
};
}

// build pokemon embed
Expand Down
3 changes: 3 additions & 0 deletions src/components/battleInfoActionRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const buildBattleInfoActionRow = (battle, stateId, selectionIndex = 0) => {
buttonConfigs.push({
label: "Moves",
disabled: false,
emoji: "⚔️",
data: {
...infoRowData,
selectionIndex: i,
Expand All @@ -48,6 +49,7 @@ const buildBattleInfoActionRow = (battle, stateId, selectionIndex = 0) => {
buttonConfigs.push({
label: "Hide",
disabled: false,
emoji: "⬇️",
data: {
...infoRowData,
selectionIndex: i + 1,
Expand All @@ -58,6 +60,7 @@ const buildBattleInfoActionRow = (battle, stateId, selectionIndex = 0) => {
buttonConfigs.push({
label: "Refresh",
disabled: false,
emoji: "🔄",
data: {
...infoRowData,
selectionIndex: i + 2,
Expand Down
10 changes: 5 additions & 5 deletions src/config/battleConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -1086,7 +1086,7 @@ const effectConfig = Object.freeze({
dispellable: true,
effectAdd(battle, _source, target) {
battle.addToLog(
`${target.name} is restricted and cannot gain combat readiness!`
`${target.name} is restricted and cannot gain combat readiness via boosts!`
);
target.restricted = true;
},
Expand Down Expand Up @@ -6477,7 +6477,7 @@ const moveExecutes = {
const pokemons = source.getPatternTargets(
party,
targetPatterns.ALL_EXCEPT_SELF,
1
source.position
);
if (pokemons.length > 0) {
const pokemon = pokemons[Math.floor(Math.random() * pokemons.length)];
Expand Down Expand Up @@ -7461,7 +7461,7 @@ const moveExecutes = {
const pokemons = source.getPatternTargets(
party,
targetPatterns.ALL_EXCEPT_SELF,
1
source.position
);
if (pokemons.length > 0) {
const pokemon = pokemons.reduce((a, b) =>
Expand Down Expand Up @@ -10043,7 +10043,7 @@ const moveExecutes = {
const pokemons = source.getPatternTargets(
party,
targetPatterns.ALL_EXCEPT_SELF,
1
source.position
);
if (pokemons.length > 0) {
const pokemon = pokemons[Math.floor(Math.random() * pokemons.length)];
Expand Down Expand Up @@ -11049,7 +11049,7 @@ const moveExecutes = {
const pokemons = source.getPatternTargets(
party,
targetPatterns.ALL_EXCEPT_SELF,
1
source.position
);
if (pokemons.length > 0) {
const pokemon = pokemons[Math.floor(Math.random() * pokemons.length)];
Expand Down
27 changes: 13 additions & 14 deletions src/config/questConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,8 @@ const newTutorialConfigRaw = {
buyPokeballs: {
name: "Buying Pokeballs",
emoji: emojis.POKEBALL,
description:
"One of the best ways to get more Pokeballs is to buy them from the Pokemart every day. Use `/pokemart` to buy Pokeballs, or use `/buy itemid: 0 quantity: 5` to buy the maximum amount.",
requirementString: "Buy 5x Pokeballs",
description: `One of the best ways to get more Pokeballs is to buy them from the Pokemart every day. Use \`/pokemart\` to buy ${emojis.POKEBALL} Pokeballs, or use \`/buy itemid: 0 quantity: 5\` to buy the maximum amount.`,
requirementString: `Buy 5x ${emojis.POKEBALL} Pokeballs`,
proceedString:
"Use `/pokemart` or `/buy itemid: 0 quantity: 5` to buy 5 Pokeballs!",
checkRequirements: async (trainer) =>
Expand Down Expand Up @@ -160,7 +159,7 @@ const newTutorialConfigRaw = {
name: "Learning to Battle: Taking Turns",
emoji: "➡️",
description:
"Before you battle, you must learn how they work! Battles in Pokestar are unique; all 6 Pokemon fight at a time!\n\n**Taking turns is based off combat readiness.** Pokemon with higher speed gain combat readiness faster. The current active Pokemon is highlighted in asterisks.\n\nYou may view the combat readiness of a team by clicking the **NPC** or **Player** tabs. There is also an indicator of which Pokemon moves next.",
"Before you battle, you must learn how they work! Battles in Pokestar are unique; **all 6 Pokemon fight at a time!**\n\n**Taking turns is based off combat readiness.** Pokemon with higher speed gain combat readiness faster. The current active Pokemon is highlighted in asterisks.\n\nYou may view the combat readiness of a team by clicking the **🔴 NPC** or **🔵 Player** tabs. There is also an indicator of which Pokemon moves next.",
requirementString: "Complete the previous stage",
proceedString:
"Read the description to learn about battles, and complete the previous stage.",
Expand All @@ -176,7 +175,7 @@ const newTutorialConfigRaw = {
name: "Learning to Battle: Using Moves",
emoji: "🔥",
description:
"When its your turn, you can use a move! **Use the dropdown menu to select a move.**\n\nWhen selecting a move, a description will appear. This includes important move information such as its type, power, effect, and cooldown.",
"When its your turn, you can use a move! **Use the dropdown menu to select a move.**\n\nWhen selecting a move, a description will appear. This includes important move information such as its **type, power, effect, and cooldown.**",
requirementString: "Complete the previous stage",
proceedString:
"Read the description to learn about battles, and complete the previous stage.",
Expand All @@ -193,7 +192,7 @@ const newTutorialConfigRaw = {
emoji: "🎯",
description:
"When using a move, you must select a target! **Click on the Pokemon you want to target from the dropdown menu.** Most moves may only target certain Pokemon, which is indicated in the **Target:** section of the move description." +
"\n\n**Some moves may affect an area of Pokemon.** When a target is selected, that area is indicated by a wider border. **When satisfied, click the confirm button to use the move.** This can be disabled in your `/settings`." +
"\n\n**Some moves may affect an area of Pokemon.** When a target is selected, that area is indicated by a wider border. **When satisfied, click the ⚔️ Confirm button to use the move.** This can be disabled in your `/settings`." +
"\n\nFor more detailed battle mechanics, check out the [documentation on Github](https://github.com/ewei068/pokestar?tab=readme-ov-file#-battle-mechanics).",
requirementString: "Complete the previous stage",
proceedString:
Expand All @@ -210,7 +209,7 @@ const newTutorialConfigRaw = {
name: "Battle an NPC!",
emoji: "⚔️",
description:
"Now that you know how to battle, **use `/pve` to battle an NPC!** I'd recommend starting with the Bug Catcher on Very Easy difficulty.",
"Now that you know how to battle, **use `/pve` to battle an NPC!** I'd recommend starting with the <:bugcatcher:1117871382399815812> Bug Catcher on Very Easy difficulty.",
requirementString: "Win any NPC Battle",
proceedString: "Use `/pve` to battle an NPC!",
checkRequirements: async (trainer) =>
Expand Down Expand Up @@ -263,15 +262,15 @@ const newTutorialConfigRaw = {
name: "Training Pokemon",
emoji: "🏋️",
description:
"One way to give your Pokemon more EXP is by training! Copy a Pokemon's ID, then **Use `/train` to train a Pokemon to level 8.**",
requirementString: "Have 1x Pokemon at level 8 or higher",
proceedString: "Use `/train` to train a Pokemon to level 8.",
"One way to give your Pokemon more EXP is by training! Copy a Pokemon's ID, then **Use `/train` to train a Pokemon to level 12.**\n\nTip: On desktop, you can use the Up Arrow key to use your last command.",
requirementString: "Have 1x Pokemon at level 12 or higher",
proceedString: "Use `/train` to train a Pokemon to level 12.",
checkRequirements: async (trainer) => {
const { data: pokemons } = await listPokemons(trainer, {
pageSize: 1,
page: 1,
filter: {
level: { $gte: 8 },
level: { $gte: 12 },
},
});
return (pokemons?.length ?? 0) > 0;
Expand Down Expand Up @@ -338,8 +337,8 @@ const newTutorialConfigRaw = {
name: "Purchasing Locations",
emoji: "🌍",
description:
"You may purchase and upgrade locations from the `/pokemart` for permanent boosts! **Use `/pokemart` to purchase the Home location and upgrade it to level 3.** The Home locations improves the EXP gained from `/train`!\n\nYou can use `/locations` to view your locations.",
requirementString: "Have a level 3 Home location",
"You may purchase and upgrade locations from the `/pokemart` for permanent boosts! **Use `/pokemart` to purchase the 🏠 Home location and upgrade it to level 3.** The Home locations improves the EXP gained from `/train`!\n\nYou can use `/locations` to view your locations.",
requirementString: "Have a level 3 🏠 Home location",
proceedString:
"Use `/pokemart` to purchase the Home location and upgrade it to level 3!",
checkRequirements: async (trainer) =>
Expand All @@ -358,7 +357,7 @@ const newTutorialConfigRaw = {
name: "Beginner Pokemon Leveling",
emoji: "📈",
description:
"Now that you can `/train` your Pokemon faster, **train 6 Pokemon to level 15.**",
"Now that you can `/train` your Pokemon faster, **train 6 Pokemon to level 15.**\n\nTip: On desktop, you can use the Up Arrow key to use your last command.",
requirementString: "Train 6x Pokemon to level 15",
proceedString: "Use `/train` to train 6 Pokemon to level 15!",
checkRequirements: async (trainer) => {
Expand Down
4 changes: 2 additions & 2 deletions src/config/trainerConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -1566,8 +1566,8 @@ const levelConfig = {
const getTrainerLevelExp = (level) => 50 * (level ** 2 - level);

const expMultiplier = (level) =>
// 3 * x ^ (1/2)
3 * level ** (1 / 2);
// 2.5 * x ^ (1/2) + 15
2.5 * level ** (1 / 2) + 15;

const NUM_DAILY_REWARDS = process.env.STAGE === stageNames.ALPHA ? 100 : 5;
const NUM_DAILY_SHARDS = process.env.STAGE === stageNames.ALPHA ? 100 : 5;
Expand Down
31 changes: 28 additions & 3 deletions src/deact/deact.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,23 @@ const createElement = (
isDeactCreateElement: true,
});

/**
* TODO: hacky? should I be keeping track of interaction rather than message ref?
* @param {DeactInstance} rootInstance
*/
const forceUpdate = async (rootInstance) => {
const renderedElement = await rootInstance.renderCurrentElement();
if (
!renderedElement.messageRef ||
!renderedElement.element ||
renderedElement.err
) {
return;
}

await renderedElement.messageRef.edit(renderedElement.element);
};

// TODO: figure out how to remove ref parameter

/**
Expand Down Expand Up @@ -379,7 +396,6 @@ function useEffect(callback, deps, ref) {
}
cleanupRef.current = cleanup;
}
return cleanupRef.current;
}

/**
Expand All @@ -389,8 +405,16 @@ function useEffect(callback, deps, ref) {
* @returns {Promise<(() => void) | void>}
*/
async function useAwaitedEffect(callback, deps, ref) {
const promise = await useEffect(callback, deps, ref);
return await promise;
// TODO: can't seem to use useEffect because we have to await the cleanup callback
const cleanupRef = useRef(null, ref);
const haveDepsChanged = useCompareAndSetDeps(deps, ref);
if (haveDepsChanged || !ref.finishedMounting) {
const cleanup = await callback();
if (cleanupRef.current) {
await cleanupRef.current();
}
cleanupRef.current = cleanup;
}
}

/**
Expand All @@ -408,6 +432,7 @@ module.exports = {
userTypeEnum,
createRoot,
createElement,
forceUpdate,
createModal,
triggerBoundCallback,
makeComponentIdWithStateId,
Expand Down
2 changes: 2 additions & 0 deletions src/elements/pokemon/PokemonList.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,12 +354,14 @@ module.exports = async (
[
createElement(Button, {
emoji: "⚙️",
label: "Filter / Sort",
callbackBindingKey: settingsActionBinding,
style: filtersShown ? ButtonStyle.Primary : ButtonStyle.Secondary,
data: {},
}),
createElement(Button, {
emoji: "❌",
label: "Clear Filters",
callbackBindingKey: clearFiltersActionBinding,
style: ButtonStyle.Secondary,
data: {},
Expand Down
Loading

0 comments on commit fefceab

Please sign in to comment.