diff --git a/.gitignore b/.gitignore
index 270cc79ff..e2fdc4641 100644
--- a/.gitignore
+++ b/.gitignore
@@ -102,4 +102,5 @@ apps/expo-cityvote/.tamagui/css/*
# NuGetScracthroot
NuGetScratchroot/
-dotnet-install.sh
\ No newline at end of file
+dotnet-install.sh
+vite.config.*.timestamp*
\ No newline at end of file
diff --git a/apps/cryptothrone.com/astro.config.mjs b/apps/cryptothrone.com/astro.config.mjs
index 9922199b9..b14c53c90 100644
--- a/apps/cryptothrone.com/astro.config.mjs
+++ b/apps/cryptothrone.com/astro.config.mjs
@@ -9,6 +9,7 @@ import { fileURLToPath } from 'node:url';
// https://astro.build/config
export default defineConfig({
+ site: 'https://cryptothrone.com',
outDir: '../../dist/apps/cryptothrone.com',
integrations: [
react(),
diff --git a/apps/cryptothrone.com/project.json b/apps/cryptothrone.com/project.json
index bb26275df..2bf51c07a 100644
--- a/apps/cryptothrone.com/project.json
+++ b/apps/cryptothrone.com/project.json
@@ -5,11 +5,33 @@
"sourceRoot": "apps/cryptothrone.com/src",
"tags": [],
"targets": {
+ "dev": {
+ "executor": "nx:run-commands",
+ "options": {
+ "cwd": "apps/cryptothrone.com",
+ "commands": [
+ "UV_THREADPOOL_SIZE=4 NODE_OPTIONS=\"--max-old-space-size=4096\" nx exec -- astro dev"
+ ],
+ "parallel": false
+ }
+
+ },
"build": {
+ "executor": "nx:run-commands",
+ "options": {
+ "cwd": "apps/cryptothrone.com",
+ "commands": [
+ "UV_THREADPOOL_SIZE=4 NODE_OPTIONS=\"--max-old-space-size=4096\" nx exec -- astro build"
+ ],
+ "parallel": false
+ }
+
+ },
+ "buildx": {
"executor": "@nxtensions/astro:build",
"options": {}
},
- "dev": {
+ "devx": {
"executor": "@nxtensions/astro:dev",
"options": {}
},
diff --git a/apps/cryptothrone.com/src/components/game/menu/ModalDice.tsx b/apps/cryptothrone.com/src/components/game/menu/ModalDice.tsx
index 36b197ac7..6f64d940b 100644
--- a/apps/cryptothrone.com/src/components/game/menu/ModalDice.tsx
+++ b/apps/cryptothrone.com/src/components/game/menu/ModalDice.tsx
@@ -1,5 +1,5 @@
// DiceRollModal.tsx
-import React, { useState, useEffect, memo } from 'react';
+import React, { useState, useEffect, memo, useCallback } from 'react';
import { useStore } from '@nanostores/react';
import { playerStealDiceRoll } from './tempstore';
import { eventEmitterInstance as EventEmitter, notificationType, queryItemDB, type DiceRollResultEventData, type PlayerStealEventData } from '@kbve/laser';
@@ -48,23 +48,23 @@ const ModalDice: React.FC = () => {
let message = '';
switch (true) {
- case roll >= 24:
+ case roll == 24:
itemName = '01J27QABD2GPFNRVK69S51HSGB';
message = `You successfully stole a ${itemName}!`;
break;
- case roll >= 20:
+ case roll == 23:
itemName = '01J27QN2KZG1RDZW4CE9Q9Z3YQ';
message = `You successfully stole a ${itemName}!`;
break;
- case roll >= 18:
+ case roll == 19:
itemName = '01J269PK47V1DWX2S1251DEASD';
message = `You successfully stole a ${itemName}!`;
break;
- case roll >= 15:
+ case roll == 18:
itemName = 'Blue Shark';
message = `You successfully stole a ${itemName}!`;
break;
- case roll >= 12:
+ case roll == 17:
itemName = 'Salmon';
message = `You successfully stole a ${itemName}!`;
break;
@@ -101,17 +101,17 @@ const ModalDice: React.FC = () => {
}
};
- const handleClose = () => {
+ const handleClose = useCallback(() => {
updateDiceValues([]);
setDiceValues([]);
setCurrentRoll(null);
playerStealDiceRoll.set(null);
- };
+ }, []);
if (!_npc$) return null;
return (
-
+
@@ -125,7 +125,7 @@ const DiceRollMessage: React.FC<{ npcName: string, roll: number | null }> = ({ n
Steal Attempt
- Roll the dice to steal from {npcName}. You need a total of 12 or higher to succeed.
+ Roll the dice to steal from {npcName}. You need a total of 17 or higher to succeed.
-
-
+
-
@@ -105,16 +101,10 @@ let description = 'HB Dashboard Proof of Concept';
-
-
User Nav Bar Here!
-
-
-
-
diff --git a/apps/herbmail.com/src/pages/dice.astro b/apps/herbmail.com/src/pages/dice.astro
index 7d481402e..97296f250 100644
--- a/apps/herbmail.com/src/pages/dice.astro
+++ b/apps/herbmail.com/src/pages/dice.astro
@@ -1,6 +1,5 @@
---
import Layout from '../layouts/Layout.astro';
-import { AstroVe as VE } from '@kbve/astro-ve';
let title = 'Dice Panda';
let description = 'Dice Roller';
---
diff --git a/apps/herbmail.com/src/pages/index.astro b/apps/herbmail.com/src/pages/index.astro
index 72f5e8791..885cb2d89 100644
--- a/apps/herbmail.com/src/pages/index.astro
+++ b/apps/herbmail.com/src/pages/index.astro
@@ -1,14 +1,23 @@
---
import Layout from '../layouts/Layout.astro';
-import { AstroVe as VE } from '@kbve/astro-ve';
-
let title = 'Welcome to HerbMail.com';
let description =
'Discover dedicated email services for personal AI agents. Streamline communication and empower your AI with a tailored inbox. Join now!';
+
+import { Button } from '@kbve/shadcnui/button';
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from '@kbve/shadcnui/card';
---
-
+
@@ -22,219 +31,23 @@ let description =
src="/assets/json/lottie/mailer.lottie"
>
+
+
+
+
+
+ Card Title
+ Card Description
+
+
+
+
+
+
Card Footer
+
+
-
-
-
- Your personal AI Agent in your inbox. 🦆
-
-
- Send emails or messages to each of your agents,
- having them generate market research, code and more.
- ◈
-
- Wait list
- coming soon!
-
-
-
- Join Now
-
-
- Login
-
-
-
- Test
-
-
-
-
-
-
-
-
-
-
-
-
- Atlas
-
-
-
- We're not reinventing the wheel
-
-
- Unleash the limitless potential of AI agents in
- your digital realm! From automating mundane
- tasks to sparking innovative solutions, these
- intelligent assistants can reshape your daily
- routine. Dive into a world where your personal
- agent curates content, offers design insights,
- crafts compelling narratives, and even decodes
- intricate data. Step beyond the ordinary and
- reimagine what's possible with AI at your
- fingertips!
-
-
-
- Atlas
-
-
-
-
-
-
-
-
- Your agent on every platform!
-
-
- Seamlessly integrate your personal agent across
- all platforms!
-
-
- Message your agent on Discord! Ask it to code
- review on Github! Even email it your daily
- tasks!
-
-
-
- Discord.sh
-
-
- Discord.sh
-
-
-
-
-
-
-
-
- Plant Ideas.
-
-
- Register Now!
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Transforming communication, mastering tasks, and
- simplifying life.
-
-
Explore More! →
-
-
-
-
-
- Documentations →
-
-
Documents!
-
-
-
-
-
-
-
-
-
-
-
-
- Login
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/herbmail.com/tailwind.config.cjs b/apps/herbmail.com/tailwind.config.cjs
index e43a657d9..aa329a38a 100644
--- a/apps/herbmail.com/tailwind.config.cjs
+++ b/apps/herbmail.com/tailwind.config.cjs
@@ -2,7 +2,7 @@ const {
createGlobPatternsForDependencies,
} = require('@nxtensions/astro/tailwind');
const { join } = require('path');
-const { buildConfig } = require('../../packages/uti/src/tailwind.config'); // Adjust the relative path
+const { buildConfig } = require('../../packages/shadcnutils/src/tailwind.config');
/** @type {import('tailwindcss').Config} */
module.exports = buildConfig(__dirname, {
@@ -13,51 +13,4 @@ module.exports = buildConfig(__dirname, {
),
...createGlobPatternsForDependencies(__dirname),
],
- theme: {
- extend: {
- colors: {
- // Color extension is prefixed as KBVE to avoid class conflicts
- kbve: '#8C52FF',
-
- 'kbve-primary': '#48BB78',
- 'kbve-primary-light': '#48BB78',
-
- 'kbve-secondary': '#1c033c',
-
- 'kbve-menu-primary': '#27272A',
- 'kbve-svg-primary': '#91ffff',
- 'kbve-svg-primary-dyn': 'var(--color-kbve-svg-primary-dyn)',
- 'kbve-text-primary': '',
- 'kbve-text-primary-dyn': 'var(--color-kbve-text-primary-dyn)',
- 'kbve-text-secondary': '',
- 'kbve-text-secondary-dyn': 'var(--color-kbve-text-secondary-dyn)',
- },
- backgroundColor: {
- default: 'var(--color-background)',
- 'kbve-menu-bg': '#09090b',
- 'kbve-menu-bg-dyn': 'var(--color-kbve-menu-bg-dyn)',
-
- offset: '#23262d',
- },
-
- backgroundImage: (theme) => ({
- 'custom-gradient': `linear-gradient(to right, ${theme(
- 'colors.kbve'
- )}, ${theme('colors.kbve-primary')}, ${theme(
- 'colors.kbve-secondary'
- )})`,
- }),
-
- keyframes: {
- float: {
- '0%, 100%': { transform: 'translate3d(0, 0, 0)' },
- '50%': { transform: 'translate3d(0, 30px, 0)' },
- },
- 'ltr-linear-infinite': {
- '0%, 100%': { 'background-position': '0 0' },
- '50%': { 'background-position': '400% 0%' },
- },
- },
- },
- },
});
diff --git a/apps/herbmail.com/tsconfig.json b/apps/herbmail.com/tsconfig.json
index 0ea2f668d..3af0f3dfb 100644
--- a/apps/herbmail.com/tsconfig.json
+++ b/apps/herbmail.com/tsconfig.json
@@ -1,6 +1,7 @@
{
- "extends": "../../tsconfig.base.json",
+ "extends": "astro/tsconfigs/strict",
"compilerOptions": {
+ "baseUrl": ".",
// Enable top-level await, and other modern ESM features
"target": "ESNext",
"module": "ESNext",
@@ -26,7 +27,21 @@
// Until the majority of users are on TypeScript 5.0, we'll have to supress those deprecation errors
"ignoreDeprecations": "5.0",
// Allow JavaScript files to be imported
- "allowJs": true
+ "allowJs": true,
+
+ // Plugins 11-07-2024
+ "plugins": [
+ {
+ "name": "@astrojs/ts-plugin"
+ }
+ ],
+ "paths": {
+ "@kbve/astropad": ["../../packages/astropad/index.ts"],
+ "@kbve/astro-ve": ["../../packages/astro-ve/index.ts"],
+ "@kbve/laser": ["../../packages/laser/src/index.ts"],
+ "@kbve/shadcnui/*": ["../../packages/shadcnui/src/*"],
+ "@kbve/shadcnutils": ["../../packages/shadcnutils/src/index.ts"],
+ }
},
"include": ["src"]
}
diff --git a/apps/kbve.com/src/content/docs/itemdb/index.mdx b/apps/kbve.com/src/content/docs/itemdb/index.mdx
index d1163f654..b10fc30f2 100644
--- a/apps/kbve.com/src/content/docs/itemdb/index.mdx
+++ b/apps/kbve.com/src/content/docs/itemdb/index.mdx
@@ -4,7 +4,7 @@ description: |
An abstract item database for all our games!
The goal is to build the items for the various games through the MDX files, thus providing a way for users to see the most current in-game stats
sidebar:
- label: Food
+ label: ItemDB
order: 40
unsplash: 1590840960672-db24958eef9a
img: https://images.unsplash.com/photo-1590840960672-db24958eef9a?fit=crop&w=1400&h=700&q=75
diff --git a/apps/kbve.com/src/content/docs/shop/index.mdx b/apps/kbve.com/src/content/docs/shop/index.mdx
new file mode 100644
index 000000000..54f6a2106
--- /dev/null
+++ b/apps/kbve.com/src/content/docs/shop/index.mdx
@@ -0,0 +1,18 @@
+---
+title: Shop
+description: |
+ The greatest shop of all time?!
+ This will be the best possible shop yet, but all through markdown and more!
+sidebar:
+ label: Shop
+ order: 40
+unsplash: 1590840960672-db24958eef9a
+img: https://images.unsplash.com/photo-1590840960672-db24958eef9a?fit=crop&w=1400&h=700&q=75
+tags:
+ - shop
+ - services
+---
+
+## Shop
+
+More to come soon!
\ No newline at end of file
diff --git a/apps/kbve.com/src/content/journal/11-07.mdx b/apps/kbve.com/src/content/journal/11-07.mdx
index 828c12ae7..f0624974d 100644
--- a/apps/kbve.com/src/content/journal/11-07.mdx
+++ b/apps/kbve.com/src/content/journal/11-07.mdx
@@ -14,21 +14,43 @@ import { Adsense, Tasks } from '@kbve/astropad';
## 2024
-- 12:52AM
+- 12:52AM
- **Astro**
+ **Astro**
- We got the `rareicon.com` to build and run under the dev without any major issues.
- However there are some issues with the threejs when trying to run the kbve under the custom astro command.
- Finally, we got the base of the astro to build and now we can bring back the main pipeline!
- We had almost three full weeks of down time but we did get a good list of packages to clean up.
- I will make new issue tickets for the packages that we might have to remove.
-
+ We got the `rareicon.com` to build and run under the dev without any major issues.
+ However there are some issues with the threejs when trying to run the kbve under the custom astro command.
+ Finally, we got the base of the astro to build and now we can bring back the main pipeline!
+ We had almost three full weeks of down time but we did get a good list of packages to clean up.
+ I will make new issue tickets for the packages that we might have to remove.
-- 04:22AM
+- 04:22AM
- **TSLA**
+ **TSLA**
- Oh no! Almost the $300 mark!?
- We got two more days before those options expire, I am just hoping it does not cross the $300 mark and praying that we get a bit of room to extend out those calls.
-
\ No newline at end of file
+ Oh no! Almost the $300 mark!?
+ We got two more days before those options expire, I am just hoping it does not cross the $300 mark and praying that we get a bit of room to extend out those calls.
+
+- 05:07AM
+
+ **Herbmail**
+
+ We can keep it astro based but the template should be fixed up and replaced again.
+ Yet before we do that , we need to adjust the astro build process for it as well.
+ The changes will be the same as the other astro config changes we made earlier, so it should be quick.
+
+- 05:47PM
+
+ **Nx**
+
+ Updating the nx instance to version `v20.0.11` and now looping back around and making sure that everything else works.
+ There were some issues with the out-dated plugins but we can address them later this week.
+
+- 06:06PM
+
+ **ShadCN**
+
+ Time to get the herbmail frontpage converted over to a shadcn based template.
+ I wonder if we could do it all through chatgpt?
+ Wild that in almost 2025 and the astro icons is still broken and not working out of the box?
+ I am somewhat surprised that I might have to create and publish a working astro package for icons.
\ No newline at end of file
diff --git a/apps/kbve.com/src/content/journal/11-08.mdx b/apps/kbve.com/src/content/journal/11-08.mdx
new file mode 100644
index 000000000..82d1158df
--- /dev/null
+++ b/apps/kbve.com/src/content/journal/11-08.mdx
@@ -0,0 +1,33 @@
+---
+title: 'November: 8th'
+category: Daily
+date: 2024-11-08 12:00:00
+client: Self
+unsplash: 1721332154373-17e78d19b4a4
+img: https://images.unsplash.com/photo-1721332154373-17e78d19b4a4?crop=entropy&cs=srgb&fm=jpg&ixid=MnwzNjM5Nzd8MHwxfHJhbmRvbXx8fHx8fHx8fDE2ODE3NDg2ODY&ixlib=rb-4.0.3&q=85
+description: November 8th.
+tags:
+ - daily
+---
+
+import { Adsense, Tasks } from '@kbve/astropad';
+
+## 2024
+
+- 12:10PM
+
+ **TSLA**
+
+ The run up past $300 is a huge problem for me!
+ I am going to have to find a way to postpone the sell off of my 5 contracts, going to roll it into next week at 305 strike.
+ Hopefully the post trump era stock cools off next week but this is a dangerous rally, it can easily skip to $400 without me even looking.
+
+- 10:08PM
+
+ **CryptoThrone**
+
+ More updates to the crypto throne, including changing the tileset.
+ We might have to go with a new way of handling the tilesets, I think if we switch out of the static and go with a dynamic?
+ The map chunking is still broken and I am not too sure if I will be able to fix.
+ Maybe instead of having the chunking done during the loading state, I might have to pre-chunk them ahead of time.
+ Looking through the different examples, it seems that people use build tools to make a custom json chunkset of the map, which makes sense because javascript is not too powerful.
\ No newline at end of file
diff --git a/apps/kbve.com/src/content/journal/11-09.mdx b/apps/kbve.com/src/content/journal/11-09.mdx
new file mode 100644
index 000000000..b39002788
--- /dev/null
+++ b/apps/kbve.com/src/content/journal/11-09.mdx
@@ -0,0 +1,25 @@
+---
+title: 'November: 9th'
+category: Daily
+date: 2024-11-09 12:00:00
+client: Self
+unsplash: 1721332154373-17e78d19b4a4
+img: https://images.unsplash.com/photo-1721332154373-17e78d19b4a4?crop=entropy&cs=srgb&fm=jpg&ixid=MnwzNjM5Nzd8MHwxfHJhbmRvbXx8fHx8fHx8fDE2ODE3NDg2ODY&ixlib=rb-4.0.3&q=85
+description: November 9th.
+tags:
+ - daily
+---
+
+import { Adsense, Tasks } from '@kbve/astropad';
+
+## 2024
+
+- 7:30PM
+
+ **Unity**
+
+ Just finished a great 3 hour session in Unity!
+ I got a better understanding of the tilemaps, upgraded unity to LTS 6 and made my first sprite animation.
+ By next week, I want to create at least 3 or 5 new characters for the game.
+ Now I have to spend most of the day working on the different tilemaps.
+
diff --git a/apps/kbve.com/tailwind.config.cjs b/apps/kbve.com/tailwind.config.cjs
index c37aa0a4e..793ecc5d5 100644
--- a/apps/kbve.com/tailwind.config.cjs
+++ b/apps/kbve.com/tailwind.config.cjs
@@ -1,6 +1,6 @@
-// const {
-// createGlobPatternsForDependencies,
-// } = require('@nxtensions/astro/tailwind');
+const {
+ createGlobPatternsForDependencies,
+ } = require('@nxtensions/astro/tailwind');
const { join } = require('path');
/** @type {import('tailwindcss').Config} */
@@ -13,10 +13,8 @@ module.exports = {
'src/**/!(*.stories|*.spec).{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
),
- // ...createGlobPatternsForDependencies(__dirname),
+ ...createGlobPatternsForDependencies(__dirname),
- // Include the flowbite files from the node_modules directory higher up in the monorepo
- // join(__dirname, '../../node_modules/flowbite/**/*.js'),
join(__dirname, '../../node_modules/preline/dist/*.js'),
],
darkMode: 'class',
@@ -102,7 +100,6 @@ module.exports = {
require('@tailwindcss/typography'),
require('tailwindcss/nesting'),
require('preline/plugin'),
- //require('flowbite/plugin'),
require('@tailwindcss/forms'),
function ({ addUtilities }) {
const newUtilities = {
diff --git a/components.json b/components.json
index 3fffab1de..d7fbe45e4 100644
--- a/components.json
+++ b/components.json
@@ -3,13 +3,13 @@
"style": "default",
"rsc": false,
"tailwind": {
- "config": "packages/uti/tailwind.config.js",
- "css": "packages/uti/global.css",
+ "config": "packages/shadcnutils/tailwind.config.js",
+ "css": "packages/shadcnutils/global.css",
"baseColor": "neutral",
"cssVariables": true
},
"aliases": {
- "components": "@kbve/shade",
- "utils": "@kbve/uti"
+ "components": "@kbve/shadcnui",
+ "utils": "@kbve/shadcnutils"
}
}
diff --git a/migrations.json b/migrations.json
index 403f99b4c..48b62a4c2 100644
--- a/migrations.json
+++ b/migrations.json
@@ -1,12 +1,18 @@
{
- "migrations": [
- {
- "cli": "nx",
- "version": "18.2.0",
- "description": "Move executor options to target defaults.",
- "factory": "./src/migrations/update-18-2-0/move-options-to-target-defaults",
- "package": "@nxtensions/astro",
- "name": "move-options-to-target-defaults"
- }
- ]
+ "migrations": [
+ {
+ "version": "20.0.4-beta.0",
+ "description": "Add gitignore entry for temporary vite config files.",
+ "implementation": "./src/migrations/update-20-0-4/add-vite-temp-files-to-git-ignore",
+ "package": "@nx/vite",
+ "name": "update-20-0-4"
+ },
+ {
+ "version": "20.0.6-beta.0",
+ "description": "Add gitignore entry for temporary vite config files and remove previous incorrect glob.",
+ "implementation": "./src/migrations/update-20-0-4/add-vite-temp-files-to-git-ignore",
+ "package": "@nx/vite",
+ "name": "update-20-0-6"
+ }
+ ]
}
diff --git a/nx.json b/nx.json
index 2fbf8b5b9..b7a52bd2a 100644
--- a/nx.json
+++ b/nx.json
@@ -1,194 +1,247 @@
{
- "$schema": "./node_modules/nx/schemas/nx-schema.json",
- "affected": {
- "defaultBase": "main"
- },
- "targetDefaults": {
- "build": {
- "dependsOn": ["^build"],
- "inputs": ["production", "^production"],
- "cache": true
- },
- "e2e": {
- "inputs": ["default", "^production"],
- "cache": true
- },
- "lint": {
- "inputs": [
- "default",
- "{workspaceRoot}/.eslintrc.json",
- "{workspaceRoot}/.eslintignore",
- "{workspaceRoot}/eslint.config.js"
- ],
- "cache": true
- },
- "test": {
- "inputs": [
- "default",
- "^production",
- "{workspaceRoot}/jest.preset.js"
- ],
- "cache": true
- },
- "@nx/vite:test": {
- "cache": true,
- "inputs": ["default", "^production"]
- },
- "@nx/eslint:lint": {
- "inputs": [
- "default",
- "{workspaceRoot}/.eslintrc.json",
- "{workspaceRoot}/.eslintignore",
- "{workspaceRoot}/eslint.config.js"
- ],
- "cache": true
- },
- "@nx/expo:build": {
- "cache": true,
- "dependsOn": ["^build"],
- "inputs": ["production", "^production"]
- },
- "@nx/vite:build": {
- "cache": true,
- "dependsOn": ["^build"],
- "inputs": ["production", "^production"]
- },
- "@nx/esbuild:esbuild": {
- "cache": true,
- "dependsOn": ["^build"],
- "inputs": ["production", "^production"]
- },
- "@nxtensions/astro:build": {
- "dependsOn": ["^build"],
- "inputs": ["production", "^production"],
- "cache": true,
- "outputs": ["{workspaceRoot}/dist/{projectRoot}"]
- },
- "@nxtensions/astro:check": {
- "inputs": ["production", "^production"],
- "cache": true
- },
- "@nxtensions/astro:preview": {
- "dependsOn": ["build"]
- },
- "@nx/js:tsc": {
- "cache": true,
- "dependsOn": ["^build"],
- "inputs": ["production", "^production"]
- }
- },
- "namedInputs": {
- "default": ["{projectRoot}/**/*", "sharedGlobals"],
- "production": [
- "default",
- "!{projectRoot}/.eslintrc.json",
- "!{projectRoot}/eslint.config.js",
- "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
- "!{projectRoot}/tsconfig.spec.json",
- "!{projectRoot}/jest.config.[jt]s",
- "!{projectRoot}/src/test-setup.[jt]s",
- "!{projectRoot}/test-setup.[jt]s"
- ],
- "sharedGlobals": ["{workspaceRoot}/go.work"]
- },
- "workspaceLayout": {
- "projectNameAndRootFormat": "derived",
- "appsDir": "apps",
- "libsDir": "packages"
- },
- "plugins": [
- "@nxtensions/astro",
- "@nxlv/python",
- "@monodon/rust",
- {
- "plugin": "@nx/next/plugin",
- "options": {
- "buildTargetName": "build",
- "devTargetName": "dev",
- "startTargetName": "start"
- }
- },
- {
- "plugin": "@nx/eslint/plugin",
- "options": {
- "targetName": "lint"
- }
- },
- {
- "plugin": "@nx/vite/plugin",
- "options": {
- "buildTargetName": "build",
- "previewTargetName": "preview",
- "testTargetName": "test",
- "serveTargetName": "serve",
- "serveStaticTargetName": "serve-static"
- }
- },
- {
- "plugin": "@nx/expo/plugin",
- "options": {
- "startTargetName": "start",
- "serveTargetName": "serve",
- "runIosTargetName": "run-ios",
- "runAndroidTargetName": "run-android",
- "exportTargetName": "export",
- "prebuildTargetName": "prebuild",
- "installTargetName": "install",
- "buildTargetName": "build",
- "submitTargetName": "submit"
- }
- },
- "@nx-go/nx-go"
- ],
- "defaultProject": "kbve.com",
- "generators": {
- "@nx/react": {
- "application": {
- "babel": true,
- "style": "styled-components",
- "linter": "eslint",
- "bundler": "vite"
- },
- "library": {
- "style": "styled-components",
- "linter": "eslint",
- "unitTestRunner": "vitest"
- },
- "component": {
- "style": "styled-components"
- }
- },
- "@nx/next": {
- "application": {
- "style": "styled-components",
- "linter": "eslint"
- }
- }
- },
- "nxCloudAccessToken": "MDVjYTYxYmUtNjU1OS00NTJjLWFhYzQtZWE4MDNlNDkwOGZhfHJlYWQ=",
- "release": {
- "projectsRelationship": "independent",
- "projects": [
- "packages/*",
- "!astro-ve",
- "!astropad",
- "!expo-bbq",
- "!erust",
- "!holy",
- "!kbve",
- "!jedi"
- ],
- "version": {
- "conventionalCommits": false
- }
- },
- "tasksRunnerOptions": {
- "default": {
- "runner": "nx/tasks-runners/default",
- "options": {
- "cacheableOperations": ["build", "lint", "test", "e2e"]
- }
- }
- },
- "useLegacyCache": true
+ "$schema": "./node_modules/nx/schemas/nx-schema.json",
+ "affected": {
+ "defaultBase": "main"
+ },
+ "targetDefaults": {
+ "build": {
+ "dependsOn": [
+ "^build"
+ ],
+ "inputs": [
+ "production",
+ "^production"
+ ],
+ "cache": true
+ },
+ "e2e": {
+ "inputs": [
+ "default",
+ "^production"
+ ],
+ "cache": true
+ },
+ "lint": {
+ "inputs": [
+ "default",
+ "{workspaceRoot}/.eslintrc.json",
+ "{workspaceRoot}/.eslintignore",
+ "{workspaceRoot}/eslint.config.js"
+ ],
+ "cache": true
+ },
+ "test": {
+ "inputs": [
+ "default",
+ "^production",
+ "{workspaceRoot}/jest.preset.js"
+ ],
+ "cache": true
+ },
+ "@nx/vite:test": {
+ "cache": true,
+ "inputs": [
+ "default",
+ "^production"
+ ]
+ },
+ "@nx/eslint:lint": {
+ "inputs": [
+ "default",
+ "{workspaceRoot}/.eslintrc.json",
+ "{workspaceRoot}/.eslintignore",
+ "{workspaceRoot}/eslint.config.js"
+ ],
+ "cache": true
+ },
+ "@nx/expo:build": {
+ "cache": true,
+ "dependsOn": [
+ "^build"
+ ],
+ "inputs": [
+ "production",
+ "^production"
+ ]
+ },
+ "@nx/vite:build": {
+ "cache": true,
+ "dependsOn": [
+ "^build"
+ ],
+ "inputs": [
+ "production",
+ "^production"
+ ]
+ },
+ "@nx/esbuild:esbuild": {
+ "cache": true,
+ "dependsOn": [
+ "^build"
+ ],
+ "inputs": [
+ "production",
+ "^production"
+ ]
+ },
+ "@nxtensions/astro:build": {
+ "dependsOn": [
+ "^build"
+ ],
+ "inputs": [
+ "production",
+ "^production"
+ ],
+ "cache": true,
+ "outputs": [
+ "{workspaceRoot}/dist/{projectRoot}"
+ ]
+ },
+ "@nxtensions/astro:check": {
+ "inputs": [
+ "production",
+ "^production"
+ ],
+ "cache": true
+ },
+ "@nxtensions/astro:preview": {
+ "dependsOn": [
+ "build"
+ ]
+ },
+ "@nx/js:tsc": {
+ "cache": true,
+ "dependsOn": [
+ "^build"
+ ],
+ "inputs": [
+ "production",
+ "^production"
+ ]
+ }
+ },
+ "namedInputs": {
+ "default": [
+ "{projectRoot}/**/*",
+ "sharedGlobals"
+ ],
+ "production": [
+ "default",
+ "!{projectRoot}/.eslintrc.json",
+ "!{projectRoot}/eslint.config.js",
+ "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
+ "!{projectRoot}/tsconfig.spec.json",
+ "!{projectRoot}/jest.config.[jt]s",
+ "!{projectRoot}/src/test-setup.[jt]s",
+ "!{projectRoot}/test-setup.[jt]s"
+ ],
+ "sharedGlobals": [
+ "{workspaceRoot}/go.work"
+ ]
+ },
+ "workspaceLayout": {
+ "projectNameAndRootFormat": "derived",
+ "appsDir": "apps",
+ "libsDir": "packages"
+ },
+ "plugins": [
+ "@nxtensions/astro",
+ "@nxlv/python",
+ "@monodon/rust",
+ {
+ "plugin": "@nx/next/plugin",
+ "options": {
+ "buildTargetName": "build",
+ "devTargetName": "dev",
+ "startTargetName": "start"
+ }
+ },
+ {
+ "plugin": "@nx/eslint/plugin",
+ "options": {
+ "targetName": "lint"
+ }
+ },
+ {
+ "plugin": "@nx/vite/plugin",
+ "options": {
+ "buildTargetName": "build",
+ "previewTargetName": "preview",
+ "testTargetName": "test",
+ "serveTargetName": "serve",
+ "serveStaticTargetName": "serve-static"
+ }
+ },
+ {
+ "plugin": "@nx/expo/plugin",
+ "options": {
+ "startTargetName": "start",
+ "serveTargetName": "serve",
+ "runIosTargetName": "run-ios",
+ "runAndroidTargetName": "run-android",
+ "exportTargetName": "export",
+ "prebuildTargetName": "prebuild",
+ "installTargetName": "install",
+ "buildTargetName": "build",
+ "submitTargetName": "submit"
+ }
+ },
+ "@nx-go/nx-go"
+ ],
+ "defaultProject": "kbve.com",
+ "generators": {
+ "@nx/react": {
+ "application": {
+ "babel": true,
+ "style": "styled-components",
+ "linter": "eslint",
+ "bundler": "vite"
+ },
+ "library": {
+ "style": "styled-components",
+ "linter": "eslint",
+ "unitTestRunner": "vitest"
+ },
+ "component": {
+ "style": "styled-components"
+ }
+ },
+ "@nx/next": {
+ "application": {
+ "style": "styled-components",
+ "linter": "eslint"
+ }
+ }
+ },
+ "nxCloudAccessToken": "MDVjYTYxYmUtNjU1OS00NTJjLWFhYzQtZWE4MDNlNDkwOGZhfHJlYWQ=",
+ "release": {
+ "projectsRelationship": "independent",
+ "projects": [
+ "packages/*",
+ "!astro-ve",
+ "!astropad",
+ "!expo-bbq",
+ "!erust",
+ "!holy",
+ "!kbve",
+ "!jedi"
+ ],
+ "version": {
+ "conventionalCommits": false
+ }
+ },
+ "tasksRunnerOptions": {
+ "default": {
+ "runner": "nx/tasks-runners/default",
+ "options": {
+ "cacheableOperations": [
+ "build",
+ "lint",
+ "test",
+ "e2e"
+ ]
+ }
+ }
+ },
+ "useLegacyCache": true
}
diff --git a/package.json b/package.json
index f8068841b..11e3b79d5 100644
--- a/package.json
+++ b/package.json
@@ -27,25 +27,25 @@
"@nanostores/react": "^0.7.3",
"@nestjs/schematics": "^10.2.2",
"@nestjs/testing": "^10.4.5",
- "@nx-extend/shadcn-ui": "^4.0.0",
+ "@nx-extend/shadcn-ui": "^4.1.2",
"@nx-go/nx-go": "^3.2.0",
"@nx-tools/nx-container": "^6.1.0",
- "@nx/cypress": "20.0.1",
- "@nx/devkit": "20.0.1",
- "@nx/esbuild": "20.0.1",
- "@nx/eslint": "20.0.1",
- "@nx/eslint-plugin": "20.0.1",
- "@nx/expo": "20.0.1",
- "@nx/express": "20.0.1",
- "@nx/jest": "20.0.1",
- "@nx/js": "20.0.1",
- "@nx/next": "20.0.1",
- "@nx/node": "20.0.1",
- "@nx/react": "20.0.1",
- "@nx/vite": "20.0.1",
- "@nx/web": "20.0.1",
- "@nx/webpack": "20.0.1",
- "@nx/workspace": "20.0.1",
+ "@nx/cypress": "20.0.11",
+ "@nx/devkit": "20.0.11",
+ "@nx/esbuild": "20.0.11",
+ "@nx/eslint": "20.0.11",
+ "@nx/eslint-plugin": "20.0.11",
+ "@nx/expo": "20.0.11",
+ "@nx/express": "20.0.11",
+ "@nx/jest": "20.0.11",
+ "@nx/js": "20.0.11",
+ "@nx/next": "20.0.11",
+ "@nx/node": "20.0.11",
+ "@nx/react": "20.0.11",
+ "@nx/vite": "20.0.11",
+ "@nx/web": "20.0.11",
+ "@nx/webpack": "20.0.11",
+ "@nx/workspace": "20.0.11",
"@nxlv/python": "19.2.1",
"@nxtensions/astro": "19.0.1",
"@playwright/test": "^1.48.1",
@@ -61,8 +61,8 @@
"@types/express": "~4.17.21",
"@types/jest": "29.5.13",
"@types/node": "18.19.17",
- "@types/react": "18.2.79",
- "@types/react-dom": "18.2.25",
+ "@types/react": "18.3.1",
+ "@types/react-dom": "18.3.0",
"@types/react-grid-layout": "^1.3.5",
"@types/react-is": "18.3.0",
"@types/styled-components": "5.1.26",
@@ -105,12 +105,12 @@
"lit": "^3.2.1",
"nanostores": "^0.9.5",
"node-appwrite": "^11.1.1",
- "nx": "20.0.1",
+ "nx": "20.0.11",
"preact": "^10.24.3",
"prettier": "^3.3.3",
"prettier-plugin-astro": "^0.13.0",
- "react": "18.2.0",
- "react-dom": "18.2.0",
+ "react": "18.3.1",
+ "react-dom": "18.3.1",
"react-test-renderer": "18.2.0",
"rehype-external-links": "^3.0.0",
"solid-devtools": "^0.30.1",
@@ -137,13 +137,40 @@
"@expo/metro-runtime": "3.2.3",
"@hcaptcha/react-hcaptcha": "^1.11.0",
"@hcaptcha/react-native-hcaptcha": "^1.7.2",
+ "@hookform/resolvers": "^3.9.1",
"@lottiefiles/react-lottie-player": "^3.5.4",
"@mapbox/node-pre-gyp": "^1.0.11",
"@nestjs/common": "^10.4.5",
"@nestjs/platform-express": "^10.4.5",
"@planetscale/database": "^1.19.0",
"@preline/tooltip": "^2.5.0",
+ "@radix-ui/react-accordion": "^1.2.1",
+ "@radix-ui/react-alert-dialog": "^1.1.2",
+ "@radix-ui/react-aspect-ratio": "^1.1.0",
+ "@radix-ui/react-avatar": "^1.1.1",
+ "@radix-ui/react-checkbox": "^1.1.2",
+ "@radix-ui/react-collapsible": "^1.1.1",
+ "@radix-ui/react-context-menu": "^2.2.2",
+ "@radix-ui/react-dialog": "^1.1.2",
+ "@radix-ui/react-dropdown-menu": "^2.1.2",
+ "@radix-ui/react-hover-card": "^1.1.2",
"@radix-ui/react-label": "^2.1.0",
+ "@radix-ui/react-menubar": "^1.1.2",
+ "@radix-ui/react-navigation-menu": "^1.2.1",
+ "@radix-ui/react-popover": "^1.1.2",
+ "@radix-ui/react-progress": "^1.1.0",
+ "@radix-ui/react-radio-group": "^1.2.1",
+ "@radix-ui/react-scroll-area": "^1.2.0",
+ "@radix-ui/react-select": "^2.1.2",
+ "@radix-ui/react-separator": "^1.1.0",
+ "@radix-ui/react-slider": "^1.2.1",
+ "@radix-ui/react-slot": "^1.1.0",
+ "@radix-ui/react-switch": "^1.1.1",
+ "@radix-ui/react-tabs": "^1.1.1",
+ "@radix-ui/react-toast": "^1.2.2",
+ "@radix-ui/react-toggle": "^1.1.0",
+ "@radix-ui/react-toggle-group": "^1.1.0",
+ "@radix-ui/react-tooltip": "^1.1.3",
"@react-native-async-storage/async-storage": "^1.23.1",
"@react-native-masked-view/masked-view": "^0.3.1",
"@react-three/drei": "^9.114.4",
@@ -170,11 +197,14 @@
"axios": "1.6.7",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
+ "cmdk": "1.0.0",
"comlink": "^4.4.1",
"cross-fetch": "^4.0.0",
+ "date-fns": "^4.1.0",
"dexie": "^4.0.8",
"dexie-react-hooks": "^1.1.7",
"dompurify": "^3.1.7",
+ "embla-carousel-react": "^8.3.1",
"expo": "51.0.38",
"expo-font": "^12.0.10",
"expo-linear-gradient": "^13.0.2",
@@ -184,21 +214,25 @@
"expo-status-bar": "1.12.1",
"express": "^4.21.1",
"framer-motion": "^11.11.11",
- "grid-engine": "^2.45.4",
+ "grid-engine": "^2.45.5",
"gsap": "^3.12.5",
"html-react-parser": "^5.1.18",
"http-proxy-middleware": "^2.0.7",
+ "input-otp": "^1.4.1",
"lottie-react-native": "^6.7.0",
"lucide-react": "^0.395.0",
"marked": "^13.0.3",
"matter-js": "0.17.1",
"next": "14.2.3",
+ "next-themes": "^0.4.3",
"phaser": "^3.86.0",
"poly-decomp": "^0.3.0",
"preline": "^2.5.1",
+ "react-day-picker": "8.10.1",
"react-force-graph": "^1.44.6",
"react-grid-layout": "^1.5.0",
"react-helmet-async": "^2.0.5",
+ "react-hook-form": "^7.53.1",
"react-is": "18.3.1",
"react-native": "0.74.5",
"react-native-reanimated": "3.10.1",
@@ -208,11 +242,14 @@
"react-native-url-polyfill": "^2.0.0",
"react-native-web": "0.19.12",
"react-native-webview": "^13.8.6",
+ "react-resizable-panels": "^2.1.6",
"react-spring": "^9.7.4",
+ "recharts": "^2.13.3",
"reflect-metadata": "^0.1.14",
"rollup-plugin-copy": "^3.5.0",
"rxjs": "^7.8.1",
"shiki": "^1.22.0",
+ "sonner": "^1.7.0",
"styled-components": "5.3.6",
"tailwind-merge": "^2.5.4",
"tailwindcss-animate": "^1.0.7",
@@ -221,7 +258,9 @@
"three-csg-ts": "^3.2.0",
"three-spritetext": "^1.9.0",
"tslib": "^2.8.0",
- "uwebsockets-express": "^1.3.11"
+ "uwebsockets-express": "^1.3.11",
+ "vaul": "^1.1.1",
+ "zod": "^3.23.8"
},
"nx": {
"includedScripts": []
diff --git a/packages/laser/src/lib/phaser/map/mapdatabase.ts b/packages/laser/src/lib/phaser/map/mapdatabase.ts
index 77fc485fc..46ed85189 100644
--- a/packages/laser/src/lib/phaser/map/mapdatabase.ts
+++ b/packages/laser/src/lib/phaser/map/mapdatabase.ts
@@ -3,7 +3,12 @@
import Dexie from 'dexie';
import axios from 'axios';
import { Debug } from '../../utils/debug';
-import { IMapData, type Bounds, type INPCObjectGPS } from '../../../types';
+import {
+ IMapData,
+ type Bounds,
+ type INPCObjectGPS,
+ type ITilemapJson,
+} from '../../../types';
/**
* Represents a Dexie-based database for managing maps, JSON files, and tileset images.
@@ -13,6 +18,30 @@ class MapDatabase extends Dexie {
maps: Dexie.Table;
jsonFiles: Dexie.Table<{ tilemapKey: string; jsonData: string }, string>;
tilesetImages: Dexie.Table<{ tilemapKey: string; imageData: Blob }, string>;
+ chunks: Dexie.Table<
+ {
+ tilemapKey: string;
+ chunkX: number;
+ chunkY: number;
+ jsonData: ITilemapJson;
+ imageData?: Blob;
+ },
+ [string, number, number]
+ >;
+ //tileJsonData: Dexie.Table<{ tilemapKey: string; jsonContent: object }, string>;
+ tileJsonData: Dexie.Table<
+ { tilemapKey: string; jsonContent: ITilemapJson },
+ string
+ >;
+
+ // Map Settings - quick access to avoid calling dexie.
+ chunkSize = 10;
+ tileWidth = 32;
+ tileHeight = 32;
+ chunkWidth = this.chunkSize * this.tileWidth;
+ chunkHeight = this.chunkSize * this.tileHeight;
+ scale = 1;
+ displayedChunks: Set = new Set();
constructor() {
super('MapDatabase');
@@ -20,10 +49,21 @@ class MapDatabase extends Dexie {
maps: 'tilemapKey',
jsonFiles: 'tilemapKey',
tilesetImages: 'tilemapKey',
+ chunks: '[tilemapKey+chunkX+chunkY]',
+ tileJsonData: 'tilemapKey',
});
this.maps = this.table('maps');
this.jsonFiles = this.table('jsonFiles');
this.tilesetImages = this.table('tilesetImages');
+ this.chunks = this.table('chunks');
+ this.tileJsonData = this.table('tileJsonData');
+ }
+
+ /**
+ * Resets map-related variables for safety before loading a new map.
+ */
+ resetMapSettings() {
+ this.displayedChunks.clear();
}
/**
@@ -445,6 +485,543 @@ class MapDatabase extends Dexie {
scene.load.start();
});
}
+
+ //** Map Chunking */
+ //** vChunk */
+ //** v0 - Still building out the core functions. */
+
+ /**
+ * Ensures all necessary assets for the given tilemap are available in the database.
+ * This includes map data, JSON data, and tileset image, and chunks the map if needed.
+ *
+ * @param {string} tilemapKey - The unique key identifying the map.
+ * @returns {Promise} Resolves when all assets are verified and available in Dexie.
+ */
+ async prepareMapLoad(tilemapKey: string): Promise {
+ // Check and ensure map data is available
+ const mapData = await this.getMap(tilemapKey);
+ if (!mapData) {
+ throw new Error(`Map with key ${tilemapKey} not found`);
+ }
+
+ // Check and ensure JSON data is available
+ const jsonData = await this.getJsonData(tilemapKey);
+ if (!jsonData) {
+ throw new Error(`JSON data for map ${tilemapKey} not found`);
+ }
+
+ // Check and ensure tileset image is available
+ const tilesetImage = await this.getTilesetImage(tilemapKey);
+ if (!tilesetImage) {
+ throw new Error(`Tileset image for map ${tilemapKey} not found`);
+ }
+
+ // Create URL for tileset image if not already set
+ let tilesetImageUrl: string | null = null;
+ try {
+ tilesetImageUrl = URL.createObjectURL(tilesetImage);
+ } catch (error) {
+ throw new Error(
+ `Failed to create object URL for tileset image: ${error}`,
+ );
+ }
+
+ if (!tilesetImageUrl) {
+ throw new Error(
+ `Tileset image URL for map ${tilemapKey} could not be created.`,
+ );
+ }
+
+ // Finally, chunk the map for efficient loading if it's not already chunked
+ await this.chunkMap(tilemapKey);
+ }
+
+ /**
+ * Helper function to retrieve parsed JSON data for a tilemap.
+ * @param {string} tilemapKey - The unique key identifying the map.
+ * @returns {Promise} Parsed JSON data if available.
+ */
+ async getParsedJsonData(tilemapKey: string): Promise {
+ // Check if parsed JSON data is already stored in `tileJsonData`
+ const tileJsonEntry = await this.tileJsonData.get(tilemapKey);
+ if (tileJsonEntry) {
+ return tileJsonEntry.jsonContent as ITilemapJson;
+ }
+
+ // Fetch JSON data path from `jsonFiles` table
+ const jsonFileEntry = await this.jsonFiles.get(tilemapKey);
+ if (!jsonFileEntry) {
+ Debug.error(`JSON file path for ${tilemapKey} not found`);
+ return null;
+ }
+
+ try {
+ // Fetch and parse the JSON data from the file path
+ const response = await axios.get(jsonFileEntry.jsonData);
+ const jsonData: ITilemapJson = response.data;
+
+ // Store parsed JSON data in `tileJsonData`
+ await this.tileJsonData.put({ tilemapKey, jsonContent: jsonData });
+ return jsonData;
+ } catch (error) {
+ Debug.error(
+ `Failed to fetch or parse JSON data for ${tilemapKey}:`,
+ error,
+ );
+ return null;
+ }
+ }
+
+ /**
+ * Adds a chunk to the database with a reference to its parent map.
+ * @param {string} tilemapKey - The map identifier.
+ * @param {number} chunkX - The X coordinate of the chunk.
+ * @param {number} chunkY - The Y coordinate of the chunk.
+ * @param {ITilemapJson} jsonData - JSON data specific to the chunk.
+ * @param {Blob} [imageData] - Optional image data for the chunk's tileset.
+ */
+ async addChunk(
+ tilemapKey: string,
+ chunkX: number,
+ chunkY: number,
+ jsonData: ITilemapJson,
+ imageData?: Blob,
+ ) {
+ Debug.log(`Adding chunk for (${chunkX}, ${chunkY}) of ${tilemapKey}`);
+ Debug.log(`Chunk data: ${jsonData}`);
+ await this.chunks.put({
+ tilemapKey,
+ chunkX,
+ chunkY,
+ jsonData,
+ imageData,
+ });
+ }
+
+ /**
+ * Retrieves a specific chunk by its coordinates within a map.
+ * @param {string} tilemapKey - The map identifier.
+ * @param {number} chunkX - The X coordinate of the chunk.
+ * @param {number} chunkY - The Y coordinate of the chunk.
+ * @returns {Promise<{ jsonData: ITilemapJson; imageData?: Blob } | undefined>} The chunk data if found.
+ */
+ async getChunk(
+ tilemapKey: string,
+ chunkX: number,
+ chunkY: number,
+ ): Promise<{ jsonData: ITilemapJson; imageData?: Blob } | undefined> {
+ const chunk = await this.chunks.get([tilemapKey, chunkX, chunkY]);
+ Debug.log(
+ `Retrieved chunk for (${chunkX}, ${chunkY}) of ${tilemapKey}: ${chunk}`,
+ );
+ return chunk;
+ }
+
+ /**
+ * Removes a specific chunk from the database using a compound key array.
+ * @param {string} tilemapKey - The map identifier.
+ * @param {number} chunkX - The X coordinate of the chunk.
+ * @param {number} chunkY - The Y coordinate of the chunk.
+ * @returns {Promise} A promise that resolves once the chunk is removed.
+ */
+ async removeChunk(
+ tilemapKey: string,
+ chunkX: number,
+ chunkY: number,
+ ): Promise {
+ await this.chunks.delete([tilemapKey, chunkX, chunkY]);
+ }
+
+ /**
+ * Removes multiple chunks that are no longer needed (e.g., out-of-view chunks).
+ * @param {string} tilemapKey - The map identifier.
+ * @param {Array<{ chunkX: number; chunkY: number }>} chunkCoords - Array of chunk coordinates to remove.
+ * @returns {Promise} A promise that resolves once all specified chunks are removed.
+ */
+ async removeChunks(
+ tilemapKey: string,
+ chunkCoords: Array<{ chunkX: number; chunkY: number }>,
+ ): Promise {
+ const removalPromises = chunkCoords.map(({ chunkX, chunkY }) =>
+ this.removeChunk(tilemapKey, chunkX, chunkY),
+ );
+ await Promise.all(removalPromises);
+ }
+
+ private async extractChunkJsonData(
+ tilemapKey: string,
+ chunkX: number,
+ chunkY: number,
+ chunkSize: number,
+ ): Promise {
+ // Use getParsedJsonData to ensure JSON data is fetched and cached properly
+ const fullTileData = await this.getParsedJsonData(tilemapKey);
+ if (!fullTileData) {
+ Debug.error(`Parsed JSON data for map ${tilemapKey} not found`);
+ return null;
+ }
+
+ const startX = chunkX * chunkSize;
+ const startY = chunkY * chunkSize;
+ const endX = Math.min(startX + chunkSize, fullTileData.width);
+ const endY = Math.min(startY + chunkSize, fullTileData.height);
+
+ const chunkTileData: number[] = [];
+ for (let y = startY; y < endY; y++) {
+ const row = fullTileData.layers[0].data.slice(
+ y * fullTileData.width + startX,
+ y * fullTileData.width + endX,
+ );
+ chunkTileData.push(...row);
+ }
+
+ return {
+ ...fullTileData, // Spread the full data structure
+ width: endX - startX,
+ height: endY - startY,
+ layers: [
+ {
+ ...fullTileData.layers[0],
+ data: chunkTileData,
+ },
+ ],
+ };
+ }
+
+ /**
+ * Retrieves the width and height of a map from its JSON data.
+ * @param {string} tilemapKey - The unique key identifying the map.
+ * @returns {Promise<{ width: number, height: number } | undefined>} The dimensions of the map if found.
+ */
+ async getMapDimensions(
+ tilemapKey: string,
+ ): Promise<{ width: number; height: number } | undefined> {
+ const jsonData = await this.getParsedJsonData(tilemapKey);
+ if (!jsonData) {
+ Debug.error(`Failed to retrieve JSON data for ${tilemapKey}`);
+ return undefined;
+ }
+
+ const width = jsonData.width;
+ const height = jsonData.height;
+
+ if (width > 0 && height > 0) {
+ return { width, height };
+ } else {
+ Debug.error(`Invalid JSON data for map ${tilemapKey}`);
+ return undefined;
+ }
+ }
+
+ /**
+ * Initializes and chunks the map data into 10x10 pieces.
+ * @param {string} tilemapKey - The unique key identifying the map.
+ * @returns {Promise} Resolves when the map has been chunked and stored.
+ */
+ async chunkMap(tilemapKey: string): Promise {
+ const mapDimensions = await this.getMapDimensions(tilemapKey);
+ if (!mapDimensions) {
+ Debug.error(`Failed to retrieve dimensions for map ${tilemapKey}`);
+ return;
+ }
+
+ const { width, height } = mapDimensions;
+ const chunkSize = 10; // Size of each chunk
+ const numChunksX = Math.ceil(width / chunkSize);
+ const numChunksY = Math.ceil(height / chunkSize);
+
+ Debug.log(`Starting chunkMap for ${tilemapKey}`);
+ for (let chunkX = 0; chunkX < numChunksX; chunkX++) {
+ for (let chunkY = 0; chunkY < numChunksY; chunkY++) {
+ const jsonData = await this.extractChunkJsonData(
+ tilemapKey,
+ chunkX,
+ chunkY,
+ chunkSize,
+ );
+
+ if (!jsonData) {
+ Debug.error(
+ `Failed to extract JSON data for chunk (${chunkX}, ${chunkY}) of ${tilemapKey}`,
+ );
+ continue; // Skip this chunk if JSON data extraction failed
+ }
+
+ Debug.log(
+ `Storing chunk (${chunkX}, ${chunkY}) for ${tilemapKey}`,
+ );
+ await this.addChunk(tilemapKey, chunkX, chunkY, jsonData);
+ }
+ }
+ Debug.log(`Finished chunkMap for ${tilemapKey}`);
+ }
+
+ /**
+ * Loads a specific chunk into the Phaser scene.
+ * @param {Phaser.Scene} scene - The Phaser scene.
+ * @param {string} tilemapKey - The map identifier.
+ * @param {number} chunkX - The X coordinate of the chunk.
+ * @param {number} chunkY - The Y coordinate of the chunk.
+ */
+/**
+ * Loads a specific chunk into the Phaser scene.
+ * @param {Phaser.Scene} scene - The Phaser scene.
+ * @param {string} tilemapKey - The map identifier.
+ * @param {number} chunkX - The X coordinate of the chunk.
+ * @param {number} chunkY - The Y coordinate of the chunk.
+ */
+async loadChunkIntoScene(
+ scene: Phaser.Scene,
+ tilemapKey: string,
+ chunkX: number,
+ chunkY: number,
+): Promise {
+ const chunkData = await this.getChunk(tilemapKey, chunkX, chunkY);
+ if (!chunkData) {
+ Debug.error(`Chunk data for (${chunkX}, ${chunkY}) not found`);
+ return;
+ }
+
+ const chunkTilemapKey = `${tilemapKey}_${chunkX}_${chunkY}`;
+
+ // Use the original tileset key from mapData
+ const mapData = await this.getMap(tilemapKey);
+ if (!mapData) {
+ Debug.error(`Map data not found for ${tilemapKey}`);
+ return;
+ }
+
+ const tilesetName = chunkData.jsonData.tilesets[0].name; // Use tileset name from JSON
+
+ // Ensure tileset image is loaded with the correct key before loading the chunk
+ if (!scene.textures.exists(tilesetName)) {
+ const tilesetImage = await this.getTilesetImage(tilemapKey);
+ if (tilesetImage) {
+ const tilesetImageUrl = URL.createObjectURL(tilesetImage);
+ scene.load.image(tilesetName, tilesetImageUrl); // Load the image with the correct name
+
+ await new Promise((resolve) => scene.load.once('complete', resolve));
+ scene.load.start();
+ } else {
+ Debug.error(`Failed to load tileset image for ${tilesetName}`);
+ return;
+ }
+ }
+
+ // Use tilemapTiledJSON to load chunk data as a Tiled map
+ scene.load.tilemapTiledJSON(chunkTilemapKey, chunkData.jsonData);
+
+ await new Promise((resolve) => scene.load.once('complete', resolve));
+ scene.load.start();
+
+ // Create tilemap and add all layers
+ const map = scene.make.tilemap({ key: chunkTilemapKey });
+ const tileset = map.addTilesetImage(tilesetName); // Use tileset name for matching
+ if (tileset) {
+
+ // Loop through each layer in the tilemap and create it
+ map.layers.forEach((layerData, index) => {
+ const layer = map.createLayer(index, tileset, 0, 0);
+ if (layer) {
+ layer.setScale(this.scale);
+ Debug.log(`Layer ${index} created for chunk (${chunkX}, ${chunkY}) with tileset ${tilesetName}.`);
+ } else {
+ Debug.error(`Layer ${index} could not be created for chunk (${chunkX}, ${chunkY}).`);
+ }
+ });
+ } else {
+ Debug.error(`Tileset ${tilesetName} could not be added to tilemap.`);
+ }
+}
+
+
+ //
+ /**
+ * Removes a specific chunk from the Phaser scene.
+ * @param {Phaser.Scene} scene - The Phaser scene.
+ * @param {string} tilemapKey - The map identifier.
+ * @param {number} chunkX - The X coordinate of the chunk.
+ * @param {number} chunkY - The Y coordinate of the chunk.
+ */
+ removeChunkFromScene(
+ scene: Phaser.Scene,
+ tilemapKey: string,
+ chunkX: number,
+ chunkY: number,
+ ): void {
+ const chunkTilemapKey = `${tilemapKey}_${chunkX}_${chunkY}`;
+
+ // Remove the layer and tilemap
+ const map = scene.make.tilemap({ key: chunkTilemapKey });
+ if (map) {
+ map.destroy();
+ }
+
+ // Remove from cache
+ scene.cache.tilemap.remove(chunkTilemapKey);
+ }
+
+ /**
+ * Updates the visible chunks based on the player's position.
+ * @param {Phaser.Scene} scene - The Phaser scene.
+ * @param {string} tilemapKey - The map identifier.
+ * @param {number} playerX - Player's X position in the world.
+ * @param {number} playerY - Player's Y position in the world.
+ * @param {number} viewRadius - Number of chunks to load around the player.
+ */
+ async updateVisibleChunks(
+ scene: Phaser.Scene,
+ tilemapKey: string,
+ playerX: number,
+ playerY: number,
+ viewRadius: number,
+ ): Promise {
+ const mapData = await this.getMap(tilemapKey);
+ if (!mapData) {
+ console.error(`Map data for ${tilemapKey} not found`);
+ return;
+ }
+
+ const tileWidth = this.tileWidth;
+ const tileHeight = this.tileHeight;
+ const chunkSize = this.chunkSize;
+
+ const playerChunkX = Math.floor(playerX / (chunkSize * tileWidth));
+ const playerChunkY = Math.floor(playerY / (chunkSize * tileHeight));
+
+ const newDisplayedChunks = new Set();
+
+ // Calculate chunks to display
+ for (let dx = -viewRadius; dx <= viewRadius; dx++) {
+ for (let dy = -viewRadius; dy <= viewRadius; dy++) {
+ const chunkX = playerChunkX + dx;
+ const chunkY = playerChunkY + dy;
+ const chunkKey = `${chunkX},${chunkY}`;
+
+ // Load the chunk if it's within the map bounds
+ if (chunkX >= 0 && chunkY >= 0) {
+ await this.loadChunkIntoScene(
+ scene,
+ tilemapKey,
+ chunkX,
+ chunkY,
+ );
+ newDisplayedChunks.add(chunkKey);
+ }
+ }
+ }
+
+ // Remove chunks that are no longer in view
+ for (const chunkKey of this.displayedChunks) {
+ if (!newDisplayedChunks.has(chunkKey)) {
+ const [chunkX, chunkY] = chunkKey.split(',').map(Number);
+ this.removeChunkFromScene(scene, tilemapKey, chunkX, chunkY);
+ }
+ }
+
+ // Update the displayed chunks
+ this.displayedChunks = newDisplayedChunks;
+ }
+
+ /**
+ * Load the initial chunk of the map based on player's starting position.
+ * This serves as the starting map for the scene.
+ * @param {Phaser.Scene} scene - The Phaser scene
+ * @param {string} tilemapKey - The unique key identifying the map
+ * @param {number} startX - Player's starting X position in the world
+ * @param {number} startY - Player's starting Y position in the world
+ */
+ async loadNewMap(
+ scene: Phaser.Scene,
+ tilemapKey: string,
+ startX: number,
+ startY: number,
+ ): Promise {
+ Debug.log(`Loading map with key: ${tilemapKey}`);
+ this.resetMapSettings();
+
+ const mapData = await this.getMap(tilemapKey);
+ if (!mapData) {
+ Debug.error(`Map data not found for ${tilemapKey}`);
+ return null;
+ }
+
+ // Determine which chunk the player starts in
+ const playerChunkX = Math.floor(startX / (this.chunkWidth || 1));
+ const playerChunkY = Math.floor(startY / (this.chunkHeight || 1));
+
+ // Load the initial chunk for player's starting position
+ const initialChunkData = await this.getChunk(
+ tilemapKey,
+ playerChunkX,
+ playerChunkY,
+ );
+ if (!initialChunkData) {
+ Debug.error(
+ `Chunk (${playerChunkX}, ${playerChunkY}) not found for ${tilemapKey}`,
+ );
+ return null;
+ }
+
+ // Prepare tileset image if not loaded
+ const tilesetKey = mapData.tilesetKey;
+ if (!scene.textures.exists(tilesetKey)) {
+ const tilesetImage = await this.getTilesetImage(tilemapKey);
+ if (tilesetImage) {
+ const tilesetImageUrl = URL.createObjectURL(tilesetImage);
+ scene.load.image(tilesetKey, tilesetImageUrl);
+ await new Promise((resolve) => {
+ scene.load.once('complete', resolve);
+ scene.load.start();
+ });
+ } else {
+ Debug.error(`Failed to load tileset image for ${tilesetKey}`);
+ return null;
+ }
+ }
+
+ // Use tilemapTiledJSON to add the initial chunk data to Phaser's cache
+ const initialChunkKey = `${tilemapKey}_${playerChunkX}_${playerChunkY}`;
+ scene.load.tilemapTiledJSON(initialChunkKey, initialChunkData.jsonData);
+
+ // Wait for JSON data to load
+ await new Promise((resolve) => scene.load.once('complete', resolve));
+ scene.load.start();
+
+ // Create the tilemap for the initial chunk
+ const map = scene.make.tilemap({ key: initialChunkKey });
+ if (!map) {
+ Debug.error(
+ `Tilemap could not be created for chunk (${playerChunkX}, ${playerChunkY})`,
+ );
+ return null;
+ }
+
+ // Add tileset and layer to the scene
+ const tileset = map.addTilesetImage(mapData.tilesetName, tilesetKey);
+ if (!tileset) {
+ Debug.error(`Tileset ${tilesetKey} could not be added to tilemap.`);
+ return null;
+ }
+
+ // Create layers for the initial chunk
+ for (let i = 0; i < map.layers.length; i++) {
+ const layer = map.createLayer(
+ i,
+ tileset,
+ 0,
+ 0,
+ );
+ if (layer) {
+ layer.setScale(mapData.scale || this.scale);
+ Debug.log(`Layer ${i} created for initial chunk.`);
+ } else {
+ Debug.error(`Layer ${i} could not be created.`);
+ }
+ }
+
+ return map;
+ }
}
// Export the class itself
diff --git a/packages/laser/src/lib/phaser/player/playercontroller.ts b/packages/laser/src/lib/phaser/player/playercontroller.ts
index 855b40bb3..dfed680fd 100644
--- a/packages/laser/src/lib/phaser/player/playercontroller.ts
+++ b/packages/laser/src/lib/phaser/player/playercontroller.ts
@@ -12,6 +12,7 @@ export class PlayerController {
private cursor: Phaser.Types.Input.Keyboard.CursorKeys | undefined;
private wasdKeys!: { [key: string]: Phaser.Input.Keyboard.Key; };
private tooltip: Phaser.GameObjects.Text;
+ private tileSize = 48;
constructor(scene: Scene, gridEngine: any, quadtree: Quadtree) {
@@ -25,6 +26,7 @@ export class PlayerController {
font: '16px Arial',
backgroundColor: '#000000',
}).setDepth(4).setPadding(3,2,2,3).setVisible(false);
+ this.tileSize;
}
private initializeWASDKeys() {
@@ -195,18 +197,30 @@ export class PlayerController {
}
private checkForNearbyObjects() {
- const tileSize = 48; // Adjust this based on your game's tile size
const playerPosition = this.gridEngine.getPosition('player') as Point;
- const screenX = playerPosition.x * tileSize;
- const screenY = playerPosition.y * tileSize;
-
+ const screenX = playerPosition.x * this.tileSize;
+ const screenY = playerPosition.y * this.tileSize;
+
const foundRanges = this.quadtree.query(playerPosition);
-
+
if (foundRanges.length > 0) {
- this.tooltip.setPosition(screenX, screenY - 60).setVisible(true);
+ this.tooltip.setPosition(screenX, screenY - 60).setVisible(true);
} else {
- this.tooltip.setVisible(false);
+ this.tooltip.setVisible(false);
}
+}
+
+
+ // Method to get the player's X coordinate in the world
+ getPlayerCoordsX(): number {
+ const playerPosition = this.gridEngine.getPosition('player') as Point;
+ return playerPosition.x;
+ }
+
+ // Method to get the player's Y coordinate in the world
+ getPlayerCoordsY(): number {
+ const playerPosition = this.gridEngine.getPosition('player') as Point;
+ return playerPosition.y;
}
handleMovement() {
diff --git a/packages/laser/src/types.ts b/packages/laser/src/types.ts
index 3505a6d66..221f9e6f0 100644
--- a/packages/laser/src/types.ts
+++ b/packages/laser/src/types.ts
@@ -565,6 +565,72 @@ export interface IMapData {
jsonDataUrl: string;
}
+export interface ITilemapJson {
+ compressionlevel: number;
+ height: number;
+ infinite: boolean;
+ layers: Layer[];
+ nextlayerid: number;
+ nextobjectid: number;
+ orientation: string;
+ renderorder: string;
+ tiledversion: string;
+ tileheight: number;
+ tilesets: Tileset[];
+ tilewidth: number;
+ type: string;
+ version: string;
+ width: number;
+ properties?: Property[]; // Root-level properties, if any like 'ge_collide'
+ }
+
+ export interface Layer {
+ data: number[];
+ height: number;
+ id: number;
+ name: string;
+ opacity: number;
+ type: string;
+ visible: boolean;
+ width: number;
+ x: number;
+ y: number;
+ properties?: Property[]; // Layer-specific properties
+ }
+
+ export interface Property {
+ name: string;
+ type: string;
+ value: boolean | number | string | any;
+ }
+
+ export interface Tileset {
+ columns: number;
+ firstgid: number;
+ image: string;
+ imageheight: number;
+ imagewidth: number;
+ margin: number;
+ name: string;
+ spacing: number;
+ tilecount: number;
+ tileheight: number;
+ tilewidth: number;
+ tiles?: Tile[]; // Optional in case no specific tiles are defined
+ }
+
+ export interface Tile {
+ id: number;
+ properties?: Property[]; // Tile-specific properties
+ }
+
+ export interface ChunkedLayer {
+ chunks: Map;
+ width: number;
+ height: number;
+ chunkSize: number;
+ }
+
//* QuadTree */
export interface Bounds {
xMin: number;
diff --git a/packages/shade/.babelrc b/packages/shadcnui/.babelrc
similarity index 100%
rename from packages/shade/.babelrc
rename to packages/shadcnui/.babelrc
diff --git a/packages/shade/.eslintrc.json b/packages/shadcnui/.eslintrc.json
similarity index 100%
rename from packages/shade/.eslintrc.json
rename to packages/shadcnui/.eslintrc.json
diff --git a/packages/shadcnui/README.md b/packages/shadcnui/README.md
new file mode 100644
index 000000000..e643c6497
--- /dev/null
+++ b/packages/shadcnui/README.md
@@ -0,0 +1,7 @@
+# ShadCN UI
+
+Previous named `Shade` but to keep it easier to reference and remember, we will call this `ShadeCNUI`.
+
+## Running unit tests
+
+Run `pnpm nx test shadcnui` to execute the unit tests via [Jest](https://kbve.com/application/javascript).
diff --git a/packages/shade/project.json b/packages/shadcnui/project.json
similarity index 76%
rename from packages/shade/project.json
rename to packages/shadcnui/project.json
index 186e48603..d39487f7f 100644
--- a/packages/shade/project.json
+++ b/packages/shadcnui/project.json
@@ -1,7 +1,7 @@
{
- "name": "shade",
+ "name": "shadcnui",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
- "sourceRoot": "packages/shade/src",
+ "sourceRoot": "packages/shadcnui/src",
"projectType": "library",
"tags": [],
"targets": {
diff --git a/packages/shadcnui/src/accordion.tsx b/packages/shadcnui/src/accordion.tsx
new file mode 100644
index 000000000..a87ab16b2
--- /dev/null
+++ b/packages/shadcnui/src/accordion.tsx
@@ -0,0 +1,56 @@
+import * as React from "react"
+import * as AccordionPrimitive from "@radix-ui/react-accordion"
+import { ChevronDown } from "lucide-react"
+
+import { cn } from "@kbve/shadcnutils"
+
+const Accordion = AccordionPrimitive.Root
+
+const AccordionItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AccordionItem.displayName = "AccordionItem"
+
+const AccordionTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ svg]:rotate-180",
+ className
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+))
+AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
+
+const AccordionContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+