diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/public/index.html b/index.html similarity index 56% rename from public/index.html rename to index.html index 8422651..9010969 100644 --- a/public/index.html +++ b/index.html @@ -1,17 +1,16 @@ - - + + - + - - - + + Witchery: Resurrected @@ -20,5 +19,7 @@
+ + diff --git a/package.json b/package.json index f822fa2..212203f 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { - "name": "witchery-resurrected-web", - "version": "0.1.0", + "name": "witchery-resurrected", + "version": "1.0.0", "description": "Witchery: Resurrected aims to recreate and improve the popular Witchery Minecraft mod in modern versions; with a focus on customizability.", "scripts": { - "dev": "react-scripts start", - "build": "react-scripts build" + "dev": "vite dev", + "build": "vite build" }, "repository": { "type": "git", @@ -29,14 +29,16 @@ }, "keywords": [], "dependencies": { - "@material-ui/core": "^4.12.4", - "@material-ui/icons": "^4.11.3", + "react": "^18.0.0", + "react-dom": "^18.0.0", + "react-router-dom": "^6.3.0" + }, + "devDependencies": { + "@vitejs/plugin-react": "^2.2.0", + "autoprefixer": "^10.4.13", "eslint": "^8.14.0", "eslint-config-react-app": "^7.0.1", "eslint-config-standard": "^17.0.0", - "react": "^18.0.0", - "react-dom": "^18.0.0", - "react-router-dom": "^6.3.0", - "react-scripts": "^5.0.1" + "vite": "^3.2.3" } } diff --git a/public/manifest.json b/public/manifest.json index 0f19885..1abf841 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -20,6 +20,6 @@ ], "start_url": ".", "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" + "theme_color": "#7a2a91", + "background_color": "#26272b" } diff --git a/src/assets/TEMP/attuned_stone.png b/src/assets/TEMP/attuned_stone.png new file mode 100644 index 0000000..349e97e Binary files /dev/null and b/src/assets/TEMP/attuned_stone.png differ diff --git a/src/assets/TEMP/clay_jar.png b/src/assets/TEMP/clay_jar.png new file mode 100644 index 0000000..f857e94 Binary files /dev/null and b/src/assets/TEMP/clay_jar.png differ diff --git a/src/assets/TEMP/foul_fume.png b/src/assets/TEMP/foul_fume.png new file mode 100644 index 0000000..f97c5ef Binary files /dev/null and b/src/assets/TEMP/foul_fume.png differ diff --git a/src/assets/TEMP/goddess_breath.png b/src/assets/TEMP/goddess_breath.png new file mode 100644 index 0000000..da0feb3 Binary files /dev/null and b/src/assets/TEMP/goddess_breath.png differ diff --git a/src/assets/TEMP/goddess_tear.png b/src/assets/TEMP/goddess_tear.png new file mode 100644 index 0000000..94a6e0b Binary files /dev/null and b/src/assets/TEMP/goddess_tear.png differ diff --git a/src/assets/TEMP/magic_whiff.png b/src/assets/TEMP/magic_whiff.png new file mode 100644 index 0000000..3b13570 Binary files /dev/null and b/src/assets/TEMP/magic_whiff.png differ diff --git a/src/assets/fonts/Mojang-Regular.ttf b/src/assets/fonts/Mojang-Regular.ttf new file mode 100644 index 0000000..da89fa7 Binary files /dev/null and b/src/assets/fonts/Mojang-Regular.ttf differ diff --git a/src/assets/images/background.png b/src/assets/images/background.png index c3511fc..1d9228b 100644 Binary files a/src/assets/images/background.png and b/src/assets/images/background.png differ diff --git a/src/assets/images/book.png b/src/assets/images/book.png new file mode 100644 index 0000000..be094dc Binary files /dev/null and b/src/assets/images/book.png differ diff --git a/src/assets/images/book_back_arrow.png b/src/assets/images/book_back_arrow.png new file mode 100644 index 0000000..f58700b Binary files /dev/null and b/src/assets/images/book_back_arrow.png differ diff --git a/src/assets/images/crafting_guis/crafting_table.png b/src/assets/images/crafting_guis/crafting_table.png new file mode 100644 index 0000000..c130492 Binary files /dev/null and b/src/assets/images/crafting_guis/crafting_table.png differ diff --git a/src/assets/images/crafting_guis/distillery.png b/src/assets/images/crafting_guis/distillery.png new file mode 100644 index 0000000..d7063c2 Binary files /dev/null and b/src/assets/images/crafting_guis/distillery.png differ diff --git a/src/assets/images/crafting_guis/furnace.png b/src/assets/images/crafting_guis/furnace.png new file mode 100644 index 0000000..b1f925c Binary files /dev/null and b/src/assets/images/crafting_guis/furnace.png differ diff --git a/src/assets/images/crafting_guis/kettle.png b/src/assets/images/crafting_guis/kettle.png new file mode 100644 index 0000000..09a7f57 Binary files /dev/null and b/src/assets/images/crafting_guis/kettle.png differ diff --git a/src/assets/images/crafting_guis/progress_arrow.png b/src/assets/images/crafting_guis/progress_arrow.png new file mode 100644 index 0000000..ea80587 Binary files /dev/null and b/src/assets/images/crafting_guis/progress_arrow.png differ diff --git a/src/assets/images/crafting_guis/progress_bubbles.png b/src/assets/images/crafting_guis/progress_bubbles.png new file mode 100644 index 0000000..f863c21 Binary files /dev/null and b/src/assets/images/crafting_guis/progress_bubbles.png differ diff --git a/src/assets/images/crafting_guis/progress_double_arrow.png b/src/assets/images/crafting_guis/progress_double_arrow.png new file mode 100644 index 0000000..706fcc3 Binary files /dev/null and b/src/assets/images/crafting_guis/progress_double_arrow.png differ diff --git a/src/assets/images/crafting_guis/progress_spinning_arrow.png b/src/assets/images/crafting_guis/progress_spinning_arrow.png new file mode 100644 index 0000000..9d67e21 Binary files /dev/null and b/src/assets/images/crafting_guis/progress_spinning_arrow.png differ diff --git a/src/assets/images/crafting_guis/progress_triple_arrow.png b/src/assets/images/crafting_guis/progress_triple_arrow.png new file mode 100644 index 0000000..8df3195 Binary files /dev/null and b/src/assets/images/crafting_guis/progress_triple_arrow.png differ diff --git a/src/assets/images/crafting_guis/smelting_flames.png b/src/assets/images/crafting_guis/smelting_flames.png new file mode 100644 index 0000000..cb5ba5b Binary files /dev/null and b/src/assets/images/crafting_guis/smelting_flames.png differ diff --git a/src/assets/images/crafting_guis/spinning_wheel.png b/src/assets/images/crafting_guis/spinning_wheel.png new file mode 100644 index 0000000..def5b61 Binary files /dev/null and b/src/assets/images/crafting_guis/spinning_wheel.png differ diff --git a/src/assets/images/crafting_guis/witches_oven.png b/src/assets/images/crafting_guis/witches_oven.png new file mode 100644 index 0000000..29146fb Binary files /dev/null and b/src/assets/images/crafting_guis/witches_oven.png differ diff --git a/src/assets/images/paper_overlay.png b/src/assets/images/paper_overlay.png new file mode 100644 index 0000000..57f1fc9 Binary files /dev/null and b/src/assets/images/paper_overlay.png differ diff --git a/src/client/AdminPanel.jsx b/src/client/AdminPanel.jsx index 9042a4a..0b464b7 100644 --- a/src/client/AdminPanel.jsx +++ b/src/client/AdminPanel.jsx @@ -80,14 +80,28 @@ class File extends React.Component { } class AdminPanel extends React.Component { + static authURL = 'https://discord.com/api/oauth2/authorize?client_id=665314456745541653&response_type=code&scope=identify&redirect_uri=' + + encodeURIComponent(window.location.origin + '/auth') + + static cancel (e) { + if (e) e.preventDefault() + + window.location.reload() + } + state = { - files: [] + files: [], + authToken: null } componentDidMount () { return this.addFile() } + componentWillUnmount () { + clearInterval(this.authCheckInterval) + } + render () { return (
@@ -104,11 +118,18 @@ class AdminPanel extends React.Component {
-
- - + - +
+ + + +
+ {this.state.authToken ? 'Authorized' : 'Authorize'} +
@@ -144,10 +165,30 @@ class AdminPanel extends React.Component { }) } - cancel (e) { - if (e) e.preventDefault() + openAuthWindow () { + window.open(AdminPanel.authURL, 'popup', 'width=500,height=800,scrollbars=no,resizable=no,noreferrer') - window.location.reload() + if (!this.authCheckInterval) { + this.authCheckInterval = setInterval(() => { + if ('admin_auth' in localStorage) { + this.setState({ + authToken: localStorage.getItem('admin_auth') + }) + + localStorage.removeItem('admin_auth') + + clearInterval(this.authCheckInterval) + } + }) + } + } + + checkAuth (e) { + if (!this.state.authToken) { + alert('Remember to authorize!') + + e.preventDefault() + } } } diff --git a/src/client/Auth.jsx b/src/client/Auth.jsx new file mode 100644 index 0000000..2438a25 --- /dev/null +++ b/src/client/Auth.jsx @@ -0,0 +1,15 @@ +import React from 'react' + +class Auth extends React.Component { + componentDidMount () { + localStorage.setItem('admin_auth', new URLSearchParams(window.location.search).get('code')) + + window.close() + } + + render () { + return (<>) + } +} + +export default Auth diff --git a/src/client/Compendium.jsx b/src/client/Compendium.jsx new file mode 100644 index 0000000..ea11b9e --- /dev/null +++ b/src/client/Compendium.jsx @@ -0,0 +1,108 @@ +import React from 'react' +import { + Link +} from 'react-router-dom' + +import backArrow from '../assets/images/book_back_arrow.png' + +import './styles/Compendium.css' + +// TEMP +const categories = [ + 'infusions', + 'afflictions' +] +// TEMP + +class Compendium extends React.Component { + static capitalizationRegex = /(?:^|\s)(.)/g + static hashRegex = /#(?[^/]+)(?:\/(?.+))?/ + + state = { + categories, // TEMP + sections: { + infusions: [ + { + id: 'light', + name: 'Light', + icon: null, + description: 'THIS IS THE LIGHT INFUSION' + } + ], + afflictions: [ + { + id: 'vampirism', + name: 'Vampirism', + icon: null, + description: 'THIS IS VAMPIRISIM', + forms: [ + { + id: 'bat', + name: 'Bat', + stats: { + health: '5' + }, + description: 'THIS IS THE BAT TRANSFORMATION' + } + ] + } + ] + } // TEMP + } + + constructor (props) { + super(props) + + this.title = props.title + ' - Compendium' + document.title = this.title + } + + componentDidMount () { // TODO: GET SECTIONS + const location = window.location.hash.match(Compendium.hashRegex) + + if (location) this.switchLocation(location.groups.category, location.groups.id) + } + + render () { + return ( +
+
+
+
    + {this.state.categories.map((c, i) => +
  • + {c.replace(Compendium.capitalizationRegex, (l) => l.toUpperCase())} +
  • )} +
+ + back +
+ +
+ BRUH MOMENT +
+
+
+ ) + } + + switchLocation (category, section) { + if (!(category in this.state.sections)) return + + window.location.hash = category + this.setState({ + category + }) + + const data = this.state.sections[category].find((s) => s.id === section) + if (!data) return + + window.location += '/' + section + document.title = this.title + ` [${data.name}]` + this.setState({ + section + }) + } +} + +export default Compendium diff --git a/src/client/Glossary.jsx b/src/client/Glossary.jsx new file mode 100644 index 0000000..017239c --- /dev/null +++ b/src/client/Glossary.jsx @@ -0,0 +1,487 @@ +import React from 'react' +import { + Link +} from 'react-router-dom' + +import { + CraftingTable, + Furnace, + Kettle, + WitchesOven, + Distillery, + SpinningWheel +} from './modules/CraftingGrids.jsx' + +import postFetch from './util/postFetch.js' + +import './styles/Glossary.css' + +// TEMP +import attunedIcon from '../assets/TEMP/attuned_stone.png' +import whiffIcon from '../assets/TEMP/magic_whiff.png' +import foulFumeIcon from '../assets/TEMP/foul_fume.png' +import jarIcon from '../assets/TEMP/clay_jar.png' +import goddessTearIcon from '../assets/TEMP/goddess_tear.png' +import goddessBreathIcon from '../assets/TEMP/goddess_breath.png' + +const categories = [ + 'items', + 'blocks', + 'mobs', + 'brew effects', + 'rites', + 'spells' +] + +const entries = { // TEMP + items: [ + { + id: 'witchery:attuned_stone', + name: 'Attuned Stone', + iconURL: attunedIcon, + description: 'The Attuned Stone is an item from the Witchery mod. This item is used in the creation of various items and machines, such as the Chalice, Poppet Shelf, Distillery, Kettle, and the Candelabra. Additionally, it can be used as a portable power source for circle magic, when a nearby Altar is not available. They must first be charged with the Rite of Charging.' + }, + { + id: 'witchery:magic_whiff', + name: 'Whiff of Magic', + iconURL: whiffIcon + }, + { + id: 'witchery:test_food', + name: 'Test Food', + iconURL: 'https://minecraftitemids.com/item/32/cooked_porkchop.png', + description: 'Wow, this item has multiple recipes. Let\'s see how it looks with a super long description. I wonder how it will turn out. It might turn out great since I am so good at CSS and edge cases. Who knows? Maybe it will look wonderful, in fact. Perhaps, and I say this tentatively, it will look superb. I guess we will just have to see after I save this file whence I am done typing.\nSo, it turns out, after saving this file, I actually did not type enough. I underestimated the amount of space I would have for so many descriptive characters of this item on its blowup page. It truly is a shame that my spatial estimative skills are lacking. Oh well, I guess I will have to continue honing this skill in the near future when I am in college and need to meet and fulfill various quotas in my assignments. Maybe I\'ll write a dissertation one day. Who knows?' + }, + { + id: 'witchery:test_brew', + name: 'Test Brew', + iconURL: 'https://minecraftitemids.com/item/32/438-0.png' + }, + { + id: 'witchery:gold_thread', + name: 'Gold Thread', + iconURL: 'https://minecraftitemids.com/item/32/string.png' + }, + { + id: 'witchery:foul_fume', + name: 'Foul Fume', + iconURL: foulFumeIcon + }, + { + id: 'witchery:clay_jar', + name: 'Clay Jar', + iconURL: jarIcon + }, + { + id: 'witchery:goddess_tear', + name: 'Tear of the Goddess', + iconURL: goddessTearIcon + }, + { + id: 'witchery:goddess_breath', + name: 'Breath of the Goddess', + iconURL: goddessBreathIcon + } + ]/* .concat(new Array(30).fill({ + id: 'witchery:attuned_stone', + name: 'Attuned Stone', + iconURL: attunedIcon, + description: 'The Attuned Stone is an item from the Witchery mod. This item is used in the creation of various items and machines, such as the Chalice, Poppet Shelf, Distillery, Kettle, and the Candelabra. Additionally, it can be used as a portable power source for circle magic, when a nearby Altar is not available. They must first be charged with the Rite of Charging.' + })) */ +} + +const recipes = [ + { + type: 'crafting_table', + shaped: true, + products: [ + { + id: 'witchery:attuned_stone', + count: 1 + } + ], + ingredients: [ + { + id: 'witchery:magic_whiff', + count: 1 + }, null, null, + { + id: 'minecraft:diamond', + count: 1 + }, null, null, + { + id: 'minecraft:lava_bucket', + count: 1 + }, null, null + ] + }, + { + type: 'furnace', + products: [ + { + id: 'witchery:test_food', + count: 1 + } + ], + ingredients: [ + { + id: 'minecraft:porkchop', + count: 1 + } + ] + }, + { + type: 'crafting_table', + shaped: true, + products: [ + { + id: 'witchery:test_food', + count: 1 + } + ], + ingredients: [ + { + id: 'minecraft:porkchop', + count: 4 + } + ] + }, + { + type: 'kettle', + products: [ + { + id: 'witchery:test_brew', + count: 1 + } + ], + ingredients: [ + { + id: 'minecraft:oak_sapling', + count: 1 + }, + { + id: 'minecraft:bedrock', + count: 1 + }, + { + id: 'minecraft:budding_amethyst', + count: 1 + }, + { + id: 'minecraft:deepslate_gold_ore', + count: 1 + }, + { + id: 'minecraft:tube_coral_fan', + count: 1 + }, + { + id: 'minecraft:iron_pickaxe', + count: 1 + } + ] + }, + { + type: 'spinning_wheel', + products: [ + { + id: 'witchery:gold_thread', + count: 3 + } + ], + ingredients: [ + { + id: 'minecraft:hay_bale', + count: 1 + }, + { + id: 'witchery:magic_whiff', + count: 2 + }, + { + id: 'invalid', + count: 1 + } + ] + }, + { + type: 'witches_oven', + products: [ + { + id: 'minecraft:coal', + count: 1 + }, + { + id: 'witchery:foul_fume', + uncertain: true + } + ], + ingredients: [ + { + id: 'minecraft:oak_wood' + }, + { + id: 'witchery:clay_jar', + count: 1 + } + ] + }, + { + type: 'distillery', + products: [ + { + id: 'witchery:goddess_tear' + }, + { + id: 'witchery:goddess_breath' + }, + { + id: 'minecraft:slimeball' + }, + { + id: 'witchery:foul_fume' + } + ], + ingredients: [ + { + id: 'witchery:goddess_breath' + }, + { + id: 'minecraft:lapis_lazuli' + }, + { + id: 'witchery:clay_jar', + count: 3 + } + ] + } +] +// TEMP + +// TODO: FETCH ENTRIES FROM CATEGORY ON LOAD. IF CATEGORY DOESN'T EXIST, DEFAULT TO ITEMS + +class Glossary extends React.Component { + static grids = { + crafting_table: CraftingTable, + furnace: Furnace, + kettle: Kettle, + witches_oven: WitchesOven, + distillery: Distillery, + spinning_wheel: SpinningWheel + } + + static capitalizationRegex = /(?:^|\s)(.)/g + static hashRegex = /#(?[^/]+)(?:\/(?.+))?/ + static idRegex = /^(.+):(.+)$/ + + state = { + entries, // TEMP, + recipes, // TEMP + category: categories[0], // NOTE: AUTOMATICALLY SET CATEGORY TO FIRST CATEGORY WHEN FETCHED + search: '', + blowup: null, + itemCache: {} + } + + constructor (props) { + super(props) + + this.title = props.title + ' - Glossary' + document.title = this.title + } + + componentDidMount () { // TODO: GET ENTRIES + const location = window.location.hash.match(Glossary.hashRegex) + + if (location) this.switchLocation(location.groups.category, location.groups.id) + } + + render () { + return ( +
+
+
+
+ {categories.map((c) => +
this.switchLocation(c)}> + {c.replace(Glossary.capitalizationRegex, (l) => l.toUpperCase())} +
+ )} +
+ + this.setState({ search: e.target.value })} + /> + +
+ {this.state.category in this.state.entries + ? this.state.entries[this.state.category] + .filter((e) => e.name.toLowerCase().replace(/\s/g, '').includes(this.state.search.toLowerCase().replace(/\s/g, ''))) + .map((e) => ( +
this.switchLocation(this.state.category, e.id)}> +
+ {e.name} +
+ + {e.name} +
+ )) + : null} +
+ + Compendium {/* Onomasticon */} +
+ +
+ {this.state.blowup + ? ( +
+
+
+
+ {this.state.blowup.name} + {this.state.blowup.id} +
+ +
+ {this.state.blowup.name} +
+
+ +
+
+ {this.state.blowup?.description?.split('\n').map((p, i) =>

{p}

)} +
+ + {this.state.blowup.recipes?.length + ? ( +
+ {this.state.blowup.recipes.map((r, i) => this.getGrid(r, this.state.blowup.id, i))} +
+ ) + : null} +
+
+
+ ) + : null} +
+
+
+ ) + } + + switchLocation (category, entry) { + if (!(category in this.state.entries)) return + + window.location.hash = category + this.setState({ + category + }) + + const data = this.state.entries[category].find((e) => e.id === entry) + if (!data) return + + window.location += '/' + entry + document.title = this.title + ` [${data.name}]` + this.setState({ + blowup: data + }) + + // TODO: GET RECIPES + const recipes = this.state.recipes.filter((r) => r.products.find((p) => p.id === entry)) + + if (recipes.length) { + this.setState({ + blowup: { + ...data, + recipes + } + }) + + const requests = [] + + for (const recipe of recipes) { + for (const slot of recipe.ingredients.concat(recipe.products)) { + if (!slot || this.state.itemCache[slot.id]) continue + + if (slot.id.match(Glossary.idRegex)?.[1] === 'minecraft') { + requests.push(fetch('https://api.minecraftitemids.com/v1/search', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + query: slot.id.match(Glossary.idRegex)[2] + }) + }) + .then(postFetch) + .then((entry) => entry.json()) + .then(({ data: [entry] }) => [slot.id, entry])) + } + } + } + + return Promise.all(requests) + .then((entries) => { + const addition = {} + + for (const [id, entry] of entries) { + if (entry) { + addition[id] = { + name: entry.displayName, + iconURL: `https://minecraftitemids.com/item/64/${entry.name}.png` + } + } + } + + this.setState({ + itemCache: { + ...this.state.itemCache, + ...addition + } + }) + }) + .catch(this.props.onError) + } + } + + getGrid (recipe, viewing, key) { + const Grid = Glossary.grids[recipe.type] + + return Grid + ? + : <Crafting method missing from glossary> + } + + getItem (id) { + if (this.state.itemCache[id]) { + return { + modded: false, + item: this.state.itemCache[id] + } + } else { + for (const category in this.state.entries) { + const entry = this.state.entries[category].find((i) => i.id === id) + + if (entry) { + return { + modded: true, + item: entry, + category + } + } + } + } + + return {} + } +} + +export default Glossary diff --git a/src/client/Home.jsx b/src/client/Home.jsx index 884542d..f2a8e11 100644 --- a/src/client/Home.jsx +++ b/src/client/Home.jsx @@ -7,11 +7,11 @@ import postFetch from './util/postFetch.js' import logo from '../assets/images/logo.png' import backvid from '../assets/videos/background.webm' -import ritualExample from '../assets/videos/examples/rituals.webm' -import infusionExample from '../assets/videos/examples/infusions.webm' -import symbolExample from '../assets/videos/examples/symbology.webm' -import brewingExample from '../assets/videos/examples/brewing.webm' -import transformExample from '../assets/videos/examples/transformations.webm' +// import ritualExample from '../assets/videos/examples/rituals.webm' +// import infusionExample from '../assets/videos/examples/infusions.webm' +// import symbolExample from '../assets/videos/examples/symbology.webm' +// import brewingExample from '../assets/videos/examples/brewing.webm' +// import transformExample from '../assets/videos/examples/transformations.webm' import batBauble from '../assets/images/bat_bauble.png' import potionBauble from '../assets/images/potion_bauble.png' @@ -23,12 +23,21 @@ import './styles/Home.css' class Home extends React.Component { static baubleShiftRate = 2 + static shiftBaubles (event) { + const container = document.getElementById('bauble-container') + + const xOffset = (event.clientX - (window.innerWidth / 2)) / window.innerWidth + const yOffset = (event.clientY - (window.innerHeight / 2)) / window.innerHeight + + container.style.transform = `translate(calc(-50% + ${xOffset * Home.baubleShiftRate}px), calc(-50% + ${yOffset * Home.baubleShiftRate}px))` + } + constructor (props) { super(props) document.title = props.title + ' - Home' - if (!('ontouchstart' in window)) window.addEventListener('mousemove', this.shiftBaubles) + if (!('ontouchstart' in window)) window.addEventListener('mousemove', Home.shiftBaubles) } componentDidMount () { @@ -44,7 +53,7 @@ class Home extends React.Component { } componentWillUnmount () { - window.removeEventListener('mousemove', this.shiftBaubles) + window.removeEventListener('mousemove', Home.shiftBaubles) } render () { @@ -143,7 +152,7 @@ class Home extends React.Component {

@@ -159,7 +168,7 @@ class Home extends React.Component {

@@ -175,7 +184,7 @@ class Home extends React.Component {

@@ -191,7 +200,7 @@ class Home extends React.Component {

@@ -207,7 +216,7 @@ class Home extends React.Component {

@@ -250,15 +259,6 @@ class Home extends React.Component { ) } - - shiftBaubles = (event) => { - const container = document.getElementById('bauble-container') - - const xOffset = (event.clientX - (window.innerWidth / 2)) / window.innerWidth - const yOffset = (event.clientY - (window.innerHeight / 2)) / window.innerHeight - - container.style.transform = `translate(calc(-50% + ${xOffset * Home.baubleShiftRate}px), calc(-50% + ${yOffset * Home.baubleShiftRate}px))` - } } export default Home diff --git a/src/client/modules/CraftingGrids.jsx b/src/client/modules/CraftingGrids.jsx new file mode 100644 index 0000000..bf8295f --- /dev/null +++ b/src/client/modules/CraftingGrids.jsx @@ -0,0 +1,291 @@ +import React from 'react' + +import smelingFlames from '../../assets/images/crafting_guis/smelting_flames.png' +import progressArrow from '../../assets/images/crafting_guis/progress_arrow.png' +import progressBubbles from '../../assets/images/crafting_guis/progress_bubbles.png' +import progressDoubleArrow from '../../assets/images/crafting_guis/progress_double_arrow.png' +import progressTripleArrow from '../../assets/images/crafting_guis/progress_triple_arrow.png' +import progressSpinningArrow from '../../assets/images/crafting_guis/progress_spinning_arrow.png' + +import '../styles/CraftingGrids.css' + +class Slot extends React.Component { + render () { + const { + modded, + item, + category + } = this.props.getItem(this.props.data?.id) + + if (item) { + return ( +
this.props.switchLocation(category, item.id) : null}> + {this.props.data?.id} + + {this.props.data?.count && this.props.data.count !== 1 + ? {this.props.data.count} + : null} + + {item.name} +
+ ) + } else if (this.props.data) { + return ( +
+ ? + + UNKNOWN ITEM +
+ ) + } else return
+ } +} + +class CraftingTable extends React.Component { + static shuffleInterval = 1000 + + grid = React.createRef() + + state = { + paused: false + } + + constructor (props) { + super(props) + + if (!props.recipe.shaped) this.interval = setInterval(this.shuffle.bind(this), CraftingTable.shuffleInterval) + } + + componentWillUnmount () { + clearInterval(this.interval) + } + + render () { + return ( +
+ {this.props.recipe.shaped + ? null + : } + +

Crafting Table

+ +
+ {this.props.recipe.ingredients.map((s, i) => )} +
+ +
+ {this.props.recipe.products.map((p, i) => + + )} +
+
+ ) + } + + shuffle () { + if (!this.state.paused) { + for (let s = this.grid.current.children.length; s >= 0; s--) this.grid.current.appendChild(this.grid.current.children[Math.floor(Math.random() * s)]) + } + } +} + +class Furnace extends React.Component { + render () { + return ( +
+

Furnace

+ +
+ {this.props.recipe.ingredients.map((s, i) => )} +
+ +
+ {this.props.recipe.products.map((p, i) => + + )} +
+ +
+ flames + + progress +
+
+ ) + } +} + +class Kettle extends React.Component { + static shuffleInterval = 1000 + + grid = React.createRef() + + state = { + paused: false + } + + constructor (props) { + super(props) + + if (!props.recipe.shaped) this.interval = setInterval(this.shuffle.bind(this), CraftingTable.shuffleInterval) + } + + componentWillUnmount () { + clearInterval(this.interval) + } + + render () { + return ( +
+ {this.props.recipe.shaped + ? null + : } + +

Kettle

+ +
+ {this.props.recipe.products.map((p, i) => + + )} +
+ +
+ {this.props.recipe.ingredients.map((s, i) => )} +
+ +
+ progress +
+
+ ) + } + + shuffle () { + if (!this.state.paused) { + for (let s = this.grid.current.children.length; s >= 0; s--) this.grid.current.appendChild(this.grid.current.children[Math.floor(Math.random() * s)]) + } + } +} + +class WitchesOven extends React.Component { + render () { + return ( +
+

Witches' Oven

+ +
+ {this.props.recipe.ingredients.map((s, i) => )} +
+ +
+ {this.props.recipe.products.map((p, i) => + + )} +
+ +
+ flames + + progress +
+
+ ) + } +} + +class Distillery extends React.Component { + render () { + return ( +
+

Distillery

+ +
+ {this.props.recipe.ingredients.map((s, i) => )} +
+ +
+ {this.props.recipe.products.map((p, i) => + + )} +
+ +
+ progress + + progress +
+
+ ) + } +} + +class SpinningWheel extends React.Component { + static shuffleInterval = 1000 + + grid = React.createRef() + + state = { + paused: false + } + + constructor (props) { + super(props) + + if (!props.recipe.shaped) this.interval = setInterval(this.shuffle.bind(this), CraftingTable.shuffleInterval) + } + + componentWillUnmount () { + clearInterval(this.interval) + } + + render () { + return ( +
+ {this.props.recipe.shaped + ? null + : } + +

Spinning Wheel

+ +
+ {this.props.recipe.ingredients.map((s, i) => )} +
+ +
+ {this.props.recipe.products.map((p, i) => + + )} +
+ +
+ progress +
+
+ ) + } + + shuffle () { + if (!this.state.paused) { + for (let s = this.grid.current.children.length; s >= 1; s--) { + this.grid.current.appendChild(this.grid.current.children[Math.floor(Math.random() * (s - 1)) + 1]) + } + } + } +} + +export { + CraftingTable, + Furnace, + Kettle, + WitchesOven, + Distillery, + SpinningWheel +} diff --git a/src/client/modules/Error.jsx b/src/client/modules/Error.jsx index 89dfa18..496df23 100644 --- a/src/client/modules/Error.jsx +++ b/src/client/modules/Error.jsx @@ -30,6 +30,8 @@ class Error extends React.Component { update () { if (this.props.error instanceof TypeError) { + console.error(this.props.error) + this.setState({ error: { type: 'network', diff --git a/src/client/modules/Navbar.jsx b/src/client/modules/Navbar.jsx index 19cc6af..ac09a1f 100644 --- a/src/client/modules/Navbar.jsx +++ b/src/client/modules/Navbar.jsx @@ -8,24 +8,24 @@ import logo from '../../assets/images/logo.png' import '../styles/Navbar.css' class Navbar extends React.Component { - componentDidMount () { - if (!this.observer) { - const navbar = document.getElementById('navbar') + static observer = new IntersectionObserver( + ([e]) => e.target.classList.toggle('stuck', e.intersectionRatio < 1), + { threshold: [1] } + ) - const observer = new IntersectionObserver( - ([e]) => e.target.classList.toggle('stuck', e.intersectionRatio < 1), - { threshold: [1] } - ) + navbar = React.createRef() - observer.observe(navbar) + componentDidMount () { + Navbar.observer.observe(this.navbar.current) + } - this.observer = observer - } + componentWillUnmount () { + this.observer?.unobserve?.(this.navbar.current) } render () { return ( -