mirror of
https://github.com/kuroppoi/entralinked.git
synced 2026-09-08 16:55:16 -05:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b38b26f9ce | ||
|
|
a95e7e2f01 | ||
|
|
53f05acab9 | ||
|
|
49a15a0156 | ||
|
|
329e1de30a | ||
|
|
0e12cb54d7 | ||
|
|
8849265805 | ||
|
|
0d964ec129 | ||
|
|
e4fff87b87 | ||
|
|
aae1abd18f | ||
|
|
038f7f5136 |
@@ -2,8 +2,9 @@
|
||||
[](https://github.com/kuroppoi/entralinked/actions)
|
||||
|
||||
Entralinked is a standalone Game Sync emulator developed for use with Pokémon Black & White and its sequels.\
|
||||
Its purpose is to serve as a simple utility for downloading Pokémon, Items, C-Gear skins, Pokédex skins and Musicals\
|
||||
to your game without needing to edit your save file.
|
||||
Its purpose is to serve as a simple utility for downloading Pokémon, Items, C-Gear skins, Pokédex skins, Musicals\
|
||||
and, in Black 2 & White 2 only, Join Avenue visitors to your game without needing to edit your save file.\
|
||||
It can also be used to Memory Link with a Black or White save file if you don't have a second DS system.
|
||||
|
||||
## Building
|
||||
|
||||
|
||||
@@ -6,11 +6,10 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -22,7 +21,7 @@ import entralinked.utility.Crc16;
|
||||
public class DlcList {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
private final Map<String, Dlc> dlcMap = new ConcurrentHashMap<>();
|
||||
private final List<Dlc> dlcList = new ArrayList<>();
|
||||
private final File dataDirectory = new File("dlc");
|
||||
|
||||
public DlcList() {
|
||||
@@ -83,26 +82,20 @@ public class DlcList {
|
||||
// Load DLC data
|
||||
Dlc dlc = loadDlcFile(file.getName(), subFile.getName(), index, dlcFile);
|
||||
|
||||
// Index DLC object if loading succeeded
|
||||
if(dlc != null) {
|
||||
dlcMap.put(dlc.name(), dlc);
|
||||
dlcList.add(dlc);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Loaded {} DLC file(s)", dlcMap.size());
|
||||
logger.info("Loaded {} DLC file(s)", dlcList.size());
|
||||
}
|
||||
|
||||
private Dlc loadDlcFile(String gameCode, String type, int index, File dlcFile) {
|
||||
String name = dlcFile.getName();
|
||||
|
||||
if(dlcMap.containsKey(name)) {
|
||||
logger.warn("Duplicate DLC name {}", name);
|
||||
return null;
|
||||
}
|
||||
|
||||
if(dlcFile.isDirectory()) {
|
||||
logger.warn("Directory '{}' in {} DLC folder", name, gameCode);
|
||||
return null;
|
||||
@@ -123,6 +116,7 @@ public class DlcList {
|
||||
int checksumInFile = (bytes[bytes.length - 2] & 0xFF) | ((bytes[bytes.length - 1] & 0xFF) << 8);
|
||||
|
||||
if(checksum != checksumInFile) {
|
||||
logger.warn("Checksum mismatch in DLC '{}'", name);
|
||||
projectedSize += 2;
|
||||
checksum = Crc16.calc(bytes, 0, bytes.length);
|
||||
checksumEmbedded = false;
|
||||
@@ -167,19 +161,18 @@ public class DlcList {
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public Dlc getDlc(String name) {
|
||||
return dlcMap.get(name);
|
||||
public Dlc getDlc(String gameCode, String type, String name) {
|
||||
List<Dlc> dlcList = getDlcList(gameCode, type).stream()
|
||||
.filter(dlc -> dlc.name().equals(name)).collect(Collectors.toList());
|
||||
return dlcList.isEmpty() ? null : dlcList.get(0);
|
||||
}
|
||||
|
||||
public int getDlcIndex(String name) {
|
||||
return dlcExists(name) ? getDlc(name).index() : 0;
|
||||
}
|
||||
|
||||
public boolean dlcExists(String name) {
|
||||
return name != null && dlcMap.containsKey(name);
|
||||
public int getDlcIndex(String gameCode, String type, String name) {
|
||||
Dlc dlc = getDlc(gameCode, type, name);
|
||||
return dlc == null ? 0 : dlc.index();
|
||||
}
|
||||
|
||||
public Collection<Dlc> getDlc() {
|
||||
return Collections.unmodifiableCollection(dlcMap.values());
|
||||
return Collections.unmodifiableCollection(dlcList);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
package entralinked.network.http.dashboard;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import entralinked.Entralinked;
|
||||
@@ -20,6 +31,7 @@ import entralinked.model.player.PlayerManager;
|
||||
import entralinked.model.player.PlayerStatus;
|
||||
import entralinked.network.http.HttpHandler;
|
||||
import entralinked.utility.GsidUtility;
|
||||
import entralinked.utility.TiledImageReader;
|
||||
import io.javalin.Javalin;
|
||||
import io.javalin.config.JavalinConfig;
|
||||
import io.javalin.http.Context;
|
||||
@@ -32,20 +44,37 @@ import io.javalin.json.JavalinJackson;
|
||||
*/
|
||||
public class DashboardHandler implements HttpHandler {
|
||||
|
||||
private final Set<Integer> availableBlackAndWhiteSpecies = Set.of(
|
||||
505, 507, 510, 511, 513, 515, 519, 523, 525, 527, 529, 531, 533, 535, 538, 539, 542, 545, 546, 548,
|
||||
550, 553, 556, 558, 559, 561, 564, 569, 572, 575, 578, 580, 583, 587, 588, 594, 596, 600, 605, 607,
|
||||
610, 613, 616, 618, 619, 621, 622, 624, 626, 628, 630, 631, 632);
|
||||
private static final Logger logger = LogManager.getLogger();
|
||||
private final Map<Dlc, BufferedImage> skinPreviewCache = new HashMap<>();
|
||||
private final DlcList dlcList;
|
||||
private final PlayerManager playerManager;
|
||||
|
||||
public DashboardHandler(Entralinked entralinked) {
|
||||
this.dlcList = entralinked.getDlcList();
|
||||
this.playerManager = entralinked.getPlayerManager();
|
||||
|
||||
// Load & cache skin previews
|
||||
logger.info("Loading C-Gear and Pokédex skin previews ...");
|
||||
List<Dlc> skins = dlcList.getDlcList(dlc -> dlc.type().startsWith("CGEAR") || dlc.type().equals("ZUKAN"));
|
||||
|
||||
for(Dlc skin : skins) {
|
||||
try(FileInputStream inputStream = new FileInputStream(skin.path())) {
|
||||
BufferedImage image =
|
||||
skin.type().equals("ZUKAN") ? TiledImageReader.readDexSkin(inputStream) :
|
||||
skin.type().equals("CGEAR") ? TiledImageReader.readCGearSkin(inputStream, true) :
|
||||
TiledImageReader.readCGearSkin(inputStream, false); // CGEAR2
|
||||
skinPreviewCache.put(skin, image);
|
||||
} catch(IOException | IndexOutOfBoundsException e) {
|
||||
logger.error("Could not load image for skin {} of type {}", skin.name(), skin.type(), e);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Cached {} skin previews", skinPreviewCache.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHandlers(Javalin javalin) {
|
||||
javalin.get("/dashboard/previewskin", this::handlePreviewSkin);
|
||||
javalin.get("/dashboard/dlc", this::handleRetrieveDlcList);
|
||||
javalin.get("/dashboard/profile", this::handleRetrieveProfile);
|
||||
javalin.post("/dashboard/profile", this::handleUpdateProfile);
|
||||
@@ -73,6 +102,31 @@ public class DashboardHandler implements HttpHandler {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* GET request handler for {@code /dashboard/previewskin}
|
||||
*/
|
||||
private void handlePreviewSkin(Context ctx) throws IOException {
|
||||
String type = ctx.queryParam("type");
|
||||
String name = ctx.queryParam("name");
|
||||
|
||||
// Make sure query parameters are present
|
||||
if(type == null || name == null) {
|
||||
ctx.status(404);
|
||||
return;
|
||||
}
|
||||
|
||||
Dlc dlc = dlcList.getDlc("IRAO", type, name);
|
||||
|
||||
// Check if DLC exists
|
||||
if(dlc == null) {
|
||||
ctx.status(404);
|
||||
return;
|
||||
}
|
||||
|
||||
// Write cached image data
|
||||
ImageIO.write(skinPreviewCache.get(dlc), "png", ctx.outputStream());
|
||||
}
|
||||
|
||||
/**
|
||||
* GET request handler for {@code /dashboard/dlc}
|
||||
*/
|
||||
@@ -86,7 +140,7 @@ public class DashboardHandler implements HttpHandler {
|
||||
}
|
||||
|
||||
// Send result
|
||||
ctx.json(dlcList.getDlcList("IRAO", type).stream().map(Dlc::name).collect(Collectors.toList()));
|
||||
ctx.json(dlcList.getDlcList("IRAO", type).stream().map(Dlc::name).sorted().collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,14 +253,8 @@ public class DashboardHandler implements HttpHandler {
|
||||
}
|
||||
|
||||
for(DreamEncounter encounter : request.encounters()) {
|
||||
if(encounter.species() < 1) {
|
||||
if(encounter.species() < 1 || encounter.species() > 649) {
|
||||
return "Species is out of range.";
|
||||
} else if(encounter.species() > 493) {
|
||||
if(!player.getGameVersion().isVersion2()) {
|
||||
return "Sorry, Generation V Pokémon are exclusive to Black Version 2 and White Version 2.";
|
||||
} else if(!availableBlackAndWhiteSpecies.contains(encounter.species())) {
|
||||
return "You have selected one or more Pokémon species that cannot be downloaded.";
|
||||
}
|
||||
} else if(encounter.move() < 0 || encounter.move() > 559) {
|
||||
return "Move ID is out of range.";
|
||||
} else if(encounter.gender() == null) {
|
||||
@@ -226,11 +274,7 @@ public class DashboardHandler implements HttpHandler {
|
||||
for(DreamItem item : request.items()) {
|
||||
if(item.id() < 0 || item.id() > 638) {
|
||||
return "Item ID is out of range";
|
||||
} else if(item.id() > 626 && !player.getGameVersion().isVersion2()) {
|
||||
return "You have selected one or more items that are exclusive to Black Version 2 and White Version 2.";
|
||||
}
|
||||
|
||||
if(item.quantity() < 0 || item.quantity() > 20) {
|
||||
} else if(item.quantity() < 0 || item.quantity() > 20) {
|
||||
return "Item quantity is out of range.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,19 +67,8 @@ public class DlsHandler implements HttpHandler {
|
||||
* POST handler for {@code /download action=list}
|
||||
*/
|
||||
private void handleRetrieveDlcList(DlsRequest request, Context ctx) throws IOException {
|
||||
String gameCode = switch(request.dlcGameCode()) {
|
||||
case "IRAJ" -> "IRAO";
|
||||
default -> request.dlcGameCode();
|
||||
};
|
||||
|
||||
// Map to generic type, I doubt there is a real difference between the language codes anyway.
|
||||
String type = switch(request.dlcType()) {
|
||||
case "CGEAR_E", "CGEAR_F", "CGEAR_I", "CGEAR_G", "CGEAR_S", "CGEAR_J", "CGEAR_K" -> "CGEAR";
|
||||
case "CGEAR2_E", "CGEAR2_F", "CGEAR2_I", "CGEAR2_G", "CGEAR2_S", "CGEAR2_J", "CGEAR2_K" -> "CGEAR2";
|
||||
case "ZUKAN_E", "ZUKAN_F", "ZUKAN_I", "ZUKAN_G", "ZUKAN_S", "ZUKAN_J", "ZUKAN_K" -> "ZUKAN";
|
||||
case "MUSICAL_E", "MUSICAL_F", "MUSICAL_I", "MUSICAL_G", "MUSICAL_S", "MUSICAL_J", "MUSICAL_K" -> "MUSICAL";
|
||||
default -> request.dlcType();
|
||||
};
|
||||
String gameCode = getDlcGameCode(request.dlcGameCode());
|
||||
String type = getRegionlessDlcType(request.dlcType());
|
||||
|
||||
// TODO NOTE: I assume that in a conventional implementation, certain DLC attributes may be omitted from the request.
|
||||
ctx.result(dlcList.getDlcListString(dlcList.getDlcList(gameCode, type, request.dlcIndex())));
|
||||
@@ -89,9 +78,11 @@ public class DlsHandler implements HttpHandler {
|
||||
* POST handler for {@code /download action=contents}
|
||||
*/
|
||||
private void handleRetrieveDlcContent(DlsRequest request, Context ctx) throws IOException {
|
||||
// Check if the requested DLC exists
|
||||
Dlc dlc = dlcList.getDlc(request.dlcName());
|
||||
String gameCode = getDlcGameCode(request.dlcGameCode());
|
||||
String type = getRegionlessDlcType(request.dlcType());
|
||||
Dlc dlc = dlcList.getDlc(gameCode, type, request.dlcName());
|
||||
|
||||
// Check if the requested DLC exists
|
||||
if(dlc == null) {
|
||||
ctx.status(HttpStatus.NOT_FOUND);
|
||||
return;
|
||||
@@ -108,4 +99,27 @@ public class DlsHandler implements HttpHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The game serial that should be used for downloading DLC based on the provided input.
|
||||
*/
|
||||
private String getDlcGameCode(String gameCode) {
|
||||
return switch(gameCode) {
|
||||
case "IRAJ" -> "IRAO";
|
||||
default -> gameCode;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The DLC type without the region identifier, or the input if it is an unknown type.
|
||||
*/
|
||||
private String getRegionlessDlcType(String dlcType) {
|
||||
return switch(dlcType) {
|
||||
case "CGEAR_E", "CGEAR_F", "CGEAR_I", "CGEAR_G", "CGEAR_S", "CGEAR_J", "CGEAR_K" -> "CGEAR";
|
||||
case "CGEAR2_E", "CGEAR2_F", "CGEAR2_I", "CGEAR2_G", "CGEAR2_S", "CGEAR2_J", "CGEAR2_K" -> "CGEAR2";
|
||||
case "ZUKAN_E", "ZUKAN_F", "ZUKAN_I", "ZUKAN_G", "ZUKAN_S", "ZUKAN_J", "ZUKAN_K" -> "ZUKAN";
|
||||
case "MUSICAL_E", "MUSICAL_F", "MUSICAL_I", "MUSICAL_G", "MUSICAL_S", "MUSICAL_J", "MUSICAL_K" -> "MUSICAL";
|
||||
default -> dlcType;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,9 +228,9 @@ public class PglHandler implements HttpHandler {
|
||||
// Write misc stuff and DLC information
|
||||
outputStream.writeShort(player.getLevelsGained());
|
||||
outputStream.write(0); // Unknown
|
||||
outputStream.write(dlcList.getDlcIndex(player.getMusical()));
|
||||
outputStream.write(dlcList.getDlcIndex(player.getCGearSkin()));
|
||||
outputStream.write(dlcList.getDlcIndex(player.getDexSkin()));
|
||||
outputStream.write(dlcList.getDlcIndex("IRAO", "MUSICAL", player.getMusical()));
|
||||
outputStream.write(dlcList.getDlcIndex("IRAO", player.getGameVersion().isVersion2() ? "CGEAR2" : "CGEAR", player.getCGearSkin()));
|
||||
outputStream.write(dlcList.getDlcIndex("IRAO", "ZUKAN", player.getDexSkin()));
|
||||
outputStream.write(decorList.isEmpty() ? 0 : 1); // Seems to be a flag for indicating whether or not decor data is present
|
||||
outputStream.write(0); // Must be zero?
|
||||
|
||||
|
||||
@@ -18,7 +18,14 @@ public class NetworkUtility {
|
||||
public static InetAddress getLocalHost() {
|
||||
try(Socket socket = new Socket()){
|
||||
socket.connect(new InetSocketAddress("github.com", 80));
|
||||
return socket.getLocalAddress();
|
||||
InetAddress address = socket.getLocalAddress();
|
||||
|
||||
// Fall back to the network interface method if this is not a private IP address
|
||||
if(!address.isSiteLocalAddress()) {
|
||||
return getLocalHostFromNetworkInterfaces();
|
||||
}
|
||||
|
||||
return address;
|
||||
} catch(IOException e) {
|
||||
logger.error("Couldn't get local host using socket - falling back to the network interface method", e);
|
||||
return getLocalHostFromNetworkInterfaces();
|
||||
@@ -42,8 +49,8 @@ public class NetworkUtility {
|
||||
while(addresses.hasMoreElements()) {
|
||||
InetAddress address = addresses.nextElement();
|
||||
|
||||
// Return if IPv4
|
||||
if(address instanceof Inet4Address) {
|
||||
// Return this address if it is a local IPv4 address
|
||||
if(address.isSiteLocalAddress() && address instanceof Inet4Address) {
|
||||
return address;
|
||||
}
|
||||
}
|
||||
@@ -52,6 +59,7 @@ public class NetworkUtility {
|
||||
logger.error("Could not determine local host - falling back to loopback address", e);
|
||||
}
|
||||
|
||||
logger.warn("No local host candidate could be found - loopback address will be used");
|
||||
return InetAddress.getLoopbackAddress();
|
||||
}
|
||||
}
|
||||
|
||||
130
src/main/java/entralinked/utility/TiledImageReader.java
Normal file
130
src/main/java/entralinked/utility/TiledImageReader.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package entralinked.utility;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Utility class for reading tiled images (C-Gear & Pokédex skin data) into a usable {@link BufferedImage}.
|
||||
*/
|
||||
public class TiledImageReader {
|
||||
|
||||
public static final int TILE_WIDTH = 8;
|
||||
public static final int TILE_HEIGHT = 8;
|
||||
public static final int TILE_SIZE = TILE_WIDTH * TILE_HEIGHT;
|
||||
public static final int COLOR_PALETTE_SIZE = 16;
|
||||
public static final int SCREEN_WIDTH = 256;
|
||||
public static final int SCREEN_HEIGHT = 192;
|
||||
public static final int SCREEN_TILE_COUNT = SCREEN_WIDTH * SCREEN_HEIGHT / TILE_SIZE;
|
||||
|
||||
/**
|
||||
* Calls {@link #readTiledImage(InputStream, int, boolean)} with a tile count of 255.
|
||||
*
|
||||
* @param normalizeIndices Should be {@code true} if the provided C-Gear skin data is from the original Black & White.
|
||||
* @return A {@link BufferedImage} representing the read C-Gear skin data.
|
||||
*/
|
||||
public static BufferedImage readCGearSkin(InputStream inputStream, boolean normalizeIndices) throws IOException {
|
||||
return readTiledImage(inputStream, 255, normalizeIndices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #readTiledImage(InputStream, int, boolean)} with a tile count of 768 and index normalization disabled.
|
||||
*
|
||||
* @return A {@link BufferedImage} representing the read Pokédex skin data.
|
||||
*/
|
||||
public static BufferedImage readDexSkin(InputStream inputStream) throws IOException {
|
||||
return readTiledImage(inputStream, 768, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads tiled image data from the provided {@link InputStream} and returns a {@link BufferedImage} representing the read image data.
|
||||
*
|
||||
* @param tileCount The number of tiles to be read. This should be equal to the maximum number of tiles for this image.
|
||||
* @param normalizedIndices Indicates that tile indices are not linear (Black & White C-Gear skins) and should be normalized.
|
||||
* @return A {@link BufferedImage} representing the read image data.
|
||||
*/
|
||||
public static BufferedImage readTiledImage(InputStream inputStream, int tileCount, boolean normalizeIndices) throws IOException {
|
||||
BufferedImage image = new BufferedImage(SCREEN_WIDTH, SCREEN_HEIGHT, BufferedImage.TYPE_INT_RGB); // Result image
|
||||
int[] tileData = new int[tileCount * TILE_SIZE];
|
||||
int[] tileIndices = new int[tileCount]; // Tile index lookup table
|
||||
int[] colorPalette = new int[COLOR_PALETTE_SIZE];
|
||||
|
||||
// Read tile data.
|
||||
for(int i = 0; i < tileCount; i++) {
|
||||
for(int j = 0; j < TILE_SIZE / 2; j++) {
|
||||
int paletteIndices = inputStream.read(); // Contains color palette indices for 2 adjacent pixels.
|
||||
tileData[i * TILE_SIZE + j * 2] = paletteIndices & (COLOR_PALETTE_SIZE - 1);
|
||||
tileData[i * TILE_SIZE + j * 2 + 1] = (paletteIndices >> 4) & (COLOR_PALETTE_SIZE - 1);
|
||||
}
|
||||
|
||||
// Store the index of the tile as it would be in memory so we can look it up later when we're mapping the tiles.
|
||||
tileIndices[i] = normalizeIndices ? i + i / 17 * 15 + 0xA0A0 : i;
|
||||
}
|
||||
|
||||
// Read color data.
|
||||
// Pokédex skins contain room for 240 extra colors, 64 of which are defined and appear to be used as the
|
||||
// 'background' colors in cases where the skin is only an overlay that is displayed on top of the 'true' Pokédex.
|
||||
for(int i = 0; i < COLOR_PALETTE_SIZE; i++) {
|
||||
int color = inputStream.read() | inputStream.read() << 8;
|
||||
|
||||
// Convert BGR555 to RGB888
|
||||
int red = (color & 0x1F) << 3;
|
||||
int green = ((color & 0x3E0) >> 5) << 3;
|
||||
int blue = ((color & 0x7C00) >> 10) << 3;
|
||||
colorPalette[i] = (red << 16) | (green << 8) | blue;
|
||||
}
|
||||
|
||||
// Map tiles to the resulting image.
|
||||
// In cases where the tile count is 768 or greater, which is exactly enough tiles to fill the entire screen,
|
||||
// the tiles will be applied in the order they are provided and no additional mapping data will be read.
|
||||
// This is always the case for Pokédex skins, and never the case for C-Gear skins.
|
||||
if(tileCount < SCREEN_TILE_COUNT) {
|
||||
// Not enough tiles -- read additional mapping data to figure out their placement.
|
||||
for(int i = 0; i < SCREEN_TILE_COUNT; i++) {
|
||||
int x = i * TILE_WIDTH % SCREEN_WIDTH;
|
||||
int y = i * TILE_WIDTH / SCREEN_WIDTH * TILE_HEIGHT;
|
||||
int leftBits = inputStream.read();
|
||||
int rightBits = inputStream.read();
|
||||
int memoryIndex = leftBits | (rightBits & ~12) << 8;
|
||||
int flipBits = rightBits & 12;
|
||||
|
||||
// The normalized tile index is the index of the in-memory tile index in the lookup table.
|
||||
int tileIndex = 0;
|
||||
|
||||
for(int k = 0; k < tileIndices.length; k++) {
|
||||
if(memoryIndex == tileIndices[k]) {
|
||||
tileIndex = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the pixels of this tile to the resulting image.
|
||||
for(int j = 0; j < TILE_SIZE; j++) {
|
||||
// Get the color index for this pixel based on how the tile is flipped.
|
||||
int tilePixelIndex = switch(flipBits) {
|
||||
case 4 -> (TILE_WIDTH * (j / TILE_WIDTH) + TILE_WIDTH) - j % TILE_WIDTH - 1; // Flip horizontally
|
||||
case 8 -> TILE_SIZE - (TILE_WIDTH * (j / TILE_WIDTH) + TILE_WIDTH) + j % TILE_WIDTH; // Flip vertically
|
||||
case 12 -> TILE_SIZE - j - 1; // Flip horizontally & vertically
|
||||
default -> j; // Don't flip
|
||||
};
|
||||
|
||||
// Finally, set the pixel!
|
||||
int paletteIndex = tileData[tileIndex * TILE_SIZE + tilePixelIndex];
|
||||
image.setRGB(x + j % TILE_WIDTH, y + j / TILE_WIDTH, colorPalette[paletteIndex]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// There are enough tiles to fill up the entire screen, so let's just place them in order.
|
||||
for(int i = 0; i < SCREEN_TILE_COUNT; i++) {
|
||||
int x = i * TILE_WIDTH % SCREEN_WIDTH;
|
||||
int y = i * TILE_WIDTH / SCREEN_WIDTH * TILE_WIDTH;
|
||||
|
||||
for(int j = 0; j < TILE_SIZE; j++) {
|
||||
image.setRGB(x + j % TILE_WIDTH, y + j / TILE_WIDTH, colorPalette[tileData[i * TILE_SIZE + j]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="styles/list.css">
|
||||
</head>
|
||||
<body onload="fillTableWithItems()">
|
||||
<div id="main-container" class="root-container">
|
||||
<label class="big-label">List of items that can be downloaded</label>
|
||||
<label>NOTE: Item IDs greater than 626 are exclusive to Black Version 2 and White Version 2</label>
|
||||
<table id="item-table">
|
||||
<!-- Filled by list.js -->
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
<script src="scripts/list.js"></script>
|
||||
</html>
|
||||
@@ -12,5 +12,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script src="scripts/utility.js"></script>
|
||||
<script src="scripts/login.js"></script>
|
||||
</html>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="styles/profile.css">
|
||||
</head>
|
||||
<body onload="fetchProfileData()">
|
||||
<body>
|
||||
<div id="main-container" class="root-container" style="display:none;">
|
||||
<div>
|
||||
<div>
|
||||
@@ -64,6 +64,30 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div id="visitor-table-container" style="display:none;">
|
||||
<!-- Join Avenue Visitor Configuration -->
|
||||
<label>Join Avenue Visitors (Max. 12)</label><br>
|
||||
<div>
|
||||
<table id="visitor-table" class="visitor-table">
|
||||
<tr>
|
||||
<td><a id="visitor0" href="#configureVisitor" onclick="configureVisitor(0)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor1" href="#configureVisitor" onclick="configureVisitor(1)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor2" href="#configureVisitor" onclick="configureVisitor(2)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor3" href="#configureVisitor" onclick="configureVisitor(3)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor4" href="#configureVisitor" onclick="configureVisitor(4)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor5" href="#configureVisitor" onclick="configureVisitor(5)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="visitor6" href="#configureVisitor" onclick="configureVisitor(6)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor7" href="#configureVisitor" onclick="configureVisitor(7)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor8" href="#configureVisitor" onclick="configureVisitor(8)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor9" href="#configureVisitor" onclick="configureVisitor(9)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor10" href="#configureVisitor" onclick="configureVisitor(10)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor11" href="#configureVisitor" onclick="configureVisitor(11)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<!-- Item Configuration -->
|
||||
<label>Items (Max. 20)</label><br>
|
||||
@@ -96,49 +120,28 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div id="visitor-table-container" style="display:none;">
|
||||
<!-- Join Avenue Visitor Configuration -->
|
||||
<label>Join Avenue Visitors (Max. 12)</label><br>
|
||||
<div>
|
||||
<table id="visitor-table" class="visitor-table">
|
||||
<tr>
|
||||
<td><a id="visitor0" href="#configureVisitor" onclick="configureVisitor(0)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor1" href="#configureVisitor" onclick="configureVisitor(1)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor2" href="#configureVisitor" onclick="configureVisitor(2)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor3" href="#configureVisitor" onclick="configureVisitor(3)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor4" href="#configureVisitor" onclick="configureVisitor(4)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor5" href="#configureVisitor" onclick="configureVisitor(5)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a id="visitor6" href="#configureVisitor" onclick="configureVisitor(6)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor7" href="#configureVisitor" onclick="configureVisitor(7)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor8" href="#configureVisitor" onclick="configureVisitor(8)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor9" href="#configureVisitor" onclick="configureVisitor(9)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor10" href="#configureVisitor" onclick="configureVisitor(10)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
<td><a id="visitor11" href="#configureVisitor" onclick="configureVisitor(11)"><image src="/sprites/trainers/none.png"/></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<!-- Misc Configurations -->
|
||||
<div class="grid-container">
|
||||
<div>
|
||||
<label>C-Gear Skin</label>
|
||||
<label>C-Gear Skin | </label><a href="#" onclick="return previewSkin('cgear-skin', 'CGEAR')">Preview</a>
|
||||
<select id="cgear-skin">
|
||||
<option value="none">Do not change</option>
|
||||
<option disabled>──────────────────────</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Pokédex Skin</label>
|
||||
<label>Pokédex Skin | </label><a href="#" onclick="return previewSkin('dex-skin', 'ZUKAN')">Preview</a>
|
||||
<select id="dex-skin">
|
||||
<option value="none">Do not change</option>
|
||||
<option disabled>──────────────────────</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Musical Show</label>
|
||||
<select id="musical">
|
||||
<option value="none">Do not change</option>
|
||||
<option disabled>──────────────────────</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
@@ -158,12 +161,20 @@
|
||||
<div class="content">
|
||||
<button class="close-button" onclick="closeEncounterForm()">X</button>
|
||||
<form id="encounter-form">
|
||||
<label for="encounter-form-species">Species ID | </label><a href="/dashboard/species.html" target="_blank">View list</a>
|
||||
<input id="encounter-form-species" name="species" type="number" value="1" min="1" max="493"/>
|
||||
<label for="encounter-form-move">Move ID</label>
|
||||
<input id="encounter-form-move" name="move" type="number" value="0" min="0" max="559"/>
|
||||
<label for="encounter-form-form">Forme Index</label>
|
||||
<input id="encounter-form-form" name="form" type="number" value="0" min="0" max="31"/>
|
||||
<label for="encounter-form-species">Species</label>
|
||||
<select id="encounter-form-species" name="species" value="1">
|
||||
<!-- Filled by profile.js -->
|
||||
</select>
|
||||
<label for="encounter-form-move">Special Move</label>
|
||||
<select id="encounter-form-move" name="move" value="0">
|
||||
<option value="0">None</option>
|
||||
<!-- Filled by profile.js -->
|
||||
</select>
|
||||
<label for="encounter-form-form">Form</label>
|
||||
<select id="encounter-form-form" name="form" value="0">
|
||||
<option value="0">Normal</option>
|
||||
<!-- Filled by profile.js -->
|
||||
</select>
|
||||
<label for="encounter-form-gender">Gender</label>
|
||||
<select id="encounter-form-gender" name="gender" value="GENDERLESS">
|
||||
<option value="MALE">Male</option>
|
||||
@@ -191,8 +202,10 @@
|
||||
<div class="content">
|
||||
<button class="close-button" onclick="closeEncounterForm()">X</button>
|
||||
<form id="item-form">
|
||||
<label for="item-form-id">Item ID | </label><a href="/dashboard/items.html" target="_blank">View list</a>
|
||||
<input id="item-form-id" name="id" type="number" value="1" min="1" max="626"/>
|
||||
<label for="item-form-id">Item</label>
|
||||
<select id="item-form-id" name="id" value="1">
|
||||
<!-- Filled by profile.js -->
|
||||
</select>
|
||||
<label for="item-form-quantity">Quantity</label>
|
||||
<input id="item-form-quantity" name="quantity" type="number" value="1" min="1" max="20"/>
|
||||
</form>
|
||||
@@ -246,12 +259,16 @@
|
||||
<label for="visitor-form-personality">Personality (Affects phrases used)</label>
|
||||
<input id="visitor-form-personality" name="personality" type="number" value="0" min="0" max="7"/>
|
||||
<label for="visitor-form-dreamer">Tucked-in Pokémon Species</label>
|
||||
<input id="visitor-form-dreamer" name="personality" type="number" value="1" min="1" max="649"/>
|
||||
<select id="visitor-form-dreamer" name="dreamerSpecies" value="1">
|
||||
<!-- Filled by profile.js -->
|
||||
</select>
|
||||
</form>
|
||||
<button class="big-button" onclick="saveVisitor()">Confirm</button>
|
||||
<button class="big-button" onclick="removeVisitor()">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script src="scripts/utility.js"></script>
|
||||
<script src="scripts/pokedata.js"></script>
|
||||
<script src="scripts/profile.js"></script>
|
||||
</html>
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
const AVAILABLE_GENERATION_V_POKEMON = new Array(
|
||||
505, 507, 510, 511, 513, 515, 519, 523, 525, 527, 529, 531, 533, 535, 538, 539, 542, 545, 546, 548,
|
||||
550, 553, 556, 558, 559, 561, 564, 569, 572, 575, 578, 580, 583, 587, 588, 594, 596, 600, 605, 607,
|
||||
610, 613, 616, 618, 619, 621, 622, 624, 626, 628, 630, 631, 632);
|
||||
|
||||
const ITEMS_TO_EXCLUDE = new Array(113, 114, 115, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 426, 427);
|
||||
|
||||
function fillTable(tableId, rowSize, elementCount, htmlAppender) {
|
||||
let tableHTML = "";
|
||||
let index = 0;
|
||||
|
||||
for(let value = 1; value <= elementCount; value++) {
|
||||
// Call the provided HTML appender to see what should be appended to the table
|
||||
let toAppend = htmlAppender(value);
|
||||
|
||||
// If there is nothing to append (that is, the function has decided this element should be skipped)
|
||||
// then continue to the next loop.
|
||||
if(!toAppend) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Open a new table row if it should
|
||||
if(index % rowSize == 0) {
|
||||
tableHTML += "<tr>";
|
||||
}
|
||||
|
||||
// Append the HTML data
|
||||
tableHTML += toAppend;
|
||||
|
||||
// Close the table row if it has reached the maximum number of elements
|
||||
if(index % rowSize == rowSize) {
|
||||
tableHTML += "<tr>";
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
// Set the inner HTML
|
||||
document.getElementById(tableId).innerHTML = tableHTML;
|
||||
}
|
||||
|
||||
function fillTableWithSpecies() {
|
||||
fillTable("species-table", 10, 649, (species) => {
|
||||
// Skip this element if species is from Generation V and cannot be downloaded
|
||||
if(species > 493 && !AVAILABLE_GENERATION_V_POKEMON.includes(species)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return "<td style='width:96px;height:96px;'><image src='/sprites/pokemon/normal/" + species + ".png'/><br>#" + species + "</td>";
|
||||
});
|
||||
}
|
||||
|
||||
function fillTableWithItems() {
|
||||
fillTable("item-table", 20, 638, (item) => {
|
||||
if(ITEMS_TO_EXCLUDE.includes(item)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return "<td style='width:48px;height:48px;'><image src='/sprites/items/" + item + ".png'/><br>#" + item + "</td>"
|
||||
});
|
||||
}
|
||||
@@ -1,20 +1,11 @@
|
||||
const ELEMENT_GSID_INPUT = document.getElementById("gsid");
|
||||
|
||||
function postLogin() {
|
||||
let loginData = {
|
||||
fetchData("POST", "/dashboard/login", new URLSearchParams({
|
||||
gsid: ELEMENT_GSID_INPUT.value
|
||||
}
|
||||
|
||||
fetch("/dashboard/login", {
|
||||
method: "POST",
|
||||
body: new URLSearchParams(loginData)
|
||||
}).then((response) => {
|
||||
return response.json();
|
||||
}).then((response) => {
|
||||
console.log(response);
|
||||
|
||||
if(response.error) {
|
||||
alert(response.message);
|
||||
})).then((response) => {
|
||||
if(response.error) {
|
||||
window.alert(response.message);
|
||||
} else {
|
||||
window.location.href = "/dashboard/profile.html";
|
||||
}
|
||||
|
||||
1860
src/main/resources/dashboard/scripts/pokedata.js
Normal file
1860
src/main/resources/dashboard/scripts/pokedata.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
// HTML document elements
|
||||
const ELEMENT_GAME_SUMMARY = document.getElementById("game-summary");
|
||||
|
||||
// Dreamer elements
|
||||
const ELEMENT_DREAMER_SPRITE = document.getElementById("dreamer-sprite");
|
||||
const ELEMENT_DREAMER_SPECIES = document.getElementById("dreamer-species");
|
||||
const ELEMENT_DREAMER_NATURE = document.getElementById("dreamer-nature");
|
||||
@@ -10,18 +10,15 @@ const ELEMENT_DREAMER_TRAINER = document.getElementById("dreamer-trainer");
|
||||
const ELEMENT_DREAMER_TRAINER_ID = document.getElementById("dreamer-trainer-id");
|
||||
const ELEMENT_DREAMER_LEVEL = document.getElementById("dreamer-level");
|
||||
|
||||
// Encounter form elements
|
||||
const ELEMENT_ENCOUNTER_SPECIES = document.getElementById("encounter-form-species");
|
||||
const ELEMENT_ENCOUNTER_MOVE = document.getElementById("encounter-form-move");
|
||||
const ELEMENT_ENCOUNTER_FORM = document.getElementById("encounter-form-form");
|
||||
const ELEMENT_ENCOUNTER_GENDER = document.getElementById("encounter-form-gender");
|
||||
const ELEMENT_ENCOUNTER_ANIMATION = document.getElementById("encounter-form-animation");
|
||||
|
||||
// Item form elements
|
||||
const ELEMENT_ITEM_ID = document.getElementById("item-form-id");
|
||||
const ELEMENT_ITEM_QUANTITY = document.getElementById("item-form-quantity");
|
||||
|
||||
// Join Avenue Visitor form elements
|
||||
const ELEMENT_VISITOR_NAME = document.getElementById("visitor-form-name");
|
||||
const ELEMENT_VISITOR_TYPE = document.getElementById("visitor-form-type");
|
||||
const ELEMENT_VISITOR_SHOP_TYPE = document.getElementById("visitor-form-shop-type");
|
||||
@@ -29,39 +26,11 @@ const ELEMENT_VISITOR_GAME = document.getElementById("visitor-form-game");
|
||||
const ELEMENT_VISITOR_PERSONALITY = document.getElementById("visitor-form-personality");
|
||||
const ELEMENT_VISITOR_DREAMER = document.getElementById("visitor-form-dreamer");
|
||||
|
||||
// Misc input elements
|
||||
const ELEMENT_CGEAR_SKIN_INPUT = document.getElementById("cgear-skin");
|
||||
const ELEMENT_DEX_SKIN_INPUT = document.getElementById("dex-skin");
|
||||
const ELEMENT_MUSICAL_INPUT = document.getElementById("musical");
|
||||
const ELEMENT_LEVEL_GAIN_INPUT = document.getElementById("level-gain-input");
|
||||
|
||||
// Create event listeners
|
||||
ELEMENT_ENCOUNTER_SPECIES.addEventListener("change", clampValue);
|
||||
ELEMENT_ENCOUNTER_MOVE.addEventListener("change", clampValue);
|
||||
ELEMENT_ITEM_ID.addEventListener("change", clampValue);
|
||||
ELEMENT_ITEM_QUANTITY.addEventListener("change", clampValue);
|
||||
ELEMENT_LEVEL_GAIN_INPUT.addEventListener("change", clampValue);
|
||||
ELEMENT_VISITOR_PERSONALITY.addEventListener("change", clampValue);
|
||||
ELEMENT_VISITOR_DREAMER.addEventListener("change", clampValue);
|
||||
|
||||
function clampValue() {
|
||||
let value = parseInt(this.value);
|
||||
|
||||
if(value < this.min) {
|
||||
this.value = this.min;
|
||||
} else if(value > this.max) {
|
||||
console.log(value);
|
||||
this.value = this.max;
|
||||
}
|
||||
}
|
||||
|
||||
// Other constant stuff
|
||||
const AVAILABLE_GENERATION_V_POKEMON = new Array(
|
||||
505, 507, 510, 511, 513, 515, 519, 523, 525, 527, 529, 531, 533, 535, 538, 539, 542, 545, 546, 548,
|
||||
550, 553, 556, 558, 559, 561, 564, 569, 572, 575, 578, 580, 583, 587, 588, 594, 596, 600, 605, 607,
|
||||
610, 613, 616, 618, 619, 621, 622, 624, 626, 628, 630, 631, 632); // Defining this 3 times is a brilliant idea.
|
||||
|
||||
|
||||
// Local variables
|
||||
var encounterTableIndex = -1;
|
||||
var itemTableIndex = -1;
|
||||
@@ -72,13 +41,124 @@ var profile = {
|
||||
visitors: []
|
||||
};
|
||||
|
||||
(async function() {
|
||||
// Create event listeners
|
||||
clampOnChange(ELEMENT_ITEM_QUANTITY);
|
||||
clampOnChange(ELEMENT_LEVEL_GAIN_INPUT);
|
||||
clampOnChange(ELEMENT_VISITOR_PERSONALITY);
|
||||
|
||||
// Fetch profile data
|
||||
await fetchData("GET", "/dashboard/profile").then((response) => {
|
||||
// Update game summary
|
||||
profile.gameVersion = response.gameVersion;
|
||||
ELEMENT_GAME_SUMMARY.innerHTML = "Game Card in use: " + profile.gameVersion;
|
||||
|
||||
// Update dreamer summary
|
||||
if(response.dreamerInfo) {
|
||||
let dreamerInfo = response.dreamerInfo;
|
||||
ELEMENT_DREAMER_SPRITE.innerHTML = "<image src='" + response.dreamerSprite + "'/>";
|
||||
ELEMENT_DREAMER_SPECIES.innerHTML = POKE_SPECIES_LIST[dreamerInfo.species - 1].name;
|
||||
ELEMENT_DREAMER_NATURE.innerHTML = stringToWord(dreamerInfo.nature);
|
||||
ELEMENT_DREAMER_NAME.innerHTML = dreamerInfo.nickname;
|
||||
ELEMENT_DREAMER_GENDER.innerHTML = stringToWord(dreamerInfo.gender);
|
||||
ELEMENT_DREAMER_TRAINER.innerHTML = dreamerInfo.trainerName;
|
||||
ELEMENT_DREAMER_TRAINER_ID.innerHTML = ("0000" + dreamerInfo.trainerId).slice(-5);
|
||||
ELEMENT_DREAMER_LEVEL.innerHTML = dreamerInfo.level;
|
||||
}
|
||||
|
||||
// Update encounter table
|
||||
if(response.encounters){
|
||||
profile.encounters = response.encounters;
|
||||
updateEncounterTable(0, 10);
|
||||
}
|
||||
|
||||
// Update item table
|
||||
if(response.items){
|
||||
profile.items = response.items;
|
||||
updateItemTable(0, 20);
|
||||
}
|
||||
|
||||
// Update Join Avenue visitor table
|
||||
if(response.avenueVisitors) {
|
||||
profile.visitors = response.avenueVisitors;
|
||||
updateVisitorTable(0, 12);
|
||||
}
|
||||
|
||||
// Update selected DLC
|
||||
profile.cgearSkin = response.cgearSkin ? response.cgearSkin : "none";
|
||||
profile.dexSkin = response.dexSkin ? response.dexSkin : "none";
|
||||
profile.musical = response.musical ? response.musical : "none";
|
||||
fetchDlcData();
|
||||
ELEMENT_LEVEL_GAIN_INPUT.value = response.levelsGained;
|
||||
|
||||
// Show Join Avenue visitor table if Black 2 or White 2
|
||||
if(isVersion2()) {
|
||||
document.getElementById("visitor-table-container").style.display = "block";
|
||||
}
|
||||
|
||||
// Show div
|
||||
document.getElementById("main-container").style.display = "flex";
|
||||
});
|
||||
|
||||
// Add species data
|
||||
for(let i in POKE_SPECIES_LIST) {
|
||||
let species = POKE_SPECIES_LIST[i];
|
||||
let formattedName = "#" + ("00" + species.id).slice(-3) + " - " + species.name;
|
||||
ELEMENT_VISITOR_DREAMER.options[ELEMENT_VISITOR_DREAMER.options.length] = new Option(formattedName, species.id);
|
||||
|
||||
if(species.downloadable && (isVersion2() || species.id <= 493)) {
|
||||
ELEMENT_ENCOUNTER_SPECIES.options[ELEMENT_ENCOUNTER_SPECIES.options.length] = new Option(formattedName, species.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Add move data
|
||||
for(let i in POKE_MOVE_LIST) {
|
||||
let move = POKE_MOVE_LIST[i];
|
||||
let formattedName = "#" + ("00" + move.id).slice(-3) + " - " + move.name;
|
||||
ELEMENT_ENCOUNTER_MOVE.options[ELEMENT_ENCOUNTER_MOVE.options.length] = new Option(formattedName, move.id);
|
||||
}
|
||||
|
||||
// Add item data
|
||||
for(let i in ITEM_LIST) {
|
||||
let item = ITEM_LIST[i];
|
||||
|
||||
if(isVersion2() || item.id <= 626) {
|
||||
let formattedName = "#" + ("00" + item.id).slice(-3) + " - " + item.name;
|
||||
ELEMENT_ITEM_ID.options[ELEMENT_ITEM_ID.options.length] = new Option(formattedName, item.id);
|
||||
}
|
||||
}
|
||||
|
||||
// Event listener for changing the form selector contents when species changes
|
||||
ELEMENT_ENCOUNTER_SPECIES.addEventListener("change", function() {
|
||||
updateEncounterFormOptions();
|
||||
ELEMENT_ENCOUNTER_FORM.value = 0;
|
||||
});
|
||||
})();
|
||||
|
||||
/**
|
||||
* Encounter configuration stuff
|
||||
*/
|
||||
|
||||
function updateEncounterFormOptions() {
|
||||
clearSelectOptions(ELEMENT_ENCOUNTER_FORM);
|
||||
let species = POKE_SPECIES_LIST[ELEMENT_ENCOUNTER_SPECIES.value - 1];
|
||||
|
||||
// Update special form options
|
||||
if(species.forms) {
|
||||
for(let i in species.forms) {
|
||||
ELEMENT_ENCOUNTER_FORM.options[ELEMENT_ENCOUNTER_FORM.options.length] = new Option(species.forms[i], i);
|
||||
}
|
||||
} else {
|
||||
ELEMENT_ENCOUNTER_FORM.options[ELEMENT_ENCOUNTER_FORM.options.length] = new Option("Normal", 0);
|
||||
}
|
||||
}
|
||||
|
||||
function configureEncounter(index) {
|
||||
encounterTableIndex = Math.min(10, Math.min(index, profile.encounters.length));
|
||||
|
||||
// Load existing settings
|
||||
let encounter = profile.encounters[encounterTableIndex];
|
||||
ELEMENT_ENCOUNTER_SPECIES.value = encounter ? encounter.species : 1;
|
||||
ELEMENT_ENCOUNTER_MOVE.value = encounter ? encounter.move : 1;
|
||||
updateEncounterFormOptions();
|
||||
ELEMENT_ENCOUNTER_MOVE.value = encounter ? encounter.move : 0;
|
||||
ELEMENT_ENCOUNTER_FORM.value = encounter ? encounter.form : 0;
|
||||
ELEMENT_ENCOUNTER_GENDER.value = encounter ? encounter.gender : "GENDERLESS";
|
||||
ELEMENT_ENCOUNTER_ANIMATION.value = encounter ? encounter.animation : "WALK_AROUND";
|
||||
@@ -86,64 +166,37 @@ function configureEncounter(index) {
|
||||
|
||||
function saveEncounter() {
|
||||
if(encounterTableIndex < 0) {
|
||||
closeEncounterForm();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this species can be downloaded
|
||||
let species = parseInt(ELEMENT_ENCOUNTER_SPECIES.value);
|
||||
|
||||
if(species > 493 && !AVAILABLE_GENERATION_V_POKEMON.includes(species)) {
|
||||
alert("This Pokémon species cannot be downloaded. Click 'View list' in the encounter form to view a list of available Pokémon.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create encounter data
|
||||
let encounterData = {
|
||||
species: species,
|
||||
profile.encounters[encounterTableIndex] = {
|
||||
species: ELEMENT_ENCOUNTER_SPECIES.value,
|
||||
move: ELEMENT_ENCOUNTER_MOVE.value,
|
||||
form: ELEMENT_ENCOUNTER_FORM.value,
|
||||
gender: ELEMENT_ENCOUNTER_GENDER.value,
|
||||
animation: ELEMENT_ENCOUNTER_ANIMATION.value
|
||||
}
|
||||
|
||||
// Set form to highest form available if it too great
|
||||
let maxForm = 0;
|
||||
|
||||
switch(species) {
|
||||
case 201: maxForm = 27; break; // Unown
|
||||
case 386: maxForm = 3; break; // Deoxys
|
||||
case 412:
|
||||
case 413: maxForm = 2; break; // Burmy & Wormadam
|
||||
case 422:
|
||||
case 423:
|
||||
case 487: maxForm = 1; break; // Shellos, Gastrodon & Giratina
|
||||
case 479: maxForm = 5; break; // Rotom
|
||||
case 493: maxForm = 16; break; // Arceus
|
||||
case 550: maxForm = 1; break; // Basculin
|
||||
}
|
||||
|
||||
if(encounterData.form > maxForm) {
|
||||
encounterData.form = maxForm;
|
||||
}
|
||||
|
||||
profile.encounters[encounterTableIndex] = encounterData;
|
||||
};
|
||||
updateEncounterCell(encounterTableIndex);
|
||||
closeEncounterForm();
|
||||
}
|
||||
|
||||
function removeEncounter() {
|
||||
if(encounterTableIndex < 0) {
|
||||
closeEncounterForm();
|
||||
return;
|
||||
}
|
||||
|
||||
let oldLength = profile.encounters.length;
|
||||
profile.encounters.splice(encounterTableIndex, 1);
|
||||
|
||||
for(let i = encounterTableIndex; i < oldLength; i++) {
|
||||
updateEncounterTable(encounterTableIndex, oldLength);
|
||||
closeEncounterForm();
|
||||
}
|
||||
|
||||
function updateEncounterTable(startIndex, endIndex) {
|
||||
for(let i = startIndex; i < endIndex; i++) {
|
||||
updateEncounterCell(i);
|
||||
}
|
||||
|
||||
closeEncounterForm();
|
||||
}
|
||||
|
||||
function updateEncounterCell(index) {
|
||||
@@ -155,6 +208,7 @@ function updateEncounterCell(index) {
|
||||
if(encounterData) {
|
||||
spriteImage = spriteBase + encounterData.species + ".png";
|
||||
|
||||
// Use unique form sprite if it exists
|
||||
if(encounterData.form > 0) {
|
||||
let formSpriteImage = spriteBase + encounterData.species + "-" + encounterData.form + ".png";
|
||||
|
||||
@@ -172,10 +226,12 @@ function closeEncounterForm() {
|
||||
window.location.href = "#";
|
||||
}
|
||||
|
||||
/**
|
||||
* Join Avenue visitor configuration stuff
|
||||
*/
|
||||
|
||||
function configureVisitor(index) {
|
||||
visitorTableIndex = Math.min(12, Math.min(index, profile.visitors.length));
|
||||
|
||||
// Load existing settings
|
||||
let visitor = profile.visitors[visitorTableIndex];
|
||||
ELEMENT_VISITOR_NAME.value = visitor ? visitor.name : "";
|
||||
ELEMENT_VISITOR_TYPE.value = visitor ? visitor.type : "ACE_TRAINER_MALE";
|
||||
@@ -187,6 +243,7 @@ function configureVisitor(index) {
|
||||
|
||||
function saveVisitor() {
|
||||
if(visitorTableIndex < 0) {
|
||||
closeVisitorForm();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -209,7 +266,7 @@ function saveVisitor() {
|
||||
}
|
||||
|
||||
// I'll make country codes configurable later... probably
|
||||
let visitorData = {
|
||||
profile.visitors[visitorTableIndex] = {
|
||||
name: ELEMENT_VISITOR_NAME.value,
|
||||
type: ELEMENT_VISITOR_TYPE.value,
|
||||
shopType: ELEMENT_VISITOR_SHOP_TYPE.value,
|
||||
@@ -218,26 +275,28 @@ function saveVisitor() {
|
||||
stateProvinceCode: 48, // Washington, D.C.
|
||||
personality: ELEMENT_VISITOR_PERSONALITY.value,
|
||||
dreamerSpecies: ELEMENT_VISITOR_DREAMER.value
|
||||
}
|
||||
|
||||
profile.visitors[visitorTableIndex] = visitorData;
|
||||
};
|
||||
console.log(profile.visitors[visitorTableIndex]);
|
||||
updateVisitorCell(visitorTableIndex);
|
||||
closeVisitorForm();
|
||||
}
|
||||
|
||||
function removeVisitor() {
|
||||
if(visitorTableIndex < 0) {
|
||||
closeVisitorForm();
|
||||
return;
|
||||
}
|
||||
|
||||
let oldLength = profile.visitors.length;
|
||||
profile.visitors.splice(visitorTableIndex, 1);
|
||||
|
||||
for(let i = visitorTableIndex; i < oldLength; i++) {
|
||||
updateVisitorTable(visitorTableIndex, oldLength);
|
||||
closeVisitorForm();
|
||||
}
|
||||
|
||||
function updateVisitorTable(startIndex, endIndex) {
|
||||
for(let i = startIndex; i < endIndex; i++) {
|
||||
updateVisitorCell(i);
|
||||
}
|
||||
|
||||
closeVisitorForm();
|
||||
}
|
||||
|
||||
function updateVisitorCell(index) {
|
||||
@@ -262,10 +321,12 @@ function closeVisitorForm() {
|
||||
window.location.href = "#";
|
||||
}
|
||||
|
||||
/**
|
||||
* Item configuration stuff
|
||||
*/
|
||||
|
||||
function configureItem(index) {
|
||||
itemTableIndex = Math.min(20, Math.min(index, profile.items.length));
|
||||
|
||||
// Loadg existing settings
|
||||
let item = profile.items[itemTableIndex];
|
||||
ELEMENT_ITEM_ID.value = item ? item.id : 1;
|
||||
ELEMENT_ITEM_QUANTITY.value = item ? item.quantity : 1;
|
||||
@@ -273,32 +334,34 @@ function configureItem(index) {
|
||||
|
||||
function saveItem() {
|
||||
if(itemTableIndex < 0) {
|
||||
closeItemForm();
|
||||
return;
|
||||
}
|
||||
|
||||
let itemData = {
|
||||
profile.items[itemTableIndex] = {
|
||||
id: ELEMENT_ITEM_ID.value,
|
||||
quantity: ELEMENT_ITEM_QUANTITY.value
|
||||
}
|
||||
|
||||
profile.items[itemTableIndex] = itemData;
|
||||
};
|
||||
updateItemCell(itemTableIndex);
|
||||
closeItemForm();
|
||||
}
|
||||
|
||||
function removeItem() {
|
||||
if(itemTableIndex < 0) {
|
||||
closeItemForm();
|
||||
return;
|
||||
}
|
||||
|
||||
let oldLength = profile.items.length;
|
||||
profile.items.splice(itemTableIndex, 1);
|
||||
|
||||
for(let i = itemTableIndex; i < oldLength; i++) {
|
||||
updateItemTable(itemTableIndex, oldLength);
|
||||
closeItemForm();
|
||||
}
|
||||
|
||||
function updateItemTable(startIndex, endIndex) {
|
||||
for(let i = startIndex; i < endIndex; i++) {
|
||||
updateItemCell(i);
|
||||
}
|
||||
|
||||
closeItemForm();
|
||||
}
|
||||
|
||||
function updateItemCell(index) {
|
||||
@@ -325,145 +388,55 @@ function closeItemForm() {
|
||||
window.location.href = "#";
|
||||
}
|
||||
|
||||
async function fetchData(path) {
|
||||
return fetchData(path, "GET", null);
|
||||
}
|
||||
/**
|
||||
* Miscellaneous stuff
|
||||
*/
|
||||
|
||||
async function fetchData(path, method, body) {
|
||||
let response = await fetch(path, {
|
||||
method: method,
|
||||
body: body
|
||||
});
|
||||
function previewSkin(inputElementId, type) {
|
||||
let value = document.getElementById(inputElementId).value;
|
||||
|
||||
// Return to login page if unauthorized
|
||||
if(response.status == 401) {
|
||||
window.location.href = "/dashboard/login.html";
|
||||
return;
|
||||
if(value == "none") {
|
||||
window.alert("Please select a skin to preview it.");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return await response.json();
|
||||
} catch(error) {
|
||||
window.alert(error);
|
||||
if(type == "CGEAR" && isVersion2()) {
|
||||
type = "CGEAR2";
|
||||
}
|
||||
|
||||
return null;
|
||||
window.open("/dashboard/previewskin?type=" + type + "&name=" + value);
|
||||
return false;
|
||||
}
|
||||
|
||||
function fetchDlcData() {
|
||||
let cgearType = profile.gameVersion.includes("2") ? "CGEAR2" : "CGEAR"; // Not a good way to do this!
|
||||
|
||||
// Fetch CGear skins
|
||||
fetchData("/dashboard/dlc?type=" + cgearType).then((response) => {
|
||||
addValuesToComboBox(ELEMENT_CGEAR_SKIN_INPUT, response);
|
||||
ELEMENT_CGEAR_SKIN_INPUT.value = profile.cgearSkin;
|
||||
// Fetch C-Gear skins
|
||||
fetchData("GET", "/dashboard/dlc?type=" + (isVersion2() ? "CGEAR2" : "CGEAR")).then((response) => {
|
||||
addDlcNames(ELEMENT_CGEAR_SKIN_INPUT, response);
|
||||
ELEMENT_CGEAR_SKIN_INPUT.value = response.includes(profile.cgearSkin) ? profile.cgearSkin : "none";
|
||||
});
|
||||
|
||||
// Fetch Dex skins
|
||||
fetchData("/dashboard/dlc?type=ZUKAN").then((response) => {
|
||||
addValuesToComboBox(ELEMENT_DEX_SKIN_INPUT, response);
|
||||
ELEMENT_DEX_SKIN_INPUT.value = profile.dexSkin;
|
||||
fetchData("GET", "/dashboard/dlc?type=ZUKAN").then((response) => {
|
||||
addDlcNames(ELEMENT_DEX_SKIN_INPUT, response);
|
||||
ELEMENT_DEX_SKIN_INPUT.value = response.includes(profile.dexSkin) ? profile.dexSkin : "none";
|
||||
});
|
||||
|
||||
// Fetch musicals
|
||||
fetchData("/dashboard/dlc?type=MUSICAL").then((response) => {
|
||||
addValuesToComboBox(ELEMENT_MUSICAL_INPUT, response);
|
||||
ELEMENT_MUSICAL_INPUT.value = profile.musical;
|
||||
fetchData("GET", "/dashboard/dlc?type=MUSICAL").then((response) => {
|
||||
addDlcNames(ELEMENT_MUSICAL_INPUT, response);
|
||||
ELEMENT_MUSICAL_INPUT.value = response.includes(profile.musical) ? profile.musical : "none";
|
||||
});
|
||||
}
|
||||
|
||||
// TODO
|
||||
function fetchProfileData() {
|
||||
fetchData("/dashboard/profile").then((response) => {
|
||||
let gameVersion = response["gameVersion"];
|
||||
let dreamerSprite = response["dreamerSprite"];
|
||||
let dreamerInfo = response["dreamerInfo"];
|
||||
let encounters = response["encounters"];
|
||||
let items = response["items"];
|
||||
let visitors = response["avenueVisitors"];
|
||||
let cgearSkin = response["cgearSkin"];
|
||||
let dexSkin = response["dexSkin"];
|
||||
let musical = response["musical"];
|
||||
let levelsGained = response["levelsGained"];
|
||||
|
||||
// Update game summary
|
||||
profile.gameVersion = gameVersion;
|
||||
ELEMENT_GAME_SUMMARY.innerHTML = "Game Card in use: " + gameVersion;
|
||||
|
||||
// Still don't like this!
|
||||
if(gameVersion.includes("2")) {
|
||||
ELEMENT_ENCOUNTER_SPECIES.max = 649;
|
||||
ELEMENT_ITEM_ID.max = 638;
|
||||
|
||||
// Show Join Avenue visitor table
|
||||
document.getElementById("visitor-table-container").style.display = "block";
|
||||
}
|
||||
|
||||
// Update dreamer summary
|
||||
if(dreamerInfo) {
|
||||
let species = dreamerInfo["species"];
|
||||
let nature = dreamerInfo["nature"];
|
||||
let nickname = dreamerInfo["nickname"];
|
||||
let gender = dreamerInfo["gender"];
|
||||
let trainerName = dreamerInfo["trainerName"];
|
||||
let trainerId = dreamerInfo["trainerId"];
|
||||
let level = dreamerInfo["level"];
|
||||
|
||||
// Set element values
|
||||
ELEMENT_DREAMER_SPRITE.innerHTML = "<image src='" + dreamerSprite + "'/>";
|
||||
ELEMENT_DREAMER_SPECIES.innerHTML = "#" + species;
|
||||
ELEMENT_DREAMER_NATURE.innerHTML = stringToWord(nature);
|
||||
ELEMENT_DREAMER_NAME.innerHTML = nickname;
|
||||
ELEMENT_DREAMER_GENDER.innerHTML = stringToWord(gender);
|
||||
ELEMENT_DREAMER_TRAINER.innerHTML = trainerName;
|
||||
ELEMENT_DREAMER_TRAINER_ID.innerHTML = ("0000" + trainerId).slice(-5);
|
||||
ELEMENT_DREAMER_LEVEL.innerHTML = level;
|
||||
}
|
||||
|
||||
// Update encounter table
|
||||
if(encounters){
|
||||
profile.encounters = encounters;
|
||||
|
||||
for(let i = 0; i < 10; i++) {
|
||||
updateEncounterCell(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Update item table
|
||||
if(items){
|
||||
profile.items = items;
|
||||
|
||||
for(let i = 0; i < 20; i++) {
|
||||
updateItemCell(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Update Join Avenue visitor table
|
||||
if(visitors) {
|
||||
profile.visitors = visitors;
|
||||
|
||||
for(let i = 0; i < 12; i++) {
|
||||
updateVisitorCell(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Update selected DLC
|
||||
profile.cgearSkin = cgearSkin ? cgearSkin : "none";
|
||||
profile.dexSkin = dexSkin ? dexSkin : "none";
|
||||
profile.musical = musical ? musical : "none";
|
||||
fetchDlcData();
|
||||
|
||||
// Update level gain
|
||||
ELEMENT_LEVEL_GAIN_INPUT.value = levelsGained;
|
||||
|
||||
// Show div
|
||||
document.getElementById("main-container").style.display = "flex";
|
||||
});
|
||||
function addDlcNames(selectElement, names) {
|
||||
for(let i in names) {
|
||||
let name = names[i];
|
||||
selectElement.options[selectElement.options.length] = new Option(name.replace(/\.[^/.]+$/, ""), name);
|
||||
}
|
||||
}
|
||||
|
||||
function postProfileData() {
|
||||
// Construct body
|
||||
let profileData = {
|
||||
fetchData("POST", "/dashboard/profile", JSON.stringify({
|
||||
encounters: profile.encounters,
|
||||
items: profile.items,
|
||||
avenueVisitors: profile.visitors,
|
||||
@@ -471,36 +444,18 @@ function postProfileData() {
|
||||
dexSkin: ELEMENT_DEX_SKIN_INPUT.value,
|
||||
musical: ELEMENT_MUSICAL_INPUT.value,
|
||||
gainedLevels: ELEMENT_LEVEL_GAIN_INPUT.value
|
||||
}
|
||||
|
||||
// Send data
|
||||
fetchData("/dashboard/profile", "POST", JSON.stringify(profileData)).then((response) => {
|
||||
})).then((response) => {
|
||||
alert(response.message);
|
||||
});
|
||||
}
|
||||
|
||||
function postLogout() {
|
||||
fetchData("/dashboard/logout", "POST", null).then((response) => {
|
||||
fetchData("POST", "/dashboard/logout").then((response) => {
|
||||
// Assume it succeeded
|
||||
window.location.href = "/dashboard/login.html";
|
||||
});
|
||||
}
|
||||
|
||||
// TODO bad
|
||||
function checkURL(url) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('HEAD', url, false);
|
||||
request.send();
|
||||
return request.status == 200;
|
||||
}
|
||||
|
||||
function stringToWord(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
|
||||
}
|
||||
|
||||
function addValuesToComboBox(selectorElement, values) {
|
||||
for(i in values) {
|
||||
let value = values[i];
|
||||
selectorElement.options[selectorElement.options.length] = new Option(value, value);
|
||||
}
|
||||
function isVersion2() {
|
||||
return profile.gameVersion.includes("2");
|
||||
}
|
||||
|
||||
59
src/main/resources/dashboard/scripts/utility.js
Normal file
59
src/main/resources/dashboard/scripts/utility.js
Normal file
@@ -0,0 +1,59 @@
|
||||
function clampOnChange(inputElement) {
|
||||
inputElement.addEventListener("change", function() {
|
||||
let value = parseInt(this.value);
|
||||
|
||||
if(value < this.min) {
|
||||
this.value = this.min;
|
||||
} else if(value > this.max) {
|
||||
this.value = this.max;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function clearSelectOptions(selectElement) {
|
||||
let startIndex = selectElement.options.length - 1;
|
||||
|
||||
for(i = startIndex; i >= 0; i--) {
|
||||
selectElement.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
function stringToWord(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
|
||||
}
|
||||
|
||||
function checkURL(url) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('HEAD', url, false);
|
||||
request.send();
|
||||
return request.status == 200;
|
||||
}
|
||||
|
||||
async function fetchData(method, path) {
|
||||
return fetchData(method, path, null);
|
||||
}
|
||||
|
||||
async function fetchData(method, path, body) {
|
||||
let response = await fetch(path, {
|
||||
method: method,
|
||||
body: body
|
||||
});
|
||||
|
||||
if(response.status != 200) {
|
||||
if(response.status == 401) {
|
||||
window.location.href = "/dashboard/login.html"; // TODO not epic idea to put this here
|
||||
return {};
|
||||
}
|
||||
|
||||
window.alert("Server returned status code " + response.status + " while fetching " + path);
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
return await response.json();
|
||||
} catch(error) {
|
||||
window.alert("Could not deserialize JSON response: " + error);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="styles/list.css">
|
||||
</head>
|
||||
<body onload="fillTableWithSpecies()">
|
||||
<div id="main-container" class="root-container"">
|
||||
<label class="big-label">List of Pokémon species that can be downloaded</label>
|
||||
<label>NOTE: Generation V Pokémon are exclusive to Black Version 2 and White Version 2</label>
|
||||
<table id="species-table">
|
||||
<!-- Filled by list.js -->
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
<script src="scripts/list.js"></script>
|
||||
</html>
|
||||
@@ -1,37 +0,0 @@
|
||||
body {
|
||||
background-color: #191919;
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
img {
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
label {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
table {
|
||||
border-spacing: 8px;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
td {
|
||||
background-color: #262626;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.root-container {
|
||||
display: grid;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
width: 1136; /* Hack-ish */
|
||||
}
|
||||
|
||||
.big-label {
|
||||
font-size: 36px;
|
||||
}
|
||||
@@ -46,7 +46,7 @@ a:link, a:visited {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.root-container div {
|
||||
.root-container div:first-child {
|
||||
max-width: 532px;
|
||||
margin: auto;
|
||||
}
|
||||
@@ -72,16 +72,15 @@ a:link, a:visited {
|
||||
margin-top: 3px;
|
||||
margin-left: -2px;
|
||||
margin-bottom: 20px;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.dreamer-summary th {
|
||||
text-align: left;
|
||||
padding-left: 10px;
|
||||
min-width: 76px;
|
||||
}
|
||||
|
||||
.dreamer-summary td {
|
||||
width: 50%;
|
||||
background-color: #262626;
|
||||
border-radius: 10px;
|
||||
padding-left: 5px;
|
||||
@@ -128,8 +127,8 @@ a:link, a:visited {
|
||||
background-color: #191919;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 25%;
|
||||
transform: translate(-50%, 0%);
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
padding: 20px;
|
||||
width: 500px;
|
||||
}
|
||||
@@ -191,4 +190,5 @@ a:link, a:visited {
|
||||
.big-button {
|
||||
font-size: 16px;
|
||||
padding: 16px 32px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
@@ -1,44 +1,59 @@
|
||||
/dlc/IRAO/CGEAR/C003_munna_1_en.bin
|
||||
/dlc/IRAO/CGEAR/C004_pikachu_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C005_chirami_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C007_tabunne_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C008_pocchama_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C009_guregguru_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C010_fushigibana_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C011_rizadon_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C012_kamekkusu_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C013_zekurom_1_e_v02.bin
|
||||
/dlc/IRAO/CGEAR/C014_reshiram_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C015_vikutini_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C017_porigonz_0_e.bin
|
||||
/dlc/IRAO/CGEAR/C018_WCS_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C019_shikijikaharu_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C020_shikijikanatu_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C021_shikijikaaki_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C022_shikijikafuyu_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C023_iwaparesu_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C024_zoroark_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C025_giaru_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C030_kerudhio_1_en.bin
|
||||
/dlc/IRAO/CGEAR/C031-1_meroetta_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C032-1_wcs2012_1_e.bin
|
||||
/dlc/IRAO/CGEAR/C100_defo_1_en.bin
|
||||
/dlc/IRAO/CGEAR2/C003-2_munna_1_en.bin
|
||||
/dlc/IRAO/CGEAR2/C013-2_zekrom_1_e.bin
|
||||
/dlc/IRAO/CGEAR2/C014-2_reshiram_1_e.bin
|
||||
/dlc/IRAO/CGEAR2/C015-2_victini_1_e.bin
|
||||
/dlc/IRAO/CGEAR2/C030-2_kerudhio_1_en.bin
|
||||
/dlc/IRAO/CGEAR2/C031-2_meroetta_1_e.bin
|
||||
/dlc/IRAO/CGEAR2/C033_halloween_1_en.bin
|
||||
/dlc/IRAO/CGEAR2/C035_BK_1_e.bin
|
||||
/dlc/IRAO/CGEAR2/C036_WK_1_e.bin
|
||||
/dlc/IRAO/CGEAR2/C100-2_default_1_e.bin
|
||||
/dlc/IRAO/MUSICAL/M010_munna_1_e_02.bin
|
||||
/dlc/IRAO/MUSICAL/M013_meloetta_1_e.bin
|
||||
/dlc/IRAO/ZUKAN/Z003_BWsyokigirl_1_en.bin
|
||||
/dlc/IRAO/ZUKAN/Z004_BWsyokiboy_1_en.bin
|
||||
/dlc/IRAO/ZUKAN/Z007_hyuu_1_en.bin
|
||||
/dlc/IRAO/ZUKAN/Z008_bell_1_e.bin
|
||||
/dlc/IRAO/ZUKAN/Z009_tyeren_1_e.bin
|
||||
/dlc/IRAO/ZUKAN/Z100_defogirl_1_en.bin
|
||||
/dlc/IRAO/ZUKAN/Z101_defoboy_1_en.bin
|
||||
/dlc/IRAO/CGEAR/01 - Default.bin
|
||||
/dlc/IRAO/CGEAR/02 - Meadow Munna.bin
|
||||
/dlc/IRAO/CGEAR/03 - Aim for the Top.bin
|
||||
/dlc/IRAO/CGEAR/04 - Twinkle Minccino.bin
|
||||
/dlc/IRAO/CGEAR/05 - PokeSma Purrloin.bin
|
||||
/dlc/IRAO/CGEAR/06 - Earful Audino.bin
|
||||
/dlc/IRAO/CGEAR/07 - Happy Piplup.bin
|
||||
/dlc/IRAO/CGEAR/08 - Poison Jab Croagunk.bin
|
||||
/dlc/IRAO/CGEAR/09 - Venusaur!.bin
|
||||
/dlc/IRAO/CGEAR/10 - Charizard!.bin
|
||||
/dlc/IRAO/CGEAR/11 - Blastoise!.bin
|
||||
/dlc/IRAO/CGEAR/12 - V for Victory!.bin
|
||||
/dlc/IRAO/CGEAR/13 - Virtual Pokemon.bin
|
||||
/dlc/IRAO/CGEAR/14 - Pokemon Cafe.bin
|
||||
/dlc/IRAO/CGEAR/15 - Hero Reshiram.bin
|
||||
/dlc/IRAO/CGEAR/16 - Hero Zekrom.bin
|
||||
/dlc/IRAO/CGEAR/17 - 2011 Worlds C-Gear.bin
|
||||
/dlc/IRAO/CGEAR/18 - Zoroark!.bin
|
||||
/dlc/IRAO/CGEAR/19 - Spring Deerling.bin
|
||||
/dlc/IRAO/CGEAR/20 - Summer Deerling.bin
|
||||
/dlc/IRAO/CGEAR/21 - Autumn Deerling.bin
|
||||
/dlc/IRAO/CGEAR/22 - Winter Deerling.bin
|
||||
/dlc/IRAO/CGEAR/23 - CRUSTLE!.bin
|
||||
/dlc/IRAO/CGEAR/24 - KLINK!.bin
|
||||
/dlc/IRAO/CGEAR/25 - Ducklett Friends.bin
|
||||
/dlc/IRAO/CGEAR/26 - Guidance Cobalion.bin
|
||||
/dlc/IRAO/CGEAR/27 - Trial Terrakion.bin
|
||||
/dlc/IRAO/CGEAR/28 - Rumination Virizion.bin
|
||||
/dlc/IRAO/CGEAR/29 - Keldeo Ordinary Forme.bin
|
||||
/dlc/IRAO/CGEAR/30 - Aria of the Night Sky.bin
|
||||
/dlc/IRAO/CGEAR/31 - 2012 Worlds C-Gear.bin
|
||||
/dlc/IRAO/CGEAR/32 - Red Genesect.bin
|
||||
/dlc/IRAO/CGEAR2/01 - Default.bin
|
||||
/dlc/IRAO/CGEAR2/02 - Meadow Munna.bin
|
||||
/dlc/IRAO/CGEAR2/03 - Keldeo Resolute Forme.bin
|
||||
/dlc/IRAO/CGEAR2/04 - 2012 Worlds C-Gear.bin
|
||||
/dlc/IRAO/CGEAR2/05 - Pumpkin Pikachu.bin
|
||||
/dlc/IRAO/CGEAR2/06 - V for Victory!.bin
|
||||
/dlc/IRAO/CGEAR2/07 - Hero Reshiram.bin
|
||||
/dlc/IRAO/CGEAR2/08 - Hero Zekrom.bin
|
||||
/dlc/IRAO/CGEAR2/09 - Aria of the Night Sky.bin
|
||||
/dlc/IRAO/CGEAR2/10 - Sleeping Eevee.bin
|
||||
/dlc/IRAO/CGEAR2/11 - Venusaur!.bin
|
||||
/dlc/IRAO/CGEAR2/12 - Charizard!.bin
|
||||
/dlc/IRAO/CGEAR2/13 - Blastoise!.bin
|
||||
/dlc/IRAO/CGEAR2/14 - Red Genesect.bin
|
||||
/dlc/IRAO/CGEAR2/15 - Black Kyurem.bin
|
||||
/dlc/IRAO/CGEAR2/16 - White Kyurem.bin
|
||||
/dlc/IRAO/MUSICAL/01 - Charming Munna.bin
|
||||
/dlc/IRAO/MUSICAL/02 - MELOETTAAA!!!.bin
|
||||
/dlc/IRAO/ZUKAN/01 - Default (Pink).bin
|
||||
/dlc/IRAO/ZUKAN/02 - Default (Red).bin
|
||||
/dlc/IRAO/ZUKAN/03 - Unova Trio (Pink).bin
|
||||
/dlc/IRAO/ZUKAN/04 - Unova Trio (Red).bin
|
||||
/dlc/IRAO/ZUKAN/05 - Kanto Trio (Pink).bin
|
||||
/dlc/IRAO/ZUKAN/06 - Kanto Trio (Red).bin
|
||||
/dlc/IRAO/ZUKAN/07 - Hugh.bin
|
||||
/dlc/IRAO/ZUKAN/08 - Bianca.bin
|
||||
/dlc/IRAO/ZUKAN/09 - Cheren.bin
|
||||
|
||||
BIN
src/main/resources/dlc/IRAO/CGEAR/05 - PokeSma Purrloin.bin
Normal file
BIN
src/main/resources/dlc/IRAO/CGEAR/05 - PokeSma Purrloin.bin
Normal file
Binary file not shown.
BIN
src/main/resources/dlc/IRAO/CGEAR/14 - Pokemon Cafe.bin
Normal file
BIN
src/main/resources/dlc/IRAO/CGEAR/14 - Pokemon Cafe.bin
Normal file
Binary file not shown.
BIN
src/main/resources/dlc/IRAO/CGEAR/25 - Ducklett Friends.bin
Normal file
BIN
src/main/resources/dlc/IRAO/CGEAR/25 - Ducklett Friends.bin
Normal file
Binary file not shown.
BIN
src/main/resources/dlc/IRAO/CGEAR/26 - Guidance Cobalion.bin
Normal file
BIN
src/main/resources/dlc/IRAO/CGEAR/26 - Guidance Cobalion.bin
Normal file
Binary file not shown.
BIN
src/main/resources/dlc/IRAO/CGEAR/27 - Trial Terrakion.bin
Normal file
BIN
src/main/resources/dlc/IRAO/CGEAR/27 - Trial Terrakion.bin
Normal file
Binary file not shown.
BIN
src/main/resources/dlc/IRAO/CGEAR/28 - Rumination Virizion.bin
Normal file
BIN
src/main/resources/dlc/IRAO/CGEAR/28 - Rumination Virizion.bin
Normal file
Binary file not shown.
BIN
src/main/resources/dlc/IRAO/CGEAR/32 - Red Genesect.bin
Normal file
BIN
src/main/resources/dlc/IRAO/CGEAR/32 - Red Genesect.bin
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
src/main/resources/dlc/IRAO/CGEAR2/10 - Sleeping Eevee.bin
Normal file
BIN
src/main/resources/dlc/IRAO/CGEAR2/10 - Sleeping Eevee.bin
Normal file
Binary file not shown.
BIN
src/main/resources/dlc/IRAO/CGEAR2/11 - Venusaur!.bin
Normal file
BIN
src/main/resources/dlc/IRAO/CGEAR2/11 - Venusaur!.bin
Normal file
Binary file not shown.
BIN
src/main/resources/dlc/IRAO/CGEAR2/12 - Charizard!.bin
Normal file
BIN
src/main/resources/dlc/IRAO/CGEAR2/12 - Charizard!.bin
Normal file
Binary file not shown.
BIN
src/main/resources/dlc/IRAO/CGEAR2/13 - Blastoise!.bin
Normal file
BIN
src/main/resources/dlc/IRAO/CGEAR2/13 - Blastoise!.bin
Normal file
Binary file not shown.
BIN
src/main/resources/dlc/IRAO/CGEAR2/14 - Red Genesect.bin
Normal file
BIN
src/main/resources/dlc/IRAO/CGEAR2/14 - Red Genesect.bin
Normal file
Binary file not shown.
BIN
src/main/resources/dlc/IRAO/ZUKAN/05 - Kanto Trio (Pink).bin
Normal file
BIN
src/main/resources/dlc/IRAO/ZUKAN/05 - Kanto Trio (Pink).bin
Normal file
Binary file not shown.
BIN
src/main/resources/dlc/IRAO/ZUKAN/06 - Kanto Trio (Red).bin
Normal file
BIN
src/main/resources/dlc/IRAO/ZUKAN/06 - Kanto Trio (Red).bin
Normal file
Binary file not shown.
Reference in New Issue
Block a user