TeamLumi / luminescent-team

GNU Affero General Public License v3.0
13 stars 6 forks source link

Add the Mapper to the main branch #114

Closed AarCon closed 3 months ago

AarCon commented 4 months ago

Summary

This changes a lot of things. It's actually only the second time that I've merged from the dev branch. The main new additions are the Mapper and more tools in the utils/dex to get items (Field, Scripted, and Hidden), shops (PokeMart, Special, and Heart Scale), and trainers (Scripted, and Placedata).

Mapper Components

The Mapper Itself

There are a lot of moving parts to this that I'll cover in multiple sections. The overall summary of this is that draws a canvas and updates that canvas according to several inputs given to it. The way the canvas is updated in broad terms is that it's initialized in it's entirety with the image and the overlay. From there, to optimize performance, the canvas only updates small relevant chunks.

States

This first section may be a bit confusing at first having let variables and react states co-mingling, but this is a necessary feature. The let variables are responsible for updating the actual canvas and the react states are for updating the UI elements like the Encounters dropdowns. You may be asking, "Why do I need separate states for the canvas?" And that's a valid question. The answer to that question is that a vanilla <canvas/> element does not have the same life cycles as React does. The canvas elements are updated before React states change since react uses an asynchronous update life cycle and canvas is synchronous.

Setting up the Canvas

The next chunk of this file is responsible for the setup for the canvas. The canvasRef is initialized and then mounted only on the initial render with just the image and the overlay which is handled by the drawOverlay. This set up process will never happen again for as long as the browser isn't refreshed. To updated the canvas, the drawRect and clearRect functions are called when needed.

Updating the Canvas

In order to update the Canvas in an efficient manner, we use the drawRect and clearRect functions.

drawRect:

clearRect:

Listener Events for the Canvas

Because the <canvas /> element is synchronous, custom listener events are required to be dispatched so React elements can interact with the canvas. There are 6 listeners in total that the canvas is listening to:

  1. Location Dropdown

    • Handled by updateLocationDataFromDropdown
    • This is a custom listener attached to the canvas to update the selected location when the dropdown is used.
    • It grabs the coordinates for the selected area, clears the hover highlight (if any) in that area, resets any previous selected area highlights, draws the new highlight, and sets all of the map data.
  2. Pokemon Name Dropdown

    • Handled by updatePokemonLocationsFromDropdown
    • This is a custom listener that receives a pokemon object when the respective dropdown is updated.
    • It takes the pokemonId of the pokemon object and searches for all of the locations that pokemon can be found.
    • It checks if there are any previous encounter highlighted areas and clears them as long as it isn't the selected location
    • It then draws all of the areas detailed that aren't hovered on or selected as encounter highlights
  3. Color Settings

    • Handled by updateColorSettings
    • This is a custom listener that waits for the Save Changes button to be pressed in the Setting Modal.
    • It takes the colorSettings and clears out any highlighted areas and sets the pokemon dropdown to the default of "Bulbasaur".
    • If there was a selected area, it is reselected.
  4. Click

    • Handled by handleClick
    • The mouse's position is being calculated by this whenever a click occurs in the canvas bounds.
    • This acts very similarly to the Location Dropdown where it will clear the selected location of hover highlights and then select highlight it.
  5. Mouse Move

    • Handled by handleMouseMove.
    • This is the fastest updating listener and it needs to be in order to keep up.
    • In order to update this listener, it tracks the mouse cursor inside of the canvas.
    • If the mouse cursor lines up with an actual map location, it will hover highlight that location by drawing on top of the canvas and storing the original canvas image.
    • When the mouse cursor either doesn't line up with a map location or it changes which map location it is hovering over, it will clear the previous hover highlight, place to original image data back and perform the previous step of highlighting and storing.
  6. Mouse Leave

    • This handles when the mouse leaves the bounds of the canvas. And now that I'm looking at it closer, it basically does nothing. Setting the HoveredZone to null is already handled by the mouse move and we don't actually want to clear the encounter locations when you leave the canvas.
    • Will likely delete this in a future PR.

