Enhance sprite-picker
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="icon" href="/favicon.ico">
|
<link rel="icon" href="/favicon.ico">
|
||||||
|
<link rel="stylesheet" type="text/css" href="https://alttpr.com/css/app.css">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Vite App</title>
|
<title>Vite App</title>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
12
package-lock.json
generated
12
package-lock.json
generated
@@ -18,7 +18,8 @@
|
|||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"mustache": "^4.2.0",
|
"mustache": "^4.2.0",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.5.0"
|
"vue-router": "^4.5.0",
|
||||||
|
"vue-select": "^4.0.0-beta.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@modyfi/vite-plugin-yaml": "^1.1.1",
|
"@modyfi/vite-plugin-yaml": "^1.1.1",
|
||||||
@@ -3143,6 +3144,15 @@
|
|||||||
"vue": "^3.2.0"
|
"vue": "^3.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-select": {
|
||||||
|
"version": "4.0.0-beta.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-select/-/vue-select-4.0.0-beta.6.tgz",
|
||||||
|
"integrity": "sha512-K+zrNBSpwMPhAxYLTCl56gaMrWZGgayoWCLqe5rWwkB8aUbAUh7u6sXjIR7v4ckp2WKC7zEEUY27g6h1MRsIHw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "3.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"mustache": "^4.2.0",
|
"mustache": "^4.2.0",
|
||||||
"vue": "^3.5.13",
|
"vue": "^3.5.13",
|
||||||
"vue-router": "^4.5.0"
|
"vue-router": "^4.5.0",
|
||||||
|
"vue-select": "^4.0.0-beta.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@modyfi/vite-plugin-yaml": "^1.1.1",
|
"@modyfi/vite-plugin-yaml": "^1.1.1",
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ export default class ZSPR {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_parse() {
|
_parse() {
|
||||||
this.gfxOffset = (this.patch[12] << 24) | (this.patch[11] << 16) | (this.patch[10] << 8) | this.patch[9];
|
this.gfxOffset = ((this.patch[12] << 24) | (this.patch[11] << 16) | (this.patch[10] << 8) | this.patch[9]) >>> 0;
|
||||||
this.palOffset = (this.patch[18] << 24) | (this.patch[17] << 16) | (this.patch[16] << 8) | this.patch[15];
|
this.palOffset = ((this.patch[18] << 24) | (this.patch[17] << 16) | (this.patch[16] << 8) | this.patch[15]) >>> 0;
|
||||||
const metadataOffset = 0x1D;
|
const metadataOffset = 0x1D;
|
||||||
const dec8 = new TextDecoder("utf-8");
|
const dec8 = new TextDecoder("utf-8");
|
||||||
const dec16 = new TextDecoder("utf-16");
|
const dec16 = new TextDecoder("utf-16");
|
||||||
|
|||||||
@@ -26,3 +26,13 @@
|
|||||||
--bs-btn-color: #880;
|
--bs-btn-color: #880;
|
||||||
--bs-btn-active-color: #550;
|
--bs-btn-active-color: #550;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sprite-preview-icon {
|
||||||
|
display: inline;
|
||||||
|
float: left;
|
||||||
|
padding-right: 0.7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sprite-preview-name {
|
||||||
|
padding-left: 0.7em;
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ export default defineComponent({
|
|||||||
rom_checksum: "3322EFFC",
|
rom_checksum: "3322EFFC",
|
||||||
baserom: null,
|
baserom: null,
|
||||||
baserom_error: null,
|
baserom_error: null,
|
||||||
sprite: null,
|
|
||||||
patch: null,
|
patch: null,
|
||||||
error: null,
|
error: null,
|
||||||
settings: null,
|
settings: null,
|
||||||
@@ -54,7 +53,6 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.fetchSeed();
|
await this.fetchSeed();
|
||||||
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
permalink() {
|
permalink() {
|
||||||
@@ -121,15 +119,10 @@ export default defineComponent({
|
|||||||
}.bind(this);
|
}.bind(this);
|
||||||
reader.readAsArrayBuffer(file);
|
reader.readAsArrayBuffer(file);
|
||||||
},
|
},
|
||||||
spriteUpdate(sprite) {
|
|
||||||
this.sprite = sprite;
|
|
||||||
},
|
|
||||||
async patchRom() {
|
async patchRom() {
|
||||||
var rom = bps.apply(this.patch, this.baserom);
|
var rom = bps.apply(this.patch, this.baserom);
|
||||||
|
|
||||||
if (this.sprite) {
|
await this.$refs.spritepicker.patch(rom);
|
||||||
this.sprite.apply(rom);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$refs.heartbeep.patch(rom);
|
this.$refs.heartbeep.patch(rom);
|
||||||
this.$refs.heartcolor.patch(rom);
|
this.$refs.heartcolor.patch(rom);
|
||||||
@@ -187,7 +180,7 @@ export default defineComponent({
|
|||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<SpritePicker @spriteUpdate="spriteUpdate" />
|
<SpritePicker ref="spritepicker" />
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
|
|||||||
@@ -3,59 +3,160 @@ import { defineComponent } from 'vue';
|
|||||||
|
|
||||||
import ZSPR from "@/ZSPR.js";
|
import ZSPR from "@/ZSPR.js";
|
||||||
import localforage from "localforage";
|
import localforage from "localforage";
|
||||||
|
import officialSprites from "@/data/sprites.json";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
import vSelect from "vue-select";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
components: {
|
||||||
|
vSelect,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
sprite: null,
|
uploadedZspr: null,
|
||||||
sprite_error: null,
|
sprite_error: null,
|
||||||
|
spriteType: null,
|
||||||
|
officialSprite: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
const spriteType = await localforage.getItem("selected_sprite_type") ?? "default";
|
||||||
|
this.officialSprite = await localforage.getItem("selected_official_sprite");
|
||||||
|
const uploaded = await localforage.getItem("uploaded_sprite");
|
||||||
|
if (uploaded) {
|
||||||
|
const dataTransfer = new DataTransfer();
|
||||||
|
dataTransfer.items.add(uploaded);
|
||||||
|
document.getElementById("sprite-input").files = dataTransfer.files;
|
||||||
|
await this.uploadSprite(uploaded, false);
|
||||||
|
}
|
||||||
|
this.spriteType = spriteType;
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
async spriteType(newValue, oldValue) {
|
||||||
|
await localforage.setItem("selected_sprite_type", newValue);
|
||||||
|
},
|
||||||
|
async officialSprite(newValue, oldValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.spriteType = "official";
|
||||||
|
} else {
|
||||||
|
this.spriteType = "default";
|
||||||
|
}
|
||||||
|
await localforage.setItem("selected_official_sprite", JSON.parse(JSON.stringify(newValue)));
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
spriteList() {
|
||||||
|
return officialSprites;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
uploadSprite(file) {
|
async patch(rom) {
|
||||||
|
if (this.spriteType == "custom" && this.uploadedZspr != null) {
|
||||||
|
this.uploadedZspr.apply(rom);
|
||||||
|
} else if (this.spriteType == "official" && this.officialSprite
|
||||||
|
&& this.officialSprite.file) {
|
||||||
|
await axios.get(this.officialSprite.file, { responseType: "arraybuffer" })
|
||||||
|
.then(response => {
|
||||||
|
const buffer = new Uint8Array(response.data);
|
||||||
|
const zspr = new ZSPR(buffer);
|
||||||
|
|
||||||
|
if (!zspr.valid) {
|
||||||
|
throw "Downloaded sprite invalid";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
zspr.apply(rom);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
uploadSprite(file, updateType) {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
this.sprite_error = null;
|
this.sprite_error = null;
|
||||||
this.sprite = null;
|
this.uploadedZspr = null;
|
||||||
|
if (updateType) {
|
||||||
|
this.spriteType = "default";
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = function() {
|
reader.onload = async function() {
|
||||||
const buffer = new Uint8Array(reader.result);
|
const buffer = new Uint8Array(reader.result);
|
||||||
const zspr = new ZSPR(buffer);
|
const zspr = new ZSPR(buffer);
|
||||||
|
|
||||||
if (!zspr.valid) {
|
if (!zspr.valid) {
|
||||||
this.sprite_error = "Invalid sprite file";
|
this.sprite_error = "Invalid sprite file";
|
||||||
this.sprite = null;
|
this.uploadedZspr = null;
|
||||||
|
if (updateType) {
|
||||||
|
this.spriteType = "default";
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sprite_error = null;
|
this.sprite_error = null;
|
||||||
this.sprite = zspr;
|
this.uploadedZspr = zspr;
|
||||||
localforage.setItem("sprite", file);
|
if (updateType) {
|
||||||
this.$emit("spriteUpdate", this.sprite);
|
this.spriteType = "custom";
|
||||||
|
}
|
||||||
|
await localforage.setItem("uploaded_sprite", file);
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
reader.readAsArrayBuffer(file);
|
reader.readAsArrayBuffer(file);
|
||||||
},
|
},
|
||||||
}
|
getSpriteClass(name) {
|
||||||
|
return "icon-custom-" + name.replace(/\W/g, "");
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<label for="sprite-input" class="form-label">
|
<div class="input-group mt-2">
|
||||||
Custom Sprite:
|
<div class="input-group-text">
|
||||||
<template v-if="sprite">
|
<input name="spriteType" v-model="spriteType" value="default" class="mt-0" type="radio">
|
||||||
{{ sprite.spriteName }} by {{ sprite.spriteAuthor }}
|
</div>
|
||||||
</template>
|
<label class="input-group-text">
|
||||||
|
Default Link Sprite
|
||||||
</label>
|
</label>
|
||||||
<input id="sprite-input" class="form-control" type="file" accept=".zspr" @change="uploadSprite($event.target.files[0])" />
|
</div>
|
||||||
|
<div class="input-group mt-2">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<input name="spriteType" v-model="spriteType" value="official" class="mt-0" type="radio">
|
||||||
|
</div>
|
||||||
|
<label class="input-group-text" for="spriteSelector">
|
||||||
|
Sprite:
|
||||||
|
</label>
|
||||||
|
<vSelect label="name" :options="spriteList" class="form-control"
|
||||||
|
id="spriteSelector" v-model="officialSprite">
|
||||||
|
<template #option="option">
|
||||||
|
<div class="icon-custom sprite-preview-icon" :class="getSpriteClass(option.name)"></div>
|
||||||
|
<span class="sprite-preview-name">{{ option.name }}</span>
|
||||||
|
</template>
|
||||||
|
<template #selected-option="option">
|
||||||
|
<div class="icon-custom sprite-preview-icon" :class="getSpriteClass(option.name)"></div>
|
||||||
|
<span class="sprite-preview-name">{{ option.name }}</span>
|
||||||
|
</template>
|
||||||
|
</vSelect>
|
||||||
|
</div>
|
||||||
|
<div class="input-group mt-2">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<input name="spriteType" v-model="spriteType" value="custom" class="mt-0" type="radio">
|
||||||
|
</div>
|
||||||
|
<input id="sprite-input" class="form-control" type="file" accept=".zspr" @change="uploadSprite($event.target.files[0], true)" />
|
||||||
|
</div>
|
||||||
<div v-if="sprite_error" class="invalid">
|
<div v-if="sprite_error" class="invalid">
|
||||||
{{ sprite_error }}
|
{{ sprite_error }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#spriteSelector {
|
||||||
|
padding: 0;
|
||||||
|
--vs-border-style: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
7376
src/data/sprites.json
Normal file
7376
src/data/sprites.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@ import router from "./router";
|
|||||||
|
|
||||||
import "bootstrap/dist/css/bootstrap.min.css";
|
import "bootstrap/dist/css/bootstrap.min.css";
|
||||||
import "bootstrap-icons/font/bootstrap-icons.css";
|
import "bootstrap-icons/font/bootstrap-icons.css";
|
||||||
|
import "vue-select/dist/vue-select.css";
|
||||||
import "bootstrap";
|
import "bootstrap";
|
||||||
|
|
||||||
import "./assets/main.css";
|
import "./assets/main.css";
|
||||||
|
|||||||
Reference in New Issue
Block a user