diff --git a/Coolui v3 test/.browserslistrc b/Coolui v3 test/.browserslistrc
new file mode 100644
index 0000000000..3e5809a308
--- /dev/null
+++ b/Coolui v3 test/.browserslistrc
@@ -0,0 +1,11 @@
+# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
+# For additional information regarding the format and rule options, please see:
+# https://github.com/browserslist/browserslist#queries
+# You can see what browsers were selected by your queries by running:
+# npx browserslist
+
+last 1 Chrome version
+last 1 Firefox version
+last 1 Edge major versions
+last 2 Safari major versions
+last 2 iOS major versions
diff --git a/Coolui v3 test/.editorconfig b/Coolui v3 test/.editorconfig
new file mode 100644
index 0000000000..0792692308
--- /dev/null
+++ b/Coolui v3 test/.editorconfig
@@ -0,0 +1,16 @@
+# Editor configuration, see https://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 4
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.ts]
+quote_type = single
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false
diff --git a/Coolui v3 test/.gitignore b/Coolui v3 test/.gitignore
new file mode 100644
index 0000000000..154341fb13
--- /dev/null
+++ b/Coolui v3 test/.gitignore
@@ -0,0 +1,31 @@
+/dist
+/tmp
+/out-tsc
+/node_modules
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+.history/*
+/.sass-cache
+/connect.lock
+/coverage
+*.log
+.git
+.DS_Store
+Thumbs.db
+
+# Nitro
+/build
+*.zip
+.env
+public/renderer-config*
+public/ui-config*
diff --git a/Coolui v3 test/README.md b/Coolui v3 test/README.md
new file mode 100644
index 0000000000..ebdb05ca6b
--- /dev/null
+++ b/Coolui v3 test/README.md
@@ -0,0 +1,55 @@
+# v2.2.0 - Cool UI Beta !! Use at Own Risk as it is still in Beta !!
+
+## Prerequisites
+
+- [Git](https://git-scm.com/)
+- [NodeJS](https://nodejs.org/) >= 18
+ - If using NodeJS < 18 remove `--openssl-legacy-provider` from the package.json scripts
+- [Yarn](https://yarnpkg.com/) `npm i yarn -g`
+
+## Installation
+
+- First you should open terminal and navigate to the folder where you want to clone Nitro and Nitro-Renderer
+- Clone Nitro (Expl. C:\Github\)
+ - `git clone https://github.com/duckietm/Nitro-Cool-UI.git` <== For now switch to Dev-RendererV2
+ - `git clone https://github.com/duckietm/Nitro-Cool-UI-Renderer.git`
+ - Install the dependencies for the renderer : cd C:\Github\Nitro-Cool-UI-Renderer
+ - `yarn install`
+ - Now we will create a Link for the CoolUI : `yarn link` This will give you a link address `yarn link "@nitrots/nitro-renderer"`
+ - Install the dependencies for Cool UI : cd C:\Github\Nitro-Cool-UI
+ - `yarn install`
+ - `yarn link "@nitrots/nitro-renderer` <== This will link the renderer in the project
+- Rename a few files
+ - Rename `public/renderer-config.json.example` to `public/renderer-config.json`
+ - Rename `public/ui-config.json.example` to `public/ui-config.json`
+- Set your links
+ - Open `public/renderer-config.json`
+ - Update `socket.url, asset.url, image.library.url, & hof.furni.url`
+ - Open `public/ui-config.json`
+ - Update `camera.url, thumbnails.url, url.prefix, habbopages.url`
+ - `yarn build` <== the final step to build the DIST folder this is where your browser needs to point / or upload this to your /client if you do the compile on a other machine (preferd)
+ - You can override any variable by passing it to `NitroConfig` in the index.html
+
+## Usage
+
+- To use Nitro you need `.nitro` assets generated, see [nitro-converter](https://git.krews.org/nitro/nitro-converter) for instructions
+- See [Morningstar Websockets](https://git.krews.org/nitro/ms-websockets) for instructions on configuring websockets on your server
+
+### Development
+
+Run Nitro in development mode when you are editing the files, this way you can see the changes in your browser instantly
+
+```
+yarn start
+```
+
+### Production
+
+To build a production version of Nitro just run the following command
+
+```
+yarn build:prod
+```
+
+- A `dist` folder will be generated, these are the files that must be uploaded to your webserver
+- Consult your CMS documentation for compatibility with Nitro and how to add the production files
diff --git a/Coolui v3 test/css-utils/CSSColorUtils.js b/Coolui v3 test/css-utils/CSSColorUtils.js
new file mode 100644
index 0000000000..77077fc1b4
--- /dev/null
+++ b/Coolui v3 test/css-utils/CSSColorUtils.js
@@ -0,0 +1,73 @@
+const lightenHexColor = (hex, percent) =>
+{
+// Remove the hash symbol if present
+ hex = hex.replace(/^#/, '');
+
+ // Convert hex to RGB
+ let r = parseInt(hex.substring(0, 2), 16);
+ let g = parseInt(hex.substring(2, 4), 16);
+ let b = parseInt(hex.substring(4, 6), 16);
+
+ // Adjust RGB values
+ r = Math.round(Math.min(255, r + 255 * percent));
+ g = Math.round(Math.min(255, g + 255 * percent));
+ b = Math.round(Math.min(255, b + 255 * percent));
+
+ // Convert RGB back to hex
+ const result = ((r << 16) | (g << 8) | b).toString(16);
+
+ // Make sure result has 6 digits
+ return '#' + result.padStart(6, '0');
+}
+
+const darkenHexColor = (hex, percent) =>
+{
+ // Remove the hash symbol if present
+ hex = hex.replace(/^#/, '');
+
+ // Convert hex to RGB
+ let r = parseInt(hex.substring(0, 2), 16);
+ let g = parseInt(hex.substring(2, 4), 16);
+ let b = parseInt(hex.substring(4, 6), 16);
+
+ // Calculate the darkened RGB values
+ r = Math.round(Math.max(0, r - 255 * percent));
+ g = Math.round(Math.max(0, g - 255 * percent));
+ b = Math.round(Math.max(0, b - 255 * percent));
+
+ // Convert RGB back to hex
+ const result = ((r << 16) | (g << 8) | b).toString(16);
+
+ // Make sure result has 6 digits
+ return '#' + result.padStart(6, '0');
+};
+
+
+const generateShades = (colors) =>
+{
+ for (let color in colors)
+ {
+ let hex = colors[color]
+ let extended = {}
+ const shades = [ 50, 100, 200, 300, 400, 500, 600, 700, 900, 950 ];
+
+ for (let i = 0; i < shades.length; i++)
+ {
+ let shade = shades[i];
+ extended[shade] = lightenHexColor(hex, shades[(shades.length - 1 - i) ] / 950);
+ extended[-shade] = darkenHexColor(hex, shades[(shades.length - 1 - i) ] / 950)
+ }
+
+ colors[color] = {
+ DEFAULT: hex,
+ ...extended
+ }
+ }
+
+ return colors;
+}
+
+module.exports = {
+ generateShades,
+ lightenHexColor
+}
diff --git a/Coolui v3 test/eslint.config.mjs b/Coolui v3 test/eslint.config.mjs
new file mode 100644
index 0000000000..108a976b99
--- /dev/null
+++ b/Coolui v3 test/eslint.config.mjs
@@ -0,0 +1,138 @@
+import typescriptEslintPlugin from "@typescript-eslint/eslint-plugin";
+import typescriptEslintParser from "@typescript-eslint/parser";
+import reactPlugin from "eslint-plugin-react";
+import reactHooksPlugin from "eslint-plugin-react-hooks";
+import path from "path";
+import { fileURLToPath } from "url";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+export default [
+ {
+ files: ["**/*.jsx", "**/*.js", "**/*.tsx", "**/*.ts"],
+ plugins: {
+ react: reactPlugin,
+ "react-hooks": reactHooksPlugin,
+ "@typescript-eslint": typescriptEslintPlugin,
+ },
+ languageOptions: {
+ parser: typescriptEslintParser,
+ ecmaVersion: "latest",
+ parserOptions: {
+ sourceType: "module",
+ project: "./tsconfig.json",
+ tsconfigRootDir: __dirname,
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ },
+ rules: {
+ ...reactPlugin.configs.recommended.rules,
+ ...reactHooksPlugin.configs.recommended.rules,
+ ...typescriptEslintPlugin.configs.recommended.rules,
+ ...typescriptEslintPlugin.configs[
+ "recommended-requiring-type-checking"
+ ].rules,
+ 'indent': [
+ 'error',
+ 4,
+ {
+ 'SwitchCase': 1
+ }
+ ],
+ 'no-multi-spaces': [
+ 'error'
+ ],
+ 'no-trailing-spaces': [
+ 'error',
+ {
+ 'skipBlankLines': false,
+ 'ignoreComments': true
+ }
+ ],
+ 'linebreak-style': [
+ 'off'
+ ],
+ 'quotes': [
+ 'error',
+ 'single'
+ ],
+ 'semi': [
+ 'error',
+ 'always'
+ ],
+ 'brace-style': [
+ 'error',
+ 'allman'
+ ],
+ 'object-curly-spacing': [
+ 'error',
+ 'always'
+ ],
+ '@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/no-unsafe-assignment': 'off',
+ '@typescript-eslint/no-unsafe-call': 'off',
+ '@typescript-eslint/no-unsafe-member-access': 'off',
+ '@typescript-eslint/no-floating-promises': 'off',
+ '@typescript-eslint/require-await': 'off',
+ '@typescript-eslint/no-unsafe-argument': 'off',
+ '@typescript-eslint/no-unsafe-return': 'off',
+ '@typescript-eslint/no-misused-promises': 'off',
+ '@typescript-eslint/explicit-module-boundary-types': [
+ 'off',
+ {
+ 'allowedNames': [
+ 'getMessageArray'
+ ]
+ }
+ ],
+ '@typescript-eslint/unbound-method': [
+ 'off'
+ ],
+ '@typescript-eslint/ban-ts-comment': [
+ 'off'
+ ],
+ '@typescript-eslint/no-empty-function': [
+ 'error',
+ {
+ 'allow': [
+ 'functions',
+ 'arrowFunctions',
+ 'generatorFunctions',
+ 'methods',
+ 'generatorMethods',
+ 'constructors'
+ ]
+ }
+ ],
+ '@typescript-eslint/no-unused-vars': [
+ 'off'
+ ],
+ '@typescript-eslint/ban-types': [
+ 'error',
+ {
+ 'types':
+ {
+ 'String': true,
+ 'Boolean': true,
+ 'Number': true,
+ 'Symbol': true,
+ '{}': false,
+ 'Object': false,
+ 'object': false,
+ 'Function': false
+ },
+ 'extendDefaults': true
+ }
+ ],
+ 'react/react-in-jsx-scope': 'off'
+ },
+ settings: {
+ react: {
+ version: "18.3.1",
+ },
+ },
+ },
+];
diff --git a/Coolui v3 test/index.html b/Coolui v3 test/index.html
new file mode 100644
index 0000000000..e3f0ecf0df
--- /dev/null
+++ b/Coolui v3 test/index.html
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Nitro
+
+
+
+
+
+
+
+
diff --git a/Coolui v3 test/package.json b/Coolui v3 test/package.json
new file mode 100644
index 0000000000..01d45906ac
--- /dev/null
+++ b/Coolui v3 test/package.json
@@ -0,0 +1,49 @@
+{
+ "name": "nitro-react",
+ "version": "2.2",
+ "homepage": ".",
+ "private": true,
+ "scripts": {
+ "start": "vite --host",
+ "build": "vite build",
+ "build:prod": "npx browserslist@latest --update-db && yarn build",
+ "eslint": "eslint ./src"
+ },
+ "dependencies": {
+ "@babel/runtime": "^7.26.9",
+ "@tanstack/react-virtual": "3.2.0",
+ "@types/react-transition-group": "^4.4.10",
+ "dompurify": "^3.1.5",
+ "framer-motion": "^11.2.12",
+ "react": "^18.3.1",
+ "react-bootstrap": "^2.10.9",
+ "react-dom": "^18.3.1",
+ "react-icons": "^5.2.1",
+ "react-slider": "^2.0.6",
+ "react-tiny-popover": "^8.0.4",
+ "react-youtube": "^7.13.1",
+ "use-between": "^1.3.5"
+ },
+ "devDependencies": {
+ "@tailwindcss/forms": "^0.5.7",
+ "@types/node": "^20.11.30",
+ "@types/react": "^18.3.3",
+ "@types/react-dom": "^18.3.0",
+ "@types/react-slider": "^1.3.6",
+ "@typescript-eslint/eslint-plugin": "^7.13.1",
+ "@typescript-eslint/parser": "^7.13.1",
+ "@vitejs/plugin-react": "^4.3.1",
+ "autoprefixer": "^10.4.19",
+ "eslint": "^9.5.0",
+ "eslint-plugin-react": "^7.34.2",
+ "eslint-plugin-react-hooks": "^5.1.0-rc-1434af3d22-20240618",
+ "postcss": "^8.4.38",
+ "postcss-nested": "^6.0.1",
+ "sass": "^1.77.4",
+ "tailwindcss": "^3.4.4",
+ "typescript": "^5.4.5",
+ "typescript-eslint": "^7.13.1",
+ "vite": "^5.2.13",
+ "vite-tsconfig-paths": "^4.3.2"
+ }
+}
diff --git a/Coolui v3 test/postcss.config.js b/Coolui v3 test/postcss.config.js
new file mode 100644
index 0000000000..9855208474
--- /dev/null
+++ b/Coolui v3 test/postcss.config.js
@@ -0,0 +1,8 @@
+/** @type {import("postcss-load-config").Config} */
+
+module.exports = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {}
+ }
+}
diff --git a/Coolui v3 test/public/android-chrome-192x192.png b/Coolui v3 test/public/android-chrome-192x192.png
new file mode 100644
index 0000000000..634eb063ec
Binary files /dev/null and b/Coolui v3 test/public/android-chrome-192x192.png differ
diff --git a/Coolui v3 test/public/android-chrome-512x512.png b/Coolui v3 test/public/android-chrome-512x512.png
new file mode 100644
index 0000000000..33cf6c6ab1
Binary files /dev/null and b/Coolui v3 test/public/android-chrome-512x512.png differ
diff --git a/Coolui v3 test/public/apple-touch-icon.png b/Coolui v3 test/public/apple-touch-icon.png
new file mode 100644
index 0000000000..349b3dd3f5
Binary files /dev/null and b/Coolui v3 test/public/apple-touch-icon.png differ
diff --git a/Coolui v3 test/public/browserconfig.xml b/Coolui v3 test/public/browserconfig.xml
new file mode 100644
index 0000000000..b3930d0f04
--- /dev/null
+++ b/Coolui v3 test/public/browserconfig.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+ #da532c
+
+
+
diff --git a/Coolui v3 test/public/favicon-16x16.png b/Coolui v3 test/public/favicon-16x16.png
new file mode 100644
index 0000000000..788a7f12b4
Binary files /dev/null and b/Coolui v3 test/public/favicon-16x16.png differ
diff --git a/Coolui v3 test/public/favicon-32x32.png b/Coolui v3 test/public/favicon-32x32.png
new file mode 100644
index 0000000000..ef4a6061d0
Binary files /dev/null and b/Coolui v3 test/public/favicon-32x32.png differ
diff --git a/Coolui v3 test/public/favicon.ico b/Coolui v3 test/public/favicon.ico
new file mode 100644
index 0000000000..34ba1b3d4a
Binary files /dev/null and b/Coolui v3 test/public/favicon.ico differ
diff --git a/Coolui v3 test/public/mstile-150x150.png b/Coolui v3 test/public/mstile-150x150.png
new file mode 100644
index 0000000000..3a555aa561
Binary files /dev/null and b/Coolui v3 test/public/mstile-150x150.png differ
diff --git a/Coolui v3 test/public/robots.txt b/Coolui v3 test/public/robots.txt
new file mode 100644
index 0000000000..e9e57dc4d4
--- /dev/null
+++ b/Coolui v3 test/public/robots.txt
@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:
diff --git a/Coolui v3 test/public/safari-pinned-tab.svg b/Coolui v3 test/public/safari-pinned-tab.svg
new file mode 100644
index 0000000000..dc7ced35bb
--- /dev/null
+++ b/Coolui v3 test/public/safari-pinned-tab.svg
@@ -0,0 +1,154 @@
+
+
+
diff --git a/Coolui v3 test/public/site.webmanifest b/Coolui v3 test/public/site.webmanifest
new file mode 100644
index 0000000000..6264b894b3
--- /dev/null
+++ b/Coolui v3 test/public/site.webmanifest
@@ -0,0 +1,20 @@
+{
+ "start_url": "/",
+ "name": "Nitro",
+ "short_name": "Nitro",
+ "icons": [
+ {
+ "src": "android-chrome-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "android-chrome-512x512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ],
+ "theme_color": "#ffffff",
+ "background_color": "#ffffff",
+ "display": "standalone"
+}
diff --git a/Coolui v3 test/src/App.tsx b/Coolui v3 test/src/App.tsx
new file mode 100644
index 0000000000..d3566cc5da
--- /dev/null
+++ b/Coolui v3 test/src/App.tsx
@@ -0,0 +1,99 @@
+import { GetAssetManager, GetAvatarRenderManager, GetCommunication, GetConfiguration, GetLocalizationManager, GetRoomEngine, GetRoomSessionManager, GetSessionDataManager, GetSoundManager, GetStage, GetTexturePool, GetTicker, HabboWebTools, LegacyExternalInterface, LoadGameUrlEvent, NitroLogger, NitroVersion, PrepareRenderer } from '@nitrots/nitro-renderer';
+import { FC, useEffect, useState } from 'react';
+import { GetUIVersion } from './api';
+import { Base } from './common';
+import { LoadingView } from './components/loading/LoadingView';
+import { MainView } from './components/MainView';
+import { useMessageEvent } from './hooks';
+
+NitroVersion.UI_VERSION = GetUIVersion();
+
+export const App: FC<{}> = props =>
+{
+ const [ isReady, setIsReady ] = useState(false);
+
+ useMessageEvent(LoadGameUrlEvent, event =>
+ {
+ const parser = event.getParser();
+
+ if(!parser) return;
+
+ LegacyExternalInterface.callGame('showGame', parser.url);
+ });
+
+ useEffect(() =>
+ {
+ const prepare = async (width: number, height: number) =>
+ {
+ try
+ {
+ if(!window.NitroConfig) throw new Error('NitroConfig is not defined!');
+
+ const renderer = await PrepareRenderer({
+ width: Math.floor(width),
+ height: Math.floor(height),
+ resolution: window.devicePixelRatio,
+ autoDensity: true,
+ backgroundAlpha: 0,
+ preference: 'webgl',
+ eventMode: 'none',
+ failIfMajorPerformanceCaveat: false,
+ roundPixels: true,
+ useBackBuffer: true // Enable back buffer for blend filters
+ });
+
+ await GetConfiguration().init();
+
+ GetTicker().maxFPS = GetConfiguration().getValue('system.fps.max', 24);
+ NitroLogger.LOG_DEBUG = GetConfiguration().getValue('system.log.debug', true);
+ NitroLogger.LOG_WARN = GetConfiguration().getValue('system.log.warn', false);
+ NitroLogger.LOG_ERROR = GetConfiguration().getValue('system.log.error', false);
+ NitroLogger.LOG_EVENTS = GetConfiguration().getValue('system.log.events', false);
+ NitroLogger.LOG_PACKETS = GetConfiguration().getValue('system.log.packets', false);
+
+ const assetUrls = GetConfiguration().getValue('preload.assets.urls').map(url => GetConfiguration().interpolate(url)) ?? [];
+
+ await Promise.all(
+ [
+ GetAssetManager().downloadAssets(assetUrls),
+ GetLocalizationManager().init(),
+ GetAvatarRenderManager().init(),
+ GetSoundManager().init(),
+ GetSessionDataManager().init(),
+ GetRoomSessionManager().init()
+ ]
+ );
+
+ await GetRoomEngine().init();
+ await GetCommunication().init();
+
+ if(LegacyExternalInterface.available) LegacyExternalInterface.call('legacyTrack', 'authentication', 'authok', []);
+
+ HabboWebTools.sendHeartBeat();
+
+ setInterval(() => HabboWebTools.sendHeartBeat(), 10000);
+
+ GetTicker().add(ticker => GetRoomEngine().update(ticker));
+ GetTicker().add(ticker => renderer.render(GetStage()));
+ GetTicker().add(ticker => GetTexturePool().run());
+
+ setIsReady(true);
+ }
+ catch(err)
+ {
+ NitroLogger.error(err);
+ }
+ };
+
+ prepare(window.innerWidth, window.innerHeight);
+ }, []);
+
+ return (
+
+ { !isReady &&
+ }
+ { isReady && }
+
+
+ );
+};
\ No newline at end of file
diff --git a/Coolui v3 test/src/api/GetRendererVersion.ts b/Coolui v3 test/src/api/GetRendererVersion.ts
new file mode 100644
index 0000000000..bb9e461816
--- /dev/null
+++ b/Coolui v3 test/src/api/GetRendererVersion.ts
@@ -0,0 +1,3 @@
+import { NitroVersion } from '@nitrots/nitro-renderer';
+
+export const GetRendererVersion = () => NitroVersion.RENDERER_VERSION;
diff --git a/Coolui v3 test/src/api/GetUIVersion.ts b/Coolui v3 test/src/api/GetUIVersion.ts
new file mode 100644
index 0000000000..bdbe922385
--- /dev/null
+++ b/Coolui v3 test/src/api/GetUIVersion.ts
@@ -0,0 +1 @@
+export const GetUIVersion = () => '2.2.0';
diff --git a/Coolui v3 test/src/api/achievements/AchievementCategory.ts b/Coolui v3 test/src/api/achievements/AchievementCategory.ts
new file mode 100644
index 0000000000..906d8da4ba
--- /dev/null
+++ b/Coolui v3 test/src/api/achievements/AchievementCategory.ts
@@ -0,0 +1,40 @@
+import { AchievementData } from '@nitrots/nitro-renderer';
+import { AchievementUtilities } from './AchievementUtilities';
+import { IAchievementCategory } from './IAchievementCategory';
+
+export class AchievementCategory implements IAchievementCategory
+{
+ private _code: string;
+ private _achievements: AchievementData[];
+
+ constructor(code: string)
+ {
+ this._code = code;
+ this._achievements = [];
+ }
+
+ public getProgress(): number
+ {
+ return AchievementUtilities.getAchievementCategoryProgress(this);
+ }
+
+ public getMaxProgress(): number
+ {
+ return AchievementUtilities.getAchievementCategoryMaxProgress(this);
+ }
+
+ public get code(): string
+ {
+ return this._code;
+ }
+
+ public get achievements(): AchievementData[]
+ {
+ return this._achievements;
+ }
+
+ public set achievements(achievements: AchievementData[])
+ {
+ this._achievements = achievements;
+ }
+}
diff --git a/Coolui v3 test/src/api/achievements/AchievementUtilities.ts b/Coolui v3 test/src/api/achievements/AchievementUtilities.ts
new file mode 100644
index 0000000000..30f14033d5
--- /dev/null
+++ b/Coolui v3 test/src/api/achievements/AchievementUtilities.ts
@@ -0,0 +1,97 @@
+import { AchievementData, GetLocalizationManager } from '@nitrots/nitro-renderer';
+import { GetConfigurationValue } from '../nitro';
+import { IAchievementCategory } from './IAchievementCategory';
+
+export class AchievementUtilities
+{
+ public static getAchievementBadgeCode(achievement: AchievementData): string
+ {
+ if(!achievement) return null;
+
+ let badgeId = achievement.badgeId;
+
+ if(!achievement.finalLevel) badgeId = GetLocalizationManager().getPreviousLevelBadgeId(badgeId);
+
+ return badgeId;
+ }
+
+ public static getAchievementCategoryImageUrl(category: IAchievementCategory, progress: number = null, icon: boolean = false): string
+ {
+ const imageUrl = GetConfigurationValue('achievements.images.url');
+
+ let imageName = icon ? 'achicon_' : 'achcategory_';
+
+ imageName += category.code;
+
+ if(progress !== null) imageName += `_${ ((progress > 0) ? 'active' : 'inactive') }`;
+
+ return imageUrl.replace('%image%', imageName);
+ }
+
+ public static getAchievementCategoryMaxProgress(category: IAchievementCategory): number
+ {
+ if(!category) return 0;
+
+ let progress = 0;
+
+ for(const achievement of category.achievements)
+ {
+ progress += achievement.levelCount;
+ }
+
+ return progress;
+ }
+
+ public static getAchievementCategoryProgress(category: IAchievementCategory): number
+ {
+ if(!category) return 0;
+
+ let progress = 0;
+
+ for(const achievement of category.achievements) progress += (achievement.finalLevel ? achievement.level : (achievement.level - 1));
+
+ return progress;
+ }
+
+ public static getAchievementCategoryTotalUnseen(category: IAchievementCategory): number
+ {
+ if(!category) return 0;
+
+ let unseen = 0;
+
+ for(const achievement of category.achievements) ((achievement.unseen > 0) && unseen++);
+
+ return unseen;
+ }
+
+ public static getAchievementHasStarted(achievement: AchievementData): boolean
+ {
+ if(!achievement) return false;
+
+ if(achievement.finalLevel || ((achievement.level - 1) > 0)) return true;
+
+ return false;
+ }
+
+ public static getAchievementIsIgnored(achievement: AchievementData): boolean
+ {
+ if(!achievement) return false;
+
+ const ignored = GetConfigurationValue('achievements.unseen.ignored');
+ const value = achievement.badgeId.replace(/[0-9]/g, '');
+ const index = ignored.indexOf(value);
+
+ if(index >= 0) return true;
+
+ return false;
+ }
+
+ public static getAchievementLevel(achievement: AchievementData): number
+ {
+ if(!achievement) return 0;
+
+ if(achievement.finalLevel) return achievement.level;
+
+ return (achievement.level - 1);
+ }
+}
diff --git a/Coolui v3 test/src/api/achievements/IAchievementCategory.ts b/Coolui v3 test/src/api/achievements/IAchievementCategory.ts
new file mode 100644
index 0000000000..a049d464ae
--- /dev/null
+++ b/Coolui v3 test/src/api/achievements/IAchievementCategory.ts
@@ -0,0 +1,7 @@
+import { AchievementData } from '@nitrots/nitro-renderer';
+
+export interface IAchievementCategory
+{
+ code: string;
+ achievements: AchievementData[];
+}
diff --git a/Coolui v3 test/src/api/achievements/index.ts b/Coolui v3 test/src/api/achievements/index.ts
new file mode 100644
index 0000000000..a3d44b73fb
--- /dev/null
+++ b/Coolui v3 test/src/api/achievements/index.ts
@@ -0,0 +1,3 @@
+export * from './AchievementCategory';
+export * from './AchievementUtilities';
+export * from './IAchievementCategory';
diff --git a/Coolui v3 test/src/api/avatar/AvatarEditorAction.ts b/Coolui v3 test/src/api/avatar/AvatarEditorAction.ts
new file mode 100644
index 0000000000..064d6dffcb
--- /dev/null
+++ b/Coolui v3 test/src/api/avatar/AvatarEditorAction.ts
@@ -0,0 +1,7 @@
+export class AvatarEditorAction
+{
+ public static ACTION_SAVE: string = 'AEA_ACTION_SAVE';
+ public static ACTION_CLEAR: string = 'AEA_ACTION_CLEAR';
+ public static ACTION_RESET: string = 'AEA_ACTION_RESET';
+ public static ACTION_RANDOMIZE: string = 'AEA_ACTION_RANDOMIZE';
+}
diff --git a/Coolui v3 test/src/api/avatar/AvatarEditorColorSorter.ts b/Coolui v3 test/src/api/avatar/AvatarEditorColorSorter.ts
new file mode 100644
index 0000000000..7ae5960b4f
--- /dev/null
+++ b/Coolui v3 test/src/api/avatar/AvatarEditorColorSorter.ts
@@ -0,0 +1,17 @@
+import { IPartColor } from '@nitrots/nitro-renderer';
+
+export const AvatarEditorColorSorter = (a: IPartColor, b: IPartColor) =>
+{
+ const clubLevelA = (!a ? -1 : a.clubLevel);
+ const clubLevelB = (!b ? -1 : b.clubLevel);
+
+ if(clubLevelA < clubLevelB) return -1;
+
+ if(clubLevelA > clubLevelB) return 1;
+
+ if(a.index < b.index) return -1;
+
+ if(a.index > b.index) return 1;
+
+ return 0;
+};
diff --git a/Coolui v3 test/src/api/avatar/AvatarEditorPartSorter.ts b/Coolui v3 test/src/api/avatar/AvatarEditorPartSorter.ts
new file mode 100644
index 0000000000..41e98d85c6
--- /dev/null
+++ b/Coolui v3 test/src/api/avatar/AvatarEditorPartSorter.ts
@@ -0,0 +1,35 @@
+import { IFigurePartSet } from '@nitrots/nitro-renderer';
+
+export const AvatarEditorPartSorter = (hcFirst: boolean) =>
+{
+ return (a: { partSet: IFigurePartSet, usesColor: boolean, isClear?: boolean }, b: { partSet: IFigurePartSet, usesColor: boolean, isClear?: boolean }) =>
+ {
+ const clubLevelA = (!a.partSet ? -1 : a.partSet.clubLevel);
+ const clubLevelB = (!b.partSet ? -1 : b.partSet.clubLevel);
+ const isSellableA = (!a.partSet ? false : a.partSet.isSellable);
+ const isSellableB = (!b.partSet ? false : b.partSet.isSellable);
+
+ if(isSellableA && !isSellableB) return 1;
+
+ if(isSellableB && !isSellableA) return -1;
+
+ if(hcFirst)
+ {
+ if(clubLevelA > clubLevelB) return -1;
+
+ if(clubLevelA < clubLevelB) return 1;
+ }
+ else
+ {
+ if(clubLevelA < clubLevelB) return -1;
+
+ if(clubLevelA > clubLevelB) return 1;
+ }
+
+ if(a.partSet.id < b.partSet.id) return -1;
+
+ if(a.partSet.id > b.partSet.id) return 1;
+
+ return 0;
+ };
+};
diff --git a/Coolui v3 test/src/api/avatar/AvatarEditorThumbnailsHelper.ts b/Coolui v3 test/src/api/avatar/AvatarEditorThumbnailsHelper.ts
new file mode 100644
index 0000000000..88a7906314
--- /dev/null
+++ b/Coolui v3 test/src/api/avatar/AvatarEditorThumbnailsHelper.ts
@@ -0,0 +1,196 @@
+import { AvatarFigurePartType, AvatarScaleType, AvatarSetType, GetAssetManager, GetAvatarRenderManager, IFigurePart, IGraphicAsset, IPartColor, NitroAlphaFilter, NitroContainer, NitroRectangle, NitroSprite, TextureUtils } from '@nitrots/nitro-renderer';
+import { IAvatarEditorCategoryPartItem } from './IAvatarEditorCategoryPartItem';
+
+export class AvatarEditorThumbnailsHelper
+{
+ private static THUMBNAIL_CACHE: Map = new Map();
+ private static THUMB_DIRECTIONS: number[] = [ 2, 6, 0, 4, 3, 1 ];
+ private static ALPHA_FILTER: NitroAlphaFilter = new NitroAlphaFilter({ alpha: 0.2 });
+ private static DRAW_ORDER: string[] = [
+ AvatarFigurePartType.LEFT_HAND_ITEM,
+ AvatarFigurePartType.LEFT_HAND,
+ AvatarFigurePartType.LEFT_SLEEVE,
+ AvatarFigurePartType.LEFT_COAT_SLEEVE,
+ AvatarFigurePartType.BODY,
+ AvatarFigurePartType.SHOES,
+ AvatarFigurePartType.LEGS,
+ AvatarFigurePartType.CHEST,
+ AvatarFigurePartType.CHEST_ACCESSORY,
+ AvatarFigurePartType.COAT_CHEST,
+ AvatarFigurePartType.CHEST_PRINT,
+ AvatarFigurePartType.WAIST_ACCESSORY,
+ AvatarFigurePartType.RIGHT_HAND,
+ AvatarFigurePartType.RIGHT_SLEEVE,
+ AvatarFigurePartType.RIGHT_COAT_SLEEVE,
+ AvatarFigurePartType.HEAD,
+ AvatarFigurePartType.FACE,
+ AvatarFigurePartType.EYES,
+ AvatarFigurePartType.HAIR,
+ AvatarFigurePartType.HAIR_BIG,
+ AvatarFigurePartType.FACE_ACCESSORY,
+ AvatarFigurePartType.EYE_ACCESSORY,
+ AvatarFigurePartType.HEAD_ACCESSORY,
+ AvatarFigurePartType.HEAD_ACCESSORY_EXTRA,
+ AvatarFigurePartType.RIGHT_HAND_ITEM,
+ ];
+
+ private static getThumbnailKey(setType: string, part: IAvatarEditorCategoryPartItem): string
+ {
+ return `${ setType }-${ part.partSet.id }`;
+ }
+
+ public static clearCache(): void
+ {
+ this.THUMBNAIL_CACHE.clear();
+ }
+
+ public static async build(setType: string, part: IAvatarEditorCategoryPartItem, useColors: boolean, partColors: IPartColor[], isDisabled: boolean = false): Promise
+ {
+ if(!setType || !setType.length || !part || !part.partSet || !part.partSet.parts || !part.partSet.parts.length) return null;
+
+ const thumbnailKey = this.getThumbnailKey(setType, part);
+ const cached = this.THUMBNAIL_CACHE.get(thumbnailKey);
+
+ if(cached) return cached;
+
+ const buildContainer = (part: IAvatarEditorCategoryPartItem, useColors: boolean, partColors: IPartColor[], isDisabled: boolean = false) =>
+ {
+ const container = new NitroContainer();
+ const parts = part.partSet.parts.concat().sort(this.sortByDrawOrder);
+
+ for(const part of parts)
+ {
+ if(!part) continue;
+
+ let asset: IGraphicAsset = null;
+ let direction = 0;
+ let hasAsset = false;
+
+ while(!hasAsset && (direction < AvatarEditorThumbnailsHelper.THUMB_DIRECTIONS.length))
+ {
+ const assetName = `${ AvatarFigurePartType.SCALE }_${ AvatarFigurePartType.STD }_${ part.type }_${ part.id }_${ AvatarEditorThumbnailsHelper.THUMB_DIRECTIONS[direction] }_${ AvatarFigurePartType.DEFAULT_FRAME }`;
+
+ asset = GetAssetManager().getAsset(assetName);
+
+ if(asset && asset.texture)
+ {
+ hasAsset = true;
+ }
+ else
+ {
+ direction++;
+ }
+ }
+
+ if(!hasAsset)
+ {
+ console.log(`${ AvatarFigurePartType.SCALE }_${ AvatarFigurePartType.STD }_${ part.type }_${ part.id }`);
+ continue;
+ }
+
+ const x = asset.offsetX;
+ const y = asset.offsetY;
+
+ const sprite = new NitroSprite(asset.texture);
+
+ sprite.position.set(x, y);
+
+ if(useColors && (part.colorLayerIndex > 0) && partColors && partColors.length)
+ {
+ const color = partColors[(part.colorLayerIndex - 1)];
+
+ if(color) sprite.tint = color.rgb;
+ }
+
+ if(isDisabled) container.filters = [ AvatarEditorThumbnailsHelper.ALPHA_FILTER ];
+
+ container.addChild(sprite);
+ }
+
+ return container;
+ };
+
+ return new Promise(async (resolve, reject) =>
+ {
+ const resetFigure = async (figure: string) =>
+ {
+ const container = buildContainer(part, useColors, partColors, isDisabled);
+ const imageUrl = await TextureUtils.generateImageUrl(container);
+
+ AvatarEditorThumbnailsHelper.THUMBNAIL_CACHE.set(thumbnailKey, imageUrl);
+
+ resolve(imageUrl);
+ };
+
+ const figureContainer = GetAvatarRenderManager().createFigureContainer(`${ setType }-${ part.partSet.id }`);
+
+ if(!GetAvatarRenderManager().isFigureContainerReady(figureContainer))
+ {
+ GetAvatarRenderManager().downloadAvatarFigure(figureContainer, {
+ resetFigure,
+ dispose: null,
+ disposed: false
+ });
+ }
+ else
+ {
+ resetFigure(null);
+ }
+ });
+ }
+
+ public static async buildForFace(figureString: string, isDisabled: boolean = false): Promise
+ {
+ if(!figureString || !figureString.length) return null;
+
+ const thumbnailKey = figureString;
+ const cached = this.THUMBNAIL_CACHE.get(thumbnailKey);
+
+ if(cached) return cached;
+
+ return new Promise(async (resolve, reject) =>
+ {
+ const resetFigure = async (figure: string) =>
+ {
+ const avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, null, { resetFigure, dispose: null, disposed: false });
+
+ if(avatarImage.isPlaceholder()) return;
+
+ const texture = avatarImage.processAsTexture(AvatarSetType.HEAD, false);
+ const sprite = new NitroSprite(texture);
+
+ if(isDisabled) sprite.filters = [ AvatarEditorThumbnailsHelper.ALPHA_FILTER ];
+
+ const imageUrl = await TextureUtils.generateImageUrl({
+ target: sprite,
+ frame: new NitroRectangle(0, 0, texture.width, texture.height)
+ });
+
+ sprite.destroy();
+ avatarImage.dispose();
+
+ AvatarEditorThumbnailsHelper.THUMBNAIL_CACHE.set(thumbnailKey, imageUrl);
+
+ resolve(imageUrl);
+ };
+
+ resetFigure(figureString);
+ });
+ }
+
+ private static sortByDrawOrder(a: IFigurePart, b: IFigurePart): number
+ {
+ const indexA = AvatarEditorThumbnailsHelper.DRAW_ORDER.indexOf(a.type);
+ const indexB = AvatarEditorThumbnailsHelper.DRAW_ORDER.indexOf(b.type);
+
+ if(indexA < indexB) return -1;
+
+ if(indexA > indexB) return 1;
+
+ if(a.index < b.index) return -1;
+
+ if(a.index > b.index) return 1;
+
+ return 0;
+ }
+}
diff --git a/Coolui v3 test/src/api/avatar/IAvatarEditorCategory.ts b/Coolui v3 test/src/api/avatar/IAvatarEditorCategory.ts
new file mode 100644
index 0000000000..a7cfd51163
--- /dev/null
+++ b/Coolui v3 test/src/api/avatar/IAvatarEditorCategory.ts
@@ -0,0 +1,9 @@
+import { IPartColor } from '@nitrots/nitro-renderer';
+import { IAvatarEditorCategoryPartItem } from './IAvatarEditorCategoryPartItem';
+
+export interface IAvatarEditorCategory
+{
+ setType: string;
+ partItems: IAvatarEditorCategoryPartItem[];
+ colorItems: IPartColor[][];
+}
diff --git a/Coolui v3 test/src/api/avatar/IAvatarEditorCategoryPartItem.ts b/Coolui v3 test/src/api/avatar/IAvatarEditorCategoryPartItem.ts
new file mode 100644
index 0000000000..d1cbc0dbac
--- /dev/null
+++ b/Coolui v3 test/src/api/avatar/IAvatarEditorCategoryPartItem.ts
@@ -0,0 +1,10 @@
+import { IFigurePartSet } from '@nitrots/nitro-renderer';
+
+export interface IAvatarEditorCategoryPartItem
+{
+ id?: number;
+ partSet?: IFigurePartSet;
+ usesColor?: boolean;
+ maxPaletteCount?: number;
+ isClear?: boolean;
+}
diff --git a/Coolui v3 test/src/api/avatar/index.ts b/Coolui v3 test/src/api/avatar/index.ts
new file mode 100644
index 0000000000..415185e940
--- /dev/null
+++ b/Coolui v3 test/src/api/avatar/index.ts
@@ -0,0 +1,6 @@
+export * from './AvatarEditorAction';
+export * from './AvatarEditorColorSorter';
+export * from './AvatarEditorPartSorter';
+export * from './AvatarEditorThumbnailsHelper';
+export * from './IAvatarEditorCategory';
+export * from './IAvatarEditorCategoryPartItem';
diff --git a/Coolui v3 test/src/api/camera/CameraEditorTabs.ts b/Coolui v3 test/src/api/camera/CameraEditorTabs.ts
new file mode 100644
index 0000000000..6e894e740c
--- /dev/null
+++ b/Coolui v3 test/src/api/camera/CameraEditorTabs.ts
@@ -0,0 +1,5 @@
+export class CameraEditorTabs
+{
+ public static readonly COLORMATRIX: string = 'colormatrix';
+ public static readonly COMPOSITE: string = 'composite';
+}
diff --git a/Coolui v3 test/src/api/camera/CameraPicture.ts b/Coolui v3 test/src/api/camera/CameraPicture.ts
new file mode 100644
index 0000000000..020e1ec563
--- /dev/null
+++ b/Coolui v3 test/src/api/camera/CameraPicture.ts
@@ -0,0 +1,9 @@
+import { NitroTexture } from '@nitrots/nitro-renderer';
+
+export class CameraPicture
+{
+ constructor(
+ public texture: NitroTexture,
+ public imageUrl: string)
+ {}
+}
diff --git a/Coolui v3 test/src/api/camera/CameraPictureThumbnail.ts b/Coolui v3 test/src/api/camera/CameraPictureThumbnail.ts
new file mode 100644
index 0000000000..3e3f78252f
--- /dev/null
+++ b/Coolui v3 test/src/api/camera/CameraPictureThumbnail.ts
@@ -0,0 +1,7 @@
+export class CameraPictureThumbnail
+{
+ constructor(
+ public effectName: string,
+ public thumbnailUrl: string)
+ {}
+}
diff --git a/Coolui v3 test/src/api/camera/index.ts b/Coolui v3 test/src/api/camera/index.ts
new file mode 100644
index 0000000000..93c6ccb9a1
--- /dev/null
+++ b/Coolui v3 test/src/api/camera/index.ts
@@ -0,0 +1,3 @@
+export * from './CameraEditorTabs';
+export * from './CameraPicture';
+export * from './CameraPictureThumbnail';
diff --git a/Coolui v3 test/src/api/campaign/CalendarItem.ts b/Coolui v3 test/src/api/campaign/CalendarItem.ts
new file mode 100644
index 0000000000..d3634b3dd7
--- /dev/null
+++ b/Coolui v3 test/src/api/campaign/CalendarItem.ts
@@ -0,0 +1,30 @@
+import { ICalendarItem } from './ICalendarItem';
+
+export class CalendarItem implements ICalendarItem
+{
+ private _productName: string;
+ private _customImage: string;
+ private _furnitureClassName: string;
+
+ constructor(productName: string, customImage: string, furnitureClassName: string)
+ {
+ this._productName = productName;
+ this._customImage = customImage;
+ this._furnitureClassName = furnitureClassName;
+ }
+
+ public get productName(): string
+ {
+ return this._productName;
+ }
+
+ public get customImage(): string
+ {
+ return this._customImage;
+ }
+
+ public get furnitureClassName(): string
+ {
+ return this._furnitureClassName;
+ }
+}
diff --git a/Coolui v3 test/src/api/campaign/CalendarItemState.ts b/Coolui v3 test/src/api/campaign/CalendarItemState.ts
new file mode 100644
index 0000000000..1b91ca3ff7
--- /dev/null
+++ b/Coolui v3 test/src/api/campaign/CalendarItemState.ts
@@ -0,0 +1,7 @@
+export class CalendarItemState
+{
+ public static readonly STATE_UNLOCKED = 1;
+ public static readonly STATE_LOCKED_AVAILABLE = 2;
+ public static readonly STATE_LOCKED_EXPIRED = 3;
+ public static readonly STATE_LOCKED_FUTURE = 4;
+}
diff --git a/Coolui v3 test/src/api/campaign/ICalendarItem.ts b/Coolui v3 test/src/api/campaign/ICalendarItem.ts
new file mode 100644
index 0000000000..87dfbd6dc6
--- /dev/null
+++ b/Coolui v3 test/src/api/campaign/ICalendarItem.ts
@@ -0,0 +1,6 @@
+export interface ICalendarItem
+{
+ readonly productName: string;
+ readonly customImage: string;
+ readonly furnitureClassName: string;
+}
diff --git a/Coolui v3 test/src/api/campaign/index.ts b/Coolui v3 test/src/api/campaign/index.ts
new file mode 100644
index 0000000000..a86e40c424
--- /dev/null
+++ b/Coolui v3 test/src/api/campaign/index.ts
@@ -0,0 +1,3 @@
+export * from './CalendarItem';
+export * from './CalendarItemState';
+export * from './ICalendarItem';
diff --git a/Coolui v3 test/src/api/catalog/BuilderFurniPlaceableStatus.ts b/Coolui v3 test/src/api/catalog/BuilderFurniPlaceableStatus.ts
new file mode 100644
index 0000000000..40eb6f65c5
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/BuilderFurniPlaceableStatus.ts
@@ -0,0 +1,10 @@
+export class BuilderFurniPlaceableStatus
+{
+ public static OKAY: number = 0;
+ public static MISSING_OFFER: number = 1;
+ public static FURNI_LIMIT_REACHED: number = 2;
+ public static NOT_IN_ROOM: number = 3;
+ public static NOT_ROOM_OWNER: number = 4;
+ public static GUILD_ROOM: number = 5;
+ public static VISITORS_IN_ROOM: number = 6;
+}
diff --git a/Coolui v3 test/src/api/catalog/CatalogNode.ts b/Coolui v3 test/src/api/catalog/CatalogNode.ts
new file mode 100644
index 0000000000..5e7c2fc35f
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/CatalogNode.ts
@@ -0,0 +1,124 @@
+import { NodeData } from '@nitrots/nitro-renderer';
+import { ICatalogNode } from './ICatalogNode';
+
+export class CatalogNode implements ICatalogNode
+{
+ private _depth: number = 0;
+ private _localization: string = '';
+ private _pageId: number = -1;
+ private _pageName: string = '';
+ private _iconId: number = 0;
+ private _children: ICatalogNode[];
+ private _offerIds: number[];
+ private _parent: ICatalogNode;
+ private _isVisible: boolean;
+ private _isActive: boolean;
+ private _isOpen: boolean;
+
+ constructor(node: NodeData, depth: number, parent: ICatalogNode)
+ {
+ this._depth = depth;
+ this._parent = parent;
+ this._localization = node.localization;
+ this._pageId = node.pageId;
+ this._pageName = node.pageName;
+ this._iconId = node.icon;
+ this._children = [];
+ this._offerIds = node.offerIds;
+ this._isVisible = node.visible;
+ this._isActive = false;
+ this._isOpen = false;
+ }
+
+ public activate(): void
+ {
+ this._isActive = true;
+ }
+
+ public deactivate(): void
+ {
+ this._isActive = false;
+ }
+
+ public open(): void
+ {
+ this._isOpen = true;
+ }
+
+ public close(): void
+ {
+ this._isOpen = false;
+ }
+
+ public addChild(child: ICatalogNode):void
+ {
+ if(!child) return;
+
+ this._children.push(child);
+ }
+
+ public get depth(): number
+ {
+ return this._depth;
+ }
+
+ public get isBranch(): boolean
+ {
+ return (this._children.length > 0);
+ }
+
+ public get isLeaf(): boolean
+ {
+ return (this._children.length === 0);
+ }
+
+ public get localization(): string
+ {
+ return this._localization;
+ }
+
+ public get pageId(): number
+ {
+ return this._pageId;
+ }
+
+ public get pageName(): string
+ {
+ return this._pageName;
+ }
+
+ public get iconId(): number
+ {
+ return this._iconId;
+ }
+
+ public get children(): ICatalogNode[]
+ {
+ return this._children;
+ }
+
+ public get offerIds(): number[]
+ {
+ return this._offerIds;
+ }
+
+ public get parent(): ICatalogNode
+ {
+ return this._parent;
+ }
+
+ public get isVisible(): boolean
+ {
+ return this._isVisible;
+ }
+
+ public get isActive(): boolean
+ {
+ return this._isActive;
+ }
+
+ public get isOpen(): boolean
+ {
+ return this._isOpen;
+ }
+}
diff --git a/Coolui v3 test/src/api/catalog/CatalogPage.ts b/Coolui v3 test/src/api/catalog/CatalogPage.ts
new file mode 100644
index 0000000000..1e806094b1
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/CatalogPage.ts
@@ -0,0 +1,59 @@
+import { ICatalogPage } from './ICatalogPage';
+import { IPageLocalization } from './IPageLocalization';
+import { IPurchasableOffer } from './IPurchasableOffer';
+
+export class CatalogPage implements ICatalogPage
+{
+ public static MODE_NORMAL: number = 0;
+
+ private _pageId: number;
+ private _layoutCode: string;
+ private _localization: IPageLocalization;
+ private _offers: IPurchasableOffer[];
+ private _acceptSeasonCurrencyAsCredits: boolean;
+ private _mode: number;
+
+ constructor(pageId: number, layoutCode: string, localization: IPageLocalization, offers: IPurchasableOffer[], acceptSeasonCurrencyAsCredits: boolean, mode: number = -1)
+ {
+ this._pageId = pageId;
+ this._layoutCode = layoutCode;
+ this._localization = localization;
+ this._offers = offers;
+ this._acceptSeasonCurrencyAsCredits = acceptSeasonCurrencyAsCredits;
+
+ for(const offer of offers) (offer.page = this);
+
+ if(mode === -1) this._mode = CatalogPage.MODE_NORMAL;
+ else this._mode = mode;
+ }
+
+ public get pageId(): number
+ {
+ return this._pageId;
+ }
+
+ public get layoutCode(): string
+ {
+ return this._layoutCode;
+ }
+
+ public get localization(): IPageLocalization
+ {
+ return this._localization;
+ }
+
+ public get offers(): IPurchasableOffer[]
+ {
+ return this._offers;
+ }
+
+ public get acceptSeasonCurrencyAsCredits(): boolean
+ {
+ return this._acceptSeasonCurrencyAsCredits;
+ }
+
+ public get mode(): number
+ {
+ return this._mode;
+ }
+}
diff --git a/Coolui v3 test/src/api/catalog/CatalogPageName.ts b/Coolui v3 test/src/api/catalog/CatalogPageName.ts
new file mode 100644
index 0000000000..ed217d875d
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/CatalogPageName.ts
@@ -0,0 +1,26 @@
+export class CatalogPageName
+{
+ public static DUCKET_INFO: string = 'ducket_info';
+ public static CREDITS: string = 'credits';
+ public static AVATAR_EFFECTS: string = 'avatar_effects';
+ public static HC_MEMBERSHIP: string = 'hc_membership';
+ public static CLUB_GIFTS: string = 'club_gifts';
+ public static LIMITED_SOLD: string = 'limited_sold';
+ public static PET_ACCESSORIES: string = 'pet_accessories';
+ public static TRAX_SONGS: string = 'trax_songs';
+ public static NEW_ADDITIONS: string = 'new_additions';
+ public static QUEST_SHELL: string = 'quest_shell';
+ public static QUEST_SNOWFLAKES: string = 'quest_snowflakes';
+ public static VAL_QUESTS: string = 'val_quests';
+ public static GUILD_CUSTOM_FURNI: string = 'guild_custom_furni';
+ public static GIFT_SHOP: string = 'gift_shop';
+ public static HORSE_STYLES: string = 'horse_styles';
+ public static HORSE_SHOE: string = 'horse_shoe';
+ public static SET_EASTER: string = 'set_easter';
+ public static ECOTRON_TRANSFORM: string = 'ecotron_transform';
+ public static LOYALTY_INFO: string = 'loyalty_info';
+ public static ROOM_BUNDLES: string = 'room_bundles';
+ public static ROOM_BUNDLES_MOBILE: string = 'room_bundles_mobile';
+ public static HABBO_CLUB_DESKTOP: string = 'habbo_club_desktop';
+ public static MOBILE_SUBSCRIPTIONS: string = 'mobile_subscriptions';
+}
diff --git a/Coolui v3 test/src/api/catalog/CatalogPetPalette.ts b/Coolui v3 test/src/api/catalog/CatalogPetPalette.ts
new file mode 100644
index 0000000000..3b3c13446e
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/CatalogPetPalette.ts
@@ -0,0 +1,10 @@
+import { SellablePetPaletteData } from '@nitrots/nitro-renderer';
+
+export class CatalogPetPalette
+{
+ constructor(
+ public readonly breed: string,
+ public readonly palettes: SellablePetPaletteData[]
+ )
+ {}
+}
diff --git a/Coolui v3 test/src/api/catalog/CatalogPurchaseState.ts b/Coolui v3 test/src/api/catalog/CatalogPurchaseState.ts
new file mode 100644
index 0000000000..b442f621cc
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/CatalogPurchaseState.ts
@@ -0,0 +1,10 @@
+export class CatalogPurchaseState
+{
+ public static NONE = 0;
+ public static CONFIRM = 1;
+ public static PURCHASE = 2;
+ public static NO_CREDITS = 3;
+ public static NO_POINTS = 4;
+ public static SOLD_OUT = 5;
+ public static FAILED = 6;
+}
diff --git a/Coolui v3 test/src/api/catalog/CatalogType.ts b/Coolui v3 test/src/api/catalog/CatalogType.ts
new file mode 100644
index 0000000000..670ad6f844
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/CatalogType.ts
@@ -0,0 +1,5 @@
+export class CatalogType
+{
+ public static NORMAL: string = 'NORMAL';
+ public static BUILDER: string = 'BUILDERS_CLUB';
+}
diff --git a/Coolui v3 test/src/api/catalog/CatalogUtilities.ts b/Coolui v3 test/src/api/catalog/CatalogUtilities.ts
new file mode 100644
index 0000000000..c2e1d5eb79
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/CatalogUtilities.ts
@@ -0,0 +1,124 @@
+import { GetRoomEngine, SellablePetPaletteData } from '@nitrots/nitro-renderer';
+import { ICatalogNode } from './ICatalogNode';
+
+export const GetPixelEffectIcon = (id: number) =>
+{
+ return '';
+};
+
+export const GetSubscriptionProductIcon = (id: number) =>
+{
+ return '';
+};
+
+export const GetOfferNodes = (offerNodes: Map, offerId: number) =>
+{
+ const nodes = offerNodes.get(offerId);
+ const allowedNodes: ICatalogNode[] = [];
+
+ if(nodes && nodes.length)
+ {
+ for(const node of nodes)
+ {
+ if(!node.isVisible) continue;
+
+ allowedNodes.push(node);
+ }
+ }
+
+ return allowedNodes;
+};
+
+export const FilterCatalogNode = (search: string, furniLines: string[], node: ICatalogNode, nodes: ICatalogNode[]) =>
+{
+ if(node.isVisible && (node.pageId > 0))
+ {
+ let nodeAdded = false;
+
+ const hayStack = [ node.pageName, node.localization ].join(' ').toLowerCase().replace(/ /gi, '');
+
+ if(hayStack.indexOf(search) > -1)
+ {
+ nodes.push(node);
+
+ nodeAdded = true;
+ }
+
+ if(!nodeAdded)
+ {
+ for(const furniLine of furniLines)
+ {
+ if(hayStack.indexOf(furniLine) >= 0)
+ {
+ nodes.push(node);
+
+ break;
+ }
+ }
+ }
+ }
+
+ for(const child of node.children) FilterCatalogNode(search, furniLines, child, nodes);
+};
+
+export function GetPetIndexFromLocalization(localization: string)
+{
+ if(!localization.length) return 0;
+
+ let index = (localization.length - 1);
+
+ while(index >= 0)
+ {
+ if(isNaN(parseInt(localization.charAt(index)))) break;
+
+ index--;
+ }
+
+ if(index > 0) return parseInt(localization.substring(index + 1));
+
+ return -1;
+}
+
+export function GetPetAvailableColors(petIndex: number, palettes: SellablePetPaletteData[]): number[][]
+{
+ switch(petIndex)
+ {
+ case 0:
+ return [ [ 16743226 ], [ 16750435 ], [ 16764339 ], [ 0xF59500 ], [ 16498012 ], [ 16704690 ], [ 0xEDD400 ], [ 16115545 ], [ 16513201 ], [ 8694111 ], [ 11585939 ], [ 14413767 ], [ 6664599 ], [ 9553845 ], [ 12971486 ], [ 8358322 ], [ 10002885 ], [ 13292268 ], [ 10780600 ], [ 12623573 ], [ 14403561 ], [ 12418717 ], [ 14327229 ], [ 15517403 ], [ 14515069 ], [ 15764368 ], [ 16366271 ], [ 0xABABAB ], [ 0xD4D4D4 ], [ 0xFFFFFF ], [ 14256481 ], [ 14656129 ], [ 15848130 ], [ 14005087 ], [ 14337152 ], [ 15918540 ], [ 15118118 ], [ 15531929 ], [ 9764857 ], [ 11258085 ] ];
+ case 1:
+ return [ [ 16743226 ], [ 16750435 ], [ 16764339 ], [ 0xF59500 ], [ 16498012 ], [ 16704690 ], [ 0xEDD400 ], [ 16115545 ], [ 16513201 ], [ 8694111 ], [ 11585939 ], [ 14413767 ], [ 6664599 ], [ 9553845 ], [ 12971486 ], [ 8358322 ], [ 10002885 ], [ 13292268 ], [ 10780600 ], [ 12623573 ], [ 14403561 ], [ 12418717 ], [ 14327229 ], [ 15517403 ], [ 14515069 ], [ 15764368 ], [ 16366271 ], [ 0xABABAB ], [ 0xD4D4D4 ], [ 0xFFFFFF ], [ 14256481 ], [ 14656129 ], [ 15848130 ], [ 14005087 ], [ 14337152 ], [ 15918540 ], [ 15118118 ], [ 15531929 ], [ 9764857 ], [ 11258085 ] ];
+ case 2:
+ return [ [ 16579283 ], [ 15378351 ], [ 8830016 ], [ 15257125 ], [ 9340985 ], [ 8949607 ], [ 6198292 ], [ 8703620 ], [ 9889626 ], [ 8972045 ], [ 12161285 ], [ 13162269 ], [ 8620113 ], [ 12616503 ], [ 8628101 ], [ 0xD2FF00 ], [ 9764857 ] ];
+ case 3:
+ return [ [ 0xFFFFFF ], [ 0xEEEEEE ], [ 0xDDDDDD ] ];
+ case 4:
+ return [ [ 0xFFFFFF ], [ 16053490 ], [ 15464440 ], [ 16248792 ], [ 15396319 ], [ 15007487 ] ];
+ case 5:
+ return [ [ 0xFFFFFF ], [ 0xEEEEEE ], [ 0xDDDDDD ] ];
+ case 6:
+ return [ [ 0xFFFFFF ], [ 0xEEEEEE ], [ 0xDDDDDD ], [ 16767177 ], [ 16770205 ], [ 16751331 ] ];
+ case 7:
+ return [ [ 0xCCCCCC ], [ 0xAEAEAE ], [ 16751331 ], [ 10149119 ], [ 16763290 ], [ 16743786 ] ];
+ default: {
+ const colors: number[][] = [];
+
+ for(const palette of palettes)
+ {
+ const petColorResult = GetRoomEngine().getPetColorResult(petIndex, palette.paletteId);
+
+ if(!petColorResult) continue;
+
+ if(petColorResult.primaryColor === petColorResult.secondaryColor)
+ {
+ colors.push([ petColorResult.primaryColor ]);
+ }
+ else
+ {
+ colors.push([ petColorResult.primaryColor, petColorResult.secondaryColor ]);
+ }
+ }
+
+ return colors;
+ }
+ }
+}
diff --git a/Coolui v3 test/src/api/catalog/FurnitureOffer.ts b/Coolui v3 test/src/api/catalog/FurnitureOffer.ts
new file mode 100644
index 0000000000..367f24717a
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/FurnitureOffer.ts
@@ -0,0 +1,120 @@
+import { GetProductOfferComposer, IFurnitureData } from '@nitrots/nitro-renderer';
+import { GetProductDataForLocalization, SendMessageComposer } from '../nitro';
+import { ICatalogPage } from './ICatalogPage';
+import { IProduct } from './IProduct';
+import { IPurchasableOffer } from './IPurchasableOffer';
+import { Offer } from './Offer';
+import { Product } from './Product';
+
+export class FurnitureOffer implements IPurchasableOffer
+{
+ private _furniData:IFurnitureData;
+ private _page: ICatalogPage;
+ private _product: IProduct;
+
+ constructor(furniData: IFurnitureData)
+ {
+ this._furniData = furniData;
+ this._product = (new Product(this._furniData.type, this._furniData.id, this._furniData.customParams, 1, GetProductDataForLocalization(this._furniData.className), this._furniData) as IProduct);
+ }
+
+ public activate(): void
+ {
+ SendMessageComposer(new GetProductOfferComposer((this._furniData.rentOfferId > -1) ? this._furniData.rentOfferId : this._furniData.purchaseOfferId));
+ }
+
+ public get offerId(): number
+ {
+ return (this.isRentOffer) ? this._furniData.rentOfferId : this._furniData.purchaseOfferId;
+ }
+
+ public get priceInActivityPoints(): number
+ {
+ return 0;
+ }
+
+ public get activityPointType(): number
+ {
+ return 0;
+ }
+
+ public get priceInCredits(): number
+ {
+ return 0;
+ }
+
+ public get page(): ICatalogPage
+ {
+ return this._page;
+ }
+
+ public set page(page: ICatalogPage)
+ {
+ this._page = page;
+ }
+
+ public get priceType(): string
+ {
+ return '';
+ }
+
+ public get product(): IProduct
+ {
+ return this._product;
+ }
+
+ public get products(): IProduct[]
+ {
+ return [ this._product ];
+ }
+
+ public get localizationId(): string
+ {
+ return 'roomItem.name.' + this._furniData.id;
+ }
+
+ public get bundlePurchaseAllowed(): boolean
+ {
+ return false;
+ }
+
+ public get isRentOffer(): boolean
+ {
+ return (this._furniData.rentOfferId > -1);
+ }
+
+ public get giftable(): boolean
+ {
+ return false;
+ }
+
+ public get pricingModel(): string
+ {
+ return Offer.PRICING_MODEL_FURNITURE;
+ }
+
+ public get clubLevel(): number
+ {
+ return 0;
+ }
+
+ public get badgeCode(): string
+ {
+ return '';
+ }
+
+ public get localizationName(): string
+ {
+ return this._furniData.name;
+ }
+
+ public get localizationDescription(): string
+ {
+ return this._furniData.description;
+ }
+
+ public get isLazy(): boolean
+ {
+ return true;
+ }
+}
diff --git a/Coolui v3 test/src/api/catalog/GetImageIconUrlForProduct.ts b/Coolui v3 test/src/api/catalog/GetImageIconUrlForProduct.ts
new file mode 100644
index 0000000000..f0d195dac8
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/GetImageIconUrlForProduct.ts
@@ -0,0 +1,19 @@
+import { GetRoomEngine } from '@nitrots/nitro-renderer';
+import { ProductTypeEnum } from './ProductTypeEnum';
+
+export const GetImageIconUrlForProduct = (productType: string, productClassId: number, extraData: string = null) =>
+{
+ let imageUrl: string = null;
+
+ switch(productType.toLocaleLowerCase())
+ {
+ case ProductTypeEnum.FLOOR:
+ imageUrl = GetRoomEngine().getFurnitureFloorIconUrl(productClassId);
+ break;
+ case ProductTypeEnum.WALL:
+ imageUrl = GetRoomEngine().getFurnitureWallIconUrl(productClassId, extraData);
+ break;
+ }
+
+ return imageUrl;
+};
diff --git a/Coolui v3 test/src/api/catalog/GiftWrappingConfiguration.ts b/Coolui v3 test/src/api/catalog/GiftWrappingConfiguration.ts
new file mode 100644
index 0000000000..9d29b8c8e7
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/GiftWrappingConfiguration.ts
@@ -0,0 +1,51 @@
+import { GiftWrappingConfigurationParser } from '@nitrots/nitro-renderer';
+
+export class GiftWrappingConfiguration
+{
+ private _isEnabled: boolean = false;
+ private _price: number = null;
+ private _stuffTypes: number[] = null;
+ private _boxTypes: number[] = null;
+ private _ribbonTypes: number[] = null;
+ private _defaultStuffTypes: number[] = null;
+
+ constructor(parser: GiftWrappingConfigurationParser)
+ {
+ this._isEnabled = parser.isEnabled;
+ this._price = parser.price;
+ this._boxTypes = parser.boxTypes;
+ this._ribbonTypes = parser.ribbonTypes;
+ this._stuffTypes = parser.giftWrappers;
+ this._defaultStuffTypes = parser.giftFurnis;
+ }
+
+ public get isEnabled(): boolean
+ {
+ return this._isEnabled;
+ }
+
+ public get price(): number
+ {
+ return this._price;
+ }
+
+ public get stuffTypes(): number[]
+ {
+ return this._stuffTypes;
+ }
+
+ public get boxTypes(): number[]
+ {
+ return this._boxTypes;
+ }
+
+ public get ribbonTypes(): number[]
+ {
+ return this._ribbonTypes;
+ }
+
+ public get defaultStuffTypes(): number[]
+ {
+ return this._defaultStuffTypes;
+ }
+}
diff --git a/Coolui v3 test/src/api/catalog/ICatalogNode.ts b/Coolui v3 test/src/api/catalog/ICatalogNode.ts
new file mode 100644
index 0000000000..6253a7506e
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/ICatalogNode.ts
@@ -0,0 +1,21 @@
+export interface ICatalogNode
+{
+ activate(): void;
+ deactivate(): void;
+ open(): void;
+ close(): void;
+ addChild(node: ICatalogNode): void;
+ readonly depth: number;
+ readonly isBranch: boolean;
+ readonly isLeaf: boolean;
+ readonly localization: string;
+ readonly pageId: number;
+ readonly pageName: string;
+ readonly iconId: number;
+ readonly children: ICatalogNode[];
+ readonly offerIds: number[];
+ readonly parent: ICatalogNode;
+ readonly isVisible: boolean;
+ readonly isActive: boolean;
+ readonly isOpen: boolean;
+}
diff --git a/Coolui v3 test/src/api/catalog/ICatalogOptions.ts b/Coolui v3 test/src/api/catalog/ICatalogOptions.ts
new file mode 100644
index 0000000000..20356947d1
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/ICatalogOptions.ts
@@ -0,0 +1,13 @@
+import { ClubGiftInfoParser, ClubOfferData, HabboGroupEntryData, MarketplaceConfigurationMessageParser } from '@nitrots/nitro-renderer';
+import { CatalogPetPalette } from './CatalogPetPalette';
+import { GiftWrappingConfiguration } from './GiftWrappingConfiguration';
+
+export interface ICatalogOptions
+{
+ groups?: HabboGroupEntryData[];
+ petPalettes?: CatalogPetPalette[];
+ clubOffers?: ClubOfferData[];
+ clubGifts?: ClubGiftInfoParser;
+ giftConfiguration?: GiftWrappingConfiguration;
+ marketplaceConfiguration?: MarketplaceConfigurationMessageParser;
+}
diff --git a/Coolui v3 test/src/api/catalog/ICatalogPage.ts b/Coolui v3 test/src/api/catalog/ICatalogPage.ts
new file mode 100644
index 0000000000..ed11ba0d18
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/ICatalogPage.ts
@@ -0,0 +1,12 @@
+import { IPageLocalization } from './IPageLocalization';
+import { IPurchasableOffer } from './IPurchasableOffer';
+
+export interface ICatalogPage
+{
+ readonly pageId: number;
+ readonly layoutCode: string;
+ readonly localization: IPageLocalization;
+ readonly offers: IPurchasableOffer[];
+ readonly acceptSeasonCurrencyAsCredits: boolean;
+ readonly mode: number;
+}
diff --git a/Coolui v3 test/src/api/catalog/IMarketplaceSearchOptions.ts b/Coolui v3 test/src/api/catalog/IMarketplaceSearchOptions.ts
new file mode 100644
index 0000000000..9489ef0ffb
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/IMarketplaceSearchOptions.ts
@@ -0,0 +1,7 @@
+export interface IMarketplaceSearchOptions
+{
+ query: string;
+ type: number;
+ minPrice: number;
+ maxPrice: number;
+}
diff --git a/Coolui v3 test/src/api/catalog/IPageLocalization.ts b/Coolui v3 test/src/api/catalog/IPageLocalization.ts
new file mode 100644
index 0000000000..ad652e1a49
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/IPageLocalization.ts
@@ -0,0 +1,5 @@
+export interface IPageLocalization
+{
+ getText(index: number): string
+ getImage(index: number): string
+}
diff --git a/Coolui v3 test/src/api/catalog/IProduct.ts b/Coolui v3 test/src/api/catalog/IProduct.ts
new file mode 100644
index 0000000000..4a1a392dec
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/IProduct.ts
@@ -0,0 +1,16 @@
+import { IFurnitureData, IProductData } from '@nitrots/nitro-renderer';
+import { IPurchasableOffer } from './IPurchasableOffer';
+
+export interface IProduct
+{
+ getIconUrl(offer?: IPurchasableOffer): string;
+ productType: string;
+ productClassId: number;
+ extraParam: string;
+ productCount: number;
+ productData: IProductData;
+ furnitureData: IFurnitureData;
+ isUniqueLimitedItem: boolean;
+ uniqueLimitedItemSeriesSize: number;
+ uniqueLimitedItemsLeft: number;
+}
diff --git a/Coolui v3 test/src/api/catalog/IPurchasableOffer.ts b/Coolui v3 test/src/api/catalog/IPurchasableOffer.ts
new file mode 100644
index 0000000000..b18286517d
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/IPurchasableOffer.ts
@@ -0,0 +1,25 @@
+import { ICatalogPage } from './ICatalogPage';
+import { IProduct } from './IProduct';
+
+export interface IPurchasableOffer
+{
+ activate(): void;
+ clubLevel: number;
+ page: ICatalogPage;
+ offerId: number;
+ localizationId: string;
+ priceInCredits: number;
+ priceInActivityPoints: number;
+ activityPointType: number;
+ giftable: boolean;
+ product: IProduct;
+ pricingModel: string;
+ priceType: string;
+ bundlePurchaseAllowed: boolean;
+ isRentOffer: boolean;
+ badgeCode: string;
+ localizationName: string;
+ localizationDescription: string;
+ isLazy: boolean;
+ products: IProduct[];
+}
diff --git a/Coolui v3 test/src/api/catalog/IPurchaseOptions.ts b/Coolui v3 test/src/api/catalog/IPurchaseOptions.ts
new file mode 100644
index 0000000000..c9fab89271
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/IPurchaseOptions.ts
@@ -0,0 +1,9 @@
+import { IObjectData } from '@nitrots/nitro-renderer';
+
+export interface IPurchaseOptions
+{
+ quantity?: number;
+ extraData?: string;
+ extraParamRequired?: boolean;
+ previewStuffData?: IObjectData;
+}
diff --git a/Coolui v3 test/src/api/catalog/MarketplaceOfferData.ts b/Coolui v3 test/src/api/catalog/MarketplaceOfferData.ts
new file mode 100644
index 0000000000..ba1fa88bee
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/MarketplaceOfferData.ts
@@ -0,0 +1,128 @@
+import { IObjectData } from '@nitrots/nitro-renderer';
+
+export class MarketplaceOfferData
+{
+ public static readonly TYPE_FLOOR: number = 1;
+ public static readonly TYPE_WALL: number = 2;
+
+ private _offerId: number;
+ private _furniId: number;
+ private _furniType: number;
+ private _extraData: string;
+ private _stuffData: IObjectData;
+ private _price: number;
+ private _averagePrice: number;
+ private _imageCallback: number;
+ private _status: number;
+ private _timeLeftMinutes: number = -1;
+ private _offerCount: number;
+ private _image: string;
+
+ constructor(offerId: number, furniId: number, furniType: number, extraData: string, stuffData: IObjectData, price: number, status: number, averagePrice: number, offerCount: number = -1)
+ {
+ this._offerId = offerId;
+ this._furniId = furniId;
+ this._furniType = furniType;
+ this._extraData = extraData;
+ this._stuffData = stuffData;
+ this._price = price;
+ this._status = status;
+ this._averagePrice = averagePrice;
+ this._offerCount = offerCount;
+ }
+
+ public get offerId(): number
+ {
+ return this._offerId;
+ }
+
+ public set offerId(offerId: number)
+ {
+ this._offerId = offerId;
+ }
+
+ public get furniId(): number
+ {
+ return this._furniId;
+ }
+
+ public get furniType(): number
+ {
+ return this._furniType;
+ }
+
+ public get extraData(): string
+ {
+ return this._extraData;
+ }
+
+ public get stuffData(): IObjectData
+ {
+ return this._stuffData;
+ }
+
+ public get price(): number
+ {
+ return this._price;
+ }
+
+ public set price(price: number)
+ {
+ this._price = price;
+ }
+
+ public get averagePrice(): number
+ {
+ return this._averagePrice;
+ }
+
+ public get image(): string
+ {
+ return this._image;
+ }
+
+ public set image(image: string)
+ {
+ this._image = image;
+ }
+
+ public get imageCallback(): number
+ {
+ return this._imageCallback;
+ }
+
+ public set imageCallback(callback: number)
+ {
+ this._imageCallback = callback;
+ }
+
+ public get status(): number
+ {
+ return this._status;
+ }
+
+ public get timeLeftMinutes(): number
+ {
+ return this._timeLeftMinutes;
+ }
+
+ public set timeLeftMinutes(minutes: number)
+ {
+ this._timeLeftMinutes = minutes;
+ }
+
+ public get offerCount(): number
+ {
+ return this._offerCount;
+ }
+
+ public set offerCount(count: number)
+ {
+ this._offerCount = count;
+ }
+
+ public get isUniqueLimitedItem(): boolean
+ {
+ return (this.stuffData && (this.stuffData.uniqueSeries > 0));
+ }
+}
diff --git a/Coolui v3 test/src/api/catalog/MarketplaceOfferState.ts b/Coolui v3 test/src/api/catalog/MarketplaceOfferState.ts
new file mode 100644
index 0000000000..6267a5f659
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/MarketplaceOfferState.ts
@@ -0,0 +1,7 @@
+export class MarketPlaceOfferState
+{
+ public static readonly ONGOING = 1;
+ public static readonly ONGOING_OWN = 1;
+ public static readonly SOLD = 2;
+ public static readonly EXPIRED = 3;
+}
diff --git a/Coolui v3 test/src/api/catalog/MarketplaceSearchType.ts b/Coolui v3 test/src/api/catalog/MarketplaceSearchType.ts
new file mode 100644
index 0000000000..ac7a701994
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/MarketplaceSearchType.ts
@@ -0,0 +1,6 @@
+export class MarketplaceSearchType
+{
+ public static readonly BY_ACTIVITY = 1;
+ public static readonly BY_VALUE = 2;
+ public static readonly ADVANCED = 3;
+}
diff --git a/Coolui v3 test/src/api/catalog/Offer.ts b/Coolui v3 test/src/api/catalog/Offer.ts
new file mode 100644
index 0000000000..9182c03e7d
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/Offer.ts
@@ -0,0 +1,245 @@
+import { GetFurnitureData, GetProductDataForLocalization, LocalizeText, ProductTypeEnum } from '..';
+import { ICatalogPage } from './ICatalogPage';
+import { IProduct } from './IProduct';
+import { IPurchasableOffer } from './IPurchasableOffer';
+import { Product } from './Product';
+
+export class Offer implements IPurchasableOffer
+{
+ public static PRICING_MODEL_UNKNOWN: string = 'pricing_model_unknown';
+ public static PRICING_MODEL_SINGLE: string = 'pricing_model_single';
+ public static PRICING_MODEL_MULTI: string = 'pricing_model_multi';
+ public static PRICING_MODEL_BUNDLE: string = 'pricing_model_bundle';
+ public static PRICING_MODEL_FURNITURE: string = 'pricing_model_furniture';
+ public static PRICE_TYPE_NONE: string = 'price_type_none';
+ public static PRICE_TYPE_CREDITS: string = 'price_type_credits';
+ public static PRICE_TYPE_ACTIVITYPOINTS: string = 'price_type_activitypoints';
+ public static PRICE_TYPE_CREDITS_ACTIVITYPOINTS: string = 'price_type_credits_and_activitypoints';
+
+ private _pricingModel: string;
+ private _priceType: string;
+ private _offerId: number;
+ private _localizationId: string;
+ private _priceInCredits: number;
+ private _priceInActivityPoints: number;
+ private _activityPointType: number;
+ private _giftable: boolean;
+ private _isRentOffer: boolean;
+ private _page: ICatalogPage;
+ private _clubLevel: number = 0;
+ private _products: IProduct[];
+ private _badgeCode: string;
+ private _bundlePurchaseAllowed: boolean = false;
+
+ constructor(offerId: number, localizationId: string, isRentOffer: boolean, priceInCredits: number, priceInActivityPoints: number, activityPointType: number, giftable: boolean, clubLevel: number, products: IProduct[], bundlePurchaseAllowed: boolean)
+ {
+ this._offerId = offerId;
+ this._localizationId = localizationId;
+ this._isRentOffer = isRentOffer;
+ this._priceInCredits = priceInCredits;
+ this._priceInActivityPoints = priceInActivityPoints;
+ this._activityPointType = activityPointType;
+ this._giftable = giftable;
+ this._clubLevel = clubLevel;
+ this._products = products;
+ this._bundlePurchaseAllowed = bundlePurchaseAllowed;
+
+ this.setPricingModelForProducts();
+ this.setPricingType();
+
+ for(const product of products)
+ {
+ if(product.productType === ProductTypeEnum.BADGE)
+ {
+ this._badgeCode = product.extraParam;
+
+ break;
+ }
+ }
+ }
+
+ public activate(): void
+ {
+
+ }
+
+ public get clubLevel(): number
+ {
+ return this._clubLevel;
+ }
+
+ public get page(): ICatalogPage
+ {
+ return this._page;
+ }
+
+ public set page(k: ICatalogPage)
+ {
+ this._page = k;
+ }
+
+ public get offerId(): number
+ {
+ return this._offerId;
+ }
+
+ public get localizationId(): string
+ {
+ return this._localizationId;
+ }
+
+ public get priceInCredits(): number
+ {
+ return this._priceInCredits;
+ }
+
+ public get priceInActivityPoints(): number
+ {
+ return this._priceInActivityPoints;
+ }
+
+ public get activityPointType(): number
+ {
+ return this._activityPointType;
+ }
+
+ public get giftable(): boolean
+ {
+ return this._giftable;
+ }
+
+ public get product(): IProduct
+ {
+ if(!this._products || !this._products.length) return null;
+
+ if(this._products.length === 1) return this._products[0];
+
+ const products = Product.stripAddonProducts(this._products);
+
+ if(products.length) return products[0];
+
+ return null;
+ }
+
+ public get pricingModel(): string
+ {
+ return this._pricingModel;
+ }
+
+ public get priceType(): string
+ {
+ return this._priceType;
+ }
+
+ public get bundlePurchaseAllowed(): boolean
+ {
+ return this._bundlePurchaseAllowed;
+ }
+
+ public get isRentOffer(): boolean
+ {
+ return this._isRentOffer;
+ }
+
+ public get badgeCode(): string
+ {
+ return this._badgeCode;
+ }
+
+ public get localizationName(): string
+ {
+ const productData = GetProductDataForLocalization(this._localizationId);
+
+ if(productData) return productData.name;
+
+ return LocalizeText(this._localizationId);
+ }
+
+ public get localizationDescription(): string
+ {
+ const productData = GetProductDataForLocalization(this._localizationId);
+
+ if(productData) return productData.description;
+
+ return LocalizeText(this._localizationId);
+ }
+
+ public get isLazy(): boolean
+ {
+ return false;
+ }
+
+ public get products(): IProduct[]
+ {
+ return this._products;
+ }
+
+ private setPricingModelForProducts(): void
+ {
+ const products = Product.stripAddonProducts(this._products);
+
+ if(products.length === 1)
+ {
+ if(products[0].productCount === 1)
+ {
+ this._pricingModel = Offer.PRICING_MODEL_SINGLE;
+ }
+ else
+ {
+ this._pricingModel = Offer.PRICING_MODEL_MULTI;
+ }
+ }
+
+ else if(products.length > 1)
+ {
+ this._pricingModel = Offer.PRICING_MODEL_BUNDLE;
+ }
+
+ else
+ {
+ this._pricingModel = Offer.PRICING_MODEL_UNKNOWN;
+ }
+ }
+
+ private setPricingType(): void
+ {
+ if((this._priceInCredits > 0) && (this._priceInActivityPoints > 0))
+ {
+ this._priceType = Offer.PRICE_TYPE_CREDITS_ACTIVITYPOINTS;
+ }
+
+ else if(this._priceInCredits > 0)
+ {
+ this._priceType = Offer.PRICE_TYPE_CREDITS;
+ }
+
+ else if(this._priceInActivityPoints > 0)
+ {
+ this._priceType = Offer.PRICE_TYPE_ACTIVITYPOINTS;
+ }
+
+ else
+ {
+ this._priceType = Offer.PRICE_TYPE_NONE;
+ }
+ }
+
+ public clone(): IPurchasableOffer
+ {
+ const products: IProduct[] = [];
+ const productData = GetProductDataForLocalization(this.localizationId);
+
+ for(const product of this._products)
+ {
+ const furnitureData = GetFurnitureData(product.productClassId, product.productType);
+
+ products.push(new Product(product.productType, product.productClassId, product.extraParam, product.productCount, productData, furnitureData));
+ }
+
+ const offer = new Offer(this.offerId, this.localizationId, this.isRentOffer, this.priceInCredits, this.priceInActivityPoints, this.activityPointType, this.giftable, this.clubLevel, products, this.bundlePurchaseAllowed);
+
+ offer.page = this.page;
+
+ return offer;
+ }
+}
diff --git a/Coolui v3 test/src/api/catalog/PageLocalization.ts b/Coolui v3 test/src/api/catalog/PageLocalization.ts
new file mode 100644
index 0000000000..f24ae87288
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/PageLocalization.ts
@@ -0,0 +1,36 @@
+import { GetConfigurationValue } from '../nitro';
+import { IPageLocalization } from './IPageLocalization';
+
+export class PageLocalization implements IPageLocalization
+{
+ private _images: string[];
+ private _texts: string[];
+
+ constructor(images: string[], texts: string[])
+ {
+ this._images = images;
+ this._texts = texts;
+ }
+
+ public getText(index: number): string
+ {
+ let message = (this._texts[index] || '');
+
+ if(message && message.length) message = message.replace(/\r\n|\r|\n/g, '
');
+
+ return message;
+ }
+
+ public getImage(index: number): string
+ {
+ const imageName = (this._images[index] || '');
+
+ if(!imageName || !imageName.length) return null;
+
+ let assetUrl = GetConfigurationValue('catalog.asset.image.url');
+
+ assetUrl = assetUrl.replace('%name%', imageName);
+
+ return assetUrl;
+ }
+}
diff --git a/Coolui v3 test/src/api/catalog/PlacedObjectPurchaseData.ts b/Coolui v3 test/src/api/catalog/PlacedObjectPurchaseData.ts
new file mode 100644
index 0000000000..43d23e3cd0
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/PlacedObjectPurchaseData.ts
@@ -0,0 +1,41 @@
+import { IFurnitureData, IProductData } from '@nitrots/nitro-renderer';
+import { IPurchasableOffer } from './IPurchasableOffer';
+
+export class PlacedObjectPurchaseData
+{
+ constructor(
+ public readonly roomId: number,
+ public readonly objectId: number,
+ public readonly category: number,
+ public readonly wallLocation: string,
+ public readonly x: number,
+ public readonly y: number,
+ public readonly direction: number,
+ public readonly offer: IPurchasableOffer)
+ {}
+
+ public get offerId(): number
+ {
+ return this.offer.offerId;
+ }
+
+ public get productClassId(): number
+ {
+ return this.offer.product.productClassId;
+ }
+
+ public get productData(): IProductData
+ {
+ return this.offer.product.productData;
+ }
+
+ public get furniData(): IFurnitureData
+ {
+ return this.offer.product.furnitureData;
+ }
+
+ public get extraParam(): string
+ {
+ return this.offer.product.extraParam;
+ }
+}
diff --git a/Coolui v3 test/src/api/catalog/Product.ts b/Coolui v3 test/src/api/catalog/Product.ts
new file mode 100644
index 0000000000..17d9340f16
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/Product.ts
@@ -0,0 +1,143 @@
+import { GetRoomEngine, GetSessionDataManager, IFurnitureData, IObjectData, IProductData } from '@nitrots/nitro-renderer';
+import { GetConfigurationValue } from '../nitro';
+import { GetPixelEffectIcon, GetSubscriptionProductIcon } from './CatalogUtilities';
+import { IProduct } from './IProduct';
+import { IPurchasableOffer } from './IPurchasableOffer';
+import { ProductTypeEnum } from './ProductTypeEnum';
+
+export class Product implements IProduct
+{
+ public static EFFECT_CLASSID_NINJA_DISAPPEAR: number = 108;
+
+ private _productType: string;
+ private _productClassId: number;
+ private _extraParam: string;
+ private _productCount: number;
+ private _productData: IProductData;
+ private _furnitureData: IFurnitureData;
+ private _isUniqueLimitedItem: boolean;
+ private _uniqueLimitedItemSeriesSize: number;
+ private _uniqueLimitedItemsLeft: number;
+
+ constructor(productType: string, productClassId: number, extraParam: string, productCount: number, productData: IProductData, furnitureData: IFurnitureData, isUniqueLimitedItem: boolean = false, uniqueLimitedItemSeriesSize: number = 0, uniqueLimitedItemsLeft: number = 0)
+ {
+ this._productType = productType.toLowerCase();
+ this._productClassId = productClassId;
+ this._extraParam = extraParam;
+ this._productCount = productCount;
+ this._productData = productData;
+ this._furnitureData = furnitureData;
+ this._isUniqueLimitedItem = isUniqueLimitedItem;
+ this._uniqueLimitedItemSeriesSize = uniqueLimitedItemSeriesSize;
+ this._uniqueLimitedItemsLeft = uniqueLimitedItemsLeft;
+ }
+
+ public static stripAddonProducts(products: IProduct[]): IProduct[]
+ {
+ if(products.length === 1) return products;
+
+ return products.filter(product => ((product.productType !== ProductTypeEnum.BADGE) && (product.productType !== ProductTypeEnum.EFFECT) && (product.productClassId !== Product.EFFECT_CLASSID_NINJA_DISAPPEAR)));
+ }
+
+ public getIconUrl(offer: IPurchasableOffer = null, stuffData: IObjectData = null): string
+ {
+ switch(this._productType)
+ {
+ case ProductTypeEnum.FLOOR:
+ return GetRoomEngine().getFurnitureFloorIconUrl(this.productClassId);
+ case ProductTypeEnum.WALL: {
+ if(offer && this._furnitureData)
+ {
+ let iconName = '';
+
+ switch(this._furnitureData.className)
+ {
+ case 'floor':
+ iconName = [ 'th', this._furnitureData.className, offer.product.extraParam ].join('_');
+ break;
+ case 'wallpaper':
+ iconName = [ 'th', 'wall', offer.product.extraParam ].join('_');
+ break;
+ case 'landscape':
+ iconName = [ 'th', this._furnitureData.className, (offer.product.extraParam || '').replace('.', '_'), '001' ].join('_');
+ break;
+ }
+
+ if(iconName !== '')
+ {
+ const assetUrl = GetConfigurationValue('catalog.asset.url');
+
+ return `${ assetUrl }/${ iconName }.png`;
+ }
+ }
+
+ return GetRoomEngine().getFurnitureWallIconUrl(this.productClassId, this._extraParam);
+ }
+ case ProductTypeEnum.EFFECT:
+ return GetPixelEffectIcon(this.productClassId);
+ case ProductTypeEnum.HABBO_CLUB:
+ return GetSubscriptionProductIcon(this.productClassId);
+ case ProductTypeEnum.BADGE:
+ return GetSessionDataManager().getBadgeUrl(this._extraParam);
+ case ProductTypeEnum.ROBOT:
+ return null;
+ }
+
+ return null;
+ }
+
+ public get productType(): string
+ {
+ return this._productType;
+ }
+
+ public get productClassId(): number
+ {
+ return this._productClassId;
+ }
+
+ public get extraParam(): string
+ {
+ return this._extraParam;
+ }
+
+ public set extraParam(extraParam: string)
+ {
+ this._extraParam = extraParam;
+ }
+
+ public get productCount(): number
+ {
+ return this._productCount;
+ }
+
+ public get productData(): IProductData
+ {
+ return this._productData;
+ }
+
+ public get furnitureData(): IFurnitureData
+ {
+ return this._furnitureData;
+ }
+
+ public get isUniqueLimitedItem(): boolean
+ {
+ return this._isUniqueLimitedItem;
+ }
+
+ public get uniqueLimitedItemSeriesSize(): number
+ {
+ return this._uniqueLimitedItemSeriesSize;
+ }
+
+ public get uniqueLimitedItemsLeft(): number
+ {
+ return this._uniqueLimitedItemsLeft;
+ }
+
+ public set uniqueLimitedItemsLeft(uniqueLimitedItemsLeft: number)
+ {
+ this._uniqueLimitedItemsLeft = uniqueLimitedItemsLeft;
+ }
+}
diff --git a/Coolui v3 test/src/api/catalog/ProductTypeEnum.ts b/Coolui v3 test/src/api/catalog/ProductTypeEnum.ts
new file mode 100644
index 0000000000..f24908163c
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/ProductTypeEnum.ts
@@ -0,0 +1,11 @@
+export class ProductTypeEnum
+{
+ public static WALL: string = 'i';
+ public static FLOOR: string = 's';
+ public static EFFECT: string = 'e';
+ public static HABBO_CLUB: string = 'h';
+ public static BADGE: string = 'b';
+ public static GAME_TOKEN: string = 'GAME_TOKEN';
+ public static PET: string = 'p';
+ public static ROBOT: string = 'r';
+}
diff --git a/Coolui v3 test/src/api/catalog/RequestedPage.ts b/Coolui v3 test/src/api/catalog/RequestedPage.ts
new file mode 100644
index 0000000000..205cc3e207
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/RequestedPage.ts
@@ -0,0 +1,63 @@
+export class RequestedPage
+{
+ public static REQUEST_TYPE_NONE: number = 0;
+ public static REQUEST_TYPE_ID: number = 1;
+ public static REQUEST_TYPE_OFFER: number = 2;
+ public static REQUEST_TYPE_NAME: number = 3;
+
+ private _requestType: number;
+ private _requestById: number;
+ private _requestedByOfferId: number;
+ private _requestByName: string;
+
+ constructor()
+ {
+ this._requestType = RequestedPage.REQUEST_TYPE_NONE;
+ }
+
+ public resetRequest():void
+ {
+ this._requestType = RequestedPage.REQUEST_TYPE_NONE;
+ this._requestById = -1;
+ this._requestedByOfferId = -1;
+ this._requestByName = null;
+ }
+
+ public get requestType(): number
+ {
+ return this._requestType;
+ }
+
+ public get requestById(): number
+ {
+ return this._requestById;
+ }
+
+ public set requestById(id: number)
+ {
+ this._requestType = RequestedPage.REQUEST_TYPE_ID;
+ this._requestById = id;
+ }
+
+ public get requestedByOfferId(): number
+ {
+ return this._requestedByOfferId;
+ }
+
+ public set requestedByOfferId(offerId: number)
+ {
+ this._requestType = RequestedPage.REQUEST_TYPE_OFFER;
+ this._requestedByOfferId = offerId;
+ }
+
+ public get requestByName(): string
+ {
+ return this._requestByName;
+ }
+
+ public set requestByName(name: string)
+ {
+ this._requestType = RequestedPage.REQUEST_TYPE_NAME;
+ this._requestByName = name;
+ }
+}
diff --git a/Coolui v3 test/src/api/catalog/SearchResult.ts b/Coolui v3 test/src/api/catalog/SearchResult.ts
new file mode 100644
index 0000000000..120aed4b3f
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/SearchResult.ts
@@ -0,0 +1,11 @@
+import { ICatalogNode } from './ICatalogNode';
+import { IPurchasableOffer } from './IPurchasableOffer';
+
+export class SearchResult
+{
+ constructor(
+ public readonly searchValue: string,
+ public readonly offers: IPurchasableOffer[],
+ public readonly filteredNodes: ICatalogNode[])
+ {}
+}
diff --git a/Coolui v3 test/src/api/catalog/index.ts b/Coolui v3 test/src/api/catalog/index.ts
new file mode 100644
index 0000000000..6c5b9e2ea9
--- /dev/null
+++ b/Coolui v3 test/src/api/catalog/index.ts
@@ -0,0 +1,29 @@
+export * from './BuilderFurniPlaceableStatus';
+export * from './CatalogNode';
+export * from './CatalogPage';
+export * from './CatalogPageName';
+export * from './CatalogPetPalette';
+export * from './CatalogPurchaseState';
+export * from './CatalogType';
+export * from './CatalogUtilities';
+export * from './FurnitureOffer';
+export * from './GetImageIconUrlForProduct';
+export * from './GiftWrappingConfiguration';
+export * from './ICatalogNode';
+export * from './ICatalogOptions';
+export * from './ICatalogPage';
+export * from './IMarketplaceSearchOptions';
+export * from './IPageLocalization';
+export * from './IProduct';
+export * from './IPurchasableOffer';
+export * from './IPurchaseOptions';
+export * from './MarketplaceOfferData';
+export * from './MarketplaceOfferState';
+export * from './MarketplaceSearchType';
+export * from './Offer';
+export * from './PageLocalization';
+export * from './PlacedObjectPurchaseData';
+export * from './Product';
+export * from './ProductTypeEnum';
+export * from './RequestedPage';
+export * from './SearchResult';
diff --git a/Coolui v3 test/src/api/chat-history/ChatEntryType.ts b/Coolui v3 test/src/api/chat-history/ChatEntryType.ts
new file mode 100644
index 0000000000..045f00ce94
--- /dev/null
+++ b/Coolui v3 test/src/api/chat-history/ChatEntryType.ts
@@ -0,0 +1,6 @@
+export class ChatEntryType
+{
+ public static TYPE_CHAT = 1;
+ public static TYPE_ROOM_INFO = 2;
+ public static TYPE_IM = 3;
+}
diff --git a/Coolui v3 test/src/api/chat-history/ChatHistoryCurrentDate.ts b/Coolui v3 test/src/api/chat-history/ChatHistoryCurrentDate.ts
new file mode 100644
index 0000000000..35d6143fda
--- /dev/null
+++ b/Coolui v3 test/src/api/chat-history/ChatHistoryCurrentDate.ts
@@ -0,0 +1,6 @@
+export const ChatHistoryCurrentDate = () =>
+{
+ const currentTime = new Date();
+
+ return `${ currentTime.getHours().toString().padStart(2, '0') }:${ currentTime.getMinutes().toString().padStart(2, '0') }`;
+};
diff --git a/Coolui v3 test/src/api/chat-history/IChatEntry.ts b/Coolui v3 test/src/api/chat-history/IChatEntry.ts
new file mode 100644
index 0000000000..1bf7a52068
--- /dev/null
+++ b/Coolui v3 test/src/api/chat-history/IChatEntry.ts
@@ -0,0 +1,17 @@
+export interface IChatEntry
+{
+ id: number;
+ webId: number;
+ entityId: number;
+ name: string;
+ look?: string;
+ message?: string;
+ entityType?: number;
+ style?: number;
+ chatType?: number;
+ imageUrl?: string;
+ color?: string;
+ roomId: number;
+ timestamp: string;
+ type: number;
+}
diff --git a/Coolui v3 test/src/api/chat-history/IRoomHistoryEntry.ts b/Coolui v3 test/src/api/chat-history/IRoomHistoryEntry.ts
new file mode 100644
index 0000000000..4986154a12
--- /dev/null
+++ b/Coolui v3 test/src/api/chat-history/IRoomHistoryEntry.ts
@@ -0,0 +1,5 @@
+export interface IRoomHistoryEntry
+{
+ id: number;
+ name: string;
+}
diff --git a/Coolui v3 test/src/api/chat-history/MessengerHistoryCurrentDate.ts b/Coolui v3 test/src/api/chat-history/MessengerHistoryCurrentDate.ts
new file mode 100644
index 0000000000..b5f7972513
--- /dev/null
+++ b/Coolui v3 test/src/api/chat-history/MessengerHistoryCurrentDate.ts
@@ -0,0 +1,6 @@
+export const MessengerHistoryCurrentDate = (secondsSinceNow: number = 0) =>
+{
+ const currentTime = secondsSinceNow ? new Date(Date.now() - secondsSinceNow * 1000) : new Date();
+
+ return `${ currentTime.getHours().toString().padStart(2, '0') }:${ currentTime.getMinutes().toString().padStart(2, '0') }`;
+};
diff --git a/Coolui v3 test/src/api/chat-history/index.ts b/Coolui v3 test/src/api/chat-history/index.ts
new file mode 100644
index 0000000000..a9893744bb
--- /dev/null
+++ b/Coolui v3 test/src/api/chat-history/index.ts
@@ -0,0 +1,5 @@
+export * from './ChatEntryType';
+export * from './ChatHistoryCurrentDate';
+export * from './IChatEntry';
+export * from './IRoomHistoryEntry';
+export * from './MessengerHistoryCurrentDate';
diff --git a/Coolui v3 test/src/api/events/DispatchEvent.ts b/Coolui v3 test/src/api/events/DispatchEvent.ts
new file mode 100644
index 0000000000..79e2f5ce31
--- /dev/null
+++ b/Coolui v3 test/src/api/events/DispatchEvent.ts
@@ -0,0 +1,3 @@
+import { IEventDispatcher, NitroEvent } from '@nitrots/nitro-renderer';
+
+export const DispatchEvent = (eventDispatcher: IEventDispatcher, event: NitroEvent) => eventDispatcher.dispatchEvent(event);
diff --git a/Coolui v3 test/src/api/events/DispatchMainEvent.ts b/Coolui v3 test/src/api/events/DispatchMainEvent.ts
new file mode 100644
index 0000000000..e316b30971
--- /dev/null
+++ b/Coolui v3 test/src/api/events/DispatchMainEvent.ts
@@ -0,0 +1,4 @@
+import { GetEventDispatcher, NitroEvent } from '@nitrots/nitro-renderer';
+import { DispatchEvent } from './DispatchEvent';
+
+export const DispatchMainEvent = (event: NitroEvent) => DispatchEvent(GetEventDispatcher(), event);
diff --git a/Coolui v3 test/src/api/events/DispatchUiEvent.ts b/Coolui v3 test/src/api/events/DispatchUiEvent.ts
new file mode 100644
index 0000000000..5200bb4ff8
--- /dev/null
+++ b/Coolui v3 test/src/api/events/DispatchUiEvent.ts
@@ -0,0 +1,5 @@
+import { NitroEvent } from '@nitrots/nitro-renderer';
+import { DispatchEvent } from './DispatchEvent';
+import { UI_EVENT_DISPATCHER } from './UI_EVENT_DISPATCHER';
+
+export const DispatchUiEvent = (event: NitroEvent) => DispatchEvent(UI_EVENT_DISPATCHER, event);
diff --git a/Coolui v3 test/src/api/events/UI_EVENT_DISPATCHER.ts b/Coolui v3 test/src/api/events/UI_EVENT_DISPATCHER.ts
new file mode 100644
index 0000000000..cb573117d0
--- /dev/null
+++ b/Coolui v3 test/src/api/events/UI_EVENT_DISPATCHER.ts
@@ -0,0 +1,3 @@
+import { EventDispatcher, IEventDispatcher } from '@nitrots/nitro-renderer';
+
+export const UI_EVENT_DISPATCHER: IEventDispatcher = new EventDispatcher();
diff --git a/Coolui v3 test/src/api/events/index.ts b/Coolui v3 test/src/api/events/index.ts
new file mode 100644
index 0000000000..b7c22ee5aa
--- /dev/null
+++ b/Coolui v3 test/src/api/events/index.ts
@@ -0,0 +1,4 @@
+export * from './DispatchEvent';
+export * from './DispatchMainEvent';
+export * from './DispatchUiEvent';
+export * from './UI_EVENT_DISPATCHER';
diff --git a/Coolui v3 test/src/api/friends/GetGroupChatData.ts b/Coolui v3 test/src/api/friends/GetGroupChatData.ts
new file mode 100644
index 0000000000..d1a2c7b978
--- /dev/null
+++ b/Coolui v3 test/src/api/friends/GetGroupChatData.ts
@@ -0,0 +1,13 @@
+import { IGroupChatData } from './IGroupChatData';
+
+export const GetGroupChatData = (extraData: string) =>
+{
+ if(!extraData || !extraData.length) return null;
+
+ const splitData = extraData.split('/');
+ const username = splitData[0];
+ const figure = splitData[1];
+ const userId = parseInt(splitData[2]);
+
+ return ({ username: username, figure: figure, userId: userId } as IGroupChatData);
+};
diff --git a/Coolui v3 test/src/api/friends/IGroupChatData.ts b/Coolui v3 test/src/api/friends/IGroupChatData.ts
new file mode 100644
index 0000000000..24a3f9cf4b
--- /dev/null
+++ b/Coolui v3 test/src/api/friends/IGroupChatData.ts
@@ -0,0 +1,6 @@
+export interface IGroupChatData
+{
+ username: string;
+ figure: string;
+ userId: number;
+}
diff --git a/Coolui v3 test/src/api/friends/MessengerFriend.ts b/Coolui v3 test/src/api/friends/MessengerFriend.ts
new file mode 100644
index 0000000000..b5cfc88736
--- /dev/null
+++ b/Coolui v3 test/src/api/friends/MessengerFriend.ts
@@ -0,0 +1,43 @@
+import { FriendParser } from '@nitrots/nitro-renderer';
+
+export class MessengerFriend
+{
+ public static RELATIONSHIP_NONE: number = 0;
+ public static RELATIONSHIP_HEART: number = 1;
+ public static RELATIONSHIP_SMILE: number = 2;
+ public static RELATIONSHIP_BOBBA: number = 3;
+
+ public id: number = -1;
+ public name: string = null;
+ public gender: number = 0;
+ public online: boolean = false;
+ public followingAllowed: boolean = false;
+ public figure: string = null;
+ public categoryId: number = 0;
+ public motto: string = null;
+ public realName: string = null;
+ public lastAccess: string = null;
+ public persistedMessageUser: boolean = false;
+ public vipMember: boolean = false;
+ public pocketHabboUser: boolean = false;
+ public relationshipStatus: number = -1;
+ public unread: number = 0;
+
+ public populate(parser: FriendParser): void
+ {
+ this.id = parser.id;
+ this.name = parser.name;
+ this.gender = parser.gender;
+ this.online = parser.online;
+ this.followingAllowed = parser.followingAllowed;
+ this.figure = parser.figure;
+ this.categoryId = parser.categoryId;
+ this.motto = parser.motto;
+ this.realName = parser.realName;
+ this.lastAccess = parser.lastAccess;
+ this.persistedMessageUser = parser.persistedMessageUser;
+ this.vipMember = parser.vipMember;
+ this.pocketHabboUser = parser.pocketHabboUser;
+ this.relationshipStatus = parser.relationshipStatus;
+ }
+}
diff --git a/Coolui v3 test/src/api/friends/MessengerGroupType.ts b/Coolui v3 test/src/api/friends/MessengerGroupType.ts
new file mode 100644
index 0000000000..d46a1b6300
--- /dev/null
+++ b/Coolui v3 test/src/api/friends/MessengerGroupType.ts
@@ -0,0 +1,5 @@
+export class MessengerGroupType
+{
+ public static readonly GROUP_CHAT = 0;
+ public static readonly PRIVATE_CHAT = 1;
+}
diff --git a/Coolui v3 test/src/api/friends/MessengerIconState.ts b/Coolui v3 test/src/api/friends/MessengerIconState.ts
new file mode 100644
index 0000000000..63f8c133c5
--- /dev/null
+++ b/Coolui v3 test/src/api/friends/MessengerIconState.ts
@@ -0,0 +1,6 @@
+export class MessengerIconState
+{
+ public static HIDDEN: number = 0;
+ public static SHOW: number = 1;
+ public static UNREAD: number = 2;
+}
diff --git a/Coolui v3 test/src/api/friends/MessengerRequest.ts b/Coolui v3 test/src/api/friends/MessengerRequest.ts
new file mode 100644
index 0000000000..89ceec5be4
--- /dev/null
+++ b/Coolui v3 test/src/api/friends/MessengerRequest.ts
@@ -0,0 +1,41 @@
+import { FriendRequestData } from '@nitrots/nitro-renderer';
+
+export class MessengerRequest
+{
+ private _id: number;
+ private _name: string;
+ private _requesterUserId: number;
+ private _figureString: string;
+
+ public populate(data: FriendRequestData): boolean
+ {
+ if(!data) return false;
+
+ this._id = data.requestId;
+ this._name = data.requesterName;
+ this._figureString = data.figureString;
+ this._requesterUserId = data.requesterUserId;
+
+ return true;
+ }
+
+ public get id(): number
+ {
+ return this._id;
+ }
+
+ public get name(): string
+ {
+ return this._name;
+ }
+
+ public get requesterUserId(): number
+ {
+ return this._requesterUserId;
+ }
+
+ public get figureString(): string
+ {
+ return this._figureString;
+ }
+}
diff --git a/Coolui v3 test/src/api/friends/MessengerSettings.ts b/Coolui v3 test/src/api/friends/MessengerSettings.ts
new file mode 100644
index 0000000000..e890fcc027
--- /dev/null
+++ b/Coolui v3 test/src/api/friends/MessengerSettings.ts
@@ -0,0 +1,11 @@
+import { FriendCategoryData } from '@nitrots/nitro-renderer';
+
+export class MessengerSettings
+{
+ constructor(
+ public userFriendLimit: number = 0,
+ public normalFriendLimit: number = 0,
+ public extendedFriendLimit: number = 0,
+ public categories: FriendCategoryData[] = [])
+ {}
+}
diff --git a/Coolui v3 test/src/api/friends/MessengerThread.ts b/Coolui v3 test/src/api/friends/MessengerThread.ts
new file mode 100644
index 0000000000..30e931c4b3
--- /dev/null
+++ b/Coolui v3 test/src/api/friends/MessengerThread.ts
@@ -0,0 +1,96 @@
+import { GetGroupChatData } from './GetGroupChatData';
+import { MessengerFriend } from './MessengerFriend';
+import { MessengerGroupType } from './MessengerGroupType';
+import { MessengerThreadChat } from './MessengerThreadChat';
+import { MessengerThreadChatGroup } from './MessengerThreadChatGroup';
+
+export class MessengerThread
+{
+ public static MESSAGE_RECEIVED: string = 'MT_MESSAGE_RECEIVED';
+ public static THREAD_ID: number = 0;
+
+ private _threadId: number;
+ private _participant: MessengerFriend;
+ private _groups: MessengerThreadChatGroup[];
+ private _lastUpdated: Date;
+ private _unreadCount: number;
+
+ constructor(participant: MessengerFriend)
+ {
+ this._threadId = ++MessengerThread.THREAD_ID;
+ this._participant = participant;
+ this._groups = [];
+ this._lastUpdated = new Date();
+ this._unreadCount = 0;
+ }
+
+ public addMessage(senderId: number, message: string, secondsSinceSent: number = 0, extraData: string = null, type: number = 0): MessengerThreadChat
+ {
+ const isGroupChat = (senderId < 0 && extraData);
+ const userId = isGroupChat ? GetGroupChatData(extraData).userId : senderId;
+
+ const group = this.getLastGroup(userId);
+
+ if(!group) return;
+
+ if(isGroupChat) group.type = MessengerGroupType.GROUP_CHAT;
+
+ const chat = new MessengerThreadChat(senderId, message, secondsSinceSent, extraData, type);
+
+ group.addChat(chat);
+
+ this._lastUpdated = new Date();
+
+ this._unreadCount++;
+
+ return chat;
+ }
+
+ private getLastGroup(userId: number): MessengerThreadChatGroup
+ {
+ let group = this._groups[(this._groups.length - 1)];
+
+ if(group && (group.userId === userId)) return group;
+
+ group = new MessengerThreadChatGroup(userId);
+
+ this._groups.push(group);
+
+ return group;
+ }
+
+ public setRead(): void
+ {
+ this._unreadCount = 0;
+ }
+
+ public get threadId(): number
+ {
+ return this._threadId;
+ }
+
+ public get participant(): MessengerFriend
+ {
+ return this._participant;
+ }
+
+ public get groups(): MessengerThreadChatGroup[]
+ {
+ return this._groups;
+ }
+
+ public get lastUpdated(): Date
+ {
+ return this._lastUpdated;
+ }
+
+ public get unreadCount(): number
+ {
+ return this._unreadCount;
+ }
+
+ public get unread(): boolean
+ {
+ return (this._unreadCount > 0);
+ }
+}
diff --git a/Coolui v3 test/src/api/friends/MessengerThreadChat.ts b/Coolui v3 test/src/api/friends/MessengerThreadChat.ts
new file mode 100644
index 0000000000..2927feccbd
--- /dev/null
+++ b/Coolui v3 test/src/api/friends/MessengerThreadChat.ts
@@ -0,0 +1,54 @@
+export class MessengerThreadChat
+{
+ public static CHAT: number = 0;
+ public static ROOM_INVITE: number = 1;
+ public static STATUS_NOTIFICATION: number = 2;
+ public static SECURITY_NOTIFICATION: number = 3;
+
+ private _type: number;
+ private _senderId: number;
+ private _message: string;
+ private _secondsSinceSent: number;
+ private _extraData: string;
+ private _date: Date;
+
+ constructor(senderId: number, message: string, secondsSinceSent: number = 0, extraData: string = null, type: number = 0)
+ {
+ this._type = type;
+ this._senderId = senderId;
+ this._message = message;
+ this._secondsSinceSent = secondsSinceSent;
+ this._extraData = extraData;
+ this._date = new Date();
+ }
+
+ public get type(): number
+ {
+ return this._type;
+ }
+
+ public get senderId(): number
+ {
+ return this._senderId;
+ }
+
+ public get message(): string
+ {
+ return this._message;
+ }
+
+ public get secondsSinceSent(): number
+ {
+ return this._secondsSinceSent;
+ }
+
+ public get extraData(): string
+ {
+ return this._extraData;
+ }
+
+ public get date(): Date
+ {
+ return this._date;
+ }
+}
diff --git a/Coolui v3 test/src/api/friends/MessengerThreadChatGroup.ts b/Coolui v3 test/src/api/friends/MessengerThreadChatGroup.ts
new file mode 100644
index 0000000000..1668aedce8
--- /dev/null
+++ b/Coolui v3 test/src/api/friends/MessengerThreadChatGroup.ts
@@ -0,0 +1,41 @@
+import { MessengerGroupType } from './MessengerGroupType';
+import { MessengerThreadChat } from './MessengerThreadChat';
+
+export class MessengerThreadChatGroup
+{
+ private _userId: number;
+ private _chats: MessengerThreadChat[];
+ private _type: number;
+
+ constructor(userId: number, type = MessengerGroupType.PRIVATE_CHAT)
+ {
+ this._userId = userId;
+ this._chats = [];
+ this._type = type;
+ }
+
+ public addChat(message: MessengerThreadChat): void
+ {
+ this._chats.push(message);
+ }
+
+ public get userId(): number
+ {
+ return this._userId;
+ }
+
+ public get chats(): MessengerThreadChat[]
+ {
+ return this._chats;
+ }
+
+ public get type(): number
+ {
+ return this._type;
+ }
+
+ public set type(type: number)
+ {
+ this._type = type;
+ }
+}
diff --git a/Coolui v3 test/src/api/friends/OpenMessengerChat.ts b/Coolui v3 test/src/api/friends/OpenMessengerChat.ts
new file mode 100644
index 0000000000..6050f2f1ff
--- /dev/null
+++ b/Coolui v3 test/src/api/friends/OpenMessengerChat.ts
@@ -0,0 +1,7 @@
+import { CreateLinkEvent } from '@nitrots/nitro-renderer';
+
+export function OpenMessengerChat(friendId: number = 0): void
+{
+ if(friendId === 0) CreateLinkEvent('friends-messenger/toggle');
+ else CreateLinkEvent(`friends-messenger/${ friendId }`);
+}
diff --git a/Coolui v3 test/src/api/friends/index.ts b/Coolui v3 test/src/api/friends/index.ts
new file mode 100644
index 0000000000..ce1ed60a0a
--- /dev/null
+++ b/Coolui v3 test/src/api/friends/index.ts
@@ -0,0 +1,11 @@
+export * from './GetGroupChatData';
+export * from './IGroupChatData';
+export * from './MessengerFriend';
+export * from './MessengerGroupType';
+export * from './MessengerIconState';
+export * from './MessengerRequest';
+export * from './MessengerSettings';
+export * from './MessengerThread';
+export * from './MessengerThreadChat';
+export * from './MessengerThreadChatGroup';
+export * from './OpenMessengerChat';
diff --git a/Coolui v3 test/src/api/groups/GetGroupInformation.ts b/Coolui v3 test/src/api/groups/GetGroupInformation.ts
new file mode 100644
index 0000000000..14fe326757
--- /dev/null
+++ b/Coolui v3 test/src/api/groups/GetGroupInformation.ts
@@ -0,0 +1,7 @@
+import { GroupInformationComposer } from '@nitrots/nitro-renderer';
+import { SendMessageComposer } from '../nitro';
+
+export function GetGroupInformation(groupId: number): void
+{
+ SendMessageComposer(new GroupInformationComposer(groupId, true));
+}
diff --git a/Coolui v3 test/src/api/groups/GetGroupManager.ts b/Coolui v3 test/src/api/groups/GetGroupManager.ts
new file mode 100644
index 0000000000..2044a45a9f
--- /dev/null
+++ b/Coolui v3 test/src/api/groups/GetGroupManager.ts
@@ -0,0 +1,6 @@
+import { CreateLinkEvent } from '@nitrots/nitro-renderer';
+
+export function GetGroupManager(groupId: number): void
+{
+ CreateLinkEvent(`groups/manage/${ groupId }`);
+}
diff --git a/Coolui v3 test/src/api/groups/GetGroupMembers.ts b/Coolui v3 test/src/api/groups/GetGroupMembers.ts
new file mode 100644
index 0000000000..9e10b0101b
--- /dev/null
+++ b/Coolui v3 test/src/api/groups/GetGroupMembers.ts
@@ -0,0 +1,7 @@
+import { CreateLinkEvent } from '@nitrots/nitro-renderer';
+
+export function GetGroupMembers(groupId: number, levelId?: number): void
+{
+ if(!levelId) CreateLinkEvent(`group-members/${ groupId }`);
+ else CreateLinkEvent(`group-members/${ groupId }/${ levelId }`);
+}
diff --git a/Coolui v3 test/src/api/groups/GroupBadgePart.ts b/Coolui v3 test/src/api/groups/GroupBadgePart.ts
new file mode 100644
index 0000000000..bb6f5e70a9
--- /dev/null
+++ b/Coolui v3 test/src/api/groups/GroupBadgePart.ts
@@ -0,0 +1,30 @@
+export class GroupBadgePart
+{
+ public static BASE: string = 'b';
+ public static SYMBOL: string = 's';
+
+ public type: string;
+ public key: number;
+ public color: number;
+ public position: number;
+
+ constructor(type: string, key?: number, color?: number, position?: number)
+ {
+ this.type = type;
+ this.key = key ? key : 0;
+ this.color = color ? color : 0;
+ this.position = position ? position : 4;
+ }
+
+ public get code(): string
+ {
+ if((this.key === 0) && (this.type !== GroupBadgePart.BASE)) return null;
+
+ return GroupBadgePart.getCode(this.type, this.key, this.color, this.position);
+ }
+
+ public static getCode(type: string, key: number, color: number, position: number): string
+ {
+ return type + (key < 10 ? '0' : '') + key + (color < 10 ? '0' : '') + color + position;
+ }
+}
diff --git a/Coolui v3 test/src/api/groups/GroupMembershipType.ts b/Coolui v3 test/src/api/groups/GroupMembershipType.ts
new file mode 100644
index 0000000000..532c836411
--- /dev/null
+++ b/Coolui v3 test/src/api/groups/GroupMembershipType.ts
@@ -0,0 +1,6 @@
+export class GroupMembershipType
+{
+ public static NOT_MEMBER: number = 0;
+ public static MEMBER: number = 1;
+ public static REQUEST_PENDING: number = 2;
+}
diff --git a/Coolui v3 test/src/api/groups/GroupType.ts b/Coolui v3 test/src/api/groups/GroupType.ts
new file mode 100644
index 0000000000..744c6c6dae
--- /dev/null
+++ b/Coolui v3 test/src/api/groups/GroupType.ts
@@ -0,0 +1,6 @@
+export class GroupType
+{
+ public static REGULAR: number = 0;
+ public static EXCLUSIVE: number = 1;
+ public static PRIVATE: number = 2;
+}
diff --git a/Coolui v3 test/src/api/groups/IGroupCustomize.ts b/Coolui v3 test/src/api/groups/IGroupCustomize.ts
new file mode 100644
index 0000000000..44fc4ff3e9
--- /dev/null
+++ b/Coolui v3 test/src/api/groups/IGroupCustomize.ts
@@ -0,0 +1,8 @@
+export interface IGroupCustomize
+{
+ badgeBases: { id: number, images: string[] }[];
+ badgeSymbols: { id: number, images: string[] }[];
+ badgePartColors: { id: number, color: string }[];
+ groupColorsA: { id: number, color: string }[];
+ groupColorsB: { id: number, color: string }[];
+}
diff --git a/Coolui v3 test/src/api/groups/IGroupData.ts b/Coolui v3 test/src/api/groups/IGroupData.ts
new file mode 100644
index 0000000000..bb65b4912a
--- /dev/null
+++ b/Coolui v3 test/src/api/groups/IGroupData.ts
@@ -0,0 +1,13 @@
+import { GroupBadgePart } from './GroupBadgePart';
+
+export interface IGroupData
+{
+ groupId: number;
+ groupName: string;
+ groupDescription: string;
+ groupHomeroomId: number;
+ groupState: number;
+ groupCanMembersDecorate: boolean;
+ groupColors: number[];
+ groupBadgeParts: GroupBadgePart[];
+}
diff --git a/Coolui v3 test/src/api/groups/ToggleFavoriteGroup.ts b/Coolui v3 test/src/api/groups/ToggleFavoriteGroup.ts
new file mode 100644
index 0000000000..82385d4fcb
--- /dev/null
+++ b/Coolui v3 test/src/api/groups/ToggleFavoriteGroup.ts
@@ -0,0 +1,7 @@
+import { GroupFavoriteComposer, GroupUnfavoriteComposer, HabboGroupEntryData } from '@nitrots/nitro-renderer';
+import { SendMessageComposer } from '../nitro';
+
+export const ToggleFavoriteGroup = (group: HabboGroupEntryData) =>
+{
+ SendMessageComposer(group.favourite ? new GroupUnfavoriteComposer(group.groupId) : new GroupFavoriteComposer(group.groupId));
+};
diff --git a/Coolui v3 test/src/api/groups/TryJoinGroup.ts b/Coolui v3 test/src/api/groups/TryJoinGroup.ts
new file mode 100644
index 0000000000..63959bf4e2
--- /dev/null
+++ b/Coolui v3 test/src/api/groups/TryJoinGroup.ts
@@ -0,0 +1,4 @@
+import { GroupJoinComposer } from '@nitrots/nitro-renderer';
+import { SendMessageComposer } from '../nitro';
+
+export const TryJoinGroup = (groupId: number) => SendMessageComposer(new GroupJoinComposer(groupId));
diff --git a/Coolui v3 test/src/api/groups/index.ts b/Coolui v3 test/src/api/groups/index.ts
new file mode 100644
index 0000000000..4842948b34
--- /dev/null
+++ b/Coolui v3 test/src/api/groups/index.ts
@@ -0,0 +1,10 @@
+export * from './GetGroupInformation';
+export * from './GetGroupManager';
+export * from './GetGroupMembers';
+export * from './GroupBadgePart';
+export * from './GroupMembershipType';
+export * from './GroupType';
+export * from './IGroupCustomize';
+export * from './IGroupData';
+export * from './ToggleFavoriteGroup';
+export * from './TryJoinGroup';
diff --git a/Coolui v3 test/src/api/guide-tool/GuideSessionState.ts b/Coolui v3 test/src/api/guide-tool/GuideSessionState.ts
new file mode 100644
index 0000000000..c5e24f3753
--- /dev/null
+++ b/Coolui v3 test/src/api/guide-tool/GuideSessionState.ts
@@ -0,0 +1,23 @@
+export class GuideSessionState
+{
+ public static readonly NONE: string = 'NONE';
+ public static readonly ERROR: string = 'ERROR';
+ public static readonly REJECTED: string = 'REJECTED';
+ public static readonly USER_CREATE: string = 'USER_CREATE';
+ public static readonly USER_PENDING: string = 'USER_PENDING';
+ public static readonly USER_ONGOING: string = 'USER_ONGOING';
+ public static readonly USER_FEEDBACK: string = 'USER_FEEDBACK';
+ public static readonly USER_NO_HELPERS: string = 'USER_NO_HELPERS';
+ public static readonly USER_SOMETHING_WRONG: string = 'USER_SOMETHING_WRONG';
+ public static readonly USER_THANKS: string = 'USER_THANKS';
+ public static readonly USER_GUIDE_DISCONNECTED: string = 'USER_GUIDE_DISCONNECTED';
+ public static readonly GUIDE_TOOL_MENU: string = 'GUIDE_TOOL_MENU';
+ public static readonly GUIDE_ACCEPT: string = 'GUIDE_ACCEPT';
+ public static readonly GUIDE_ONGOING: string = 'GUIDE_ONGOING';
+ public static readonly GUIDE_CLOSED: string = 'GUIDE_CLOSED';
+ public static readonly GUARDIAN_CHAT_REVIEW_ACCEPT: string = 'GUARDIAN_CHAT_REVIEW_ACCEPT';
+ public static readonly GUARDIAN_CHAT_REVIEW_WAIT_FOR_VOTERS: string = 'GUARDIAN_CHAT_REVIEW_WAIT_FOR_VOTERS';
+ public static readonly GUARDIAN_CHAT_REVIEW_VOTE: string = 'GUARDIAN_CHAT_REVIEW_VOTE';
+ public static readonly GUARDIAN_CHAT_REVIEW_WAIT_FOR_RESULTS: string = 'GUARDIAN_CHAT_REVIEW_WAIT_FOR_RESULTS';
+ public static readonly GUARDIAN_CHAT_REVIEW_RESULTS: string = 'GUARDIAN_CHAT_REVIEW_RESULTS';
+}
diff --git a/Coolui v3 test/src/api/guide-tool/GuideToolMessage.ts b/Coolui v3 test/src/api/guide-tool/GuideToolMessage.ts
new file mode 100644
index 0000000000..3ed87be5de
--- /dev/null
+++ b/Coolui v3 test/src/api/guide-tool/GuideToolMessage.ts
@@ -0,0 +1,21 @@
+export class GuideToolMessage
+{
+ private _message: string;
+ private _roomId: number;
+
+ constructor(message: string, roomId?: number)
+ {
+ this._message = message;
+ this._roomId = roomId;
+ }
+
+ public get message(): string
+ {
+ return this._message;
+ }
+
+ public get roomId(): number
+ {
+ return this._roomId;
+ }
+}
diff --git a/Coolui v3 test/src/api/guide-tool/GuideToolMessageGroup.ts b/Coolui v3 test/src/api/guide-tool/GuideToolMessageGroup.ts
new file mode 100644
index 0000000000..bf03c9b9ea
--- /dev/null
+++ b/Coolui v3 test/src/api/guide-tool/GuideToolMessageGroup.ts
@@ -0,0 +1,28 @@
+import { GuideToolMessage } from './GuideToolMessage';
+
+export class GuideToolMessageGroup
+{
+ private _userId: number;
+ private _messages: GuideToolMessage[];
+
+ constructor(userId: number)
+ {
+ this._userId = userId;
+ this._messages = [];
+ }
+
+ public addChat(message: GuideToolMessage): void
+ {
+ this._messages.push(message);
+ }
+
+ public get userId(): number
+ {
+ return this._userId;
+ }
+
+ public get messages(): GuideToolMessage[]
+ {
+ return this._messages;
+ }
+}
diff --git a/Coolui v3 test/src/api/guide-tool/index.ts b/Coolui v3 test/src/api/guide-tool/index.ts
new file mode 100644
index 0000000000..1400adc9bd
--- /dev/null
+++ b/Coolui v3 test/src/api/guide-tool/index.ts
@@ -0,0 +1,3 @@
+export * from './GuideSessionState';
+export * from './GuideToolMessage';
+export * from './GuideToolMessageGroup';
diff --git a/Coolui v3 test/src/api/hc-center/ClubStatus.ts b/Coolui v3 test/src/api/hc-center/ClubStatus.ts
new file mode 100644
index 0000000000..e3cba00097
--- /dev/null
+++ b/Coolui v3 test/src/api/hc-center/ClubStatus.ts
@@ -0,0 +1,6 @@
+export class ClubStatus
+{
+ public static ACTIVE: string = 'active';
+ public static NONE: string = 'none';
+ public static EXPIRED: string = 'expired';
+}
diff --git a/Coolui v3 test/src/api/hc-center/GetClubBadge.ts b/Coolui v3 test/src/api/hc-center/GetClubBadge.ts
new file mode 100644
index 0000000000..79cf9790d0
--- /dev/null
+++ b/Coolui v3 test/src/api/hc-center/GetClubBadge.ts
@@ -0,0 +1,11 @@
+const DEFAULT_BADGE: string = 'HC1';
+const BADGES: string[] = [ 'ACH_VipHC1', 'ACH_VipHC2', 'ACH_VipHC3', 'ACH_VipHC4', 'ACH_VipHC5', 'HC1', 'HC2', 'HC3', 'HC4', 'HC5' ];
+
+export const GetClubBadge = (badgeCodes: string[]) =>
+{
+ let badgeCode: string = null;
+
+ BADGES.forEach(badge => ((badgeCodes.indexOf(badge) > -1) && (badgeCode = badge)));
+
+ return (badgeCode || DEFAULT_BADGE);
+};
diff --git a/Coolui v3 test/src/api/hc-center/index.ts b/Coolui v3 test/src/api/hc-center/index.ts
new file mode 100644
index 0000000000..cee8f692d8
--- /dev/null
+++ b/Coolui v3 test/src/api/hc-center/index.ts
@@ -0,0 +1,2 @@
+export * from './ClubStatus';
+export * from './GetClubBadge';
diff --git a/Coolui v3 test/src/api/help/CallForHelpResult.ts b/Coolui v3 test/src/api/help/CallForHelpResult.ts
new file mode 100644
index 0000000000..37e7ea1b78
--- /dev/null
+++ b/Coolui v3 test/src/api/help/CallForHelpResult.ts
@@ -0,0 +1,5 @@
+export class CallForHelpResult
+{
+ public static readonly TOO_MANY_PENDING_CALLS_CODE = 1;
+ public static readonly HAS_ABUSIVE_CALL_CODE = 2;
+}
diff --git a/Coolui v3 test/src/api/help/GetCloseReasonKey.ts b/Coolui v3 test/src/api/help/GetCloseReasonKey.ts
new file mode 100644
index 0000000000..8658492f7a
--- /dev/null
+++ b/Coolui v3 test/src/api/help/GetCloseReasonKey.ts
@@ -0,0 +1,8 @@
+export const GetCloseReasonKey = (code: number) =>
+{
+ if(code === 1) return 'useless';
+
+ if(code === 2) return 'abusive';
+
+ return 'resolved';
+};
diff --git a/Coolui v3 test/src/api/help/IHelpReport.ts b/Coolui v3 test/src/api/help/IHelpReport.ts
new file mode 100644
index 0000000000..861170728e
--- /dev/null
+++ b/Coolui v3 test/src/api/help/IHelpReport.ts
@@ -0,0 +1,19 @@
+import { IChatEntry } from '../chat-history';
+
+export interface IHelpReport
+{
+ reportType: number;
+ reportedUserId: number;
+ reportedChats: IChatEntry[];
+ cfhCategory: number;
+ cfhTopic: number;
+ roomId: number;
+ roomName: string;
+ groupId: number;
+ threadId: number;
+ messageId: number;
+ extraData: string;
+ roomObjectId: number;
+ message: string;
+ currentStep: number;
+}
diff --git a/Coolui v3 test/src/api/help/IReportedUser.ts b/Coolui v3 test/src/api/help/IReportedUser.ts
new file mode 100644
index 0000000000..90a3887eb6
--- /dev/null
+++ b/Coolui v3 test/src/api/help/IReportedUser.ts
@@ -0,0 +1,5 @@
+export interface IReportedUser
+{
+ id: number;
+ username: string;
+}
diff --git a/Coolui v3 test/src/api/help/ReportState.ts b/Coolui v3 test/src/api/help/ReportState.ts
new file mode 100644
index 0000000000..ae3a3bd3b6
--- /dev/null
+++ b/Coolui v3 test/src/api/help/ReportState.ts
@@ -0,0 +1,8 @@
+export class ReportState
+{
+ public static readonly SELECT_USER = 0;
+ public static readonly SELECT_CHATS = 1;
+ public static readonly SELECT_TOPICS = 2;
+ public static readonly INPUT_REPORT_MESSAGE = 3;
+ public static readonly REPORT_SUMMARY = 4;
+}
diff --git a/Coolui v3 test/src/api/help/ReportType.ts b/Coolui v3 test/src/api/help/ReportType.ts
new file mode 100644
index 0000000000..24eb7aecf3
--- /dev/null
+++ b/Coolui v3 test/src/api/help/ReportType.ts
@@ -0,0 +1,11 @@
+export class ReportType
+{
+ public static readonly EMERGENCY = 1;
+ public static readonly GUIDE = 2;
+ public static readonly IM = 3;
+ public static readonly ROOM = 4;
+ public static readonly BULLY = 6;
+ public static readonly THREAD = 7;
+ public static readonly MESSAGE = 8;
+ public static readonly PHOTO = 9;
+}
diff --git a/Coolui v3 test/src/api/help/index.ts b/Coolui v3 test/src/api/help/index.ts
new file mode 100644
index 0000000000..6fa2045502
--- /dev/null
+++ b/Coolui v3 test/src/api/help/index.ts
@@ -0,0 +1,6 @@
+export * from './CallForHelpResult';
+export * from './GetCloseReasonKey';
+export * from './IHelpReport';
+export * from './IReportedUser';
+export * from './ReportState';
+export * from './ReportType';
diff --git a/Coolui v3 test/src/api/index.ts b/Coolui v3 test/src/api/index.ts
new file mode 100644
index 0000000000..7089277d4f
--- /dev/null
+++ b/Coolui v3 test/src/api/index.ts
@@ -0,0 +1,28 @@
+export * from './GetRendererVersion';
+export * from './GetUIVersion';
+export * from './achievements';
+export * from './avatar';
+export * from './camera';
+export * from './campaign';
+export * from './catalog';
+export * from './chat-history';
+export * from './events';
+export * from './friends';
+export * from './groups';
+export * from './guide-tool';
+export * from './hc-center';
+export * from './help';
+export * from './inventory';
+export * from './mod-tools';
+export * from './navigator';
+export * from './nitro';
+export * from './nitro/room';
+export * from './nitro/session';
+export * from './notification';
+export * from './purse';
+export * from './room';
+export * from './room/events';
+export * from './room/widgets';
+export * from './user';
+export * from './utils';
+export * from './wired';
diff --git a/Coolui v3 test/src/api/inventory/FurniCategory.ts b/Coolui v3 test/src/api/inventory/FurniCategory.ts
new file mode 100644
index 0000000000..65289472c6
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/FurniCategory.ts
@@ -0,0 +1,26 @@
+export class FurniCategory
+{
+ public static DEFAULT: number = 1;
+ public static WALL_PAPER: number = 2;
+ public static FLOOR: number = 3;
+ public static LANDSCAPE: number = 4;
+ public static POST_IT: number = 5;
+ public static POSTER: number = 6;
+ public static SOUND_SET: number = 7;
+ public static TRAX_SONG: number = 8;
+ public static PRESENT: number = 9;
+ public static ECOTRON_BOX: number = 10;
+ public static TROPHY: number = 11;
+ public static CREDIT_FURNI: number = 12;
+ public static PET_SHAMPOO: number = 13;
+ public static PET_CUSTOM_PART: number = 14;
+ public static PET_CUSTOM_PART_SHAMPOO: number = 15;
+ public static PET_SADDLE: number = 16;
+ public static GUILD_FURNI: number = 17;
+ public static GAME_FURNI: number = 18;
+ public static MONSTERPLANT_SEED: number = 19;
+ public static MONSTERPLANT_REVIVAL: number = 20;
+ public static MONSTERPLANT_REBREED: number = 21;
+ public static MONSTERPLANT_FERTILIZE: number = 22;
+ public static FIGURE_PURCHASABLE_SET: number = 23;
+}
diff --git a/Coolui v3 test/src/api/inventory/FurnitureItem.ts b/Coolui v3 test/src/api/inventory/FurnitureItem.ts
new file mode 100644
index 0000000000..655d1d372f
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/FurnitureItem.ts
@@ -0,0 +1,245 @@
+import { GetTickerTime, IFurnitureItemData, IObjectData } from '@nitrots/nitro-renderer';
+import { IFurnitureItem } from './IFurnitureItem';
+
+export class FurnitureItem implements IFurnitureItem
+{
+ private _expirationTimeStamp: number;
+ private _isWallItem: boolean;
+ private _songId: number;
+ private _locked: boolean;
+ private _id: number;
+ private _ref: number;
+ private _category: number;
+ private _type: number;
+ private _stuffData: IObjectData;
+ private _extra: number;
+ private _recyclable: boolean;
+ private _tradeable: boolean;
+ private _groupable: boolean;
+ private _sellable: boolean;
+ private _secondsToExpiration: number;
+ private _hasRentPeriodStarted: boolean;
+ private _creationDay: number;
+ private _creationMonth: number;
+ private _creationYear: number;
+ private _slotId: string;
+ private _isRented: boolean;
+ private _flatId: number;
+
+ constructor(parser: IFurnitureItemData)
+ {
+ if(!parser) return;
+
+ this._locked = false;
+ this._id = parser.itemId;
+ this._type = parser.spriteId;
+ this._ref = parser.ref;
+ this._category = parser.category;
+ this._groupable = ((parser.isGroupable) && (!(parser.rentable)));
+ this._tradeable = parser.tradable;
+ this._recyclable = parser.isRecycleable;
+ this._sellable = parser.sellable;
+ this._stuffData = parser.stuffData;
+ this._extra = parser.extra;
+ this._secondsToExpiration = parser.secondsToExpiration;
+ this._expirationTimeStamp = parser.expirationTimeStamp;
+ this._hasRentPeriodStarted = parser.hasRentPeriodStarted;
+ this._creationDay = parser.creationDay;
+ this._creationMonth = parser.creationMonth;
+ this._creationYear = parser.creationYear;
+ this._slotId = parser.slotId;
+ this._songId = parser.songId;
+ this._flatId = parser.flatId;
+ this._isRented = parser.rentable;
+ this._isWallItem = parser.isWallItem;
+ }
+
+ public get rentable(): boolean
+ {
+ return this._isRented;
+ }
+
+ public get id(): number
+ {
+ return this._id;
+ }
+
+ public get ref(): number
+ {
+ return this._ref;
+ }
+
+ public get category(): number
+ {
+ return this._category;
+ }
+
+ public get type(): number
+ {
+ return this._type;
+ }
+
+ public get stuffData(): IObjectData
+ {
+ return this._stuffData;
+ }
+
+ public set stuffData(k: IObjectData)
+ {
+ this._stuffData = k;
+ }
+
+ public get extra(): number
+ {
+ return this._extra;
+ }
+
+ public get recyclable(): boolean
+ {
+ return this._recyclable;
+ }
+
+ public get isTradable(): boolean
+ {
+ return this._tradeable;
+ }
+
+ public get isGroupable(): boolean
+ {
+ return this._groupable;
+ }
+
+ public get sellable(): boolean
+ {
+ return this._sellable;
+ }
+
+ public get secondsToExpiration(): number
+ {
+ if(this._secondsToExpiration === -1) return -1;
+
+ let time = -1;
+
+ if(this._hasRentPeriodStarted)
+ {
+ time = (this._secondsToExpiration - ((GetTickerTime() - this._expirationTimeStamp) / 1000));
+
+ if(time < 0) time = 0;
+ }
+ else
+ {
+ time = this._secondsToExpiration;
+ }
+
+ return time;
+ }
+
+ public get creationDay(): number
+ {
+ return this._creationDay;
+ }
+
+ public get creationMonth(): number
+ {
+ return this._creationMonth;
+ }
+
+ public get creationYear(): number
+ {
+ return this._creationYear;
+ }
+
+ public get slotId(): string
+ {
+ return this._slotId;
+ }
+
+ public get songId(): number
+ {
+ return this._songId;
+ }
+
+ public get locked(): boolean
+ {
+ return this._locked;
+ }
+
+ public set locked(k: boolean)
+ {
+ this._locked = k;
+ }
+
+ public get flatId(): number
+ {
+ return this._flatId;
+ }
+
+ public get isWallItem(): boolean
+ {
+ return this._isWallItem;
+ }
+
+ public get hasRentPeriodStarted(): boolean
+ {
+ return this._hasRentPeriodStarted;
+ }
+
+ public get expirationTimeStamp(): number
+ {
+ return this._expirationTimeStamp;
+ }
+
+ public update(parser: IFurnitureItemData): void
+ {
+ this._type = parser.spriteId;
+ this._ref = parser.ref;
+ this._category = parser.category;
+ this._groupable = (parser.isGroupable && !parser.rentable);
+ this._tradeable = parser.tradable;
+ this._recyclable = parser.isRecycleable;
+ this._sellable = parser.sellable;
+ this._stuffData = parser.stuffData;
+ this._extra = parser.extra;
+ this._secondsToExpiration = parser.secondsToExpiration;
+ this._expirationTimeStamp = parser.expirationTimeStamp;
+ this._hasRentPeriodStarted = parser.hasRentPeriodStarted;
+ this._creationDay = parser.creationDay;
+ this._creationMonth = parser.creationMonth;
+ this._creationYear = parser.creationYear;
+ this._slotId = parser.slotId;
+ this._songId = parser.songId;
+ this._flatId = parser.flatId;
+ this._isRented = parser.rentable;
+ this._isWallItem = parser.isWallItem;
+ }
+
+ public clone(): FurnitureItem
+ {
+ const item = new FurnitureItem(null);
+
+ item._expirationTimeStamp = this._expirationTimeStamp;
+ item._isWallItem = this._isWallItem;
+ item._songId = this._songId;
+ item._locked = this._locked;
+ item._id = this._id;
+ item._ref = this._ref;
+ item._category = this._category;
+ item._type = this._type;
+ item._stuffData = this._stuffData;
+ item._extra = this._extra;
+ item._recyclable = this._recyclable;
+ item._tradeable = this._tradeable;
+ item._groupable = this._groupable;
+ item._sellable = this._sellable;
+ item._secondsToExpiration = this._secondsToExpiration;
+ item._hasRentPeriodStarted = this._hasRentPeriodStarted;
+ item._creationDay = this._creationDay;
+ item._creationMonth = this._creationMonth;
+ item._creationYear = this._creationYear;
+ item._slotId = this._slotId;
+ item._isRented = this._isRented;
+ item._flatId = this._flatId;
+
+ return item;
+ }
+}
diff --git a/Coolui v3 test/src/api/inventory/FurnitureUtilities.ts b/Coolui v3 test/src/api/inventory/FurnitureUtilities.ts
new file mode 100644
index 0000000000..93b9765e8d
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/FurnitureUtilities.ts
@@ -0,0 +1,171 @@
+import { FurnitureListItemParser, GetRoomEngine, IObjectData } from '@nitrots/nitro-renderer';
+import { FurniCategory } from './FurniCategory';
+import { FurnitureItem } from './FurnitureItem';
+import { GroupItem } from './GroupItem';
+
+export const createGroupItem = (type: number, category: number, stuffData: IObjectData, extra: number = NaN) => new GroupItem(type, category, GetRoomEngine(), stuffData, extra);
+
+const addSingleFurnitureItem = (set: GroupItem[], item: FurnitureItem, unseen: boolean) =>
+{
+ const groupItems: GroupItem[] = [];
+
+ for(const groupItem of set)
+ {
+ if(groupItem.type === item.type) groupItems.push(groupItem);
+ }
+
+ for(const groupItem of groupItems)
+ {
+ if(groupItem.getItemById(item.id)) return groupItem;
+ }
+
+ const groupItem = createGroupItem(item.type, item.category, item.stuffData, item.extra);
+
+ groupItem.push(item);
+
+ if(unseen)
+ {
+ groupItem.hasUnseenItems = true;
+
+ set.unshift(groupItem);
+ }
+ else
+ {
+ set.push(groupItem);
+ }
+
+ return groupItem;
+};
+
+const addGroupableFurnitureItem = (set: GroupItem[], item: FurnitureItem, unseen: boolean) =>
+{
+ let existingGroup: GroupItem = null;
+
+ for(const groupItem of set)
+ {
+ if((groupItem.type === item.type) && (groupItem.isWallItem === item.isWallItem) && groupItem.isGroupable)
+ {
+ if(item.category === FurniCategory.POSTER)
+ {
+ if(groupItem.stuffData.getLegacyString() === item.stuffData.getLegacyString())
+ {
+ existingGroup = groupItem;
+
+ break;
+ }
+ }
+
+ else if(item.category === FurniCategory.GUILD_FURNI)
+ {
+ if(item.stuffData.compare(groupItem.stuffData))
+ {
+ existingGroup = groupItem;
+
+ break;
+ }
+ }
+
+ else
+ {
+ existingGroup = groupItem;
+
+ break;
+ }
+ }
+ }
+
+ if(existingGroup)
+ {
+ existingGroup.push(item);
+
+ if(unseen)
+ {
+ existingGroup.hasUnseenItems = true;
+
+ const index = set.indexOf(existingGroup);
+
+ if(index >= 0) set.splice(index, 1);
+
+ set.unshift(existingGroup);
+ }
+
+ return existingGroup;
+ }
+
+ existingGroup = createGroupItem(item.type, item.category, item.stuffData, item.extra);
+
+ existingGroup.push(item);
+
+ if(unseen)
+ {
+ existingGroup.hasUnseenItems = true;
+
+ set.unshift(existingGroup);
+ }
+ else
+ {
+ set.push(existingGroup);
+ }
+
+ return existingGroup;
+};
+
+export const addFurnitureItem = (set: GroupItem[], item: FurnitureItem, unseen: boolean) =>
+{
+ if(!item.isGroupable)
+ {
+ addSingleFurnitureItem(set, item, unseen);
+ }
+ else
+ {
+ addGroupableFurnitureItem(set, item, unseen);
+ }
+};
+
+export const mergeFurniFragments = (fragment: Map, totalFragments: number, fragmentNumber: number, fragments: Map[]) =>
+{
+ if(totalFragments === 1) return fragment;
+
+ fragments[fragmentNumber] = fragment;
+
+ for(const frag of fragments)
+ {
+ if(!frag) return null;
+ }
+
+ const merged: Map = new Map();
+
+ for(const frag of fragments)
+ {
+ for(const [ key, value ] of frag) merged.set(key, value);
+
+ frag.clear();
+ }
+
+ fragments = null;
+
+ return merged;
+};
+
+export const getAllItemIds = (groupItems: GroupItem[]) =>
+{
+ const itemIds: number[] = [];
+
+ for(const groupItem of groupItems)
+ {
+ let totalCount = groupItem.getTotalCount();
+
+ if(groupItem.category === FurniCategory.POST_IT) totalCount = 1;
+
+ let i = 0;
+
+ while(i < totalCount)
+ {
+ itemIds.push(groupItem.getItemByIndex(i).id);
+
+ i++;
+ }
+ }
+
+ return itemIds;
+};
diff --git a/Coolui v3 test/src/api/inventory/GroupItem.ts b/Coolui v3 test/src/api/inventory/GroupItem.ts
new file mode 100644
index 0000000000..8569321eb1
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/GroupItem.ts
@@ -0,0 +1,461 @@
+import { IObjectData, IRoomEngine } from '@nitrots/nitro-renderer';
+import { LocalizeText } from '../utils';
+import { FurniCategory } from './FurniCategory';
+import { FurnitureItem } from './FurnitureItem';
+import { IFurnitureItem } from './IFurnitureItem';
+
+export class GroupItem
+{
+ private _type: number;
+ private _category: number;
+ private _roomEngine: IRoomEngine;
+ private _stuffData: IObjectData;
+ private _extra: number;
+ private _isWallItem: boolean;
+ private _iconUrl: string;
+ private _name: string;
+ private _description: string;
+ private _locked: boolean;
+ private _selected: boolean;
+ private _hasUnseenItems: boolean;
+ private _items: FurnitureItem[];
+
+ constructor(type: number = -1, category: number = -1, roomEngine: IRoomEngine = null, stuffData: IObjectData = null, extra: number = -1)
+ {
+ this._type = type;
+ this._category = category;
+ this._roomEngine = roomEngine;
+ this._stuffData = stuffData;
+ this._extra = extra;
+ this._isWallItem = false;
+ this._iconUrl = null;
+ this._name = null;
+ this._description = null;
+ this._locked = false;
+ this._selected = false;
+ this._hasUnseenItems = false;
+ this._items = [];
+ }
+
+ public clone(): GroupItem
+ {
+ const groupItem = new GroupItem();
+
+ groupItem._type = this._type;
+ groupItem._category = this._category;
+ groupItem._roomEngine = this._roomEngine;
+ groupItem._stuffData = this._stuffData;
+ groupItem._extra = this._extra;
+ groupItem._isWallItem = this._isWallItem;
+ groupItem._iconUrl = this._iconUrl;
+ groupItem._name = this._name;
+ groupItem._description = this._description;
+ groupItem._locked = this._locked;
+ groupItem._selected = this._selected;
+ groupItem._hasUnseenItems = this._hasUnseenItems;
+ groupItem._items = this._items;
+
+ return groupItem;
+ }
+
+ public prepareGroup(): void
+ {
+ this.setIcon();
+ this.setName();
+ this.setDescription();
+ }
+
+ public dispose(): void
+ {
+
+ }
+
+ public getItemByIndex(index: number): FurnitureItem
+ {
+ return this._items[index];
+ }
+
+ public getItemById(id: number): FurnitureItem
+ {
+ for(const item of this._items)
+ {
+ if(item.id !== id) continue;
+
+ return item;
+ }
+
+ return null;
+ }
+
+ public getTradeItems(count: number): IFurnitureItem[]
+ {
+ const items: IFurnitureItem[] = [];
+
+ const furnitureItem = this.getLastItem();
+
+ if(!furnitureItem) return items;
+
+ let found = 0;
+ let i = 0;
+
+ while(i < this._items.length)
+ {
+ if(found >= count) break;
+
+ const item = this.getItemByIndex(i);
+
+ if(!item.locked && item.isTradable && (item.type === furnitureItem.type))
+ {
+ items.push(item);
+
+ found++;
+ }
+
+ i++;
+ }
+
+ return items;
+ }
+
+ public push(item: FurnitureItem): void
+ {
+ const items = [ ...this._items ];
+
+ let index = 0;
+
+ while(index < items.length)
+ {
+ let existingItem = items[index];
+
+ if(existingItem.id === item.id)
+ {
+ existingItem = existingItem.clone();
+
+ existingItem.locked = false;
+
+ items.splice(index, 1);
+
+ items.push(existingItem);
+
+ this._items = items;
+
+ return;
+ }
+
+ index++;
+ }
+
+ items.push(item);
+
+ this._items = items;
+
+ if(this._items.length === 1) this.prepareGroup();
+ }
+
+ public pop(): FurnitureItem
+ {
+ const items = [ ...this._items ];
+
+ let item: FurnitureItem = null;
+
+ if(items.length > 0)
+ {
+ const index = (items.length - 1);
+
+ item = items[index];
+
+ items.splice(index, 1);
+ }
+
+ this._items = items;
+
+ return item;
+ }
+
+ public remove(k: number): FurnitureItem
+ {
+ const items = [ ...this._items ];
+
+ let index = 0;
+
+ while(index < items.length)
+ {
+ let existingItem = items[index];
+
+ if(existingItem.id === k)
+ {
+ items.splice(index, 1);
+
+ this._items = items;
+
+ return existingItem;
+ }
+
+ index++;
+ }
+
+ return null;
+ }
+
+ public getTotalCount(): number
+ {
+ if(this._category === FurniCategory.POST_IT)
+ {
+ let count = 0;
+ let index = 0;
+
+ while(index < this._items.length)
+ {
+ const item = this.getItemByIndex(index);
+
+ count = (count + parseInt(item.stuffData.getLegacyString()));
+
+ index++;
+ }
+
+ return count;
+ }
+
+ return this._items.length;
+ }
+
+ public getUnlockedCount(): number
+ {
+ if(this.category === FurniCategory.POST_IT) return this.getTotalCount();
+
+ let count = 0;
+ let index = 0;
+
+ while(index < this._items.length)
+ {
+ const item = this.getItemByIndex(index);
+
+ if(!item.locked) count++;
+
+ index++;
+ }
+
+ return count;
+ }
+
+ public getLastItem(): FurnitureItem
+ {
+ if(!this._items.length) return null;
+
+ const item = this.getItemByIndex((this._items.length - 1));
+
+ return item;
+ }
+
+ public unlockAllItems(): void
+ {
+ const items = [ ...this._items ];
+
+ let index = 0;
+
+ while(index < items.length)
+ {
+ const item = items[index];
+
+ if(item.locked)
+ {
+ const newItem = item.clone();
+
+ newItem.locked = false;
+
+ items[index] = newItem;
+ }
+
+ index++;
+ }
+
+ this._items = items;
+ }
+
+ public lockItemIds(itemIds: number[]): boolean
+ {
+ const items = [ ...this._items ];
+
+ let index = 0;
+ let updated = false;
+
+ while(index < items.length)
+ {
+ const item = items[index];
+ const locked = (itemIds.indexOf(item.ref) >= 0);
+
+ if(item.locked !== locked)
+ {
+ updated = true;
+
+ const newItem = item.clone();
+
+ newItem.locked = locked;
+
+ items[index] = newItem;
+ }
+
+ index++;
+ }
+
+ this._items = items;
+
+ return updated;
+ }
+
+ private setName(): void
+ {
+ const k = this.getLastItem();
+
+ if(!k)
+ {
+ this._name = '';
+
+ return;
+ }
+
+ let key = '';
+
+ switch(this._category)
+ {
+ case FurniCategory.POSTER:
+ key = (('poster_' + k.stuffData.getLegacyString()) + '_name');
+ break;
+ case FurniCategory.TRAX_SONG:
+ this._name = 'SONG_NAME';
+ return;
+ default:
+ if(this.isWallItem)
+ {
+ key = ('wallItem.name.' + k.type);
+ }
+ else
+ {
+ key = ('roomItem.name.' + k.type);
+ }
+ }
+
+ this._name = LocalizeText(key);
+ }
+
+ private setDescription(): void
+ {
+ this._description = '';
+ }
+
+ private setIcon(): void
+ {
+ if(this._iconUrl) return;
+
+ let url = null;
+
+ if(this.isWallItem)
+ {
+ url = this._roomEngine.getFurnitureWallIconUrl(this._type, this._stuffData.getLegacyString());
+ }
+ else
+ {
+ url = this._roomEngine.getFurnitureFloorIconUrl(this._type);
+ }
+
+ if(!url) return;
+
+ this._iconUrl = url;
+ }
+
+ public get type(): number
+ {
+ return this._type;
+ }
+
+ public get category(): number
+ {
+ return this._category;
+ }
+
+ public get stuffData(): IObjectData
+ {
+ return this._stuffData;
+ }
+
+ public get extra(): number
+ {
+ return this._extra;
+ }
+
+ public get iconUrl(): string
+ {
+ return this._iconUrl;
+ }
+
+ public get name(): string
+ {
+ return this._name;
+ }
+
+ public get description(): string
+ {
+ return this._description;
+ }
+
+ public get hasUnseenItems(): boolean
+ {
+ return this._hasUnseenItems;
+ }
+
+ public set hasUnseenItems(flag: boolean)
+ {
+ this._hasUnseenItems = flag;
+ }
+
+ public get locked(): boolean
+ {
+ return this._locked;
+ }
+
+ public set locked(flag: boolean)
+ {
+ this._locked = flag;
+ }
+
+ public get selected(): boolean
+ {
+ return this._selected;
+ }
+
+ public set selected(flag: boolean)
+ {
+ this._selected = flag;
+ }
+
+ public get isWallItem(): boolean
+ {
+ const item = this.getItemByIndex(0);
+
+ return (item ? item.isWallItem : false);
+ }
+
+ public get isGroupable(): boolean
+ {
+ const item = this.getItemByIndex(0);
+
+ return (item ? item.isGroupable : false);
+ }
+
+ public get isSellable(): boolean
+ {
+ const item = this.getItemByIndex(0);
+
+ return (item ? item.sellable : false);
+ }
+
+ public get items(): FurnitureItem[]
+ {
+ return this._items;
+ }
+
+ public set items(items: FurnitureItem[])
+ {
+ this._items = items;
+ }
+}
diff --git a/Coolui v3 test/src/api/inventory/IBotItem.ts b/Coolui v3 test/src/api/inventory/IBotItem.ts
new file mode 100644
index 0000000000..0a370ba6c4
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/IBotItem.ts
@@ -0,0 +1,6 @@
+import { BotData } from '@nitrots/nitro-renderer';
+
+export interface IBotItem
+{
+ botData: BotData;
+}
diff --git a/Coolui v3 test/src/api/inventory/IFurnitureItem.ts b/Coolui v3 test/src/api/inventory/IFurnitureItem.ts
new file mode 100644
index 0000000000..435597d257
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/IFurnitureItem.ts
@@ -0,0 +1,17 @@
+import { IObjectData } from '@nitrots/nitro-renderer';
+
+export interface IFurnitureItem
+{
+ id: number;
+ ref: number;
+ type: number;
+ stuffData: IObjectData;
+ extra: number;
+ category: number;
+ recyclable: boolean;
+ isTradable: boolean;
+ isGroupable: boolean;
+ sellable: boolean;
+ locked: boolean;
+ isWallItem: boolean;
+}
diff --git a/Coolui v3 test/src/api/inventory/IPetItem.ts b/Coolui v3 test/src/api/inventory/IPetItem.ts
new file mode 100644
index 0000000000..910d5dffeb
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/IPetItem.ts
@@ -0,0 +1,6 @@
+import { PetData } from '@nitrots/nitro-renderer';
+
+export interface IPetItem
+{
+ petData: PetData;
+}
diff --git a/Coolui v3 test/src/api/inventory/IUnseenItemTracker.ts b/Coolui v3 test/src/api/inventory/IUnseenItemTracker.ts
new file mode 100644
index 0000000000..8a70a16638
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/IUnseenItemTracker.ts
@@ -0,0 +1,12 @@
+export interface IUnseenItemTracker
+{
+ dispose(): void;
+ resetCategory(category: number): boolean;
+ resetItems(category: number, itemIds: number[]): boolean;
+ isUnseen(category: number, itemId: number): boolean;
+ removeUnseen(category: number, itemId: number): boolean;
+ getIds(category: number): number[];
+ getCount(category: number): number;
+ getFullCount(): number;
+ addItems(category: number, itemIds: number[]): void;
+}
diff --git a/Coolui v3 test/src/api/inventory/InventoryUtilities.ts b/Coolui v3 test/src/api/inventory/InventoryUtilities.ts
new file mode 100644
index 0000000000..ac28cbdbfd
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/InventoryUtilities.ts
@@ -0,0 +1,117 @@
+import { CreateLinkEvent, FurniturePlacePaintComposer, GetRoomEngine, GetRoomSessionManager, RoomObjectCategory, RoomObjectPlacementSource, RoomObjectType } from '@nitrots/nitro-renderer';
+import { SendMessageComposer } from '../nitro';
+import { FurniCategory } from './FurniCategory';
+import { GroupItem } from './GroupItem';
+import { IBotItem } from './IBotItem';
+import { IPetItem } from './IPetItem';
+
+let objectMoverRequested = false;
+let itemIdInPlacing = -1;
+
+export const isObjectMoverRequested = () => objectMoverRequested;
+
+export const setObjectMoverRequested = (flag: boolean) => objectMoverRequested = flag;
+
+export const getPlacingItemId = () => itemIdInPlacing;
+
+export const setPlacingItemId = (id: number) => (itemIdInPlacing = id);
+
+export const cancelRoomObjectPlacement = () =>
+{
+ if(getPlacingItemId() === -1) return;
+
+ GetRoomEngine().cancelRoomObjectPlacement();
+
+ setPlacingItemId(-1);
+ setObjectMoverRequested(false);
+};
+
+export const attemptPetPlacement = (petItem: IPetItem, flag: boolean = false) =>
+{
+ const petData = petItem.petData;
+
+ if(!petData) return false;
+
+ const session = GetRoomSessionManager().getSession(1);
+
+ if(!session) return false;
+
+ if(!session.isRoomOwner && !session.allowPets) return false;
+
+ CreateLinkEvent('inventory/hide');
+
+ if(GetRoomEngine().processRoomObjectPlacement(RoomObjectPlacementSource.INVENTORY, -(petData.id), RoomObjectCategory.UNIT, RoomObjectType.PET, petData.figureData.figuredata))
+ {
+ setPlacingItemId(petData.id);
+ setObjectMoverRequested(true);
+ }
+
+ return true;
+};
+
+export const attemptItemPlacement = (groupItem: GroupItem, flag: boolean = false) =>
+{
+ if(!groupItem || !groupItem.getUnlockedCount()) return false;
+
+ const item = groupItem.getLastItem();
+
+ if(!item) return false;
+
+ if((item.category === FurniCategory.FLOOR) || (item.category === FurniCategory.WALL_PAPER) || (item.category === FurniCategory.LANDSCAPE))
+ {
+ if(flag) return false;
+
+ SendMessageComposer(new FurniturePlacePaintComposer(item.id));
+
+ return false;
+ }
+ else
+ {
+ CreateLinkEvent('inventory/hide');
+
+ let category = 0;
+ let isMoving = false;
+
+ if(item.isWallItem) category = RoomObjectCategory.WALL;
+ else category = RoomObjectCategory.FLOOR;
+
+ if((item.category === FurniCategory.POSTER)) // or external image from furnidata
+ {
+ isMoving = GetRoomEngine().processRoomObjectPlacement(RoomObjectPlacementSource.INVENTORY, item.id, category, item.type, item.stuffData.getLegacyString());
+ }
+ else
+ {
+ isMoving = GetRoomEngine().processRoomObjectPlacement(RoomObjectPlacementSource.INVENTORY, item.id, category, item.type, item.extra.toString(), item.stuffData);
+ }
+
+ if(isMoving)
+ {
+ setPlacingItemId(item.ref);
+ setObjectMoverRequested(true);
+ }
+ }
+
+ return true;
+};
+
+
+export const attemptBotPlacement = (botItem: IBotItem, flag: boolean = false) =>
+{
+ const botData = botItem.botData;
+
+ if(!botData) return false;
+
+ const session = GetRoomSessionManager().getSession(1);
+
+ if(!session || !session.isRoomOwner) return false;
+
+ CreateLinkEvent('inventory/hide');
+
+ if(GetRoomEngine().processRoomObjectPlacement(RoomObjectPlacementSource.INVENTORY, -(botData.id), RoomObjectCategory.UNIT, RoomObjectType.RENTABLE_BOT, botData.figure))
+ {
+ setPlacingItemId(botData.id);
+ setObjectMoverRequested(true);
+ }
+
+ return true;
+};
diff --git a/Coolui v3 test/src/api/inventory/PetUtilities.ts b/Coolui v3 test/src/api/inventory/PetUtilities.ts
new file mode 100644
index 0000000000..c53ada2cef
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/PetUtilities.ts
@@ -0,0 +1,103 @@
+import { CreateLinkEvent, PetData } from '@nitrots/nitro-renderer';
+import { IPetItem } from './IPetItem';
+import { cancelRoomObjectPlacement, getPlacingItemId } from './InventoryUtilities';
+import { UnseenItemCategory } from './UnseenItemCategory';
+
+export const getAllPetIds = (petItems: IPetItem[]) => petItems.map(item => item.petData.id);
+
+export const addSinglePetItem = (petData: PetData, set: IPetItem[], unseen: boolean = true) =>
+{
+ const petItem = { petData };
+
+ if(unseen)
+ {
+ //petItem.isUnseen = true;
+
+ set.unshift(petItem);
+ }
+ else
+ {
+ set.push(petItem);
+ }
+
+ return petItem;
+};
+
+export const removePetItemById = (id: number, set: IPetItem[]) =>
+{
+ let index = 0;
+
+ while(index < set.length)
+ {
+ const petItem = set[index];
+
+ if(petItem && (petItem.petData.id === id))
+ {
+ if(getPlacingItemId() === petItem.petData.id)
+ {
+ cancelRoomObjectPlacement();
+
+ CreateLinkEvent('inventory/open');
+ }
+
+ set.splice(index, 1);
+
+ return petItem;
+ }
+
+ index++;
+ }
+
+ return null;
+};
+
+export const processPetFragment = (set: IPetItem[], fragment: Map, isUnseen: (category: number, itemId: number) => boolean) =>
+{
+ const existingIds = getAllPetIds(set);
+ const addedIds: number[] = [];
+ const removedIds: number[] = [];
+
+ for(const key of fragment.keys()) (existingIds.indexOf(key) === -1) && addedIds.push(key);
+
+ for(const itemId of existingIds) (!fragment.get(itemId)) && removedIds.push(itemId);
+
+ const emptyExistingSet = (existingIds.length === 0);
+
+ for(const id of removedIds) removePetItemById(id, set);
+
+ for(const id of addedIds)
+ {
+ const parser = fragment.get(id);
+
+ if(!parser) continue;
+
+ addSinglePetItem(parser, set, isUnseen(UnseenItemCategory.PET, parser.id));
+ }
+
+ return set;
+};
+
+export const mergePetFragments = (fragment: Map, totalFragments: number, fragmentNumber: number, fragments: Map[]) =>
+{
+ if(totalFragments === 1) return fragment;
+
+ fragments[fragmentNumber] = fragment;
+
+ for(const frag of fragments)
+ {
+ if(!frag) return null;
+ }
+
+ const merged: Map = new Map();
+
+ for(const frag of fragments)
+ {
+ for(const [ key, value ] of frag) merged.set(key, value);
+
+ frag.clear();
+ }
+
+ fragments = null;
+
+ return merged;
+};
diff --git a/Coolui v3 test/src/api/inventory/TradeState.ts b/Coolui v3 test/src/api/inventory/TradeState.ts
new file mode 100644
index 0000000000..3df418ba95
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/TradeState.ts
@@ -0,0 +1,10 @@
+export class TradeState
+{
+ public static TRADING_STATE_READY: number = 0;
+ public static TRADING_STATE_RUNNING: number = 1;
+ public static TRADING_STATE_COUNTDOWN: number = 2;
+ public static TRADING_STATE_CONFIRMING: number = 3;
+ public static TRADING_STATE_CONFIRMED: number = 4;
+ public static TRADING_STATE_COMPLETED: number = 5;
+ public static TRADING_STATE_CANCELLED: number = 6;
+}
diff --git a/Coolui v3 test/src/api/inventory/TradeUserData.ts b/Coolui v3 test/src/api/inventory/TradeUserData.ts
new file mode 100644
index 0000000000..ba3d66bc0b
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/TradeUserData.ts
@@ -0,0 +1,15 @@
+import { AdvancedMap } from '@nitrots/nitro-renderer';
+import { GroupItem } from './GroupItem';
+
+export class TradeUserData
+{
+ constructor(
+ public userId: number = -1,
+ public userName: string = '',
+ public userItems: AdvancedMap = new AdvancedMap(),
+ public itemCount: number = 0,
+ public creditsCount: number = 0,
+ public accepts: boolean = false,
+ public canTrade: boolean = false)
+ {}
+}
diff --git a/Coolui v3 test/src/api/inventory/TradingNotificationType.ts b/Coolui v3 test/src/api/inventory/TradingNotificationType.ts
new file mode 100644
index 0000000000..4aed49059d
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/TradingNotificationType.ts
@@ -0,0 +1,12 @@
+export class TradingNotificationType
+{
+ public static ALERT_SCAM: number = 0;
+ public static HOTEL_TRADING_DISABLED = 1;
+ public static YOU_NOT_ALLOWED: number = 2;
+ public static THEY_NOT_ALLOWED: number = 4;
+ public static ROOM_DISABLED: number = 6;
+ public static YOU_OPEN: number = 7;
+ public static THEY_OPEN: number = 8;
+ public static ERROR_WHILE_COMMIT: number = 9;
+ public static THEY_CANCELLED: number = 10;
+}
diff --git a/Coolui v3 test/src/api/inventory/TradingUtilities.ts b/Coolui v3 test/src/api/inventory/TradingUtilities.ts
new file mode 100644
index 0000000000..8cdfb96bed
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/TradingUtilities.ts
@@ -0,0 +1,70 @@
+import { AdvancedMap, GetSessionDataManager, IObjectData, ItemDataStructure, StringDataType } from '@nitrots/nitro-renderer';
+import { FurniCategory } from './FurniCategory';
+import { FurnitureItem } from './FurnitureItem';
+import { createGroupItem } from './FurnitureUtilities';
+import { GroupItem } from './GroupItem';
+
+const isExternalImage = (spriteId: number) => GetSessionDataManager().getWallItemData(spriteId)?.isExternalImage || false;
+
+export const parseTradeItems = (items: ItemDataStructure[]) =>
+{
+ const existingItems = new AdvancedMap();
+ const totalItems = items.length;
+
+ if(totalItems)
+ {
+ for(const item of items)
+ {
+ const spriteId = item.spriteId;
+ const category = item.category;
+
+ let name = (item.furniType + spriteId);
+
+ if(!item.isGroupable || isExternalImage(spriteId))
+ {
+ name = ('itemid' + item.itemId);
+ }
+
+ if(item.category === FurniCategory.POSTER)
+ {
+ name = (item.itemId + 'poster' + item.stuffData.getLegacyString());
+ }
+
+ else if(item.category === FurniCategory.GUILD_FURNI)
+ {
+ name = '';
+ }
+
+ let groupItem = ((item.isGroupable && !isExternalImage(item.spriteId)) ? existingItems.getValue(name) : null);
+
+ if(!groupItem)
+ {
+ groupItem = createGroupItem(spriteId, category, item.stuffData);
+
+ existingItems.add(name, groupItem);
+ }
+
+ groupItem.push(new FurnitureItem(item));
+ }
+ }
+
+ return existingItems;
+};
+
+export const getGuildFurniType = (spriteId: number, stuffData: IObjectData) =>
+{
+ let type = spriteId.toString();
+
+ if(!(stuffData instanceof StringDataType)) return type;
+
+ let i = 1;
+
+ while(i < 5)
+ {
+ type = (type + (',' + stuffData.getValue(i)));
+
+ i++;
+ }
+
+ return type;
+};
diff --git a/Coolui v3 test/src/api/inventory/UnseenItemCategory.ts b/Coolui v3 test/src/api/inventory/UnseenItemCategory.ts
new file mode 100644
index 0000000000..cbd7e9b78c
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/UnseenItemCategory.ts
@@ -0,0 +1,9 @@
+export class UnseenItemCategory
+{
+ public static FURNI: number = 1;
+ public static RENTABLE: number = 2;
+ public static PET: number = 3;
+ public static BADGE: number = 4;
+ public static BOT: number = 5;
+ public static GAMES: number = 6;
+}
diff --git a/Coolui v3 test/src/api/inventory/index.ts b/Coolui v3 test/src/api/inventory/index.ts
new file mode 100644
index 0000000000..6a245d7635
--- /dev/null
+++ b/Coolui v3 test/src/api/inventory/index.ts
@@ -0,0 +1,15 @@
+export * from './FurniCategory';
+export * from './FurnitureItem';
+export * from './FurnitureUtilities';
+export * from './GroupItem';
+export * from './IBotItem';
+export * from './IFurnitureItem';
+export * from './IPetItem';
+export * from './IUnseenItemTracker';
+export * from './InventoryUtilities';
+export * from './PetUtilities';
+export * from './TradeState';
+export * from './TradeUserData';
+export * from './TradingNotificationType';
+export * from './TradingUtilities';
+export * from './UnseenItemCategory';
diff --git a/Coolui v3 test/src/api/mod-tools/GetIssueCategoryName.ts b/Coolui v3 test/src/api/mod-tools/GetIssueCategoryName.ts
new file mode 100644
index 0000000000..ce2b902b33
--- /dev/null
+++ b/Coolui v3 test/src/api/mod-tools/GetIssueCategoryName.ts
@@ -0,0 +1,35 @@
+export const GetIssueCategoryName = (categoryId: number) =>
+{
+ switch(categoryId)
+ {
+ case 1:
+ case 2:
+ return 'Normal';
+ case 3:
+ return 'Automatic';
+ case 4:
+ return 'Automatic IM';
+ case 5:
+ return 'Guide System';
+ case 6:
+ return 'IM';
+ case 7:
+ return 'Room';
+ case 8:
+ return 'Panic';
+ case 9:
+ return 'Guardian';
+ case 10:
+ return 'Automatic Helper';
+ case 11:
+ return 'Discussion';
+ case 12:
+ return 'Selfie';
+ case 14:
+ return 'Photo';
+ case 15:
+ return 'Ambassador';
+ }
+
+ return 'Unknown';
+};
diff --git a/Coolui v3 test/src/api/mod-tools/ISelectedUser.ts b/Coolui v3 test/src/api/mod-tools/ISelectedUser.ts
new file mode 100644
index 0000000000..4f6e76b8db
--- /dev/null
+++ b/Coolui v3 test/src/api/mod-tools/ISelectedUser.ts
@@ -0,0 +1,5 @@
+export interface ISelectedUser
+{
+ userId: number;
+ username: string;
+}
diff --git a/Coolui v3 test/src/api/mod-tools/IUserInfo.ts b/Coolui v3 test/src/api/mod-tools/IUserInfo.ts
new file mode 100644
index 0000000000..8d49aa743c
--- /dev/null
+++ b/Coolui v3 test/src/api/mod-tools/IUserInfo.ts
@@ -0,0 +1,6 @@
+export interface IUserInfo
+{
+ nameKey: string;
+ nameKeyFallback: string;
+ value: string;
+}
diff --git a/Coolui v3 test/src/api/mod-tools/ModActionDefinition.ts b/Coolui v3 test/src/api/mod-tools/ModActionDefinition.ts
new file mode 100644
index 0000000000..b28aa9cecc
--- /dev/null
+++ b/Coolui v3 test/src/api/mod-tools/ModActionDefinition.ts
@@ -0,0 +1,49 @@
+export class ModActionDefinition
+{
+ public static ALERT: number = 1;
+ public static MUTE: number = 2;
+ public static BAN: number = 3;
+ public static KICK: number = 4;
+ public static TRADE_LOCK: number = 5;
+ public static MESSAGE: number = 6;
+
+ private readonly _actionId: number;
+ private readonly _name: string;
+ private readonly _actionType: number;
+ private readonly _sanctionTypeId: number;
+ private readonly _actionLengthHours: number;
+
+ constructor(actionId: number, actionName: string, actionType: number, sanctionTypeId: number, actionLengthHours:number)
+ {
+ this._actionId = actionId;
+ this._name = actionName;
+ this._actionType = actionType;
+ this._sanctionTypeId = sanctionTypeId;
+ this._actionLengthHours = actionLengthHours;
+ }
+
+ public get actionId(): number
+ {
+ return this._actionId;
+ }
+
+ public get name(): string
+ {
+ return this._name;
+ }
+
+ public get actionType(): number
+ {
+ return this._actionType;
+ }
+
+ public get sanctionTypeId(): number
+ {
+ return this._sanctionTypeId;
+ }
+
+ public get actionLengthHours(): number
+ {
+ return this._actionLengthHours;
+ }
+}
diff --git a/Coolui v3 test/src/api/mod-tools/index.ts b/Coolui v3 test/src/api/mod-tools/index.ts
new file mode 100644
index 0000000000..004bbaa329
--- /dev/null
+++ b/Coolui v3 test/src/api/mod-tools/index.ts
@@ -0,0 +1,4 @@
+export * from './GetIssueCategoryName';
+export * from './ISelectedUser';
+export * from './IUserInfo';
+export * from './ModActionDefinition';
diff --git a/Coolui v3 test/src/api/navigator/DoorStateType.ts b/Coolui v3 test/src/api/navigator/DoorStateType.ts
new file mode 100644
index 0000000000..1f8a8efe44
--- /dev/null
+++ b/Coolui v3 test/src/api/navigator/DoorStateType.ts
@@ -0,0 +1,12 @@
+export class DoorStateType
+{
+ public static NONE: number = 0;
+ public static START_DOORBELL: number = 1;
+ public static START_PASSWORD: number = 2;
+ public static STATE_PENDING_SERVER: number = 3;
+ public static UPDATE_STATE: number = 4;
+ public static STATE_WAITING: number = 5;
+ public static STATE_NO_ANSWER: number = 6;
+ public static STATE_WRONG_PASSWORD: number = 7;
+ public static STATE_ACCEPTED: number = 8;
+}
diff --git a/Coolui v3 test/src/api/navigator/INavigatorData.ts b/Coolui v3 test/src/api/navigator/INavigatorData.ts
new file mode 100644
index 0000000000..e50b6fe50d
--- /dev/null
+++ b/Coolui v3 test/src/api/navigator/INavigatorData.ts
@@ -0,0 +1,17 @@
+import { RoomDataParser } from '@nitrots/nitro-renderer';
+
+export interface INavigatorData
+{
+ homeRoomId: number;
+ settingsReceived: boolean;
+ enteredGuestRoom: RoomDataParser;
+ currentRoomOwner: boolean;
+ currentRoomId: number;
+ currentRoomIsStaffPick: boolean;
+ createdFlatId: number;
+ avatarId: number;
+ roomPicker: boolean;
+ eventMod: boolean;
+ currentRoomRating: number;
+ canRate: boolean;
+}
diff --git a/Coolui v3 test/src/api/navigator/INavigatorSearchFilter.ts b/Coolui v3 test/src/api/navigator/INavigatorSearchFilter.ts
new file mode 100644
index 0000000000..179d5d5b8f
--- /dev/null
+++ b/Coolui v3 test/src/api/navigator/INavigatorSearchFilter.ts
@@ -0,0 +1,5 @@
+export interface INavigatorSearchFilter
+{
+ name: string;
+ query: string;
+}
diff --git a/Coolui v3 test/src/api/navigator/IRoomChatSettings.ts b/Coolui v3 test/src/api/navigator/IRoomChatSettings.ts
new file mode 100644
index 0000000000..aee426cb16
--- /dev/null
+++ b/Coolui v3 test/src/api/navigator/IRoomChatSettings.ts
@@ -0,0 +1,8 @@
+export interface IRoomChatSettings
+{
+ mode: number;
+ weight: number;
+ speed: number;
+ distance: number;
+ protection: number;
+}
diff --git a/Coolui v3 test/src/api/navigator/IRoomData.ts b/Coolui v3 test/src/api/navigator/IRoomData.ts
new file mode 100644
index 0000000000..9146314762
--- /dev/null
+++ b/Coolui v3 test/src/api/navigator/IRoomData.ts
@@ -0,0 +1,23 @@
+import { IRoomChatSettings } from './IRoomChatSettings';
+import { IRoomModerationSettings } from './IRoomModerationSettings';
+
+export interface IRoomData
+{
+ roomId: number;
+ roomName: string;
+ roomDescription: string;
+ categoryId: number;
+ userCount: number;
+ tags: string[];
+ tradeState: number;
+ allowWalkthrough: boolean;
+ lockState: number;
+ password: string;
+ allowPets: boolean;
+ allowPetsEat: boolean;
+ hideWalls: boolean;
+ wallThickness: number;
+ floorThickness: number;
+ chatSettings: IRoomChatSettings;
+ moderationSettings: IRoomModerationSettings;
+}
diff --git a/Coolui v3 test/src/api/navigator/IRoomModel.ts b/Coolui v3 test/src/api/navigator/IRoomModel.ts
new file mode 100644
index 0000000000..73dfe2788c
--- /dev/null
+++ b/Coolui v3 test/src/api/navigator/IRoomModel.ts
@@ -0,0 +1,6 @@
+export interface IRoomModel
+{
+ clubLevel: number;
+ tileSize: number;
+ name: string;
+}
diff --git a/Coolui v3 test/src/api/navigator/IRoomModerationSettings.ts b/Coolui v3 test/src/api/navigator/IRoomModerationSettings.ts
new file mode 100644
index 0000000000..266fe478f5
--- /dev/null
+++ b/Coolui v3 test/src/api/navigator/IRoomModerationSettings.ts
@@ -0,0 +1,6 @@
+export interface IRoomModerationSettings
+{
+ allowMute: number;
+ allowKick: number;
+ allowBan: number;
+}
diff --git a/Coolui v3 test/src/api/navigator/NavigatorSearchResultViewDisplayMode.ts b/Coolui v3 test/src/api/navigator/NavigatorSearchResultViewDisplayMode.ts
new file mode 100644
index 0000000000..b532d1af21
--- /dev/null
+++ b/Coolui v3 test/src/api/navigator/NavigatorSearchResultViewDisplayMode.ts
@@ -0,0 +1,6 @@
+export class NavigatorSearchResultViewDisplayMode
+{
+ public static readonly LIST: number = 0;
+ public static readonly THUMBNAILS: number = 1;
+ public static readonly FORCED_THUMBNAILS: number = 2;
+}
diff --git a/Coolui v3 test/src/api/navigator/RoomInfoData.ts b/Coolui v3 test/src/api/navigator/RoomInfoData.ts
new file mode 100644
index 0000000000..fc0a93bd8f
--- /dev/null
+++ b/Coolui v3 test/src/api/navigator/RoomInfoData.ts
@@ -0,0 +1,60 @@
+import { RoomDataParser } from '@nitrots/nitro-renderer';
+
+export class RoomInfoData
+{
+ private _enteredGuestRoom: RoomDataParser = null;
+ private _createdRoomId: number = 0;
+ private _currentRoomId: number = 0;
+ private _currentRoomOwner: boolean = false;
+ private _canRate: boolean = false;
+
+ public get enteredGuestRoom(): RoomDataParser
+ {
+ return this._enteredGuestRoom;
+ }
+
+ public set enteredGuestRoom(data: RoomDataParser)
+ {
+ this._enteredGuestRoom = data;
+ }
+
+ public get createdRoomId(): number
+ {
+ return this._createdRoomId;
+ }
+
+ public set createdRoomId(id: number)
+ {
+ this._createdRoomId = id;
+ }
+
+ public get currentRoomId(): number
+ {
+ return this._currentRoomId;
+ }
+
+ public set currentRoomId(id: number)
+ {
+ this._currentRoomId = id;
+ }
+
+ public get currentRoomOwner(): boolean
+ {
+ return this._currentRoomOwner;
+ }
+
+ public set currentRoomOwner(flag: boolean)
+ {
+ this._currentRoomOwner = flag;
+ }
+
+ public get canRate(): boolean
+ {
+ return this._canRate;
+ }
+
+ public set canRate(flag: boolean)
+ {
+ this._canRate = flag;
+ }
+}
diff --git a/Coolui v3 test/src/api/navigator/RoomSettingsUtils.ts b/Coolui v3 test/src/api/navigator/RoomSettingsUtils.ts
new file mode 100644
index 0000000000..36f636f24f
--- /dev/null
+++ b/Coolui v3 test/src/api/navigator/RoomSettingsUtils.ts
@@ -0,0 +1,10 @@
+const BuildMaxVisitorsList = () =>
+{
+ const list: number[] = [];
+
+ for(let i = 10; i <= 100; i = i + 10) list.push(i);
+
+ return list;
+};
+
+export const GetMaxVisitorsList = BuildMaxVisitorsList();
diff --git a/Coolui v3 test/src/api/navigator/SearchFilterOptions.ts b/Coolui v3 test/src/api/navigator/SearchFilterOptions.ts
new file mode 100644
index 0000000000..aaf1290260
--- /dev/null
+++ b/Coolui v3 test/src/api/navigator/SearchFilterOptions.ts
@@ -0,0 +1,24 @@
+import { INavigatorSearchFilter } from './INavigatorSearchFilter';
+
+export const SearchFilterOptions: INavigatorSearchFilter[] = [
+ {
+ name: 'anything',
+ query: null
+ },
+ {
+ name: 'room.name',
+ query: 'roomname'
+ },
+ {
+ name: 'owner',
+ query: 'owner'
+ },
+ {
+ name: 'tag',
+ query: 'tag'
+ },
+ {
+ name: 'group',
+ query: 'group'
+ }
+];
diff --git a/Coolui v3 test/src/api/navigator/TryVisitRoom.ts b/Coolui v3 test/src/api/navigator/TryVisitRoom.ts
new file mode 100644
index 0000000000..81138d6c29
--- /dev/null
+++ b/Coolui v3 test/src/api/navigator/TryVisitRoom.ts
@@ -0,0 +1,7 @@
+import { GetGuestRoomMessageComposer } from '@nitrots/nitro-renderer';
+import { SendMessageComposer } from '../nitro';
+
+export function TryVisitRoom(roomId: number): void
+{
+ SendMessageComposer(new GetGuestRoomMessageComposer(roomId, false, true));
+}
diff --git a/Coolui v3 test/src/api/navigator/index.ts b/Coolui v3 test/src/api/navigator/index.ts
new file mode 100644
index 0000000000..bceb33e4ef
--- /dev/null
+++ b/Coolui v3 test/src/api/navigator/index.ts
@@ -0,0 +1,12 @@
+export * from './DoorStateType';
+export * from './INavigatorData';
+export * from './INavigatorSearchFilter';
+export * from './IRoomChatSettings';
+export * from './IRoomData';
+export * from './IRoomModel';
+export * from './IRoomModerationSettings';
+export * from './NavigatorSearchResultViewDisplayMode';
+export * from './RoomInfoData';
+export * from './RoomSettingsUtils';
+export * from './SearchFilterOptions';
+export * from './TryVisitRoom';
diff --git a/Coolui v3 test/src/api/nitro/GetConfigurationValue.ts b/Coolui v3 test/src/api/nitro/GetConfigurationValue.ts
new file mode 100644
index 0000000000..755ca1d0ba
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/GetConfigurationValue.ts
@@ -0,0 +1,6 @@
+import { GetConfiguration } from '@nitrots/nitro-renderer';
+
+export function GetConfigurationValue(key: string, value: T = null): T
+{
+ return GetConfiguration().getValue(key, value);
+}
diff --git a/Coolui v3 test/src/api/nitro/OpenUrl.ts b/Coolui v3 test/src/api/nitro/OpenUrl.ts
new file mode 100644
index 0000000000..44992e8a3e
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/OpenUrl.ts
@@ -0,0 +1,15 @@
+import { CreateLinkEvent, HabboWebTools } from '@nitrots/nitro-renderer';
+
+export const OpenUrl = (url: string) =>
+{
+ if(!url || !url.length) return;
+
+ if(url.startsWith('http'))
+ {
+ HabboWebTools.openWebPage(url);
+ }
+ else
+ {
+ CreateLinkEvent(url);
+ }
+};
diff --git a/Coolui v3 test/src/api/nitro/SendMessageComposer.ts b/Coolui v3 test/src/api/nitro/SendMessageComposer.ts
new file mode 100644
index 0000000000..4229c28c77
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/SendMessageComposer.ts
@@ -0,0 +1,3 @@
+import { GetCommunication, IMessageComposer } from '@nitrots/nitro-renderer';
+
+export const SendMessageComposer = (event: IMessageComposer) => GetCommunication().connection.send(event);
diff --git a/Coolui v3 test/src/api/nitro/index.ts b/Coolui v3 test/src/api/nitro/index.ts
new file mode 100644
index 0000000000..11b9d02c91
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/index.ts
@@ -0,0 +1,5 @@
+export * from './GetConfigurationValue';
+export * from './OpenUrl';
+export * from './SendMessageComposer';
+export * from './room';
+export * from './session';
diff --git a/Coolui v3 test/src/api/nitro/room/DispatchMouseEvent.ts b/Coolui v3 test/src/api/nitro/room/DispatchMouseEvent.ts
new file mode 100644
index 0000000000..ccbe0bcd08
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/room/DispatchMouseEvent.ts
@@ -0,0 +1,54 @@
+import { GetRoomEngine, MouseEventType } from '@nitrots/nitro-renderer';
+
+let didMouseMove = false;
+let lastClick = 0;
+let clickCount = 0;
+
+export const DispatchMouseEvent = (event: MouseEvent, canvasId: number = 1) =>
+{
+ const x = event.clientX;
+ const y = event.clientY;
+
+ let eventType = event.type;
+
+ if(eventType === MouseEventType.MOUSE_CLICK)
+ {
+ if(lastClick)
+ {
+ clickCount = 1;
+
+ if(lastClick >= Date.now() - 300) clickCount++;
+ }
+
+ lastClick = Date.now();
+
+ if(clickCount === 2)
+ {
+ if(!didMouseMove) eventType = MouseEventType.DOUBLE_CLICK;
+
+ clickCount = 0;
+ lastClick = null;
+ }
+ }
+
+ switch(eventType)
+ {
+ case MouseEventType.MOUSE_CLICK:
+ break;
+ case MouseEventType.DOUBLE_CLICK:
+ break;
+ case MouseEventType.MOUSE_MOVE:
+ didMouseMove = true;
+ break;
+ case MouseEventType.MOUSE_DOWN:
+ didMouseMove = false;
+ break;
+ case MouseEventType.MOUSE_UP:
+ break;
+ case MouseEventType.RIGHT_CLICK:
+ break;
+ default: return;
+ }
+
+ GetRoomEngine().dispatchMouseEvent(canvasId, x, y, eventType, event.altKey, (event.ctrlKey || event.metaKey), event.shiftKey, false);
+};
diff --git a/Coolui v3 test/src/api/nitro/room/DispatchTouchEvent.ts b/Coolui v3 test/src/api/nitro/room/DispatchTouchEvent.ts
new file mode 100644
index 0000000000..7a90997259
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/room/DispatchTouchEvent.ts
@@ -0,0 +1,81 @@
+import { GetRoomEngine, MouseEventType, TouchEventType } from '@nitrots/nitro-renderer';
+
+let didMouseMove = false;
+let lastClick = 0;
+let clickCount = 0;
+
+export const DispatchTouchEvent = (event: TouchEvent, canvasId: number = 1, longTouch: boolean = false, altKey: boolean = false, ctrlKey: boolean = false, shiftKey: boolean = false) =>
+{
+ let x = 0;
+ let y = 0;
+
+ if(event.touches[0])
+ {
+ x = event.touches[0].clientX;
+ y = event.touches[0].clientY;
+ }
+
+ else if(event.changedTouches[0])
+ {
+ x = event.changedTouches[0].clientX;
+ y = event.changedTouches[0].clientY;
+ }
+
+ let eventType = event.type;
+
+ if(longTouch) eventType = TouchEventType.TOUCH_LONG;
+
+ if(eventType === MouseEventType.MOUSE_CLICK || eventType === TouchEventType.TOUCH_END)
+ {
+ eventType = MouseEventType.MOUSE_CLICK;
+
+ if(lastClick)
+ {
+ clickCount = 1;
+
+ if(lastClick >= (Date.now() - 300)) clickCount++;
+ }
+
+ lastClick = Date.now();
+
+ if(clickCount === 2)
+ {
+ if(!didMouseMove) eventType = MouseEventType.DOUBLE_CLICK;
+
+ clickCount = 0;
+ lastClick = null;
+ }
+ }
+
+ switch(eventType)
+ {
+ case MouseEventType.MOUSE_CLICK:
+ break;
+ case MouseEventType.DOUBLE_CLICK:
+ break;
+ case TouchEventType.TOUCH_START:
+ eventType = MouseEventType.MOUSE_DOWN;
+
+ didMouseMove = false;
+ break;
+ case TouchEventType.TOUCH_MOVE:
+ eventType = MouseEventType.MOUSE_MOVE;
+
+ didMouseMove = true;
+ break;
+ case TouchEventType.TOUCH_END:
+ eventType = MouseEventType.MOUSE_UP;
+ break;
+ case TouchEventType.TOUCH_LONG:
+ eventType = MouseEventType.MOUSE_DOWN_LONG;
+ break;
+ default: return;
+ }
+
+ if(eventType === TouchEventType.TOUCH_START)
+ {
+ GetRoomEngine().dispatchMouseEvent(canvasId, x, y, eventType, altKey, ctrlKey, shiftKey, false);
+ }
+
+ GetRoomEngine().dispatchMouseEvent(canvasId, x, y, eventType, altKey, ctrlKey, shiftKey, false);
+};
diff --git a/Coolui v3 test/src/api/nitro/room/GetOwnRoomObject.ts b/Coolui v3 test/src/api/nitro/room/GetOwnRoomObject.ts
new file mode 100644
index 0000000000..aae0b77d0a
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/room/GetOwnRoomObject.ts
@@ -0,0 +1,31 @@
+import { GetRoomEngine, GetSessionDataManager, IRoomObjectController, RoomObjectCategory } from '@nitrots/nitro-renderer';
+import { GetRoomSession } from '../session';
+
+export function GetOwnRoomObject(): IRoomObjectController
+{
+ const userId = GetSessionDataManager().userId;
+ const roomId = GetRoomEngine().activeRoomId;
+ const category = RoomObjectCategory.UNIT;
+ const totalObjects = GetRoomEngine().getTotalObjectsForManager(roomId, category);
+
+ let i = 0;
+
+ while(i < totalObjects)
+ {
+ const roomObject = GetRoomEngine().getRoomObjectByIndex(roomId, i, category);
+
+ if(roomObject)
+ {
+ const userData = GetRoomSession().userDataManager.getUserDataByIndex(roomObject.id);
+
+ if(userData)
+ {
+ if(userData.webID === userId) return roomObject;
+ }
+ }
+
+ i++;
+ }
+
+ return null;
+}
diff --git a/Coolui v3 test/src/api/nitro/room/GetRoomObjectBounds.ts b/Coolui v3 test/src/api/nitro/room/GetRoomObjectBounds.ts
new file mode 100644
index 0000000000..dca0338f22
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/room/GetRoomObjectBounds.ts
@@ -0,0 +1,13 @@
+import { GetRoomEngine } from '@nitrots/nitro-renderer';
+
+export const GetRoomObjectBounds = (roomId: number, objectId: number, category: number, canvasId = 1) =>
+{
+ const rectangle = GetRoomEngine().getRoomObjectBoundingRectangle(roomId, objectId, category, canvasId);
+
+ if(!rectangle) return null;
+
+ rectangle.x = Math.round(rectangle.x);
+ rectangle.y = Math.round(rectangle.y);
+
+ return rectangle;
+};
diff --git a/Coolui v3 test/src/api/nitro/room/GetRoomObjectScreenLocation.ts b/Coolui v3 test/src/api/nitro/room/GetRoomObjectScreenLocation.ts
new file mode 100644
index 0000000000..4152609413
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/room/GetRoomObjectScreenLocation.ts
@@ -0,0 +1,13 @@
+import { GetRoomEngine } from '@nitrots/nitro-renderer';
+
+export const GetRoomObjectScreenLocation = (roomId: number, objectId: number, category: number, canvasId = 1) =>
+{
+ const point = GetRoomEngine().getRoomObjectScreenLocation(roomId, objectId, category, canvasId);
+
+ if(!point) return null;
+
+ point.x = Math.round(point.x);
+ point.y = Math.round(point.y);
+
+ return point;
+};
diff --git a/Coolui v3 test/src/api/nitro/room/InitializeRoomInstanceRenderingCanvas.ts b/Coolui v3 test/src/api/nitro/room/InitializeRoomInstanceRenderingCanvas.ts
new file mode 100644
index 0000000000..1289b5effa
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/room/InitializeRoomInstanceRenderingCanvas.ts
@@ -0,0 +1,9 @@
+import { GetRoomEngine } from '@nitrots/nitro-renderer';
+
+export const InitializeRoomInstanceRenderingCanvas = (width: number, height: number, canvasId: number = 1) =>
+{
+ const roomEngine = GetRoomEngine();
+ const roomId = roomEngine.activeRoomId;
+
+ roomEngine.initializeRoomInstanceRenderingCanvas(roomId, canvasId, width, height);
+};
diff --git a/Coolui v3 test/src/api/nitro/room/IsFurnitureSelectionDisabled.ts b/Coolui v3 test/src/api/nitro/room/IsFurnitureSelectionDisabled.ts
new file mode 100644
index 0000000000..e86f9a311a
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/room/IsFurnitureSelectionDisabled.ts
@@ -0,0 +1,22 @@
+import { GetRoomEngine, GetSessionDataManager, RoomEngineObjectEvent, RoomObjectVariable } from '@nitrots/nitro-renderer';
+
+export function IsFurnitureSelectionDisabled(event: RoomEngineObjectEvent): boolean
+{
+ let result = false;
+
+ const roomObject = GetRoomEngine().getRoomObject(event.roomId, event.objectId, event.category);
+
+ if(roomObject)
+ {
+ const selectionDisabled = (roomObject.model.getValue(RoomObjectVariable.FURNITURE_SELECTION_DISABLED) === 1);
+
+ if(selectionDisabled)
+ {
+ result = true;
+
+ if(GetSessionDataManager().isModerator) result = false;
+ }
+ }
+
+ return result;
+}
diff --git a/Coolui v3 test/src/api/nitro/room/ProcessRoomObjectOperation.ts b/Coolui v3 test/src/api/nitro/room/ProcessRoomObjectOperation.ts
new file mode 100644
index 0000000000..5a1c997472
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/room/ProcessRoomObjectOperation.ts
@@ -0,0 +1,6 @@
+import { GetRoomEngine } from '@nitrots/nitro-renderer';
+
+export function ProcessRoomObjectOperation(objectId: number, category: number, operation: string): void
+{
+ GetRoomEngine().processRoomObjectOperation(objectId, category, operation);
+}
diff --git a/Coolui v3 test/src/api/nitro/room/SetActiveRoomId.ts b/Coolui v3 test/src/api/nitro/room/SetActiveRoomId.ts
new file mode 100644
index 0000000000..9446537e5c
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/room/SetActiveRoomId.ts
@@ -0,0 +1,6 @@
+import { GetRoomEngine } from '@nitrots/nitro-renderer';
+
+export function SetActiveRoomId(roomId: number): void
+{
+ GetRoomEngine().setActiveRoomId(roomId);
+}
diff --git a/Coolui v3 test/src/api/nitro/room/index.ts b/Coolui v3 test/src/api/nitro/room/index.ts
new file mode 100644
index 0000000000..2af9c28594
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/room/index.ts
@@ -0,0 +1,9 @@
+export * from './DispatchMouseEvent';
+export * from './DispatchTouchEvent';
+export * from './GetOwnRoomObject';
+export * from './GetRoomObjectBounds';
+export * from './GetRoomObjectScreenLocation';
+export * from './InitializeRoomInstanceRenderingCanvas';
+export * from './IsFurnitureSelectionDisabled';
+export * from './ProcessRoomObjectOperation';
+export * from './SetActiveRoomId';
diff --git a/Coolui v3 test/src/api/nitro/session/CanManipulateFurniture.ts b/Coolui v3 test/src/api/nitro/session/CanManipulateFurniture.ts
new file mode 100644
index 0000000000..ba89efd5ae
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/CanManipulateFurniture.ts
@@ -0,0 +1,9 @@
+import { GetRoomEngine, GetSessionDataManager, IRoomSession, RoomControllerLevel } from '@nitrots/nitro-renderer';
+import { IsOwnerOfFurniture } from './IsOwnerOfFurniture';
+
+export function CanManipulateFurniture(roomSession: IRoomSession, objectId: number, category: number): boolean
+{
+ if(!roomSession) return false;
+
+ return (roomSession.isRoomOwner || (roomSession.controllerLevel >= RoomControllerLevel.GUEST) || GetSessionDataManager().isModerator || IsOwnerOfFurniture(GetRoomEngine().getRoomObject(roomSession.roomId, objectId, category)));
+}
diff --git a/Coolui v3 test/src/api/nitro/session/CreateRoomSession.ts b/Coolui v3 test/src/api/nitro/session/CreateRoomSession.ts
new file mode 100644
index 0000000000..3f12bb4f16
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/CreateRoomSession.ts
@@ -0,0 +1,6 @@
+import { GetRoomSessionManager } from '@nitrots/nitro-renderer';
+
+export function CreateRoomSession(roomId: number, password: string = null): void
+{
+ GetRoomSessionManager().createSession(roomId, password);
+}
diff --git a/Coolui v3 test/src/api/nitro/session/GetCanStandUp.ts b/Coolui v3 test/src/api/nitro/session/GetCanStandUp.ts
new file mode 100644
index 0000000000..841ada94bb
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/GetCanStandUp.ts
@@ -0,0 +1,13 @@
+import { AvatarAction, RoomObjectVariable } from '@nitrots/nitro-renderer';
+import { GetOwnRoomObject } from '../room';
+
+export function GetCanStandUp(): string
+{
+ const roomObject = GetOwnRoomObject();
+
+ if(!roomObject) return AvatarAction.POSTURE_STAND;
+
+ const model = roomObject.model;
+
+ return model.getValue(RoomObjectVariable.FIGURE_CAN_STAND_UP);
+}
diff --git a/Coolui v3 test/src/api/nitro/session/GetCanUseExpression.ts b/Coolui v3 test/src/api/nitro/session/GetCanUseExpression.ts
new file mode 100644
index 0000000000..da27f6a8f2
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/GetCanUseExpression.ts
@@ -0,0 +1,14 @@
+import { RoomObjectVariable } from '@nitrots/nitro-renderer';
+import { GetOwnRoomObject } from '../room';
+
+export function GetCanUseExpression(): boolean
+{
+ const roomObject = GetOwnRoomObject();
+
+ if(!roomObject) return false;
+
+ const model = roomObject.model;
+ const effectId = model.getValue(RoomObjectVariable.FIGURE_EFFECT);
+
+ return !((effectId === 29) || (effectId === 30) || (effectId === 185));
+}
diff --git a/Coolui v3 test/src/api/nitro/session/GetClubMemberLevel.ts b/Coolui v3 test/src/api/nitro/session/GetClubMemberLevel.ts
new file mode 100644
index 0000000000..d3cdc37947
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/GetClubMemberLevel.ts
@@ -0,0 +1,9 @@
+import { GetSessionDataManager, HabboClubLevelEnum } from '@nitrots/nitro-renderer';
+import { GetConfigurationValue } from '../GetConfigurationValue';
+
+export function GetClubMemberLevel(): number
+{
+ if(GetConfigurationValue('hc.disabled', false)) return HabboClubLevelEnum.VIP;
+
+ return GetSessionDataManager().clubLevel;
+}
diff --git a/Coolui v3 test/src/api/nitro/session/GetFurnitureData.ts b/Coolui v3 test/src/api/nitro/session/GetFurnitureData.ts
new file mode 100644
index 0000000000..b7646df25d
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/GetFurnitureData.ts
@@ -0,0 +1,19 @@
+import { GetSessionDataManager, IFurnitureData } from '@nitrots/nitro-renderer';
+import { ProductTypeEnum } from '../../catalog';
+
+export function GetFurnitureData(furniClassId: number, productType: string): IFurnitureData
+{
+ let furniData: IFurnitureData = null;
+
+ switch(productType.toLowerCase())
+ {
+ case ProductTypeEnum.FLOOR:
+ furniData = GetSessionDataManager().getFloorItemData(furniClassId);
+ break;
+ case ProductTypeEnum.WALL:
+ furniData = GetSessionDataManager().getWallItemData(furniClassId);
+ break;
+ }
+
+ return furniData;
+}
diff --git a/Coolui v3 test/src/api/nitro/session/GetFurnitureDataForProductOffer.ts b/Coolui v3 test/src/api/nitro/session/GetFurnitureDataForProductOffer.ts
new file mode 100644
index 0000000000..b0377651d9
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/GetFurnitureDataForProductOffer.ts
@@ -0,0 +1,20 @@
+import { CatalogPageMessageProductData, FurnitureType, GetSessionDataManager, IFurnitureData } from '@nitrots/nitro-renderer';
+
+export function GetFurnitureDataForProductOffer(offer: CatalogPageMessageProductData): IFurnitureData
+{
+ if (!offer) return null;
+
+ let furniData: IFurnitureData = null;
+
+ switch ((offer.productType) as FurnitureType)
+ {
+ case FurnitureType.FLOOR:
+ furniData = GetSessionDataManager().getFloorItemData(offer.furniClassId);
+ break;
+ case FurnitureType.WALL:
+ furniData = GetSessionDataManager().getWallItemData(offer.furniClassId);
+ break;
+ }
+
+ return furniData;
+}
diff --git a/Coolui v3 test/src/api/nitro/session/GetFurnitureDataForRoomObject.ts b/Coolui v3 test/src/api/nitro/session/GetFurnitureDataForRoomObject.ts
new file mode 100644
index 0000000000..fb76b9ebcf
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/GetFurnitureDataForRoomObject.ts
@@ -0,0 +1,20 @@
+import { GetRoomEngine, GetSessionDataManager, IFurnitureData, RoomObjectCategory, RoomObjectVariable } from '@nitrots/nitro-renderer';
+
+export function GetFurnitureDataForRoomObject(roomId: number, objectId: number, category: number): IFurnitureData
+{
+ const roomObject = GetRoomEngine().getRoomObject(roomId, objectId, category);
+
+ if(!roomObject) return;
+
+ const typeId = roomObject.model.getValue(RoomObjectVariable.FURNITURE_TYPE_ID);
+
+ switch(category)
+ {
+ case RoomObjectCategory.FLOOR:
+ return GetSessionDataManager().getFloorItemData(typeId);
+ case RoomObjectCategory.WALL:
+ return GetSessionDataManager().getWallItemData(typeId);
+ }
+
+ return null;
+}
diff --git a/Coolui v3 test/src/api/nitro/session/GetOwnPosture.ts b/Coolui v3 test/src/api/nitro/session/GetOwnPosture.ts
new file mode 100644
index 0000000000..ed2a698c40
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/GetOwnPosture.ts
@@ -0,0 +1,13 @@
+import { AvatarAction, RoomObjectVariable } from '@nitrots/nitro-renderer';
+import { GetOwnRoomObject } from '../room';
+
+export function GetOwnPosture(): string
+{
+ const roomObject = GetOwnRoomObject();
+
+ if(!roomObject) return AvatarAction.POSTURE_STAND;
+
+ const model = roomObject.model;
+
+ return model.getValue(RoomObjectVariable.FIGURE_POSTURE);
+}
diff --git a/Coolui v3 test/src/api/nitro/session/GetProductDataForLocalization.ts b/Coolui v3 test/src/api/nitro/session/GetProductDataForLocalization.ts
new file mode 100644
index 0000000000..ac89803989
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/GetProductDataForLocalization.ts
@@ -0,0 +1,8 @@
+import { GetSessionDataManager, IProductData } from '@nitrots/nitro-renderer';
+
+export function GetProductDataForLocalization(localizationId: string): IProductData
+{
+ if(!localizationId) return null;
+
+ return GetSessionDataManager().getProductData(localizationId);
+}
diff --git a/Coolui v3 test/src/api/nitro/session/GetRoomSession.ts b/Coolui v3 test/src/api/nitro/session/GetRoomSession.ts
new file mode 100644
index 0000000000..da2af41564
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/GetRoomSession.ts
@@ -0,0 +1,3 @@
+import { GetRoomSessionManager } from '@nitrots/nitro-renderer';
+
+export const GetRoomSession = () => GetRoomSessionManager().getSession(-1);
diff --git a/Coolui v3 test/src/api/nitro/session/GoToDesktop.ts b/Coolui v3 test/src/api/nitro/session/GoToDesktop.ts
new file mode 100644
index 0000000000..34f2031f14
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/GoToDesktop.ts
@@ -0,0 +1,7 @@
+import { DesktopViewComposer } from '@nitrots/nitro-renderer';
+import { SendMessageComposer } from '../SendMessageComposer';
+
+export function GoToDesktop(): void
+{
+ SendMessageComposer(new DesktopViewComposer());
+}
diff --git a/Coolui v3 test/src/api/nitro/session/HasHabboClub.ts b/Coolui v3 test/src/api/nitro/session/HasHabboClub.ts
new file mode 100644
index 0000000000..9cee03f06d
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/HasHabboClub.ts
@@ -0,0 +1,6 @@
+import { GetSessionDataManager, HabboClubLevelEnum } from '@nitrots/nitro-renderer';
+
+export function HasHabboClub(): boolean
+{
+ return (GetSessionDataManager().clubLevel >= HabboClubLevelEnum.CLUB);
+}
diff --git a/Coolui v3 test/src/api/nitro/session/HasHabboVip.ts b/Coolui v3 test/src/api/nitro/session/HasHabboVip.ts
new file mode 100644
index 0000000000..f5a3e21164
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/HasHabboVip.ts
@@ -0,0 +1,6 @@
+import { GetSessionDataManager, HabboClubLevelEnum } from '@nitrots/nitro-renderer';
+
+export function HasHabboVip(): boolean
+{
+ return (GetSessionDataManager().clubLevel >= HabboClubLevelEnum.VIP);
+}
diff --git a/Coolui v3 test/src/api/nitro/session/IsOwnerOfFloorFurniture.ts b/Coolui v3 test/src/api/nitro/session/IsOwnerOfFloorFurniture.ts
new file mode 100644
index 0000000000..5675db94d1
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/IsOwnerOfFloorFurniture.ts
@@ -0,0 +1,14 @@
+import { GetRoomEngine, GetSessionDataManager, RoomObjectCategory, RoomObjectVariable } from '@nitrots/nitro-renderer';
+import { GetRoomSession } from './GetRoomSession';
+
+export function IsOwnerOfFloorFurniture(id: number): boolean
+{
+ const roomObject = GetRoomEngine().getRoomObject(GetRoomSession().roomId, id, RoomObjectCategory.FLOOR);
+
+ if(!roomObject || !roomObject.model) return false;
+
+ const userId = GetSessionDataManager().userId;
+ const objectOwnerId = roomObject.model.getValue(RoomObjectVariable.FURNITURE_OWNER_ID);
+
+ return (userId === objectOwnerId);
+}
diff --git a/Coolui v3 test/src/api/nitro/session/IsOwnerOfFurniture.ts b/Coolui v3 test/src/api/nitro/session/IsOwnerOfFurniture.ts
new file mode 100644
index 0000000000..49ce166da8
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/IsOwnerOfFurniture.ts
@@ -0,0 +1,11 @@
+import { GetSessionDataManager, IRoomObject, RoomObjectVariable } from '@nitrots/nitro-renderer';
+
+export function IsOwnerOfFurniture(roomObject: IRoomObject): boolean
+{
+ if(!roomObject || !roomObject.model) return false;
+
+ const userId = GetSessionDataManager().userId;
+ const objectOwnerId = roomObject.model.getValue(RoomObjectVariable.FURNITURE_OWNER_ID);
+
+ return (userId === objectOwnerId);
+}
diff --git a/Coolui v3 test/src/api/nitro/session/IsRidingHorse.ts b/Coolui v3 test/src/api/nitro/session/IsRidingHorse.ts
new file mode 100644
index 0000000000..9c70b5dd70
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/IsRidingHorse.ts
@@ -0,0 +1,14 @@
+import { RoomObjectVariable } from '@nitrots/nitro-renderer';
+import { GetOwnRoomObject } from '../room';
+
+export function IsRidingHorse(): boolean
+{
+ const roomObject = GetOwnRoomObject();
+
+ if(!roomObject) return false;
+
+ const model = roomObject.model;
+ const effectId = model.getValue(RoomObjectVariable.FIGURE_EFFECT);
+
+ return (effectId === 77);
+}
diff --git a/Coolui v3 test/src/api/nitro/session/StartRoomSession.ts b/Coolui v3 test/src/api/nitro/session/StartRoomSession.ts
new file mode 100644
index 0000000000..c203a77f23
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/StartRoomSession.ts
@@ -0,0 +1,6 @@
+import { GetRoomSessionManager, IRoomSession } from '@nitrots/nitro-renderer';
+
+export function StartRoomSession(session: IRoomSession): void
+{
+ GetRoomSessionManager().startSession(session);
+}
diff --git a/Coolui v3 test/src/api/nitro/session/VisitDesktop.ts b/Coolui v3 test/src/api/nitro/session/VisitDesktop.ts
new file mode 100644
index 0000000000..2309f010a7
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/VisitDesktop.ts
@@ -0,0 +1,11 @@
+import { GetRoomSessionManager } from '@nitrots/nitro-renderer';
+import { GetRoomSession } from './GetRoomSession';
+import { GoToDesktop } from './GoToDesktop';
+
+export const VisitDesktop = () =>
+{
+ if(!GetRoomSession()) return;
+
+ GoToDesktop();
+ GetRoomSessionManager().removeSession(-1);
+};
diff --git a/Coolui v3 test/src/api/nitro/session/index.ts b/Coolui v3 test/src/api/nitro/session/index.ts
new file mode 100644
index 0000000000..4c0491d51a
--- /dev/null
+++ b/Coolui v3 test/src/api/nitro/session/index.ts
@@ -0,0 +1,19 @@
+export * from './CanManipulateFurniture';
+export * from './CreateRoomSession';
+export * from './GetCanStandUp';
+export * from './GetCanUseExpression';
+export * from './GetClubMemberLevel';
+export * from './GetFurnitureData';
+export * from './GetFurnitureDataForProductOffer';
+export * from './GetFurnitureDataForRoomObject';
+export * from './GetOwnPosture';
+export * from './GetProductDataForLocalization';
+export * from './GetRoomSession';
+export * from './GoToDesktop';
+export * from './HasHabboClub';
+export * from './HasHabboVip';
+export * from './IsOwnerOfFloorFurniture';
+export * from './IsOwnerOfFurniture';
+export * from './IsRidingHorse';
+export * from './StartRoomSession';
+export * from './VisitDesktop';
diff --git a/Coolui v3 test/src/api/notification/NotificationAlertItem.ts b/Coolui v3 test/src/api/notification/NotificationAlertItem.ts
new file mode 100644
index 0000000000..2d7702c7b9
--- /dev/null
+++ b/Coolui v3 test/src/api/notification/NotificationAlertItem.ts
@@ -0,0 +1,67 @@
+import { NotificationAlertType } from './NotificationAlertType';
+
+export class NotificationAlertItem
+{
+ private static ITEM_ID: number = -1;
+
+ private _id: number;
+ private _messages: string[];
+ private _alertType: string;
+ private _clickUrl: string;
+ private _clickUrlText: string;
+ private _title: string;
+ private _imageUrl: string;
+
+ constructor(messages: string[], alertType: string = NotificationAlertType.DEFAULT, clickUrl: string = null, clickUrlText: string = null, title: string = null, imageUrl: string = null)
+ {
+ NotificationAlertItem.ITEM_ID += 1;
+
+ this._id = NotificationAlertItem.ITEM_ID;
+ this._messages = messages;
+ this._alertType = alertType;
+ this._clickUrl = clickUrl;
+ this._clickUrlText = clickUrlText;
+ this._title = title;
+ this._imageUrl = imageUrl;
+ }
+
+ public get id(): number
+ {
+ return this._id;
+ }
+
+ public get messages(): string[]
+ {
+ return this._messages;
+ }
+
+ public set alertType(alertType: string)
+ {
+ this._alertType = alertType;
+ }
+
+ public get alertType(): string
+ {
+ return this._alertType;
+ }
+
+ public get clickUrl(): string
+ {
+ return this._clickUrl;
+ }
+
+ public get clickUrlText(): string
+ {
+ return this._clickUrlText;
+ }
+
+ public get title(): string
+ {
+ return this._title;
+ }
+
+ public get imageUrl(): string
+ {
+ return this._imageUrl;
+ }
+}
diff --git a/Coolui v3 test/src/api/notification/NotificationAlertType.ts b/Coolui v3 test/src/api/notification/NotificationAlertType.ts
new file mode 100644
index 0000000000..ad804e806c
--- /dev/null
+++ b/Coolui v3 test/src/api/notification/NotificationAlertType.ts
@@ -0,0 +1,10 @@
+export class NotificationAlertType
+{
+ public static DEFAULT: string = 'default';
+ public static MOTD: string = 'motd';
+ public static MODERATION: string = 'moderation';
+ public static EVENT: string = 'event';
+ public static NITRO: string = 'nitro';
+ public static SEARCH: string = 'search';
+ public static ALERT: string = 'alert';
+}
diff --git a/Coolui v3 test/src/api/notification/NotificationBubbleItem.ts b/Coolui v3 test/src/api/notification/NotificationBubbleItem.ts
new file mode 100644
index 0000000000..fe90dab700
--- /dev/null
+++ b/Coolui v3 test/src/api/notification/NotificationBubbleItem.ts
@@ -0,0 +1,48 @@
+import { NotificationBubbleType } from './NotificationBubbleType';
+
+export class NotificationBubbleItem
+{
+ private static ITEM_ID: number = -1;
+
+ private _id: number;
+ private _message: string;
+ private _notificationType: string;
+ private _iconUrl: string;
+ private _linkUrl: string;
+
+ constructor(message: string, notificationType: string = NotificationBubbleType.INFO, iconUrl: string = null, linkUrl: string = null)
+ {
+ NotificationBubbleItem.ITEM_ID += 1;
+
+ this._id = NotificationBubbleItem.ITEM_ID;
+ this._message = message;
+ this._notificationType = notificationType;
+ this._iconUrl = iconUrl;
+ this._linkUrl = linkUrl;
+ }
+
+ public get id(): number
+ {
+ return this._id;
+ }
+
+ public get message(): string
+ {
+ return this._message;
+ }
+
+ public get notificationType(): string
+ {
+ return this._notificationType;
+ }
+
+ public get iconUrl(): string
+ {
+ return this._iconUrl;
+ }
+
+ public get linkUrl(): string
+ {
+ return this._linkUrl;
+ }
+}
diff --git a/Coolui v3 test/src/api/notification/NotificationBubbleType.ts b/Coolui v3 test/src/api/notification/NotificationBubbleType.ts
new file mode 100644
index 0000000000..858573b503
--- /dev/null
+++ b/Coolui v3 test/src/api/notification/NotificationBubbleType.ts
@@ -0,0 +1,19 @@
+export class NotificationBubbleType
+{
+ public static FRIENDOFFLINE: string = 'friendoffline';
+ public static FRIENDONLINE: string = 'friendonline';
+ public static THIRDPARTYFRIENDOFFLINE: string = 'thirdpartyfriendoffline';
+ public static THIRDPARTYFRIENDONLINE: string = 'thirdpartyfriendonline';
+ public static ACHIEVEMENT: string = 'achievement';
+ public static BADGE_RECEIVED: string = 'badge_received';
+ public static INFO: string = 'info';
+ public static RECYCLEROK: string = 'recyclerok';
+ public static RESPECT: string = 'respect';
+ public static CLUB: string = 'club';
+ public static SOUNDMACHINE: string = 'soundmachine';
+ public static PETLEVEL: string = 'petlevel';
+ public static CLUBGIFT: string = 'clubgift';
+ public static BUYFURNI: string = 'buyfurni';
+ public static VIP: string = 'vip';
+ public static ROOMMESSAGESPOSTED: string = 'roommessagesposted';
+}
diff --git a/Coolui v3 test/src/api/notification/NotificationConfirmItem.ts b/Coolui v3 test/src/api/notification/NotificationConfirmItem.ts
new file mode 100644
index 0000000000..045566264b
--- /dev/null
+++ b/Coolui v3 test/src/api/notification/NotificationConfirmItem.ts
@@ -0,0 +1,67 @@
+export class NotificationConfirmItem
+{
+ private static ITEM_ID: number = -1;
+
+ private _id: number;
+ private _confirmType: string;
+ private _message: string;
+ private _onConfirm: Function;
+ private _onCancel: Function;
+ private _confirmText: string;
+ private _cancelText: string;
+ private _title: string;
+
+ constructor(confirmType: string, message: string, onConfirm: Function, onCancel: Function, confirmText: string, cancelText: string, title: string)
+ {
+ NotificationConfirmItem.ITEM_ID += 1;
+
+ this._id = NotificationConfirmItem.ITEM_ID;
+ this._confirmType = confirmType;
+ this._message = message;
+ this._onConfirm = onConfirm;
+ this._onCancel = onCancel;
+ this._confirmText = confirmText;
+ this._cancelText = cancelText;
+ this._title = title;
+ }
+
+ public get id(): number
+ {
+ return this._id;
+ }
+
+ public get confirmType(): string
+ {
+ return this._confirmType;
+ }
+
+ public get message(): string
+ {
+ return this._message;
+ }
+
+ public get onConfirm(): Function
+ {
+ return this._onConfirm;
+ }
+
+ public get onCancel(): Function
+ {
+ return this._onCancel;
+ }
+
+ public get confirmText(): string
+ {
+ return this._confirmText;
+ }
+
+ public get cancelText(): string
+ {
+ return this._cancelText;
+ }
+
+ public get title(): string
+ {
+ return this._title;
+ }
+}
diff --git a/Coolui v3 test/src/api/notification/NotificationConfirmType.ts b/Coolui v3 test/src/api/notification/NotificationConfirmType.ts
new file mode 100644
index 0000000000..533ca053fd
--- /dev/null
+++ b/Coolui v3 test/src/api/notification/NotificationConfirmType.ts
@@ -0,0 +1,4 @@
+export class NotificationConfirmType
+{
+ public static DEFAULT: string = 'default';
+}
diff --git a/Coolui v3 test/src/api/notification/index.ts b/Coolui v3 test/src/api/notification/index.ts
new file mode 100644
index 0000000000..23476d358a
--- /dev/null
+++ b/Coolui v3 test/src/api/notification/index.ts
@@ -0,0 +1,6 @@
+export * from './NotificationAlertItem';
+export * from './NotificationAlertType';
+export * from './NotificationBubbleItem';
+export * from './NotificationBubbleType';
+export * from './NotificationConfirmItem';
+export * from './NotificationConfirmType';
diff --git a/Coolui v3 test/src/api/purse/IPurse.ts b/Coolui v3 test/src/api/purse/IPurse.ts
new file mode 100644
index 0000000000..9fffb188ee
--- /dev/null
+++ b/Coolui v3 test/src/api/purse/IPurse.ts
@@ -0,0 +1,15 @@
+export interface IPurse
+{
+ credits: number;
+ activityPoints: Map;
+ clubDays: number;
+ clubPeriods: number;
+ hasClubLeft: boolean;
+ isVip: boolean;
+ pastClubDays: number;
+ pastVipDays: number;
+ isExpiring: boolean;
+ minutesUntilExpiration: number;
+ minutesSinceLastModified: number;
+ clubLevel: number;
+}
diff --git a/Coolui v3 test/src/api/purse/Purse.ts b/Coolui v3 test/src/api/purse/Purse.ts
new file mode 100644
index 0000000000..6970e59c53
--- /dev/null
+++ b/Coolui v3 test/src/api/purse/Purse.ts
@@ -0,0 +1,165 @@
+import { GetTickerTime, HabboClubLevelEnum } from '@nitrots/nitro-renderer';
+import { IPurse } from './IPurse';
+
+export class Purse implements IPurse
+{
+ private _credits: number = 0;
+ private _activityPoints: Map = new Map();
+ private _clubDays: number = 0;
+ private _clubPeriods: number = 0;
+ private _isVIP: boolean = false;
+ private _pastClubDays: number = 0;
+ private _pastVipDays: number = 0;
+ private _isExpiring: boolean = false;
+ private _minutesUntilExpiration: number = 0;
+ private _minutesSinceLastModified: number = 0;
+ private _lastUpdated: number = 0;
+
+ public static from(purse: Purse): Purse
+ {
+ const newPurse = new Purse();
+
+ newPurse._credits = purse._credits;
+ newPurse._activityPoints = purse._activityPoints;
+ newPurse._clubDays = purse._clubDays;
+ newPurse._clubPeriods = purse._clubPeriods;
+ newPurse._isVIP = purse._isVIP;
+ newPurse._pastClubDays = purse._pastClubDays;
+ newPurse._pastVipDays = purse._pastVipDays;
+ newPurse._isExpiring = purse._isExpiring;
+ newPurse._minutesUntilExpiration = purse._minutesUntilExpiration;
+ newPurse._minutesSinceLastModified = purse._minutesSinceLastModified;
+ newPurse._lastUpdated = purse._lastUpdated;
+
+ return newPurse;
+ }
+
+ public get credits(): number
+ {
+ return this._credits;
+ }
+
+ public set credits(credits: number)
+ {
+ this._lastUpdated = GetTickerTime();
+ this._credits = credits;
+ }
+
+ public get activityPoints(): Map
+ {
+ return this._activityPoints;
+ }
+
+ public set activityPoints(k: Map)
+ {
+ this._lastUpdated = GetTickerTime();
+ this._activityPoints = k;
+ }
+
+ public get clubDays(): number
+ {
+ return this._clubDays;
+ }
+
+ public set clubDays(k: number)
+ {
+ this._lastUpdated = GetTickerTime();
+ this._clubDays = k;
+ }
+
+ public get clubPeriods(): number
+ {
+ return this._clubPeriods;
+ }
+
+ public set clubPeriods(k: number)
+ {
+ this._lastUpdated = GetTickerTime();
+ this._clubPeriods = k;
+ }
+
+ public get hasClubLeft(): boolean
+ {
+ return (this._clubDays > 0) || (this._clubPeriods > 0);
+ }
+
+ public get isVip(): boolean
+ {
+ return this._isVIP;
+ }
+
+ public set isVip(k: boolean)
+ {
+ this._isVIP = k;
+ }
+
+ public get pastClubDays(): number
+ {
+ return this._pastClubDays;
+ }
+
+ public set pastClubDays(k: number)
+ {
+ this._lastUpdated = GetTickerTime();
+ this._pastClubDays = k;
+ }
+
+ public get pastVipDays(): number
+ {
+ return this._pastVipDays;
+ }
+
+ public set pastVipDays(k: number)
+ {
+ this._lastUpdated = GetTickerTime();
+ this._pastVipDays = k;
+ }
+
+ public get isExpiring(): boolean
+ {
+ return this._isExpiring;
+ }
+
+ public set isExpiring(k: boolean)
+ {
+ this._isExpiring = k;
+ }
+
+ public get minutesUntilExpiration(): number
+ {
+ var k: number = ((GetTickerTime() - this._lastUpdated) / (1000 * 60));
+ var _local_2: number = (this._minutesUntilExpiration - k);
+ return (_local_2 > 0) ? _local_2 : 0;
+ }
+
+ public set minutesUntilExpiration(k: number)
+ {
+ this._lastUpdated = GetTickerTime();
+ this._minutesUntilExpiration = k;
+ }
+
+ public get minutesSinceLastModified(): number
+ {
+ return this._minutesSinceLastModified;
+ }
+
+ public set minutesSinceLastModified(k: number)
+ {
+ this._lastUpdated = GetTickerTime();
+ this._minutesSinceLastModified = k;
+ }
+
+ public get lastUpdated(): number
+ {
+ return this._lastUpdated;
+ }
+
+ public get clubLevel(): number
+ {
+ if(((this.clubDays === 0) && (this.clubPeriods === 0))) return HabboClubLevelEnum.NO_CLUB;
+
+ if(this.isVip) return HabboClubLevelEnum.VIP;
+
+ return HabboClubLevelEnum.CLUB;
+ }
+}
diff --git a/Coolui v3 test/src/api/purse/index.ts b/Coolui v3 test/src/api/purse/index.ts
new file mode 100644
index 0000000000..ed34480480
--- /dev/null
+++ b/Coolui v3 test/src/api/purse/index.ts
@@ -0,0 +1,2 @@
+export * from './IPurse';
+export * from './Purse';
diff --git a/Coolui v3 test/src/api/room/events/RoomWidgetPollUpdateEvent.ts b/Coolui v3 test/src/api/room/events/RoomWidgetPollUpdateEvent.ts
new file mode 100644
index 0000000000..edfb8fd0d2
--- /dev/null
+++ b/Coolui v3 test/src/api/room/events/RoomWidgetPollUpdateEvent.ts
@@ -0,0 +1,110 @@
+import { IPollQuestion } from '@nitrots/nitro-renderer';
+import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
+
+export class RoomWidgetPollUpdateEvent extends RoomWidgetUpdateEvent
+{
+ public static readonly OFFER = 'RWPUW_OFFER';
+ public static readonly ERROR = 'RWPUW_ERROR';
+ public static readonly CONTENT = 'RWPUW_CONTENT';
+
+ private _id = -1;
+ private _summary: string;
+ private _headline: string;
+ private _numQuestions = 0;
+ private _startMessage = '';
+ private _endMessage = '';
+ private _questionArray: IPollQuestion[] = null;
+ private _pollType = '';
+ private _npsPoll = false;
+
+ constructor(type: string, id: number)
+ {
+ super(type);
+ this._id = id;
+ }
+
+ public get id(): number
+ {
+ return this._id;
+ }
+
+ public get summary(): string
+ {
+ return this._summary;
+ }
+
+ public set summary(k: string)
+ {
+ this._summary = k;
+ }
+
+ public get headline(): string
+ {
+ return this._headline;
+ }
+
+ public set headline(k: string)
+ {
+ this._headline = k;
+ }
+
+ public get numQuestions(): number
+ {
+ return this._numQuestions;
+ }
+
+ public set numQuestions(k: number)
+ {
+ this._numQuestions = k;
+ }
+
+ public get startMessage(): string
+ {
+ return this._startMessage;
+ }
+
+ public set startMessage(k: string)
+ {
+ this._startMessage = k;
+ }
+
+ public get endMessage(): string
+ {
+ return this._endMessage;
+ }
+
+ public set endMessage(k: string)
+ {
+ this._endMessage = k;
+ }
+
+ public get questionArray(): IPollQuestion[]
+ {
+ return this._questionArray;
+ }
+
+ public set questionArray(k: IPollQuestion[])
+ {
+ this._questionArray = k;
+ }
+
+ public get pollType(): string
+ {
+ return this._pollType;
+ }
+
+ public set pollType(k: string)
+ {
+ this._pollType = k;
+ }
+
+ public get npsPoll(): boolean
+ {
+ return this._npsPoll;
+ }
+
+ public set npsPoll(k: boolean)
+ {
+ this._npsPoll = k;
+ }
+}
diff --git a/Coolui v3 test/src/api/room/events/RoomWidgetUpdateBackgroundColorPreviewEvent.ts b/Coolui v3 test/src/api/room/events/RoomWidgetUpdateBackgroundColorPreviewEvent.ts
new file mode 100644
index 0000000000..30135a3b4e
--- /dev/null
+++ b/Coolui v3 test/src/api/room/events/RoomWidgetUpdateBackgroundColorPreviewEvent.ts
@@ -0,0 +1,35 @@
+import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
+
+export class RoomWidgetUpdateBackgroundColorPreviewEvent extends RoomWidgetUpdateEvent
+{
+ public static PREVIEW = 'RWUBCPE_PREVIEW';
+ public static CLEAR_PREVIEW = 'RWUBCPE_CLEAR_PREVIEW';
+
+ private _hue: number;
+ private _saturation: number;
+ private _lightness: number;
+
+ constructor(type: string, hue: number = 0, saturation: number = 0, lightness: number = 0)
+ {
+ super(type);
+
+ this._hue = hue;
+ this._saturation = saturation;
+ this._lightness = lightness;
+ }
+
+ public get hue(): number
+ {
+ return this._hue;
+ }
+
+ public get saturation(): number
+ {
+ return this._saturation;
+ }
+
+ public get lightness(): number
+ {
+ return this._lightness;
+ }
+}
diff --git a/Coolui v3 test/src/api/room/events/RoomWidgetUpdateChatInputContentEvent.ts b/Coolui v3 test/src/api/room/events/RoomWidgetUpdateChatInputContentEvent.ts
new file mode 100644
index 0000000000..9352372fac
--- /dev/null
+++ b/Coolui v3 test/src/api/room/events/RoomWidgetUpdateChatInputContentEvent.ts
@@ -0,0 +1,29 @@
+import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
+
+export class RoomWidgetUpdateChatInputContentEvent extends RoomWidgetUpdateEvent
+{
+ public static CHAT_INPUT_CONTENT: string = 'RWUCICE_CHAT_INPUT_CONTENT';
+ public static WHISPER: string = 'whisper';
+ public static SHOUT: string = 'shout';
+
+ private _chatMode: string = '';
+ private _userName: string = '';
+
+ constructor(chatMode: string, userName: string)
+ {
+ super(RoomWidgetUpdateChatInputContentEvent.CHAT_INPUT_CONTENT);
+
+ this._chatMode = chatMode;
+ this._userName = userName;
+ }
+
+ public get chatMode(): string
+ {
+ return this._chatMode;
+ }
+
+ public get userName(): string
+ {
+ return this._userName;
+ }
+}
diff --git a/Coolui v3 test/src/api/room/events/RoomWidgetUpdateEvent.ts b/Coolui v3 test/src/api/room/events/RoomWidgetUpdateEvent.ts
new file mode 100644
index 0000000000..0ac8ff816c
--- /dev/null
+++ b/Coolui v3 test/src/api/room/events/RoomWidgetUpdateEvent.ts
@@ -0,0 +1,4 @@
+import { NitroEvent } from '@nitrots/nitro-renderer';
+
+export class RoomWidgetUpdateEvent extends NitroEvent
+{}
diff --git a/Coolui v3 test/src/api/room/events/RoomWidgetUpdateRentableBotChatEvent.ts b/Coolui v3 test/src/api/room/events/RoomWidgetUpdateRentableBotChatEvent.ts
new file mode 100644
index 0000000000..6191e1b995
--- /dev/null
+++ b/Coolui v3 test/src/api/room/events/RoomWidgetUpdateRentableBotChatEvent.ts
@@ -0,0 +1,62 @@
+import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
+
+export class RoomWidgetUpdateRentableBotChatEvent extends RoomWidgetUpdateEvent
+{
+ public static UPDATE_CHAT: string = 'RWURBCE_UPDATE_CHAT';
+
+ private _objectId: number;
+ private _category: number;
+ private _botId: number;
+ private _chat: string;
+ private _automaticChat: boolean;
+ private _chatDelay: number;
+ private _mixSentences: boolean;
+
+ constructor(objectId: number, category: number, botId: number, chat: string, automaticChat: boolean, chatDelay: number, mixSentences: boolean)
+ {
+ super(RoomWidgetUpdateRentableBotChatEvent.UPDATE_CHAT);
+
+ this._objectId = objectId;
+ this._category = category;
+ this._botId = botId;
+ this._chat = chat;
+ this._automaticChat = automaticChat;
+ this._chatDelay = chatDelay;
+ this._mixSentences = mixSentences;
+ }
+
+ public get objectId(): number
+ {
+ return this._objectId;
+ }
+
+ public get category(): number
+ {
+ return this._category;
+ }
+
+ public get botId(): number
+ {
+ return this._botId;
+ }
+
+ public get chat(): string
+ {
+ return this._chat;
+ }
+
+ public get automaticChat(): boolean
+ {
+ return this._automaticChat;
+ }
+
+ public get chatDelay(): number
+ {
+ return this._chatDelay;
+ }
+
+ public get mixSentences(): boolean
+ {
+ return this._mixSentences;
+ }
+}
diff --git a/Coolui v3 test/src/api/room/events/RoomWidgetUpdateRoomObjectEvent.ts b/Coolui v3 test/src/api/room/events/RoomWidgetUpdateRoomObjectEvent.ts
new file mode 100644
index 0000000000..0660276c06
--- /dev/null
+++ b/Coolui v3 test/src/api/room/events/RoomWidgetUpdateRoomObjectEvent.ts
@@ -0,0 +1,43 @@
+import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
+
+export class RoomWidgetUpdateRoomObjectEvent extends RoomWidgetUpdateEvent
+{
+ public static OBJECT_SELECTED: string = 'RWUROE_OBJECT_SELECTED';
+ public static OBJECT_DESELECTED: string = 'RWUROE_OBJECT_DESELECTED';
+ public static USER_REMOVED: string = 'RWUROE_USER_REMOVED';
+ public static FURNI_REMOVED: string = 'RWUROE_FURNI_REMOVED';
+ public static FURNI_ADDED: string = 'RWUROE_FURNI_ADDED';
+ public static USER_ADDED: string = 'RWUROE_USER_ADDED';
+ public static OBJECT_ROLL_OVER: string = 'RWUROE_OBJECT_ROLL_OVER';
+ public static OBJECT_ROLL_OUT: string = 'RWUROE_OBJECT_ROLL_OUT';
+ public static OBJECT_REQUEST_MANIPULATION: string = 'RWUROE_OBJECT_REQUEST_MANIPULATION';
+ public static OBJECT_DOUBLE_CLICKED: string = 'RWUROE_OBJECT_DOUBLE_CLICKED';
+
+ private _id: number;
+ private _category: number;
+ private _roomId: number;
+
+ constructor(type: string, id: number, category: number, roomId: number)
+ {
+ super(type);
+
+ this._id = id;
+ this._category = category;
+ this._roomId = roomId;
+ }
+
+ public get id(): number
+ {
+ return this._id;
+ }
+
+ public get category(): number
+ {
+ return this._category;
+ }
+
+ public get roomId(): number
+ {
+ return this._roomId;
+ }
+}
diff --git a/Coolui v3 test/src/api/room/events/index.ts b/Coolui v3 test/src/api/room/events/index.ts
new file mode 100644
index 0000000000..e5ed0d8c6b
--- /dev/null
+++ b/Coolui v3 test/src/api/room/events/index.ts
@@ -0,0 +1,6 @@
+export * from './RoomWidgetPollUpdateEvent';
+export * from './RoomWidgetUpdateBackgroundColorPreviewEvent';
+export * from './RoomWidgetUpdateChatInputContentEvent';
+export * from './RoomWidgetUpdateEvent';
+export * from './RoomWidgetUpdateRentableBotChatEvent';
+export * from './RoomWidgetUpdateRoomObjectEvent';
diff --git a/Coolui v3 test/src/api/room/index.ts b/Coolui v3 test/src/api/room/index.ts
new file mode 100644
index 0000000000..56aea79e46
--- /dev/null
+++ b/Coolui v3 test/src/api/room/index.ts
@@ -0,0 +1,2 @@
+export * from './events';
+export * from './widgets';
diff --git a/Coolui v3 test/src/api/room/widgets/AvatarInfoFurni.ts b/Coolui v3 test/src/api/room/widgets/AvatarInfoFurni.ts
new file mode 100644
index 0000000000..47743e9712
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/AvatarInfoFurni.ts
@@ -0,0 +1,37 @@
+import { IObjectData } from '@nitrots/nitro-renderer';
+import { IAvatarInfo } from './IAvatarInfo';
+
+export class AvatarInfoFurni implements IAvatarInfo
+{
+ public static FURNI: string = 'IFI_FURNI';
+
+ public id: number = 0;
+ public category: number = 0;
+ public name: string = '';
+ public description: string = '';
+ public isWallItem: boolean = false;
+ public isStickie: boolean = false;
+ public isRoomOwner: boolean = false;
+ public roomControllerLevel: number = 0;
+ public isAnyRoomController: boolean = false;
+ public expiration: number = -1;
+ public purchaseCatalogPageId: number = -1;
+ public purchaseOfferId: number = -1;
+ public extraParam: string = '';
+ public isOwner: boolean = false;
+ public stuffData: IObjectData = null;
+ public groupId: number = 0;
+ public ownerId: number = 0;
+ public ownerName: string = '';
+ public usagePolicy: number = 0;
+ public rentCatalogPageId: number = -1;
+ public rentOfferId: number = -1;
+ public purchaseCouldBeUsedForBuyout: boolean = false;
+ public rentCouldBeUsedForBuyout: boolean = false;
+ public availableForBuildersClub: boolean = false;
+ public tileSizeX: number = 1;
+ public tileSizeY: number = 1;
+
+ constructor(public readonly type: string)
+ {}
+}
diff --git a/Coolui v3 test/src/api/room/widgets/AvatarInfoName.ts b/Coolui v3 test/src/api/room/widgets/AvatarInfoName.ts
new file mode 100644
index 0000000000..66a6a7e3d6
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/AvatarInfoName.ts
@@ -0,0 +1,11 @@
+export class AvatarInfoName
+{
+ constructor(
+ public readonly roomIndex: number,
+ public readonly category: number,
+ public readonly id: number,
+ public readonly name: string,
+ public readonly userType: number,
+ public readonly isFriend: boolean = false)
+ {}
+}
diff --git a/Coolui v3 test/src/api/room/widgets/AvatarInfoPet.ts b/Coolui v3 test/src/api/room/widgets/AvatarInfoPet.ts
new file mode 100644
index 0000000000..0c0435a934
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/AvatarInfoPet.ts
@@ -0,0 +1,46 @@
+import { IAvatarInfo } from './IAvatarInfo';
+
+export class AvatarInfoPet implements IAvatarInfo
+{
+ public static PET_INFO: string = 'IPI_PET_INFO';
+
+ public level: number = 0;
+ public maximumLevel: number = 0;
+ public experience: number = 0;
+ public levelExperienceGoal: number = 0;
+ public energy: number = 0;
+ public maximumEnergy: number = 0;
+ public happyness: number = 0;
+ public maximumHappyness: number = 0;
+ public respectsPetLeft: number = 0;
+ public respect: number = 0;
+ public age: number = 0;
+ public name: string = '';
+ public id: number = -1;
+ public image: HTMLImageElement = null;
+ public petType: number = 0;
+ public petBreed: number = 0;
+ public petFigure: string = '';
+ public posture: string = 'std';
+ public isOwner: boolean = false;
+ public ownerId: number = -1;
+ public ownerName: string = '';
+ public canRemovePet: boolean = false;
+ public roomIndex: number = 0;
+ public unknownRarityLevel: number = 0;
+ public saddle: boolean = false;
+ public rider: boolean = false;
+ public breedable: boolean = false;
+ public skillTresholds: number[] = [];
+ public publiclyRideable: number = 0;
+ public fullyGrown: boolean = false;
+ public dead: boolean = false;
+ public rarityLevel: number = 0;
+ public maximumTimeToLive: number = 0;
+ public remainingTimeToLive: number = 0;
+ public remainingGrowTime: number = 0;
+ public publiclyBreedable: boolean = false;
+
+ constructor(public readonly type: string)
+ {}
+}
diff --git a/Coolui v3 test/src/api/room/widgets/AvatarInfoRentableBot.ts b/Coolui v3 test/src/api/room/widgets/AvatarInfoRentableBot.ts
new file mode 100644
index 0000000000..77fb10ca5b
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/AvatarInfoRentableBot.ts
@@ -0,0 +1,23 @@
+import { IAvatarInfo } from './IAvatarInfo';
+
+export class AvatarInfoRentableBot implements IAvatarInfo
+{
+ public static RENTABLE_BOT: string = 'IRBI_RENTABLE_BOT';
+
+ public name: string = '';
+ public motto: string = '';
+ public webID: number = 0;
+ public figure: string = '';
+ public badges: string[] = [];
+ public carryItem: number = 0;
+ public roomIndex: number = 0;
+ public amIOwner: boolean = false;
+ public amIAnyRoomController: boolean = false;
+ public roomControllerLevel: number = 0;
+ public ownerId: number = -1;
+ public ownerName: string = '';
+ public botSkills: number[] = [];
+
+ constructor(public readonly type: string)
+ {}
+}
diff --git a/Coolui v3 test/src/api/room/widgets/AvatarInfoUser.ts b/Coolui v3 test/src/api/room/widgets/AvatarInfoUser.ts
new file mode 100644
index 0000000000..fa3fc1ab04
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/AvatarInfoUser.ts
@@ -0,0 +1,52 @@
+import { IAvatarInfo } from './IAvatarInfo';
+
+export class AvatarInfoUser implements IAvatarInfo
+{
+ public static OWN_USER: string = 'IUI_OWN_USER';
+ public static PEER: string = 'IUI_PEER';
+ public static BOT: string = 'IUI_BOT';
+ public static TRADE_REASON_OK: number = 0;
+ public static TRADE_REASON_SHUTDOWN: number = 2;
+ public static TRADE_REASON_NO_TRADING: number = 3;
+ public static DEFAULT_BOT_BADGE_ID: string = 'BOT';
+
+ public name: string = '';
+ public motto: string = '';
+ public achievementScore: number = 0;
+ public backgroundId: number = 0;
+ public standId: number = 0;
+ public overlayId: number = 0;
+ public webID: number = 0;
+ public xp: number = 0;
+ public userType: number = -1;
+ public figure: string = '';
+ public badges: string[] = [];
+ public groupId: number = 0;
+ public groupName: string = '';
+ public groupBadgeId: string = '';
+ public carryItem: number = 0;
+ public roomIndex: number = 0;
+ public isSpectatorMode: boolean = false;
+ public allowNameChange: boolean = false;
+ public amIOwner: boolean = false;
+ public amIAnyRoomController: boolean = false;
+ public roomControllerLevel: number = 0;
+ public canBeKicked: boolean = false;
+ public canBeBanned: boolean = false;
+ public canBeMuted: boolean = false;
+ public respectLeft: number = 0;
+ public isIgnored: boolean = false;
+ public isGuildRoom: boolean = false;
+ public canTrade: boolean = false;
+ public canTradeReason: number = 0;
+ public targetRoomControllerLevel: number = 0;
+ public isAmbassador: boolean = false;
+
+ constructor(public readonly type: string)
+ {}
+
+ public get isOwnUser(): boolean
+ {
+ return (this.type === AvatarInfoUser.OWN_USER);
+ }
+}
diff --git a/Coolui v3 test/src/api/room/widgets/AvatarInfoUtilities.ts b/Coolui v3 test/src/api/room/widgets/AvatarInfoUtilities.ts
new file mode 100644
index 0000000000..b8154ee6b4
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/AvatarInfoUtilities.ts
@@ -0,0 +1,442 @@
+import { GetRoomEngine, GetSessionDataManager, GetTickerTime, IFurnitureData, IRoomModerationSettings, IRoomPetData, IRoomUserData, ObjectDataFactory, PetFigureData, PetType, RoomControllerLevel, RoomModerationSettings, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomTradingLevelEnum, RoomWidgetEnumItemExtradataParameter } from '@nitrots/nitro-renderer';
+import { GetRoomSession, IsOwnerOfFurniture } from '../../nitro';
+import { LocalizeText } from '../../utils';
+import { AvatarInfoFurni } from './AvatarInfoFurni';
+import { AvatarInfoName } from './AvatarInfoName';
+import { AvatarInfoPet } from './AvatarInfoPet';
+import { AvatarInfoRentableBot } from './AvatarInfoRentableBot';
+import { AvatarInfoUser } from './AvatarInfoUser';
+
+export class AvatarInfoUtilities
+{
+ public static getObjectName(objectId: number, category: number): AvatarInfoName
+ {
+ const roomSession = GetRoomSession();
+
+ let id = -1;
+ let name: string = null;
+ let userType = 0;
+
+ switch(category)
+ {
+ case RoomObjectCategory.FLOOR:
+ case RoomObjectCategory.WALL: {
+ const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, objectId, category);
+
+ if(!roomObject) break;
+
+ if(roomObject.type.indexOf('poster') === 0)
+ {
+ name = LocalizeText('${poster_' + parseInt(roomObject.type.replace('poster', '')) + '_name}');
+ }
+ else
+ {
+ let furniData: IFurnitureData = null;
+
+ const typeId = roomObject.model.getValue(RoomObjectVariable.FURNITURE_TYPE_ID);
+
+ if(category === RoomObjectCategory.FLOOR)
+ {
+ furniData = GetSessionDataManager().getFloorItemData(typeId);
+ }
+
+ else if(category === RoomObjectCategory.WALL)
+ {
+ furniData = GetSessionDataManager().getWallItemData(typeId);
+ }
+
+ if(!furniData) break;
+
+ id = furniData.id;
+ name = furniData.name;
+ }
+ break;
+ }
+ case RoomObjectCategory.UNIT: {
+ const userData = roomSession.userDataManager.getUserDataByIndex(objectId);
+
+ if(!userData) break;
+
+ id = userData.webID;
+ name = userData.name;
+ userType = userData.type;
+ break;
+ }
+ }
+
+ if(!name || !name.length) return null;
+
+ return new AvatarInfoName(objectId, category, id, name, userType);
+ }
+
+ public static getFurniInfo(objectId: number, category: number): AvatarInfoFurni
+ {
+ const roomSession = GetRoomSession();
+ const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, objectId, category);
+
+ if(!roomObject) return null;
+
+ const furniInfo = new AvatarInfoFurni(AvatarInfoFurni.FURNI);
+
+ furniInfo.id = objectId;
+ furniInfo.category = category;
+
+ const model = roomObject.model;
+
+ if(model.getValue(RoomWidgetEnumItemExtradataParameter.INFOSTAND_EXTRA_PARAM)) furniInfo.extraParam = model.getValue(RoomWidgetEnumItemExtradataParameter.INFOSTAND_EXTRA_PARAM);
+
+ const objectData = ObjectDataFactory.getData(model.getValue(RoomObjectVariable.FURNITURE_DATA_FORMAT));
+
+ objectData.initializeFromRoomObjectModel(model);
+
+ furniInfo.stuffData = objectData;
+
+ const objectType = roomObject.type;
+
+ if(objectType.indexOf('poster') === 0)
+ {
+ const posterId = parseInt(objectType.replace('poster', ''));
+
+ furniInfo.name = LocalizeText(('${poster_' + posterId) + '_name}');
+ furniInfo.description = LocalizeText(('${poster_' + posterId) + '_desc}');
+ }
+ else
+ {
+ const typeId = model.getValue(RoomObjectVariable.FURNITURE_TYPE_ID);
+
+ let furnitureData: IFurnitureData = null;
+
+ if(category === RoomObjectCategory.FLOOR)
+ {
+ furnitureData = GetSessionDataManager().getFloorItemData(typeId);
+ }
+
+ else if(category === RoomObjectCategory.WALL)
+ {
+ furnitureData = GetSessionDataManager().getWallItemData(typeId);
+ }
+
+ if(furnitureData)
+ {
+ furniInfo.name = furnitureData.name;
+ furniInfo.description = furnitureData.description;
+ furniInfo.purchaseOfferId = furnitureData.purchaseOfferId;
+ furniInfo.purchaseCouldBeUsedForBuyout = furnitureData.purchaseCouldBeUsedForBuyout;
+ furniInfo.rentOfferId = furnitureData.rentOfferId;
+ furniInfo.rentCouldBeUsedForBuyout = furnitureData.rentCouldBeUsedForBuyout;
+ furniInfo.availableForBuildersClub = furnitureData.availableForBuildersClub;
+ furniInfo.tileSizeX = furnitureData.tileSizeX;
+ furniInfo.tileSizeY = furnitureData.tileSizeY;
+ }
+ }
+
+ if(objectType.indexOf('post_it') > -1) furniInfo.isStickie = true;
+
+ const expiryTime = model.getValue(RoomObjectVariable.FURNITURE_EXPIRY_TIME);
+ const expiryTimestamp = model.getValue(RoomObjectVariable.FURNITURE_EXPIRTY_TIMESTAMP);
+
+ furniInfo.expiration = ((expiryTime < 0) ? expiryTime : Math.max(0, (expiryTime - ((GetTickerTime() - expiryTimestamp) / 1000))));
+
+ /* let roomObjectImage = GetRoomEngine().getRoomObjectImage(roomSession.roomId, objectId, category, new Vector3d(180), 64, null);
+
+ if(!roomObjectImage.data || (roomObjectImage.data.width > 140) || (roomObjectImage.data.height > 200))
+ {
+ roomObjectImage = GetRoomEngine().getRoomObjectImage(roomSession.roomId, objectId, category, new Vector3d(180), 1, null);
+ }
+
+ furniInfo.image = roomObjectImage.getImage(); */
+ furniInfo.isWallItem = (category === RoomObjectCategory.WALL);
+ furniInfo.isRoomOwner = roomSession.isRoomOwner;
+ furniInfo.roomControllerLevel = roomSession.controllerLevel;
+ furniInfo.isAnyRoomController = GetSessionDataManager().isModerator;
+ furniInfo.ownerId = model.getValue(RoomObjectVariable.FURNITURE_OWNER_ID);
+ furniInfo.ownerName = model.getValue(RoomObjectVariable.FURNITURE_OWNER_NAME);
+ furniInfo.usagePolicy = model.getValue(RoomObjectVariable.FURNITURE_USAGE_POLICY);
+
+ const guildId = model.getValue(RoomObjectVariable.FURNITURE_GUILD_CUSTOMIZED_GUILD_ID);
+
+ if(guildId !== 0) furniInfo.groupId = guildId;
+
+ if(IsOwnerOfFurniture(roomObject)) furniInfo.isOwner = true;
+
+ return furniInfo;
+ }
+
+ public static getUserInfo(category: number, userData: IRoomUserData): AvatarInfoUser
+ {
+ const roomSession = GetRoomSession();
+
+ const userInfo = new AvatarInfoUser((userData.webID === GetSessionDataManager().userId) ? AvatarInfoUser.OWN_USER : AvatarInfoUser.PEER);
+
+ userInfo.isSpectatorMode = roomSession.isSpectator;
+ userInfo.name = userData.name;
+ userInfo.motto = userData.custom;
+ userInfo.backgroundId = userData.background;
+ userInfo.standId = userData.stand;
+ userInfo.overlayId = userData.overlay;
+ userInfo.achievementScore = userData.activityPoints;
+ userInfo.webID = userData.webID;
+ userInfo.roomIndex = userData.roomIndex;
+ userInfo.userType = RoomObjectType.USER;
+
+ const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, userData.roomIndex, category);
+
+ if(roomObject) userInfo.carryItem = (roomObject.model.getValue(RoomObjectVariable.FIGURE_CARRY_OBJECT) || 0);
+
+ if(userInfo.type === AvatarInfoUser.OWN_USER) userInfo.allowNameChange = GetSessionDataManager().canChangeName;
+
+ userInfo.amIOwner = roomSession.isRoomOwner;
+ userInfo.isGuildRoom = roomSession.isGuildRoom;
+ userInfo.roomControllerLevel = roomSession.controllerLevel;
+ userInfo.amIAnyRoomController = GetSessionDataManager().isModerator;
+ userInfo.isAmbassador = GetSessionDataManager().isAmbassador;
+
+ if(userInfo.type === AvatarInfoUser.PEER)
+ {
+ if(roomObject)
+ {
+ userInfo.targetRoomControllerLevel = roomObject.model.getValue(RoomObjectVariable.FIGURE_FLAT_CONTROL);
+ userInfo.canBeMuted = this.canBeMuted(userInfo);
+ userInfo.canBeKicked = this.canBeKicked(userInfo);
+ userInfo.canBeBanned = this.canBeBanned(userInfo);
+ }
+
+ userInfo.isIgnored = GetSessionDataManager().isUserIgnored(userData.name);
+ userInfo.respectLeft = GetSessionDataManager().respectsLeft;
+
+ const isShuttingDown = GetSessionDataManager().isSystemShutdown;
+ const tradeMode = roomSession.tradeMode;
+
+ if(isShuttingDown)
+ {
+ userInfo.canTrade = false;
+ }
+ else
+ {
+ switch(tradeMode)
+ {
+ case RoomTradingLevelEnum.ROOM_CONTROLLER_REQUIRED: {
+ const roomController = ((userInfo.roomControllerLevel !== RoomControllerLevel.NONE) && (userInfo.roomControllerLevel !== RoomControllerLevel.GUILD_MEMBER));
+ const targetController = ((userInfo.targetRoomControllerLevel !== RoomControllerLevel.NONE) && (userInfo.targetRoomControllerLevel !== RoomControllerLevel.GUILD_MEMBER));
+
+ userInfo.canTrade = (roomController || targetController);
+ break;
+ }
+ case RoomTradingLevelEnum.FREE_TRADING:
+ userInfo.canTrade = true;
+ break;
+ default:
+ userInfo.canTrade = false;
+ break;
+ }
+ }
+
+ userInfo.canTradeReason = AvatarInfoUser.TRADE_REASON_OK;
+
+ if(isShuttingDown) userInfo.canTradeReason = AvatarInfoUser.TRADE_REASON_SHUTDOWN;
+
+ if(tradeMode !== RoomTradingLevelEnum.FREE_TRADING) userInfo.canTradeReason = AvatarInfoUser.TRADE_REASON_NO_TRADING;
+
+ // const _local_12 = GetSessionDataManager().userId;
+ // _local_13 = GetSessionDataManager().getUserTags(_local_12);
+ // this._Str_16287(_local_12, _local_13);
+ }
+
+ userInfo.groupId = userData.groupId;
+ userInfo.groupBadgeId = GetSessionDataManager().getGroupBadge(userInfo.groupId);
+ userInfo.groupName = userData.groupName;
+ userInfo.badges = roomSession.userDataManager.getUserBadges(userData.webID);
+ userInfo.figure = userData.figure;
+ //var _local_8:Array = GetSessionDataManager().getUserTags(userData.webID);
+ //this._Str_16287(userData.webId, _local_8);
+ //this._container.habboGroupsManager.updateVisibleExtendedProfile(userData.webID);
+ //this._container.connection.send(new GetRelationshipStatusInfoMessageComposer(userData.webId));
+
+ return userInfo;
+ }
+
+ public static getBotInfo(category: number, userData: IRoomUserData): AvatarInfoUser
+ {
+ const roomSession = GetRoomSession();
+ const userInfo = new AvatarInfoUser(AvatarInfoUser.BOT);
+
+ userInfo.name = userData.name;
+ userInfo.motto = userData.custom;
+ userInfo.webID = userData.webID;
+ userInfo.roomIndex = userData.roomIndex;
+ userInfo.userType = userData.type;
+
+ const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, userData.roomIndex, category);
+
+ if(roomObject) userInfo.carryItem = (roomObject.model.getValue(RoomObjectVariable.FIGURE_CARRY_OBJECT) || 0);
+
+ userInfo.amIOwner = roomSession.isRoomOwner;
+ userInfo.isGuildRoom = roomSession.isGuildRoom;
+ userInfo.roomControllerLevel = roomSession.controllerLevel;
+ userInfo.amIAnyRoomController = GetSessionDataManager().isModerator;
+ userInfo.isAmbassador = GetSessionDataManager().isAmbassador;
+ userInfo.badges = [ AvatarInfoUser.DEFAULT_BOT_BADGE_ID ];
+ userInfo.figure = userData.figure;
+
+ return userInfo;
+ }
+
+ public static getRentableBotInfo(category: number, userData: IRoomUserData): AvatarInfoRentableBot
+ {
+ const roomSession = GetRoomSession();
+ const botInfo = new AvatarInfoRentableBot(AvatarInfoRentableBot.RENTABLE_BOT);
+
+ botInfo.name = userData.name;
+ botInfo.motto = userData.custom;
+ botInfo.webID = userData.webID;
+ botInfo.roomIndex = userData.roomIndex;
+ botInfo.ownerId = userData.ownerId;
+ botInfo.ownerName = userData.ownerName;
+ botInfo.botSkills = userData.botSkills;
+
+ const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, userData.roomIndex, category);
+
+ if(roomObject) botInfo.carryItem = (roomObject.model.getValue(RoomObjectVariable.FIGURE_CARRY_OBJECT) || 0);
+
+ botInfo.amIOwner = roomSession.isRoomOwner;
+ botInfo.roomControllerLevel = roomSession.controllerLevel;
+ botInfo.amIAnyRoomController = GetSessionDataManager().isModerator;
+ botInfo.badges = [ AvatarInfoUser.DEFAULT_BOT_BADGE_ID ];
+ botInfo.figure = userData.figure;
+
+ return botInfo;
+ }
+
+ public static getPetInfo(petData: IRoomPetData): AvatarInfoPet
+ {
+ const roomSession = GetRoomSession();
+ const userData = roomSession.userDataManager.getPetData(petData.id);
+
+ if(!userData) return;
+
+ const figure = new PetFigureData(userData.figure);
+
+ let posture: string = null;
+
+ if(figure.typeId === PetType.MONSTERPLANT)
+ {
+ if(petData.level >= petData.adultLevel) posture = 'std';
+ else posture = ('grw' + petData.level);
+ }
+
+ const isOwner = (petData.ownerId === GetSessionDataManager().userId);
+ const petInfo = new AvatarInfoPet(AvatarInfoPet.PET_INFO);
+
+ petInfo.name = userData.name;
+ petInfo.id = petData.id;
+ petInfo.ownerId = petData.ownerId;
+ petInfo.ownerName = petData.ownerName;
+ petInfo.rarityLevel = petData.rarityLevel;
+ petInfo.petType = figure.typeId;
+ petInfo.petBreed = figure.paletteId;
+ petInfo.petFigure = userData.figure;
+ petInfo.posture = posture;
+ petInfo.isOwner = isOwner;
+ petInfo.roomIndex = userData.roomIndex;
+ petInfo.level = petData.level;
+ petInfo.maximumLevel = petData.maximumLevel;
+ petInfo.experience = petData.experience;
+ petInfo.levelExperienceGoal = petData.levelExperienceGoal;
+ petInfo.energy = petData.energy;
+ petInfo.maximumEnergy = petData.maximumEnergy;
+ petInfo.happyness = petData.happyness;
+ petInfo.maximumHappyness = petData.maximumHappyness;
+ petInfo.respect = petData.respect;
+ petInfo.respectsPetLeft = GetSessionDataManager().respectsPetLeft;
+ petInfo.age = petData.age;
+ petInfo.saddle = petData.saddle;
+ petInfo.rider = petData.rider;
+ petInfo.breedable = petData.breedable;
+ petInfo.fullyGrown = petData.fullyGrown;
+ petInfo.dead = petData.dead;
+ petInfo.rarityLevel = petData.rarityLevel;
+ petInfo.skillTresholds = petData.skillTresholds;
+ petInfo.canRemovePet = false;
+ petInfo.publiclyRideable = petData.publiclyRideable;
+ petInfo.maximumTimeToLive = petData.maximumTimeToLive;
+ petInfo.remainingTimeToLive = petData.remainingTimeToLive;
+ petInfo.remainingGrowTime = petData.remainingGrowTime;
+ petInfo.publiclyBreedable = petData.publiclyBreedable;
+
+ if(isOwner || roomSession.isRoomOwner || GetSessionDataManager().isModerator || (roomSession.controllerLevel >= RoomControllerLevel.GUEST)) petInfo.canRemovePet = true;
+
+ return petInfo;
+ }
+
+ private static checkGuildSetting(userInfo: AvatarInfoUser): boolean
+ {
+ if(userInfo.isGuildRoom) return (userInfo.roomControllerLevel >= RoomControllerLevel.GUILD_ADMIN);
+
+ return (userInfo.roomControllerLevel >= RoomControllerLevel.GUEST);
+ }
+
+ private static isValidSetting(userInfo: AvatarInfoUser, checkSetting: (userInfo: AvatarInfoUser, moderation: IRoomModerationSettings) => boolean): boolean
+ {
+ const roomSession = GetRoomSession();
+
+ if(!roomSession.isPrivateRoom) return false;
+
+ const moderation = roomSession.moderationSettings;
+
+ let flag = false;
+
+ if(moderation) flag = checkSetting(userInfo, moderation);
+
+ return (flag && (userInfo.targetRoomControllerLevel < RoomControllerLevel.ROOM_OWNER));
+ }
+
+ private static canBeMuted(userInfo: AvatarInfoUser): boolean
+ {
+ const checkSetting = (userInfo: AvatarInfoUser, moderation: IRoomModerationSettings) =>
+ {
+ switch(moderation.allowMute)
+ {
+ case RoomModerationSettings.MODERATION_LEVEL_USER_WITH_RIGHTS:
+ return this.checkGuildSetting(userInfo);
+ default:
+ return (userInfo.roomControllerLevel >= RoomControllerLevel.ROOM_OWNER);
+ }
+ };
+
+ return this.isValidSetting(userInfo, checkSetting);
+ }
+
+ private static canBeKicked(userInfo: AvatarInfoUser): boolean
+ {
+ const checkSetting = (userInfo: AvatarInfoUser, moderation: IRoomModerationSettings) =>
+ {
+ switch(moderation.allowKick)
+ {
+ case RoomModerationSettings.MODERATION_LEVEL_ALL:
+ return true;
+ case RoomModerationSettings.MODERATION_LEVEL_USER_WITH_RIGHTS:
+ return this.checkGuildSetting(userInfo);
+ default:
+ return (userInfo.roomControllerLevel >= RoomControllerLevel.ROOM_OWNER);
+ }
+ };
+
+ return this.isValidSetting(userInfo, checkSetting);
+ }
+
+ private static canBeBanned(userInfo: AvatarInfoUser): boolean
+ {
+ const checkSetting = (userInfo: AvatarInfoUser, moderation: IRoomModerationSettings) =>
+ {
+ switch(moderation.allowBan)
+ {
+ case RoomModerationSettings.MODERATION_LEVEL_USER_WITH_RIGHTS:
+ return this.checkGuildSetting(userInfo);
+ default:
+ return (userInfo.roomControllerLevel >= RoomControllerLevel.ROOM_OWNER);
+ }
+ };
+
+ return this.isValidSetting(userInfo, checkSetting);
+ }
+}
diff --git a/Coolui v3 test/src/api/room/widgets/BotSkillsEnum.ts b/Coolui v3 test/src/api/room/widgets/BotSkillsEnum.ts
new file mode 100644
index 0000000000..b879cdc99b
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/BotSkillsEnum.ts
@@ -0,0 +1,18 @@
+export class BotSkillsEnum
+{
+ public static GENERIC_SKILL: number = 0;
+ public static DRESS_UP: number = 1;
+ public static SETUP_CHAT: number = 2;
+ public static RANDOM_WALK: number = 3;
+ public static DANCE: number = 4;
+ public static CHANGE_BOT_NAME: number = 5;
+ public static SERVE_BEVERAGE: number = 6;
+ public static INCLIENT_LINK: number = 7;
+ public static NUX_PROCEED: number = 8;
+ public static CHANGE_BOT_MOTTO: number = 9;
+ public static NUX_TAKE_TOUR: number = 10;
+ public static NO_PICK_UP: number = 12;
+ public static NAVIGATOR_SEARCH: number = 14;
+ public static DONATE_TO_USER: number = 24;
+ public static DONATE_TO_ALL: number = 25;
+}
diff --git a/Coolui v3 test/src/api/room/widgets/ChatBubbleMessage.ts b/Coolui v3 test/src/api/room/widgets/ChatBubbleMessage.ts
new file mode 100644
index 0000000000..3e31e389d8
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/ChatBubbleMessage.ts
@@ -0,0 +1,54 @@
+export class ChatBubbleMessage
+{
+ public static BUBBLE_COUNTER: number = 0;
+
+ public id: number = -1;
+ public width: number = 0;
+ public height: number = 0;
+ public elementRef: HTMLDivElement = null;
+ public skipMovement: boolean = false;
+
+ private _top: number = 0;
+ private _left: number = 0;
+
+ constructor(
+ public senderId: number = -1,
+ public senderCategory: number = -1,
+ public roomId: number = -1,
+ public text: string = '',
+ public formattedText: string = '',
+ public username: string = '',
+ public location: { x: number, y: number } = null,
+ public type: number = 0,
+ public styleId: number = 0,
+ public imageUrl: string = null,
+ public color: string = null
+ )
+ {
+ this.id = ++ChatBubbleMessage.BUBBLE_COUNTER;
+ }
+
+ public get top(): number
+ {
+ return this._top;
+ }
+
+ public set top(value: number)
+ {
+ this._top = value;
+
+ if(this.elementRef) this.elementRef.style.top = (this._top + 'px');
+ }
+
+ public get left(): number
+ {
+ return this._left;
+ }
+
+ public set left(value: number)
+ {
+ this._left = value;
+
+ if(this.elementRef) this.elementRef.style.left = (this._left + 'px');
+ }
+}
diff --git a/Coolui v3 test/src/api/room/widgets/ChatBubbleUtilities.ts b/Coolui v3 test/src/api/room/widgets/ChatBubbleUtilities.ts
new file mode 100644
index 0000000000..fff0a14c4b
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/ChatBubbleUtilities.ts
@@ -0,0 +1,69 @@
+import { AvatarFigurePartType, AvatarScaleType, AvatarSetType, GetAvatarRenderManager, GetRoomEngine, PetFigureData, TextureUtils, Vector3d } from '@nitrots/nitro-renderer';
+
+export class ChatBubbleUtilities
+{
+ public static AVATAR_COLOR_CACHE: Map = new Map();
+ public static AVATAR_IMAGE_CACHE: Map = new Map();
+ public static PET_IMAGE_CACHE: Map = new Map();
+
+ private static placeHolderImageUrl: string = '';
+
+ public static async setFigureImage(figure: string): Promise
+ {
+ const avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, null, {
+ resetFigure: figure => this.setFigureImage(figure),
+ dispose: () =>
+ {},
+ disposed: false
+ });
+
+ if(!avatarImage) return null;
+
+ const isPlaceholder = avatarImage.isPlaceholder();
+
+ if(isPlaceholder && this.placeHolderImageUrl?.length) return this.placeHolderImageUrl;
+
+ figure = avatarImage.getFigure().getFigureString();
+
+ const imageUrl = avatarImage.processAsImageUrl(AvatarSetType.HEAD);
+ const color = avatarImage.getPartColor(AvatarFigurePartType.CHEST);
+
+ if(isPlaceholder) this.placeHolderImageUrl = imageUrl;
+
+ this.AVATAR_COLOR_CACHE.set(figure, ((color && color.rgb) || 16777215));
+ this.AVATAR_IMAGE_CACHE.set(figure, imageUrl);
+
+ avatarImage.dispose();
+
+ return imageUrl;
+ }
+
+ public static async getUserImage(figure: string): Promise
+ {
+ let existing = this.AVATAR_IMAGE_CACHE.get(figure);
+
+ if(!existing) existing = await this.setFigureImage(figure);
+
+ return existing;
+ }
+
+ public static async getPetImage(figure: string, direction: number, _arg_3: boolean, scale: number = 64, posture: string = null)
+ {
+ let existing = this.PET_IMAGE_CACHE.get((figure + posture));
+
+ if(existing) return existing;
+
+ const figureData = new PetFigureData(figure);
+ const typeId = figureData.typeId;
+ const image = GetRoomEngine().getRoomObjectPetImage(typeId, figureData.paletteId, figureData.color, new Vector3d((direction * 45)), scale, null, false, 0, figureData.customParts, posture);
+
+ if(image)
+ {
+ existing = await TextureUtils.generateImageUrl(image.data);
+
+ this.PET_IMAGE_CACHE.set((figure + posture), existing);
+ }
+
+ return existing;
+ }
+}
diff --git a/Coolui v3 test/src/api/room/widgets/ChatMessageTypeEnum.ts b/Coolui v3 test/src/api/room/widgets/ChatMessageTypeEnum.ts
new file mode 100644
index 0000000000..1a5296b1d7
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/ChatMessageTypeEnum.ts
@@ -0,0 +1,6 @@
+export class ChatMessageTypeEnum
+{
+ public static CHAT_DEFAULT: number = 0;
+ public static CHAT_WHISPER: number = 1;
+ public static CHAT_SHOUT: number = 2;
+}
diff --git a/Coolui v3 test/src/api/room/widgets/DimmerFurnitureWidgetPresetItem.ts b/Coolui v3 test/src/api/room/widgets/DimmerFurnitureWidgetPresetItem.ts
new file mode 100644
index 0000000000..009e530a1b
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/DimmerFurnitureWidgetPresetItem.ts
@@ -0,0 +1,9 @@
+export class DimmerFurnitureWidgetPresetItem
+{
+ constructor(
+ public id: number = 0,
+ public type: number = 0,
+ public color: number = 0,
+ public light: number = 0)
+ {}
+}
diff --git a/Coolui v3 test/src/api/room/widgets/DoChatsOverlap.ts b/Coolui v3 test/src/api/room/widgets/DoChatsOverlap.ts
new file mode 100644
index 0000000000..74f0d7fe1a
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/DoChatsOverlap.ts
@@ -0,0 +1,6 @@
+import { ChatBubbleMessage } from './ChatBubbleMessage';
+
+export const DoChatsOverlap = (a: ChatBubbleMessage, b: ChatBubbleMessage, additionalBTop: number, padding: number = 0) =>
+{
+ return !((((a.left + padding) + a.width) < (b.left + padding)) || ((a.left + padding) > ((b.left + padding) + b.width)) || ((a.top + a.height) < (b.top + additionalBTop)) || (a.top > ((b.top + additionalBTop) + b.height)));
+};
diff --git a/Coolui v3 test/src/api/room/widgets/FurnitureDimmerUtilities.ts b/Coolui v3 test/src/api/room/widgets/FurnitureDimmerUtilities.ts
new file mode 100644
index 0000000000..f55fc871c1
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/FurnitureDimmerUtilities.ts
@@ -0,0 +1,30 @@
+import { GetRoomEngine } from '@nitrots/nitro-renderer';
+import { GetRoomSession } from '../../nitro';
+
+export class FurnitureDimmerUtilities
+{
+ public static AVAILABLE_COLORS: number[] = [ 7665141, 21495, 15161822, 15353138, 15923281, 8581961, 0 ];
+ public static HTML_COLORS: string[] = [ '#74F5F5', '#0053F7', '#E759DE', '#EA4532', '#F2F851', '#82F349', '#000000' ];
+ public static MIN_BRIGHTNESS: number = 76;
+ public static MAX_BRIGHTNESS: number = 255;
+
+ public static savePreset(presetNumber: number, effectTypeId: number, color: number, brightness: number, apply: boolean): void
+ {
+ GetRoomSession().updateMoodlightData(presetNumber, effectTypeId, color, brightness, apply);
+ }
+
+ public static changeState(): void
+ {
+ GetRoomSession().toggleMoodlightState();
+ }
+
+ public static previewDimmer(color: number, brightness: number, bgOnly: boolean): void
+ {
+ GetRoomEngine().updateObjectRoomColor(GetRoomSession().roomId, color, brightness, bgOnly);
+ }
+
+ public static scaleBrightness(value: number): number
+ {
+ return ~~((((value - this.MIN_BRIGHTNESS) * (100 - 0)) / (this.MAX_BRIGHTNESS - this.MIN_BRIGHTNESS)) + 0);
+ }
+}
diff --git a/Coolui v3 test/src/api/room/widgets/GetDiskColor.ts b/Coolui v3 test/src/api/room/widgets/GetDiskColor.ts
new file mode 100644
index 0000000000..97cd24d700
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/GetDiskColor.ts
@@ -0,0 +1,37 @@
+const DISK_COLOR_RED_MIN: number = 130;
+const DISK_COLOR_RED_RANGE: number = 100;
+const DISK_COLOR_GREEN_MIN: number = 130;
+const DISK_COLOR_GREEN_RANGE: number = 100;
+const DISK_COLOR_BLUE_MIN: number = 130;
+const DISK_COLOR_BLUE_RANGE: number = 100;
+
+export const GetDiskColor = (name: string) =>
+{
+ let r: number = 0;
+ let g: number = 0;
+ let b: number = 0;
+ let index: number = 0;
+
+ while(index < name.length)
+ {
+ switch((index % 3))
+ {
+ case 0:
+ r = (r + ( name.charCodeAt(index) * 37) );
+ break;
+ case 1:
+ g = (g + ( name.charCodeAt(index) * 37) );
+ break;
+ case 2:
+ b = (b + ( name.charCodeAt(index) * 37) );
+ break;
+ }
+ index++;
+ }
+
+ r = ((r % DISK_COLOR_RED_RANGE) + DISK_COLOR_RED_MIN);
+ g = ((g % DISK_COLOR_GREEN_RANGE) + DISK_COLOR_GREEN_MIN);
+ b = ((b % DISK_COLOR_BLUE_RANGE) + DISK_COLOR_BLUE_MIN);
+
+ return `rgb(${ r },${ g },${ b })`;
+};
diff --git a/Coolui v3 test/src/api/room/widgets/IAvatarInfo.ts b/Coolui v3 test/src/api/room/widgets/IAvatarInfo.ts
new file mode 100644
index 0000000000..23fb47ba54
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/IAvatarInfo.ts
@@ -0,0 +1,4 @@
+export interface IAvatarInfo
+{
+ type: string;
+}
diff --git a/Coolui v3 test/src/api/room/widgets/ICraftingIngredient.ts b/Coolui v3 test/src/api/room/widgets/ICraftingIngredient.ts
new file mode 100644
index 0000000000..cb2b031158
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/ICraftingIngredient.ts
@@ -0,0 +1,6 @@
+export interface ICraftingIngredient
+{
+ name: string;
+ iconUrl: string;
+ count: number;
+}
diff --git a/Coolui v3 test/src/api/room/widgets/ICraftingRecipe.ts b/Coolui v3 test/src/api/room/widgets/ICraftingRecipe.ts
new file mode 100644
index 0000000000..dd99291fdf
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/ICraftingRecipe.ts
@@ -0,0 +1,6 @@
+export interface ICraftingRecipe
+{
+ name: string;
+ localizedName: string;
+ iconUrl: string;
+}
diff --git a/Coolui v3 test/src/api/room/widgets/IPhotoData.ts b/Coolui v3 test/src/api/room/widgets/IPhotoData.ts
new file mode 100644
index 0000000000..9a7b846ce1
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/IPhotoData.ts
@@ -0,0 +1,42 @@
+export interface IPhotoData
+{
+ /**
+ * creator username
+ */
+ n?: string;
+
+ /**
+ * creator user id
+ */
+ s?: number;
+
+ /**
+ * photo unique id
+ */
+ u?: number;
+
+ /**
+ * creation timestamp
+ */
+ t?: number;
+
+ /**
+ * photo caption
+ */
+ m?: string;
+
+ /**
+ * photo image url
+ */
+ w?: string;
+
+ /**
+ * owner id
+ */
+ oi?: number;
+
+ /**
+ * owner name
+ */
+ o?: string;
+}
\ No newline at end of file
diff --git a/Coolui v3 test/src/api/room/widgets/MannequinUtilities.ts b/Coolui v3 test/src/api/room/widgets/MannequinUtilities.ts
new file mode 100644
index 0000000000..74d45f9163
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/MannequinUtilities.ts
@@ -0,0 +1,38 @@
+import { AvatarFigurePartType, GetAvatarRenderManager, IAvatarFigureContainer } from '@nitrots/nitro-renderer';
+
+export class MannequinUtilities
+{
+ public static MANNEQUIN_FIGURE = [ 'hd', 99999, [ 99998 ] ];
+ public static MANNEQUIN_CLOTHING_PART_TYPES = [
+ AvatarFigurePartType.CHEST_ACCESSORY,
+ AvatarFigurePartType.COAT_CHEST,
+ AvatarFigurePartType.CHEST,
+ AvatarFigurePartType.LEGS,
+ AvatarFigurePartType.SHOES,
+ AvatarFigurePartType.WAIST_ACCESSORY
+ ];
+
+ public static getMergedMannequinFigureContainer(figure: string, targetFigure: string): IAvatarFigureContainer
+ {
+ const figureContainer = GetAvatarRenderManager().createFigureContainer(figure);
+ const targetFigureContainer = GetAvatarRenderManager().createFigureContainer(targetFigure);
+
+ for(const part of this.MANNEQUIN_CLOTHING_PART_TYPES) figureContainer.removePart(part);
+
+ for(const part of targetFigureContainer.getPartTypeIds()) figureContainer.updatePart(part, targetFigureContainer.getPartSetId(part), targetFigureContainer.getPartColorIds(part));
+
+ return figureContainer;
+ }
+
+ public static transformAsMannequinFigure(figureContainer: IAvatarFigureContainer): void
+ {
+ for(const part of figureContainer.getPartTypeIds())
+ {
+ if(this.MANNEQUIN_CLOTHING_PART_TYPES.indexOf(part) >= 0) continue;
+
+ figureContainer.removePart(part);
+ }
+
+ figureContainer.updatePart((this.MANNEQUIN_FIGURE[0] as string), (this.MANNEQUIN_FIGURE[1] as number), (this.MANNEQUIN_FIGURE[2] as number[]));
+ };
+}
diff --git a/Coolui v3 test/src/api/room/widgets/PetSupplementEnum.ts b/Coolui v3 test/src/api/room/widgets/PetSupplementEnum.ts
new file mode 100644
index 0000000000..eb23687513
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/PetSupplementEnum.ts
@@ -0,0 +1,5 @@
+export class PetSupplementEnum
+{
+ public static WATER: number = 0;
+ public static LIGHT: number = 1;
+}
diff --git a/Coolui v3 test/src/api/room/widgets/PostureTypeEnum.ts b/Coolui v3 test/src/api/room/widgets/PostureTypeEnum.ts
new file mode 100644
index 0000000000..21352d7875
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/PostureTypeEnum.ts
@@ -0,0 +1,5 @@
+export class PostureTypeEnum
+{
+ public static POSTURE_STAND: number = 0;
+ public static POSTURE_SIT: number = 1;
+}
diff --git a/Coolui v3 test/src/api/room/widgets/RoomDimmerPreset.ts b/Coolui v3 test/src/api/room/widgets/RoomDimmerPreset.ts
new file mode 100644
index 0000000000..86600d5835
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/RoomDimmerPreset.ts
@@ -0,0 +1,35 @@
+export class RoomDimmerPreset
+{
+ private _id: number;
+ private _type: number;
+ private _color: number;
+ private _brightness: number;
+
+ constructor(id: number, type: number, color: number, brightness: number)
+ {
+ this._id = id;
+ this._type = type;
+ this._color = color;
+ this._brightness = brightness;
+ }
+
+ public get id(): number
+ {
+ return this._id;
+ }
+
+ public get type(): number
+ {
+ return this._type;
+ }
+
+ public get color(): number
+ {
+ return this._color;
+ }
+
+ public get brightness(): number
+ {
+ return this._brightness;
+ }
+}
diff --git a/Coolui v3 test/src/api/room/widgets/RoomObjectItem.ts b/Coolui v3 test/src/api/room/widgets/RoomObjectItem.ts
new file mode 100644
index 0000000000..f4fb2d6fe7
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/RoomObjectItem.ts
@@ -0,0 +1,28 @@
+export class RoomObjectItem
+{
+ private _id: number;
+ private _category: number;
+ private _name: string;
+
+ constructor(id: number, category: number, name: string)
+ {
+ this._id = id;
+ this._category = category;
+ this._name = name;
+ }
+
+ public get id(): number
+ {
+ return this._id;
+ }
+
+ public get category(): number
+ {
+ return this._category;
+ }
+
+ public get name(): string
+ {
+ return this._name;
+ }
+}
diff --git a/Coolui v3 test/src/api/room/widgets/UseProductItem.ts b/Coolui v3 test/src/api/room/widgets/UseProductItem.ts
new file mode 100644
index 0000000000..d3e2088939
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/UseProductItem.ts
@@ -0,0 +1,12 @@
+export class UseProductItem
+{
+ constructor(
+ public readonly id: number,
+ public readonly category: number,
+ public readonly name: string,
+ public readonly requestRoomObjectId: number,
+ public readonly targetRoomObjectId: number,
+ public readonly requestInventoryStripId: number,
+ public readonly replace: boolean)
+ {}
+}
diff --git a/Coolui v3 test/src/api/room/widgets/VoteValue.ts b/Coolui v3 test/src/api/room/widgets/VoteValue.ts
new file mode 100644
index 0000000000..ecf4336eab
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/VoteValue.ts
@@ -0,0 +1,8 @@
+export const VALUE_KEY_DISLIKE = '0';
+export const VALUE_KEY_LIKE = '1';
+
+export interface VoteValue
+{
+ value: string;
+ secondsLeft: number;
+}
diff --git a/Coolui v3 test/src/api/room/widgets/YoutubeVideoPlaybackStateEnum.ts b/Coolui v3 test/src/api/room/widgets/YoutubeVideoPlaybackStateEnum.ts
new file mode 100644
index 0000000000..2d1784158e
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/YoutubeVideoPlaybackStateEnum.ts
@@ -0,0 +1,9 @@
+export class YoutubeVideoPlaybackStateEnum
+{
+ public static readonly UNSTARTED = -1;
+ public static readonly ENDED = 0;
+ public static readonly PLAYING = 1;
+ public static readonly PAUSED = 2;
+ public static readonly BUFFERING = 3;
+ public static readonly CUED = 5;
+}
diff --git a/Coolui v3 test/src/api/room/widgets/index.ts b/Coolui v3 test/src/api/room/widgets/index.ts
new file mode 100644
index 0000000000..6c50c8383a
--- /dev/null
+++ b/Coolui v3 test/src/api/room/widgets/index.ts
@@ -0,0 +1,26 @@
+export * from './AvatarInfoFurni';
+export * from './AvatarInfoName';
+export * from './AvatarInfoPet';
+export * from './AvatarInfoRentableBot';
+export * from './AvatarInfoUser';
+export * from './AvatarInfoUtilities';
+export * from './BotSkillsEnum';
+export * from './ChatBubbleMessage';
+export * from './ChatBubbleUtilities';
+export * from './ChatMessageTypeEnum';
+export * from './DimmerFurnitureWidgetPresetItem';
+export * from './DoChatsOverlap';
+export * from './FurnitureDimmerUtilities';
+export * from './GetDiskColor';
+export * from './IAvatarInfo';
+export * from './ICraftingIngredient';
+export * from './ICraftingRecipe';
+export * from './IPhotoData';
+export * from './MannequinUtilities';
+export * from './PetSupplementEnum';
+export * from './PostureTypeEnum';
+export * from './RoomDimmerPreset';
+export * from './RoomObjectItem';
+export * from './UseProductItem';
+export * from './VoteValue';
+export * from './YoutubeVideoPlaybackStateEnum';
diff --git a/Coolui v3 test/src/api/user/GetUserProfile.ts b/Coolui v3 test/src/api/user/GetUserProfile.ts
new file mode 100644
index 0000000000..13c67aa465
--- /dev/null
+++ b/Coolui v3 test/src/api/user/GetUserProfile.ts
@@ -0,0 +1,7 @@
+import { UserProfileComposer } from '@nitrots/nitro-renderer';
+import { SendMessageComposer } from '../nitro';
+
+export function GetUserProfile(userId: number): void
+{
+ SendMessageComposer(new UserProfileComposer(userId));
+}
diff --git a/Coolui v3 test/src/api/user/index.ts b/Coolui v3 test/src/api/user/index.ts
new file mode 100644
index 0000000000..1c609ea780
--- /dev/null
+++ b/Coolui v3 test/src/api/user/index.ts
@@ -0,0 +1 @@
+export * from './GetUserProfile';
diff --git a/Coolui v3 test/src/api/utils/CloneObject.ts b/Coolui v3 test/src/api/utils/CloneObject.ts
new file mode 100644
index 0000000000..b306fac054
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/CloneObject.ts
@@ -0,0 +1,14 @@
+export const CloneObject = (object: T): T =>
+{
+ if((object == null) || ('object' != typeof object)) return object;
+
+ // @ts-ignore
+ const copy = new object.constructor();
+
+ for(const attr in object)
+ {
+ if(object.hasOwnProperty(attr)) copy[attr] = object[attr];
+ }
+
+ return copy;
+};
diff --git a/Coolui v3 test/src/api/utils/ColorUtils.ts b/Coolui v3 test/src/api/utils/ColorUtils.ts
new file mode 100644
index 0000000000..ff3a0bf012
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/ColorUtils.ts
@@ -0,0 +1,65 @@
+export class ColorUtils
+{
+ public static makeColorHex(color: string): string
+ {
+ return ('#' + color);
+ }
+
+ public static makeColorNumberHex(color: number): string
+ {
+ let val = color.toString(16);
+ return ( '#' + val.padStart(6, '0'));
+ }
+
+ public static convertFromHex(color: string): number
+ {
+ return parseInt(color.replace('#', ''), 16);
+ }
+
+ public static uintHexColor(color: number): string
+ {
+ const realColor = color >>>0;
+
+ return ColorUtils.makeColorHex(realColor.toString(16).substring(2));
+ }
+
+ /**
+ * Converts an integer format into an array of 8-bit values
+ * @param {number} value value in integer format
+ * @returns {Array} 8-bit values
+ */
+ public static int_to_8BitVals(value: number): [number, number, number, number]
+ {
+ const val1 = ((value >> 24) & 0xFF);
+ const val2 = ((value >> 16) & 0xFF);
+ const val3 = ((value >> 8) & 0xFF);
+ const val4 = (value & 0xFF);
+
+ return [ val1, val2, val3, val4 ];
+ }
+
+ /**
+ * Combines 4 8-bit values into a 32-bit integer. Values are combined in
+ * in the order of the parameters
+ * @param val1
+ * @param val2
+ * @param val3
+ * @param val4
+ * @returns 32-bit integer of combined values
+ */
+ public static eight_bitVals_to_int(val1: number, val2: number, val3: number, val4: number): number
+ {
+ return (((val1) << 24) + ((val2) << 16) + ((val3) << 8) + (val4| 0));
+ }
+
+ public static int2rgb(color: number): string
+ {
+ color >>>= 0;
+ const b = color & 0xFF;
+ const g = (color & 0xFF00) >>> 8;
+ const r = (color & 0xFF0000) >>> 16;
+ const a = ((color & 0xFF000000) >>> 24) / 255;
+
+ return 'rgba(' + [ r, g, b, 1 ].join(',') + ')';
+ }
+}
diff --git a/Coolui v3 test/src/api/utils/ConvertSeconds.ts b/Coolui v3 test/src/api/utils/ConvertSeconds.ts
new file mode 100644
index 0000000000..351dda81c5
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/ConvertSeconds.ts
@@ -0,0 +1,9 @@
+export const ConvertSeconds = (seconds: number) =>
+{
+ let numDays = Math.floor(seconds / 86400);
+ let numHours = Math.floor((seconds % 86400) / 3600);
+ let numMinutes = Math.floor(((seconds % 86400) % 3600) / 60);
+ let numSeconds = ((seconds % 86400) % 3600) % 60;
+
+ return numDays.toString().padStart(2, '0') + ':' + numHours.toString().padStart(2, '0') + ':' + numMinutes.toString().padStart(2, '0') + ':' + numSeconds.toString().padStart(2, '0');
+};
diff --git a/Coolui v3 test/src/api/utils/FixedSizeStack.ts b/Coolui v3 test/src/api/utils/FixedSizeStack.ts
new file mode 100644
index 0000000000..af8e09aa4b
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/FixedSizeStack.ts
@@ -0,0 +1,65 @@
+export class FixedSizeStack
+{
+ private _data: number[];
+ private _maxSize: number;
+ private _index: number;
+
+ constructor(k: number)
+ {
+ this._data = [];
+ this._maxSize = k;
+ this._index = 0;
+ }
+
+ public reset(): void
+ {
+ this._data = [];
+ this._index = 0;
+ }
+
+ public addValue(k: number): void
+ {
+ if(this._data.length < this._maxSize)
+ {
+ this._data.push(k);
+ }
+ else
+ {
+ this._data[this._index] = k;
+ }
+
+ this._index = ((this._index + 1) % this._maxSize);
+ }
+
+ public getMax(): number
+ {
+ let k = Number.MIN_VALUE;
+
+ let _local_2 = 0;
+
+ while(_local_2 < this._maxSize)
+ {
+ if(this._data[_local_2] > k) k = this._data[_local_2];
+
+ _local_2++;
+ }
+
+ return k;
+ }
+
+ public getMin(): number
+ {
+ let k = Number.MAX_VALUE;
+
+ let _local_2 = 0;
+
+ while(_local_2 < this._maxSize)
+ {
+ if(this._data[_local_2] < k) k = this._data[_local_2];
+
+ _local_2++;
+ }
+
+ return k;
+ }
+}
diff --git a/Coolui v3 test/src/api/utils/FriendlyTime.ts b/Coolui v3 test/src/api/utils/FriendlyTime.ts
new file mode 100644
index 0000000000..7acb39c577
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/FriendlyTime.ts
@@ -0,0 +1,47 @@
+import { LocalizeText } from './LocalizeText';
+
+export class FriendlyTime
+{
+ private static MINUTE: number = 60;
+ private static HOUR: number = (60 * FriendlyTime.MINUTE);
+ private static DAY: number = (24 * FriendlyTime.HOUR);
+ private static WEEK: number = (7 * FriendlyTime.DAY);
+ private static MONTH: number = (30 * FriendlyTime.DAY);
+ private static YEAR: number = (365 * FriendlyTime.DAY);
+
+
+ public static format(seconds: number, key: string = '', threshold: number = 3): string
+ {
+ if(seconds > (threshold * FriendlyTime.YEAR)) return FriendlyTime.getLocalization(('friendlytime.years' + key), Math.round((seconds / FriendlyTime.YEAR)));
+
+ if(seconds > (threshold * FriendlyTime.MONTH)) return FriendlyTime.getLocalization(('friendlytime.months' + key), Math.round((seconds / FriendlyTime.MONTH)));
+
+ if(seconds > (threshold * FriendlyTime.DAY)) return FriendlyTime.getLocalization(('friendlytime.days' + key), Math.round((seconds / FriendlyTime.DAY)));
+
+ if(seconds > (threshold * FriendlyTime.HOUR)) return FriendlyTime.getLocalization(('friendlytime.hours' + key), Math.round((seconds / FriendlyTime.HOUR)));
+
+ if(seconds > (threshold * FriendlyTime.MINUTE)) return FriendlyTime.getLocalization(('friendlytime.minutes' + key), Math.round((seconds / FriendlyTime.MINUTE)));
+
+ return FriendlyTime.getLocalization(('friendlytime.seconds' + key), Math.round(seconds));
+ }
+
+ public static shortFormat(seconds: number, key: string = '', threshold: number = 3): string
+ {
+ if(seconds > (threshold * FriendlyTime.YEAR)) return FriendlyTime.getLocalization(('friendlytime.years.short' + key), Math.round((seconds / FriendlyTime.YEAR)));
+
+ if(seconds > (threshold * FriendlyTime.MONTH)) return FriendlyTime.getLocalization(('friendlytime.months.short' + key), Math.round((seconds / FriendlyTime.MONTH)));
+
+ if(seconds > (threshold * FriendlyTime.DAY)) return FriendlyTime.getLocalization(('friendlytime.days.short' + key), Math.round((seconds / FriendlyTime.DAY)));
+
+ if(seconds > (threshold * FriendlyTime.HOUR)) return FriendlyTime.getLocalization(('friendlytime.hours.short' + key), Math.round((seconds / FriendlyTime.HOUR)));
+
+ if(seconds > (threshold * FriendlyTime.MINUTE)) return FriendlyTime.getLocalization(('friendlytime.minutes.short' + key), Math.round((seconds / FriendlyTime.MINUTE)));
+
+ return FriendlyTime.getLocalization(('friendlytime.seconds.short' + key), Math.round(seconds));
+ }
+
+ public static getLocalization(key: string, amount: number): string
+ {
+ return LocalizeText(key, [ 'amount' ], [ amount.toString() ]);
+ }
+}
diff --git a/Coolui v3 test/src/api/utils/GetLocalStorage.ts b/Coolui v3 test/src/api/utils/GetLocalStorage.ts
new file mode 100644
index 0000000000..a4270cfc23
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/GetLocalStorage.ts
@@ -0,0 +1,11 @@
+export const GetLocalStorage = (key: string) =>
+{
+ try
+ {
+ JSON.parse(window.localStorage.getItem(key)) as T ?? null;
+ }
+ catch (e)
+ {
+ return null;
+ }
+};
diff --git a/Coolui v3 test/src/api/utils/LocalStorageKeys.ts b/Coolui v3 test/src/api/utils/LocalStorageKeys.ts
new file mode 100644
index 0000000000..6c922790fc
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/LocalStorageKeys.ts
@@ -0,0 +1,5 @@
+export class LocalStorageKeys
+{
+ public static CATALOG_PLACE_MULTIPLE_OBJECTS: string = 'catalogPlaceMultipleObjects';
+ public static CATALOG_SKIP_PURCHASE_CONFIRMATION: string = 'catalogSkipPurchaseConfirmation';
+}
diff --git a/Coolui v3 test/src/api/utils/LocalizeBadgeDescription.ts b/Coolui v3 test/src/api/utils/LocalizeBadgeDescription.ts
new file mode 100644
index 0000000000..11f178e776
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/LocalizeBadgeDescription.ts
@@ -0,0 +1,10 @@
+import { GetLocalizationManager } from '@nitrots/nitro-renderer';
+
+export const LocalizeBadgeDescription = (key: string) =>
+{
+ let badgeDesc = GetLocalizationManager().getBadgeDesc(key);
+
+ if(!badgeDesc || !badgeDesc.length) badgeDesc = `badge_desc_${ key }`;
+
+ return badgeDesc;
+};
diff --git a/Coolui v3 test/src/api/utils/LocalizeBageName.ts b/Coolui v3 test/src/api/utils/LocalizeBageName.ts
new file mode 100644
index 0000000000..47645cbf33
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/LocalizeBageName.ts
@@ -0,0 +1,10 @@
+import { GetLocalizationManager } from '@nitrots/nitro-renderer';
+
+export const LocalizeBadgeName = (key: string) =>
+{
+ let badgeName = GetLocalizationManager().getBadgeName(key);
+
+ if(!badgeName || !badgeName.length) badgeName = `badge_name_${ key }`;
+
+ return badgeName;
+};
diff --git a/Coolui v3 test/src/api/utils/LocalizeFormattedNumber.ts b/Coolui v3 test/src/api/utils/LocalizeFormattedNumber.ts
new file mode 100644
index 0000000000..fab30d466f
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/LocalizeFormattedNumber.ts
@@ -0,0 +1,6 @@
+export function LocalizeFormattedNumber(number: number): string
+{
+ if(!number || isNaN(number)) return '0';
+
+ return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
+};
diff --git a/Coolui v3 test/src/api/utils/LocalizeShortNumber.ts b/Coolui v3 test/src/api/utils/LocalizeShortNumber.ts
new file mode 100644
index 0000000000..30975ecac1
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/LocalizeShortNumber.ts
@@ -0,0 +1,36 @@
+export function LocalizeShortNumber(number: number): string
+{
+ if(!number || isNaN(number)) return '0';
+
+ let abs = Math.abs(number);
+
+ const rounder = Math.pow(10, 1);
+ const isNegative = (number < 0);
+
+ let key = '';
+
+ const powers = [
+ { key: 'Q', value: Math.pow(10, 15) },
+ { key: 'T', value: Math.pow(10, 12) },
+ { key: 'B', value: Math.pow(10, 9) },
+ { key: 'M', value: Math.pow(10, 6) },
+ { key: 'K', value: 1000 }
+ ];
+
+ for(const power of powers)
+ {
+ let reduced = abs / power.value;
+
+ reduced = Math.round(reduced * rounder) / rounder;
+
+ if(reduced >= 1)
+ {
+ abs = reduced;
+ key = power.key;
+
+ break;
+ }
+ }
+
+ return ((isNegative ? '-' : '') + abs + key);
+}
diff --git a/Coolui v3 test/src/api/utils/LocalizeText.ts b/Coolui v3 test/src/api/utils/LocalizeText.ts
new file mode 100644
index 0000000000..68d0273918
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/LocalizeText.ts
@@ -0,0 +1,6 @@
+import { GetLocalizationManager } from '@nitrots/nitro-renderer';
+
+export function LocalizeText(key: string, parameters: string[] = null, replacements: string[] = null): string
+{
+ return GetLocalizationManager().getValueWithParameters(key, parameters, replacements);
+}
diff --git a/Coolui v3 test/src/api/utils/PlaySound.ts b/Coolui v3 test/src/api/utils/PlaySound.ts
new file mode 100644
index 0000000000..b0f903cd83
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/PlaySound.ts
@@ -0,0 +1,24 @@
+import { MouseEventType, NitroSoundEvent } from '@nitrots/nitro-renderer';
+import { DispatchMainEvent } from '../events';
+
+let canPlaySound = false;
+
+export const PlaySound = (sampleCode: string) =>
+{
+ if(!canPlaySound) return;
+
+ DispatchMainEvent(new NitroSoundEvent(NitroSoundEvent.PLAY_SOUND, sampleCode));
+};
+
+const eventTypes = [ MouseEventType.MOUSE_CLICK ];
+
+const startListening = () =>
+{
+ const stopListening = () => eventTypes.forEach(type => window.removeEventListener(type, onEvent));
+
+ const onEvent = (event: Event) => ((canPlaySound = true) && stopListening());
+
+ eventTypes.forEach(type => window.addEventListener(type, onEvent));
+};
+
+startListening();
diff --git a/Coolui v3 test/src/api/utils/ProductImageUtility.ts b/Coolui v3 test/src/api/utils/ProductImageUtility.ts
new file mode 100644
index 0000000000..544351322a
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/ProductImageUtility.ts
@@ -0,0 +1,58 @@
+import { FurnitureType, GetRoomEngine } from '@nitrots/nitro-renderer';
+import { FurniCategory } from '../inventory';
+
+export class ProductImageUtility
+{
+ public static getProductImageUrl(productType: FurnitureType, furniClassId: number, extraParam: string): string
+ {
+ let imageUrl: string = null;
+
+ switch(productType)
+ {
+ case FurnitureType.FLOOR:
+ imageUrl = GetRoomEngine().getFurnitureFloorIconUrl(furniClassId);
+ break;
+ case FurnitureType.WALL:
+ const productCategory = this.getProductCategory(CatalogPageMessageProductData.I, furniClassId);
+
+ if(productCategory === 1)
+ {
+ imageUrl = GetRoomEngine().getFurnitureWallIconUrl(furniClassId, extraParam);
+ }
+ else
+ {
+ switch(productCategory)
+ {
+ case FurniCategory.WALL_PAPER:
+ break;
+ case FurniCategory.LANDSCAPE:
+ break;
+ case FurniCategory.FLOOR:
+ break;
+ }
+ }
+ break;
+ case FurnitureType.EFFECT:
+ // fx_icon_furniClassId_png
+ break;
+ }
+
+ return imageUrl;
+ }
+
+ public static getProductCategory(productType: FurnitureType, furniClassId: number): number
+ {
+ if(productType === FurnitureType.FLOOR) return 1;
+
+ if(productType === FurnitureType.WALL)
+ {
+ if(furniClassId === 3001) return FurniCategory.WALL_PAPER;
+
+ if(furniClassId === 3002) return FurniCategory.FLOOR;
+
+ if(furniClassId === 4057) return FurniCategory.LANDSCAPE;
+ }
+
+ return 1;
+ }
+}
diff --git a/Coolui v3 test/src/api/utils/Randomizer.ts b/Coolui v3 test/src/api/utils/Randomizer.ts
new file mode 100644
index 0000000000..1f67a129ce
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/Randomizer.ts
@@ -0,0 +1,28 @@
+export class Randomizer
+{
+ public static getRandomNumber(count: number): number
+ {
+ return Math.floor(Math.random() * count);
+ }
+
+ public static getRandomElement(elements: T[]): T
+ {
+ return elements[this.getRandomNumber(elements.length)];
+ }
+
+ public static getRandomElements(elements: T[], count: number): T[]
+ {
+ const result: T[] = new Array(count);
+ let len = elements.length;
+ const taken = new Array(len);
+
+ while(count--)
+ {
+ var x = this.getRandomNumber(len);
+ result[count] = elements[x in taken ? taken[x] : x];
+ taken[x] = --len in taken ? taken[len] : len;
+ }
+
+ return result;
+ }
+}
diff --git a/Coolui v3 test/src/api/utils/RoomChatFormatter.ts b/Coolui v3 test/src/api/utils/RoomChatFormatter.ts
new file mode 100644
index 0000000000..f87840b337
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/RoomChatFormatter.ts
@@ -0,0 +1,75 @@
+const allowedColours: Map = new Map();
+
+allowedColours.set('r', 'red');
+allowedColours.set('b', 'blue');
+allowedColours.set('g', 'green');
+allowedColours.set('y', 'yellow');
+allowedColours.set('w', 'white');
+allowedColours.set('o', 'orange');
+allowedColours.set('c', 'cyan');
+allowedColours.set('br', 'brown');
+allowedColours.set('pr', 'purple');
+allowedColours.set('pk', 'pink');
+
+allowedColours.set('red', 'red');
+allowedColours.set('blue', 'blue');
+allowedColours.set('green', 'green');
+allowedColours.set('yellow', 'yellow');
+allowedColours.set('white', 'white');
+allowedColours.set('orange', 'orange');
+allowedColours.set('cyan', 'cyan');
+allowedColours.set('brown', 'brown');
+allowedColours.set('purple', 'purple');
+allowedColours.set('pink', 'pink');
+
+const encodeHTML = (str: string) =>
+{
+ return str.replace(/([\u00A0-\u9999<>&])(.|$)/g, (full, char, next) =>
+ {
+ if(char !== '&' || next !== '#')
+ {
+ if(/[\u00A0-\u9999<>&]/.test(next)) next = '' + next.charCodeAt(0) + ';';
+
+ return '' + char.charCodeAt(0) + ';' + next;
+ }
+
+ return full;
+ });
+};
+
+export const RoomChatFormatter = (content: string) =>
+{
+ let result = '';
+
+ content = encodeHTML(content);
+ //content = (joypixels.shortnameToUnicode(content) as string)
+
+ if(content.startsWith('@') && content.indexOf('@', 1) > -1)
+ {
+ let match = null;
+
+ while((match = /@[a-zA-Z]+@/g.exec(content)) !== null)
+ {
+ const colorTag = match[0].toString();
+ const colorName = colorTag.substr(1, colorTag.length - 2);
+ const text = content.replace(colorTag, '');
+
+ if(!allowedColours.has(colorName))
+ {
+ result = text;
+ }
+ else
+ {
+ const color = allowedColours.get(colorName);
+ result = '' + text + '';
+ }
+ break;
+ }
+ }
+ else
+ {
+ result = content;
+ }
+
+ return result;
+};
diff --git a/Coolui v3 test/src/api/utils/SetLocalStorage.ts b/Coolui v3 test/src/api/utils/SetLocalStorage.ts
new file mode 100644
index 0000000000..02aa8f3da1
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/SetLocalStorage.ts
@@ -0,0 +1 @@
+export const SetLocalStorage = (key: string, value: T) => window.localStorage.setItem(key, JSON.stringify(value));
diff --git a/Coolui v3 test/src/api/utils/SoundNames.ts b/Coolui v3 test/src/api/utils/SoundNames.ts
new file mode 100644
index 0000000000..4459651b35
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/SoundNames.ts
@@ -0,0 +1,9 @@
+export class SoundNames
+{
+ public static CAMERA_SHUTTER = 'camera_shutter';
+ public static CREDITS = 'credits';
+ public static DUCKETS = 'duckets';
+ public static MESSENGER_NEW_THREAD = 'messenger_new_thread';
+ public static MESSENGER_MESSAGE_RECEIVED = 'messenger_message_received';
+ public static MODTOOLS_NEW_TICKET = 'modtools_new_ticket';
+}
diff --git a/Coolui v3 test/src/api/utils/WindowSaveOptions.ts b/Coolui v3 test/src/api/utils/WindowSaveOptions.ts
new file mode 100644
index 0000000000..9aa8456313
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/WindowSaveOptions.ts
@@ -0,0 +1,5 @@
+export interface WindowSaveOptions
+{
+ offset: { x: number, y: number };
+ size: { width: number, height: number };
+}
diff --git a/Coolui v3 test/src/api/utils/index.ts b/Coolui v3 test/src/api/utils/index.ts
new file mode 100644
index 0000000000..1824addff3
--- /dev/null
+++ b/Coolui v3 test/src/api/utils/index.ts
@@ -0,0 +1,19 @@
+export * from './CloneObject';
+export * from './ColorUtils';
+export * from './ConvertSeconds';
+export * from './FixedSizeStack';
+export * from './FriendlyTime';
+export * from './GetLocalStorage';
+export * from './LocalStorageKeys';
+export * from './LocalizeBadgeDescription';
+export * from './LocalizeBageName';
+export * from './LocalizeFormattedNumber';
+export * from './LocalizeShortNumber';
+export * from './LocalizeText';
+export * from './PlaySound';
+export * from './ProductImageUtility';
+export * from './Randomizer';
+export * from './RoomChatFormatter';
+export * from './SetLocalStorage';
+export * from './SoundNames';
+export * from './WindowSaveOptions';
diff --git a/Coolui v3 test/src/api/wired/GetWiredTimeLocale.ts b/Coolui v3 test/src/api/wired/GetWiredTimeLocale.ts
new file mode 100644
index 0000000000..39f3516004
--- /dev/null
+++ b/Coolui v3 test/src/api/wired/GetWiredTimeLocale.ts
@@ -0,0 +1,8 @@
+export const GetWiredTimeLocale = (value: number) =>
+{
+ const time = Math.floor((value / 2));
+
+ if(!(value % 2)) return time.toString();
+
+ return (time + 0.5).toString();
+};
diff --git a/Coolui v3 test/src/api/wired/WiredActionLayoutCode.ts b/Coolui v3 test/src/api/wired/WiredActionLayoutCode.ts
new file mode 100644
index 0000000000..5282dc5905
--- /dev/null
+++ b/Coolui v3 test/src/api/wired/WiredActionLayoutCode.ts
@@ -0,0 +1,29 @@
+export class WiredActionLayoutCode
+{
+ public static TOGGLE_FURNI_STATE: number = 0;
+ public static RESET: number = 1;
+ public static SET_FURNI_STATE: number = 3;
+ public static MOVE_FURNI: number = 4;
+ public static GIVE_SCORE: number = 6;
+ public static CHAT: number = 7;
+ public static TELEPORT: number = 8;
+ public static JOIN_TEAM: number = 9;
+ public static LEAVE_TEAM: number = 10;
+ public static CHASE: number = 11;
+ public static FLEE: number = 12;
+ public static MOVE_AND_ROTATE_FURNI: number = 13;
+ public static GIVE_SCORE_TO_PREDEFINED_TEAM: number = 14;
+ public static TOGGLE_TO_RANDOM_STATE: number = 15;
+ public static MOVE_FURNI_TO: number = 16;
+ public static GIVE_REWARD: number = 17;
+ public static CALL_ANOTHER_STACK: number = 18;
+ public static KICK_FROM_ROOM: number = 19;
+ public static MUTE_USER: number = 20;
+ public static BOT_TELEPORT: number = 21;
+ public static BOT_MOVE: number = 22;
+ public static BOT_TALK: number = 23;
+ public static BOT_GIVE_HAND_ITEM: number = 24;
+ public static BOT_FOLLOW_AVATAR: number = 25;
+ public static BOT_CHANGE_FIGURE: number = 26;
+ public static BOT_TALK_DIRECT_TO_AVTR: number = 27;
+}
diff --git a/Coolui v3 test/src/api/wired/WiredConditionLayoutCode.ts b/Coolui v3 test/src/api/wired/WiredConditionLayoutCode.ts
new file mode 100644
index 0000000000..58cae5db58
--- /dev/null
+++ b/Coolui v3 test/src/api/wired/WiredConditionLayoutCode.ts
@@ -0,0 +1,29 @@
+export class WiredConditionlayout
+{
+ public static STATES_MATCH: number = 0;
+ public static FURNIS_HAVE_AVATARS: number = 1;
+ public static ACTOR_IS_ON_FURNI: number = 2;
+ public static TIME_ELAPSED_MORE: number = 3;
+ public static TIME_ELAPSED_LESS: number = 4;
+ public static USER_COUNT_IN: number = 5;
+ public static ACTOR_IS_IN_TEAM: number = 6;
+ public static HAS_STACKED_FURNIS: number = 7;
+ public static STUFF_TYPE_MATCHES: number = 8;
+ public static STUFFS_IN_FORMATION: number = 9;
+ public static ACTOR_IS_GROUP_MEMBER: number = 10;
+ public static ACTOR_IS_WEARING_BADGE: number = 11;
+ public static ACTOR_IS_WEARING_EFFECT: number = 12;
+ public static NOT_STATES_MATCH: number = 13;
+ public static FURNI_NOT_HAVE_HABBO: number = 14;
+ public static NOT_ACTOR_ON_FURNI: number = 15;
+ public static NOT_USER_COUNT_IN: number = 16;
+ public static NOT_ACTOR_IN_TEAM: number = 17;
+ public static NOT_HAS_STACKED_FURNIS: number = 18;
+ public static NOT_FURNI_IS_OF_TYPE: number = 19;
+ public static NOT_STUFFS_IN_FORMATION: number = 20;
+ public static NOT_ACTOR_IN_GROUP: number = 21;
+ public static NOT_ACTOR_WEARS_BADGE: number = 22;
+ public static NOT_ACTOR_WEARING_EFFECT: number = 23;
+ public static DATE_RANGE_ACTIVE: number = 24;
+ public static ACTOR_HAS_HANDITEM: number = 25;
+}
diff --git a/Coolui v3 test/src/api/wired/WiredDateToString.ts b/Coolui v3 test/src/api/wired/WiredDateToString.ts
new file mode 100644
index 0000000000..825adc8ec5
--- /dev/null
+++ b/Coolui v3 test/src/api/wired/WiredDateToString.ts
@@ -0,0 +1 @@
+export const WiredDateToString = (date: Date) => `${ date.getFullYear() }/${ ('0' + (date.getMonth() + 1)).slice(-2) }/${ ('0' + date.getDate()).slice(-2) } ${ ('0' + date.getHours()).slice(-2) }:${ ('0' + date.getMinutes()).slice(-2) }`;
diff --git a/Coolui v3 test/src/api/wired/WiredFurniType.ts b/Coolui v3 test/src/api/wired/WiredFurniType.ts
new file mode 100644
index 0000000000..447e970127
--- /dev/null
+++ b/Coolui v3 test/src/api/wired/WiredFurniType.ts
@@ -0,0 +1,7 @@
+export class WiredFurniType
+{
+ public static STUFF_SELECTION_OPTION_NONE: number = 0;
+ public static STUFF_SELECTION_OPTION_BY_ID: number = 1;
+ public static STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE: number = 2;
+ public static STUFF_SELECTION_OPTION_BY_ID_BY_TYPE_OR_FROM_CONTEXT: number = 3;
+}
diff --git a/Coolui v3 test/src/api/wired/WiredSelectionVisualizer.ts b/Coolui v3 test/src/api/wired/WiredSelectionVisualizer.ts
new file mode 100644
index 0000000000..18edbf7c7b
--- /dev/null
+++ b/Coolui v3 test/src/api/wired/WiredSelectionVisualizer.ts
@@ -0,0 +1,85 @@
+import { GetRoomEngine, IRoomObject, IRoomObjectSpriteVisualization, RoomObjectCategory, WiredFilter } from '@nitrots/nitro-renderer';
+
+export class WiredSelectionVisualizer
+{
+ private static _selectionShader: WiredFilter = new WiredFilter({
+ lineColor: [ 1, 1, 1 ],
+ color: [ 0.6, 0.6, 0.6 ]
+ });
+
+ public static show(furniId: number): void
+ {
+ WiredSelectionVisualizer.applySelectionShader(WiredSelectionVisualizer.getRoomObject(furniId));
+ }
+
+ public static hide(furniId: number): void
+ {
+ WiredSelectionVisualizer.clearSelectionShader(WiredSelectionVisualizer.getRoomObject(furniId));
+ }
+
+ public static clearSelectionShaderFromFurni(furniIds: number[]): void
+ {
+ for(const furniId of furniIds)
+ {
+ WiredSelectionVisualizer.clearSelectionShader(WiredSelectionVisualizer.getRoomObject(furniId));
+ }
+ }
+
+ public static applySelectionShaderToFurni(furniIds: number[]): void
+ {
+ for(const furniId of furniIds)
+ {
+ WiredSelectionVisualizer.applySelectionShader(WiredSelectionVisualizer.getRoomObject(furniId));
+ }
+ }
+
+ private static getRoomObject(objectId: number): IRoomObject
+ {
+ const roomEngine = GetRoomEngine();
+
+ return roomEngine.getRoomObject(roomEngine.activeRoomId, objectId, RoomObjectCategory.FLOOR);
+ }
+
+ private static applySelectionShader(roomObject: IRoomObject): void
+ {
+ if(!roomObject) return;
+
+ const visualization = (roomObject.visualization as IRoomObjectSpriteVisualization);
+
+ if(!visualization) return;
+
+ for(const sprite of visualization.sprites)
+ {
+ if(sprite.blendMode === 'add') continue;
+
+ if(!sprite.filters) sprite.filters = [];
+
+ sprite.filters.push(WiredSelectionVisualizer._selectionShader);
+
+ sprite.increaseUpdateCounter();
+ }
+ }
+
+ private static clearSelectionShader(roomObject: IRoomObject): void
+ {
+ if(!roomObject) return;
+
+ const visualization = (roomObject.visualization as IRoomObjectSpriteVisualization);
+
+ if(!visualization) return;
+
+ for(const sprite of visualization.sprites)
+ {
+ if(!sprite.filters) continue;
+
+ const index = sprite.filters.indexOf(WiredSelectionVisualizer._selectionShader);
+
+ if(index >= 0)
+ {
+ sprite.filters.splice(index, 1);
+
+ sprite.increaseUpdateCounter();
+ }
+ }
+ }
+}
diff --git a/Coolui v3 test/src/api/wired/WiredStringDelimeter.ts b/Coolui v3 test/src/api/wired/WiredStringDelimeter.ts
new file mode 100644
index 0000000000..bc4cf2e41a
--- /dev/null
+++ b/Coolui v3 test/src/api/wired/WiredStringDelimeter.ts
@@ -0,0 +1 @@
+export const WIRED_STRING_DELIMETER: string = '\t';
diff --git a/Coolui v3 test/src/api/wired/WiredTriggerLayoutCode.ts b/Coolui v3 test/src/api/wired/WiredTriggerLayoutCode.ts
new file mode 100644
index 0000000000..fd758dff54
--- /dev/null
+++ b/Coolui v3 test/src/api/wired/WiredTriggerLayoutCode.ts
@@ -0,0 +1,17 @@
+export class WiredTriggerLayout
+{
+ public static AVATAR_SAYS_SOMETHING: number = 0;
+ public static AVATAR_WALKS_ON_FURNI: number = 1;
+ public static AVATAR_WALKS_OFF_FURNI: number = 2;
+ public static EXECUTE_ONCE: number = 3;
+ public static TOGGLE_FURNI: number = 4;
+ public static EXECUTE_PERIODICALLY: number = 6;
+ public static AVATAR_ENTERS_ROOM: number = 7;
+ public static GAME_STARTS: number = 8;
+ public static GAME_ENDS: number = 9;
+ public static SCORE_ACHIEVED: number = 10;
+ public static COLLISION: number = 11;
+ public static EXECUTE_PERIODICALLY_LONG: number = 12;
+ public static BOT_REACHED_STUFF: number = 13;
+ public static BOT_REACHED_AVATAR: number = 14;
+}
diff --git a/Coolui v3 test/src/api/wired/index.ts b/Coolui v3 test/src/api/wired/index.ts
new file mode 100644
index 0000000000..6590adfe21
--- /dev/null
+++ b/Coolui v3 test/src/api/wired/index.ts
@@ -0,0 +1,8 @@
+export * from './GetWiredTimeLocale';
+export * from './WiredActionLayoutCode';
+export * from './WiredConditionLayoutCode';
+export * from './WiredDateToString';
+export * from './WiredFurniType';
+export * from './WiredSelectionVisualizer';
+export * from './WiredStringDelimeter';
+export * from './WiredTriggerLayoutCode';
diff --git a/Coolui v3 test/src/assets/images/achievements/back-arrow.png b/Coolui v3 test/src/assets/images/achievements/back-arrow.png
new file mode 100644
index 0000000000..e795c0ee17
Binary files /dev/null and b/Coolui v3 test/src/assets/images/achievements/back-arrow.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/arrow-left-icon.png b/Coolui v3 test/src/assets/images/avatareditor/arrow-left-icon.png
new file mode 100644
index 0000000000..f94a7dfdd7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/arrow-left-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/arrow-right-icon.png b/Coolui v3 test/src/assets/images/avatareditor/arrow-right-icon.png
new file mode 100644
index 0000000000..1d2217fb27
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/arrow-right-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/avatar-editor-spritesheet.png b/Coolui v3 test/src/assets/images/avatareditor/avatar-editor-spritesheet.png
new file mode 100644
index 0000000000..0c91ca0ffc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/avatar-editor-spritesheet.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/ca-icon.png b/Coolui v3 test/src/assets/images/avatareditor/ca-icon.png
new file mode 100644
index 0000000000..c9803b9505
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/ca-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/ca-selected-icon.png b/Coolui v3 test/src/assets/images/avatareditor/ca-selected-icon.png
new file mode 100644
index 0000000000..b118c3ed60
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/ca-selected-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/cc-icon.png b/Coolui v3 test/src/assets/images/avatareditor/cc-icon.png
new file mode 100644
index 0000000000..4a8844e49c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/cc-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/cc-selected-icon.png b/Coolui v3 test/src/assets/images/avatareditor/cc-selected-icon.png
new file mode 100644
index 0000000000..3493751af1
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/cc-selected-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/ch-icon.png b/Coolui v3 test/src/assets/images/avatareditor/ch-icon.png
new file mode 100644
index 0000000000..ef7da1131b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/ch-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/ch-selected-icon.png b/Coolui v3 test/src/assets/images/avatareditor/ch-selected-icon.png
new file mode 100644
index 0000000000..c5e9f3402c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/ch-selected-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/clear-icon.png b/Coolui v3 test/src/assets/images/avatareditor/clear-icon.png
new file mode 100644
index 0000000000..e0d50abca8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/clear-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/cp-icon.png b/Coolui v3 test/src/assets/images/avatareditor/cp-icon.png
new file mode 100644
index 0000000000..5e460f153c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/cp-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/cp-selected-icon.png b/Coolui v3 test/src/assets/images/avatareditor/cp-selected-icon.png
new file mode 100644
index 0000000000..a067085d27
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/cp-selected-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/ea-icon.png b/Coolui v3 test/src/assets/images/avatareditor/ea-icon.png
new file mode 100644
index 0000000000..c227ae1071
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/ea-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/ea-selected-icon.png b/Coolui v3 test/src/assets/images/avatareditor/ea-selected-icon.png
new file mode 100644
index 0000000000..e7678c4a6e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/ea-selected-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/fa-icon.png b/Coolui v3 test/src/assets/images/avatareditor/fa-icon.png
new file mode 100644
index 0000000000..9b72ff5e38
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/fa-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/fa-selected-icon.png b/Coolui v3 test/src/assets/images/avatareditor/fa-selected-icon.png
new file mode 100644
index 0000000000..a1d26b61c5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/fa-selected-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/female-icon.png b/Coolui v3 test/src/assets/images/avatareditor/female-icon.png
new file mode 100644
index 0000000000..8e6e8202c3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/female-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/female-selected-icon.png b/Coolui v3 test/src/assets/images/avatareditor/female-selected-icon.png
new file mode 100644
index 0000000000..50ffde0999
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/female-selected-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/ha-icon.png b/Coolui v3 test/src/assets/images/avatareditor/ha-icon.png
new file mode 100644
index 0000000000..f0a819181a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/ha-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/ha-selected-icon.png b/Coolui v3 test/src/assets/images/avatareditor/ha-selected-icon.png
new file mode 100644
index 0000000000..4c81ece520
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/ha-selected-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/he-icon.png b/Coolui v3 test/src/assets/images/avatareditor/he-icon.png
new file mode 100644
index 0000000000..7cf6dc4c73
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/he-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/he-selected-icon.png b/Coolui v3 test/src/assets/images/avatareditor/he-selected-icon.png
new file mode 100644
index 0000000000..32633559b9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/he-selected-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/hr-icon.png b/Coolui v3 test/src/assets/images/avatareditor/hr-icon.png
new file mode 100644
index 0000000000..de299902af
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/hr-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/hr-selected-icon.png b/Coolui v3 test/src/assets/images/avatareditor/hr-selected-icon.png
new file mode 100644
index 0000000000..c694b82a50
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/hr-selected-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/lg-icon.png b/Coolui v3 test/src/assets/images/avatareditor/lg-icon.png
new file mode 100644
index 0000000000..0bdd750e4e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/lg-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/lg-selected-icon.png b/Coolui v3 test/src/assets/images/avatareditor/lg-selected-icon.png
new file mode 100644
index 0000000000..7a2853b027
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/lg-selected-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/loading-icon.png b/Coolui v3 test/src/assets/images/avatareditor/loading-icon.png
new file mode 100644
index 0000000000..50d132b3e4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/loading-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/male-icon.png b/Coolui v3 test/src/assets/images/avatareditor/male-icon.png
new file mode 100644
index 0000000000..95a1b35267
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/male-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/male-selected-icon.png b/Coolui v3 test/src/assets/images/avatareditor/male-selected-icon.png
new file mode 100644
index 0000000000..85debbb3ef
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/male-selected-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/sellable-icon.png b/Coolui v3 test/src/assets/images/avatareditor/sellable-icon.png
new file mode 100644
index 0000000000..4485b518d6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/sellable-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/sh-icon.png b/Coolui v3 test/src/assets/images/avatareditor/sh-icon.png
new file mode 100644
index 0000000000..915c7c1e8d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/sh-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/sh-selected-icon.png b/Coolui v3 test/src/assets/images/avatareditor/sh-selected-icon.png
new file mode 100644
index 0000000000..12c6deb1fb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/sh-selected-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/spotlight-icon.png b/Coolui v3 test/src/assets/images/avatareditor/spotlight-icon.png
new file mode 100644
index 0000000000..8755373c50
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/spotlight-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/wa-icon.png b/Coolui v3 test/src/assets/images/avatareditor/wa-icon.png
new file mode 100644
index 0000000000..8a73b7aef1
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/wa-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/avatareditor/wa-selected-icon.png b/Coolui v3 test/src/assets/images/avatareditor/wa-selected-icon.png
new file mode 100644
index 0000000000..5348be318f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/avatareditor/wa-selected-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_0.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_0.png
new file mode 100644
index 0000000000..dc80b28b07
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_0.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_1.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_1.gif
new file mode 100644
index 0000000000..5a07f4f828
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_1.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_1.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_1.png
new file mode 100644
index 0000000000..5b2fc5a87c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_1.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_10.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_10.png
new file mode 100644
index 0000000000..80764d4557
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_10.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_100.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_100.gif
new file mode 100644
index 0000000000..8ad3ab7ade
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_100.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_101.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_101.png
new file mode 100644
index 0000000000..22d8307160
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_101.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_102.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_102.gif
new file mode 100644
index 0000000000..e5ee68a7cc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_102.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_103.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_103.gif
new file mode 100644
index 0000000000..d0645c1bd6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_103.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_104.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_104.gif
new file mode 100644
index 0000000000..8198d2feb8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_104.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_105.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_105.gif
new file mode 100644
index 0000000000..508d38d300
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_105.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_106.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_106.gif
new file mode 100644
index 0000000000..1a2f8aba4b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_106.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_107.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_107.gif
new file mode 100644
index 0000000000..2f24a27cfb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_107.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_108.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_108.gif
new file mode 100644
index 0000000000..b996f344a0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_108.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_109.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_109.gif
new file mode 100644
index 0000000000..23db9e6907
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_109.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_11.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_11.png
new file mode 100644
index 0000000000..ebcc2a7fc6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_11.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_110.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_110.gif
new file mode 100644
index 0000000000..fed52bbc76
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_110.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_111.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_111.gif
new file mode 100644
index 0000000000..fa94d517c6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_111.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_112.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_112.gif
new file mode 100644
index 0000000000..974a1ff0ac
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_112.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_113.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_113.gif
new file mode 100644
index 0000000000..f63d02949e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_113.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_114.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_114.gif
new file mode 100644
index 0000000000..024c9b4283
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_114.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_115.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_115.gif
new file mode 100644
index 0000000000..6f0cb29f7a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_115.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_116.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_116.gif
new file mode 100644
index 0000000000..02b464e9bf
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_116.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_117.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_117.gif
new file mode 100644
index 0000000000..2c6a4a696f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_117.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_118.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_118.gif
new file mode 100644
index 0000000000..5b7d61fea9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_118.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_119.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_119.gif
new file mode 100644
index 0000000000..52c4a2ccbd
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_119.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_12.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_12.png
new file mode 100644
index 0000000000..6876efdd26
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_12.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_120.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_120.gif
new file mode 100644
index 0000000000..4d2a314dd9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_120.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_121.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_121.gif
new file mode 100644
index 0000000000..f5bc596931
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_121.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_122.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_122.gif
new file mode 100644
index 0000000000..a625ecce1d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_122.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_123.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_123.gif
new file mode 100644
index 0000000000..adda1dca59
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_123.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_124.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_124.gif
new file mode 100644
index 0000000000..551af328b5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_124.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_125.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_125.gif
new file mode 100644
index 0000000000..629010b56c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_125.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_126.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_126.gif
new file mode 100644
index 0000000000..c799c79030
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_126.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_127.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_127.gif
new file mode 100644
index 0000000000..885ce780a5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_127.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_128.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_128.gif
new file mode 100644
index 0000000000..7603a4a4bc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_128.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_129.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_129.gif
new file mode 100644
index 0000000000..cd7c75ce92
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_129.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_13.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_13.png
new file mode 100644
index 0000000000..d57057e9c1
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_13.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_130.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_130.gif
new file mode 100644
index 0000000000..54e01ceb94
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_130.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_131.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_131.gif
new file mode 100644
index 0000000000..19102c8f3b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_131.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_132.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_132.gif
new file mode 100644
index 0000000000..492c05defa
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_132.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_133.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_133.gif
new file mode 100644
index 0000000000..5a033102bc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_133.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_134.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_134.gif
new file mode 100644
index 0000000000..13a07114d4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_134.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_135.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_135.gif
new file mode 100644
index 0000000000..2f24a27cfb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_135.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_136.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_136.gif
new file mode 100644
index 0000000000..3d4d8f7104
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_136.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_137.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_137.gif
new file mode 100644
index 0000000000..80cbbebc82
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_137.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_138.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_138.gif
new file mode 100644
index 0000000000..0c7b67fb15
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_138.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_139.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_139.gif
new file mode 100644
index 0000000000..6c76b2f224
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_139.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_14.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_14.png
new file mode 100644
index 0000000000..5e8debc4c3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_14.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_140.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_140.gif
new file mode 100644
index 0000000000..bce3709758
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_140.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_141.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_141.gif
new file mode 100644
index 0000000000..05a7ad9efe
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_141.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_142.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_142.gif
new file mode 100644
index 0000000000..aeef99c2e8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_142.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_143.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_143.gif
new file mode 100644
index 0000000000..5c1e597dc1
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_143.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_144.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_144.gif
new file mode 100644
index 0000000000..a56328395a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_144.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_145.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_145.gif
new file mode 100644
index 0000000000..2b6e9c1fd6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_145.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_146.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_146.gif
new file mode 100644
index 0000000000..ccc026484a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_146.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_147.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_147.gif
new file mode 100644
index 0000000000..9ea76f7b57
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_147.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_148.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_148.gif
new file mode 100644
index 0000000000..2974315ed5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_148.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_149.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_149.gif
new file mode 100644
index 0000000000..7520899b0d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_149.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_15.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_15.png
new file mode 100644
index 0000000000..a9ec7693ea
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_15.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_150.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_150.gif
new file mode 100644
index 0000000000..f0f50fade7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_150.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_151.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_151.gif
new file mode 100644
index 0000000000..77c150f970
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_151.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_152.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_152.gif
new file mode 100644
index 0000000000..1ded113e8e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_152.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_153.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_153.gif
new file mode 100644
index 0000000000..19b9ecb7cd
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_153.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_154.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_154.gif
new file mode 100644
index 0000000000..38338a0304
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_154.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_155.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_155.gif
new file mode 100644
index 0000000000..8770d24919
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_155.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_156.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_156.gif
new file mode 100644
index 0000000000..614d7b23b3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_156.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_157.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_157.gif
new file mode 100644
index 0000000000..707a559b80
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_157.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_158.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_158.gif
new file mode 100644
index 0000000000..ea576caa90
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_158.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_159.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_159.gif
new file mode 100644
index 0000000000..d7b28fb4b6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_159.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_16.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_16.png
new file mode 100644
index 0000000000..0afb1ed877
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_16.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_160.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_160.gif
new file mode 100644
index 0000000000..d9824e8a1a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_160.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_161.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_161.gif
new file mode 100644
index 0000000000..91b9518ecb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_161.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_162.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_162.gif
new file mode 100644
index 0000000000..6029e0e96b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_162.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_163.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_163.gif
new file mode 100644
index 0000000000..8c597811b1
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_163.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_164.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_164.gif
new file mode 100644
index 0000000000..b6804ab94f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_164.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_165.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_165.gif
new file mode 100644
index 0000000000..d01da99f5d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_165.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_166.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_166.gif
new file mode 100644
index 0000000000..ce7f41563b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_166.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_167.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_167.gif
new file mode 100644
index 0000000000..806869673a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_167.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_168.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_168.gif
new file mode 100644
index 0000000000..5de9226da4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_168.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_169.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_169.gif
new file mode 100644
index 0000000000..f344b74708
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_169.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_17.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_17.png
new file mode 100644
index 0000000000..3d593e4825
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_17.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_170.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_170.png
new file mode 100644
index 0000000000..1880697b26
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_170.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_171.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_171.png
new file mode 100644
index 0000000000..f1c1767fcc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_171.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_172.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_172.png
new file mode 100644
index 0000000000..6de0de046c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_172.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_173.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_173.png
new file mode 100644
index 0000000000..ee36cc76df
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_173.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_174.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_174.png
new file mode 100644
index 0000000000..920270b235
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_174.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_175.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_175.png
new file mode 100644
index 0000000000..5f2305ca30
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_175.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_176.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_176.png
new file mode 100644
index 0000000000..f66effc6e9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_176.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_177.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_177.gif
new file mode 100644
index 0000000000..7801fe0c04
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_177.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_178.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_178.png
new file mode 100644
index 0000000000..287d1ed8c3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_178.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_179.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_179.png
new file mode 100644
index 0000000000..39266b34cc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_179.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_18.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_18.png
new file mode 100644
index 0000000000..81bab2873d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_18.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_180.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_180.png
new file mode 100644
index 0000000000..4e1bb7aa4d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_180.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_181.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_181.png
new file mode 100644
index 0000000000..62c6dd2710
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_181.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_182.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_182.png
new file mode 100644
index 0000000000..81613a1d7d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_182.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_183.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_183.png
new file mode 100644
index 0000000000..1d839eb0e3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_183.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_184.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_184.png
new file mode 100644
index 0000000000..dd5388797f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_184.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_185.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_185.png
new file mode 100644
index 0000000000..669819e2f0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_185.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_186.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_186.png
new file mode 100644
index 0000000000..82709ad377
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_186.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_187.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_187.gif
new file mode 100644
index 0000000000..f782ce7907
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_187.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_19.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_19.png
new file mode 100644
index 0000000000..10bb78c01d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_19.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_2.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_2.png
new file mode 100644
index 0000000000..5b2fc5a87c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_2.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_20.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_20.png
new file mode 100644
index 0000000000..a60ef76da3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_20.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_21.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_21.png
new file mode 100644
index 0000000000..8029ae00b5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_21.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_22.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_22.png
new file mode 100644
index 0000000000..07a1915cd7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_22.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_23.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_23.png
new file mode 100644
index 0000000000..86dbad3adf
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_23.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_24.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_24.png
new file mode 100644
index 0000000000..79278d18e4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_24.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_25.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_25.png
new file mode 100644
index 0000000000..d8025a683d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_25.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_26.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_26.png
new file mode 100644
index 0000000000..60e0c3715e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_26.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_27.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_27.png
new file mode 100644
index 0000000000..8e942a3501
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_27.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_28.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_28.png
new file mode 100644
index 0000000000..689c2b9b41
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_28.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_29.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_29.png
new file mode 100644
index 0000000000..3260464c60
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_29.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_3.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_3.png
new file mode 100644
index 0000000000..41034f7d4a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_3.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_30.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_30.png
new file mode 100644
index 0000000000..3db687a20c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_30.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_31.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_31.png
new file mode 100644
index 0000000000..348b000bd5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_31.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_32.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_32.png
new file mode 100644
index 0000000000..7b11ec3f79
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_32.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_33.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_33.png
new file mode 100644
index 0000000000..a5ba7926b2
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_33.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_34.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_34.png
new file mode 100644
index 0000000000..c022119c7c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_34.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_35.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_35.png
new file mode 100644
index 0000000000..bacd72f3e3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_35.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_36.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_36.gif
new file mode 100644
index 0000000000..b30f769d97
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_36.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_36.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_36.png
new file mode 100644
index 0000000000..572e6611c7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_36.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_37.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_37.png
new file mode 100644
index 0000000000..572e6611c7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_37.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_38.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_38.png
new file mode 100644
index 0000000000..e47c7ad660
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_38.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_39.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_39.png
new file mode 100644
index 0000000000..a4f199975c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_39.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_4.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_4.png
new file mode 100644
index 0000000000..b150551b71
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_4.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_40.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_40.png
new file mode 100644
index 0000000000..1af2c2e6bf
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_40.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_41.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_41.png
new file mode 100644
index 0000000000..4201d9e6b4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_41.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_42.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_42.png
new file mode 100644
index 0000000000..61057a3baf
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_42.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_43.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_43.png
new file mode 100644
index 0000000000..51781231ce
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_43.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_44.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_44.png
new file mode 100644
index 0000000000..4f282a3426
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_44.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_45.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_45.png
new file mode 100644
index 0000000000..0cfec58e22
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_45.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_46.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_46.png
new file mode 100644
index 0000000000..0b370708ad
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_46.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_47.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_47.png
new file mode 100644
index 0000000000..c8192c62fe
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_47.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_48.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_48.png
new file mode 100644
index 0000000000..167ff55e18
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_48.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_49.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_49.png
new file mode 100644
index 0000000000..ec8e60f160
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_49.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_5.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_5.png
new file mode 100644
index 0000000000..f3a86fe03a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_5.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_50.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_50.gif
new file mode 100644
index 0000000000..f1e97a4515
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_50.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_50.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_50.png
new file mode 100644
index 0000000000..275494e6cb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_50.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_51.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_51.gif
new file mode 100644
index 0000000000..f1e97a4515
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_51.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_52.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_52.gif
new file mode 100644
index 0000000000..0f0c0719e9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_52.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_52.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_52.png
new file mode 100644
index 0000000000..f0fadde78e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_52.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_53.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_53.gif
new file mode 100644
index 0000000000..fe88155545
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_53.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_53.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_53.png
new file mode 100644
index 0000000000..629010b56c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_53.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_54.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_54.gif
new file mode 100644
index 0000000000..2dab5fe573
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_54.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_55.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_55.gif
new file mode 100644
index 0000000000..a7688a198f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_55.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_56.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_56.gif
new file mode 100644
index 0000000000..c0fc06d8f9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_56.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_57.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_57.gif
new file mode 100644
index 0000000000..2db068f2db
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_57.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_58.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_58.gif
new file mode 100644
index 0000000000..d06f671a26
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_58.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_59.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_59.gif
new file mode 100644
index 0000000000..a103717c7b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_59.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_6.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_6.png
new file mode 100644
index 0000000000..79ecaab5f3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_6.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_60.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_60.gif
new file mode 100644
index 0000000000..9c7ad3822e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_60.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_61.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_61.gif
new file mode 100644
index 0000000000..aa0d872629
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_61.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_62.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_62.gif
new file mode 100644
index 0000000000..b02708ee73
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_62.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_63.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_63.gif
new file mode 100644
index 0000000000..ced6c2bf6b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_63.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_64.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_64.gif
new file mode 100644
index 0000000000..c0514928c0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_64.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_65.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_65.gif
new file mode 100644
index 0000000000..b48ed88eda
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_65.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_66.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_66.gif
new file mode 100644
index 0000000000..45860c15d8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_66.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_67.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_67.gif
new file mode 100644
index 0000000000..613512a15a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_67.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_68.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_68.gif
new file mode 100644
index 0000000000..221547af10
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_68.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_69.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_69.gif
new file mode 100644
index 0000000000..e23d28217c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_69.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_7.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_7.png
new file mode 100644
index 0000000000..7e61d10c34
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_7.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_70.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_70.gif
new file mode 100644
index 0000000000..8f626fa192
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_70.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_71.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_71.gif
new file mode 100644
index 0000000000..083417c7ee
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_71.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_72.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_72.gif
new file mode 100644
index 0000000000..af10f9bf2e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_72.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_73.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_73.gif
new file mode 100644
index 0000000000..9293170fa9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_73.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_74.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_74.gif
new file mode 100644
index 0000000000..a3c49ad645
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_74.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_75.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_75.gif
new file mode 100644
index 0000000000..82709ad377
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_75.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_76.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_76.gif
new file mode 100644
index 0000000000..bd9b9cd513
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_76.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_77.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_77.gif
new file mode 100644
index 0000000000..14b2dc018c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_77.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_78.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_78.gif
new file mode 100644
index 0000000000..66288b3138
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_78.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_79.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_79.gif
new file mode 100644
index 0000000000..82709ad377
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_79.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_8.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_8.png
new file mode 100644
index 0000000000..89a49bb2a3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_8.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_80.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_80.gif
new file mode 100644
index 0000000000..c1d6fa3072
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_80.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_81.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_81.gif
new file mode 100644
index 0000000000..899e77c8d3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_81.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_82.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_82.gif
new file mode 100644
index 0000000000..833069627b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_82.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_83.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_83.gif
new file mode 100644
index 0000000000..2cf235568b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_83.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_84.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_84.gif
new file mode 100644
index 0000000000..27282b7795
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_84.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_85.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_85.gif
new file mode 100644
index 0000000000..7862b23df7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_85.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_86.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_86.png
new file mode 100644
index 0000000000..16ce06d4ed
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_86.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_87.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_87.gif
new file mode 100644
index 0000000000..3833cae5d7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_87.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_88.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_88.gif
new file mode 100644
index 0000000000..ac1004b200
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_88.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_89.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_89.gif
new file mode 100644
index 0000000000..071b4ac666
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_89.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_9.png b/Coolui v3 test/src/assets/images/backgrounds/background/bg_9.png
new file mode 100644
index 0000000000..f48f4a4dcc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_9.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_90.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_90.gif
new file mode 100644
index 0000000000..5babf7434e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_90.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_91.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_91.gif
new file mode 100644
index 0000000000..cae21c57e2
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_91.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_92.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_92.gif
new file mode 100644
index 0000000000..1ca656ef71
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_92.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_93.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_93.gif
new file mode 100644
index 0000000000..ee7848789e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_93.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_94.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_94.gif
new file mode 100644
index 0000000000..f2ebe51ce3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_94.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_95.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_95.gif
new file mode 100644
index 0000000000..568187f6d7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_95.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_96.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_96.gif
new file mode 100644
index 0000000000..8dfd887816
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_96.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_97.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_97.gif
new file mode 100644
index 0000000000..62b8504b1e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_97.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_98.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_98.gif
new file mode 100644
index 0000000000..7f5ab864e0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_98.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/background/bg_99.gif b/Coolui v3 test/src/assets/images/backgrounds/background/bg_99.gif
new file mode 100644
index 0000000000..327a73bace
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/background/bg_99.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_0.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_0.png
new file mode 100644
index 0000000000..dc80b28b07
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_0.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_1.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_1.gif
new file mode 100644
index 0000000000..5a07f4f828
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_1.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_10.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_10.png
new file mode 100644
index 0000000000..80764d4557
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_10.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_100.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_100.gif
new file mode 100644
index 0000000000..8ad3ab7ade
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_100.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_102.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_102.gif
new file mode 100644
index 0000000000..e5ee68a7cc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_102.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_103.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_103.gif
new file mode 100644
index 0000000000..d0645c1bd6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_103.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_104.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_104.gif
new file mode 100644
index 0000000000..8198d2feb8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_104.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_105.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_105.gif
new file mode 100644
index 0000000000..508d38d300
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_105.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_106.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_106.gif
new file mode 100644
index 0000000000..1a2f8aba4b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_106.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_107.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_107.gif
new file mode 100644
index 0000000000..2f24a27cfb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_107.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_108.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_108.gif
new file mode 100644
index 0000000000..b996f344a0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_108.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_109.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_109.gif
new file mode 100644
index 0000000000..23db9e6907
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_109.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_11.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_11.png
new file mode 100644
index 0000000000..ebcc2a7fc6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_11.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_110.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_110.gif
new file mode 100644
index 0000000000..fed52bbc76
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_110.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_111.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_111.gif
new file mode 100644
index 0000000000..fa94d517c6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_111.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_112.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_112.gif
new file mode 100644
index 0000000000..974a1ff0ac
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_112.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_113.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_113.gif
new file mode 100644
index 0000000000..f63d02949e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_113.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_114.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_114.gif
new file mode 100644
index 0000000000..024c9b4283
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_114.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_115.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_115.gif
new file mode 100644
index 0000000000..6f0cb29f7a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_115.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_116.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_116.gif
new file mode 100644
index 0000000000..02b464e9bf
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_116.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_117.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_117.gif
new file mode 100644
index 0000000000..2c6a4a696f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_117.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_118.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_118.gif
new file mode 100644
index 0000000000..5b7d61fea9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_118.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_119.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_119.gif
new file mode 100644
index 0000000000..52c4a2ccbd
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_119.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_12.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_12.png
new file mode 100644
index 0000000000..6876efdd26
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_12.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_120.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_120.gif
new file mode 100644
index 0000000000..4d2a314dd9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_120.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_121.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_121.gif
new file mode 100644
index 0000000000..f5bc596931
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_121.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_122.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_122.gif
new file mode 100644
index 0000000000..a625ecce1d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_122.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_123.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_123.gif
new file mode 100644
index 0000000000..adda1dca59
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_123.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_124.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_124.gif
new file mode 100644
index 0000000000..551af328b5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_124.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_125.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_125.gif
new file mode 100644
index 0000000000..629010b56c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_125.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_126.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_126.gif
new file mode 100644
index 0000000000..c799c79030
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_126.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_127.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_127.gif
new file mode 100644
index 0000000000..885ce780a5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_127.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_128.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_128.gif
new file mode 100644
index 0000000000..7603a4a4bc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_128.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_129.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_129.gif
new file mode 100644
index 0000000000..cd7c75ce92
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_129.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_13.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_13.png
new file mode 100644
index 0000000000..d57057e9c1
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_13.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_130.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_130.gif
new file mode 100644
index 0000000000..54e01ceb94
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_130.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_131.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_131.gif
new file mode 100644
index 0000000000..19102c8f3b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_131.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_132.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_132.gif
new file mode 100644
index 0000000000..492c05defa
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_132.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_133.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_133.gif
new file mode 100644
index 0000000000..5a033102bc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_133.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_134.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_134.gif
new file mode 100644
index 0000000000..13a07114d4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_134.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_135.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_135.gif
new file mode 100644
index 0000000000..2f24a27cfb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_135.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_136.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_136.gif
new file mode 100644
index 0000000000..3d4d8f7104
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_136.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_137.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_137.gif
new file mode 100644
index 0000000000..80cbbebc82
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_137.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_138.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_138.gif
new file mode 100644
index 0000000000..0c7b67fb15
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_138.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_139.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_139.gif
new file mode 100644
index 0000000000..6c76b2f224
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_139.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_14.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_14.png
new file mode 100644
index 0000000000..5e8debc4c3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_14.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_140.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_140.gif
new file mode 100644
index 0000000000..bce3709758
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_140.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_141.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_141.gif
new file mode 100644
index 0000000000..05a7ad9efe
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_141.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_142.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_142.gif
new file mode 100644
index 0000000000..aeef99c2e8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_142.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_143.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_143.gif
new file mode 100644
index 0000000000..5c1e597dc1
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_143.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_144.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_144.gif
new file mode 100644
index 0000000000..a56328395a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_144.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_145.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_145.gif
new file mode 100644
index 0000000000..2b6e9c1fd6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_145.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_146.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_146.gif
new file mode 100644
index 0000000000..ccc026484a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_146.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_147.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_147.gif
new file mode 100644
index 0000000000..9ea76f7b57
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_147.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_148.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_148.gif
new file mode 100644
index 0000000000..2974315ed5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_148.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_149.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_149.gif
new file mode 100644
index 0000000000..7520899b0d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_149.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_15.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_15.png
new file mode 100644
index 0000000000..a9ec7693ea
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_15.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_150.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_150.gif
new file mode 100644
index 0000000000..f0f50fade7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_150.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_151.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_151.gif
new file mode 100644
index 0000000000..77c150f970
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_151.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_152.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_152.gif
new file mode 100644
index 0000000000..1ded113e8e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_152.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_153.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_153.gif
new file mode 100644
index 0000000000..19b9ecb7cd
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_153.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_154.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_154.gif
new file mode 100644
index 0000000000..38338a0304
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_154.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_155.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_155.gif
new file mode 100644
index 0000000000..8770d24919
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_155.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_156.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_156.gif
new file mode 100644
index 0000000000..614d7b23b3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_156.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_157.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_157.gif
new file mode 100644
index 0000000000..707a559b80
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_157.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_158.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_158.gif
new file mode 100644
index 0000000000..ea576caa90
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_158.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_159.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_159.gif
new file mode 100644
index 0000000000..d7b28fb4b6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_159.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_16.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_16.png
new file mode 100644
index 0000000000..0afb1ed877
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_16.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_160.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_160.gif
new file mode 100644
index 0000000000..d9824e8a1a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_160.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_161.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_161.gif
new file mode 100644
index 0000000000..91b9518ecb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_161.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_162.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_162.gif
new file mode 100644
index 0000000000..6029e0e96b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_162.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_163.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_163.gif
new file mode 100644
index 0000000000..8c597811b1
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_163.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_164.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_164.gif
new file mode 100644
index 0000000000..b6804ab94f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_164.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_165.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_165.gif
new file mode 100644
index 0000000000..d01da99f5d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_165.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_166.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_166.gif
new file mode 100644
index 0000000000..ce7f41563b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_166.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_167.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_167.gif
new file mode 100644
index 0000000000..806869673a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_167.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_168.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_168.gif
new file mode 100644
index 0000000000..5de9226da4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_168.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_169.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_169.gif
new file mode 100644
index 0000000000..f344b74708
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_169.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_17.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_17.png
new file mode 100644
index 0000000000..3d593e4825
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_17.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_170.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_170.png
new file mode 100644
index 0000000000..1880697b26
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_170.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_171.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_171.png
new file mode 100644
index 0000000000..f1c1767fcc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_171.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_172.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_172.png
new file mode 100644
index 0000000000..6de0de046c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_172.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_173.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_173.png
new file mode 100644
index 0000000000..ee36cc76df
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_173.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_174.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_174.png
new file mode 100644
index 0000000000..920270b235
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_174.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_175.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_175.png
new file mode 100644
index 0000000000..5f2305ca30
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_175.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_176.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_176.png
new file mode 100644
index 0000000000..f66effc6e9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_176.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_178.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_178.png
new file mode 100644
index 0000000000..287d1ed8c3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_178.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_179.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_179.png
new file mode 100644
index 0000000000..39266b34cc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_179.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_18.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_18.png
new file mode 100644
index 0000000000..81bab2873d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_18.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_180.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_180.png
new file mode 100644
index 0000000000..4e1bb7aa4d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_180.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_181.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_181.png
new file mode 100644
index 0000000000..62c6dd2710
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_181.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_182.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_182.png
new file mode 100644
index 0000000000..81613a1d7d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_182.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_183.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_183.png
new file mode 100644
index 0000000000..1d839eb0e3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_183.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_184.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_184.png
new file mode 100644
index 0000000000..dd5388797f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_184.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_185.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_185.png
new file mode 100644
index 0000000000..669819e2f0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_185.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_186.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_186.png
new file mode 100644
index 0000000000..82709ad377
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_186.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_187.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_187.gif
new file mode 100644
index 0000000000..f782ce7907
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_187.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_188.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_188.gif
new file mode 100644
index 0000000000..8d5e2a275f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_188.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_19.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_19.png
new file mode 100644
index 0000000000..10bb78c01d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_19.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_2.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_2.png
new file mode 100644
index 0000000000..5b2fc5a87c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_2.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_20.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_20.png
new file mode 100644
index 0000000000..a60ef76da3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_20.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_21.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_21.png
new file mode 100644
index 0000000000..8029ae00b5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_21.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_22.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_22.png
new file mode 100644
index 0000000000..07a1915cd7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_22.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_23.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_23.png
new file mode 100644
index 0000000000..86dbad3adf
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_23.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_24.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_24.png
new file mode 100644
index 0000000000..79278d18e4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_24.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_25.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_25.png
new file mode 100644
index 0000000000..d8025a683d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_25.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_26.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_26.png
new file mode 100644
index 0000000000..60e0c3715e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_26.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_27.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_27.png
new file mode 100644
index 0000000000..8e942a3501
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_27.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_28.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_28.png
new file mode 100644
index 0000000000..689c2b9b41
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_28.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_29.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_29.png
new file mode 100644
index 0000000000..3260464c60
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_29.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_3.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_3.png
new file mode 100644
index 0000000000..41034f7d4a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_3.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_30.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_30.png
new file mode 100644
index 0000000000..3db687a20c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_30.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_31.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_31.png
new file mode 100644
index 0000000000..348b000bd5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_31.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_32.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_32.png
new file mode 100644
index 0000000000..7b11ec3f79
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_32.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_33.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_33.png
new file mode 100644
index 0000000000..a5ba7926b2
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_33.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_34.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_34.png
new file mode 100644
index 0000000000..c022119c7c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_34.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_35.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_35.png
new file mode 100644
index 0000000000..bacd72f3e3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_35.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_36.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_36.gif
new file mode 100644
index 0000000000..b30f769d97
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_36.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_37.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_37.png
new file mode 100644
index 0000000000..572e6611c7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_37.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_38.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_38.png
new file mode 100644
index 0000000000..e47c7ad660
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_38.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_39.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_39.png
new file mode 100644
index 0000000000..a4f199975c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_39.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_4.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_4.png
new file mode 100644
index 0000000000..b150551b71
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_4.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_40.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_40.png
new file mode 100644
index 0000000000..1af2c2e6bf
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_40.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_41.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_41.png
new file mode 100644
index 0000000000..4201d9e6b4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_41.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_42.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_42.png
new file mode 100644
index 0000000000..61057a3baf
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_42.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_43.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_43.png
new file mode 100644
index 0000000000..51781231ce
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_43.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_44.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_44.png
new file mode 100644
index 0000000000..4f282a3426
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_44.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_45.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_45.png
new file mode 100644
index 0000000000..0cfec58e22
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_45.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_46.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_46.png
new file mode 100644
index 0000000000..0b370708ad
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_46.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_47.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_47.png
new file mode 100644
index 0000000000..c8192c62fe
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_47.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_48.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_48.png
new file mode 100644
index 0000000000..167ff55e18
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_48.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_49.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_49.png
new file mode 100644
index 0000000000..ec8e60f160
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_49.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_5.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_5.png
new file mode 100644
index 0000000000..f3a86fe03a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_5.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_50.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_50.png
new file mode 100644
index 0000000000..275494e6cb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_50.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_51.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_51.gif
new file mode 100644
index 0000000000..f1e97a4515
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_51.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_52.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_52.gif
new file mode 100644
index 0000000000..0f0c0719e9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_52.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_53.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_53.gif
new file mode 100644
index 0000000000..fe88155545
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_53.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_54.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_54.gif
new file mode 100644
index 0000000000..2dab5fe573
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_54.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_55.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_55.gif
new file mode 100644
index 0000000000..a7688a198f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_55.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_56.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_56.gif
new file mode 100644
index 0000000000..c0fc06d8f9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_56.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_57.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_57.gif
new file mode 100644
index 0000000000..2db068f2db
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_57.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_58.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_58.gif
new file mode 100644
index 0000000000..d06f671a26
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_58.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_59.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_59.gif
new file mode 100644
index 0000000000..a103717c7b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_59.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_6.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_6.png
new file mode 100644
index 0000000000..79ecaab5f3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_6.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_60.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_60.gif
new file mode 100644
index 0000000000..9c7ad3822e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_60.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_61.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_61.gif
new file mode 100644
index 0000000000..aa0d872629
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_61.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_62.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_62.gif
new file mode 100644
index 0000000000..b02708ee73
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_62.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_63.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_63.gif
new file mode 100644
index 0000000000..ced6c2bf6b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_63.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_64.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_64.gif
new file mode 100644
index 0000000000..c0514928c0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_64.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_65.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_65.gif
new file mode 100644
index 0000000000..b48ed88eda
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_65.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_66.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_66.gif
new file mode 100644
index 0000000000..45860c15d8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_66.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_67.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_67.gif
new file mode 100644
index 0000000000..613512a15a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_67.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_68.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_68.gif
new file mode 100644
index 0000000000..221547af10
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_68.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_69.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_69.gif
new file mode 100644
index 0000000000..e23d28217c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_69.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_7.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_7.png
new file mode 100644
index 0000000000..7e61d10c34
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_7.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_70.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_70.gif
new file mode 100644
index 0000000000..8f626fa192
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_70.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_71.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_71.gif
new file mode 100644
index 0000000000..083417c7ee
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_71.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_72.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_72.gif
new file mode 100644
index 0000000000..af10f9bf2e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_72.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_73.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_73.gif
new file mode 100644
index 0000000000..9293170fa9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_73.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_74.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_74.gif
new file mode 100644
index 0000000000..a3c49ad645
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_74.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_75.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_75.gif
new file mode 100644
index 0000000000..82709ad377
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_75.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_76.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_76.gif
new file mode 100644
index 0000000000..bd9b9cd513
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_76.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_77.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_77.gif
new file mode 100644
index 0000000000..14b2dc018c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_77.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_78.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_78.gif
new file mode 100644
index 0000000000..66288b3138
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_78.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_79.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_79.gif
new file mode 100644
index 0000000000..82709ad377
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_79.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_8.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_8.png
new file mode 100644
index 0000000000..89a49bb2a3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_8.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_80.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_80.gif
new file mode 100644
index 0000000000..c1d6fa3072
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_80.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_81.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_81.gif
new file mode 100644
index 0000000000..899e77c8d3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_81.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_82.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_82.gif
new file mode 100644
index 0000000000..833069627b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_82.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_83.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_83.gif
new file mode 100644
index 0000000000..2cf235568b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_83.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_84.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_84.gif
new file mode 100644
index 0000000000..27282b7795
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_84.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_85.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_85.gif
new file mode 100644
index 0000000000..7862b23df7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_85.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_86.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_86.gif
new file mode 100644
index 0000000000..f9f0d0cbd4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_86.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_87.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_87.gif
new file mode 100644
index 0000000000..3833cae5d7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_87.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_88.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_88.gif
new file mode 100644
index 0000000000..ac1004b200
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_88.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_89.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_89.gif
new file mode 100644
index 0000000000..071b4ac666
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_89.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_9.png b/Coolui v3 test/src/assets/images/backgrounds/new/bg_9.png
new file mode 100644
index 0000000000..f48f4a4dcc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_9.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_90.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_90.gif
new file mode 100644
index 0000000000..5babf7434e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_90.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_91.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_91.gif
new file mode 100644
index 0000000000..cae21c57e2
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_91.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_92.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_92.gif
new file mode 100644
index 0000000000..1ca656ef71
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_92.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_93.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_93.gif
new file mode 100644
index 0000000000..ee7848789e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_93.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_94.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_94.gif
new file mode 100644
index 0000000000..f2ebe51ce3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_94.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_95.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_95.gif
new file mode 100644
index 0000000000..568187f6d7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_95.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_96.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_96.gif
new file mode 100644
index 0000000000..8dfd887816
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_96.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_97.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_97.gif
new file mode 100644
index 0000000000..62b8504b1e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_97.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_98.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_98.gif
new file mode 100644
index 0000000000..7f5ab864e0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_98.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/new/bg_99.gif b/Coolui v3 test/src/assets/images/backgrounds/new/bg_99.gif
new file mode 100644
index 0000000000..327a73bace
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/new/bg_99.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_0.png b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_0.png
new file mode 100644
index 0000000000..afd389ab20
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_0.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_1.png b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_1.png
new file mode 100644
index 0000000000..22cadb7c91
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_1.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_2.png b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_2.png
new file mode 100644
index 0000000000..16aee82738
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_2.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_3.png b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_3.png
new file mode 100644
index 0000000000..ca5299231c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_3.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_4.png b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_4.png
new file mode 100644
index 0000000000..b61dc84c40
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_4.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_5.gif b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_5.gif
new file mode 100644
index 0000000000..841a7b63fb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_5.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_6.png b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_6.png
new file mode 100644
index 0000000000..a1f95e77e8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_6.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_7.png b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_7.png
new file mode 100644
index 0000000000..987719ea27
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_7.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_8.png b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_8.png
new file mode 100644
index 0000000000..3c0f341853
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay/overlay_8.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay_0.png b/Coolui v3 test/src/assets/images/backgrounds/overlay_0.png
new file mode 100644
index 0000000000..afd389ab20
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay_0.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay_1.png b/Coolui v3 test/src/assets/images/backgrounds/overlay_1.png
new file mode 100644
index 0000000000..22cadb7c91
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay_1.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay_2.png b/Coolui v3 test/src/assets/images/backgrounds/overlay_2.png
new file mode 100644
index 0000000000..16aee82738
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay_2.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay_3.png b/Coolui v3 test/src/assets/images/backgrounds/overlay_3.png
new file mode 100644
index 0000000000..ca5299231c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay_3.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay_4.png b/Coolui v3 test/src/assets/images/backgrounds/overlay_4.png
new file mode 100644
index 0000000000..b61dc84c40
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay_4.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/overlay_ring.gif b/Coolui v3 test/src/assets/images/backgrounds/overlay_ring.gif
new file mode 100644
index 0000000000..841a7b63fb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/overlay_ring.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_0.png b/Coolui v3 test/src/assets/images/backgrounds/stand_0.png
new file mode 100644
index 0000000000..afd389ab20
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_0.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_1.png b/Coolui v3 test/src/assets/images/backgrounds/stand_1.png
new file mode 100644
index 0000000000..27ebe0f772
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_1.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_10.png b/Coolui v3 test/src/assets/images/backgrounds/stand_10.png
new file mode 100644
index 0000000000..59c2820763
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_10.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_11.png b/Coolui v3 test/src/assets/images/backgrounds/stand_11.png
new file mode 100644
index 0000000000..6974b38adf
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_11.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_12.png b/Coolui v3 test/src/assets/images/backgrounds/stand_12.png
new file mode 100644
index 0000000000..da4cccb2be
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_12.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_13.png b/Coolui v3 test/src/assets/images/backgrounds/stand_13.png
new file mode 100644
index 0000000000..ab4e8be736
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_13.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_14.png b/Coolui v3 test/src/assets/images/backgrounds/stand_14.png
new file mode 100644
index 0000000000..1a60cce5c3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_14.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_15.png b/Coolui v3 test/src/assets/images/backgrounds/stand_15.png
new file mode 100644
index 0000000000..211802b946
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_15.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_16.png b/Coolui v3 test/src/assets/images/backgrounds/stand_16.png
new file mode 100644
index 0000000000..b1e945dd5d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_16.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_17.png b/Coolui v3 test/src/assets/images/backgrounds/stand_17.png
new file mode 100644
index 0000000000..11907635b5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_17.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_18.png b/Coolui v3 test/src/assets/images/backgrounds/stand_18.png
new file mode 100644
index 0000000000..fd5aae57fb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_18.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_19.png b/Coolui v3 test/src/assets/images/backgrounds/stand_19.png
new file mode 100644
index 0000000000..c52280409b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_19.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_2.png b/Coolui v3 test/src/assets/images/backgrounds/stand_2.png
new file mode 100644
index 0000000000..9f789bd900
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_2.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_20.png b/Coolui v3 test/src/assets/images/backgrounds/stand_20.png
new file mode 100644
index 0000000000..695122db2a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_20.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_21.gif b/Coolui v3 test/src/assets/images/backgrounds/stand_21.gif
new file mode 100644
index 0000000000..7449b3959f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_21.gif differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_3.png b/Coolui v3 test/src/assets/images/backgrounds/stand_3.png
new file mode 100644
index 0000000000..800748e7db
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_3.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_4.png b/Coolui v3 test/src/assets/images/backgrounds/stand_4.png
new file mode 100644
index 0000000000..d7042dab6c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_4.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_5.png b/Coolui v3 test/src/assets/images/backgrounds/stand_5.png
new file mode 100644
index 0000000000..0f28a2927b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_5.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_6.png b/Coolui v3 test/src/assets/images/backgrounds/stand_6.png
new file mode 100644
index 0000000000..f20be87447
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_6.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_7.png b/Coolui v3 test/src/assets/images/backgrounds/stand_7.png
new file mode 100644
index 0000000000..9a72c8f863
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_7.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_8.png b/Coolui v3 test/src/assets/images/backgrounds/stand_8.png
new file mode 100644
index 0000000000..b0f8021ac2
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_8.png differ
diff --git a/Coolui v3 test/src/assets/images/backgrounds/stand_9.png b/Coolui v3 test/src/assets/images/backgrounds/stand_9.png
new file mode 100644
index 0000000000..bf42a9424d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/backgrounds/stand_9.png differ
diff --git a/Coolui v3 test/src/assets/images/campaign/available.png b/Coolui v3 test/src/assets/images/campaign/available.png
new file mode 100644
index 0000000000..1cc8fa6219
Binary files /dev/null and b/Coolui v3 test/src/assets/images/campaign/available.png differ
diff --git a/Coolui v3 test/src/assets/images/campaign/campaign_day_generic_bg.png b/Coolui v3 test/src/assets/images/campaign/campaign_day_generic_bg.png
new file mode 100644
index 0000000000..25b3c622dc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/campaign/campaign_day_generic_bg.png differ
diff --git a/Coolui v3 test/src/assets/images/campaign/campaign_opened.png b/Coolui v3 test/src/assets/images/campaign/campaign_opened.png
new file mode 100644
index 0000000000..9ec1bb30be
Binary files /dev/null and b/Coolui v3 test/src/assets/images/campaign/campaign_opened.png differ
diff --git a/Coolui v3 test/src/assets/images/campaign/campaign_spritesheet.png b/Coolui v3 test/src/assets/images/campaign/campaign_spritesheet.png
new file mode 100644
index 0000000000..eeac9d499e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/campaign/campaign_spritesheet.png differ
diff --git a/Coolui v3 test/src/assets/images/campaign/locked.png b/Coolui v3 test/src/assets/images/campaign/locked.png
new file mode 100644
index 0000000000..3805e50234
Binary files /dev/null and b/Coolui v3 test/src/assets/images/campaign/locked.png differ
diff --git a/Coolui v3 test/src/assets/images/campaign/locked_bg.png b/Coolui v3 test/src/assets/images/campaign/locked_bg.png
new file mode 100644
index 0000000000..93927dd5a8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/campaign/locked_bg.png differ
diff --git a/Coolui v3 test/src/assets/images/campaign/next.png b/Coolui v3 test/src/assets/images/campaign/next.png
new file mode 100644
index 0000000000..88a2883a7a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/campaign/next.png differ
diff --git a/Coolui v3 test/src/assets/images/campaign/prev.png b/Coolui v3 test/src/assets/images/campaign/prev.png
new file mode 100644
index 0000000000..914a659212
Binary files /dev/null and b/Coolui v3 test/src/assets/images/campaign/prev.png differ
diff --git a/Coolui v3 test/src/assets/images/campaign/unavailable.png b/Coolui v3 test/src/assets/images/campaign/unavailable.png
new file mode 100644
index 0000000000..dc134c3f12
Binary files /dev/null and b/Coolui v3 test/src/assets/images/campaign/unavailable.png differ
diff --git a/Coolui v3 test/src/assets/images/campaign/unlocked_bg.png b/Coolui v3 test/src/assets/images/campaign/unlocked_bg.png
new file mode 100644
index 0000000000..31c35ba172
Binary files /dev/null and b/Coolui v3 test/src/assets/images/campaign/unlocked_bg.png differ
diff --git a/Coolui v3 test/src/assets/images/catalog/diamond_info_illustration.gif b/Coolui v3 test/src/assets/images/catalog/diamond_info_illustration.gif
new file mode 100644
index 0000000000..d082ef4d7b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/catalog/diamond_info_illustration.gif differ
diff --git a/Coolui v3 test/src/assets/images/catalog/hc_banner_big.png b/Coolui v3 test/src/assets/images/catalog/hc_banner_big.png
new file mode 100644
index 0000000000..4b2c4ec39e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/catalog/hc_banner_big.png differ
diff --git a/Coolui v3 test/src/assets/images/catalog/hc_big.png b/Coolui v3 test/src/assets/images/catalog/hc_big.png
new file mode 100644
index 0000000000..5f8c0a2360
Binary files /dev/null and b/Coolui v3 test/src/assets/images/catalog/hc_big.png differ
diff --git a/Coolui v3 test/src/assets/images/catalog/hc_small.png b/Coolui v3 test/src/assets/images/catalog/hc_small.png
new file mode 100644
index 0000000000..99833309b2
Binary files /dev/null and b/Coolui v3 test/src/assets/images/catalog/hc_small.png differ
diff --git a/Coolui v3 test/src/assets/images/catalog/paint-icon.png b/Coolui v3 test/src/assets/images/catalog/paint-icon.png
new file mode 100644
index 0000000000..f2bf7ea3fa
Binary files /dev/null and b/Coolui v3 test/src/assets/images/catalog/paint-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/catalog/target-price.png b/Coolui v3 test/src/assets/images/catalog/target-price.png
new file mode 100644
index 0000000000..8639afd55a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/catalog/target-price.png differ
diff --git a/Coolui v3 test/src/assets/images/catalog/vip.png b/Coolui v3 test/src/assets/images/catalog/vip.png
new file mode 100644
index 0000000000..9a3aad6d2f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/catalog/vip.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_0.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_0.png
new file mode 100644
index 0000000000..da685365b1
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_0.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_0_1_33_34_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_0_1_33_34_pointer.png
new file mode 100644
index 0000000000..e7a4740b2f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_0_1_33_34_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_0_transparent.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_0_transparent.png
new file mode 100644
index 0000000000..596d2c7c9e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_0_transparent.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_1.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_1.png
new file mode 100644
index 0000000000..492dbde7da
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_1.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_10.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_10.png
new file mode 100644
index 0000000000..cbf25a8346
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_10.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_10_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_10_pointer.png
new file mode 100644
index 0000000000..f7bd858733
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_10_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_11.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_11.png
new file mode 100644
index 0000000000..a8026d6708
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_11.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_11_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_11_pointer.png
new file mode 100644
index 0000000000..d6c4482a78
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_11_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_12.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_12.png
new file mode 100644
index 0000000000..fa73413c7e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_12.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_12_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_12_pointer.png
new file mode 100644
index 0000000000..309a550636
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_12_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_13.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_13.png
new file mode 100644
index 0000000000..a9f2c41b71
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_13.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_13_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_13_pointer.png
new file mode 100644
index 0000000000..65be4f2320
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_13_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_14.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_14.png
new file mode 100644
index 0000000000..54232f244e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_14.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_14_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_14_pointer.png
new file mode 100644
index 0000000000..4975897a17
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_14_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_15.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_15.png
new file mode 100644
index 0000000000..6a7ad1306a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_15.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_15_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_15_pointer.png
new file mode 100644
index 0000000000..b4a69e1d03
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_15_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_16.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_16.png
new file mode 100644
index 0000000000..ae0a23eb5d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_16.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_16_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_16_pointer.png
new file mode 100644
index 0000000000..abb9625e3e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_16_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_17.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_17.png
new file mode 100644
index 0000000000..51930255e8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_17.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_17_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_17_pointer.png
new file mode 100644
index 0000000000..b73b25b6eb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_17_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_18.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_18.png
new file mode 100644
index 0000000000..ad07d05ae9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_18.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_18_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_18_pointer.png
new file mode 100644
index 0000000000..f364525700
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_18_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_19.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_19.png
new file mode 100644
index 0000000000..c0dfb4120d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_19.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_19_20_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_19_20_pointer.png
new file mode 100644
index 0000000000..990c21f908
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_19_20_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_2.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_2.png
new file mode 100644
index 0000000000..dc33507bba
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_2.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_20.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_20.png
new file mode 100644
index 0000000000..127d0f924a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_20.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_21.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_21.png
new file mode 100644
index 0000000000..933daf5afb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_21.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_21_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_21_pointer.png
new file mode 100644
index 0000000000..fe1f3126e8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_21_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_22.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_22.png
new file mode 100644
index 0000000000..a77a733da9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_22.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_22_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_22_pointer.png
new file mode 100644
index 0000000000..855ceffd16
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_22_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_23.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_23.png
new file mode 100644
index 0000000000..d2a8fb4940
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_23.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_23_37_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_23_37_pointer.png
new file mode 100644
index 0000000000..786c84945f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_23_37_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_24.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_24.png
new file mode 100644
index 0000000000..73ee650574
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_24.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_24_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_24_pointer.png
new file mode 100644
index 0000000000..4653eef442
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_24_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_25.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_25.png
new file mode 100644
index 0000000000..60dcaad550
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_25.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_25_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_25_pointer.png
new file mode 100644
index 0000000000..7567395b81
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_25_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_26.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_26.png
new file mode 100644
index 0000000000..0b43dec579
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_26.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_26_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_26_pointer.png
new file mode 100644
index 0000000000..d97093f43e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_26_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_27.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_27.png
new file mode 100644
index 0000000000..57de9a9cee
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_27.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_27_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_27_pointer.png
new file mode 100644
index 0000000000..d0c0cee210
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_27_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_28.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_28.png
new file mode 100644
index 0000000000..3337b797ba
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_28.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_28_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_28_pointer.png
new file mode 100644
index 0000000000..850b99e4cd
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_28_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_29.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_29.png
new file mode 100644
index 0000000000..9eb5aecb27
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_29.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_29_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_29_pointer.png
new file mode 100644
index 0000000000..1462b37ac8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_29_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_2_31_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_2_31_pointer.png
new file mode 100644
index 0000000000..ad9db877d2
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_2_31_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_3.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_3.png
new file mode 100644
index 0000000000..6298809647
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_3.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_30.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_30.png
new file mode 100644
index 0000000000..581fc70923
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_30.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_30_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_30_pointer.png
new file mode 100644
index 0000000000..8660de9cd4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_30_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_32.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_32.png
new file mode 100644
index 0000000000..598d8c8513
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_32.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_32_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_32_pointer.png
new file mode 100644
index 0000000000..a68ddfbe09
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_32_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_33_34.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_33_34.png
new file mode 100644
index 0000000000..d871e1a825
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_33_34.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_33_extra.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_33_extra.png
new file mode 100644
index 0000000000..5b398baa0f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_33_extra.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_34_extra.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_34_extra.png
new file mode 100644
index 0000000000..9a67674fce
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_34_extra.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_35.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_35.png
new file mode 100644
index 0000000000..e4e7ea6535
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_35.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_35_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_35_pointer.png
new file mode 100644
index 0000000000..a8e8c32b3c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_35_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_36.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_36.png
new file mode 100644
index 0000000000..a96e5e0c2b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_36.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_36_extra.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_36_extra.png
new file mode 100644
index 0000000000..8e72fe4473
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_36_extra.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_36_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_36_pointer.png
new file mode 100644
index 0000000000..caa9e3c073
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_36_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_37.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_37.png
new file mode 100644
index 0000000000..43e609e069
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_37.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_38.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_38.png
new file mode 100644
index 0000000000..326cdf489f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_38.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_38_extra.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_38_extra.png
new file mode 100644
index 0000000000..73cfcafb14
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_38_extra.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_38_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_38_pointer.png
new file mode 100644
index 0000000000..402e543ca6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_38_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_3_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_3_pointer.png
new file mode 100644
index 0000000000..55df368254
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_3_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_4.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_4.png
new file mode 100644
index 0000000000..c5f5706127
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_4.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_4_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_4_pointer.png
new file mode 100644
index 0000000000..beb69192f4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_4_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_5.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_5.png
new file mode 100644
index 0000000000..fa33a77638
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_5.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_5_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_5_pointer.png
new file mode 100644
index 0000000000..ce8131a054
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_5_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_6.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_6.png
new file mode 100644
index 0000000000..ad6a0f2e94
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_6.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_6_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_6_pointer.png
new file mode 100644
index 0000000000..8c343f5e6c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_6_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_7.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_7.png
new file mode 100644
index 0000000000..8ced1de793
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_7.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_7_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_7_pointer.png
new file mode 100644
index 0000000000..6c056520de
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_7_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_8.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_8.png
new file mode 100644
index 0000000000..2d370694a4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_8.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_8_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_8_pointer.png
new file mode 100644
index 0000000000..f786e7e182
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_8_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_9.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_9.png
new file mode 100644
index 0000000000..23d572145a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_9.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_9_pointer.png b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_9_pointer.png
new file mode 100644
index 0000000000..9bc5919bc9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chatbubbles/bubble_9_pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/chathistory_background.png b/Coolui v3 test/src/assets/images/chat/chathistory_background.png
new file mode 100644
index 0000000000..301d9512cd
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/chathistory_background.png differ
diff --git a/Coolui v3 test/src/assets/images/chat/styles-icon.png b/Coolui v3 test/src/assets/images/chat/styles-icon.png
new file mode 100644
index 0000000000..74cd9ecda0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/chat/styles-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-0.png b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-0.png
new file mode 100644
index 0000000000..8c272a0a97
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-0.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-1.png b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-1.png
new file mode 100644
index 0000000000..52e488f68b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-1.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-2.png b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-2.png
new file mode 100644
index 0000000000..da1a1cb52a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-2.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-3.png b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-3.png
new file mode 100644
index 0000000000..15712a91ac
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-3.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-4.png b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-4.png
new file mode 100644
index 0000000000..eb1b4b5e0f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-4.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-5.png b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-5.png
new file mode 100644
index 0000000000..46e6f4d9b6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-5.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-6.png b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-6.png
new file mode 100644
index 0000000000..fda613acc0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-6.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-7.png b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-7.png
new file mode 100644
index 0000000000..96fa8e483b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/door-direction-7.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/icon-active-squaresselect.png b/Coolui v3 test/src/assets/images/floorplaneditor/icon-active-squaresselect.png
new file mode 100644
index 0000000000..ea9d9f7e87
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/icon-active-squaresselect.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/icon-deselect.png b/Coolui v3 test/src/assets/images/floorplaneditor/icon-deselect.png
new file mode 100644
index 0000000000..6fa4f742ed
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/icon-deselect.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/icon-door.png b/Coolui v3 test/src/assets/images/floorplaneditor/icon-door.png
new file mode 100644
index 0000000000..1b56bb2b56
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/icon-door.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/icon-select.png b/Coolui v3 test/src/assets/images/floorplaneditor/icon-select.png
new file mode 100644
index 0000000000..dc81c0ed66
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/icon-select.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/icon-squaresselect.png b/Coolui v3 test/src/assets/images/floorplaneditor/icon-squaresselect.png
new file mode 100644
index 0000000000..351098a3a5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/icon-squaresselect.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/icon-tile-down.png b/Coolui v3 test/src/assets/images/floorplaneditor/icon-tile-down.png
new file mode 100644
index 0000000000..352c48dffb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/icon-tile-down.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/icon-tile-set.png b/Coolui v3 test/src/assets/images/floorplaneditor/icon-tile-set.png
new file mode 100644
index 0000000000..eac61532ca
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/icon-tile-set.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/icon-tile-unset.png b/Coolui v3 test/src/assets/images/floorplaneditor/icon-tile-unset.png
new file mode 100644
index 0000000000..3f5e21817a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/icon-tile-unset.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/icon-tile-up.png b/Coolui v3 test/src/assets/images/floorplaneditor/icon-tile-up.png
new file mode 100644
index 0000000000..27153e0ce0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/icon-tile-up.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/preview_tile.png b/Coolui v3 test/src/assets/images/floorplaneditor/preview_tile.png
new file mode 100644
index 0000000000..607f450134
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/preview_tile.png differ
diff --git a/Coolui v3 test/src/assets/images/floorplaneditor/selected_height_icon.png b/Coolui v3 test/src/assets/images/floorplaneditor/selected_height_icon.png
new file mode 100644
index 0000000000..f763fde5aa
Binary files /dev/null and b/Coolui v3 test/src/assets/images/floorplaneditor/selected_height_icon.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/friends-spritesheet.png b/Coolui v3 test/src/assets/images/friends/friends-spritesheet.png
new file mode 100644
index 0000000000..aa72325dbd
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/friends-spritesheet.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-accept.png b/Coolui v3 test/src/assets/images/friends/icon-accept.png
new file mode 100644
index 0000000000..da56941a0d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-accept.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-add.png b/Coolui v3 test/src/assets/images/friends/icon-add.png
new file mode 100644
index 0000000000..d570c8f58f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-add.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-bobba.png b/Coolui v3 test/src/assets/images/friends/icon-bobba.png
new file mode 100644
index 0000000000..eaea89573d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-bobba.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-chat.png b/Coolui v3 test/src/assets/images/friends/icon-chat.png
new file mode 100644
index 0000000000..631b7bc268
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-chat.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-deny.png b/Coolui v3 test/src/assets/images/friends/icon-deny.png
new file mode 100644
index 0000000000..c74e5c067c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-deny.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-follow.png b/Coolui v3 test/src/assets/images/friends/icon-follow.png
new file mode 100644
index 0000000000..9e045168a6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-follow.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-friendbar-chat.png b/Coolui v3 test/src/assets/images/friends/icon-friendbar-chat.png
new file mode 100644
index 0000000000..e0c1645933
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-friendbar-chat.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-friendbar-visit.png b/Coolui v3 test/src/assets/images/friends/icon-friendbar-visit.png
new file mode 100644
index 0000000000..fac57d9a67
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-friendbar-visit.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-heart.png b/Coolui v3 test/src/assets/images/friends/icon-heart.png
new file mode 100644
index 0000000000..5dd1015e96
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-heart.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-new-message.png b/Coolui v3 test/src/assets/images/friends/icon-new-message.png
new file mode 100644
index 0000000000..46d23f5a29
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-new-message.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-none.png b/Coolui v3 test/src/assets/images/friends/icon-none.png
new file mode 100644
index 0000000000..ececd9ee44
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-none.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-profile-sm-hover.png b/Coolui v3 test/src/assets/images/friends/icon-profile-sm-hover.png
new file mode 100644
index 0000000000..f8f42f2bf0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-profile-sm-hover.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-profile-sm.png b/Coolui v3 test/src/assets/images/friends/icon-profile-sm.png
new file mode 100644
index 0000000000..60107e1e19
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-profile-sm.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-profile.png b/Coolui v3 test/src/assets/images/friends/icon-profile.png
new file mode 100644
index 0000000000..2c8ec5c713
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-profile.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-smile.png b/Coolui v3 test/src/assets/images/friends/icon-smile.png
new file mode 100644
index 0000000000..62fcae6ebc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-smile.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/icon-warning.png b/Coolui v3 test/src/assets/images/friends/icon-warning.png
new file mode 100644
index 0000000000..3a3ffcf9ef
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/icon-warning.png differ
diff --git a/Coolui v3 test/src/assets/images/friends/messenger_notification_icon.png b/Coolui v3 test/src/assets/images/friends/messenger_notification_icon.png
new file mode 100644
index 0000000000..73f7e5eaba
Binary files /dev/null and b/Coolui v3 test/src/assets/images/friends/messenger_notification_icon.png differ
diff --git a/Coolui v3 test/src/assets/images/gamecenter/selectedIcon.png b/Coolui v3 test/src/assets/images/gamecenter/selectedIcon.png
new file mode 100644
index 0000000000..718e48b8b3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/gamecenter/selectedIcon.png differ
diff --git a/Coolui v3 test/src/assets/images/gift/gift_tag.png b/Coolui v3 test/src/assets/images/gift/gift_tag.png
new file mode 100644
index 0000000000..3c24813e19
Binary files /dev/null and b/Coolui v3 test/src/assets/images/gift/gift_tag.png differ
diff --git a/Coolui v3 test/src/assets/images/gift/incognito.png b/Coolui v3 test/src/assets/images/gift/incognito.png
new file mode 100644
index 0000000000..304c30c253
Binary files /dev/null and b/Coolui v3 test/src/assets/images/gift/incognito.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/creator_images.png b/Coolui v3 test/src/assets/images/groups/creator_images.png
new file mode 100644
index 0000000000..b39b0d7b83
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/creator_images.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/creator_tabs.png b/Coolui v3 test/src/assets/images/groups/creator_tabs.png
new file mode 100644
index 0000000000..e95b9f0cb0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/creator_tabs.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/icons/group_decorate_icon.png b/Coolui v3 test/src/assets/images/groups/icons/group_decorate_icon.png
new file mode 100644
index 0000000000..3a395fbb7b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/icons/group_decorate_icon.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/icons/group_favorite.png b/Coolui v3 test/src/assets/images/groups/icons/group_favorite.png
new file mode 100644
index 0000000000..4cd734d12d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/icons/group_favorite.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/icons/group_icon_admin.png b/Coolui v3 test/src/assets/images/groups/icons/group_icon_admin.png
new file mode 100644
index 0000000000..bad07e4ae6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/icons/group_icon_admin.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/icons/group_icon_big_admin.png b/Coolui v3 test/src/assets/images/groups/icons/group_icon_big_admin.png
new file mode 100644
index 0000000000..2d3e28765d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/icons/group_icon_big_admin.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/icons/group_icon_big_member.png b/Coolui v3 test/src/assets/images/groups/icons/group_icon_big_member.png
new file mode 100644
index 0000000000..3435937e26
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/icons/group_icon_big_member.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/icons/group_icon_big_owner.png b/Coolui v3 test/src/assets/images/groups/icons/group_icon_big_owner.png
new file mode 100644
index 0000000000..52b8361e59
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/icons/group_icon_big_owner.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/icons/group_icon_not_admin.png b/Coolui v3 test/src/assets/images/groups/icons/group_icon_not_admin.png
new file mode 100644
index 0000000000..7c8b9ee492
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/icons/group_icon_not_admin.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/icons/group_icon_small_owner.png b/Coolui v3 test/src/assets/images/groups/icons/group_icon_small_owner.png
new file mode 100644
index 0000000000..7230ecc590
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/icons/group_icon_small_owner.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/icons/group_notfavorite.png b/Coolui v3 test/src/assets/images/groups/icons/group_notfavorite.png
new file mode 100644
index 0000000000..835d8eb7e4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/icons/group_notfavorite.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/icons/grouptype_icon_0.png b/Coolui v3 test/src/assets/images/groups/icons/grouptype_icon_0.png
new file mode 100644
index 0000000000..9dc4191d59
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/icons/grouptype_icon_0.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/icons/grouptype_icon_1.png b/Coolui v3 test/src/assets/images/groups/icons/grouptype_icon_1.png
new file mode 100644
index 0000000000..ed37213e38
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/icons/grouptype_icon_1.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/icons/grouptype_icon_2.png b/Coolui v3 test/src/assets/images/groups/icons/grouptype_icon_2.png
new file mode 100644
index 0000000000..6a5c0571fe
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/icons/grouptype_icon_2.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/no-group-1.png b/Coolui v3 test/src/assets/images/groups/no-group-1.png
new file mode 100644
index 0000000000..caf0182de3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/no-group-1.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/no-group-2.png b/Coolui v3 test/src/assets/images/groups/no-group-2.png
new file mode 100644
index 0000000000..797c946800
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/no-group-2.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/no-group-3.png b/Coolui v3 test/src/assets/images/groups/no-group-3.png
new file mode 100644
index 0000000000..e5d6a7b77d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/no-group-3.png differ
diff --git a/Coolui v3 test/src/assets/images/groups/no-group-spritesheet.png b/Coolui v3 test/src/assets/images/groups/no-group-spritesheet.png
new file mode 100644
index 0000000000..3eed4b97d0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/groups/no-group-spritesheet.png differ
diff --git a/Coolui v3 test/src/assets/images/guide-tool/guide_tool_duty_switch.png b/Coolui v3 test/src/assets/images/guide-tool/guide_tool_duty_switch.png
new file mode 100644
index 0000000000..f7de6be96b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/guide-tool/guide_tool_duty_switch.png differ
diff --git a/Coolui v3 test/src/assets/images/guide-tool/guide_tool_info_icon.png b/Coolui v3 test/src/assets/images/guide-tool/guide_tool_info_icon.png
new file mode 100644
index 0000000000..32c4a3531d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/guide-tool/guide_tool_info_icon.png differ
diff --git a/Coolui v3 test/src/assets/images/hc-center/benefits.png b/Coolui v3 test/src/assets/images/hc-center/benefits.png
new file mode 100644
index 0000000000..508ecc823c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/hc-center/benefits.png differ
diff --git a/Coolui v3 test/src/assets/images/hc-center/clock.png b/Coolui v3 test/src/assets/images/hc-center/clock.png
new file mode 100644
index 0000000000..4c37e957de
Binary files /dev/null and b/Coolui v3 test/src/assets/images/hc-center/clock.png differ
diff --git a/Coolui v3 test/src/assets/images/hc-center/hc_logo.gif b/Coolui v3 test/src/assets/images/hc-center/hc_logo.gif
new file mode 100644
index 0000000000..8834034f38
Binary files /dev/null and b/Coolui v3 test/src/assets/images/hc-center/hc_logo.gif differ
diff --git a/Coolui v3 test/src/assets/images/hc-center/payday.png b/Coolui v3 test/src/assets/images/hc-center/payday.png
new file mode 100644
index 0000000000..c9bd80ba04
Binary files /dev/null and b/Coolui v3 test/src/assets/images/hc-center/payday.png differ
diff --git a/Coolui v3 test/src/assets/images/help/help_index.png b/Coolui v3 test/src/assets/images/help/help_index.png
new file mode 100644
index 0000000000..2844f41e11
Binary files /dev/null and b/Coolui v3 test/src/assets/images/help/help_index.png differ
diff --git a/Coolui v3 test/src/assets/images/hotelview/arrow_down.png b/Coolui v3 test/src/assets/images/hotelview/arrow_down.png
new file mode 100644
index 0000000000..70d0b9c7be
Binary files /dev/null and b/Coolui v3 test/src/assets/images/hotelview/arrow_down.png differ
diff --git a/Coolui v3 test/src/assets/images/hotelview/hotelview.png b/Coolui v3 test/src/assets/images/hotelview/hotelview.png
new file mode 100644
index 0000000000..62609c2008
Binary files /dev/null and b/Coolui v3 test/src/assets/images/hotelview/hotelview.png differ
diff --git a/Coolui v3 test/src/assets/images/hotelview/infobus.gif b/Coolui v3 test/src/assets/images/hotelview/infobus.gif
new file mode 100644
index 0000000000..f15e9a4571
Binary files /dev/null and b/Coolui v3 test/src/assets/images/hotelview/infobus.gif differ
diff --git a/Coolui v3 test/src/assets/images/hotelview/lobby.png b/Coolui v3 test/src/assets/images/hotelview/lobby.png
new file mode 100644
index 0000000000..f5f3e86083
Binary files /dev/null and b/Coolui v3 test/src/assets/images/hotelview/lobby.png differ
diff --git a/Coolui v3 test/src/assets/images/hotelview/peacefulpark.gif b/Coolui v3 test/src/assets/images/hotelview/peacefulpark.gif
new file mode 100644
index 0000000000..9bde74aca2
Binary files /dev/null and b/Coolui v3 test/src/assets/images/hotelview/peacefulpark.gif differ
diff --git a/Coolui v3 test/src/assets/images/hotelview/picnic.gif b/Coolui v3 test/src/assets/images/hotelview/picnic.gif
new file mode 100644
index 0000000000..a59d214b1a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/hotelview/picnic.gif differ
diff --git a/Coolui v3 test/src/assets/images/hotelview/pool.gif b/Coolui v3 test/src/assets/images/hotelview/pool.gif
new file mode 100644
index 0000000000..18aea23399
Binary files /dev/null and b/Coolui v3 test/src/assets/images/hotelview/pool.gif differ
diff --git a/Coolui v3 test/src/assets/images/hotelview/rooftop.gif b/Coolui v3 test/src/assets/images/hotelview/rooftop.gif
new file mode 100644
index 0000000000..5d53599fa9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/hotelview/rooftop.gif differ
diff --git a/Coolui v3 test/src/assets/images/hotelview/rooftop_pool.gif b/Coolui v3 test/src/assets/images/hotelview/rooftop_pool.gif
new file mode 100644
index 0000000000..d74569cea5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/hotelview/rooftop_pool.gif differ
diff --git a/Coolui v3 test/src/assets/images/icons/arrows.png b/Coolui v3 test/src/assets/images/icons/arrows.png
new file mode 100644
index 0000000000..47a833ce7a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/arrows.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/camera-colormatrix.png b/Coolui v3 test/src/assets/images/icons/camera-colormatrix.png
new file mode 100644
index 0000000000..894396e7c6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/camera-colormatrix.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/camera-composite.png b/Coolui v3 test/src/assets/images/icons/camera-composite.png
new file mode 100644
index 0000000000..681fb18cb0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/camera-composite.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/camera-small.png b/Coolui v3 test/src/assets/images/icons/camera-small.png
new file mode 100644
index 0000000000..1c0563eeb7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/camera-small.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/chat-history.png b/Coolui v3 test/src/assets/images/icons/chat-history.png
new file mode 100644
index 0000000000..da51f37e2c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/chat-history.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/close.png b/Coolui v3 test/src/assets/images/icons/close.png
new file mode 100644
index 0000000000..a723b3eac2
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/close.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/cog.png b/Coolui v3 test/src/assets/images/icons/cog.png
new file mode 100644
index 0000000000..b7101d7184
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/cog.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/disablebubble.png b/Coolui v3 test/src/assets/images/icons/disablebubble.png
new file mode 100644
index 0000000000..bbf1390d20
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/disablebubble.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/enablebubble.png b/Coolui v3 test/src/assets/images/icons/enablebubble.png
new file mode 100644
index 0000000000..217109c5f3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/enablebubble.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/help.png b/Coolui v3 test/src/assets/images/icons/help.png
new file mode 100644
index 0000000000..50d8aa8a0e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/help.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/house-small.png b/Coolui v3 test/src/assets/images/icons/house-small.png
new file mode 100644
index 0000000000..e106a45603
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/house-small.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/icon_cog.png b/Coolui v3 test/src/assets/images/icons/icon_cog.png
new file mode 100644
index 0000000000..7175afb784
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/icon_cog.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/like-room.png b/Coolui v3 test/src/assets/images/icons/like-room.png
new file mode 100644
index 0000000000..1c13cd8512
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/like-room.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/loading-icon.png b/Coolui v3 test/src/assets/images/icons/loading-icon.png
new file mode 100644
index 0000000000..e3d64d0bd0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/loading-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/room-history-back-disabled.png b/Coolui v3 test/src/assets/images/icons/room-history-back-disabled.png
new file mode 100644
index 0000000000..78a447502e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/room-history-back-disabled.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/room-history-back-enabled.png b/Coolui v3 test/src/assets/images/icons/room-history-back-enabled.png
new file mode 100644
index 0000000000..bed6a42535
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/room-history-back-enabled.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/room-history-disabled.png b/Coolui v3 test/src/assets/images/icons/room-history-disabled.png
new file mode 100644
index 0000000000..fcd811976c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/room-history-disabled.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/room-history-enabled.png b/Coolui v3 test/src/assets/images/icons/room-history-enabled.png
new file mode 100644
index 0000000000..287227bf40
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/room-history-enabled.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/room-history-next-disabled.png b/Coolui v3 test/src/assets/images/icons/room-history-next-disabled.png
new file mode 100644
index 0000000000..3f82d0ee2e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/room-history-next-disabled.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/room-history-next-enabled.png b/Coolui v3 test/src/assets/images/icons/room-history-next-enabled.png
new file mode 100644
index 0000000000..3de01c078e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/room-history-next-enabled.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/room-link.png b/Coolui v3 test/src/assets/images/icons/room-link.png
new file mode 100644
index 0000000000..efbebf4a80
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/room-link.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/sign-exclamation.png b/Coolui v3 test/src/assets/images/icons/sign-exclamation.png
new file mode 100644
index 0000000000..7db61fb02b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/sign-exclamation.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/sign-heart.png b/Coolui v3 test/src/assets/images/icons/sign-heart.png
new file mode 100644
index 0000000000..8ac56853d0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/sign-heart.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/sign-red.png b/Coolui v3 test/src/assets/images/icons/sign-red.png
new file mode 100644
index 0000000000..ac0915e108
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/sign-red.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/sign-skull.png b/Coolui v3 test/src/assets/images/icons/sign-skull.png
new file mode 100644
index 0000000000..6221d9a5b9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/sign-skull.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/sign-smile.png b/Coolui v3 test/src/assets/images/icons/sign-smile.png
new file mode 100644
index 0000000000..1a1721cd0f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/sign-smile.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/sign-soccer.png b/Coolui v3 test/src/assets/images/icons/sign-soccer.png
new file mode 100644
index 0000000000..334f1fab46
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/sign-soccer.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/sign-yellow.png b/Coolui v3 test/src/assets/images/icons/sign-yellow.png
new file mode 100644
index 0000000000..272358f202
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/sign-yellow.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/small-room.png b/Coolui v3 test/src/assets/images/icons/small-room.png
new file mode 100644
index 0000000000..c8bbbccc0b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/small-room.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/tickets.png b/Coolui v3 test/src/assets/images/icons/tickets.png
new file mode 100644
index 0000000000..81f48834ba
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/tickets.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/user.png b/Coolui v3 test/src/assets/images/icons/user.png
new file mode 100644
index 0000000000..c4155a2c23
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/user.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/zoom-less.png b/Coolui v3 test/src/assets/images/icons/zoom-less.png
new file mode 100644
index 0000000000..36423da8e9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/zoom-less.png differ
diff --git a/Coolui v3 test/src/assets/images/icons/zoom-more.png b/Coolui v3 test/src/assets/images/icons/zoom-more.png
new file mode 100644
index 0000000000..c14a9e8d48
Binary files /dev/null and b/Coolui v3 test/src/assets/images/icons/zoom-more.png differ
diff --git a/Coolui v3 test/src/assets/images/infostand/bot_background.png b/Coolui v3 test/src/assets/images/infostand/bot_background.png
new file mode 100644
index 0000000000..cd460bbbf4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/infostand/bot_background.png differ
diff --git a/Coolui v3 test/src/assets/images/infostand/countown-timer.png b/Coolui v3 test/src/assets/images/infostand/countown-timer.png
new file mode 100644
index 0000000000..ebfe62911d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/infostand/countown-timer.png differ
diff --git a/Coolui v3 test/src/assets/images/infostand/disk-creator.png b/Coolui v3 test/src/assets/images/infostand/disk-creator.png
new file mode 100644
index 0000000000..c4e95c9dd0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/infostand/disk-creator.png differ
diff --git a/Coolui v3 test/src/assets/images/infostand/disk-icon.png b/Coolui v3 test/src/assets/images/infostand/disk-icon.png
new file mode 100644
index 0000000000..9ee4ed83d0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/infostand/disk-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/infostand/icon_edit.gif b/Coolui v3 test/src/assets/images/infostand/icon_edit.gif
new file mode 100644
index 0000000000..aabdb0a214
Binary files /dev/null and b/Coolui v3 test/src/assets/images/infostand/icon_edit.gif differ
diff --git a/Coolui v3 test/src/assets/images/infostand/pencil-icon.png b/Coolui v3 test/src/assets/images/infostand/pencil-icon.png
new file mode 100644
index 0000000000..27de0d6dd2
Binary files /dev/null and b/Coolui v3 test/src/assets/images/infostand/pencil-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/infostand/rarity-level.png b/Coolui v3 test/src/assets/images/infostand/rarity-level.png
new file mode 100644
index 0000000000..eb1278ef84
Binary files /dev/null and b/Coolui v3 test/src/assets/images/infostand/rarity-level.png differ
diff --git a/Coolui v3 test/src/assets/images/inventory/empty.png b/Coolui v3 test/src/assets/images/inventory/empty.png
new file mode 100644
index 0000000000..d975b4113c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/inventory/empty.png differ
diff --git a/Coolui v3 test/src/assets/images/inventory/rarity-level.png b/Coolui v3 test/src/assets/images/inventory/rarity-level.png
new file mode 100644
index 0000000000..218eccbd6b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/inventory/rarity-level.png differ
diff --git a/Coolui v3 test/src/assets/images/inventory/trading/locked-icon.png b/Coolui v3 test/src/assets/images/inventory/trading/locked-icon.png
new file mode 100644
index 0000000000..4f54e2d3c3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/inventory/trading/locked-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/inventory/trading/unlocked-icon.png b/Coolui v3 test/src/assets/images/inventory/trading/unlocked-icon.png
new file mode 100644
index 0000000000..d6362c4524
Binary files /dev/null and b/Coolui v3 test/src/assets/images/inventory/trading/unlocked-icon.png differ
diff --git a/Coolui v3 test/src/assets/images/loading/loading.gif b/Coolui v3 test/src/assets/images/loading/loading.gif
new file mode 100644
index 0000000000..1303b75584
Binary files /dev/null and b/Coolui v3 test/src/assets/images/loading/loading.gif differ
diff --git a/Coolui v3 test/src/assets/images/loading/progress_habbos.gif b/Coolui v3 test/src/assets/images/loading/progress_habbos.gif
new file mode 100644
index 0000000000..2224994d32
Binary files /dev/null and b/Coolui v3 test/src/assets/images/loading/progress_habbos.gif differ
diff --git a/Coolui v3 test/src/assets/images/modtool/chatlog.gif b/Coolui v3 test/src/assets/images/modtool/chatlog.gif
new file mode 100644
index 0000000000..a64ca0b4c0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/modtool/chatlog.gif differ
diff --git a/Coolui v3 test/src/assets/images/modtool/key.gif b/Coolui v3 test/src/assets/images/modtool/key.gif
new file mode 100644
index 0000000000..578ee6507d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/modtool/key.gif differ
diff --git a/Coolui v3 test/src/assets/images/modtool/m_icon.png b/Coolui v3 test/src/assets/images/modtool/m_icon.png
new file mode 100644
index 0000000000..1116b4ded8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/modtool/m_icon.png differ
diff --git a/Coolui v3 test/src/assets/images/modtool/reports.png b/Coolui v3 test/src/assets/images/modtool/reports.png
new file mode 100644
index 0000000000..4731fed34a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/modtool/reports.png differ
diff --git a/Coolui v3 test/src/assets/images/modtool/room.gif b/Coolui v3 test/src/assets/images/modtool/room.gif
new file mode 100644
index 0000000000..94e77dd9e8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/modtool/room.gif differ
diff --git a/Coolui v3 test/src/assets/images/modtool/room.png b/Coolui v3 test/src/assets/images/modtool/room.png
new file mode 100644
index 0000000000..2ce5efa4e5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/modtool/room.png differ
diff --git a/Coolui v3 test/src/assets/images/modtool/user.gif b/Coolui v3 test/src/assets/images/modtool/user.gif
new file mode 100644
index 0000000000..ab9a590dda
Binary files /dev/null and b/Coolui v3 test/src/assets/images/modtool/user.gif differ
diff --git a/Coolui v3 test/src/assets/images/modtool/wrench.gif b/Coolui v3 test/src/assets/images/modtool/wrench.gif
new file mode 100644
index 0000000000..530c78a8ce
Binary files /dev/null and b/Coolui v3 test/src/assets/images/modtool/wrench.gif differ
diff --git a/Coolui v3 test/src/assets/images/mysterybox/chain_mysterybox_box_overlay.png b/Coolui v3 test/src/assets/images/mysterybox/chain_mysterybox_box_overlay.png
new file mode 100644
index 0000000000..5914aa5a27
Binary files /dev/null and b/Coolui v3 test/src/assets/images/mysterybox/chain_mysterybox_box_overlay.png differ
diff --git a/Coolui v3 test/src/assets/images/mysterybox/key_overlay.png b/Coolui v3 test/src/assets/images/mysterybox/key_overlay.png
new file mode 100644
index 0000000000..8f8c2a5bed
Binary files /dev/null and b/Coolui v3 test/src/assets/images/mysterybox/key_overlay.png differ
diff --git a/Coolui v3 test/src/assets/images/mysterybox/mystery_box.png b/Coolui v3 test/src/assets/images/mysterybox/mystery_box.png
new file mode 100644
index 0000000000..e85f966ce1
Binary files /dev/null and b/Coolui v3 test/src/assets/images/mysterybox/mystery_box.png differ
diff --git a/Coolui v3 test/src/assets/images/mysterybox/mystery_box_key.png b/Coolui v3 test/src/assets/images/mysterybox/mystery_box_key.png
new file mode 100644
index 0000000000..79b43deee6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/mysterybox/mystery_box_key.png differ
diff --git a/Coolui v3 test/src/assets/images/mysterytrophy/frank_mystery_trophy.png b/Coolui v3 test/src/assets/images/mysterytrophy/frank_mystery_trophy.png
new file mode 100644
index 0000000000..67bfeba771
Binary files /dev/null and b/Coolui v3 test/src/assets/images/mysterytrophy/frank_mystery_trophy.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/icons/info.png b/Coolui v3 test/src/assets/images/navigator/icons/info.png
new file mode 100644
index 0000000000..b32d14d9e8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/icons/info.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/icons/room_group.png b/Coolui v3 test/src/assets/images/navigator/icons/room_group.png
new file mode 100644
index 0000000000..b059ba4444
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/icons/room_group.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/icons/room_invisible.png b/Coolui v3 test/src/assets/images/navigator/icons/room_invisible.png
new file mode 100644
index 0000000000..976fe8b41e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/icons/room_invisible.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/icons/room_locked.png b/Coolui v3 test/src/assets/images/navigator/icons/room_locked.png
new file mode 100644
index 0000000000..f46843c890
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/icons/room_locked.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/icons/room_password.png b/Coolui v3 test/src/assets/images/navigator/icons/room_password.png
new file mode 100644
index 0000000000..9fa392f8e0
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/icons/room_password.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_0.png b/Coolui v3 test/src/assets/images/navigator/models/model_0.png
new file mode 100644
index 0000000000..8b7b1d3e3d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_0.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_1.png b/Coolui v3 test/src/assets/images/navigator/models/model_1.png
new file mode 100644
index 0000000000..36f325ad32
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_1.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_2.png b/Coolui v3 test/src/assets/images/navigator/models/model_2.png
new file mode 100644
index 0000000000..921d6f1da2
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_2.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_3.png b/Coolui v3 test/src/assets/images/navigator/models/model_3.png
new file mode 100644
index 0000000000..0444324a76
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_3.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_4.png b/Coolui v3 test/src/assets/images/navigator/models/model_4.png
new file mode 100644
index 0000000000..e3714753ad
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_4.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_5.png b/Coolui v3 test/src/assets/images/navigator/models/model_5.png
new file mode 100644
index 0000000000..4036e1d317
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_5.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_6.png b/Coolui v3 test/src/assets/images/navigator/models/model_6.png
new file mode 100644
index 0000000000..dd14b3b242
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_6.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_7.png b/Coolui v3 test/src/assets/images/navigator/models/model_7.png
new file mode 100644
index 0000000000..031751ed4b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_7.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_8.png b/Coolui v3 test/src/assets/images/navigator/models/model_8.png
new file mode 100644
index 0000000000..7e38e1ffdb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_8.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_9.png b/Coolui v3 test/src/assets/images/navigator/models/model_9.png
new file mode 100644
index 0000000000..0f36c7ca95
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_9.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_a.png b/Coolui v3 test/src/assets/images/navigator/models/model_a.png
new file mode 100644
index 0000000000..cc4a072b97
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_a.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_b.png b/Coolui v3 test/src/assets/images/navigator/models/model_b.png
new file mode 100644
index 0000000000..49b780ae21
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_b.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_c.png b/Coolui v3 test/src/assets/images/navigator/models/model_c.png
new file mode 100644
index 0000000000..2ce5efa4e5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_c.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_d.png b/Coolui v3 test/src/assets/images/navigator/models/model_d.png
new file mode 100644
index 0000000000..de061c8209
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_d.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_e.png b/Coolui v3 test/src/assets/images/navigator/models/model_e.png
new file mode 100644
index 0000000000..039b927604
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_e.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_f.png b/Coolui v3 test/src/assets/images/navigator/models/model_f.png
new file mode 100644
index 0000000000..4b4fadb805
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_f.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_g.png b/Coolui v3 test/src/assets/images/navigator/models/model_g.png
new file mode 100644
index 0000000000..26d03724ca
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_g.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_h.png b/Coolui v3 test/src/assets/images/navigator/models/model_h.png
new file mode 100644
index 0000000000..d8c4be7fb3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_h.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_i.png b/Coolui v3 test/src/assets/images/navigator/models/model_i.png
new file mode 100644
index 0000000000..f5e3d55ccd
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_i.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_j.png b/Coolui v3 test/src/assets/images/navigator/models/model_j.png
new file mode 100644
index 0000000000..8be8f6732e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_j.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_k.png b/Coolui v3 test/src/assets/images/navigator/models/model_k.png
new file mode 100644
index 0000000000..96fcc8b156
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_k.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_l.png b/Coolui v3 test/src/assets/images/navigator/models/model_l.png
new file mode 100644
index 0000000000..f479323b7e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_l.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_m.png b/Coolui v3 test/src/assets/images/navigator/models/model_m.png
new file mode 100644
index 0000000000..d1d8dd76e6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_m.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_n.png b/Coolui v3 test/src/assets/images/navigator/models/model_n.png
new file mode 100644
index 0000000000..6e023a1bb1
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_n.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_o.png b/Coolui v3 test/src/assets/images/navigator/models/model_o.png
new file mode 100644
index 0000000000..458706466d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_o.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_p.png b/Coolui v3 test/src/assets/images/navigator/models/model_p.png
new file mode 100644
index 0000000000..356601e090
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_p.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_q.png b/Coolui v3 test/src/assets/images/navigator/models/model_q.png
new file mode 100644
index 0000000000..9208a14930
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_q.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_r.png b/Coolui v3 test/src/assets/images/navigator/models/model_r.png
new file mode 100644
index 0000000000..a93d80d3e6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_r.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_snowwar1.png b/Coolui v3 test/src/assets/images/navigator/models/model_snowwar1.png
new file mode 100644
index 0000000000..41bab59ce4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_snowwar1.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_snowwar2.png b/Coolui v3 test/src/assets/images/navigator/models/model_snowwar2.png
new file mode 100644
index 0000000000..41bab59ce4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_snowwar2.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_t.png b/Coolui v3 test/src/assets/images/navigator/models/model_t.png
new file mode 100644
index 0000000000..920255d74e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_t.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_u.png b/Coolui v3 test/src/assets/images/navigator/models/model_u.png
new file mode 100644
index 0000000000..96da1012a9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_u.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_v.png b/Coolui v3 test/src/assets/images/navigator/models/model_v.png
new file mode 100644
index 0000000000..6d85c22c73
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_v.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_w.png b/Coolui v3 test/src/assets/images/navigator/models/model_w.png
new file mode 100644
index 0000000000..7bc8024fdb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_w.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_x.png b/Coolui v3 test/src/assets/images/navigator/models/model_x.png
new file mode 100644
index 0000000000..ce0403737f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_x.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_y.png b/Coolui v3 test/src/assets/images/navigator/models/model_y.png
new file mode 100644
index 0000000000..430344cb2c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_y.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/models/model_z.png b/Coolui v3 test/src/assets/images/navigator/models/model_z.png
new file mode 100644
index 0000000000..0809c916b9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/models/model_z.png differ
diff --git a/Coolui v3 test/src/assets/images/navigator/thumbnail_placeholder.png b/Coolui v3 test/src/assets/images/navigator/thumbnail_placeholder.png
new file mode 100644
index 0000000000..be26f84767
Binary files /dev/null and b/Coolui v3 test/src/assets/images/navigator/thumbnail_placeholder.png differ
diff --git a/Coolui v3 test/src/assets/images/nitro/nitro-dark.svg b/Coolui v3 test/src/assets/images/nitro/nitro-dark.svg
new file mode 100644
index 0000000000..20cc53358d
--- /dev/null
+++ b/Coolui v3 test/src/assets/images/nitro/nitro-dark.svg
@@ -0,0 +1,43 @@
+
+
+
diff --git a/Coolui v3 test/src/assets/images/nitro/nitro-light.svg b/Coolui v3 test/src/assets/images/nitro/nitro-light.svg
new file mode 100644
index 0000000000..5706684a37
--- /dev/null
+++ b/Coolui v3 test/src/assets/images/nitro/nitro-light.svg
@@ -0,0 +1,43 @@
+
+
+
diff --git a/Coolui v3 test/src/assets/images/nitro/nitro-n-dark.svg b/Coolui v3 test/src/assets/images/nitro/nitro-n-dark.svg
new file mode 100644
index 0000000000..f8d0ebd654
--- /dev/null
+++ b/Coolui v3 test/src/assets/images/nitro/nitro-n-dark.svg
@@ -0,0 +1,28 @@
+
+
+
diff --git a/Coolui v3 test/src/assets/images/nitro/nitro-n-light.svg b/Coolui v3 test/src/assets/images/nitro/nitro-n-light.svg
new file mode 100644
index 0000000000..4dd94fca32
--- /dev/null
+++ b/Coolui v3 test/src/assets/images/nitro/nitro-n-light.svg
@@ -0,0 +1,29 @@
+
+
+
diff --git a/Coolui v3 test/src/assets/images/notifications/coolui.png b/Coolui v3 test/src/assets/images/notifications/coolui.png
new file mode 100644
index 0000000000..b78ce8bd89
Binary files /dev/null and b/Coolui v3 test/src/assets/images/notifications/coolui.png differ
diff --git a/Coolui v3 test/src/assets/images/notifications/frank.gif b/Coolui v3 test/src/assets/images/notifications/frank.gif
new file mode 100644
index 0000000000..211634f745
Binary files /dev/null and b/Coolui v3 test/src/assets/images/notifications/frank.gif differ
diff --git a/Coolui v3 test/src/assets/images/pets/pet-package/gnome.png b/Coolui v3 test/src/assets/images/pets/pet-package/gnome.png
new file mode 100644
index 0000000000..2c38828022
Binary files /dev/null and b/Coolui v3 test/src/assets/images/pets/pet-package/gnome.png differ
diff --git a/Coolui v3 test/src/assets/images/pets/pet-package/leprechaun_box.png b/Coolui v3 test/src/assets/images/pets/pet-package/leprechaun_box.png
new file mode 100644
index 0000000000..1603eb86f3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/pets/pet-package/leprechaun_box.png differ
diff --git a/Coolui v3 test/src/assets/images/pets/pet-package/petbox_epic.png b/Coolui v3 test/src/assets/images/pets/pet-package/petbox_epic.png
new file mode 100644
index 0000000000..e09ad774fa
Binary files /dev/null and b/Coolui v3 test/src/assets/images/pets/pet-package/petbox_epic.png differ
diff --git a/Coolui v3 test/src/assets/images/pets/pet-package/pterosaur_egg.png b/Coolui v3 test/src/assets/images/pets/pet-package/pterosaur_egg.png
new file mode 100644
index 0000000000..43ee1418ab
Binary files /dev/null and b/Coolui v3 test/src/assets/images/pets/pet-package/pterosaur_egg.png differ
diff --git a/Coolui v3 test/src/assets/images/pets/pet-package/val11_present.png b/Coolui v3 test/src/assets/images/pets/pet-package/val11_present.png
new file mode 100644
index 0000000000..3d371b5b37
Binary files /dev/null and b/Coolui v3 test/src/assets/images/pets/pet-package/val11_present.png differ
diff --git a/Coolui v3 test/src/assets/images/pets/pet-package/velociraptor_egg.png b/Coolui v3 test/src/assets/images/pets/pet-package/velociraptor_egg.png
new file mode 100644
index 0000000000..242f0dfae4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/pets/pet-package/velociraptor_egg.png differ
diff --git a/Coolui v3 test/src/assets/images/prize/prize_background.png b/Coolui v3 test/src/assets/images/prize/prize_background.png
new file mode 100644
index 0000000000..ec9c030653
Binary files /dev/null and b/Coolui v3 test/src/assets/images/prize/prize_background.png differ
diff --git a/Coolui v3 test/src/assets/images/profile/icons/offline.png b/Coolui v3 test/src/assets/images/profile/icons/offline.png
new file mode 100644
index 0000000000..677aadcf2e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/profile/icons/offline.png differ
diff --git a/Coolui v3 test/src/assets/images/profile/icons/online.gif b/Coolui v3 test/src/assets/images/profile/icons/online.gif
new file mode 100644
index 0000000000..3a79838bd3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/profile/icons/online.gif differ
diff --git a/Coolui v3 test/src/assets/images/profile/icons/tick.png b/Coolui v3 test/src/assets/images/profile/icons/tick.png
new file mode 100644
index 0000000000..ec8c52fdf7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/profile/icons/tick.png differ
diff --git a/Coolui v3 test/src/assets/images/room-spectator/room_spectator_bottom_left.png b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_bottom_left.png
new file mode 100644
index 0000000000..01688cb295
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_bottom_left.png differ
diff --git a/Coolui v3 test/src/assets/images/room-spectator/room_spectator_bottom_right.png b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_bottom_right.png
new file mode 100644
index 0000000000..59c8ef2c2d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_bottom_right.png differ
diff --git a/Coolui v3 test/src/assets/images/room-spectator/room_spectator_middle_bottom.png b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_middle_bottom.png
new file mode 100644
index 0000000000..ba6fdeccce
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_middle_bottom.png differ
diff --git a/Coolui v3 test/src/assets/images/room-spectator/room_spectator_middle_left.png b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_middle_left.png
new file mode 100644
index 0000000000..6d9aaa7958
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_middle_left.png differ
diff --git a/Coolui v3 test/src/assets/images/room-spectator/room_spectator_middle_right.png b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_middle_right.png
new file mode 100644
index 0000000000..9d963b3d11
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_middle_right.png differ
diff --git a/Coolui v3 test/src/assets/images/room-spectator/room_spectator_middle_top.png b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_middle_top.png
new file mode 100644
index 0000000000..f6559cee0e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_middle_top.png differ
diff --git a/Coolui v3 test/src/assets/images/room-spectator/room_spectator_top_left.png b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_top_left.png
new file mode 100644
index 0000000000..5e62a3c954
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_top_left.png differ
diff --git a/Coolui v3 test/src/assets/images/room-spectator/room_spectator_top_right.png b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_top_right.png
new file mode 100644
index 0000000000..825f3fb10b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-spectator/room_spectator_top_right.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/avatar-info/preview-background.png b/Coolui v3 test/src/assets/images/room-widgets/avatar-info/preview-background.png
new file mode 100644
index 0000000000..dea4f08dd4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/avatar-info/preview-background.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/camera-widget/btn.png b/Coolui v3 test/src/assets/images/room-widgets/camera-widget/btn.png
new file mode 100644
index 0000000000..76b086b18d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/camera-widget/btn.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/camera-widget/btn_down.png b/Coolui v3 test/src/assets/images/room-widgets/camera-widget/btn_down.png
new file mode 100644
index 0000000000..76f25da1d9
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/camera-widget/btn_down.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/camera-widget/btn_hi.png b/Coolui v3 test/src/assets/images/room-widgets/camera-widget/btn_hi.png
new file mode 100644
index 0000000000..5f04fc007a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/camera-widget/btn_hi.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/camera-widget/cam_bg.png b/Coolui v3 test/src/assets/images/room-widgets/camera-widget/cam_bg.png
new file mode 100644
index 0000000000..d6cf994d06
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/camera-widget/cam_bg.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/camera-widget/camera-spritesheet.png b/Coolui v3 test/src/assets/images/room-widgets/camera-widget/camera-spritesheet.png
new file mode 100644
index 0000000000..4ea82e31a7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/camera-widget/camera-spritesheet.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/camera-widget/viewfinder.png b/Coolui v3 test/src/assets/images/room-widgets/camera-widget/viewfinder.png
new file mode 100644
index 0000000000..ab6a9b24f6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/camera-widget/viewfinder.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/dimmer-widget/dimmer_banner.png b/Coolui v3 test/src/assets/images/room-widgets/dimmer-widget/dimmer_banner.png
new file mode 100644
index 0000000000..fdc6e9faba
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/dimmer-widget/dimmer_banner.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/engraving-lock-widget/engraving-lock-spritesheet.png b/Coolui v3 test/src/assets/images/room-widgets/engraving-lock-widget/engraving-lock-spritesheet.png
new file mode 100644
index 0000000000..472dc85b29
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/engraving-lock-widget/engraving-lock-spritesheet.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/exchange-credit/exchange-credit-image.png b/Coolui v3 test/src/assets/images/room-widgets/exchange-credit/exchange-credit-image.png
new file mode 100644
index 0000000000..eef5da6cb5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/exchange-credit/exchange-credit-image.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/furni-context-menu/monsterplant-preview.png b/Coolui v3 test/src/assets/images/room-widgets/furni-context-menu/monsterplant-preview.png
new file mode 100644
index 0000000000..8d3d771ef4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/furni-context-menu/monsterplant-preview.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/mannequin-widget/mannequin-spritesheet.png b/Coolui v3 test/src/assets/images/room-widgets/mannequin-widget/mannequin-spritesheet.png
new file mode 100644
index 0000000000..45e11f346d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/mannequin-widget/mannequin-spritesheet.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/disk_2.png b/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/disk_2.png
new file mode 100644
index 0000000000..3033020977
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/disk_2.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/disk_image.png b/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/disk_image.png
new file mode 100644
index 0000000000..7a8ab453dd
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/disk_image.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/move.png b/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/move.png
new file mode 100644
index 0000000000..9d1635d83e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/move.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/pause-btn.png b/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/pause-btn.png
new file mode 100644
index 0000000000..900f99b4d7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/pause-btn.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/pause.png b/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/pause.png
new file mode 100644
index 0000000000..ec5fef47dc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/pause.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/playing.png b/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/playing.png
new file mode 100644
index 0000000000..0e3449d161
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/playing.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/preview.png b/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/preview.png
new file mode 100644
index 0000000000..160f0befb6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/playlist-editor/preview.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-blue.png b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-blue.png
new file mode 100644
index 0000000000..9a14182e51
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-blue.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-christmas.png b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-christmas.png
new file mode 100644
index 0000000000..82b4732fa8
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-christmas.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-close.png b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-close.png
new file mode 100644
index 0000000000..9621c56f2c
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-close.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-dreams.png b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-dreams.png
new file mode 100644
index 0000000000..1723bdcb06
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-dreams.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-green.png b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-green.png
new file mode 100644
index 0000000000..5e73c74733
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-green.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-heart.png b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-heart.png
new file mode 100644
index 0000000000..455238513a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-heart.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-juninas.png b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-juninas.png
new file mode 100644
index 0000000000..faaea9d1e6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-juninas.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-pink.png b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-pink.png
new file mode 100644
index 0000000000..7565899834
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-pink.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-shakesp.png b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-shakesp.png
new file mode 100644
index 0000000000..d5011c74b2
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-shakesp.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-spritesheet.png b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-spritesheet.png
new file mode 100644
index 0000000000..02495714fc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-spritesheet.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-trash.png b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-trash.png
new file mode 100644
index 0000000000..96dff8f02b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-trash.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-yellow.png b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-yellow.png
new file mode 100644
index 0000000000..759d3f9902
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/stickie-widget/stickie-yellow.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/thumbnail-widget/thumbnail-camera-spritesheet.png b/Coolui v3 test/src/assets/images/room-widgets/thumbnail-widget/thumbnail-camera-spritesheet.png
new file mode 100644
index 0000000000..63a9397f20
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/thumbnail-widget/thumbnail-camera-spritesheet.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/trophy-widget/trophy-spritesheet.png b/Coolui v3 test/src/assets/images/room-widgets/trophy-widget/trophy-spritesheet.png
new file mode 100644
index 0000000000..f9184cb578
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/trophy-widget/trophy-spritesheet.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/wordquiz-widget/thumbs-down-small.png b/Coolui v3 test/src/assets/images/room-widgets/wordquiz-widget/thumbs-down-small.png
new file mode 100644
index 0000000000..78e51cfe56
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/wordquiz-widget/thumbs-down-small.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/wordquiz-widget/thumbs-down.png b/Coolui v3 test/src/assets/images/room-widgets/wordquiz-widget/thumbs-down.png
new file mode 100644
index 0000000000..fd320c51d7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/wordquiz-widget/thumbs-down.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/wordquiz-widget/thumbs-up-small.png b/Coolui v3 test/src/assets/images/room-widgets/wordquiz-widget/thumbs-up-small.png
new file mode 100644
index 0000000000..b93111f0cc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/wordquiz-widget/thumbs-up-small.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/wordquiz-widget/thumbs-up.png b/Coolui v3 test/src/assets/images/room-widgets/wordquiz-widget/thumbs-up.png
new file mode 100644
index 0000000000..dd650987dd
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/wordquiz-widget/thumbs-up.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/youtube-widget/next.png b/Coolui v3 test/src/assets/images/room-widgets/youtube-widget/next.png
new file mode 100644
index 0000000000..a02e164ba6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/youtube-widget/next.png differ
diff --git a/Coolui v3 test/src/assets/images/room-widgets/youtube-widget/prev.png b/Coolui v3 test/src/assets/images/room-widgets/youtube-widget/prev.png
new file mode 100644
index 0000000000..d48b658eba
Binary files /dev/null and b/Coolui v3 test/src/assets/images/room-widgets/youtube-widget/prev.png differ
diff --git a/Coolui v3 test/src/assets/images/stackhelper/slider-background.png b/Coolui v3 test/src/assets/images/stackhelper/slider-background.png
new file mode 100644
index 0000000000..20ab191e45
Binary files /dev/null and b/Coolui v3 test/src/assets/images/stackhelper/slider-background.png differ
diff --git a/Coolui v3 test/src/assets/images/stackhelper/slider-pointer.png b/Coolui v3 test/src/assets/images/stackhelper/slider-pointer.png
new file mode 100644
index 0000000000..8787456f5e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/stackhelper/slider-pointer.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/arrow.png b/Coolui v3 test/src/assets/images/toolbar/arrow.png
new file mode 100644
index 0000000000..bf04ea0ecf
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/arrow.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/friend-search.png b/Coolui v3 test/src/assets/images/toolbar/friend-search.png
new file mode 100644
index 0000000000..7156c4fd2e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/friend-search.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/buildersclub.png b/Coolui v3 test/src/assets/images/toolbar/icons/buildersclub.png
new file mode 100644
index 0000000000..bbf6d68126
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/buildersclub.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/camera.png b/Coolui v3 test/src/assets/images/toolbar/icons/camera.png
new file mode 100644
index 0000000000..da5d835f24
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/camera.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/catalog.png b/Coolui v3 test/src/assets/images/toolbar/icons/catalog.png
new file mode 100644
index 0000000000..f680921711
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/catalog.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/friend_all.png b/Coolui v3 test/src/assets/images/toolbar/icons/friend_all.png
new file mode 100644
index 0000000000..b2ca0d7d52
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/friend_all.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/friend_head.png b/Coolui v3 test/src/assets/images/toolbar/icons/friend_head.png
new file mode 100644
index 0000000000..6380c90c1f
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/friend_head.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/friend_search.png b/Coolui v3 test/src/assets/images/toolbar/icons/friend_search.png
new file mode 100644
index 0000000000..ebe1c65ed7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/friend_search.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/game.png b/Coolui v3 test/src/assets/images/toolbar/icons/game.png
new file mode 100644
index 0000000000..59ef8aafb5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/game.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/habbo.png b/Coolui v3 test/src/assets/images/toolbar/icons/habbo.png
new file mode 100644
index 0000000000..78cd0a486e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/habbo.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/house.png b/Coolui v3 test/src/assets/images/toolbar/icons/house.png
new file mode 100644
index 0000000000..f2c8746ab2
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/house.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/inventory.png b/Coolui v3 test/src/assets/images/toolbar/icons/inventory.png
new file mode 100644
index 0000000000..d848586ae3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/inventory.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/joinroom.png b/Coolui v3 test/src/assets/images/toolbar/icons/joinroom.png
new file mode 100644
index 0000000000..894ee78ff7
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/joinroom.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/achievements.png b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/achievements.png
new file mode 100644
index 0000000000..575464d617
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/achievements.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/clothing.png b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/clothing.png
new file mode 100644
index 0000000000..bfacabd844
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/clothing.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/cog.png b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/cog.png
new file mode 100644
index 0000000000..6180409a10
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/cog.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/forums.png b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/forums.png
new file mode 100644
index 0000000000..e22426e6d6
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/forums.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/helper-tool.png b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/helper-tool.png
new file mode 100644
index 0000000000..e324611fca
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/helper-tool.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/my-rooms.png b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/my-rooms.png
new file mode 100644
index 0000000000..8d4dcad00a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/my-rooms.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/profile.png b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/profile.png
new file mode 100644
index 0000000000..04964bfe64
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/profile.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/rooms.png b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/rooms.png
new file mode 100644
index 0000000000..00261ceed4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/rooms.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/talents.png b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/talents.png
new file mode 100644
index 0000000000..2f91dfe03b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/me-menu/talents.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/message.png b/Coolui v3 test/src/assets/images/toolbar/icons/message.png
new file mode 100644
index 0000000000..c12d5bb4a4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/message.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/message_unsee.gif b/Coolui v3 test/src/assets/images/toolbar/icons/message_unsee.gif
new file mode 100644
index 0000000000..eddfe1cc2b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/message_unsee.gif differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/modtools.png b/Coolui v3 test/src/assets/images/toolbar/icons/modtools.png
new file mode 100644
index 0000000000..24c362f0e3
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/modtools.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/rooms.png b/Coolui v3 test/src/assets/images/toolbar/icons/rooms.png
new file mode 100644
index 0000000000..00261ceed4
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/rooms.png differ
diff --git a/Coolui v3 test/src/assets/images/toolbar/icons/sendmessage.png b/Coolui v3 test/src/assets/images/toolbar/icons/sendmessage.png
new file mode 100644
index 0000000000..9f64b17c34
Binary files /dev/null and b/Coolui v3 test/src/assets/images/toolbar/icons/sendmessage.png differ
diff --git a/Coolui v3 test/src/assets/images/ui/loading_icon.png b/Coolui v3 test/src/assets/images/ui/loading_icon.png
new file mode 100644
index 0000000000..15f66be400
Binary files /dev/null and b/Coolui v3 test/src/assets/images/ui/loading_icon.png differ
diff --git a/Coolui v3 test/src/assets/images/ui/ubuntu-close-buttons.png b/Coolui v3 test/src/assets/images/ui/ubuntu-close-buttons.png
new file mode 100644
index 0000000000..d6a79a6732
Binary files /dev/null and b/Coolui v3 test/src/assets/images/ui/ubuntu-close-buttons.png differ
diff --git a/Coolui v3 test/src/assets/images/unique/catalog-info-amount-bg.png b/Coolui v3 test/src/assets/images/unique/catalog-info-amount-bg.png
new file mode 100644
index 0000000000..4a56c9b265
Binary files /dev/null and b/Coolui v3 test/src/assets/images/unique/catalog-info-amount-bg.png differ
diff --git a/Coolui v3 test/src/assets/images/unique/catalog-info-sold-out.png b/Coolui v3 test/src/assets/images/unique/catalog-info-sold-out.png
new file mode 100644
index 0000000000..79626e146b
Binary files /dev/null and b/Coolui v3 test/src/assets/images/unique/catalog-info-sold-out.png differ
diff --git a/Coolui v3 test/src/assets/images/unique/grid-bg-glass.png b/Coolui v3 test/src/assets/images/unique/grid-bg-glass.png
new file mode 100644
index 0000000000..5b64c480bf
Binary files /dev/null and b/Coolui v3 test/src/assets/images/unique/grid-bg-glass.png differ
diff --git a/Coolui v3 test/src/assets/images/unique/grid-bg-sold-out.png b/Coolui v3 test/src/assets/images/unique/grid-bg-sold-out.png
new file mode 100644
index 0000000000..94f66620ab
Binary files /dev/null and b/Coolui v3 test/src/assets/images/unique/grid-bg-sold-out.png differ
diff --git a/Coolui v3 test/src/assets/images/unique/grid-bg.png b/Coolui v3 test/src/assets/images/unique/grid-bg.png
new file mode 100644
index 0000000000..d7737ba83e
Binary files /dev/null and b/Coolui v3 test/src/assets/images/unique/grid-bg.png differ
diff --git a/Coolui v3 test/src/assets/images/unique/grid-count-bg.png b/Coolui v3 test/src/assets/images/unique/grid-count-bg.png
new file mode 100644
index 0000000000..68e13bddbb
Binary files /dev/null and b/Coolui v3 test/src/assets/images/unique/grid-count-bg.png differ
diff --git a/Coolui v3 test/src/assets/images/unique/inventory-info-amount-bg.png b/Coolui v3 test/src/assets/images/unique/inventory-info-amount-bg.png
new file mode 100644
index 0000000000..af4e31e2e5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/unique/inventory-info-amount-bg.png differ
diff --git a/Coolui v3 test/src/assets/images/unique/numbers.png b/Coolui v3 test/src/assets/images/unique/numbers.png
new file mode 100644
index 0000000000..e1ece79f46
Binary files /dev/null and b/Coolui v3 test/src/assets/images/unique/numbers.png differ
diff --git a/Coolui v3 test/src/assets/images/wired/card-action-corners.png b/Coolui v3 test/src/assets/images/wired/card-action-corners.png
new file mode 100644
index 0000000000..faec2349dc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/wired/card-action-corners.png differ
diff --git a/Coolui v3 test/src/assets/images/wired/icon_action.png b/Coolui v3 test/src/assets/images/wired/icon_action.png
new file mode 100644
index 0000000000..78e90e6302
Binary files /dev/null and b/Coolui v3 test/src/assets/images/wired/icon_action.png differ
diff --git a/Coolui v3 test/src/assets/images/wired/icon_condition.png b/Coolui v3 test/src/assets/images/wired/icon_condition.png
new file mode 100644
index 0000000000..26925a63f5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/wired/icon_condition.png differ
diff --git a/Coolui v3 test/src/assets/images/wired/icon_trigger.png b/Coolui v3 test/src/assets/images/wired/icon_trigger.png
new file mode 100644
index 0000000000..f48d13c875
Binary files /dev/null and b/Coolui v3 test/src/assets/images/wired/icon_trigger.png differ
diff --git a/Coolui v3 test/src/assets/images/wired/icon_wired_around.png b/Coolui v3 test/src/assets/images/wired/icon_wired_around.png
new file mode 100644
index 0000000000..0b4b5a1254
Binary files /dev/null and b/Coolui v3 test/src/assets/images/wired/icon_wired_around.png differ
diff --git a/Coolui v3 test/src/assets/images/wired/icon_wired_left_right.png b/Coolui v3 test/src/assets/images/wired/icon_wired_left_right.png
new file mode 100644
index 0000000000..862d6d8138
Binary files /dev/null and b/Coolui v3 test/src/assets/images/wired/icon_wired_left_right.png differ
diff --git a/Coolui v3 test/src/assets/images/wired/icon_wired_north_east.png b/Coolui v3 test/src/assets/images/wired/icon_wired_north_east.png
new file mode 100644
index 0000000000..3710854fda
Binary files /dev/null and b/Coolui v3 test/src/assets/images/wired/icon_wired_north_east.png differ
diff --git a/Coolui v3 test/src/assets/images/wired/icon_wired_north_west.png b/Coolui v3 test/src/assets/images/wired/icon_wired_north_west.png
new file mode 100644
index 0000000000..09eeefc18a
Binary files /dev/null and b/Coolui v3 test/src/assets/images/wired/icon_wired_north_west.png differ
diff --git a/Coolui v3 test/src/assets/images/wired/icon_wired_rotate_clockwise.png b/Coolui v3 test/src/assets/images/wired/icon_wired_rotate_clockwise.png
new file mode 100644
index 0000000000..2827e3d23d
Binary files /dev/null and b/Coolui v3 test/src/assets/images/wired/icon_wired_rotate_clockwise.png differ
diff --git a/Coolui v3 test/src/assets/images/wired/icon_wired_rotate_counter_clockwise.png b/Coolui v3 test/src/assets/images/wired/icon_wired_rotate_counter_clockwise.png
new file mode 100644
index 0000000000..7e281bacbc
Binary files /dev/null and b/Coolui v3 test/src/assets/images/wired/icon_wired_rotate_counter_clockwise.png differ
diff --git a/Coolui v3 test/src/assets/images/wired/icon_wired_south_east.png b/Coolui v3 test/src/assets/images/wired/icon_wired_south_east.png
new file mode 100644
index 0000000000..4217c4b830
Binary files /dev/null and b/Coolui v3 test/src/assets/images/wired/icon_wired_south_east.png differ
diff --git a/Coolui v3 test/src/assets/images/wired/icon_wired_south_west.png b/Coolui v3 test/src/assets/images/wired/icon_wired_south_west.png
new file mode 100644
index 0000000000..07ab1f95ee
Binary files /dev/null and b/Coolui v3 test/src/assets/images/wired/icon_wired_south_west.png differ
diff --git a/Coolui v3 test/src/assets/images/wired/icon_wired_up_down.png b/Coolui v3 test/src/assets/images/wired/icon_wired_up_down.png
new file mode 100644
index 0000000000..c2d243bae5
Binary files /dev/null and b/Coolui v3 test/src/assets/images/wired/icon_wired_up_down.png differ
diff --git a/Coolui v3 test/src/assets/webfonts/Ubuntu-C.ttf b/Coolui v3 test/src/assets/webfonts/Ubuntu-C.ttf
new file mode 100644
index 0000000000..8e2c4bc27a
Binary files /dev/null and b/Coolui v3 test/src/assets/webfonts/Ubuntu-C.ttf differ
diff --git a/Coolui v3 test/src/assets/webfonts/Ubuntu-b.ttf b/Coolui v3 test/src/assets/webfonts/Ubuntu-b.ttf
new file mode 100644
index 0000000000..9073aa25ff
Binary files /dev/null and b/Coolui v3 test/src/assets/webfonts/Ubuntu-b.ttf differ
diff --git a/Coolui v3 test/src/assets/webfonts/Ubuntu-i.ttf b/Coolui v3 test/src/assets/webfonts/Ubuntu-i.ttf
new file mode 100644
index 0000000000..1be5141e04
Binary files /dev/null and b/Coolui v3 test/src/assets/webfonts/Ubuntu-i.ttf differ
diff --git a/Coolui v3 test/src/assets/webfonts/Ubuntu-ib.ttf b/Coolui v3 test/src/assets/webfonts/Ubuntu-ib.ttf
new file mode 100644
index 0000000000..13ecca8123
Binary files /dev/null and b/Coolui v3 test/src/assets/webfonts/Ubuntu-ib.ttf differ
diff --git a/Coolui v3 test/src/assets/webfonts/Ubuntu-m.ttf b/Coolui v3 test/src/assets/webfonts/Ubuntu-m.ttf
new file mode 100644
index 0000000000..8de0928562
Binary files /dev/null and b/Coolui v3 test/src/assets/webfonts/Ubuntu-m.ttf differ
diff --git a/Coolui v3 test/src/assets/webfonts/Ubuntu.ttf b/Coolui v3 test/src/assets/webfonts/Ubuntu.ttf
new file mode 100644
index 0000000000..975da10fcd
Binary files /dev/null and b/Coolui v3 test/src/assets/webfonts/Ubuntu.ttf differ
diff --git a/Coolui v3 test/src/common/AutoGrid.tsx b/Coolui v3 test/src/common/AutoGrid.tsx
new file mode 100644
index 0000000000..167ee92b32
--- /dev/null
+++ b/Coolui v3 test/src/common/AutoGrid.tsx
@@ -0,0 +1,28 @@
+import { CSSProperties, FC, useMemo } from 'react';
+import { Grid, GridProps } from './Grid';
+
+export interface AutoGridProps extends GridProps
+{
+ columnMinWidth?: number;
+ columnMinHeight?: number;
+}
+
+export const AutoGrid: FC = props =>
+{
+ const { columnMinWidth = 40, columnMinHeight = 40, columnCount = 0, fullHeight = false, maxContent = true, overflow = 'auto', style = {}, ...rest } = props;
+
+ const getStyle = useMemo(() =>
+ {
+ let newStyle: CSSProperties = {};
+
+ newStyle['--nitro-grid-column-min-height'] = (columnMinHeight + 'px');
+
+ if(columnCount > 1) newStyle.gridTemplateColumns = `repeat(auto-fill, minmax(${ columnMinWidth }px, 1fr))`;
+
+ if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
+
+ return newStyle;
+ }, [ columnMinWidth, columnMinHeight, columnCount, style ]);
+
+ return ;
+};
diff --git a/Coolui v3 test/src/common/Base.tsx b/Coolui v3 test/src/common/Base.tsx
new file mode 100644
index 0000000000..a2747c65e8
--- /dev/null
+++ b/Coolui v3 test/src/common/Base.tsx
@@ -0,0 +1,84 @@
+import { CSSProperties, DetailedHTMLProps, FC, HTMLAttributes, MutableRefObject, ReactNode, useMemo } from 'react';
+import { ColorVariantType, DisplayType, FloatType, OverflowType, PositionType } from './types';
+
+export interface BaseProps extends DetailedHTMLProps, T>
+{
+ innerRef?: MutableRefObject;
+ display?: DisplayType;
+ fit?: boolean;
+ fitV?: boolean;
+ grow?: boolean;
+ shrink?: boolean;
+ fullWidth?: boolean;
+ fullHeight?: boolean;
+ overflow?: OverflowType;
+ position?: PositionType;
+ float?: FloatType;
+ pointer?: boolean;
+ visible?: boolean;
+ textColor?: ColorVariantType;
+ classNames?: string[];
+ children?: ReactNode;
+}
+
+export const Base: FC> = props =>
+{
+ const { ref = null, innerRef = null, display = null, fit = false, fitV = false, grow = false, shrink = false, fullWidth = false, fullHeight = false, overflow = null, position = null, float = null, pointer = false, visible = null, textColor = null, classNames = [], className = '', style = {}, children = null, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [];
+
+ if(display && display.length) newClassNames.push(display);
+
+ if(fit || fullWidth) newClassNames.push('w-full');
+
+ if(fit || fullHeight) newClassNames.push('h-full');
+
+ if(fitV) newClassNames.push('vw-full', 'vh-full');
+
+ if(grow) newClassNames.push('!flex-grow');
+
+ if(shrink) newClassNames.push('!flex-shrink-0');
+
+ if(overflow) newClassNames.push('overflow-' + overflow);
+
+ if(position) newClassNames.push(position);
+
+ if(float) newClassNames.push('float-' + float);
+
+ if(pointer) newClassNames.push('cursor-pointer');
+
+ if(visible !== null) newClassNames.push(visible ? 'visible' : 'invisible');
+
+ if(textColor) newClassNames.push('text-' + textColor);
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ display, fit, fitV, grow, shrink, fullWidth, fullHeight, overflow, position, float, pointer, visible, textColor, classNames ]);
+
+ const getClassName = useMemo(() =>
+ {
+ let newClassName = getClassNames.join(' ');
+
+ if(className.length) newClassName += (' ' + className);
+
+ return newClassName.trim();
+ }, [ getClassNames, className ]);
+
+ const getStyle = useMemo(() =>
+ {
+ let newStyle: CSSProperties = {};
+
+ if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
+
+ return newStyle;
+ }, [ style ]);
+
+ return (
+
+ { children }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/Button.tsx b/Coolui v3 test/src/common/Button.tsx
new file mode 100644
index 0000000000..5caca6c16a
--- /dev/null
+++ b/Coolui v3 test/src/common/Button.tsx
@@ -0,0 +1,71 @@
+import { FC, useMemo } from 'react';
+import { Flex, FlexProps } from './Flex';
+import { ButtonSizeType, ColorVariantType } from './types';
+
+export interface ButtonProps extends FlexProps
+{
+ variant?: ColorVariantType;
+ size?: ButtonSizeType;
+ active?: boolean;
+ disabled?: boolean;
+}
+
+export const Button: FC = props =>
+{
+ const { variant = 'primary', size = 'sm', active = false, disabled = false, classNames = [], ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+
+ // fucked up method i know (i dont have a clue what im doing because im a ninja)
+
+ const newClassNames: string[] = [ 'pointer-events-auto inline-block font-normal leading-normal text-[#fff] text-center no-underline align-middle cursor-pointer select-none border-[1px] border-[solid] border-[transparent] px-[.75rem] py-[.375rem] text-[.9rem] rounded-[.25rem] [transition:color_.15s_ease-in-out,_background-color_.15s_ease-in-out,_border-color_.15s_ease-in-out,_box-shadow_.15s_ease-in-out]' ];
+
+ if(variant)
+ {
+
+ if(variant == 'primary')
+ newClassNames.push('text-white bg-[#1e7295] border-[#1e7295] [box-shadow:inset_0_2px_#ffffff26,inset_0_-2px_#0000001a,0_1px_#0000001a] hover:text-white hover:bg-[#1a617f] hover:border-[#185b77]');
+
+ if(variant == 'success')
+ newClassNames.push('text-white bg-[#00800b] border-[#00800b] [box-shadow:inset_0_2px_#ffffff26,inset_0_-2px_#0000001a,0_1px_#0000001a] hover:text-white hover:bg-[#006d09] hover:border-[#006609]');
+
+ if(variant == 'danger')
+ newClassNames.push('text-white bg-[#a81a12] border-[#a81a12] [box-shadow:inset_0_2px_#ffffff26,inset_0_-2px_#0000001a,0_1px_#0000001a] hover:text-white hover:bg-[#8f160f] hover:border-[#86150e]');
+
+ if(variant == 'warning')
+ newClassNames.push('text-white bg-[#ffc107] border-[#ffc107] [box-shadow:inset_0_2px_#ffffff26,inset_0_-2px_#0000001a,0_1px_#0000001a] hover:text-[#000] hover:bg-[#ffca2c] hover:border-[#ffc720]');
+
+ if(variant == 'black')
+ newClassNames.push('text-white bg-[#000] border-[#000] [box-shadow:inset_0_2px_#ffffff26,inset_0_-2px_#0000001a,0_1px_#0000001a] hover:text-white hover:bg-[#000] hover:border-[#000]');
+
+ if(variant == 'secondary')
+ newClassNames.push('text-white bg-[#185d79] border-[#185d79] [box-shadow:inset_0_2px_#ffffff26,_inset_0_-2px_#0000001a,_0_1px_#0000001a] hover:text-white hover:bg-[#144f67] hover:border-[#134a61]');
+
+ if(variant == 'dark')
+ newClassNames.push('text-white bg-dark [box-shadow:inset_0_2px_#ffffff26,inset_0_-2px_#0000001a,0_1px_#0000001a] hover:text-white hover:bg-[#18181bfb] hover:border-[#161619fb]');
+
+ if(variant == 'gray')
+ newClassNames.push('text-white bg-[#1e7295] border-[#1e7295] [box-shadow:inset_0_2px_#ffffff26,inset_0_-2px_#0000001a,0_1px_#0000001a] hover:text-white hover:bg-[#1a617f] hover:border-[#185b77]');
+
+ }
+
+ if(size)
+ {
+ if(size == 'sm')
+ {
+ newClassNames.push('!px-[.5rem] !py-[.25rem] !text-[.7875rem] !rounded-[.2rem] !min-h-[28px]');
+ }
+ }
+
+ if(active) newClassNames.push('active');
+
+ if(disabled) newClassNames.push('pointer-events-none opacity-[.65] [box-shadow:none]');
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ variant, size, active, disabled, classNames ]);
+
+ return ;
+};
diff --git a/Coolui v3 test/src/common/ButtonGroup.tsx b/Coolui v3 test/src/common/ButtonGroup.tsx
new file mode 100644
index 0000000000..033bb1fc28
--- /dev/null
+++ b/Coolui v3 test/src/common/ButtonGroup.tsx
@@ -0,0 +1,22 @@
+import { FC, useMemo } from 'react';
+import { Base, BaseProps } from './Base';
+
+export interface ButtonGroupProps extends BaseProps
+{
+}
+
+export const ButtonGroup: FC = props =>
+{
+ const { classNames = [], ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'btn-group' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ return ;
+}
diff --git a/Coolui v3 test/src/common/Column.tsx b/Coolui v3 test/src/common/Column.tsx
new file mode 100644
index 0000000000..13cca1e2e4
--- /dev/null
+++ b/Coolui v3 test/src/common/Column.tsx
@@ -0,0 +1,46 @@
+import { FC, useMemo } from 'react';
+import { Flex, FlexProps } from './Flex';
+import { useGridContext } from './GridContext';
+import { ColumnSizesType } from './types';
+
+export interface ColumnProps extends FlexProps
+{
+ size?: ColumnSizesType;
+ offset?: ColumnSizesType;
+ column?: boolean;
+}
+
+export const Column: FC = props =>
+{
+ const { size = 0, offset = 0, column = true, gap = 2, classNames = [], ...rest } = props;
+ const { isCssGrid = false } = useGridContext();
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [];
+
+ if(size)
+ {
+ let colClassName = `col-span-${ size }`;
+
+ if(isCssGrid) colClassName = `${ colClassName }`;
+
+ newClassNames.push(colClassName);
+ }
+
+ if(offset)
+ {
+ let colClassName = `offset-${ offset }`;
+
+ if(isCssGrid) colClassName = `g-start-${ offset }`;
+
+ newClassNames.push(colClassName);
+ }
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ size, offset, isCssGrid, classNames ]);
+
+ return ;
+};
diff --git a/Coolui v3 test/src/common/Flex.tsx b/Coolui v3 test/src/common/Flex.tsx
new file mode 100644
index 0000000000..6d332acbd3
--- /dev/null
+++ b/Coolui v3 test/src/common/Flex.tsx
@@ -0,0 +1,50 @@
+import { FC, useMemo } from 'react';
+import { Base, BaseProps } from './Base';
+import { AlignItemType, AlignSelfType, JustifyContentType, SpacingType } from './types';
+
+export interface FlexProps extends BaseProps
+{
+ column?: boolean;
+ reverse?: boolean;
+ gap?: SpacingType;
+ center?: boolean;
+ alignSelf?: AlignSelfType;
+ alignItems?: AlignItemType;
+ justifyContent?: JustifyContentType;
+}
+
+export const Flex: FC = props =>
+{
+ const { display = 'flex', column = undefined, reverse = false, gap = null, center = false, alignSelf = null, alignItems = null, justifyContent = null, classNames = [], ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [];
+
+ if(column)
+ {
+ if(reverse) newClassNames.push('flex-col-span-reverse');
+ else newClassNames.push('flex-col');
+ }
+ else
+ {
+ if(reverse) newClassNames.push('flex-row-reverse');
+ }
+
+ if(gap) newClassNames.push('gap-' + gap);
+
+ if(alignSelf) newClassNames.push('self-' + alignSelf);
+
+ if(alignItems) newClassNames.push('items-' + alignItems);
+
+ if(justifyContent) newClassNames.push('justify-' + justifyContent);
+
+ if(!alignItems && !justifyContent && center) newClassNames.push('items-center', 'justify-center');
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ column, reverse, gap, center, alignSelf, alignItems, justifyContent, classNames ]);
+
+ return ;
+};
diff --git a/Coolui v3 test/src/common/FormGroup.tsx b/Coolui v3 test/src/common/FormGroup.tsx
new file mode 100644
index 0000000000..2d73f24a8f
--- /dev/null
+++ b/Coolui v3 test/src/common/FormGroup.tsx
@@ -0,0 +1,22 @@
+import { FC, useMemo } from 'react';
+import { Flex, FlexProps } from './Flex';
+
+export interface FormGroupProps extends FlexProps
+{
+}
+
+export const FormGroup: FC = props =>
+{
+ const { classNames = [], ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'form-group' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ return ;
+};
diff --git a/Coolui v3 test/src/common/Grid.tsx b/Coolui v3 test/src/common/Grid.tsx
new file mode 100644
index 0000000000..441db8abfd
--- /dev/null
+++ b/Coolui v3 test/src/common/Grid.tsx
@@ -0,0 +1,64 @@
+import { CSSProperties, FC, useMemo } from 'react';
+import { Base, BaseProps } from './Base';
+import { GridContextProvider } from './GridContext';
+import { AlignItemType, AlignSelfType, JustifyContentType, SpacingType } from './types';
+
+export interface GridProps extends BaseProps
+{
+ inline?: boolean;
+ gap?: SpacingType;
+ maxContent?: boolean;
+ columnCount?: number;
+ center?: boolean;
+ alignSelf?: AlignSelfType;
+ alignItems?: AlignItemType;
+ justifyContent?: JustifyContentType;
+}
+
+export const Grid: FC = props =>
+{
+ const { inline = false, gap = 2, maxContent = false, columnCount = 0, center = false, alignSelf = null, alignItems = null, justifyContent = null, fullHeight = true, classNames = [], style = {}, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [];
+
+
+ if(inline) newClassNames.push('inline-grid');
+ else newClassNames.push('grid grid-rows-[repeat(var(--bs-rows,_1),_1fr)] grid-cols-[repeat(var(--bs-columns,_12),_1fr)]');
+
+ if(gap) newClassNames.push('gap-' + gap);
+ else if(gap === 0) newClassNames.push('gap-0');
+
+ if(maxContent) newClassNames.push('[flex-basis:max-content]');
+
+ if(alignSelf) newClassNames.push('self-' + alignSelf);
+
+ if(alignItems) newClassNames.push('items-' + alignItems);
+
+ if(justifyContent) newClassNames.push('justify-' + justifyContent);
+
+ if(!alignItems && !justifyContent && center) newClassNames.push('items-center', 'justify-center');
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ inline, gap, maxContent, alignSelf, alignItems, justifyContent, center, classNames ]);
+
+ const getStyle = useMemo(() =>
+ {
+ let newStyle: CSSProperties = {};
+
+ if(columnCount) newStyle['--bs-columns'] = columnCount.toString();
+
+ if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
+
+ return newStyle;
+ }, [ columnCount, style ]);
+
+ return (
+
+
+
+ );
+};
diff --git a/Coolui v3 test/src/common/GridContext.tsx b/Coolui v3 test/src/common/GridContext.tsx
new file mode 100644
index 0000000000..082d4bed27
--- /dev/null
+++ b/Coolui v3 test/src/common/GridContext.tsx
@@ -0,0 +1,17 @@
+import { createContext, FC, ProviderProps, useContext } from 'react';
+
+export interface IGridContext
+{
+ isCssGrid: boolean;
+}
+
+const GridContext = createContext({
+ isCssGrid: false
+});
+
+export const GridContextProvider: FC> = props =>
+{
+ return { props.children };
+};
+
+export const useGridContext = () => useContext(GridContext);
diff --git a/Coolui v3 test/src/common/HorizontalRule.tsx b/Coolui v3 test/src/common/HorizontalRule.tsx
new file mode 100644
index 0000000000..54164ae9ff
--- /dev/null
+++ b/Coolui v3 test/src/common/HorizontalRule.tsx
@@ -0,0 +1,38 @@
+import { CSSProperties, FC, useMemo } from 'react';
+import { Base, BaseProps } from './Base';
+import { ColorVariantType } from './types';
+
+export interface HorizontalRuleProps extends BaseProps
+{
+ variant?: ColorVariantType;
+ height?: number;
+}
+
+export const HorizontalRule: FC = props =>
+{
+ const { variant = 'black', height = 1, classNames = [], style = {}, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [];
+
+ if(variant) newClassNames.push('bg-' + variant);
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ variant, classNames ]);
+
+ const getStyle = useMemo(() =>
+ {
+ let newStyle: CSSProperties = { display: 'list-item' };
+
+ if(height > 0) newStyle.height = height;
+
+ if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
+
+ return newStyle;
+ }, [ height, style ]);
+
+ return ;
+};
diff --git a/Coolui v3 test/src/common/InfiniteScroll.tsx b/Coolui v3 test/src/common/InfiniteScroll.tsx
new file mode 100644
index 0000000000..51966f887e
--- /dev/null
+++ b/Coolui v3 test/src/common/InfiniteScroll.tsx
@@ -0,0 +1,55 @@
+import { useVirtualizer } from '@tanstack/react-virtual';
+import { FC, ReactElement, useRef, useState } from 'react';
+import { Base } from './Base';
+
+interface InfiniteScrollProps
+{
+ rows: T[];
+ overscan?: number;
+ scrollToBottom?: boolean;
+ rowRender: (row: T) => ReactElement;
+}
+
+export const InfiniteScroll: FC = props =>
+{
+ const { rows = [], overscan = 5, scrollToBottom = false, rowRender = null } = props;
+ const [ scrollIndex, setScrollIndex ] = useState(rows.length - 1);
+ const parentRef = useRef(null);
+
+ const virtualizer = useVirtualizer({
+ count: rows.length,
+ overscan,
+ getScrollElement: () => parentRef.current,
+ estimateSize: () => 45,
+ });
+ const items = virtualizer.getVirtualItems();
+
+ return (
+
+
+
+ { items.map((virtualRow) => (
+
+ { rowRender(rows[virtualRow.index]) }
+
+ )) }
+
+
+
+ );
+};
diff --git a/Coolui v3 test/src/common/Popover.tsx b/Coolui v3 test/src/common/Popover.tsx
new file mode 100644
index 0000000000..5a7c5c8954
--- /dev/null
+++ b/Coolui v3 test/src/common/Popover.tsx
@@ -0,0 +1,54 @@
+import { FC, PropsWithChildren, useEffect, useRef, useState } from 'react';
+
+export const ReactPopover: FC> = props =>
+{
+ const { content = null, trigger = null, children = null } = props;
+ const [ show, setShow ] = useState(false);
+ const wrapperRef = useRef(null);
+
+ const handleMouseOver = () => (trigger === 'hover') && setShow(true);
+
+ const handleMouseLeft = () => (trigger === 'hover') && setShow(false);
+
+ useEffect(() =>
+ {
+ if(!show) return;
+
+ const handleClickOutside = (event: MouseEvent) =>
+ {
+ if(wrapperRef.current && !wrapperRef.current.contains(event.target)) setShow(false);
+ };
+
+ document.addEventListener('mousedown', handleClickOutside);
+
+ return () =>
+ {
+ // Unbind the event listener on clean up
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, [ show, wrapperRef ]);
+
+ return (
+
+
setShow(!show) }
+ >
+ { children }
+
+
+
+ );
+};
diff --git a/Coolui v3 test/src/common/Slider.tsx b/Coolui v3 test/src/common/Slider.tsx
new file mode 100644
index 0000000000..50cba28c55
--- /dev/null
+++ b/Coolui v3 test/src/common/Slider.tsx
@@ -0,0 +1,21 @@
+import { FC } from 'react';
+import ReactSlider, { ReactSliderProps } from 'react-slider';
+import { Button } from './Button';
+import { Flex } from './Flex';
+import { FaAngleLeft, FaAngleRight } from 'react-icons/fa';
+
+export interface SliderProps extends ReactSliderProps
+{
+ disabledButton?: boolean;
+}
+
+export const Slider: FC = props =>
+{
+ const { disabledButton, max, min, value, onChange, ...rest } = props;
+
+ return
+ { !disabledButton && }
+
+ { !disabledButton && }
+ ;
+}
diff --git a/Coolui v3 test/src/common/Text.tsx b/Coolui v3 test/src/common/Text.tsx
new file mode 100644
index 0000000000..da5235d92a
--- /dev/null
+++ b/Coolui v3 test/src/common/Text.tsx
@@ -0,0 +1,79 @@
+import { FC, useMemo } from 'react';
+import { Base, BaseProps } from './Base';
+import { ColorVariantType, FontSizeType, FontWeightType, TextAlignType } from './types';
+
+export interface TextProps extends BaseProps {
+ variant?: ColorVariantType;
+ fontWeight?: FontWeightType;
+ fontSize?: FontSizeType;
+ fontSizeCustom?: number;
+ align?: TextAlignType;
+ bold?: boolean;
+ underline?: boolean;
+ italics?: boolean;
+ truncate?: boolean;
+ center?: boolean;
+ textEnd?: boolean;
+ small?: boolean;
+ wrap?: boolean;
+ noWrap?: boolean;
+ textBreak?: boolean;
+}
+
+export const Text: FC = props => {
+ const {
+ variant = 'black',
+ fontWeight = null,
+ fontSize = 0,
+ fontSizeCustom,
+ align = null,
+ bold = false,
+ underline = false,
+ italics = false,
+ truncate = false,
+ center = false,
+ textEnd = false,
+ small = false,
+ wrap = false,
+ noWrap = false,
+ textBreak = false,
+ ...rest
+ } = props;
+
+ const getClassNames = useMemo(() => {
+ const newClassNames: string[] = ['inline'];
+
+ if (variant) {
+ if (variant === 'primary') newClassNames.push('text-[#1e7295]');
+ if (variant == 'secondary') newClassNames.push('text-[#185d79]');
+ if (variant === 'black') newClassNames.push('text-[#000000]');
+ if (variant == 'dark') newClassNames.push('text-[#18181b]');
+ if (variant === 'gray') newClassNames.push('text-[#6b7280]');
+ if (variant === 'white') newClassNames.push('text-[#ffffff]');
+ if (variant == 'success') newClassNames.push('text-[#00800b]');
+ if (variant == 'danger') newClassNames.push('text-[#a81a12]');
+ if (variant == 'warning') newClassNames.push('text-[#ffc107]');
+ }
+
+ if (bold) newClassNames.push('font-bold');
+ if (fontWeight) newClassNames.push('font-' + fontWeight);
+ if (fontSize) newClassNames.push('fs-' + fontSize);
+ if (fontSizeCustom) newClassNames.push('fs-custom');
+ if (align) newClassNames.push('text-' + align);
+ if (underline) newClassNames.push('underline');
+ if (italics) newClassNames.push('italic');
+ if (truncate) newClassNames.push('text-truncate');
+ if (center) newClassNames.push('text-center');
+ if (textEnd) newClassNames.push('text-end');
+ if (small) newClassNames.push('text-sm');
+ if (wrap) newClassNames.push('text-wrap');
+ if (noWrap) newClassNames.push('text-nowrap');
+ if (textBreak) newClassNames.push('text-break');
+
+ return newClassNames;
+ }, [variant, fontWeight, fontSize, fontSizeCustom, align, bold, underline, italics, truncate, center, textEnd, small, wrap, noWrap, textBreak]);
+
+ const style = fontSizeCustom ? { '--font-size': `${fontSizeCustom}px` } as React.CSSProperties : undefined;
+
+ return ;
+};
\ No newline at end of file
diff --git a/Coolui v3 test/src/common/card/NitroCardContentView.tsx b/Coolui v3 test/src/common/card/NitroCardContentView.tsx
new file mode 100644
index 0000000000..a93703be88
--- /dev/null
+++ b/Coolui v3 test/src/common/card/NitroCardContentView.tsx
@@ -0,0 +1,19 @@
+import { FC, useMemo } from 'react';
+import { Column, ColumnProps } from '..';
+
+export const NitroCardContentView: FC = props =>
+{
+ const { overflow = 'auto', classNames = [], ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ // Theme Changer
+ const newClassNames: string[] = [ 'container-fluid', 'h-full p-[8px] overflow-auto', 'bg-light' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ return ;
+};
diff --git a/Coolui v3 test/src/common/card/NitroCardContext.tsx b/Coolui v3 test/src/common/card/NitroCardContext.tsx
new file mode 100644
index 0000000000..c296b2a5da
--- /dev/null
+++ b/Coolui v3 test/src/common/card/NitroCardContext.tsx
@@ -0,0 +1,17 @@
+import { createContext, FC, ProviderProps, useContext } from 'react';
+
+interface INitroCardContext
+{
+ theme: string;
+}
+
+const NitroCardContext = createContext({
+ theme: null
+});
+
+export const NitroCardContextProvider: FC> = props =>
+{
+ return { props.children };
+};
+
+export const useNitroCardContext = () => useContext(NitroCardContext);
diff --git a/Coolui v3 test/src/common/card/NitroCardHeaderView.tsx b/Coolui v3 test/src/common/card/NitroCardHeaderView.tsx
new file mode 100644
index 0000000000..2cfd63807d
--- /dev/null
+++ b/Coolui v3 test/src/common/card/NitroCardHeaderView.tsx
@@ -0,0 +1,41 @@
+import { FC, MouseEvent } from 'react';
+import { FaFlag } from 'react-icons/fa';
+import { Base, Column, ColumnProps, Flex } from '..';
+
+interface NitroCardHeaderViewProps extends ColumnProps
+{
+ headerText: string;
+ isGalleryPhoto?: boolean;
+ noCloseButton?: boolean;
+ onReportPhoto?: (event: MouseEvent) => void;
+ onCloseClick: (event: MouseEvent) => void;
+}
+
+export const NitroCardHeaderView: FC = props =>
+{
+ const { headerText = null, isGalleryPhoto = false, noCloseButton = false, onReportPhoto = null, onCloseClick = null, justifyContent = 'center', alignItems = 'center', classNames = [], children = null, ...rest } = props;
+
+
+
+ const onMouseDown = (event: MouseEvent) =>
+ {
+ event.stopPropagation();
+ event.nativeEvent.stopImmediatePropagation();
+ };
+
+ return (
+
+
+ { headerText }
+ { isGalleryPhoto &&
+
+
+
+ }
+
+
+
+
+
+ );
+};
diff --git a/Coolui v3 test/src/common/card/NitroCardView.tsx b/Coolui v3 test/src/common/card/NitroCardView.tsx
new file mode 100644
index 0000000000..9018cace58
--- /dev/null
+++ b/Coolui v3 test/src/common/card/NitroCardView.tsx
@@ -0,0 +1,35 @@
+import { FC, useMemo, useRef } from 'react';
+import { Column, ColumnProps } from '..';
+import { DraggableWindow, DraggableWindowPosition, DraggableWindowProps } from '../draggable-window';
+import { NitroCardContextProvider } from './NitroCardContext';
+
+export interface NitroCardViewProps extends DraggableWindowProps, ColumnProps
+{
+ theme?: string;
+}
+
+export const NitroCardView: FC = props =>
+{
+ const { theme = 'primary', uniqueKey = null, handleSelector = '.drag-handler', windowPosition = DraggableWindowPosition.CENTER, disableDrag = false, overflow = 'hidden', position = 'relative', gap = 0, classNames = [], ...rest } = props;
+ const elementRef = useRef();
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'resize', 'rounded', 'shadow', ];
+
+ // Card Theme Changer
+ newClassNames.push('border-[1px] border-[#283F5D]');
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ return (
+
+
+
+
+
+ );
+};
diff --git a/Coolui v3 test/src/common/card/accordion/NitroCardAccordionContext.tsx b/Coolui v3 test/src/common/card/accordion/NitroCardAccordionContext.tsx
new file mode 100644
index 0000000000..5e65c30b39
--- /dev/null
+++ b/Coolui v3 test/src/common/card/accordion/NitroCardAccordionContext.tsx
@@ -0,0 +1,21 @@
+import { createContext, Dispatch, FC, ProviderProps, SetStateAction, useContext } from 'react';
+
+export interface INitroCardAccordionContext
+{
+ closers: Function[];
+ setClosers: Dispatch>;
+ closeAll: () => void;
+}
+
+const NitroCardAccordionContext = createContext({
+ closers: null,
+ setClosers: null,
+ closeAll: null
+});
+
+export const NitroCardAccordionContextProvider: FC> = props =>
+{
+ return ;
+};
+
+export const useNitroCardAccordionContext = () => useContext(NitroCardAccordionContext);
diff --git a/Coolui v3 test/src/common/card/accordion/NitroCardAccordionItemView.tsx b/Coolui v3 test/src/common/card/accordion/NitroCardAccordionItemView.tsx
new file mode 100644
index 0000000000..238aab40e2
--- /dev/null
+++ b/Coolui v3 test/src/common/card/accordion/NitroCardAccordionItemView.tsx
@@ -0,0 +1,18 @@
+import { FC } from 'react';
+import { Flex, FlexProps } from '../..';
+
+export interface NitroCardAccordionItemViewProps extends FlexProps
+{
+
+}
+
+export const NitroCardAccordionItemView: FC = props =>
+{
+ const { alignItems = 'center', gap = 1, children = null, ...rest } = props;
+
+ return (
+
+ { children }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/card/accordion/NitroCardAccordionSetView.tsx b/Coolui v3 test/src/common/card/accordion/NitroCardAccordionSetView.tsx
new file mode 100644
index 0000000000..1a059d4b07
--- /dev/null
+++ b/Coolui v3 test/src/common/card/accordion/NitroCardAccordionSetView.tsx
@@ -0,0 +1,84 @@
+import { FC, useCallback, useEffect, useMemo, useState } from 'react';
+import { FaCaretDown, FaCaretUp } from 'react-icons/fa';
+import { Column, ColumnProps, Flex, Text } from '../..';
+import { useNitroCardAccordionContext } from './NitroCardAccordionContext';
+
+export interface NitroCardAccordionSetViewProps extends ColumnProps
+{
+ headerText: string;
+ isExpanded?: boolean;
+}
+
+export const NitroCardAccordionSetView: FC = props =>
+{
+ const { headerText = '', isExpanded = false, gap = 0, classNames = [], children = null, ...rest } = props;
+ const [ isOpen, setIsOpen ] = useState(false);
+ const { setClosers = null, closeAll = null } = useNitroCardAccordionContext();
+
+ const onClick = () =>
+ {
+ closeAll();
+
+ setIsOpen(prevValue => !prevValue);
+ };
+
+ const onClose = useCallback(() => setIsOpen(false), []);
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames = [ 'nitro-card-accordion-set' ];
+
+ if(isOpen) newClassNames.push('active');
+
+ if(classNames && classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ isOpen, classNames ]);
+
+ useEffect(() =>
+ {
+ setIsOpen(isExpanded);
+ }, [ isExpanded ]);
+
+ useEffect(() =>
+ {
+ const closeFunction = onClose;
+
+ setClosers(prevValue =>
+ {
+ const newClosers = [ ...prevValue ];
+
+ newClosers.push(closeFunction);
+
+ return newClosers;
+ });
+
+ return () =>
+ {
+ setClosers(prevValue =>
+ {
+ const newClosers = [ ...prevValue ];
+
+ const index = newClosers.indexOf(closeFunction);
+
+ if(index >= 0) newClosers.splice(index, 1);
+
+ return newClosers;
+ });
+ };
+ }, [ onClose, setClosers ]);
+
+ return (
+
+
+ { headerText }
+ { isOpen && }
+ { !isOpen && }
+
+ { isOpen &&
+
+ { children }
+ }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/card/accordion/NitroCardAccordionView.tsx b/Coolui v3 test/src/common/card/accordion/NitroCardAccordionView.tsx
new file mode 100644
index 0000000000..0874450a0f
--- /dev/null
+++ b/Coolui v3 test/src/common/card/accordion/NitroCardAccordionView.tsx
@@ -0,0 +1,25 @@
+import { FC, useCallback, useState } from 'react';
+import { Column, ColumnProps } from '../..';
+import { NitroCardAccordionContextProvider } from './NitroCardAccordionContext';
+
+interface NitroCardAccordionViewProps extends ColumnProps
+{
+
+}
+
+export const NitroCardAccordionView: FC = props =>
+{
+ const { ...rest } = props;
+ const [ closers, setClosers ] = useState([]);
+
+ const closeAll = useCallback(() =>
+ {
+ for(const closer of closers) closer();
+ }, [ closers ]);
+
+ return (
+
+
+
+ );
+};
diff --git a/Coolui v3 test/src/common/card/accordion/index.ts b/Coolui v3 test/src/common/card/accordion/index.ts
new file mode 100644
index 0000000000..d585b3362b
--- /dev/null
+++ b/Coolui v3 test/src/common/card/accordion/index.ts
@@ -0,0 +1,4 @@
+export * from './NitroCardAccordionContext';
+export * from './NitroCardAccordionItemView';
+export * from './NitroCardAccordionSetView';
+export * from './NitroCardAccordionView';
diff --git a/Coolui v3 test/src/common/card/index.ts b/Coolui v3 test/src/common/card/index.ts
new file mode 100644
index 0000000000..3ce0d60687
--- /dev/null
+++ b/Coolui v3 test/src/common/card/index.ts
@@ -0,0 +1,6 @@
+export * from './NitroCardContentView';
+export * from './NitroCardContext';
+export * from './NitroCardHeaderView';
+export * from './NitroCardView';
+export * from './accordion';
+export * from './tabs';
diff --git a/Coolui v3 test/src/common/card/tabs/NitroCardTabsItemView.tsx b/Coolui v3 test/src/common/card/tabs/NitroCardTabsItemView.tsx
new file mode 100644
index 0000000000..2f0b014821
--- /dev/null
+++ b/Coolui v3 test/src/common/card/tabs/NitroCardTabsItemView.tsx
@@ -0,0 +1,36 @@
+import { FC, useMemo } from 'react';
+import { Flex, FlexProps } from '../../Flex';
+import { LayoutItemCountView } from '../../layout';
+
+interface NitroCardTabsItemViewProps extends FlexProps
+{
+ isActive?: boolean;
+ count?: number;
+}
+
+export const NitroCardTabsItemView: FC = props =>
+{
+ const { isActive = false, count = 0, overflow = 'hidden', position = 'relative', pointer = true, classNames = [], children = null, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'overflow-hidden relative cursor-pointer rounded-t-md flex bg-card-tab-item px-3 py-1 z-[1] border-card-border border-t border-l border-r before:absolute before:w-[93%] before:h-[3px] before:rounded-md before:top-[1.5px] before:left-0 before:right-0 before:m-auto before:z-[1] before:bg-[#C2C9D1]',
+ isActive && 'bg-card-tab-item-active -mb-[1px] before:bg-white' ];
+
+ //if (isActive) newClassNames.push('bg-[#dfdfdf] border-b-[1px_solid_black]');
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ isActive, classNames ]);
+
+ return (
+
+
+ { children }
+
+ { (count > 0) &&
+ }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/card/tabs/NitroCardTabsView.tsx b/Coolui v3 test/src/common/card/tabs/NitroCardTabsView.tsx
new file mode 100644
index 0000000000..8e5d118d3d
--- /dev/null
+++ b/Coolui v3 test/src/common/card/tabs/NitroCardTabsView.tsx
@@ -0,0 +1,22 @@
+import { FC, useMemo } from 'react';
+import { Flex, FlexProps } from '../..';
+
+export const NitroCardTabsView: FC = props =>
+{
+ const { justifyContent = 'center', gap = 1, classNames = [], children = null, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'justify-center gap-0.5 flex bg-card-tabs min-h-card-tabs max-h-card-tabs pt-1 border-b border-card-border px-2 -mt-[1px]' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ return (
+
+ { children }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/card/tabs/index.ts b/Coolui v3 test/src/common/card/tabs/index.ts
new file mode 100644
index 0000000000..517db1146c
--- /dev/null
+++ b/Coolui v3 test/src/common/card/tabs/index.ts
@@ -0,0 +1,2 @@
+export * from './NitroCardTabsItemView';
+export * from './NitroCardTabsView';
diff --git a/Coolui v3 test/src/common/draggable-window/DraggableWindow.tsx b/Coolui v3 test/src/common/draggable-window/DraggableWindow.tsx
new file mode 100644
index 0000000000..b71a3537d2
--- /dev/null
+++ b/Coolui v3 test/src/common/draggable-window/DraggableWindow.tsx
@@ -0,0 +1,245 @@
+import { MouseEventType, TouchEventType } from '@nitrots/nitro-renderer';
+import { CSSProperties, FC, Key, MouseEvent as ReactMouseEvent, ReactNode, TouchEvent as ReactTouchEvent, useCallback, useEffect, useRef, useState } from 'react';
+import { createPortal } from 'react-dom';
+import { GetLocalStorage, SetLocalStorage, WindowSaveOptions } from '../../api';
+import { DraggableWindowPosition } from './DraggableWindowPosition';
+
+const CURRENT_WINDOWS: HTMLElement[] = [];
+const POS_MEMORY: Map = new Map();
+const BOUNDS_THRESHOLD_TOP: number = 0;
+const BOUNDS_THRESHOLD_LEFT: number = 0;
+
+export interface DraggableWindowProps {
+ uniqueKey?: Key;
+ handleSelector?: string;
+ windowPosition?: string;
+ disableDrag?: boolean;
+ dragStyle?: CSSProperties;
+ offsetLeft?: number;
+ offsetTop?: number;
+ children?: ReactNode;
+}
+
+export const DraggableWindow: FC = props => {
+ const { uniqueKey = null, handleSelector = '.drag-handler', windowPosition = DraggableWindowPosition.CENTER, disableDrag = false, dragStyle = {}, children = null, offsetLeft = 0, offsetTop = 0 } = props;
+ const [delta, setDelta] = useState<{ x: number, y: number }>({ x: 0, y: 0 });
+ const [offset, setOffset] = useState<{ x: number, y: number }>({ x: 0, y: 0 });
+ const [start, setStart] = useState<{ x: number, y: number }>({ x: 0, y: 0 });
+ const [isDragging, setIsDragging] = useState(false);
+ const [isPositioned, setIsPositioned] = useState(false); // New state to control visibility
+ const [dragHandler, setDragHandler] = useState(null);
+ const elementRef = useRef();
+
+ const bringToTop = useCallback(() => {
+ let zIndex = 400;
+ for (const existingWindow of CURRENT_WINDOWS) {
+ zIndex += 1;
+ existingWindow.style.zIndex = zIndex.toString();
+ }
+ }, []);
+
+ const moveCurrentWindow = useCallback(() => {
+ const index = CURRENT_WINDOWS.indexOf(elementRef.current);
+ if (index === -1) {
+ CURRENT_WINDOWS.push(elementRef.current);
+ } else if (index === (CURRENT_WINDOWS.length - 1)) return;
+ else if (index >= 0) {
+ CURRENT_WINDOWS.splice(index, 1);
+ CURRENT_WINDOWS.push(elementRef.current);
+ }
+ bringToTop();
+ }, [bringToTop]);
+
+ const onMouseDown = useCallback((event: ReactMouseEvent) => {
+ moveCurrentWindow();
+ }, [moveCurrentWindow]);
+
+ const onTouchStart = useCallback((event: ReactTouchEvent) => {
+ moveCurrentWindow();
+ }, [moveCurrentWindow]);
+
+ const startDragging = useCallback((startX: number, startY: number) => {
+ setStart({ x: startX, y: startY });
+ setIsDragging(true);
+ }, []);
+
+ const onDragMouseDown = useCallback((event: MouseEvent) => {
+ startDragging(event.clientX, event.clientY);
+ }, [startDragging]);
+
+ const onTouchDown = useCallback((event: TouchEvent) => {
+ const touch = event.touches[0];
+ startDragging(touch.clientX, touch.clientY);
+ }, [startDragging]);
+
+ const clampPosition = useCallback((newX: number, newY: number) => {
+ if (!elementRef.current) return { x: newX, y: newY };
+
+ const windowWidth = elementRef.current.offsetWidth;
+ const windowHeight = elementRef.current.offsetHeight;
+ const viewportWidth = window.innerWidth;
+ const viewportHeight = window.innerHeight;
+
+ const clampedX = Math.max(BOUNDS_THRESHOLD_LEFT, Math.min(newX, viewportWidth - windowWidth));
+ const clampedY = Math.max(BOUNDS_THRESHOLD_TOP, Math.min(newY, viewportHeight - windowHeight));
+
+ return { x: clampedX, y: clampedY };
+ }, []);
+
+ const onDragMouseMove = useCallback((event: MouseEvent) => {
+ if (!elementRef.current || !isDragging) return;
+
+ const newDeltaX = event.clientX - start.x;
+ const newDeltaY = event.clientY - start.y;
+ const newOffsetX = offset.x + newDeltaX;
+ const newOffsetY = offset.y + newDeltaY;
+
+ const clampedPos = clampPosition(newOffsetX, newOffsetY);
+ setDelta({ x: clampedPos.x - offset.x, y: clampedPos.y - offset.y });
+ }, [start, offset, clampPosition, isDragging]);
+
+ const onDragTouchMove = useCallback((event: TouchEvent) => {
+ if (!elementRef.current || !isDragging) return;
+
+ const touch = event.touches[0];
+ const newDeltaX = touch.clientX - start.x;
+ const newDeltaY = touch.clientY - start.y;
+ const newOffsetX = offset.x + newDeltaX;
+ const newOffsetY = offset.y + newDeltaY;
+
+ const clampedPos = clampPosition(newOffsetX, newOffsetY);
+ setDelta({ x: clampedPos.x - offset.x, y: clampedPos.y - offset.y });
+ }, [start, offset, clampPosition, isDragging]);
+
+ const completeDrag = useCallback(() => {
+ if (!elementRef.current || !dragHandler || !isDragging) return;
+
+ const finalOffsetX = offset.x + delta.x;
+ const finalOffsetY = offset.y + delta.y;
+ const clampedPos = clampPosition(finalOffsetX, finalOffsetY);
+
+ setDelta({ x: 0, y: 0 });
+ setOffset({ x: clampedPos.x, y: clampedPos.y });
+ setIsDragging(false);
+
+ if (uniqueKey !== null) {
+ const newStorage = { ...GetLocalStorage(`nitro.windows.${uniqueKey}`) } as WindowSaveOptions;
+ newStorage.offset = { x: clampedPos.x, y: clampedPos.y };
+ SetLocalStorage(`nitro.windows.${uniqueKey}`, newStorage);
+ }
+ }, [dragHandler, delta, offset, uniqueKey, clampPosition, isDragging]);
+
+ const onDragMouseUp = useCallback((event: MouseEvent) => {
+ completeDrag();
+ }, [completeDrag]);
+
+ const onDragTouchUp = useCallback((event: TouchEvent) => {
+ completeDrag();
+ }, [completeDrag]);
+
+ useEffect(() => {
+ const element = elementRef.current as HTMLElement;
+ if (!element) return;
+
+ CURRENT_WINDOWS.push(element);
+ bringToTop();
+
+ if (!disableDrag) {
+ const handle = element.querySelector(handleSelector);
+ if (handle) setDragHandler(handle as HTMLElement);
+ }
+
+ const windowWidth = element.offsetWidth || 340;
+ const windowHeight = element.offsetHeight || 462;
+ let offsetX = 0;
+ let offsetY = 0;
+
+ switch (windowPosition) {
+ case DraggableWindowPosition.TOP_CENTER:
+ offsetY = 50 + offsetTop;
+ offsetX = (window.innerWidth - windowWidth) / 2 + offsetLeft;
+ break;
+ case DraggableWindowPosition.CENTER:
+ offsetY = (window.innerHeight - windowHeight) / 2 + offsetTop;
+ offsetX = (window.innerWidth - windowWidth) / 2 + offsetLeft;
+ break;
+ case DraggableWindowPosition.TOP_LEFT:
+ offsetY = 50 + offsetTop;
+ offsetX = 50 + offsetLeft;
+ break;
+ }
+
+ const clampedPos = clampPosition(offsetX, offsetY);
+ element.style.left = '0px';
+ element.style.top = '0px';
+ setOffset({ x: clampedPos.x, y: clampedPos.y });
+ setDelta({ x: 0, y: 0 });
+ setIsPositioned(true); // Mark as positioned after setting initial offset
+
+ return () => {
+ const index = CURRENT_WINDOWS.indexOf(element);
+ if (index >= 0) CURRENT_WINDOWS.splice(index, 1);
+ };
+ }, [handleSelector, windowPosition, uniqueKey, disableDrag, offsetLeft, offsetTop, bringToTop]);
+
+ useEffect(() => {
+ const element = elementRef.current as HTMLElement;
+ if (!element || !isPositioned) return;
+
+ element.style.transform = `translate(${offset.x + delta.x}px, ${offset.y + delta.y}px)`;
+ element.style.visibility = 'visible';
+ }, [offset, delta, isPositioned]);
+
+ useEffect(() => {
+ if (!dragHandler) return;
+
+ dragHandler.addEventListener(MouseEventType.MOUSE_DOWN, onDragMouseDown);
+ dragHandler.addEventListener(TouchEventType.TOUCH_START, onTouchDown);
+
+ return () => {
+ dragHandler.removeEventListener(MouseEventType.MOUSE_DOWN, onDragMouseDown);
+ dragHandler.removeEventListener(TouchEventType.TOUCH_START, onTouchDown);
+ };
+ }, [dragHandler, onDragMouseDown, onTouchDown]);
+
+ useEffect(() => {
+ if (!isDragging) return;
+
+ document.addEventListener(MouseEventType.MOUSE_UP, onDragMouseUp);
+ document.addEventListener(TouchEventType.TOUCH_END, onDragTouchUp);
+ document.addEventListener(MouseEventType.MOUSE_MOVE, onDragMouseMove);
+ document.addEventListener(TouchEventType.TOUCH_MOVE, onDragTouchMove);
+
+ return () => {
+ document.removeEventListener(MouseEventType.MOUSE_UP, onDragMouseUp);
+ document.removeEventListener(TouchEventType.TOUCH_END, onDragTouchUp);
+ document.removeEventListener(MouseEventType.MOUSE_MOVE, onDragMouseMove);
+ document.removeEventListener(TouchEventType.TOUCH_MOVE, onDragTouchMove);
+ };
+ }, [isDragging, onDragMouseUp, onDragMouseMove, onDragTouchUp, onDragTouchMove]);
+
+ useEffect(() => {
+ if (!uniqueKey) return;
+
+ const localStorage = GetLocalStorage(`nitro.windows.${uniqueKey}`);
+ if (!localStorage || !localStorage.offset) return;
+
+ const clampedPos = clampPosition(localStorage.offset.x, localStorage.offset.y);
+ setDelta({ x: 0, y: 0 });
+ setOffset({ x: clampedPos.x, y: clampedPos.y });
+ setIsPositioned(true); // Ensure positioned when loading from storage
+ }, [uniqueKey, clampPosition]);
+
+ return createPortal(
+
+ {children}
+
,
+ document.getElementById('draggable-windows-container')
+ );
+};
\ No newline at end of file
diff --git a/Coolui v3 test/src/common/draggable-window/DraggableWindowPosition.ts b/Coolui v3 test/src/common/draggable-window/DraggableWindowPosition.ts
new file mode 100644
index 0000000000..476a37ec95
--- /dev/null
+++ b/Coolui v3 test/src/common/draggable-window/DraggableWindowPosition.ts
@@ -0,0 +1,7 @@
+export class DraggableWindowPosition
+{
+ public static CENTER: string = 'DWP_CENTER';
+ public static TOP_CENTER: string = 'DWP_TOP_CENTER';
+ public static TOP_LEFT: string = 'DWP_TOP_LEFT';
+ public static NOTHING: string = 'DWP_NOTHING';
+}
diff --git a/Coolui v3 test/src/common/draggable-window/index.ts b/Coolui v3 test/src/common/draggable-window/index.ts
new file mode 100644
index 0000000000..7672f5270c
--- /dev/null
+++ b/Coolui v3 test/src/common/draggable-window/index.ts
@@ -0,0 +1,2 @@
+export * from './DraggableWindow';
+export * from './DraggableWindowPosition';
diff --git a/Coolui v3 test/src/common/index.ts b/Coolui v3 test/src/common/index.ts
new file mode 100644
index 0000000000..d8c47aefd0
--- /dev/null
+++ b/Coolui v3 test/src/common/index.ts
@@ -0,0 +1,22 @@
+
+export * from './AutoGrid';
+export * from './Base';
+export * from './Button';
+export * from './ButtonGroup';
+export * from './Column';
+export * from './Flex';
+export * from './FormGroup';
+export * from './Grid';
+export * from './GridContext';
+export * from './HorizontalRule';
+export * from './InfiniteScroll';
+export * from './Text';
+export * from './card';
+export * from './card/accordion';
+export * from './card/tabs';
+export * from './draggable-window';
+export * from './layout';
+export * from './layout/limited-edition';
+export * from './types';
+export * from "./Slider";
+export * from './utils';
diff --git a/Coolui v3 test/src/common/layout/LayoutAvatarImageView.tsx b/Coolui v3 test/src/common/layout/LayoutAvatarImageView.tsx
new file mode 100644
index 0000000000..86d589e43c
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutAvatarImageView.tsx
@@ -0,0 +1,103 @@
+import { AvatarScaleType, AvatarSetType, GetAvatarRenderManager } from '@nitrots/nitro-renderer';
+import { CSSProperties, FC, useEffect, useMemo, useRef, useState } from 'react';
+import { Base, BaseProps } from '../Base';
+
+const AVATAR_IMAGE_CACHE: Map = new Map();
+
+export interface LayoutAvatarImageViewProps extends BaseProps
+{
+ figure: string;
+ gender?: string;
+ headOnly?: boolean;
+ direction?: number;
+ scale?: number;
+}
+
+export const LayoutAvatarImageView: FC = props =>
+{
+ const { figure = '', gender = 'M', headOnly = false, direction = 0, scale = 1, classNames = [], style = {}, ...rest } = props;
+ const [ avatarUrl, setAvatarUrl ] = useState(null);
+ const [ isReady, setIsReady ] = useState(false);
+ const isDisposed = useRef(false);
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'avatar-image relative w-[90px] h-[130px] bg-no-repeat bg-[center_-8px] pointer-events-none' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ const getStyle = useMemo(() =>
+ {
+ let newStyle: CSSProperties = {};
+
+ if(avatarUrl && avatarUrl.length) newStyle.backgroundImage = `url('${ avatarUrl }')`;
+
+ if(scale !== 1)
+ {
+ newStyle.transform = `scale(${ scale })`;
+
+ if(!(scale % 1)) newStyle.imageRendering = 'pixelated';
+ }
+
+ if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
+
+ return newStyle;
+ }, [ avatarUrl, scale, style ]);
+
+ useEffect(() =>
+ {
+ if(!isReady) return;
+
+ const figureKey = [ figure, gender, direction, headOnly ].join('-');
+
+ if(AVATAR_IMAGE_CACHE.has(figureKey))
+ {
+ setAvatarUrl(AVATAR_IMAGE_CACHE.get(figureKey));
+ }
+ else
+ {
+ const resetFigure = (_figure: string) =>
+ {
+ if(isDisposed.current) return;
+
+ const avatarImage = GetAvatarRenderManager().createAvatarImage(_figure, AvatarScaleType.LARGE, gender, { resetFigure: (figure: string) => resetFigure(figure), dispose: null, disposed: false });
+
+ let setType = AvatarSetType.FULL;
+
+ if(headOnly) setType = AvatarSetType.HEAD;
+
+ avatarImage.setDirection(setType, direction);
+
+ const imageUrl = avatarImage.processAsImageUrl(setType);
+
+ if(imageUrl && !isDisposed.current)
+ {
+ if(!avatarImage.isPlaceholder()) AVATAR_IMAGE_CACHE.set(figureKey, imageUrl);
+
+ setAvatarUrl(imageUrl);
+ }
+
+ avatarImage.dispose();
+ };
+
+ resetFigure(figure);
+ }
+ }, [ figure, gender, direction, headOnly, isReady ]);
+
+ useEffect(() =>
+ {
+ isDisposed.current = false;
+
+ setIsReady(true);
+
+ return () =>
+ {
+ isDisposed.current = true;
+ };
+ }, []);
+
+ return ;
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutBackgroundImage.tsx b/Coolui v3 test/src/common/layout/LayoutBackgroundImage.tsx
new file mode 100644
index 0000000000..622d9598b1
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutBackgroundImage.tsx
@@ -0,0 +1,23 @@
+import { FC, useMemo } from 'react';
+import { Base, BaseProps } from '../Base';
+
+export interface LayoutBackgroundImageProps extends BaseProps
+{
+ imageUrl?: string;
+}
+
+export const LayoutBackgroundImage: FC = props =>
+{
+ const { imageUrl = null, fit = true, style = null, ...rest } = props;
+
+ const getStyle = useMemo(() =>
+ {
+ const newStyle = { ...style };
+
+ if(imageUrl) newStyle.background = `url(${ imageUrl }) center no-repeat`;
+
+ return newStyle;
+ }, [ style, imageUrl ]);
+
+ return ;
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutBadgeImageView.tsx b/Coolui v3 test/src/common/layout/LayoutBadgeImageView.tsx
new file mode 100644
index 0000000000..c9e0e5e364
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutBadgeImageView.tsx
@@ -0,0 +1,112 @@
+import { BadgeImageReadyEvent, GetEventDispatcher, GetSessionDataManager, NitroSprite, TextureUtils } from '@nitrots/nitro-renderer';
+import { CSSProperties, FC, useEffect, useMemo, useState } from 'react';
+import { GetConfigurationValue, LocalizeBadgeDescription, LocalizeBadgeName, LocalizeText } from '../../api';
+import { Base, BaseProps } from '../Base';
+
+export interface LayoutBadgeImageViewProps extends BaseProps
+{
+ badgeCode: string;
+ isGroup?: boolean;
+ showInfo?: boolean;
+ customTitle?: string;
+ isGrayscale?: boolean;
+ scale?: number;
+}
+
+export const LayoutBadgeImageView: FC = props =>
+{
+ const { badgeCode = null, isGroup = false, showInfo = false, customTitle = null, isGrayscale = false, scale = 1, classNames = [], style = {}, children = null, ...rest } = props;
+ const [ imageElement, setImageElement ] = useState(null);
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'relative w-[40px] h-[40px] bg-no-repeat bg-center z-50' ];
+
+ if(isGroup) newClassNames.push('group-badge');
+
+ if(isGrayscale) newClassNames.push('grayscale');
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames, isGroup, isGrayscale ]);
+
+
+ const getStyle = useMemo(() =>
+ {
+ let newStyle: CSSProperties = {};
+
+ if(imageElement)
+ {
+ newStyle.backgroundImage = `url(${ (isGroup) ? imageElement.src : GetConfigurationValue('badge.asset.url').replace('%badgename%', badgeCode.toString()) })`;
+ newStyle.width = imageElement.width;
+ newStyle.height = imageElement.height;
+ newStyle.zIndex = 50;
+ newStyle.position = 'relative';
+
+ if(scale !== 1)
+ {
+ newStyle.transform = `scale(${ scale })`;
+
+ if(!(scale % 1)) newStyle.imageRendering = 'pixelated';
+
+ newStyle.width = (imageElement.width * scale);
+ newStyle.height = (imageElement.height * scale);
+ }
+ }
+
+ if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
+
+ return newStyle;
+ }, [ badgeCode, isGroup, imageElement, scale, style ]);
+
+ useEffect(() =>
+ {
+ if(!badgeCode || !badgeCode.length) return;
+
+ let didSetBadge = false;
+
+ const onBadgeImageReadyEvent = async (event: BadgeImageReadyEvent) =>
+ {
+ if(event.badgeId !== badgeCode) return;
+
+ const element = await TextureUtils.generateImage(new NitroSprite(event.image));
+
+ console.log ('boe');
+
+ element.onload = () => setImageElement(element);
+
+ didSetBadge = true;
+
+ GetEventDispatcher().removeEventListener(BadgeImageReadyEvent.IMAGE_READY, onBadgeImageReadyEvent);
+ };
+
+ GetEventDispatcher().addEventListener(BadgeImageReadyEvent.IMAGE_READY, onBadgeImageReadyEvent);
+
+ const texture = isGroup ? GetSessionDataManager().getGroupBadgeImage(badgeCode) : GetSessionDataManager().getBadgeImage(badgeCode);
+
+ if(texture && !didSetBadge)
+ {
+ (async () =>
+ {
+ const element = await TextureUtils.generateImage(new NitroSprite(texture));
+
+
+ element.onload = () => setImageElement(element);
+ })();
+ }
+
+ return () => GetEventDispatcher().removeEventListener(BadgeImageReadyEvent.IMAGE_READY, onBadgeImageReadyEvent);
+ }, [ badgeCode, isGroup ]);
+
+ return (
+
+ { (showInfo && GetConfigurationValue('badge.descriptions.enabled', true)) &&
+
+ { isGroup ? customTitle : LocalizeBadgeName(badgeCode) }
+ { isGroup ? LocalizeText('group.badgepopup.body') : LocalizeBadgeDescription(badgeCode) }
+ }
+ { children }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutCounterTimeView.tsx b/Coolui v3 test/src/common/layout/LayoutCounterTimeView.tsx
new file mode 100644
index 0000000000..9b370954c9
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutCounterTimeView.tsx
@@ -0,0 +1,42 @@
+import { FC, useMemo } from 'react';
+import { LocalizeText } from '../../api';
+import { Base, BaseProps } from '../Base';
+
+interface LayoutCounterTimeViewProps extends BaseProps
+{
+ day: string;
+ hour: string;
+ minutes: string;
+ seconds: string;
+}
+
+export const LayoutCounterTimeView: FC = props =>
+{
+ const { day = '00', hour = '00', minutes = '00', seconds = '00', classNames = [], children = null, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'nitro-counter-time' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ return (
+
+
+
{ day != '00' ? day : hour }{ day != '00' ? LocalizeText('countdown_clock_unit_days') : LocalizeText('countdown_clock_unit_hours') }
+
+
:
+
+
{ minutes }{ LocalizeText('countdown_clock_unit_minutes') }
+
+
:
+
+
{ seconds }{ LocalizeText('countdown_clock_unit_seconds') }
+
+ { children }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutCurrencyIcon.tsx b/Coolui v3 test/src/common/layout/LayoutCurrencyIcon.tsx
new file mode 100644
index 0000000000..f311c22047
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutCurrencyIcon.tsx
@@ -0,0 +1,44 @@
+import { CSSProperties, FC, useMemo } from 'react';
+import { GetConfigurationValue } from '../../api';
+import { Base, BaseProps } from '../Base';
+
+export interface CurrencyIconProps extends BaseProps
+{
+ type: number | string;
+}
+
+export const LayoutCurrencyIcon: FC = props =>
+{
+ const { type = '', classNames = [], style = {}, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'nitro-currency-icon', 'bg-center bg-no-repeat w-[15px] h-[15px]' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ const urlString = useMemo(() =>
+ {
+ let url = GetConfigurationValue('currency.asset.icon.url', '');
+
+ url = url.replace('%type%', type.toString());
+
+ return `url(${ url })`;
+ }, [ type ]);
+
+ const getStyle = useMemo(() =>
+ {
+ let newStyle: CSSProperties = {};
+
+ newStyle.backgroundImage = urlString;
+
+ if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
+
+ return newStyle;
+ }, [ style, urlString ]);
+
+ return ;
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutFurniIconImageView.tsx b/Coolui v3 test/src/common/layout/LayoutFurniIconImageView.tsx
new file mode 100644
index 0000000000..b7eaeff7c4
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutFurniIconImageView.tsx
@@ -0,0 +1,17 @@
+import { FC } from 'react';
+import { GetImageIconUrlForProduct } from '../../api';
+import { LayoutImage, LayoutImageProps } from './LayoutImage';
+
+interface LayoutFurniIconImageViewProps extends LayoutImageProps
+{
+ productType: string;
+ productClassId: number;
+ extraData?: string;
+}
+
+export const LayoutFurniIconImageView: FC = props =>
+{
+ const { productType = 's', productClassId = -1, extraData = '', ...rest } = props;
+
+ return ;
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutFurniImageView.tsx b/Coolui v3 test/src/common/layout/LayoutFurniImageView.tsx
new file mode 100644
index 0000000000..b83d81151c
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutFurniImageView.tsx
@@ -0,0 +1,70 @@
+import { GetRoomEngine, IGetImageListener, ImageResult, TextureUtils, Vector3d } from '@nitrots/nitro-renderer';
+import { CSSProperties, FC, useEffect, useMemo, useState } from 'react';
+import { ProductTypeEnum } from '../../api';
+import { Base, BaseProps } from '../Base';
+
+interface LayoutFurniImageViewProps extends BaseProps
+{
+ productType: string;
+ productClassId: number;
+ direction?: number;
+ extraData?: string;
+ scale?: number;
+}
+
+export const LayoutFurniImageView: FC = props =>
+{
+ const { productType = 's', productClassId = -1, direction = 2, extraData = '', scale = 1, style = {}, ...rest } = props;
+ const [ imageElement, setImageElement ] = useState(null);
+
+ const getStyle = useMemo(() =>
+ {
+ let newStyle: CSSProperties = {};
+
+ if(imageElement?.src?.length)
+ {
+ newStyle.backgroundImage = `url('${ imageElement.src }')`;
+ newStyle.width = imageElement.width;
+ newStyle.height = imageElement.height;
+ }
+
+ if(scale !== 1)
+ {
+ newStyle.transform = `scale(${ scale })`;
+
+ if(!(scale % 1)) newStyle.imageRendering = 'pixelated';
+ }
+
+ if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
+
+ return newStyle;
+ }, [ imageElement, scale, style ]);
+
+ useEffect(() =>
+ {
+ let imageResult: ImageResult = null;
+
+ const listener: IGetImageListener = {
+ imageReady: async (id, texture, image) => setImageElement(await TextureUtils.generateImage(texture)),
+ imageFailed: null
+ };
+
+ switch(productType.toLocaleLowerCase())
+ {
+ case ProductTypeEnum.FLOOR:
+ imageResult = GetRoomEngine().getFurnitureFloorImage(productClassId, new Vector3d(direction), 64, listener, 0, extraData);
+ break;
+ case ProductTypeEnum.WALL:
+ imageResult = GetRoomEngine().getFurnitureWallImage(productClassId, new Vector3d(direction), 64, listener, 0, extraData);
+ break;
+ }
+
+ if(!imageResult) return;
+
+ (async () => setImageElement(await TextureUtils.generateImage(imageResult.data)))();
+ }, [ productType, productClassId, direction, extraData ]);
+
+ if(!imageElement) return null;
+
+ return ;
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutGiftTagView.tsx b/Coolui v3 test/src/common/layout/LayoutGiftTagView.tsx
new file mode 100644
index 0000000000..75004ec42f
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutGiftTagView.tsx
@@ -0,0 +1,41 @@
+import { FC } from 'react';
+import { LocalizeText } from '../../api';
+import { Column } from '../Column';
+import { Flex } from '../Flex';
+import { Text } from '../Text';
+import { LayoutAvatarImageView } from './LayoutAvatarImageView';
+
+interface LayoutGiftTagViewProps
+{
+ figure?: string;
+ userName?: string;
+ message?: string;
+ editable?: boolean;
+ onChange?: (value: string) => void;
+}
+
+export const LayoutGiftTagView: FC = props =>
+{
+ const { figure = null, userName = null, message = null, editable = false, onChange = null } = props;
+
+ return (
+
+
+ { !userName &&
}
+ { figure &&
+
+
}
+
+
+
+ { !editable &&
+ { message } }
+ { editable && (onChange !== null) &&
+ }
+ { userName &&
+ { LocalizeText('catalog.gift_wrapping_new.message_from', [ 'name' ], [ userName ]) } }
+
+
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutGridItem.tsx b/Coolui v3 test/src/common/layout/LayoutGridItem.tsx
new file mode 100644
index 0000000000..5bf73eae58
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutGridItem.tsx
@@ -0,0 +1,76 @@
+import { FC, useMemo } from 'react';
+import { Base } from '../Base';
+import { Column, ColumnProps } from '../Column';
+import { LayoutItemCountView } from './LayoutItemCountView';
+import { LayoutLimitedEditionStyledNumberView } from './limited-edition';
+
+export interface LayoutGridItemProps extends ColumnProps
+{
+ itemImage?: string;
+ itemColor?: string;
+ itemActive?: boolean;
+ itemCount?: number;
+ itemCountMinimum?: number;
+ itemUniqueSoldout?: boolean;
+ itemUniqueNumber?: number;
+ itemUnseen?: boolean;
+ itemHighlight?: boolean;
+ disabled?: boolean;
+}
+
+export const LayoutGridItem: FC = props =>
+{
+ const { itemImage = undefined, itemColor = undefined, itemActive = false, itemCount = 1, itemCountMinimum = 1, itemUniqueSoldout = false, itemUniqueNumber = -2, itemUnseen = false, itemHighlight = false, disabled = false, center = true, column = true, style = {}, classNames = [], position = 'relative', overflow = 'hidden', children = null, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'layout-grid-item', 'border', 'border-2', 'border-muted', 'rounded' ];
+
+
+ if(itemActive) newClassNames.push('!bg-[#ececec] !border-[#fff]');
+
+ if(itemUniqueSoldout || (itemUniqueNumber > 0)) newClassNames.push('unique-item');
+
+ if(itemUniqueSoldout) newClassNames.push('sold-out');
+
+ if(itemUnseen) newClassNames.push('unseen');
+
+ if(itemHighlight) newClassNames.push('has-highlight');
+
+ if(disabled) newClassNames.push('disabled');
+
+ if(itemImage === null) newClassNames.push('icon', 'loading-icon');
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ itemActive, itemUniqueSoldout, itemUniqueNumber, itemUnseen, itemHighlight, disabled, itemImage, classNames ]);
+
+ const getStyle = useMemo(() =>
+ {
+ let newStyle = { ...style };
+
+ if(itemImage && !(itemUniqueSoldout || (itemUniqueNumber > 0))) newStyle.backgroundImage = `url(${ itemImage })`;
+
+ if(itemColor) newStyle.backgroundColor = itemColor;
+
+ if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
+
+ return newStyle;
+ }, [ style, itemImage, itemColor, itemUniqueSoldout, itemUniqueNumber ]);
+
+ return (
+
+ { (itemCount > itemCountMinimum) &&
+ }
+ { (itemUniqueNumber > 0) &&
+ <>
+
+
+
+
+ > }
+ { children }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutImage.tsx b/Coolui v3 test/src/common/layout/LayoutImage.tsx
new file mode 100644
index 0000000000..f3db3bdc6d
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutImage.tsx
@@ -0,0 +1,13 @@
+import { DetailedHTMLProps, FC, HTMLAttributes } from 'react';
+
+export interface LayoutImageProps extends DetailedHTMLProps, HTMLImageElement>
+{
+ imageUrl?: string;
+}
+
+export const LayoutImage: FC = props =>
+{
+ const { imageUrl = null, className = '', ...rest } = props;
+
+ return
;
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutItemCountView.tsx b/Coolui v3 test/src/common/layout/LayoutItemCountView.tsx
new file mode 100644
index 0000000000..5b14ed54ba
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutItemCountView.tsx
@@ -0,0 +1,28 @@
+import { FC, useMemo } from 'react';
+import { Base, BaseProps } from '../Base';
+
+interface LayoutItemCountViewProps extends BaseProps
+{
+ count: number;
+}
+
+export const LayoutItemCountView: FC = props =>
+{
+ const { count = 0, position = 'absolute', classNames = [], children = null, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'inline-block px-[.65em] py-[.35em] text-[.75em] font-bold leading-none text-[#fff] text-center whitespace-nowrap align-baseline rounded-[.25rem]', '!border-[1px] !border-[solid] !border-[#283F5D]', 'border-black', 'bg-danger', 'px-1', 'top-[2px] right-[2px] text-[9.5px] px-[3px] py-[2px] ' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ return (
+
+ { count }
+ { children }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutLoadingSpinnerView.tsx b/Coolui v3 test/src/common/layout/LayoutLoadingSpinnerView.tsx
new file mode 100644
index 0000000000..1d2641f679
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutLoadingSpinnerView.tsx
@@ -0,0 +1,15 @@
+import { FC } from 'react';
+import { Base, BaseProps } from '../Base';
+
+export const LayoutLoadingSpinnerView: FC> = props =>
+{
+ const { ...rest } = props;
+
+ return (
+
+
+
+
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutMiniCameraView.tsx b/Coolui v3 test/src/common/layout/LayoutMiniCameraView.tsx
new file mode 100644
index 0000000000..70962bbf93
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutMiniCameraView.tsx
@@ -0,0 +1,73 @@
+import { GetRoomEngine, NitroRectangle, NitroTexture } from '@nitrots/nitro-renderer';
+import { FC, useRef } from 'react';
+import { LocalizeText, PlaySound, SoundNames } from '../../api';
+import { DraggableWindow } from '../draggable-window';
+
+interface LayoutMiniCameraViewProps {
+ roomId: number;
+ textureReceiver: (texture: NitroTexture) => Promise;
+ onClose: () => void;
+}
+
+export const LayoutMiniCameraView: FC = props => {
+ const { roomId = -1, textureReceiver = null, onClose = null } = props;
+ const elementRef = useRef();
+
+ const getCameraBounds = () => {
+ if (!elementRef || !elementRef.current) return null;
+
+ const frameBounds = elementRef.current.getBoundingClientRect();
+
+ return new NitroRectangle(
+ Math.floor(frameBounds.x),
+ Math.floor(frameBounds.y),
+ Math.floor(frameBounds.width),
+ Math.floor(frameBounds.height)
+ );
+ };
+
+ const takePicture = () => {
+ PlaySound(SoundNames.CAMERA_SHUTTER);
+ textureReceiver(GetRoomEngine().createTextureFromRoom(roomId, 1, getCameraBounds()));
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/Coolui v3 test/src/common/layout/LayoutNotificationAlertView.tsx b/Coolui v3 test/src/common/layout/LayoutNotificationAlertView.tsx
new file mode 100644
index 0000000000..b1cdf53576
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutNotificationAlertView.tsx
@@ -0,0 +1,35 @@
+import { FC, useMemo } from 'react';
+import { NotificationAlertType } from '../../api';
+import { NitroCardContentView, NitroCardHeaderView, NitroCardView, NitroCardViewProps } from '../card';
+
+export interface LayoutNotificationAlertViewProps extends NitroCardViewProps
+{
+ title?: string;
+ type?: string;
+ onClose: () => void;
+}
+
+export const LayoutNotificationAlertView: FC = props =>
+{
+ const { title = '', onClose = null, classNames = [], children = null,type = NotificationAlertType.DEFAULT, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'nitro-alert' ];
+
+ newClassNames.push('nitro-alert-' + type);
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames, type ]);
+
+ return (
+
+
+
+ { children }
+
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutNotificationBubbleView.tsx b/Coolui v3 test/src/common/layout/LayoutNotificationBubbleView.tsx
new file mode 100644
index 0000000000..0b62a84bb9
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutNotificationBubbleView.tsx
@@ -0,0 +1,58 @@
+import { AnimatePresence, motion } from 'framer-motion';
+import { FC, useEffect, useMemo, useState } from 'react';
+import { Flex, FlexProps } from '../Flex';
+
+export interface LayoutNotificationBubbleViewProps extends FlexProps
+{
+ fadesOut?: boolean;
+ timeoutMs?: number;
+ onClose: () => void;
+}
+
+export const LayoutNotificationBubbleView: FC = props =>
+{
+ const { fadesOut = true, timeoutMs = 8000, onClose = null, overflow = 'hidden', classNames = [], ...rest } = props;
+ const [ isVisible, setIsVisible ] = useState(false);
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'text-sm bg-[#1c1c20f2] px-[5px] py-[6px] [box-shadow:inset_0_5px_#22222799,_inset_0_-4px_#12121599] ', 'rounded' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ useEffect(() =>
+ {
+ setIsVisible(true);
+
+ return () => setIsVisible(false);
+ }, []);
+
+ useEffect(() =>
+ {
+ if(!fadesOut) return;
+
+ const timeout = setTimeout(() =>
+ {
+ setIsVisible(false);
+
+ setTimeout(() => onClose(), 300);
+ }, timeoutMs);
+
+ return () => clearTimeout(timeout);
+ }, [ fadesOut, timeoutMs, onClose ]);
+
+ return (
+
+ { isVisible &&
+
+
+ }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutPetImageView.tsx b/Coolui v3 test/src/common/layout/LayoutPetImageView.tsx
new file mode 100644
index 0000000000..acf1a7990a
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutPetImageView.tsx
@@ -0,0 +1,123 @@
+import { GetRoomEngine, IPetCustomPart, PetFigureData, TextureUtils, Vector3d } from '@nitrots/nitro-renderer';
+import { CSSProperties, FC, useEffect, useMemo, useRef, useState } from 'react';
+import { Base, BaseProps } from '../Base';
+
+interface LayoutPetImageViewProps extends BaseProps
+{
+ figure?: string;
+ typeId?: number;
+ paletteId?: number;
+ petColor?: number;
+ customParts?: IPetCustomPart[];
+ posture?: string;
+ headOnly?: boolean;
+ direction?: number;
+ scale?: number;
+}
+
+export const LayoutPetImageView: FC = props =>
+{
+ const { figure = '', typeId = -1, paletteId = -1, petColor = 0xFFFFFF, customParts = [], posture = 'std', headOnly = false, direction = 0, scale = 1, style = {}, ...rest } = props;
+ const [ petUrl, setPetUrl ] = useState(null);
+ const [ width, setWidth ] = useState(0);
+ const [ height, setHeight ] = useState(0);
+ const isDisposed = useRef(false);
+
+ const getStyle = useMemo(() =>
+ {
+ let newStyle: CSSProperties = {};
+
+ if(petUrl && petUrl.length) newStyle.backgroundImage = `url(${ petUrl })`;
+
+ if(scale !== 1)
+ {
+ newStyle.transform = `scale(${ scale })`;
+
+ if(!(scale % 1)) newStyle.imageRendering = 'pixelated';
+ }
+
+ newStyle.width = width;
+ newStyle.height = height;
+
+ if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
+
+ return newStyle;
+ }, [ petUrl, scale, style, width, height ]);
+
+ useEffect(() =>
+ {
+ let url = null;
+
+ let petTypeId = typeId;
+ let petPaletteId = paletteId;
+ let petColor1 = petColor;
+ let petCustomParts: IPetCustomPart[] = customParts;
+ let petHeadOnly = headOnly;
+
+ if(figure && figure.length)
+ {
+ const petFigureData = new PetFigureData(figure);
+
+ petTypeId = petFigureData.typeId;
+ petPaletteId = petFigureData.paletteId;
+ petColor1 = petFigureData.color;
+ petCustomParts = petFigureData.customParts;
+ }
+
+ if(petTypeId === 16) petHeadOnly = false;
+
+ const imageResult = GetRoomEngine().getRoomObjectPetImage(petTypeId, petPaletteId, petColor1, new Vector3d((direction * 45)), 64, {
+ imageReady: async (id, texture, image) =>
+ {
+ if(isDisposed.current) return;
+
+ if(image)
+ {
+ setPetUrl(image.src);
+ setWidth(image.width);
+ setHeight(image.height);
+ }
+
+ else if(texture)
+ {
+ setPetUrl(await TextureUtils.generateImageUrl(texture));
+ setWidth(texture.width);
+ setHeight(texture.height);
+ }
+ },
+ imageFailed: (id) =>
+ {
+
+ }
+ }, petHeadOnly, 0, petCustomParts, posture);
+
+ if(imageResult)
+ {
+ (async () =>
+ {
+ const image = await imageResult.getImage();
+
+ if(image)
+ {
+ setPetUrl(image.src);
+ setWidth(image.width);
+ setHeight(image.height);
+ }
+ })();
+ }
+ }, [ figure, typeId, paletteId, petColor, customParts, posture, headOnly, direction ]);
+
+ useEffect(() =>
+ {
+ isDisposed.current = false;
+
+ return () =>
+ {
+ isDisposed.current = true;
+ };
+ }, []);
+
+ const url = `url('${ petUrl }')`;
+
+ return ;
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutPrizeProductImageView.tsx b/Coolui v3 test/src/common/layout/LayoutPrizeProductImageView.tsx
new file mode 100644
index 0000000000..a83861e8f4
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutPrizeProductImageView.tsx
@@ -0,0 +1,30 @@
+import { FC } from 'react';
+import { ProductTypeEnum } from '../../api';
+import { LayoutBadgeImageView } from './LayoutBadgeImageView';
+import { LayoutCurrencyIcon } from './LayoutCurrencyIcon';
+import { LayoutFurniImageView } from './LayoutFurniImageView';
+
+interface LayoutPrizeProductImageViewProps
+{
+ productType: string;
+ classId: number;
+ extraParam?: string;
+}
+
+export const LayoutPrizeProductImageView: FC = props =>
+{
+ const { productType = ProductTypeEnum.FLOOR, classId = -1, extraParam = undefined } = props;
+
+ switch(productType)
+ {
+ case ProductTypeEnum.WALL:
+ case ProductTypeEnum.FLOOR:
+ return ;
+ case ProductTypeEnum.BADGE:
+ return ;
+ case ProductTypeEnum.HABBO_CLUB:
+ return ;
+ }
+
+ return null;
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutProgressBar.tsx b/Coolui v3 test/src/common/layout/LayoutProgressBar.tsx
new file mode 100644
index 0000000000..dbbd8f939b
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutProgressBar.tsx
@@ -0,0 +1,32 @@
+import { FC, useMemo } from 'react';
+import { Base, Column, ColumnProps, Flex } from '..';
+
+interface LayoutProgressBarProps extends ColumnProps
+{
+ text?: string;
+ progress: number;
+ maxProgress?: number;
+}
+
+export const LayoutProgressBar: FC = props =>
+{
+ const { text = '', progress = 0, maxProgress = 100, position = 'relative', justifyContent = 'center', classNames = [], children = null, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'border-[1px] border-[solid] border-[#fff] p-[2px] h-[20px] rounded-[.25rem] overflow-hidden bg-[#1E7295] ', 'text-white' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ return (
+
+ { text && (text.length > 0) &&
+ { text } }
+
+ { children }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutRarityLevelView.tsx b/Coolui v3 test/src/common/layout/LayoutRarityLevelView.tsx
new file mode 100644
index 0000000000..f6711aae7c
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutRarityLevelView.tsx
@@ -0,0 +1,28 @@
+import { FC, useMemo } from 'react';
+import { Base, BaseProps } from '../Base';
+
+interface LayoutRarityLevelViewProps extends BaseProps
+{
+ level: number;
+}
+
+export const LayoutRarityLevelView: FC = props =>
+{
+ const { level = 0, classNames = [], children = null, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'nitro-rarity-level' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ return (
+
+ { level }
+ { children }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutRoomObjectImageView.tsx b/Coolui v3 test/src/common/layout/LayoutRoomObjectImageView.tsx
new file mode 100644
index 0000000000..01328afae9
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutRoomObjectImageView.tsx
@@ -0,0 +1,59 @@
+import { GetRoomEngine, TextureUtils, Vector3d } from '@nitrots/nitro-renderer';
+import { CSSProperties, FC, useEffect, useMemo, useState } from 'react';
+import { Base, BaseProps } from '../Base';
+
+interface LayoutRoomObjectImageViewProps extends BaseProps
+{
+ roomId: number;
+ objectId: number;
+ category: number;
+ direction?: number;
+ scale?: number;
+}
+
+export const LayoutRoomObjectImageView: FC = props =>
+{
+ const { roomId = -1, objectId = 1, category = -1, direction = 2, scale = 1, style = {}, ...rest } = props;
+ const [ imageElement, setImageElement ] = useState(null);
+
+ const getStyle = useMemo(() =>
+ {
+ let newStyle: CSSProperties = {};
+
+ if(imageElement?.src?.length)
+ {
+ newStyle.backgroundImage = `url('${ imageElement.src }')`;
+ newStyle.width = imageElement.width;
+ newStyle.height = imageElement.height;
+ }
+
+ if(scale !== 1)
+ {
+ newStyle.transform = `scale(${ scale })`;
+
+ if(!(scale % 1)) newStyle.imageRendering = 'pixelated';
+ }
+
+ if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
+
+ return newStyle;
+ }, [ imageElement, scale, style ]);
+
+ useEffect(() =>
+ {
+ const imageResult = GetRoomEngine().getRoomObjectImage(roomId, objectId, category, new Vector3d(direction * 45), 64, {
+ imageReady: async (id, texture, image) => setImageElement(await TextureUtils.generateImage(texture)),
+ imageFailed: null
+ });
+
+ // needs (roomObjectImage.data.width > 140) || (roomObjectImage.data.height > 200) scale 1
+
+ if(!imageResult) return;
+
+ (async () => setImageElement(await TextureUtils.generateImage(imageResult.data)))();
+ }, [ roomId, objectId, category, direction, scale ]);
+
+ if(!imageElement) return null;
+
+ return ;
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutRoomPreviewerView.tsx b/Coolui v3 test/src/common/layout/LayoutRoomPreviewerView.tsx
new file mode 100644
index 0000000000..daceeef08f
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutRoomPreviewerView.tsx
@@ -0,0 +1,89 @@
+import { GetRenderer, GetTicker, NitroTicker, RoomPreviewer, TextureUtils } from '@nitrots/nitro-renderer';
+import { FC, MouseEvent, useEffect, useRef } from 'react';
+
+export const LayoutRoomPreviewerView: FC<{
+ roomPreviewer: RoomPreviewer;
+ height?: number;
+}> = props =>
+{
+ const { roomPreviewer = null, height = 0 } = props;
+ const elementRef = useRef();
+
+ const onClick = (event: MouseEvent) =>
+ {
+ if(!roomPreviewer) return;
+
+ if(event.shiftKey) roomPreviewer.changeRoomObjectDirection();
+ else roomPreviewer.changeRoomObjectState();
+ };
+
+ useEffect(() =>
+ {
+ if(!elementRef) return;
+
+ const width = elementRef.current.parentElement.clientWidth;
+ const texture = TextureUtils.createRenderTexture(width, height);
+
+ const update = async (ticker: NitroTicker) =>
+ {
+ if(!roomPreviewer || !elementRef.current) return;
+
+ roomPreviewer.updatePreviewRoomView();
+
+ const renderingCanvas = roomPreviewer.getRenderingCanvas();
+
+ if(!renderingCanvas.canvasUpdated) return;
+
+ GetRenderer().render({
+ target: texture,
+ container: renderingCanvas.master,
+ clear: true
+ });
+
+ let canvas = GetRenderer().texture.generateCanvas(texture);
+ const base64 = canvas.toDataURL('image/png');
+
+ canvas = null;
+
+ elementRef.current.style.backgroundImage = `url(${ base64 })`;
+ };
+
+ GetTicker().add(update);
+
+ const resizeObserver = new ResizeObserver(() =>
+ {
+ if(!roomPreviewer || !elementRef.current) return;
+
+ const width = elementRef.current.parentElement.offsetWidth;
+
+ roomPreviewer.modifyRoomCanvas(width, height);
+
+ update(GetTicker());
+ });
+
+ roomPreviewer.getRoomCanvas(width, height);
+
+ resizeObserver.observe(elementRef.current);
+
+ return () =>
+ {
+ GetTicker().remove(update);
+
+ resizeObserver.disconnect();
+
+ texture.destroy(true);
+ };
+ }, [ roomPreviewer, elementRef, height ]);
+
+ return (
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutRoomThumbnailView.tsx b/Coolui v3 test/src/common/layout/LayoutRoomThumbnailView.tsx
new file mode 100644
index 0000000000..d6626ff9ea
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutRoomThumbnailView.tsx
@@ -0,0 +1,37 @@
+import { FC, useMemo } from 'react';
+import { GetConfigurationValue } from '../../api';
+import { Base, BaseProps } from '../Base';
+
+export interface LayoutRoomThumbnailViewProps extends BaseProps
+{
+ roomId?: number;
+ customUrl?: string;
+}
+
+export const LayoutRoomThumbnailView: FC = props =>
+{
+ const { roomId = -1, customUrl = null, shrink = true, overflow = 'hidden', classNames = [], children = null, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'relative w-[110px] h-[110px] bg-[url("@/assets/images/navigator/thumbnail_placeholder.png")] bg-no-repeat bg-center', 'rounded', '!border-[1px] !border-[solid] !border-[#283F5D]' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ const getImageUrl = useMemo(() =>
+ {
+ if(customUrl && customUrl.length) return (GetConfigurationValue('image.library.url') + customUrl);
+
+ return (GetConfigurationValue('thumbnails.url').replace('%thumbnail%', roomId.toString()));
+ }, [ customUrl, roomId ]);
+
+ return (
+
+ { getImageUrl &&
}
+ { children }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/LayoutTrophyView.tsx b/Coolui v3 test/src/common/layout/LayoutTrophyView.tsx
new file mode 100644
index 0000000000..dd2c284f56
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/LayoutTrophyView.tsx
@@ -0,0 +1,42 @@
+import { FC } from 'react';
+import { LocalizeText } from '../../api';
+import { Base } from '../Base';
+import { Column } from '../Column';
+import { Flex } from '../Flex';
+import { Text } from '../Text';
+import { DraggableWindow } from '../draggable-window';
+
+interface LayoutTrophyViewProps
+{
+ color: string;
+ message: string;
+ date: string;
+ senderName: string;
+ customTitle?: string;
+ onCloseClick: () => void;
+}
+
+export const LayoutTrophyView: FC = props =>
+{
+ const { color = '', message = '', date = '', senderName = '', customTitle = null, onCloseClick = null } = props;
+
+ return (
+
+
+
+
+ { LocalizeText('widget.furni.trophy.title') }
+
+
+ { customTitle &&
+ { customTitle } }
+ { message }
+
+
+ { date }
+ { senderName }
+
+
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/UserProfileIconView.tsx b/Coolui v3 test/src/common/layout/UserProfileIconView.tsx
new file mode 100644
index 0000000000..8898c7065b
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/UserProfileIconView.tsx
@@ -0,0 +1,29 @@
+import { FC, useMemo } from 'react';
+import { GetUserProfile } from '../../api';
+import { Base, BaseProps } from '../Base';
+
+export interface UserProfileIconViewProps extends BaseProps
+{
+ userId?: number;
+ userName?: string;
+}
+
+export const UserProfileIconView: FC = props =>
+{
+ const { userId = 0, userName = null, classNames = [], pointer = true, children = null, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'bg-[url("@/assets/images/friends/friends-spritesheet.png")]', 'w-[13px] h-[11px] bg-[-51px_-91px]' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ return (
+ GetUserProfile(userId) } { ...rest }>
+ { children }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/index.ts b/Coolui v3 test/src/common/layout/index.ts
new file mode 100644
index 0000000000..cbb056854a
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/index.ts
@@ -0,0 +1,24 @@
+export * from './LayoutAvatarImageView';
+export * from './LayoutBackgroundImage';
+export * from './LayoutBadgeImageView';
+export * from './LayoutCounterTimeView';
+export * from './LayoutCurrencyIcon';
+export * from './LayoutFurniIconImageView';
+export * from './LayoutFurniImageView';
+export * from './LayoutGiftTagView';
+export * from './LayoutGridItem';
+export * from './LayoutImage';
+export * from './LayoutItemCountView';
+export * from './LayoutLoadingSpinnerView';
+export * from './LayoutMiniCameraView';
+export * from './LayoutNotificationAlertView';
+export * from './LayoutNotificationBubbleView';
+export * from './LayoutPetImageView';
+export * from './LayoutProgressBar';
+export * from './LayoutRarityLevelView';
+export * from './LayoutRoomObjectImageView';
+export * from './LayoutRoomPreviewerView';
+export * from './LayoutRoomThumbnailView';
+export * from './LayoutTrophyView';
+export * from './UserProfileIconView';
+export * from './limited-edition';
diff --git a/Coolui v3 test/src/common/layout/limited-edition/LayoutLimitedEditionCompactPlateView.tsx b/Coolui v3 test/src/common/layout/limited-edition/LayoutLimitedEditionCompactPlateView.tsx
new file mode 100644
index 0000000000..86fc9772a7
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/limited-edition/LayoutLimitedEditionCompactPlateView.tsx
@@ -0,0 +1,35 @@
+import { FC, useMemo } from 'react';
+import { Base, BaseProps } from '../../Base';
+import { LayoutLimitedEditionStyledNumberView } from './LayoutLimitedEditionStyledNumberView';
+
+interface LayoutLimitedEditionCompactPlateViewProps extends BaseProps
+{
+ uniqueNumber: number;
+ uniqueSeries: number;
+}
+
+export const LayoutLimitedEditionCompactPlateView: FC = props =>
+{
+ const { uniqueNumber = 0, uniqueSeries = 0, classNames = [], children = null, ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'unique-compact-plate', 'z-index-1' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ return (
+
+
+
+
+
+
+
+ { children }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/limited-edition/LayoutLimitedEditionCompletePlateView.tsx b/Coolui v3 test/src/common/layout/limited-edition/LayoutLimitedEditionCompletePlateView.tsx
new file mode 100644
index 0000000000..5bcf776fac
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/limited-edition/LayoutLimitedEditionCompletePlateView.tsx
@@ -0,0 +1,40 @@
+import { FC, useMemo } from 'react';
+import { LocalizeText } from '../../../api';
+import { Base, BaseProps } from '../../Base';
+import { Column } from '../../Column';
+import { LayoutLimitedEditionStyledNumberView } from './LayoutLimitedEditionStyledNumberView';
+
+interface LayoutLimitedEditionCompletePlateViewProps extends BaseProps
+{
+ uniqueLimitedItemsLeft: number;
+ uniqueLimitedSeriesSize: number;
+}
+
+export const LayoutLimitedEditionCompletePlateView: FC = props =>
+{
+ const { uniqueLimitedItemsLeft = 0, uniqueLimitedSeriesSize = 0, classNames = [], ...rest } = props;
+
+ const getClassNames = useMemo(() =>
+ {
+ const newClassNames: string[] = [ 'unique-complete-plate' ];
+
+ if(classNames.length) newClassNames.push(...classNames);
+
+ return newClassNames;
+ }, [ classNames ]);
+
+ return (
+
+
+
+ { LocalizeText('unique.items.left') }
+
+
+
+ { LocalizeText('unique.items.number.sold') }
+
+
+
+
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/limited-edition/LayoutLimitedEditionStyledNumberView.tsx b/Coolui v3 test/src/common/layout/limited-edition/LayoutLimitedEditionStyledNumberView.tsx
new file mode 100644
index 0000000000..992ad09dbd
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/limited-edition/LayoutLimitedEditionStyledNumberView.tsx
@@ -0,0 +1,18 @@
+import { FC } from 'react';
+
+interface LayoutLimitedEditionStyledNumberViewProps
+{
+ value: number;
+}
+
+export const LayoutLimitedEditionStyledNumberView: FC = props =>
+{
+ const { value = 0 } = props;
+ const numbers = value.toString().split('');
+
+ return (
+ <>
+ { numbers.map((number, index) => ) }
+ >
+ );
+};
diff --git a/Coolui v3 test/src/common/layout/limited-edition/index.ts b/Coolui v3 test/src/common/layout/limited-edition/index.ts
new file mode 100644
index 0000000000..ee41cf9f96
--- /dev/null
+++ b/Coolui v3 test/src/common/layout/limited-edition/index.ts
@@ -0,0 +1,3 @@
+export * from './LayoutLimitedEditionCompactPlateView';
+export * from './LayoutLimitedEditionCompletePlateView';
+export * from './LayoutLimitedEditionStyledNumberView';
diff --git a/Coolui v3 test/src/common/transitions/TransitionAnimation.tsx b/Coolui v3 test/src/common/transitions/TransitionAnimation.tsx
new file mode 100644
index 0000000000..8e3484970b
--- /dev/null
+++ b/Coolui v3 test/src/common/transitions/TransitionAnimation.tsx
@@ -0,0 +1,52 @@
+import { FC, ReactNode, useEffect, useState } from 'react';
+import { Transition } from 'react-transition-group';
+import { getTransitionAnimationStyle } from './TransitionAnimationStyles';
+
+interface TransitionAnimationProps
+{
+ type: string;
+ inProp: boolean;
+ timeout?: number;
+ className?: string;
+ children?: ReactNode;
+}
+
+export const TransitionAnimation: FC = props =>
+{
+ const { type = null, inProp = false, timeout = 300, className = null, children = null } = props;
+
+ const [ isChildrenVisible, setChildrenVisible ] = useState(false);
+
+ useEffect(() =>
+ {
+ let timeoutData: ReturnType = null;
+
+ if(inProp)
+ {
+ setChildrenVisible(true);
+ }
+ else
+ {
+ timeoutData = setTimeout(() =>
+ {
+ setChildrenVisible(false);
+ clearTimeout(timeout);
+ }, timeout);
+ }
+
+ return () =>
+ {
+ if(timeoutData) clearTimeout(timeoutData);
+ };
+ }, [ inProp, timeout ]);
+
+ return (
+
+ { state => (
+
+ { isChildrenVisible && children }
+
+ ) }
+
+ );
+};
diff --git a/Coolui v3 test/src/common/transitions/TransitionAnimationStyles.ts b/Coolui v3 test/src/common/transitions/TransitionAnimationStyles.ts
new file mode 100644
index 0000000000..feebdccad1
--- /dev/null
+++ b/Coolui v3 test/src/common/transitions/TransitionAnimationStyles.ts
@@ -0,0 +1,136 @@
+import { CSSProperties } from 'react';
+import { TransitionStatus } from 'react-transition-group';
+import { ENTERING, EXITING } from 'react-transition-group/Transition';
+import { TransitionAnimationTypes } from './TransitionAnimationTypes';
+
+export function getTransitionAnimationStyle(type: string, transition: TransitionStatus, timeout: number = 300): Partial
+{
+ switch(type)
+ {
+ case TransitionAnimationTypes.BOUNCE:
+ switch(transition)
+ {
+ default:
+ return {};
+ case ENTERING:
+ return {
+ animationName: 'bounceIn',
+ animationDuration: `${ timeout }ms`
+ };
+ case EXITING:
+ return {
+ animationName: 'bounceOut',
+ animationDuration: `${ timeout }ms`
+ };
+ }
+ case TransitionAnimationTypes.SLIDE_LEFT:
+ switch(transition)
+ {
+ default:
+ return {};
+ case ENTERING:
+ return {
+ animationName: 'slideInLeft',
+ animationDuration: `${ timeout }ms`
+ };
+ case EXITING:
+ return {
+ animationName: 'slideOutLeft',
+ animationDuration: `${ timeout }ms`
+ };
+ }
+ case TransitionAnimationTypes.SLIDE_RIGHT:
+ switch(transition)
+ {
+ default:
+ return {};
+ case ENTERING:
+ return {
+ animationName: 'slideInRight',
+ animationDuration: `${ timeout }ms`
+ };
+ case EXITING:
+ return {
+ animationName: 'slideOutRight',
+ animationDuration: `${ timeout }ms`
+ };
+ }
+ case TransitionAnimationTypes.FLIP_X:
+ switch(transition)
+ {
+ default:
+ return {};
+ case ENTERING:
+ return {
+ animationName: 'flipInX',
+ animationDuration: `${ timeout }ms`
+ };
+ case EXITING:
+ return {
+ animationName: 'flipOutX',
+ animationDuration: `${ timeout }ms`
+ };
+ }
+ case TransitionAnimationTypes.FADE_UP:
+ switch(transition)
+ {
+ default:
+ return {};
+ case ENTERING:
+ return {
+ animationName: 'fadeInUp',
+ animationDuration: `${ timeout }ms`
+ };
+ case EXITING:
+ return {
+ animationName: 'fadeOutDown',
+ animationDuration: `${ timeout }ms`
+ };
+ }
+ case TransitionAnimationTypes.FADE_IN:
+ switch(transition)
+ {
+ default:
+ return {};
+ case ENTERING:
+ return {
+ animationName: 'fadeIn',
+ animationDuration: `${ timeout }ms`
+ };
+ case EXITING:
+ return {
+ animationName: 'fadeOut',
+ animationDuration: `${ timeout }ms`
+ };
+ }
+ case TransitionAnimationTypes.FADE_DOWN:
+ switch(transition)
+ {
+ default:
+ return {};
+ case ENTERING:
+ return {
+ animationName: 'fadeInDown',
+ animationDuration: `${ timeout }ms`
+ };
+ case EXITING:
+ return {
+ animationName: 'fadeOutUp',
+ animationDuration: `${ timeout }ms`
+ };
+ }
+ case TransitionAnimationTypes.HEAD_SHAKE:
+ switch(transition)
+ {
+ default:
+ return {};
+ case ENTERING:
+ return {
+ animationName: 'headShake',
+ animationDuration: `${ timeout }ms`
+ };
+ }
+ }
+
+ return null;
+}
diff --git a/Coolui v3 test/src/common/transitions/TransitionAnimationTypes.ts b/Coolui v3 test/src/common/transitions/TransitionAnimationTypes.ts
new file mode 100644
index 0000000000..4ecc23be1f
--- /dev/null
+++ b/Coolui v3 test/src/common/transitions/TransitionAnimationTypes.ts
@@ -0,0 +1,11 @@
+export class TransitionAnimationTypes
+{
+ public static BOUNCE: string = 'bounce';
+ public static SLIDE_LEFT: string = 'slideLeft';
+ public static SLIDE_RIGHT: string = 'slideRight';
+ public static FLIP_X: string = 'flipX';
+ public static FADE_IN: string = 'fadeIn';
+ public static FADE_DOWN: string = 'fadeDown';
+ public static FADE_UP: string = 'fadeUp';
+ public static HEAD_SHAKE: string = 'headShake';
+}
diff --git a/Coolui v3 test/src/common/transitions/index.ts b/Coolui v3 test/src/common/transitions/index.ts
new file mode 100644
index 0000000000..283a00587a
--- /dev/null
+++ b/Coolui v3 test/src/common/transitions/index.ts
@@ -0,0 +1,3 @@
+export * from './TransitionAnimation';
+export * from './TransitionAnimationStyles';
+export * from './TransitionAnimationTypes';
diff --git a/Coolui v3 test/src/common/types/AlignItemType.ts b/Coolui v3 test/src/common/types/AlignItemType.ts
new file mode 100644
index 0000000000..5a61476df1
--- /dev/null
+++ b/Coolui v3 test/src/common/types/AlignItemType.ts
@@ -0,0 +1 @@
+export type AlignItemType = 'start' | 'end' | 'center' | 'baseline' | 'stretch';
diff --git a/Coolui v3 test/src/common/types/AlignSelfType.ts b/Coolui v3 test/src/common/types/AlignSelfType.ts
new file mode 100644
index 0000000000..8e26378bda
--- /dev/null
+++ b/Coolui v3 test/src/common/types/AlignSelfType.ts
@@ -0,0 +1 @@
+export type AlignSelfType = 'start' | 'end' | 'center' | 'baseline' | 'stretch';
diff --git a/Coolui v3 test/src/common/types/ButtonSizeType.ts b/Coolui v3 test/src/common/types/ButtonSizeType.ts
new file mode 100644
index 0000000000..a520c885b8
--- /dev/null
+++ b/Coolui v3 test/src/common/types/ButtonSizeType.ts
@@ -0,0 +1 @@
+export type ButtonSizeType = 'lg' | 'sm' | 'md';
diff --git a/Coolui v3 test/src/common/types/ColorVariantType.ts b/Coolui v3 test/src/common/types/ColorVariantType.ts
new file mode 100644
index 0000000000..945b64de7d
--- /dev/null
+++ b/Coolui v3 test/src/common/types/ColorVariantType.ts
@@ -0,0 +1 @@
+export type ColorVariantType = 'primary' | 'success' | 'danger' | 'secondary' | 'link' | 'black' | 'white' | 'dark' | 'warning' | 'muted' | 'light' | 'gray';
diff --git a/Coolui v3 test/src/common/types/ColumnSizesType.ts b/Coolui v3 test/src/common/types/ColumnSizesType.ts
new file mode 100644
index 0000000000..2b130d85e7
--- /dev/null
+++ b/Coolui v3 test/src/common/types/ColumnSizesType.ts
@@ -0,0 +1 @@
+export type ColumnSizesType = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
diff --git a/Coolui v3 test/src/common/types/DisplayType.ts b/Coolui v3 test/src/common/types/DisplayType.ts
new file mode 100644
index 0000000000..7551d72150
--- /dev/null
+++ b/Coolui v3 test/src/common/types/DisplayType.ts
@@ -0,0 +1 @@
+export type DisplayType = 'none' | 'inline' | 'inline-block' | 'block' | 'grid' | 'table' | 'table-cell' | 'table-row' | 'flex' | 'inline-flex';
diff --git a/Coolui v3 test/src/common/types/FloatType.ts b/Coolui v3 test/src/common/types/FloatType.ts
new file mode 100644
index 0000000000..63e495fe5b
--- /dev/null
+++ b/Coolui v3 test/src/common/types/FloatType.ts
@@ -0,0 +1 @@
+export type FloatType = 'start' | 'end' | 'none';
diff --git a/Coolui v3 test/src/common/types/FontSizeType.ts b/Coolui v3 test/src/common/types/FontSizeType.ts
new file mode 100644
index 0000000000..120c11c96e
--- /dev/null
+++ b/Coolui v3 test/src/common/types/FontSizeType.ts
@@ -0,0 +1 @@
+export type FontSizeType = 1 | 2 | 3 | 4 | 5 | 6;
diff --git a/Coolui v3 test/src/common/types/FontWeightType.ts b/Coolui v3 test/src/common/types/FontWeightType.ts
new file mode 100644
index 0000000000..c7c92866ce
--- /dev/null
+++ b/Coolui v3 test/src/common/types/FontWeightType.ts
@@ -0,0 +1 @@
+export type FontWeightType = 'bold' | 'bolder' | 'normal' | 'light' | 'lighter';
diff --git a/Coolui v3 test/src/common/types/JustifyContentType.ts b/Coolui v3 test/src/common/types/JustifyContentType.ts
new file mode 100644
index 0000000000..73a318d2d4
--- /dev/null
+++ b/Coolui v3 test/src/common/types/JustifyContentType.ts
@@ -0,0 +1 @@
+export type JustifyContentType = 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly';
diff --git a/Coolui v3 test/src/common/types/OverflowType.ts b/Coolui v3 test/src/common/types/OverflowType.ts
new file mode 100644
index 0000000000..9231ff9f42
--- /dev/null
+++ b/Coolui v3 test/src/common/types/OverflowType.ts
@@ -0,0 +1 @@
+export type OverflowType = 'auto' | 'hidden' | 'visible' | 'scroll' | 'y-scroll' | 'unset';
diff --git a/Coolui v3 test/src/common/types/PositionType.ts b/Coolui v3 test/src/common/types/PositionType.ts
new file mode 100644
index 0000000000..4e20b2fbdc
--- /dev/null
+++ b/Coolui v3 test/src/common/types/PositionType.ts
@@ -0,0 +1 @@
+export type PositionType = 'static' | 'relative' | 'fixed' | 'absolute' | 'sticky';
diff --git a/Coolui v3 test/src/common/types/SpacingType.ts b/Coolui v3 test/src/common/types/SpacingType.ts
new file mode 100644
index 0000000000..91c2bb573b
--- /dev/null
+++ b/Coolui v3 test/src/common/types/SpacingType.ts
@@ -0,0 +1 @@
+export type SpacingType = 0 | 1 | 2 | 3 | 4 | 5;
diff --git a/Coolui v3 test/src/common/types/TextAlignType.ts b/Coolui v3 test/src/common/types/TextAlignType.ts
new file mode 100644
index 0000000000..cb82648065
--- /dev/null
+++ b/Coolui v3 test/src/common/types/TextAlignType.ts
@@ -0,0 +1 @@
+export type TextAlignType = 'start' | 'center' | 'end';
diff --git a/Coolui v3 test/src/common/types/index.ts b/Coolui v3 test/src/common/types/index.ts
new file mode 100644
index 0000000000..333177e289
--- /dev/null
+++ b/Coolui v3 test/src/common/types/index.ts
@@ -0,0 +1,14 @@
+export * from './AlignItemType';
+export * from './AlignSelfType';
+export * from './ButtonSizeType';
+export * from './ColorVariantType';
+export * from './ColumnSizesType';
+export * from './DisplayType';
+export * from './FloatType';
+export * from './FontSizeType';
+export * from './FontWeightType';
+export * from './JustifyContentType';
+export * from './OverflowType';
+export * from './PositionType';
+export * from './SpacingType';
+export * from './TextAlignType';
diff --git a/Coolui v3 test/src/common/utils/CreateTransitionToIcon.ts b/Coolui v3 test/src/common/utils/CreateTransitionToIcon.ts
new file mode 100644
index 0000000000..20e9885876
--- /dev/null
+++ b/Coolui v3 test/src/common/utils/CreateTransitionToIcon.ts
@@ -0,0 +1,13 @@
+import { GetEventDispatcher, NitroToolbarAnimateIconEvent } from '@nitrots/nitro-renderer';
+
+export const CreateTransitionToIcon = (image: HTMLImageElement, fromElement: HTMLElement, icon: string) =>
+{
+ const bounds = fromElement.getBoundingClientRect();
+ const x = (bounds.x + (bounds.width / 2));
+ const y = (bounds.y + (bounds.height / 2));
+ const event = new NitroToolbarAnimateIconEvent(image, x, y);
+
+ event.iconName = icon;
+
+ GetEventDispatcher().dispatchEvent(event);
+};
diff --git a/Coolui v3 test/src/common/utils/FriendlyTimeView.tsx b/Coolui v3 test/src/common/utils/FriendlyTimeView.tsx
new file mode 100644
index 0000000000..4a85c4abde
--- /dev/null
+++ b/Coolui v3 test/src/common/utils/FriendlyTimeView.tsx
@@ -0,0 +1,28 @@
+import { FC, useEffect, useMemo, useState } from 'react';
+import { FriendlyTime } from '../../api';
+import { Base, BaseProps } from '../Base';
+
+interface FriendlyTimeViewProps extends BaseProps
+{
+ seconds: number;
+ isShort?: boolean;
+}
+
+export const FriendlyTimeView: FC = props =>
+{
+ const { seconds = 0, isShort = false, children = null, ...rest } = props;
+ const [ updateId, setUpdateId ] = useState(-1);
+
+ const getStartSeconds = useMemo(() => (Math.round(new Date().getSeconds()) - seconds), [ seconds ]);
+
+ useEffect(() =>
+ {
+ const interval = setInterval(() => setUpdateId(prevValue => (prevValue + 1)), 10000);
+
+ return () => clearInterval(interval);
+ }, []);
+
+ const value = (Math.round(new Date().getSeconds()) - getStartSeconds);
+
+ return { isShort ? FriendlyTime.shortFormat(value) : FriendlyTime.format(value) };
+};
diff --git a/Coolui v3 test/src/common/utils/index.ts b/Coolui v3 test/src/common/utils/index.ts
new file mode 100644
index 0000000000..11d60a3432
--- /dev/null
+++ b/Coolui v3 test/src/common/utils/index.ts
@@ -0,0 +1,2 @@
+export * from './CreateTransitionToIcon';
+export * from './FriendlyTimeView';
diff --git a/Coolui v3 test/src/components/MainView.tsx b/Coolui v3 test/src/components/MainView.tsx
new file mode 100644
index 0000000000..e8b838d1fa
--- /dev/null
+++ b/Coolui v3 test/src/components/MainView.tsx
@@ -0,0 +1,119 @@
+import { AddLinkEventTracker, GetCommunication, HabboWebTools, ILinkEventTracker, RemoveLinkEventTracker, RoomSessionEvent } from '@nitrots/nitro-renderer';
+import { AnimatePresence, motion } from 'framer-motion';
+import { FC, useEffect, useState } from 'react';
+import { useNitroEvent } from '../hooks';
+import { AchievementsView } from './achievements/AchievementsView';
+import { AvatarEditorView } from './avatar-editor';
+import { CameraWidgetView } from './camera/CameraWidgetView';
+import { CampaignView } from './campaign/CampaignView';
+import { CatalogView } from './catalog/CatalogView';
+import { ChatHistoryView } from './chat-history/ChatHistoryView';
+import { FloorplanEditorView } from './floorplan-editor/FloorplanEditorView';
+import { FriendsView } from './friends/FriendsView';
+import { GameCenterView } from './game-center/GameCenterView';
+import { GroupsView } from './groups/GroupsView';
+import { GuideToolView } from './guide-tool/GuideToolView';
+import { HcCenterView } from './hc-center/HcCenterView';
+import { HelpView } from './help/HelpView';
+import { HotelView } from './hotel-view/HotelView';
+import { InventoryView } from './inventory/InventoryView';
+import { ModToolsView } from './mod-tools/ModToolsView';
+import { NavigatorView } from './navigator/NavigatorView';
+import { NitrobubbleHiddenView } from './nitrobubblehidden/NitrobubbleHiddenView';
+import { NitropediaView } from './nitropedia/NitropediaView';
+import { RightSideView } from './right-side/RightSideView';
+import { RoomView } from './room/RoomView';
+import { ToolbarView } from './toolbar/ToolbarView';
+import { UserProfileView } from './user-profile/UserProfileView';
+import { UserSettingsView } from './user-settings/UserSettingsView';
+import { WiredView } from './wired/WiredView';
+
+export const MainView: FC<{}> = props =>
+{
+ const [ isReady, setIsReady ] = useState(false);
+ const [ landingViewVisible, setLandingViewVisible ] = useState(true);
+
+ useNitroEvent(RoomSessionEvent.CREATED, event => setLandingViewVisible(false));
+ useNitroEvent(RoomSessionEvent.ENDED, event => setLandingViewVisible(event.openLandingView));
+
+ useEffect(() =>
+ {
+ setIsReady(true);
+
+ GetCommunication().connection.ready();
+ }, []);
+
+ useEffect(() =>
+ {
+ const linkTracker: ILinkEventTracker = {
+ linkReceived: (url: string) =>
+ {
+ const parts = url.split('/');
+
+ if(parts.length < 2) return;
+
+ switch(parts[1])
+ {
+ case 'open':
+ if(parts.length > 2)
+ {
+ switch(parts[2])
+ {
+ case 'credits':
+ //HabboWebTools.openWebPageAndMinimizeClient(this._windowManager.getProperty(ExternalVariables.WEB_SHOP_RELATIVE_URL));
+ break;
+ default: {
+ const name = parts[2];
+ HabboWebTools.openHabblet(name);
+ }
+ }
+ }
+ return;
+ }
+ },
+ eventUrlPrefix: 'habblet/'
+ };
+
+ AddLinkEventTracker(linkTracker);
+
+ return () => RemoveLinkEventTracker(linkTracker);
+ }, []);
+
+ return (
+ <>
+
+ { landingViewVisible &&
+
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/Coolui v3 test/src/components/achievements/AchievementBadgeView.tsx b/Coolui v3 test/src/components/achievements/AchievementBadgeView.tsx
new file mode 100644
index 0000000000..5365265855
--- /dev/null
+++ b/Coolui v3 test/src/components/achievements/AchievementBadgeView.tsx
@@ -0,0 +1,19 @@
+import { AchievementData } from '@nitrots/nitro-renderer';
+import { FC } from 'react';
+import { AchievementUtilities } from '../../api';
+import { BaseProps, LayoutBadgeImageView } from '../../common';
+
+interface AchievementBadgeViewProps extends BaseProps
+{
+ achievement: AchievementData;
+ scale?: number;
+}
+
+export const AchievementBadgeView: FC = props =>
+{
+ const { achievement = null, scale = 1, ...rest } = props;
+
+ if(!achievement) return null;
+
+ return ;
+};
diff --git a/Coolui v3 test/src/components/achievements/AchievementCategoryView.tsx b/Coolui v3 test/src/components/achievements/AchievementCategoryView.tsx
new file mode 100644
index 0000000000..3a6f99f186
--- /dev/null
+++ b/Coolui v3 test/src/components/achievements/AchievementCategoryView.tsx
@@ -0,0 +1,42 @@
+import { FC, useEffect } from 'react';
+import { AchievementCategory } from '../../api';
+import { Column } from '../../common';
+import { useAchievements } from '../../hooks';
+import { AchievementDetailsView } from './AchievementDetailsView';
+import { AchievementListView } from './achievement-list';
+
+interface AchievementCategoryViewProps {
+ category: AchievementCategory;
+}
+
+export const AchievementCategoryView: FC = (
+ props,
+) =>
+{
+ const { category = null } = props;
+ const { selectedAchievement = null, setSelectedAchievementId = null } =
+ useAchievements();
+
+ useEffect(() =>
+ {
+ if(!category) return;
+
+ if(!selectedAchievement)
+ {
+ setSelectedAchievementId(
+ category?.achievements?.[0]?.achievementId,
+ );
+ }
+ }, [category, selectedAchievement, setSelectedAchievementId]);
+
+ if(!category) return null;
+
+ return (
+
+
+ {!!selectedAchievement && (
+
+ )}
+
+ );
+};
diff --git a/Coolui v3 test/src/components/achievements/AchievementDetailsView.tsx b/Coolui v3 test/src/components/achievements/AchievementDetailsView.tsx
new file mode 100644
index 0000000000..a413883bef
--- /dev/null
+++ b/Coolui v3 test/src/components/achievements/AchievementDetailsView.tsx
@@ -0,0 +1,53 @@
+import { AchievementData } from '@nitrots/nitro-renderer';
+import { FC } from 'react';
+import { AchievementUtilities, LocalizeBadgeDescription, LocalizeBadgeName, LocalizeText } from '../../api';
+import { Column, Flex, LayoutCurrencyIcon, LayoutProgressBar, Text } from '../../common';
+import { AchievementBadgeView } from './AchievementBadgeView';
+
+interface AchievementDetailsViewProps
+{
+ achievement: AchievementData;
+}
+
+export const AchievementDetailsView: FC = props =>
+{
+ const { achievement = null } = props;
+
+ if(!achievement) return null;
+
+ return (
+
+
+
+
+ { LocalizeText('achievements.details.level', [ 'level', 'limit' ], [ AchievementUtilities.getAchievementLevel(achievement).toString(), achievement.levelCount.toString() ]) }
+
+
+
+
+
+ { LocalizeBadgeName(AchievementUtilities.getAchievementBadgeCode(achievement)) }
+
+
+ { LocalizeBadgeDescription(AchievementUtilities.getAchievementBadgeCode(achievement)) }
+
+
+ { ((achievement.levelRewardPoints > 0) || (achievement.scoreLimit > 0)) &&
+
+ { (achievement.levelRewardPoints > 0) &&
+
+
+ { LocalizeText('achievements.details.reward') }
+
+
+ { achievement.levelRewardPoints }
+
+
+
}
+ { (achievement.scoreLimit > 0) &&
+
}
+
}
+
+
+ );
+};
diff --git a/Coolui v3 test/src/components/achievements/AchievementsView.tsx b/Coolui v3 test/src/components/achievements/AchievementsView.tsx
new file mode 100644
index 0000000000..bdb80aef76
--- /dev/null
+++ b/Coolui v3 test/src/components/achievements/AchievementsView.tsx
@@ -0,0 +1,143 @@
+import
+{
+ AddLinkEventTracker,
+ ILinkEventTracker,
+ RemoveLinkEventTracker,
+} from '@nitrots/nitro-renderer';
+import { FC, useEffect, useState } from 'react';
+import { AchievementUtilities, LocalizeText } from '../../api';
+import { Column, LayoutImage, LayoutProgressBar, Text } from '../../common';
+import { useAchievements } from '../../hooks';
+import { NitroCard } from '../../layout';
+import { AchievementCategoryView } from './AchievementCategoryView';
+import { AchievementsCategoryListView } from './category-list';
+
+export const AchievementsView: FC<{}> = (props) =>
+{
+ const [isVisible, setIsVisible] = useState(false);
+ const {
+ achievementCategories = [],
+ selectedCategoryCode = null,
+ setSelectedCategoryCode = null,
+ achievementScore = 0,
+ getProgress = 0,
+ getMaxProgress = 0,
+ selectedCategory = null,
+ } = useAchievements();
+
+ useEffect(() =>
+ {
+ const linkTracker: ILinkEventTracker = {
+ linkReceived: (url: string) =>
+ {
+ const parts = url.split('/');
+
+ if(parts.length < 2) return;
+
+ switch(parts[1])
+ {
+ case 'show':
+ setIsVisible(true);
+ return;
+ case 'hide':
+ setIsVisible(false);
+ return;
+ case 'toggle':
+ setIsVisible((prevValue) => !prevValue);
+ return;
+ }
+ },
+ eventUrlPrefix: 'achievements/',
+ };
+
+ AddLinkEventTracker(linkTracker);
+
+ return () => RemoveLinkEventTracker(linkTracker);
+ }, []);
+
+ if(!isVisible) return null;
+
+ return (
+
+ setIsVisible(false)}
+ />
+ {selectedCategory && (
+
+
setSelectedCategoryCode(null)}
+ />
+
+
+ {LocalizeText(
+ `quests.${selectedCategory.code}.name`
+ )}
+
+
+ {LocalizeText(
+ 'achievements.details.categoryprogress',
+ ['progress', 'limit'],
+ [
+ selectedCategory.getProgress().toString(),
+ selectedCategory
+ .getMaxProgress()
+ .toString(),
+ ]
+ )}
+
+
+
+
+ )}
+
+ {!selectedCategory && (
+ <>
+
+
+
+ {LocalizeText(
+ 'achievements.categories.score',
+ ['score'],
+ [achievementScore.toString()]
+ )}
+
+
+
+ >
+ )}
+ {selectedCategory && (
+
+ )}
+
+
+ );
+};
diff --git a/Coolui v3 test/src/components/achievements/achievement-list/AchievementListItemView.tsx b/Coolui v3 test/src/components/achievements/achievement-list/AchievementListItemView.tsx
new file mode 100644
index 0000000000..88386f3e4b
--- /dev/null
+++ b/Coolui v3 test/src/components/achievements/achievement-list/AchievementListItemView.tsx
@@ -0,0 +1,24 @@
+import { AchievementData } from '@nitrots/nitro-renderer';
+import { FC } from 'react';
+import { LayoutGridItem } from '../../../common';
+import { useAchievements } from '../../../hooks';
+import { AchievementBadgeView } from '../AchievementBadgeView';
+
+interface AchievementListItemViewProps
+{
+ achievement: AchievementData;
+}
+
+export const AchievementListItemView: FC
= props =>
+{
+ const { achievement = null } = props;
+ const { selectedAchievement = null, setSelectedAchievementId = null } = useAchievements();
+
+ if(!achievement) return null;
+
+ return (
+ 0) } onClick={ event => setSelectedAchievementId(achievement.achievementId) }>
+
+
+ );
+};
diff --git a/Coolui v3 test/src/components/achievements/achievement-list/AchievementListView.tsx b/Coolui v3 test/src/components/achievements/achievement-list/AchievementListView.tsx
new file mode 100644
index 0000000000..0d4647230a
--- /dev/null
+++ b/Coolui v3 test/src/components/achievements/achievement-list/AchievementListView.tsx
@@ -0,0 +1,20 @@
+import { AchievementData } from '@nitrots/nitro-renderer';
+import { FC } from 'react';
+import { AutoGrid } from '../../../common';
+import { AchievementListItemView } from './AchievementListItemView';
+
+interface AchievementListViewProps
+{
+ achievements: AchievementData[];
+}
+
+export const AchievementListView: FC = props =>
+{
+ const { achievements = null } = props;
+
+ return (
+
+ { achievements && (achievements.length > 0) && achievements.map((achievement, index) => ) }
+
+ );
+};
diff --git a/Coolui v3 test/src/components/achievements/achievement-list/index.ts b/Coolui v3 test/src/components/achievements/achievement-list/index.ts
new file mode 100644
index 0000000000..87ccb432ed
--- /dev/null
+++ b/Coolui v3 test/src/components/achievements/achievement-list/index.ts
@@ -0,0 +1,2 @@
+export * from './AchievementListItemView';
+export * from './AchievementListView';
diff --git a/Coolui v3 test/src/components/achievements/category-list/AchievementsCategoryListItemView.tsx b/Coolui v3 test/src/components/achievements/category-list/AchievementsCategoryListItemView.tsx
new file mode 100644
index 0000000000..f73c0966a3
--- /dev/null
+++ b/Coolui v3 test/src/components/achievements/category-list/AchievementsCategoryListItemView.tsx
@@ -0,0 +1,31 @@
+import { Dispatch, FC, SetStateAction } from 'react';
+import { AchievementUtilities, IAchievementCategory, LocalizeText } from '../../../api';
+import { LayoutBackgroundImage, LayoutGridItem, Text } from '../../../common';
+
+interface AchievementCategoryListItemViewProps
+{
+ category: IAchievementCategory;
+ selectedCategoryCode: string;
+ setSelectedCategoryCode: Dispatch>;
+}
+
+export const AchievementsCategoryListItemView: FC = props =>
+{
+ const { category = null, selectedCategoryCode = null, setSelectedCategoryCode = null } = props;
+
+ if(!category) return null;
+
+ const progress = AchievementUtilities.getAchievementCategoryProgress(category);
+ const maxProgress = AchievementUtilities.getAchievementCategoryMaxProgress(category);
+ const getCategoryImage = AchievementUtilities.getAchievementCategoryImageUrl(category, progress);
+ const getTotalUnseen = AchievementUtilities.getAchievementCategoryTotalUnseen(category);
+
+ return (
+ setSelectedCategoryCode(category.code) }>
+ { LocalizeText(`quests.${ category.code }.name`) }
+
+ { progress } / { maxProgress }
+
+
+ );
+};
diff --git a/Coolui v3 test/src/components/achievements/category-list/AchievementsCategoryListView.tsx b/Coolui v3 test/src/components/achievements/category-list/AchievementsCategoryListView.tsx
new file mode 100644
index 0000000000..c641f623b3
--- /dev/null
+++ b/Coolui v3 test/src/components/achievements/category-list/AchievementsCategoryListView.tsx
@@ -0,0 +1,22 @@
+import { Dispatch, FC, SetStateAction } from 'react';
+import { IAchievementCategory } from '../../../api';
+import { AutoGrid } from '../../../common';
+import { AchievementsCategoryListItemView } from './AchievementsCategoryListItemView';
+
+interface AchievementsCategoryListViewProps
+{
+ categories: IAchievementCategory[];
+ selectedCategoryCode: string;
+ setSelectedCategoryCode: Dispatch>;
+}
+
+export const AchievementsCategoryListView: FC = props =>
+{
+ const { categories = null, selectedCategoryCode = null, setSelectedCategoryCode = null } = props;
+
+ return (
+
+ { categories && (categories.length > 0) && categories.map((category, index) => ) }
+
+ );
+};
diff --git a/Coolui v3 test/src/components/achievements/category-list/index.ts b/Coolui v3 test/src/components/achievements/category-list/index.ts
new file mode 100644
index 0000000000..5a367f857b
--- /dev/null
+++ b/Coolui v3 test/src/components/achievements/category-list/index.ts
@@ -0,0 +1,2 @@
+export * from './AchievementsCategoryListItemView';
+export * from './AchievementsCategoryListView';
diff --git a/Coolui v3 test/src/components/achievements/index.ts b/Coolui v3 test/src/components/achievements/index.ts
new file mode 100644
index 0000000000..89f6737d7c
--- /dev/null
+++ b/Coolui v3 test/src/components/achievements/index.ts
@@ -0,0 +1,6 @@
+export * from './AchievementBadgeView';
+export * from './AchievementCategoryView';
+export * from './AchievementDetailsView';
+export * from './AchievementsView';
+export * from './achievement-list';
+export * from './category-list';
diff --git a/Coolui v3 test/src/components/avatar-editor/AvatarEditorFigurePreviewView.tsx b/Coolui v3 test/src/components/avatar-editor/AvatarEditorFigurePreviewView.tsx
new file mode 100644
index 0000000000..25bb60977f
--- /dev/null
+++ b/Coolui v3 test/src/components/avatar-editor/AvatarEditorFigurePreviewView.tsx
@@ -0,0 +1,40 @@
+import { AvatarDirectionAngle } from '@nitrots/nitro-renderer';
+import { FC, useState } from 'react';
+import { LayoutAvatarImageView } from '../../common';
+import { useAvatarEditor } from '../../hooks';
+import { AvatarEditorIcon } from './AvatarEditorIcon';
+
+const DEFAULT_DIRECTION: number = 4;
+
+export const AvatarEditorFigurePreviewView: FC<{}> = props =>
+{
+ const [ direction, setDirection ] = useState(DEFAULT_DIRECTION);
+ const { getFigureString = null } = useAvatarEditor();
+
+ const rotateFigure = (newDirection: number) =>
+ {
+ if(direction < AvatarDirectionAngle.MIN_DIRECTION)
+ {
+ newDirection = (AvatarDirectionAngle.MAX_DIRECTION + (direction + 1));
+ }
+
+ if(direction > AvatarDirectionAngle.MAX_DIRECTION)
+ {
+ newDirection = (direction - (AvatarDirectionAngle.MAX_DIRECTION + 1));
+ }
+
+ setDirection(newDirection);
+ };
+
+ return (
+
+
+
+
+
+
rotateFigure(direction + 1) } />
+ rotateFigure(direction - 1) } />
+
+
+ );
+};
diff --git a/Coolui v3 test/src/components/avatar-editor/AvatarEditorIcon.tsx b/Coolui v3 test/src/components/avatar-editor/AvatarEditorIcon.tsx
new file mode 100644
index 0000000000..f5623ed2c2
--- /dev/null
+++ b/Coolui v3 test/src/components/avatar-editor/AvatarEditorIcon.tsx
@@ -0,0 +1,46 @@
+import { DetailedHTMLProps, HTMLAttributes, PropsWithChildren, forwardRef } from 'react';
+import { classNames } from '../../layout';
+
+type AvatarIconType = 'male' | 'female' | 'clear' | 'sellable';
+
+export const AvatarEditorIcon = forwardRef & DetailedHTMLProps, HTMLDivElement>>((props, ref) =>
+{
+ const { icon = null, selected = false, className = null, ...rest } = props;
+
+ /*
+ switch (icon)
+ {
+ case 'male':
+
+
+ break;
+
+ case 'arrow-left':
+
+ break;
+
+ default:
+ //statements;
+ break;
+
+ }
+*/
+ return (
+
+ );
+});
+
+AvatarEditorIcon.displayName = 'AvatarEditorIcon';
diff --git a/Coolui v3 test/src/components/avatar-editor/AvatarEditorModelView.tsx b/Coolui v3 test/src/components/avatar-editor/AvatarEditorModelView.tsx
new file mode 100644
index 0000000000..53a49ab21a
--- /dev/null
+++ b/Coolui v3 test/src/components/avatar-editor/AvatarEditorModelView.tsx
@@ -0,0 +1,80 @@
+import { AvatarEditorFigureCategory, AvatarFigurePartType, FigureDataContainer } from '@nitrots/nitro-renderer';
+import { FC, useCallback, useEffect, useMemo, useState } from 'react';
+import { IAvatarEditorCategory } from '../../api';
+import { useAvatarEditor } from '../../hooks';
+import { AvatarEditorIcon } from './AvatarEditorIcon';
+import { AvatarEditorFigureSetView } from './figure-set';
+import { AvatarEditorPaletteSetView } from './palette-set';
+
+export const AvatarEditorModelView: FC<{
+ name: string,
+ categories: IAvatarEditorCategory[]
+}> = props =>
+{
+ const { name = '', categories = [] } = props;
+ const [ didChange, setDidChange ] = useState(false);
+ const [ activeSetType, setActiveSetType ] = useState('');
+ const { maxPaletteCount = 1, gender = null, setGender = null, selectedColorParts = null, getFirstSelectableColor = null, selectEditorColor = null } = useAvatarEditor();
+
+ const activeCategory = useMemo(() =>
+ {
+ return categories.find(category => category.setType === activeSetType) ?? null;
+ }, [ categories, activeSetType ]);
+
+ const selectSet = useCallback((setType: string) =>
+ {
+ const selectedPalettes = selectedColorParts[setType];
+
+ if(!selectedPalettes || !selectedPalettes.length) selectEditorColor(setType, 0, getFirstSelectableColor(setType));
+
+ setActiveSetType(setType);
+ }, [ getFirstSelectableColor, selectEditorColor, selectedColorParts ]);
+
+ useEffect(() =>
+ {
+ if(!categories || !categories.length || !didChange) return;
+
+ selectSet(categories[0]?.setType);
+ setDidChange(false);
+ }, [ categories, didChange, selectSet ]);
+
+ useEffect(() =>
+ {
+ setDidChange(true);
+ }, [ categories ]);
+
+ if(!activeCategory) return null;
+
+ return (
+
+
+ { (name === AvatarEditorFigureCategory.GENERIC) &&
+ <>
+
setGender(AvatarFigurePartType.MALE) }>
+
+
+
setGender(AvatarFigurePartType.FEMALE) }>
+
+
+ > }
+ { (name !== AvatarEditorFigureCategory.GENERIC) && (categories.length > 0) && categories.map(category =>
+ {
+ return (
+
selectSet(category.setType) }>
+
+
+ );
+ }) }
+
+
+
+ { (maxPaletteCount >= 1) &&
+
}
+ { (maxPaletteCount === 2) &&
+
}
+
+
+ );
+};
diff --git a/Coolui v3 test/src/components/avatar-editor/AvatarEditorView.tsx b/Coolui v3 test/src/components/avatar-editor/AvatarEditorView.tsx
new file mode 100644
index 0000000000..5dea113ec3
--- /dev/null
+++ b/Coolui v3 test/src/components/avatar-editor/AvatarEditorView.tsx
@@ -0,0 +1,122 @@
+import { AddLinkEventTracker, AvatarEditorFigureCategory, GetSessionDataManager, ILinkEventTracker, RemoveLinkEventTracker, UserFigureComposer } from '@nitrots/nitro-renderer';
+import { FC, useEffect, useState } from 'react';
+import { FaDice, FaRedo, FaTrash } from 'react-icons/fa';
+import { AvatarEditorAction, LocalizeText, SendMessageComposer } from '../../api';
+import { Button, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common';
+import { useAvatarEditor } from '../../hooks';
+import { AvatarEditorFigurePreviewView } from './AvatarEditorFigurePreviewView';
+import { AvatarEditorModelView } from './AvatarEditorModelView';
+import { AvatarEditorWardrobeView } from './AvatarEditorWardrobeView';
+
+const DEFAULT_MALE_FIGURE: string = 'hr-100.hd-180-7.ch-215-66.lg-270-79.sh-305-62.ha-1002-70.wa-2007';
+const DEFAULT_FEMALE_FIGURE: string = 'hr-515-33.hd-600-1.ch-635-70.lg-716-66-62.sh-735-68';
+
+export const AvatarEditorView: FC<{}> = props =>
+{
+ const [ isVisible, setIsVisible ] = useState(false);
+ const { setIsVisible: setEditorVisibility, avatarModels, activeModelKey, setActiveModelKey, loadAvatarData, getFigureStringWithFace, gender, figureSetIds = [], randomizeCurrentFigure = null, getFigureString = null } = useAvatarEditor();
+
+ const processAction = (action: string) =>
+ {
+ switch(action)
+ {
+ case AvatarEditorAction.ACTION_RESET:
+ loadAvatarData(GetSessionDataManager().figure, GetSessionDataManager().gender);
+ return;
+ case AvatarEditorAction.ACTION_CLEAR:
+ loadAvatarData(getFigureStringWithFace(0, false), gender);
+ return;
+ case AvatarEditorAction.ACTION_RANDOMIZE:
+ randomizeCurrentFigure();
+ return;
+ case AvatarEditorAction.ACTION_SAVE:
+ SendMessageComposer(new UserFigureComposer(gender, getFigureString));
+ setIsVisible(false);
+ return;
+ }
+ };
+
+ useEffect(() =>
+ {
+ const linkTracker: ILinkEventTracker = {
+ linkReceived: (url: string) =>
+ {
+ const parts = url.split('/');
+
+ if(parts.length < 2) return;
+
+ switch(parts[1])
+ {
+ case 'show':
+ setIsVisible(true);
+ return;
+ case 'hide':
+ setIsVisible(false);
+ return;
+ case 'toggle':
+ setIsVisible(prevValue => !prevValue);
+ return;
+ }
+ },
+ eventUrlPrefix: 'avatar-editor/'
+ };
+
+ AddLinkEventTracker(linkTracker);
+
+ return () => RemoveLinkEventTracker(linkTracker);
+ }, []);
+
+ useEffect(() =>
+ {
+ setEditorVisibility(isVisible);
+ }, [ isVisible, setEditorVisibility ]);
+
+ if(!isVisible) return null;
+
+ return (
+
+ setIsVisible(false) } />
+
+ { Object.keys(avatarModels).map(modelKey =>
+ {
+ const isActive = (activeModelKey === modelKey);
+
+ return (
+ setActiveModelKey(modelKey) }>
+ { LocalizeText(`avatareditor.category.${ modelKey }`) }
+
+ );
+ }) }
+
+
+
+
+ { ((activeModelKey.length > 0) && (activeModelKey !== AvatarEditorFigureCategory.WARDROBE)) &&
+
}
+ { (activeModelKey === AvatarEditorFigureCategory.WARDROBE) &&
+
}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/Coolui v3 test/src/components/avatar-editor/AvatarEditorWardrobeView.tsx b/Coolui v3 test/src/components/avatar-editor/AvatarEditorWardrobeView.tsx
new file mode 100644
index 0000000000..6981bd5f39
--- /dev/null
+++ b/Coolui v3 test/src/components/avatar-editor/AvatarEditorWardrobeView.tsx
@@ -0,0 +1,61 @@
+import { GetAvatarRenderManager, IAvatarFigureContainer, SaveWardrobeOutfitMessageComposer } from '@nitrots/nitro-renderer';
+import { FC, useCallback } from 'react';
+import { GetClubMemberLevel, GetConfigurationValue, LocalizeText, SendMessageComposer } from '../../api';
+import { Button, LayoutAvatarImageView, LayoutCurrencyIcon } from '../../common';
+import { useAvatarEditor } from '../../hooks';
+import { InfiniteGrid } from '../../layout';
+
+export const AvatarEditorWardrobeView: FC<{}> = props =>
+{
+ const { savedFigures = [], setSavedFigures = null, loadAvatarData = null, getFigureString = null, gender = null } = useAvatarEditor();
+
+ const hcDisabled = GetConfigurationValue('hc.disabled', false);
+
+ const wearFigureAtIndex = useCallback((index: number) =>
+ {
+ if((index >= savedFigures.length) || (index < 0)) return;
+
+ const [ figure, gender ] = savedFigures[index];
+
+ loadAvatarData(figure.getFigureString(), gender);
+ }, [ savedFigures, loadAvatarData ]);
+
+ const saveFigureAtWardrobeIndex = useCallback((index: number) =>
+ {
+ if((index >= savedFigures.length) || (index < 0)) return;
+
+ const newFigures = [ ...savedFigures ];
+
+ const figure = getFigureString;
+
+ newFigures[index] = [ GetAvatarRenderManager().createFigureContainer(figure), gender ];
+
+ setSavedFigures(newFigures);
+ SendMessageComposer(new SaveWardrobeOutfitMessageComposer((index + 1), figure, gender));
+ }, [ getFigureString, gender, savedFigures, setSavedFigures ]);
+
+ return (
+
+ {
+ const [ figureContainer, gender ] = item;
+
+ let clubLevel = 0;
+
+ if(figureContainer) clubLevel = GetAvatarRenderManager().getFigureClubLevel(figureContainer, gender);
+
+ return (
+
+ { figureContainer &&
+ }
+
+ { !hcDisabled && (clubLevel > 0) && }
+
+
+ { figureContainer &&
+ }
+
+
+ );
+ } } items={ savedFigures } overscan={ 5 } />
+ );
+};
diff --git a/Coolui v3 test/src/components/avatar-editor/figure-set/AvatarEditorFigureSetItemView.tsx b/Coolui v3 test/src/components/avatar-editor/figure-set/AvatarEditorFigureSetItemView.tsx
new file mode 100644
index 0000000000..4126fa3a66
--- /dev/null
+++ b/Coolui v3 test/src/components/avatar-editor/figure-set/AvatarEditorFigureSetItemView.tsx
@@ -0,0 +1,56 @@
+import { AvatarFigurePartType } from '@nitrots/nitro-renderer';
+import { FC, useEffect, useState } from 'react';
+import { AvatarEditorThumbnailsHelper, GetConfigurationValue, IAvatarEditorCategoryPartItem } from '../../../api';
+import { LayoutCurrencyIcon, LayoutGridItemProps } from '../../../common';
+import { useAvatarEditor } from '../../../hooks';
+import { InfiniteGrid } from '../../../layout';
+import { AvatarEditorIcon } from '../AvatarEditorIcon';
+
+export const AvatarEditorFigureSetItemView: FC<{
+ setType: string;
+ partItem: IAvatarEditorCategoryPartItem;
+ isSelected: boolean;
+ width?: string;
+} & LayoutGridItemProps> = props =>
+{
+ const { setType = null, partItem = null, isSelected = false, width = '100%', ...rest } = props;
+ const [ assetUrl, setAssetUrl ] = useState('');
+ const { selectedColorParts = null, getFigureStringWithFace = null } = useAvatarEditor();
+
+ const isHC = !GetConfigurationValue('hc.disabled', false) && ((partItem.partSet?.clubLevel ?? 0) > 0);
+
+ useEffect(() =>
+ {
+ if(!setType || !setType.length || !partItem) return;
+
+ const loadImage = async () =>
+ {
+ const isHC = !GetConfigurationValue('hc.disabled', false) && ((partItem.partSet?.clubLevel ?? 0) > 0);
+
+ let url: string = null;
+
+ if(setType === AvatarFigurePartType.HEAD)
+ {
+ url = await AvatarEditorThumbnailsHelper.buildForFace(getFigureStringWithFace(partItem.id), isHC);
+ }
+ else
+ {
+ url = await AvatarEditorThumbnailsHelper.build(setType, partItem, partItem.usesColor, selectedColorParts[setType] ?? null, isHC);
+ }
+
+ if(url && url.length) setAssetUrl(url);
+ };
+
+ loadImage();
+ }, [ setType, partItem, selectedColorParts, getFigureStringWithFace ]);
+
+ if(!partItem) return null;
+
+ return (
+
+ { !partItem.isClear && isHC && }
+ { partItem.isClear && }
+ { !partItem.isClear && partItem.partSet.isSellable && }
+
+ );
+};
diff --git a/Coolui v3 test/src/components/avatar-editor/figure-set/AvatarEditorFigureSetView.tsx b/Coolui v3 test/src/components/avatar-editor/figure-set/AvatarEditorFigureSetView.tsx
new file mode 100644
index 0000000000..9a5b543233
--- /dev/null
+++ b/Coolui v3 test/src/components/avatar-editor/figure-set/AvatarEditorFigureSetView.tsx
@@ -0,0 +1,41 @@
+import { FC } from 'react';
+import { IAvatarEditorCategory, IAvatarEditorCategoryPartItem } from '../../../api';
+import { useAvatarEditor } from '../../../hooks';
+import { InfiniteGrid } from '../../../layout';
+import { AvatarEditorFigureSetItemView } from './AvatarEditorFigureSetItemView';
+
+export const AvatarEditorFigureSetView: FC<{
+ category: IAvatarEditorCategory;
+ columnCount: number;
+}> = props =>
+{
+ const { category = null, columnCount = 3 } = props;
+ const { selectedParts = null, selectEditorPart } = useAvatarEditor();
+
+ const isPartItemSelected = (partItem: IAvatarEditorCategoryPartItem) =>
+ {
+ if(!category || !category.setType || !selectedParts) return false;
+
+ if(!selectedParts[category.setType])
+ {
+ if(partItem.isClear) return true;
+
+ return false;
+ }
+
+ const partId = selectedParts[category.setType];
+
+ return (partId === partItem.id);
+ };
+
+ return (
+ columnCount={ columnCount } itemRender={ (item: IAvatarEditorCategoryPartItem) =>
+ {
+ if(!item) return null;
+
+ return (
+ selectEditorPart(category.setType, item.partSet?.id ?? -1) } />
+ );
+ } } items={ category.partItems } overscan={ columnCount } />
+ );
+};
diff --git a/Coolui v3 test/src/components/avatar-editor/figure-set/index.ts b/Coolui v3 test/src/components/avatar-editor/figure-set/index.ts
new file mode 100644
index 0000000000..0c5880b261
--- /dev/null
+++ b/Coolui v3 test/src/components/avatar-editor/figure-set/index.ts
@@ -0,0 +1,2 @@
+export * from './AvatarEditorFigureSetItemView';
+export * from './AvatarEditorFigureSetView';
diff --git a/Coolui v3 test/src/components/avatar-editor/index.ts b/Coolui v3 test/src/components/avatar-editor/index.ts
new file mode 100644
index 0000000000..5ae66e5498
--- /dev/null
+++ b/Coolui v3 test/src/components/avatar-editor/index.ts
@@ -0,0 +1,7 @@
+export * from './AvatarEditorFigurePreviewView';
+export * from './AvatarEditorIcon';
+export * from './AvatarEditorModelView';
+export * from './AvatarEditorView';
+export * from './AvatarEditorWardrobeView';
+export * from './figure-set';
+export * from './palette-set';
diff --git a/Coolui v3 test/src/components/avatar-editor/palette-set/AvatarEditorPaletteSetItemView.tsx b/Coolui v3 test/src/components/avatar-editor/palette-set/AvatarEditorPaletteSetItemView.tsx
new file mode 100644
index 0000000000..8a520bc53e
--- /dev/null
+++ b/Coolui v3 test/src/components/avatar-editor/palette-set/AvatarEditorPaletteSetItemView.tsx
@@ -0,0 +1,25 @@
+import { ColorConverter, IPartColor } from '@nitrots/nitro-renderer';
+import { FC } from 'react';
+import { GetConfigurationValue } from '../../../api';
+import { LayoutCurrencyIcon, LayoutGridItemProps } from '../../../common';
+import { InfiniteGrid } from '../../../layout';
+
+export const AvatarEditorPaletteSetItem: FC<{
+ setType: string;
+ partColor: IPartColor;
+ isSelected: boolean;
+ width?: string;
+} & LayoutGridItemProps> = props =>
+{
+ const { setType = null, partColor = null, isSelected = false, width = '100%', ...rest } = props;
+
+ if(!partColor) return null;
+
+ const isHC = !GetConfigurationValue('hc.disabled', false) && (partColor.clubLevel > 0);
+
+ return (
+
+ { isHC && }
+
+ );
+};
diff --git a/Coolui v3 test/src/components/avatar-editor/palette-set/AvatarEditorPaletteSetView.tsx b/Coolui v3 test/src/components/avatar-editor/palette-set/AvatarEditorPaletteSetView.tsx
new file mode 100644
index 0000000000..d40c8d1cc0
--- /dev/null
+++ b/Coolui v3 test/src/components/avatar-editor/palette-set/AvatarEditorPaletteSetView.tsx
@@ -0,0 +1,36 @@
+import { IPartColor } from '@nitrots/nitro-renderer';
+import { FC } from 'react';
+import { IAvatarEditorCategory } from '../../../api';
+import { useAvatarEditor } from '../../../hooks';
+import { InfiniteGrid } from '../../../layout';
+import { AvatarEditorPaletteSetItem } from './AvatarEditorPaletteSetItemView';
+
+export const AvatarEditorPaletteSetView: FC<{
+ category: IAvatarEditorCategory;
+ paletteIndex: number;
+ columnCount: number;
+}> = props =>
+{
+ const { category = null, paletteIndex = -1, columnCount = 3 } = props;
+ const { selectedColorParts = null, selectEditorColor = null } = useAvatarEditor();
+
+ const isPartColorSelected = (partColor: IPartColor) =>
+ {
+ if(!category || !category.setType || !selectedColorParts || !selectedColorParts[category.setType] || !selectedColorParts[category.setType][paletteIndex]) return false;
+
+ const selectedColorPart = selectedColorParts[category.setType][paletteIndex];
+
+ return (selectedColorPart.id === partColor.id);
+ };
+
+ return (
+ columnCount={ columnCount } itemRender={ (item: IPartColor) =>
+ {
+ if(!item) return null;
+
+ return (
+ selectEditorColor(category.setType, paletteIndex, item.id) } />
+ );
+ } } items={ category.colorItems[paletteIndex] } overscan={ columnCount } />
+ );
+};
diff --git a/Coolui v3 test/src/components/avatar-editor/palette-set/index.ts b/Coolui v3 test/src/components/avatar-editor/palette-set/index.ts
new file mode 100644
index 0000000000..977e5b982a
--- /dev/null
+++ b/Coolui v3 test/src/components/avatar-editor/palette-set/index.ts
@@ -0,0 +1,2 @@
+export * from './AvatarEditorPaletteSetItemView';
+export * from './AvatarEditorPaletteSetView';
diff --git a/Coolui v3 test/src/components/backgrounds/BackgroundsView.tsx b/Coolui v3 test/src/components/backgrounds/BackgroundsView.tsx
new file mode 100644
index 0000000000..4813b47abd
--- /dev/null
+++ b/Coolui v3 test/src/components/backgrounds/BackgroundsView.tsx
@@ -0,0 +1,111 @@
+import { GetSessionDataManager, HabboClubLevelEnum} from '@nitrots/nitro-renderer';
+import { Dispatch, FC, SetStateAction, useCallback, useMemo, useState } from 'react';
+import { Base, Grid, Flex, NitroCardView, NitroCardHeaderView, NitroCardTabsView, NitroCardTabsItemView, NitroCardContentView, Text, LayoutCurrencyIcon } from '../../common';
+import { useRoom } from '../../hooks';
+import { GetClubMemberLevel, GetConfigurationValue } from '../../api';
+
+interface ItemData {
+ id: number;
+ isHcOnly: boolean;
+ minRank: number;
+ isAmbassadorOnly: boolean;
+ selectable: boolean;
+}
+
+interface BackgroundsViewProps {
+ setIsVisible: Dispatch>;
+ selectedBackground: number;
+ setSelectedBackground: Dispatch>;
+ selectedStand: number;
+ setSelectedStand: Dispatch>;
+ selectedOverlay: number;
+ setSelectedOverlay: Dispatch>;
+}
+
+const TABS = ['backgrounds', 'stands', 'overlays'] as const;
+type TabType = typeof TABS[number];
+
+export const BackgroundsView: FC = ({
+ setIsVisible,
+ selectedBackground,
+ setSelectedBackground,
+ selectedStand,
+ setSelectedStand,
+ selectedOverlay,
+ setSelectedOverlay
+}) => {
+ const [activeTab, setActiveTab] = useState('backgrounds');
+ const { roomSession } = useRoom();
+
+ const userData = useMemo(() => ({
+ isHcMember: GetClubMemberLevel() >= HabboClubLevelEnum.CLUB,
+ securityLevel: GetSessionDataManager().canChangeName,
+ isAmbassador: GetSessionDataManager().isAmbassador
+ }), []);
+
+ const processData = useCallback((configData: any[], dataType: string): ItemData[] => {
+ if (!configData?.length) return [];
+
+ return configData
+ .filter(item => {
+ const meetsRank = userData.securityLevel >= item.minRank;
+ const ambassadorEligible = !item.isAmbassadorOnly || userData.isAmbassador;
+ return item.isHcOnly || (meetsRank && ambassadorEligible);
+ })
+ .map(item => ({ id: item[`${dataType}Id`], ...item, selectable: !item.isHcOnly || userData.isHcMember }));
+ }, [userData]);
+
+ const allData = useMemo(() => ({
+ backgrounds: processData(GetConfigurationValue('backgrounds.data'), 'background'),
+ stands: processData(GetConfigurationValue('stands.data'), 'stand'),
+ overlays: processData(GetConfigurationValue('overlays.data'), 'overlay')
+ }), [processData]);
+
+ const handleSelection = useCallback((id: number) => {
+ if (!roomSession) return;
+
+ const setters = { backgrounds: setSelectedBackground, stands: setSelectedStand, overlays: setSelectedOverlay };
+
+ const currentValues = { backgrounds: selectedBackground, stands: selectedStand, overlays: selectedOverlay };
+
+ setters[activeTab](id);
+ const newValues = { ...currentValues, [activeTab]: id };
+ roomSession.sendBackgroundMessage( newValues.backgrounds, newValues.stands, newValues.overlays );
+ }, [activeTab, roomSession, selectedBackground, selectedStand, selectedOverlay, setSelectedBackground, setSelectedStand, setSelectedOverlay]);
+
+ const renderItem = useCallback((item: ItemData, type: string) => (
+ item.selectable && handleSelection(item.id)}
+ className={item.selectable ? '' : 'non-selectable'}
+ >
+
+ {item.isHcOnly && }
+
+ ), [handleSelection]);
+
+ return (
+
+ setIsVisible(false)} />
+
+ {TABS.map(tab => (
+ setActiveTab(tab)}
+ >
+ {tab.charAt(0).toUpperCase() + tab.slice(1)}
+
+ ))}
+
+
+ Select an Option
+
+ {allData[activeTab].map(item => renderItem(item, activeTab.slice(0, -1)))}
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/Coolui v3 test/src/components/camera/CameraWidgetView.tsx b/Coolui v3 test/src/components/camera/CameraWidgetView.tsx
new file mode 100644
index 0000000000..d4221e8402
--- /dev/null
+++ b/Coolui v3 test/src/components/camera/CameraWidgetView.tsx
@@ -0,0 +1,97 @@
+import { AddLinkEventTracker, ILinkEventTracker, RemoveLinkEventTracker, RoomSessionEvent } from '@nitrots/nitro-renderer';
+import { FC, useEffect, useState } from 'react';
+import { useCamera, useNitroEvent } from '../../hooks';
+import { CameraWidgetCaptureView } from './views/CameraWidgetCaptureView';
+import { CameraWidgetCheckoutView } from './views/CameraWidgetCheckoutView';
+import { CameraWidgetEditorView } from './views/editor/CameraWidgetEditorView';
+
+const MODE_NONE: number = 0;
+const MODE_CAPTURE: number = 1;
+const MODE_EDITOR: number = 2;
+const MODE_CHECKOUT: number = 3;
+
+export const CameraWidgetView: FC<{}> = props =>
+{
+ const [ mode, setMode ] = useState(MODE_NONE);
+ const [ base64Url, setSavedPictureUrl ] = useState(null);
+ const { availableEffects = [], selectedPictureIndex = -1, cameraRoll = [], setCameraRoll = null, myLevel = 0, price = { credits: 0, duckets: 0, publishDucketPrice: 0 } } = useCamera();
+
+
+ const processAction = (type: string) =>
+ {
+ switch(type)
+ {
+ case 'close':
+ setMode(MODE_NONE);
+ return;
+ case 'edit':
+ setMode(MODE_EDITOR);
+ return;
+ case 'delete':
+ setCameraRoll(prevValue =>
+ {
+ const clone = [ ...prevValue ];
+
+ clone.splice(selectedPictureIndex, 1);
+
+ return clone;
+ });
+ return;
+ case 'editor_cancel':
+ setMode(MODE_CAPTURE);
+ return;
+ }
+ };
+
+ const checkoutPictureUrl = (pictureUrl: string) =>
+ {
+ setSavedPictureUrl(pictureUrl);
+ setMode(MODE_CHECKOUT);
+ };
+
+ useNitroEvent(RoomSessionEvent.ENDED, event => setMode(MODE_NONE));
+
+ useEffect(() =>
+ {
+ const linkTracker: ILinkEventTracker = {
+ linkReceived: (url: string) =>
+ {
+ const parts = url.split('/');
+
+ if(parts.length < 2) return;
+
+ switch(parts[1])
+ {
+ case 'show':
+ setMode(MODE_CAPTURE);
+ return;
+ case 'hide':
+ setMode(MODE_NONE);
+ return;
+ case 'toggle':
+ setMode(prevValue =>
+ {
+ if(!prevValue) return MODE_CAPTURE;
+ else return MODE_NONE;
+ });
+ return;
+ }
+ },
+ eventUrlPrefix: 'camera/'
+ };
+
+ AddLinkEventTracker(linkTracker);
+
+ return () => RemoveLinkEventTracker(linkTracker);
+ }, []);
+
+ if(mode === MODE_NONE) return null;
+
+ return (
+ <>
+ { (mode === MODE_CAPTURE) && processAction('close') } onDelete={ () => processAction('delete') } onEdit={ () => processAction('edit') } /> }
+ { (mode === MODE_EDITOR) && processAction('editor_cancel') } onCheckout={ checkoutPictureUrl } onClose={ () => processAction('close') } /> }
+ { (mode === MODE_CHECKOUT) && processAction('editor_cancel') } onCloseClick={ () => processAction('close') }> }
+ >
+ );
+};
diff --git a/Coolui v3 test/src/components/camera/index.ts b/Coolui v3 test/src/components/camera/index.ts
new file mode 100644
index 0000000000..43c10cdc17
--- /dev/null
+++ b/Coolui v3 test/src/components/camera/index.ts
@@ -0,0 +1,4 @@
+export * from './CameraWidgetView';
+export * from './views';
+export * from './views/editor';
+export * from './views/editor/effect-list';
diff --git a/Coolui v3 test/src/components/camera/views/CameraWidgetCaptureView.tsx b/Coolui v3 test/src/components/camera/views/CameraWidgetCaptureView.tsx
new file mode 100644
index 0000000000..89fe25ee78
--- /dev/null
+++ b/Coolui v3 test/src/components/camera/views/CameraWidgetCaptureView.tsx
@@ -0,0 +1,90 @@
+import { GetRoomEngine, NitroRectangle, TextureUtils } from '@nitrots/nitro-renderer';
+import { FC, useRef } from 'react';
+import { FaTimes } from 'react-icons/fa';
+import { CameraPicture, GetRoomSession, LocalizeText, PlaySound, SoundNames } from '../../../api';
+import { Button, Column, DraggableWindow } from '../../../common';
+import { useCamera, useNotification } from '../../../hooks';
+
+export interface CameraWidgetCaptureViewProps
+{
+ onClose: () => void;
+ onEdit: () => void;
+ onDelete: () => void;
+}
+
+const CAMERA_ROLL_LIMIT: number = 5;
+
+export const CameraWidgetCaptureView: FC = props =>
+{
+ const { onClose = null, onEdit = null, onDelete = null } = props;
+ const { cameraRoll = null, setCameraRoll = null, selectedPictureIndex = -1, setSelectedPictureIndex = null } = useCamera();
+ const { simpleAlert = null } = useNotification();
+ const elementRef = useRef();
+
+ const selectedPicture = ((selectedPictureIndex > -1) ? cameraRoll[selectedPictureIndex] : null);
+
+ const getCameraBounds = () =>
+ {
+ if(!elementRef || !elementRef.current) return null;
+
+ const frameBounds = elementRef.current.getBoundingClientRect();
+
+ return new NitroRectangle(Math.floor(frameBounds.x), Math.floor(frameBounds.y), Math.floor(frameBounds.width), Math.floor(frameBounds.height));
+ };
+
+ const takePicture = async () =>
+ {
+ if(selectedPictureIndex > -1)
+ {
+ setSelectedPictureIndex(-1);
+ return;
+ }
+
+ const texture = GetRoomEngine().createTextureFromRoom(GetRoomSession().roomId, 1, getCameraBounds());
+
+ const clone = [ ...cameraRoll ];
+
+ if(clone.length >= CAMERA_ROLL_LIMIT)
+ {
+ simpleAlert(LocalizeText('camera.full.body'));
+
+ clone.pop();
+ }
+
+ PlaySound(SoundNames.CAMERA_SHUTTER);
+ clone.push(new CameraPicture(texture, await TextureUtils.generateImageUrl(texture)));
+
+ setCameraRoll(clone);
+ };
+
+ return (
+
+
+ { selectedPicture &&
}
+
+
+
+
+ { !selectedPicture &&
}
+ { selectedPicture &&
+
+
+
+
+
+
}
+
+
+ { (cameraRoll.length > 0) &&
+
+ { cameraRoll.map((picture, index) =>
+ {
+ return

setSelectedPictureIndex(index) } />;
+ }) }
+
}
+
+
+ );
+};
diff --git a/Coolui v3 test/src/components/camera/views/CameraWidgetCheckoutView.tsx b/Coolui v3 test/src/components/camera/views/CameraWidgetCheckoutView.tsx
new file mode 100644
index 0000000000..2ab2f5b21d
--- /dev/null
+++ b/Coolui v3 test/src/components/camera/views/CameraWidgetCheckoutView.tsx
@@ -0,0 +1,159 @@
+import { CameraPublishStatusMessageEvent, CameraPurchaseOKMessageEvent, CameraStorageUrlMessageEvent, CreateLinkEvent, GetRoomEngine, PublishPhotoMessageComposer, PurchasePhotoMessageComposer } from '@nitrots/nitro-renderer';
+import { FC, useEffect, useMemo, useState } from 'react';
+import { GetConfigurationValue, LocalizeText, SendMessageComposer } from '../../../api';
+import { Button, Column, LayoutCurrencyIcon, LayoutImage, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../common';
+import { useMessageEvent } from '../../../hooks';
+
+export interface CameraWidgetCheckoutViewProps
+{
+ base64Url: string;
+ onCloseClick: () => void;
+ onCancelClick: () => void;
+ price: { credits: number, duckets: number, publishDucketPrice: number };
+}
+
+export const CameraWidgetCheckoutView: FC = props =>
+{
+ const { base64Url = null, onCloseClick = null, onCancelClick = null, price = null } = props;
+ const [ pictureUrl, setPictureUrl ] = useState(null);
+ const [ publishUrl, setPublishUrl ] = useState(null);
+ const [ picturesBought, setPicturesBought ] = useState(0);
+ const [ wasPicturePublished, setWasPicturePublished ] = useState(false);
+ const [ isWaiting, setIsWaiting ] = useState(false);
+ const [ publishCooldown, setPublishCooldown ] = useState(0);
+
+ const publishDisabled = useMemo(() => GetConfigurationValue('camera.publish.disabled', false), []);
+
+ useMessageEvent(CameraPurchaseOKMessageEvent, event =>
+ {
+ setPicturesBought(value => (value + 1));
+ setIsWaiting(false);
+ });
+
+ useMessageEvent(CameraPublishStatusMessageEvent, event =>
+ {
+ const parser = event.getParser();
+
+ setPublishUrl(parser.extraDataId);
+ setPublishCooldown(parser.secondsToWait);
+ setWasPicturePublished(parser.ok);
+ setIsWaiting(false);
+ });
+
+ useMessageEvent(CameraStorageUrlMessageEvent, event =>
+ {
+ const parser = event.getParser();
+
+ setPictureUrl(GetConfigurationValue('camera.url') + '/' + parser.url);
+ });
+
+ const processAction = (type: string, value: string | number = null) =>
+ {
+ switch(type)
+ {
+ case 'close':
+ onCloseClick();
+ return;
+ case 'buy':
+ if(isWaiting) return;
+
+ setIsWaiting(true);
+ SendMessageComposer(new PurchasePhotoMessageComposer(''));
+ return;
+ case 'publish':
+ if(isWaiting) return;
+
+ setIsWaiting(true);
+ SendMessageComposer(new PublishPhotoMessageComposer());
+ return;
+ case 'cancel':
+ onCancelClick();
+ return;
+ }
+ };
+
+ useEffect(() =>
+ {
+ if(!base64Url) return;
+
+ GetRoomEngine().saveBase64AsScreenshot(base64Url);
+ }, [ base64Url ]);
+
+ if(!price) return null;
+
+ return (
+
+ processAction('close') } />
+
+
+ { (pictureUrl && pictureUrl.length) &&
+
}
+ { (!pictureUrl || !pictureUrl.length) &&
+
+ { LocalizeText('camera.loading') }
+
}
+
+
+
+
+ { LocalizeText('camera.purchase.header') }
+
+ { ((price.credits > 0) || (price.duckets > 0)) &&
+
+
{ LocalizeText('catalog.purchase.confirmation.dialog.cost') }
+ { (price.credits > 0) &&
+
+ { price.credits }
+
+
}
+ { (price.duckets > 0) &&
+
+ { price.duckets }
+
+
}
+
}
+ { (picturesBought > 0) &&
+
+ { LocalizeText('camera.purchase.count.info') } { picturesBought }
+ CreateLinkEvent('inventory/toggle') }>{ LocalizeText('camera.open.inventory') }
+ }
+
+
+
+
+
+ { !publishDisabled &&
+
+
+
+ { LocalizeText(wasPicturePublished ? 'camera.publish.successful' : 'camera.publish.explanation') }
+
+
+ { LocalizeText(wasPicturePublished ? 'camera.publish.success.short.info' : 'camera.publish.detailed.explanation') }
+
+ { wasPicturePublished &&
{ LocalizeText('camera.link.to.published') } }
+ { !wasPicturePublished && (price.publishDucketPrice > 0) &&
+
+
{ LocalizeText('catalog.purchase.confirmation.dialog.cost') }
+
+ { price.publishDucketPrice }
+
+
+
}
+ { (publishCooldown > 0) &&
{ LocalizeText('camera.publish.wait', [ 'minutes' ], [ Math.ceil(publishCooldown / 60).toString() ]) }
}
+
+ { !wasPicturePublished &&
+
+
+
}
+
}
+ { LocalizeText('camera.warning.disclaimer') }
+
+
+
+
+
+ );
+};
diff --git a/Coolui v3 test/src/components/camera/views/CameraWidgetShowPhotoView.tsx b/Coolui v3 test/src/components/camera/views/CameraWidgetShowPhotoView.tsx
new file mode 100644
index 0000000000..212bee385e
--- /dev/null
+++ b/Coolui v3 test/src/components/camera/views/CameraWidgetShowPhotoView.tsx
@@ -0,0 +1,68 @@
+import { GetRoomEngine, RoomObjectCategory, RoomObjectVariable } from '@nitrots/nitro-renderer';
+import { FC, useEffect, useState } from 'react';
+import { FaArrowLeft, FaArrowRight } from 'react-icons/fa';
+import { GetUserProfile, IPhotoData, LocalizeText } from '../../../api';
+import { Flex, Grid, Text } from '../../../common';
+
+export interface CameraWidgetShowPhotoViewProps {
+ currentIndex: number;
+ currentPhotos: IPhotoData[];
+ onClick?: () => void;
+}
+
+export const CameraWidgetShowPhotoView: FC = props => {
+ const { currentIndex = -1, currentPhotos = null, onClick = null } = props;
+ const [imageIndex, setImageIndex] = useState(0);
+
+ const currentImage = currentPhotos && currentPhotos.length ? currentPhotos[imageIndex] : null;
+
+ const next = () => {
+ setImageIndex(prevValue => {
+ let newIndex = prevValue + 1;
+ if (newIndex >= currentPhotos.length) newIndex = 0;
+ return newIndex;
+ });
+ };
+
+ const previous = () => {
+ setImageIndex(prevValue => {
+ let newIndex = prevValue - 1;
+ if (newIndex < 0) newIndex = currentPhotos.length - 1;
+ return newIndex;
+ });
+ };
+
+ useEffect(() => {
+ setImageIndex(currentIndex);
+ }, [currentIndex]);
+
+ if (!currentImage) return null;
+
+ const getUserData = (roomId: number, objectId: number, type: string): number | string =>
+ {
+ const roomObject = GetRoomEngine().getRoomObject(roomId, objectId, RoomObjectCategory.WALL);
+ if (!roomObject) return;
+ return type == 'username' ? roomObject.model.getValue(RoomObjectVariable.FURNITURE_OWNER_NAME) : roomObject.model.getValue(RoomObjectVariable.FURNITURE_OWNER_ID);
+ }
+
+ return (
+
+
+ {!currentImage.w && {LocalizeText('camera.loading')}}
+
+ {currentImage.m && currentImage.m.length && {currentImage.m}}
+
+ {currentImage.n || ''}
+ GetUserProfile(Number(getUserData(currentImage.s, Number(currentImage.u), 'id')))}> { getUserData(currentImage.s, Number(currentImage.u), 'username') }
+ GetUserProfile(currentImage.oi)}>{currentImage.o}
+ {new Date(currentImage.t * 1000).toLocaleDateString()}
+
+ {currentPhotos.length > 1 && (
+
+
+
+
+ )}
+
+ );
+};
\ No newline at end of file
diff --git a/Coolui v3 test/src/components/camera/views/editor/CameraWidgetEditorView.tsx b/Coolui v3 test/src/components/camera/views/editor/CameraWidgetEditorView.tsx
new file mode 100644
index 0000000000..75ae074508
--- /dev/null
+++ b/Coolui v3 test/src/components/camera/views/editor/CameraWidgetEditorView.tsx
@@ -0,0 +1,222 @@
+import { GetRoomCameraWidgetManager, IRoomCameraWidgetEffect, IRoomCameraWidgetSelectedEffect, NitroLogger, RoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer';
+import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import { FaSave, FaSearchMinus, FaSearchPlus, FaTrash } from 'react-icons/fa';
+import ReactSlider from 'react-slider';
+import { CameraEditorTabs, CameraPicture, CameraPictureThumbnail, LocalizeText } from '../../../../api';
+import { Button, Column, Flex, Grid, LayoutImage, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView, Text } from '../../../../common';
+import { CameraWidgetEffectListView } from './effect-list';
+
+export interface CameraWidgetEditorViewProps {
+ picture: CameraPicture;
+ availableEffects: IRoomCameraWidgetEffect[];
+ myLevel: number;
+ onClose: () => void;
+ onCancel: () => void;
+ onCheckout: (pictureUrl: string) => void;
+}
+
+const TABS: string[] = [ CameraEditorTabs.COLORMATRIX, CameraEditorTabs.COMPOSITE ];
+
+export const CameraWidgetEditorView: FC = props => {
+ const { picture = null, availableEffects = null, myLevel = 1, onClose = null, onCancel = null, onCheckout = null } = props;
+ const [ currentTab, setCurrentTab ] = useState(TABS[0]);
+ const [ selectedEffectName, setSelectedEffectName ] = useState(null);
+ const [ selectedEffects, setSelectedEffects ] = useState([]);
+ const [ effectsThumbnails, setEffectsThumbnails ] = useState([]);
+ const [ isZoomed, setIsZoomed ] = useState(false);
+ const [ currentPictureUrl, setCurrentPictureUrl ] = useState('');
+ const isBusy = useRef(false);
+
+ const getColorMatrixEffects = useMemo(() => {
+ return availableEffects.filter(effect => effect.colorMatrix);
+ }, [ availableEffects ]);
+
+ const getCompositeEffects = useMemo(() => {
+ return availableEffects.filter(effect => effect.texture);
+ }, [ availableEffects ]);
+
+ const getEffectList = useCallback(() => {
+ return currentTab === CameraEditorTabs.COLORMATRIX ? getColorMatrixEffects : getCompositeEffects;
+ }, [ currentTab, getColorMatrixEffects, getCompositeEffects ]);
+
+ const getSelectedEffectIndex = useCallback((name: string) => {
+ if (!name || !name.length || !selectedEffects || !selectedEffects.length) return -1;
+ return selectedEffects.findIndex(effect => effect.effect.name === name);
+ }, [ selectedEffects ]);
+
+ const getCurrentEffectIndex = useMemo(() => {
+ return getSelectedEffectIndex(selectedEffectName);
+ }, [ selectedEffectName, getSelectedEffectIndex ]);
+
+ const getCurrentEffect = useMemo(() => {
+ if (!selectedEffectName) return null;
+ return selectedEffects[getCurrentEffectIndex] || null;
+ }, [ selectedEffectName, getCurrentEffectIndex, selectedEffects ]);
+
+ const setSelectedEffectAlpha = useCallback((alpha: number) => {
+ const index = getCurrentEffectIndex;
+ if (index === -1) return;
+
+ setSelectedEffects(prevValue => {
+ const clone = [ ...prevValue ];
+ const currentEffect = clone[index];
+ clone[index] = new RoomCameraWidgetSelectedEffect(currentEffect.effect, alpha);
+ return clone;
+ });
+ }, [ getCurrentEffectIndex ]);
+
+ const processAction = useCallback((type: string, effectName: string = null) => {
+ switch (type) {
+ case 'close':
+ onClose();
+ return;
+ case 'cancel':
+ onCancel();
+ return;
+ case 'checkout':
+ onCheckout(currentPictureUrl);
+ return;
+ case 'change_tab':
+ setCurrentTab(String(effectName));
+ return;
+ case 'select_effect': {
+ const existingIndex = getSelectedEffectIndex(effectName);
+ if (existingIndex >= 0) return;
+
+ const effect = availableEffects.find(effect => effect.name === effectName);
+ if (!effect) return;
+
+ setSelectedEffects(prevValue => [ ...prevValue, new RoomCameraWidgetSelectedEffect(effect, 1) ]);
+ setSelectedEffectName(effect.name);
+ return;
+ }
+ case 'remove_effect': {
+ const existingIndex = getSelectedEffectIndex(effectName);
+ if (existingIndex === -1) return;
+
+ setSelectedEffects(prevValue => {
+ const clone = [ ...prevValue ];
+ clone.splice(existingIndex, 1);
+ return clone;
+ });
+
+ if (selectedEffectName === effectName) setSelectedEffectName(null);
+ return;
+ }
+ case 'clear_effects':
+ setSelectedEffectName(null);
+ setSelectedEffects([]);
+ return;
+ case 'download': {
+ (async () => {
+ const image = new Image();
+ image.src = currentPictureUrl;
+ const newWindow = window.open('');
+ newWindow.document.write(image.outerHTML);
+ })();
+ return;
+ }
+ case 'zoom':
+ setIsZoomed(prev => !prev);
+ return;
+ }
+ }, [ availableEffects, selectedEffectName, currentPictureUrl, getSelectedEffectIndex, onCancel, onCheckout, onClose ]);
+
+ useEffect(() => {
+ const processThumbnails = async () => {
+ const renderedEffects = await Promise.all(
+ availableEffects.map(effect =>
+ GetRoomCameraWidgetManager().applyEffects(picture.texture, [ new RoomCameraWidgetSelectedEffect(effect, 1) ], false)
+ )
+ );
+ setEffectsThumbnails(renderedEffects.map((image, index) => new CameraPictureThumbnail(availableEffects[index].name, image.src)));
+ };
+ processThumbnails();
+ }, [ picture, availableEffects ]);
+
+ useEffect(() => {
+ GetRoomCameraWidgetManager()
+ .applyEffects(picture.texture, selectedEffects, false) // Remove isZoomed from here
+ .then(imageElement => {
+ setCurrentPictureUrl(imageElement.src);
+ })
+ .catch(error => {
+ NitroLogger.error('Failed to apply effects to picture', error);
+ });
+ }, [ picture, selectedEffects ]); // Remove isZoomed from dependency array
+
+ return (
+
+ processAction('close') } />
+
+ { TABS.map(tab => (
+ processAction('change_tab', tab) }>
+
+
+ )) }
+
+
+
+
+
+
+
+
+
+ { selectedEffectName && (
+
+ { LocalizeText('camera.effect.name.' + selectedEffectName) }
+ setSelectedEffectAlpha(event) }
+ renderThumb={ ({ key, ...props }, state) => { state.valueNow }
}
+ />
+
+ ) }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/Coolui v3 test/src/components/camera/views/editor/effect-list/CameraWidgetEffectListItemView.tsx b/Coolui v3 test/src/components/camera/views/editor/effect-list/CameraWidgetEffectListItemView.tsx
new file mode 100644
index 0000000000..0b891a7457
--- /dev/null
+++ b/Coolui v3 test/src/components/camera/views/editor/effect-list/CameraWidgetEffectListItemView.tsx
@@ -0,0 +1,40 @@
+import { IRoomCameraWidgetEffect } from '@nitrots/nitro-renderer';
+import { FC } from 'react';
+import { FaLock, FaTimes } from 'react-icons/fa';
+import { LocalizeText } from '../../../../../api';
+import { Button, LayoutGridItem, Text } from '../../../../../common';
+
+export interface CameraWidgetEffectListItemViewProps
+{
+ effect: IRoomCameraWidgetEffect;
+ thumbnailUrl: string;
+ isActive: boolean;
+ isLocked: boolean;
+ selectEffect: () => void;
+ removeEffect: () => void;
+}
+
+export const CameraWidgetEffectListItemView: FC = props =>
+{
+ const { effect = null, thumbnailUrl = null, isActive = false, isLocked = false, selectEffect = null, removeEffect = null } = props;
+
+ return (
+ (!isActive && selectEffect()) }>
+ { isActive &&
+ }
+ { !isLocked && (thumbnailUrl && thumbnailUrl.length > 0) &&
+
+

+
}
+ { isLocked &&
+
+
+
+
+ { effect.minLevel }
+ }
+
+ );
+};
diff --git a/Coolui v3 test/src/components/camera/views/editor/effect-list/CameraWidgetEffectListView.tsx b/Coolui v3 test/src/components/camera/views/editor/effect-list/CameraWidgetEffectListView.tsx
new file mode 100644
index 0000000000..5f9b965cca
--- /dev/null
+++ b/Coolui v3 test/src/components/camera/views/editor/effect-list/CameraWidgetEffectListView.tsx
@@ -0,0 +1,33 @@
+import { IRoomCameraWidgetEffect, IRoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer';
+import { FC } from 'react';
+import { CameraPictureThumbnail } from '../../../../../api';
+import { Grid } from '../../../../../common';
+import { CameraWidgetEffectListItemView } from './CameraWidgetEffectListItemView';
+
+export interface CameraWidgetEffectListViewProps
+{
+ myLevel: number;
+ selectedEffects: IRoomCameraWidgetSelectedEffect[];
+ effects: IRoomCameraWidgetEffect[];
+ thumbnails: CameraPictureThumbnail[];
+ processAction: (type: string, name: string) => void;
+}
+
+export const CameraWidgetEffectListView: FC = props =>
+{
+ const { myLevel = 0, selectedEffects = [], effects = [], thumbnails = [], processAction = null } = props;
+
+ return (
+
+ { effects && (effects.length > 0) && effects.map((effect, index) =>
+ {
+ const thumbnailUrl = (thumbnails.find(thumbnail => (thumbnail.effectName === effect.name)));
+ const isActive = (selectedEffects.findIndex(selectedEffect => (selectedEffect.effect.name === effect.name)) > -1);
+
+ // return myLevel) } removeEffect={ () => processAction('remove_effect', effect.name) } selectEffect={ () => processAction('select_effect', effect.name) } thumbnailUrl={ ((thumbnailUrl && thumbnailUrl.thumbnailUrl) || null) } />;
+
+ return myLevel) } selectEffect={ () => processAction('select_effect', effect.name) } removeEffect={ () => processAction('remove_effect', effect.name) } />
+ }) }
+
+ );
+};
diff --git a/Coolui v3 test/src/components/camera/views/editor/effect-list/index.ts b/Coolui v3 test/src/components/camera/views/editor/effect-list/index.ts
new file mode 100644
index 0000000000..7a4ebdb50f
--- /dev/null
+++ b/Coolui v3 test/src/components/camera/views/editor/effect-list/index.ts
@@ -0,0 +1,2 @@
+export * from './CameraWidgetEffectListItemView';
+export * from './CameraWidgetEffectListView';
diff --git a/Coolui v3 test/src/components/camera/views/editor/index.ts b/Coolui v3 test/src/components/camera/views/editor/index.ts
new file mode 100644
index 0000000000..49c615ea64
--- /dev/null
+++ b/Coolui v3 test/src/components/camera/views/editor/index.ts
@@ -0,0 +1,2 @@
+export * from './CameraWidgetEditorView';
+export * from './effect-list';
diff --git a/Coolui v3 test/src/components/camera/views/index.ts b/Coolui v3 test/src/components/camera/views/index.ts
new file mode 100644
index 0000000000..cf44449313
--- /dev/null
+++ b/Coolui v3 test/src/components/camera/views/index.ts
@@ -0,0 +1,5 @@
+export * from './CameraWidgetCaptureView';
+export * from './CameraWidgetCheckoutView';
+export * from './CameraWidgetShowPhotoView';
+export * from './editor';
+export * from './editor/effect-list';
diff --git a/Coolui v3 test/src/components/campaign/CalendarItemView.tsx b/Coolui v3 test/src/components/campaign/CalendarItemView.tsx
new file mode 100644
index 0000000000..234387aa42
--- /dev/null
+++ b/Coolui v3 test/src/components/campaign/CalendarItemView.tsx
@@ -0,0 +1,53 @@
+import { GetRoomEngine, GetSessionDataManager } from '@nitrots/nitro-renderer';
+import { FC } from 'react';
+import { CalendarItemState, GetConfigurationValue, ICalendarItem } from '../../api';
+import { Column, Flex, LayoutImage } from '../../common';
+
+interface CalendarItemViewProps
+{
+ itemId: number;
+ state: number;
+ active?: boolean;
+ product?: ICalendarItem;
+ onClick: (itemId: number) => void;
+}
+
+export const CalendarItemView: FC = props =>
+{
+ const { itemId = -1, state = null, product = null, active = false, onClick = null } = props;
+
+ const getFurnitureIcon = (name: string) =>
+ {
+ let furniData = GetSessionDataManager().getFloorItemDataByName(name);
+ let url = null;
+
+ if(furniData) url = GetRoomEngine().getFurnitureFloorIconUrl(furniData.id);
+ else
+ {
+ furniData = GetSessionDataManager().getWallItemDataByName(name);
+
+ if(furniData) url = GetRoomEngine().getFurnitureWallIconUrl(furniData.id);
+ }
+
+ return url;
+ };
+
+ return (
+ onClick(itemId) }>
+ { (state === CalendarItemState.STATE_UNLOCKED) &&
+
+
+ { product &&
+ ('image.library.url') + product.customImage : getFurnitureIcon(product.productName) } /> }
+
+ }
+ { (state !== CalendarItemState.STATE_UNLOCKED) &&
+
+ { (state === CalendarItemState.STATE_LOCKED_AVAILABLE) &&
+ }
+ { ((state === CalendarItemState.STATE_LOCKED_EXPIRED) || (state === CalendarItemState.STATE_LOCKED_FUTURE)) &&
+ }
+ }
+
+ );
+};
diff --git a/Coolui v3 test/src/components/campaign/CalendarView.tsx b/Coolui v3 test/src/components/campaign/CalendarView.tsx
new file mode 100644
index 0000000000..057d088ef9
--- /dev/null
+++ b/Coolui v3 test/src/components/campaign/CalendarView.tsx
@@ -0,0 +1,144 @@
+import { GetSessionDataManager } from '@nitrots/nitro-renderer';
+import { FC, useState } from 'react';
+import { CalendarItemState, ICalendarItem, LocalizeText } from '../../api';
+import { Button, Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common';
+import { CalendarItemView } from './CalendarItemView';
+
+interface CalendarViewProps
+{
+ onClose(): void;
+ openPackage(id: number, asStaff: boolean): void;
+ receivedProducts: Map;
+ campaignName: string;
+ currentDay: number;
+ numDays: number;
+ openedDays: number[];
+ missedDays: number[];
+}
+
+const TOTAL_SHOWN_ITEMS = 5;
+
+export const CalendarView: FC = props =>
+{
+ const { onClose = null, campaignName = null, currentDay = null, numDays = null, missedDays = null, openedDays = null, openPackage = null, receivedProducts = null } = props;
+ const [ selectedDay, setSelectedDay ] = useState(currentDay);
+ const [ index, setIndex ] = useState(Math.max(0, (selectedDay - 1)));
+
+ const getDayState = (day: number) =>
+ {
+ if(openedDays.includes(day)) return CalendarItemState.STATE_UNLOCKED;
+
+ if(day > currentDay) return CalendarItemState.STATE_LOCKED_FUTURE;
+
+ if(missedDays.includes(day)) return CalendarItemState.STATE_LOCKED_EXPIRED;
+
+ return CalendarItemState.STATE_LOCKED_AVAILABLE;
+ };
+
+ const dayMessage = (day: number) =>
+ {
+ const state = getDayState(day);
+
+ switch(state)
+ {
+ case CalendarItemState.STATE_UNLOCKED:
+ return LocalizeText('campaign.calendar.info.unlocked');
+ case CalendarItemState.STATE_LOCKED_FUTURE:
+ return LocalizeText('campaign.calendar.info.future');
+ case CalendarItemState.STATE_LOCKED_EXPIRED:
+ return LocalizeText('campaign.calendar.info.expired');
+ default:
+ return LocalizeText('campaign.calendar.info.available.desktop');
+ }
+ };
+
+ const onClickNext = () =>
+ {
+ const nextDay = (selectedDay + 1);
+
+ if(nextDay === numDays) return;
+
+ setSelectedDay(nextDay);
+
+ if((index + TOTAL_SHOWN_ITEMS) < (nextDay + 1)) setIndex(index + 1);
+ };
+
+ const onClickPrev = () =>
+ {
+ const prevDay = (selectedDay - 1);
+
+ if(prevDay < 0) return;
+
+ setSelectedDay(prevDay);
+
+ if(index > prevDay) setIndex(index - 1);
+ };
+
+ const onClickItem = (item: number) =>
+ {
+ if(selectedDay === item)
+ {
+ const state = getDayState(item);
+
+ if(state === CalendarItemState.STATE_LOCKED_AVAILABLE) openPackage(item, false);
+
+ return;
+ }
+
+ setSelectedDay(item);
+ };
+
+ const forceOpen = () =>
+ {
+ const id = selectedDay;
+ const state = getDayState(id);
+
+ if(state !== CalendarItemState.STATE_UNLOCKED) openPackage(id, true);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ { LocalizeText('campaign.calendar.heading.day', [ 'number' ], [ (selectedDay + 1).toString() ]) }
+ { dayMessage(selectedDay) }
+
+
+ { GetSessionDataManager().isModerator &&
+ }
+
+
+
+
+
+
+
+
+
+ { [ ...Array(TOTAL_SHOWN_ITEMS) ].map((e, i) =>
+ {
+ const day = (index + i);
+
+ return (
+
+
+
+ );
+ }) }
+
+
+
+
+
+
+ );
+};
diff --git a/Coolui v3 test/src/components/campaign/CampaignView.tsx b/Coolui v3 test/src/components/campaign/CampaignView.tsx
new file mode 100644
index 0000000000..76230f489b
--- /dev/null
+++ b/Coolui v3 test/src/components/campaign/CampaignView.tsx
@@ -0,0 +1,101 @@
+import { AddLinkEventTracker, CampaignCalendarData, CampaignCalendarDataMessageEvent, CampaignCalendarDoorOpenedMessageEvent, ILinkEventTracker, OpenCampaignCalendarDoorAsStaffComposer, OpenCampaignCalendarDoorComposer, RemoveLinkEventTracker } from '@nitrots/nitro-renderer';
+import { FC, useEffect, useState } from 'react';
+import { CalendarItem, SendMessageComposer } from '../../api';
+import { useMessageEvent } from '../../hooks';
+import { CalendarView } from './CalendarView';
+
+export const CampaignView: FC<{}> = props =>
+{
+ const [ calendarData, setCalendarData ] = useState(null);
+ const [ lastOpenAttempt, setLastOpenAttempt ] = useState(-1);
+ const [ receivedProducts, setReceivedProducts ] = useState