useEffect hooks

This first useEffect hook is to update the listeners whenever the canvasRef.current is called/updated. This happens whenever the conditions are met to update the event listeners. It seems backwards and would be updated infinitely. That is what the return statement is for the remove the event listeners and cleanup. Not too sure how it works but I know that it does.

The other useEffects are to update the Encounter list whenever the encounter options are updated in the Encounter component, and update the location list that matches up with the current selected pokemon name.

Setting Up Encounters

This next section which is handled by setAllEncounters is to take all of the inputs from the Encounter component and replace and/or move around encounters according to those options.

Encounters

This is the main component that is used for displaying the Encounters. It gives the user access to change the time of day, fishing rod, swarm, radar, and incense methods at will. This also gives dropdowns to visualize the changes to the options given in real time. The PokemonAccordion was extended to allow the use of strict colors that don't change in dark/light mode.

With 3.0, this will need to be updated so the Incense with have a Toggle similar to the TOD and Rods. A future will include Special (Static/Gift) encounters in an additional dropdown.

This currently has a bug where the encounter method will not carry over to a new route when that is selected.

EncounterTable

This is a very basic and generalized component that displays a table containing all of the encounters for a specific list of encounters. The only special conditionals are:

Buttons

This is to keep the complex buttons for the EncounterTable in one place so it's easier to debug in the future. They function very similarly where only one option can be selected at a time. Their states are handled in the Mapper.jsx component and are passed in via the Encounters.jsx component. These can probably be updated to be html structure calls, but they work for now.

Settings Modal

This is a Modal that handles settings (obviously lol). This is currently only handling the highlight colors, but this will be the area to add any potential customization to the Mapper. To do that , the handleSubmit function will need to be refactored to handle more than just the highlight colors and probably pass those different settings through a new CustomEvent.

Highlight Colors

This is a form component that is used by the Settings Modal to display and change the highlight colors within the rgba color spectrum. The state is handled in part in the SettingsModal.jsx component and mainly in the Mapper.jsx component. This form uses MUI's form inputs in order to display the numbers with a label.

Currently there is a bug in the TextField component from MUI when using type="number" that doesn't allow for incremental steps that are less than 1 but greater than 0 ( 0 > 0 < 1). This will be solved in a future update by changing the TextField to be a NumberInput, when the NumberInput is no longer in beta. This means that the transparency value (a) is unable to change effectively and will therefore be disabled for the time being.

SearchBar

This component is tricky as it performs 2 tasks. The obvious function is giving access to the Pokemon Search Bar and Location Dropdown. The not so obvious function is overlaying on top of the Mapper's canvas to position the dropdowns in the correct spot on the page. Each of these uses CustomEvent to dispatch an event that the canvas can see and update to. They also have a normal state that is handled by the parent Mapper.jsx as well.

The trick to overlay on top of the canvas was to combine a few css tricks. The div that houses the dropdowns are placed in an absolute position from the top and then given the pointer-events: none; property to allow all events like clicking or hovering to pass through it. And yes this includes all of the children. So in order to allow the children to have pointer-events, they were given their own css class that turns pointer-events to all.

As for the Pokemon Search Bar and Location Dropdown, these are a lot more straightforward. There was an attempt at debouncing the Pokemon Search Input to give a time buffer, but it doesn't seem like this is necessary at this time. The only way to select a Pokemon is to click on it in the dropdown. The Location Dropdown is the same functionally except it doesn't have the debounced value. The options for the allPokemons are provided by the custom plugin pokedex-data-plugin. The Location Dropdown just uses all of the location names detailed in coordinates.js as the options to select.

Util Functions

...To Be Continued (I'm not finished lol)

BigEmperor26 commented 3 months ago

I see that there are some errors in some cases because of the utility functions that are not called with the correct parameters. As we work on #113 we should be able to solve at least some of these as well.