commit ff80c504266d7cdb11350e56e24924750d4da480 Author: Ahmed Khidr Date: Wed Jan 17 08:52:08 2024 +0400 Initial commit diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 0000000..3e5809a --- /dev/null +++ b/.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/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..695c05d --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,110 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + } + }, + "settings": { + "react": { + "pragma": "React", + "version": "18.0.0" + } + }, + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended" + ], + "plugins": [ + "@typescript-eslint", + "react" + ], + "rules": { + "linebreak-style": [ + "off" + ], + "quotes": [ + "error", + "single" + ], + "@typescript-eslint/indent": [ + "error", + 4, + { + "SwitchCase": 1 + } + ], + "array-bracket-spacing": [ + "error", + "always" + ], + "brace-style": [ + "error", + "allman" + ], + "template-curly-spacing": [ + "error", + "always" + ], + "no-multi-spaces": [ + "error" + ], + "@typescript-eslint/object-curly-spacing": [ + "error", + "always", + { + "arraysInObjects": true, + "objectsInObjects": false + } + ], + "@typescript-eslint/ban-types": [ + "error", + { + "types": { + "String": true, + "Boolean": true, + "Number": true, + "Symbol": true, + "{}": false, + "Object": false, + "object": false, + "Function": false + }, + "extendDefaults": true + } + ], + "no-switch-case-fall-through": [ + "off" + ], + "jsx-quotes": [ + "error" + ], + "react/prop-types": [ + "off" + ], + "react/jsx-curly-spacing": [ + "error", + { + "when": "always", + "children": true + } + ], + "react/jsx-equals-spacing": [ + "error" + ], + "react/jsx-newline": [ + "error", + { + "prevent": true + } + ] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..154341f --- /dev/null +++ b/.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/.gitlab-ci.yml-disabled b/.gitlab-ci.yml-disabled new file mode 100644 index 0000000..0a291a9 --- /dev/null +++ b/.gitlab-ci.yml-disabled @@ -0,0 +1,29 @@ +image: node:16.13 + +stages: + - install-dependencies + - build + +Install Dependencies: + stage: install-dependencies + script: + - yarn install + cache: + key: ${CI_COMMIT_BRANCH} + paths: + - node_modules + +Build Nitro: + stage: build + script: + - cp public/renderer-config.json.example public/renderer-config.json + - cp public/ui-config.json.example public/ui-config.json + - yarn build:prod + cache: + key: ${CI_COMMIT_BRANCH} + paths: + - node_modules + artifacts: + expire_in: 2 weeks + paths: + - build/* diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3fdf856 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,32 @@ +{ + "typescript.tsdk": "node_modules\\typescript\\lib", + "typescript.preferences.importModuleSpecifier": "relative", + "typescript.preferences.quoteStyle": "single", + "typescript.format.placeOpenBraceOnNewLineForControlBlocks": true, + "typescript.format.placeOpenBraceOnNewLineForFunctions": true, + "editor.wordWrap": "on", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true, + "source.fixAll.sortJSON": false, + "source.organizeImports": true + }, + "editor.formatOnSave": false, + "git.ignoreLimitWarning": true, + "files.eol": "\n", + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true, + "emmet.showExpandedAbbreviation": "never", + "eslint.format.enable": true, + "eslint.validate": [ + "javascript", + "typescript" + ], + "eslint.workingDirectories": [ + { + "pattern": "./src" + } + ], + "javascript.format.enable": false, + "thunder-client.saveToWorkspace": false, + "thunder-client.workspaceRelativePath": "." +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..7b6ffc1 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# Nitro React v2.1 + +## 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 +- Clone Nitro + - `git clone https://git.krews.org/nitro/nitro-react.git` +- Install the dependencies + - `yarn install` + - This may take some time, please be patient +- 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` + - 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/index.html b/index.html new file mode 100644 index 0000000..dd4538b --- /dev/null +++ b/index.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + Nitro + + + +
+ + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..e541bc3 --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "nitro-react", + "version": "2.1.1", + "homepage": ".", + "private": true, + "scripts": { + "start": "vite", + "build": "vite build", + "build:prod": "npx browserslist@latest --update-db && yarn build", + "eslint": "eslint src --ext .ts,.tsx" + }, + "dependencies": { + "@nitrots/nitro-renderer": "^1.6.6", + "@tanstack/react-virtual": "^3.0.0-alpha.0", + "emoji-toolkit": "^7.0.1", + "react": "^18.2.0", + "react-bootstrap": "^2.2.2", + "react-dom": "^18.2.0", + "react-icons": "^4.7.1", + "react-slider": "^2.0.0", + "react-youtube": "^7.13.1", + "typescript": "^4.3.5", + "use-between": "^1.3.4" + }, + "devDependencies": { + "@types/node": "^18.6.1", + "@types/react": "^18.0.15", + "@types/react-dom": "^18.0.6", + "@types/react-slider": "^1.3.1", + "@typescript-eslint/eslint-plugin": "^5.30.7", + "@typescript-eslint/parser": "^5.30.7", + "@vitejs/plugin-react": "^3.0.0", + "eslint": "^8.20.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jsx-a11y": "^6.6.0", + "eslint-plugin-react": "^7.30.1", + "eslint-plugin-react-hooks": "^4.6.0", + "sass": "^1.56.2", + "vite": "^4.0.1" + } +} diff --git a/public/android-chrome-192x192.png b/public/android-chrome-192x192.png new file mode 100644 index 0000000..634eb06 Binary files /dev/null and b/public/android-chrome-192x192.png differ diff --git a/public/android-chrome-512x512.png b/public/android-chrome-512x512.png new file mode 100644 index 0000000..33cf6c6 Binary files /dev/null and b/public/android-chrome-512x512.png differ diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000..349b3dd Binary files /dev/null and b/public/apple-touch-icon.png differ diff --git a/public/browserconfig.xml b/public/browserconfig.xml new file mode 100644 index 0000000..b3930d0 --- /dev/null +++ b/public/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #da532c + + + diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png new file mode 100644 index 0000000..788a7f1 Binary files /dev/null and b/public/favicon-16x16.png differ diff --git a/public/favicon-32x32.png b/public/favicon-32x32.png new file mode 100644 index 0000000..ef4a606 Binary files /dev/null and b/public/favicon-32x32.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..34ba1b3 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/mstile-150x150.png b/public/mstile-150x150.png new file mode 100644 index 0000000..3a555aa Binary files /dev/null and b/public/mstile-150x150.png differ diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/public/safari-pinned-tab.svg b/public/safari-pinned-tab.svg new file mode 100644 index 0000000..dc7ced3 --- /dev/null +++ b/public/safari-pinned-tab.svg @@ -0,0 +1,154 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + diff --git a/public/site.webmanifest b/public/site.webmanifest new file mode 100644 index 0000000..6264b89 --- /dev/null +++ b/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/src/App.scss b/src/App.scss new file mode 100644 index 0000000..eaf0c4c --- /dev/null +++ b/src/App.scss @@ -0,0 +1,105 @@ +$toolbar-me-zindex: 90; +$chatinput-zindex: 80; +$toolbar-zindex: 70; +$rightside-zindex: 69; +$notification-center-zindex: 68; +$toolbar-memenu-zindex: 60; +$roomtools-zindex: 50; +$context-menu-zindex: 40; +$infostand-zindex: 30; +$quiz-zindex: 21; +$chat-zindex: 20; +$highscore-zindex: 19; + +$grid-bg-color: #cdd3d9; +$grid-border-color: $muted; +$grid-active-bg-color: #ececec; +$grid-active-border-color: $white; + +$toolbar-height: 55px; + +$achievement-width: 375px; +$achievement-height: 405px; + +$avatar-editor-width: 746px; +$avatar-editor-height: 445px; + +$catalog-width: 650px; +$catalog-height: 480px; + +$inventory-width: 528px; +$inventory-height: 320px; + +$navigator-width: 420px; +$navigator-height: 440px; + +$chat-input-style-selector-widget-width: 210px; +$chat-input-style-selector-widget-height: 200px; + +$user-profile-width: 470px; +$user-profile-height: 460px; + +$nitro-widget-custom-stack-height-width: 275px; +$nitro-widget-custom-stack-height-height: 220px; + +$nitro-widget-exchange-credit-width: 375px; +$nitro-widget-exchange-credit-height: 150px; + +$nitro-widget-crafting-width: 500px; +$nitro-widget-crafting-height: 300px; + +$chat-history-width: 300px; +$chat-history-height: 300px; + +$friends-list-width: 250px; +$friends-list-height: 300px; + +$help-width: 450px; +$help-height: 290px; + +$nitropedia-width: 400px; +$nitropedia-height: 400px; + +$nitrobubblehidden-width: 400px; +$nitrobubblehidden-height: 400px; + +$messenger-width: 580px; +$messenger-height: 455px; + +$marketplace-post-offer-width: 430px; +$marketplace-post-offer-height: 250px; + +$camera-editor-width: 600px; +$camera-editor-height: 500px; + +$camera-checkout-width: 350px; + +$room-info-width: 325px; + +$nitro-group-creator-width: 383px; +$nitro-mod-tools-width: 175px; + +$nitro-group-manager-width: 390px; +$nitro-group-manager-height: 355px; + +$nitro-chooser-width: 200px; +$nitro-chooser-height: 200px; + +$nitro-doorbell-width: 300px; +$nitro-doorbell-height: 200px; + +$nitro-guide-tool-width: 250px; + +$nitro-floor-editor-width: 760px; +$nitro-floor-editor-height: 500px; + +$nitro-calendar-width: 850px; +$nitro-calendar-height: 400px; + +.nitro-app { + width: 100%; + height: 100%; +} + +@import './common'; +@import './components'; diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..072b05d --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,141 @@ +import { ConfigurationEvent, GetAssetManager, HabboWebTools, LegacyExternalInterface, Nitro, NitroCommunicationDemoEvent, NitroConfiguration, NitroEvent, NitroLocalizationEvent, NitroVersion, RoomEngineEvent } from '@nitrots/nitro-renderer'; +import { FC, useCallback, useEffect, useState } from 'react'; +import { GetCommunication, GetConfiguration, GetNitroInstance, GetUIVersion } from './api'; +import { Base, TransitionAnimation, TransitionAnimationTypes } from './common'; +import { LoadingView } from './components/loading/LoadingView'; +import { MainView } from './components/main/MainView'; +import { useConfigurationEvent, useLocalizationEvent, useMainEvent, useRoomEngineEvent } from './hooks'; + +NitroVersion.UI_VERSION = GetUIVersion(); + +export const App: FC<{}> = props => +{ + const [ isReady, setIsReady ] = useState(false); + const [ isError, setIsError ] = useState(false); + const [ message, setMessage ] = useState('Getting Ready'); + const [ percent, setPercent ] = useState(0); + const [ imageRendering, setImageRendering ] = useState(true); + + if(!GetNitroInstance()) + { + //@ts-ignore + if(!NitroConfig) throw new Error('NitroConfig is not defined!'); + + Nitro.bootstrap(); + } + + const handler = useCallback(async (event: NitroEvent) => + { + switch(event.type) + { + case ConfigurationEvent.LOADED: + GetNitroInstance().localization.init(); + setPercent(prevValue => (prevValue + 20)); + return; + case ConfigurationEvent.FAILED: + setIsError(true); + setMessage('Configuration Failed'); + return; + case Nitro.WEBGL_UNAVAILABLE: + setIsError(true); + setMessage('WebGL Required'); + return; + case Nitro.WEBGL_CONTEXT_LOST: + setIsError(true); + setMessage('WebGL Context Lost - Reloading'); + + setTimeout(() => window.location.reload(), 1500); + return; + case NitroCommunicationDemoEvent.CONNECTION_HANDSHAKING: + setPercent(prevValue => (prevValue + 20)); + return; + case NitroCommunicationDemoEvent.CONNECTION_HANDSHAKE_FAILED: + setIsError(true); + setMessage('Handshake Failed'); + return; + case NitroCommunicationDemoEvent.CONNECTION_AUTHENTICATED: + setPercent(prevValue => (prevValue + 20)); + + GetNitroInstance().init(); + + if(LegacyExternalInterface.available) LegacyExternalInterface.call('legacyTrack', 'authentication', 'authok', []); + return; + case NitroCommunicationDemoEvent.CONNECTION_ERROR: + setIsError(true); + setMessage('Connection Error'); + return; + case NitroCommunicationDemoEvent.CONNECTION_CLOSED: + //if(GetNitroInstance().roomEngine) GetNitroInstance().roomEngine.dispose(); + //setIsError(true); + setMessage('Connection Error'); + + HabboWebTools.send(-1, 'client.init.handshake.fail'); + return; + case RoomEngineEvent.ENGINE_INITIALIZED: + setPercent(prevValue => (prevValue + 20)); + + setTimeout(() => setIsReady(true), 300); + return; + case NitroLocalizationEvent.LOADED: { + const assetUrls = GetConfiguration('preload.assets.urls'); + const urls: string[] = []; + + if(assetUrls && assetUrls.length) for(const url of assetUrls) urls.push(NitroConfiguration.interpolate(url)); + + const status = await GetAssetManager().downloadAssets(urls); + + if(status) + { + GetCommunication().init(); + + setPercent(prevValue => (prevValue + 20)) + } + else + { + setIsError(true); + setMessage('Assets Failed'); + } + return; + } + } + }, []); + + useMainEvent(Nitro.WEBGL_UNAVAILABLE, handler); + useMainEvent(Nitro.WEBGL_CONTEXT_LOST, handler); + useMainEvent(NitroCommunicationDemoEvent.CONNECTION_HANDSHAKING, handler); + useMainEvent(NitroCommunicationDemoEvent.CONNECTION_HANDSHAKE_FAILED, handler); + useMainEvent(NitroCommunicationDemoEvent.CONNECTION_AUTHENTICATED, handler); + useMainEvent(NitroCommunicationDemoEvent.CONNECTION_ERROR, handler); + useMainEvent(NitroCommunicationDemoEvent.CONNECTION_CLOSED, handler); + useRoomEngineEvent(RoomEngineEvent.ENGINE_INITIALIZED, handler); + useLocalizationEvent(NitroLocalizationEvent.LOADED, handler); + useConfigurationEvent(ConfigurationEvent.LOADED, handler); + useConfigurationEvent(ConfigurationEvent.FAILED, handler); + + useEffect(() => + { + GetNitroInstance().core.configuration.init(); + + const resize = (event: UIEvent) => setImageRendering(!(window.devicePixelRatio % 1)); + + window.addEventListener('resize', resize); + + resize(null); + + return () => + { + window.removeEventListener('resize', resize); + } + }, []); + + return ( + + { (!isReady || isError) && + } + + + + + + ); +} diff --git a/src/api/GetRendererVersion.ts b/src/api/GetRendererVersion.ts new file mode 100644 index 0000000..bb9e461 --- /dev/null +++ b/src/api/GetRendererVersion.ts @@ -0,0 +1,3 @@ +import { NitroVersion } from '@nitrots/nitro-renderer'; + +export const GetRendererVersion = () => NitroVersion.RENDERER_VERSION; diff --git a/src/api/GetUIVersion.ts b/src/api/GetUIVersion.ts new file mode 100644 index 0000000..cf8d5a5 --- /dev/null +++ b/src/api/GetUIVersion.ts @@ -0,0 +1 @@ +export const GetUIVersion = () => '2.1.1'; diff --git a/src/api/achievements/AchievementCategory.ts b/src/api/achievements/AchievementCategory.ts new file mode 100644 index 0000000..906d8da --- /dev/null +++ b/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/src/api/achievements/AchievementUtilities.ts b/src/api/achievements/AchievementUtilities.ts new file mode 100644 index 0000000..b581f97 --- /dev/null +++ b/src/api/achievements/AchievementUtilities.ts @@ -0,0 +1,97 @@ +import { AchievementData } from '@nitrots/nitro-renderer'; +import { GetConfiguration, GetLocalization } 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 = GetLocalization().getPreviousLevelBadgeId(badgeId); + + return badgeId; + } + + public static getAchievementCategoryImageUrl(category: IAchievementCategory, progress: number = null, icon: boolean = false): string + { + const imageUrl = GetConfiguration('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 = GetConfiguration('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/src/api/achievements/IAchievementCategory.ts b/src/api/achievements/IAchievementCategory.ts new file mode 100644 index 0000000..a049d46 --- /dev/null +++ b/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/src/api/achievements/index.ts b/src/api/achievements/index.ts new file mode 100644 index 0000000..a3d44b7 --- /dev/null +++ b/src/api/achievements/index.ts @@ -0,0 +1,3 @@ +export * from './AchievementCategory'; +export * from './AchievementUtilities'; +export * from './IAchievementCategory'; diff --git a/src/api/avatar/AvatarEditorAction.ts b/src/api/avatar/AvatarEditorAction.ts new file mode 100644 index 0000000..064d6df --- /dev/null +++ b/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/src/api/avatar/AvatarEditorGridColorItem.ts b/src/api/avatar/AvatarEditorGridColorItem.ts new file mode 100644 index 0000000..dee3dae --- /dev/null +++ b/src/api/avatar/AvatarEditorGridColorItem.ts @@ -0,0 +1,65 @@ +import { ColorConverter, IPartColor } from '@nitrots/nitro-renderer'; + +export class AvatarEditorGridColorItem +{ + private _partColor: IPartColor; + private _isDisabled: boolean; + private _isHC: boolean; + private _isSelected: boolean; + private _notifier: () => void; + + constructor(partColor: IPartColor, isDisabled: boolean = false) + { + this._partColor = partColor; + this._isDisabled = isDisabled; + this._isHC = (this._partColor.clubLevel > 0); + this._isSelected = false; + } + + public dispose(): void + { + this._partColor = null; + } + + public get partColor(): IPartColor + { + return this._partColor; + } + + public get color(): string + { + return ColorConverter.int2rgb(this._partColor.rgb); + } + + public get isDisabled(): boolean + { + return this._isDisabled; + } + + public get isHC(): boolean + { + return this._isHC; + } + + public get isSelected(): boolean + { + return this._isSelected; + } + + public set isSelected(flag: boolean) + { + this._isSelected = flag; + + if(this.notify) this.notify(); + } + + public get notify(): () => void + { + return this._notifier; + } + + public set notify(notifier: () => void) + { + this._notifier = notifier; + } +} diff --git a/src/api/avatar/AvatarEditorGridPartItem.ts b/src/api/avatar/AvatarEditorGridPartItem.ts new file mode 100644 index 0000000..a3b1661 --- /dev/null +++ b/src/api/avatar/AvatarEditorGridPartItem.ts @@ -0,0 +1,337 @@ +import { AvatarFigurePartType, IAvatarImageListener, IAvatarRenderManager, IFigurePart, IFigurePartSet, IGraphicAsset, IPartColor, NitroAlphaFilter, NitroContainer, NitroSprite, TextureUtils } from '@nitrots/nitro-renderer'; +import { GetAvatarRenderManager } from '../nitro'; +import { FigureData } from './FigureData'; + +export class AvatarEditorGridPartItem implements IAvatarImageListener +{ + private static ALPHA_FILTER: NitroAlphaFilter = new NitroAlphaFilter(0.2); + private static THUMB_DIRECTIONS: number[] = [ 2, 6, 0, 4, 3, 1 ]; + 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 _renderManager: IAvatarRenderManager; + private _partSet: IFigurePartSet; + private _partColors: IPartColor[]; + private _useColors: boolean; + private _isDisabled: boolean; + private _thumbContainer: NitroContainer; + private _imageUrl: string; + private _maxColorIndex: number; + private _isValidFigure: boolean; + private _isHC: boolean; + private _isSellable: boolean; + private _isClear: boolean; + private _isSelected: boolean; + private _disposed: boolean; + private _isInitalized: boolean; + private _notifier: () => void; + + constructor(partSet: IFigurePartSet, partColors: IPartColor[], useColors: boolean = true, isDisabled: boolean = false) + { + this._renderManager = GetAvatarRenderManager(); + this._partSet = partSet; + this._partColors = partColors; + this._useColors = useColors; + this._isDisabled = isDisabled; + this._thumbContainer = null; + this._imageUrl = null; + this._maxColorIndex = 0; + this._isValidFigure = false; + this._isHC = false; + this._isSellable = false; + this._isClear = false; + this._isSelected = false; + this._disposed = false; + this._isInitalized = false; + + if(partSet) + { + const colors = partSet.parts; + + for(const color of colors) this._maxColorIndex = Math.max(this._maxColorIndex, color.colorLayerIndex); + } + } + + public init(): void + { + if(this._isInitalized) return; + + this._isInitalized = true; + + this.update(); + } + + public dispose(): void + { + if(this._disposed) return; + + this._renderManager = null; + this._partSet = null; + this._partColors = null; + this._imageUrl = null; + this._disposed = true; + this._isInitalized = false; + + if(this._thumbContainer) + { + this._thumbContainer.destroy(); + + this._thumbContainer = null; + } + } + + public update(): void + { + this.updateThumbVisualization(); + } + + private analyzeFigure(): boolean + { + if(!this._renderManager || !this._partSet || !this._partSet.parts || !this._partSet.parts.length) return false; + + const figureContainer = this._renderManager.createFigureContainer(((this.partSet.type + '-') + this.partSet.id)); + + if(!this._renderManager.isFigureContainerReady(figureContainer)) + { + this._renderManager.downloadAvatarFigure(figureContainer, this); + + return false; + } + + this._isValidFigure = true; + + return true; + } + + private renderThumb(): NitroContainer + { + if(!this._renderManager || !this._partSet) return null; + + if(!this._isValidFigure) + { + if(!this.analyzeFigure()) return null; + } + + const parts = this._partSet.parts.concat().sort(this.sortByDrawOrder); + const container = new NitroContainer(); + + for(const part of parts) + { + if(!part) continue; + + let asset: IGraphicAsset = null; + let direction = 0; + let hasAsset = false; + + while(!hasAsset && (direction < AvatarEditorGridPartItem.THUMB_DIRECTIONS.length)) + { + const assetName = ((((((((((FigureData.SCALE + '_') + FigureData.STD) + '_') + part.type) + '_') + part.id) + '_') + AvatarEditorGridPartItem.THUMB_DIRECTIONS[direction]) + '_') + FigureData.DEFAULT_FRAME); + + asset = this._renderManager.getAssetByName(assetName); + + if(asset && asset.texture) + { + hasAsset = true; + } + else + { + direction++; + } + } + + if(!hasAsset) continue; + + const x = asset.offsetX; + const y = asset.offsetY; + let partColor: IPartColor = null; + + if(this._useColors && (part.colorLayerIndex > 0)) + { + const color = this._partColors[(part.colorLayerIndex - 1)]; + + if(color) partColor = color; + } + + const sprite = new NitroSprite(asset.texture); + + sprite.position.set(x, y); + + if(partColor) sprite.tint = partColor.rgb; + + container.addChild(sprite); + } + + return container; + } + + private updateThumbVisualization(): void + { + if(!this._isInitalized) return; + + let container = this._thumbContainer; + + if(!container) container = this.renderThumb(); + + if(!container) return; + + if(this._partSet) + { + this._isHC = (this._partSet.clubLevel > 0); + this._isSellable = this._partSet.isSellable; + } + else + { + this._isHC = false; + this._isSellable = false; + } + + if(this._isDisabled) this.setAlpha(container, 0.2); + + this._imageUrl = TextureUtils.generateImageUrl(container); + + if(this.notify) this.notify(); + } + + private setAlpha(container: NitroContainer, alpha: number): NitroContainer + { + container.filters = [ AvatarEditorGridPartItem.ALPHA_FILTER ]; + + return container; + } + + private sortByDrawOrder(a: IFigurePart, b: IFigurePart): number + { + const indexA = AvatarEditorGridPartItem.DRAW_ORDER.indexOf(a.type); + const indexB = AvatarEditorGridPartItem.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; + } + + public resetFigure(figure: string): void + { + if(!this.analyzeFigure()) return; + + this.update(); + } + + public get disposed(): boolean + { + return this._disposed; + } + + public get id(): number + { + if(!this._partSet) return -1; + + return this._partSet.id; + } + + public get partSet(): IFigurePartSet + { + return this._partSet; + } + + public set partColors(partColors: IPartColor[]) + { + this._partColors = partColors; + + this.update(); + } + + public get isDisabled(): boolean + { + return this._isDisabled; + } + + public set thumbContainer(container: NitroContainer) + { + this._thumbContainer = container; + + this.update(); + } + + public get imageUrl(): string + { + return this._imageUrl; + } + + public get maxColorIndex(): number + { + return this._maxColorIndex; + } + + public get isHC(): boolean + { + return this._isHC; + } + + public get isSellable(): boolean + { + return this._isSellable; + } + + public get isClear(): boolean + { + return this._isClear; + } + + public set isClear(flag: boolean) + { + this._isClear = flag; + } + + public get isSelected(): boolean + { + return this._isSelected; + } + + public set isSelected(flag: boolean) + { + this._isSelected = flag; + + if(this.notify) this.notify(); + } + + public get notify(): () => void + { + return this._notifier; + } + + public set notify(notifier: () => void) + { + this._notifier = notifier; + } +} diff --git a/src/api/avatar/AvatarEditorUtilities.ts b/src/api/avatar/AvatarEditorUtilities.ts new file mode 100644 index 0000000..3626ab8 --- /dev/null +++ b/src/api/avatar/AvatarEditorUtilities.ts @@ -0,0 +1,277 @@ +import { IPartColor } from '@nitrots/nitro-renderer'; +import { GetAvatarPalette, GetAvatarRenderManager, GetAvatarSetType, GetClubMemberLevel, GetConfiguration } from '../nitro'; +import { AvatarEditorGridColorItem } from './AvatarEditorGridColorItem'; +import { AvatarEditorGridPartItem } from './AvatarEditorGridPartItem'; +import { CategoryBaseModel } from './CategoryBaseModel'; +import { CategoryData } from './CategoryData'; +import { FigureData } from './FigureData'; + +export class AvatarEditorUtilities +{ + private static MAX_PALETTES: number = 2; + + public static CURRENT_FIGURE: FigureData = null; + public static FIGURE_SET_IDS: number[] = []; + public static BOUND_FURNITURE_NAMES: string[] = []; + + public static getGender(gender: string): string + { + switch(gender) + { + case FigureData.MALE: + case 'm': + case 'M': + gender = FigureData.MALE; + break; + case FigureData.FEMALE: + case 'f': + case 'F': + gender = FigureData.FEMALE; + break; + default: + gender = FigureData.MALE; + } + + return gender; + } + + public static hasFigureSetId(setId: number): boolean + { + return (this.FIGURE_SET_IDS.indexOf(setId) >= 0); + } + + public static createCategory(model: CategoryBaseModel, name: string): CategoryData + { + if(!model || !name || !this.CURRENT_FIGURE) return null; + + const partItems: AvatarEditorGridPartItem[] = []; + const colorItems: AvatarEditorGridColorItem[][] = []; + + let i = 0; + + while(i < this.MAX_PALETTES) + { + colorItems.push([]); + + i++; + } + + const setType = GetAvatarSetType(name); + + if(!setType) return null; + + const palette = GetAvatarPalette(setType.paletteID); + + if(!palette) return null; + + let colorIds = this.CURRENT_FIGURE.getColorIds(name); + + if(!colorIds) colorIds = []; + + const partColors: IPartColor[] = new Array(colorIds.length); + const clubItemsDimmed = this.clubItemsDimmed; + const clubMemberLevel = GetClubMemberLevel(); + + for(const partColor of palette.colors.getValues()) + { + if(partColor.isSelectable && (clubItemsDimmed || (clubMemberLevel >= partColor.clubLevel))) + { + let i = 0; + + while(i < this.MAX_PALETTES) + { + const isDisabled = (clubMemberLevel < partColor.clubLevel); + const colorItem = new AvatarEditorGridColorItem(partColor, isDisabled); + + colorItems[i].push(colorItem); + + i++; + } + + if(name !== FigureData.FACE) + { + let i = 0; + + while(i < colorIds.length) + { + if(partColor.id === colorIds[i]) partColors[i] = partColor; + + i++; + } + } + } + } + + let mandatorySetIds: string[] = []; + + if(clubItemsDimmed) + { + mandatorySetIds = GetAvatarRenderManager().getMandatoryAvatarPartSetIds(this.CURRENT_FIGURE.gender, 2); + } + else + { + mandatorySetIds = GetAvatarRenderManager().getMandatoryAvatarPartSetIds(this.CURRENT_FIGURE.gender, clubMemberLevel); + } + + const isntMandatorySet = (mandatorySetIds.indexOf(name) === -1); + + if(isntMandatorySet) + { + const partItem = new AvatarEditorGridPartItem(null, null, false); + + partItem.isClear = true; + + partItems.push(partItem); + } + + const usesColors = (name !== FigureData.FACE); + const partSets = setType.partSets; + const totalPartSets = partSets.length; + + i = (totalPartSets - 1); + + while(i >= 0) + { + const partSet = partSets.getWithIndex(i); + + let isValidGender = false; + + if(partSet.gender === FigureData.UNISEX) + { + isValidGender = true; + } + + else if(partSet.gender === this.CURRENT_FIGURE.gender) + { + isValidGender = true; + } + + if(partSet.isSelectable && isValidGender && (clubItemsDimmed || (clubMemberLevel >= partSet.clubLevel))) + { + const isDisabled = (clubMemberLevel < partSet.clubLevel); + + let isValid = true; + + if(partSet.isSellable) isValid = this.hasFigureSetId(partSet.id); + + if(isValid) partItems.push(new AvatarEditorGridPartItem(partSet, partColors, usesColors, isDisabled)); + } + + i--; + } + + partItems.sort(this.clubItemsFirst ? this.clubSorter : this.noobSorter); + + // if(this._forceSellableClothingVisibility || GetNitroInstance().getConfiguration("avatareditor.support.sellablefurni", false)) + // { + // _local_31 = (this._manager.windowManager.assets.getAssetByName("camera_zoom_in") as BitmapDataAsset); + // _local_32 = (_local_31.content as BitmapData).clone(); + // _local_33 = (AvatarEditorView._Str_6802.clone() as IWindowContainer); + // _local_33.name = AvatarEditorGridView.GET_MORE; + // _local_7 = new AvatarEditorGridPartItem(_local_33, k, null, null, false); + // _local_7._Str_3093 = _local_32; + // _local_3.push(_local_7); + // } + + i = 0; + + while(i < this.MAX_PALETTES) + { + colorItems[i].sort(this.colorSorter); + + i++; + } + + return new CategoryData(name, partItems, colorItems); + } + + public static clubSorter(a: AvatarEditorGridPartItem, b: AvatarEditorGridPartItem): number + { + const clubLevelA = (!a.partSet ? 9999999999 : a.partSet.clubLevel); + const clubLevelB = (!b.partSet ? 9999999999 : 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(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; + } + + public static colorSorter(a: AvatarEditorGridColorItem, b: AvatarEditorGridColorItem): number + { + const clubLevelA = (!a.partColor ? -1 : a.partColor.clubLevel); + const clubLevelB = (!b.partColor ? -1 : b.partColor.clubLevel); + + if(clubLevelA < clubLevelB) return -1; + + if(clubLevelA > clubLevelB) return 1; + + if(a.partColor.index < b.partColor.index) return -1; + + if(a.partColor.index > b.partColor.index) return 1; + + return 0; + } + + public static noobSorter(a: AvatarEditorGridPartItem, b: AvatarEditorGridPartItem): number + { + 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(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; + } + + public static avatarSetFirstSelectableColor(name: string): number + { + const setType = GetAvatarSetType(name); + + if(!setType) return -1; + + const palette = GetAvatarPalette(setType.paletteID); + + if(!palette) return -1; + + for(const color of palette.colors.getValues()) + { + if(!color.isSelectable || (GetClubMemberLevel() < color.clubLevel)) continue; + + return color.id; + } + + return -1; + } + + public static get clubItemsFirst(): boolean + { + return GetConfiguration('avatareditor.show.clubitems.first', true); + } + + public static get clubItemsDimmed(): boolean + { + return GetConfiguration('avatareditor.show.clubitems.dimmed', true); + } +} diff --git a/src/api/avatar/BodyModel.ts b/src/api/avatar/BodyModel.ts new file mode 100644 index 0000000..7cdb34c --- /dev/null +++ b/src/api/avatar/BodyModel.ts @@ -0,0 +1,76 @@ +import { AvatarEditorFigureCategory, AvatarScaleType, AvatarSetType } from '@nitrots/nitro-renderer'; +import { GetAvatarRenderManager } from '../nitro'; +import { AvatarEditorUtilities } from './AvatarEditorUtilities'; +import { CategoryBaseModel } from './CategoryBaseModel'; +import { FigureData } from './FigureData'; + +export class BodyModel extends CategoryBaseModel +{ + private _imageCallBackHandled: boolean = false; + + public init(): void + { + super.init(); + + this.addCategory(FigureData.FACE); + + this._isInitalized = true; + } + + public selectColor(category: string, colorIndex: number, paletteId: number): void + { + super.selectColor(category, colorIndex, paletteId); + + this.updateSelectionsFromFigure(FigureData.FACE); + } + + protected updateSelectionsFromFigure(name: string): void + { + if(!this._categories || !AvatarEditorUtilities.CURRENT_FIGURE) return; + + const category = this._categories.get(name); + + if(!category) return; + + const setId = AvatarEditorUtilities.CURRENT_FIGURE.getPartSetId(name); + + let colorIds = AvatarEditorUtilities.CURRENT_FIGURE.getColorIds(name); + + if(!colorIds) colorIds = []; + + category.selectPartId(setId); + category.selectColorIds(colorIds); + + for(const part of category.parts) + { + const resetFigure = (figure: string) => + { + const figureString = AvatarEditorUtilities.CURRENT_FIGURE.getFigureStringWithFace(part.id); + const avatarImage = GetAvatarRenderManager().createAvatarImage(figureString, AvatarScaleType.LARGE, null, { resetFigure, dispose: null, disposed: false }); + + const sprite = avatarImage.getImageAsSprite(AvatarSetType.HEAD); + + if(sprite) + { + sprite.y = 10; + + part.thumbContainer = sprite; + + setTimeout(() => avatarImage.dispose(), 0); + } + } + + resetFigure(null); + } + } + + public get canSetGender(): boolean + { + return true; + } + + public get name(): string + { + return AvatarEditorFigureCategory.GENERIC; + } +} diff --git a/src/api/avatar/CategoryBaseModel.ts b/src/api/avatar/CategoryBaseModel.ts new file mode 100644 index 0000000..34dd933 --- /dev/null +++ b/src/api/avatar/CategoryBaseModel.ts @@ -0,0 +1,246 @@ +import { AvatarEditorUtilities } from './AvatarEditorUtilities'; +import { CategoryData } from './CategoryData'; +import { IAvatarEditorCategoryModel } from './IAvatarEditorCategoryModel'; + +export class CategoryBaseModel implements IAvatarEditorCategoryModel +{ + protected _categories: Map; + protected _isInitalized: boolean; + protected _maxPaletteCount: number; + private _disposed: boolean; + + constructor() + { + this._isInitalized = false; + this._maxPaletteCount = 0; + } + + public dispose(): void + { + this._categories = null; + this._disposed = true; + } + + public get disposed(): boolean + { + return this._disposed; + } + + public init(): void + { + if(!this._categories) this._categories = new Map(); + } + + public reset(): void + { + this._isInitalized = false; + + if(this._categories) + { + for(const category of this._categories.values()) (category && category.dispose()); + } + + this._categories = new Map(); + } + + protected addCategory(name: string): void + { + let existing = this._categories.get(name); + + if(existing) return; + + existing = AvatarEditorUtilities.createCategory(this, name); + + if(!existing) return; + + this._categories.set(name, existing); + + this.updateSelectionsFromFigure(name); + } + + protected updateSelectionsFromFigure(figure: string): void + { + const category = this._categories.get(figure); + + if(!category) return; + + const setId = AvatarEditorUtilities.CURRENT_FIGURE.getPartSetId(figure); + + let colorIds = AvatarEditorUtilities.CURRENT_FIGURE.getColorIds(figure); + + if(!colorIds) colorIds = []; + + category.selectPartId(setId); + category.selectColorIds(colorIds); + } + + public hasClubSelectionsOverLevel(level: number): boolean + { + if(!this._categories) return false; + + for(const category of this._categories.values()) + { + if(!category) continue; + + if(category.hasClubSelectionsOverLevel(level)) return true; + } + + return false; + } + + public hasInvalidSelectedItems(ownedItems: number[]): boolean + { + if(!this._categories) return false; + + for(const category of this._categories.values()) + { + if(category.hasInvalidSelectedItems(ownedItems)) return true; + } + + return false; + } + + public stripClubItemsOverLevel(level: number): boolean + { + if(!this._categories) return false; + + let didStrip = false; + + for(const [ name, category ] of this._categories.entries()) + { + let isValid = false; + + if(category.stripClubItemsOverLevel(level)) isValid = true; + + if(category.stripClubColorsOverLevel(level)) isValid = true; + + if(isValid) + { + const partItem = category.getCurrentPart(); + + if(partItem && AvatarEditorUtilities.CURRENT_FIGURE) + { + AvatarEditorUtilities.CURRENT_FIGURE.savePartData(name, partItem.id, category.getSelectedColorIds(), true); + } + + didStrip = true; + } + } + + return didStrip; + } + + public stripInvalidSellableItems(): boolean + { + if(!this._categories) return false; + + let didStrip = false; + + for(const [ name, category ] of this._categories.entries()) + { + const isValid = false; + + // if(category._Str_8360(this._Str_2278.manager.inventory)) _local_6 = true; + + if(isValid) + { + const partItem = category.getCurrentPart(); + + if(partItem && AvatarEditorUtilities.CURRENT_FIGURE) + { + AvatarEditorUtilities.CURRENT_FIGURE.savePartData(name, partItem.id, category.getSelectedColorIds(), true); + } + + didStrip = true; + } + } + + return didStrip; + } + + public selectPart(category: string, partIndex: number): void + { + const categoryData = this._categories.get(category); + + if(!categoryData) return; + + const selectedPartIndex = categoryData.selectedPartIndex; + + categoryData.selectPartIndex(partIndex); + + const partItem = categoryData.getCurrentPart(); + + if(!partItem) return; + + if(partItem.isDisabled) + { + categoryData.selectPartIndex(selectedPartIndex); + + // open hc window + + return; + } + + this._maxPaletteCount = partItem.maxColorIndex; + + AvatarEditorUtilities.CURRENT_FIGURE.savePartData(category, partItem.id, categoryData.getSelectedColorIds(), true); + } + + public selectColor(category: string, colorIndex: number, paletteId: number): void + { + const categoryData = this._categories.get(category); + + if(!categoryData) return; + + const paletteIndex = categoryData.getCurrentColorIndex(paletteId); + + categoryData.selectColorIndex(colorIndex, paletteId); + + const colorItem = categoryData.getSelectedColor(paletteId); + + if(colorItem.isDisabled) + { + categoryData.selectColorIndex(paletteIndex, paletteId); + + // open hc window + + return; + } + + AvatarEditorUtilities.CURRENT_FIGURE.savePartSetColourId(category, categoryData.getSelectedColorIds(), true); + } + + public getCategoryData(category: string): CategoryData + { + if(!this._isInitalized) this.init(); + + if(!this._categories) return null; + + return this._categories.get(category); + } + + public get categories(): Map + { + return this._categories; + } + + public get canSetGender(): boolean + { + return false; + } + + public get maxPaletteCount(): number + { + return (this._maxPaletteCount || 1); + } + + public set maxPaletteCount(count: number) + { + this._maxPaletteCount = count; + } + + public get name(): string + { + return null; + } +} diff --git a/src/api/avatar/CategoryData.ts b/src/api/avatar/CategoryData.ts new file mode 100644 index 0000000..db82f01 --- /dev/null +++ b/src/api/avatar/CategoryData.ts @@ -0,0 +1,487 @@ +import { IPartColor } from '@nitrots/nitro-renderer'; +import { AvatarEditorGridColorItem } from './AvatarEditorGridColorItem'; +import { AvatarEditorGridPartItem } from './AvatarEditorGridPartItem'; + +export class CategoryData +{ + private _name: string; + private _parts: AvatarEditorGridPartItem[]; + private _palettes: AvatarEditorGridColorItem[][]; + private _selectedPartIndex: number = -1; + private _paletteIndexes: number[]; + + constructor(name: string, partItems: AvatarEditorGridPartItem[], colorItems: AvatarEditorGridColorItem[][]) + { + this._name = name; + this._parts = partItems; + this._palettes = colorItems; + this._selectedPartIndex = -1; + } + + private static defaultColorId(palettes: AvatarEditorGridColorItem[], clubLevel: number): number + { + if(!palettes || !palettes.length) return -1; + + let i = 0; + + while(i < palettes.length) + { + const colorItem = palettes[i]; + + if(colorItem.partColor && (colorItem.partColor.clubLevel <= clubLevel)) + { + return colorItem.partColor.id; + } + + i++; + } + + return -1; + } + + public init(): void + { + for(const part of this._parts) + { + if(!part) continue; + + part.init(); + } + } + + public dispose(): void + { + if(this._parts) + { + for(const part of this._parts) part.dispose(); + + this._parts = null; + } + + if(this._palettes) + { + for(const palette of this._palettes) for(const colorItem of palette) colorItem.dispose(); + + this._palettes = null; + } + + this._selectedPartIndex = -1; + this._paletteIndexes = null; + } + + public selectPartId(partId: number): void + { + if(!this._parts) return; + + let i = 0; + + while(i < this._parts.length) + { + const partItem = this._parts[i]; + + if(partItem.id === partId) + { + this.selectPartIndex(i); + + return; + } + + i++; + } + } + + public selectColorIds(colorIds: number[]): void + { + if(!colorIds || !this._palettes) return; + + this._paletteIndexes = new Array(colorIds.length); + + let i = 0; + + while(i < this._palettes.length) + { + const palette = this.getPalette(i); + + if(palette) + { + let colorId = 0; + + if(colorIds.length > i) + { + colorId = colorIds[i]; + } + else + { + const colorItem = palette[0]; + + if(colorItem && colorItem.partColor) colorId = colorItem.partColor.id; + } + + let j = 0; + + while(j < palette.length) + { + const colorItem = palette[j]; + + if(colorItem.partColor.id === colorId) + { + this._paletteIndexes[i] = j; + + colorItem.isSelected = true; + } + else + { + colorItem.isSelected = false; + } + + j++; + } + } + + i++; + } + + this.updatePartColors(); + } + + public selectPartIndex(partIndex: number): AvatarEditorGridPartItem + { + if(!this._parts) return null; + + if((this._selectedPartIndex >= 0) && (this._parts.length > this._selectedPartIndex)) + { + const partItem = this._parts[this._selectedPartIndex]; + + if(partItem) partItem.isSelected = false; + } + + if(this._parts.length > partIndex) + { + const partItem = this._parts[partIndex]; + + if(partItem) + { + partItem.isSelected = true; + + this._selectedPartIndex = partIndex; + + return partItem; + } + } + + return null; + } + + public selectColorIndex(colorIndex: number, paletteId: number): AvatarEditorGridColorItem + { + const palette = this.getPalette(paletteId); + + if(!palette) return null; + + if(palette.length <= colorIndex) return null; + + this.deselectColorIndex(this._paletteIndexes[paletteId], paletteId); + + this._paletteIndexes[paletteId] = colorIndex; + + const colorItem = palette[colorIndex]; + + if(!colorItem) return null; + + colorItem.isSelected = true; + + this.updatePartColors(); + + return colorItem; + } + + public getCurrentColorIndex(k: number): number + { + return this._paletteIndexes[k]; + } + + private deselectColorIndex(colorIndex: number, paletteIndex: number): void + { + const palette = this.getPalette(paletteIndex); + + if(!palette) return; + + if(palette.length <= colorIndex) return; + + const colorItem = palette[colorIndex]; + + if(!colorItem) return; + + colorItem.isSelected = false; + } + + public getSelectedColorIds(): number[] + { + if(!this._paletteIndexes || !this._paletteIndexes.length) return null; + + if(!this._palettes || !this._palettes.length) return null; + + const palette = this._palettes[0]; + + if(!palette || (!palette.length)) return null; + + const colorItem = palette[0]; + + if(!colorItem || !colorItem.partColor) return null; + + const colorId = colorItem.partColor.id; + const colorIds: number[] = []; + + let i = 0; + + while(i < this._paletteIndexes.length) + { + const paletteSet = this._palettes[i]; + + if(!((!(paletteSet)) || (paletteSet.length <= i))) + { + if(paletteSet.length > this._paletteIndexes[i]) + { + const color = paletteSet[this._paletteIndexes[i]]; + + if(color && color.partColor) + { + colorIds.push(color.partColor.id); + } + else + { + colorIds.push(colorId); + } + } + else + { + colorIds.push(colorId); + } + } + + i++; + } + + const partItem = this.getCurrentPart(); + + if(!partItem) return null; + + return colorIds.slice(0, Math.max(partItem.maxColorIndex, 1)); + } + + private getSelectedColors(): IPartColor[] + { + const partColors: IPartColor[] = []; + + let i = 0; + + while(i < this._paletteIndexes.length) + { + const colorItem = this.getSelectedColor(i); + + if(colorItem) + { + partColors.push(colorItem.partColor); + } + else + { + partColors.push(null); + } + + i++; + } + + return partColors; + } + + public getSelectedColor(paletteId: number): AvatarEditorGridColorItem + { + const palette = this.getPalette(paletteId); + + if(!palette || (palette.length <= this._paletteIndexes[paletteId])) return null; + + return palette[this._paletteIndexes[paletteId]]; + } + + public getSelectedColorId(paletteId: number): number + { + const colorItem = this.getSelectedColor(paletteId); + + if(colorItem && (colorItem.partColor)) return colorItem.partColor.id; + + return 0; + } + + public getPalette(paletteId: number): AvatarEditorGridColorItem[] + { + if(!this._paletteIndexes || !this._palettes || (this._palettes.length <= paletteId)) + { + return null; + } + + return this._palettes[paletteId]; + } + + public getCurrentPart(): AvatarEditorGridPartItem + { + return this._parts[this._selectedPartIndex] as AvatarEditorGridPartItem; + } + + private updatePartColors(): void + { + const partColors = this.getSelectedColors(); + + for(const partItem of this._parts) + { + if(partItem) partItem.partColors = partColors; + } + } + + public hasClubSelectionsOverLevel(level: number): boolean + { + let hasInvalidSelections = false; + + const partColors = this.getSelectedColors(); + + if(partColors) + { + let i = 0; + + while(i < partColors.length) + { + const partColor = partColors[i]; + + if(partColor && (partColor.clubLevel > level)) hasInvalidSelections = true; + + i++; + } + } + + const partItem = this.getCurrentPart(); + + if(partItem && partItem.partSet) + { + const partSet = partItem.partSet; + + if(partSet && (partSet.clubLevel > level)) hasInvalidSelections = true; + } + + return hasInvalidSelections; + } + + public hasInvalidSelectedItems(ownedItems: number[]): boolean + { + const part = this.getCurrentPart(); + + if(!part) return false; + + const partSet = part.partSet; + + if(!partSet || !partSet.isSellable) return; + + return (ownedItems.indexOf(partSet.id) > -1); + } + + public stripClubItemsOverLevel(level: number): boolean + { + const partItem = this.getCurrentPart(); + + if(partItem && partItem.partSet) + { + const partSet = partItem.partSet; + + if(partSet.clubLevel > level) + { + const newPartItem = this.selectPartIndex(0); + + if(newPartItem && !newPartItem.partSet) this.selectPartIndex(1); + + return true; + } + } + + return false; + } + + public stripClubColorsOverLevel(level: number): boolean + { + const colorIds: number[] = []; + const partColors = this.getSelectedColors(); + const colorItems = this.getPalette(0); + + let didStrip = false; + + const colorId = CategoryData.defaultColorId(colorItems, level); + + if(colorId === -1) return false; + + let i = 0; + + while(i < partColors.length) + { + const partColor = partColors[i]; + + if(!partColor) + { + colorIds.push(colorId); + + didStrip = true; + } + else + { + if(partColor.clubLevel > level) + { + colorIds.push(colorId); + + didStrip = true; + } + else + { + colorIds.push(partColor.id); + } + } + + i++; + } + + if(didStrip) this.selectColorIds(colorIds); + + return didStrip; + } + + // public stripInvalidSellableItems(k:IHabboInventory): boolean + // { + // var _local_3:IFigurePartSet; + // var _local_4:AvatarEditorGridPartItem; + // var _local_2:AvatarEditorGridPartItem = this._Str_6315(); + // if (((_local_2) && (_local_2.partSet))) + // { + // _local_3 = _local_2.partSet; + // if (((_local_3.isSellable) && (!(k._Str_14439(_local_3.id))))) + // { + // _local_4 = this._Str_8066(0); + // if (((!(_local_4 == null)) && (_local_4.partSet == null))) + // { + // this._Str_8066(1); + // } + // return true; + // } + // } + // return false; + // } + + public get name(): string + { + return this._name; + } + + public get parts(): AvatarEditorGridPartItem[] + { + return this._parts; + } + + public get selectedPartIndex(): number + { + return this._selectedPartIndex; + } +} diff --git a/src/api/avatar/FigureData.ts b/src/api/avatar/FigureData.ts new file mode 100644 index 0000000..78014d1 --- /dev/null +++ b/src/api/avatar/FigureData.ts @@ -0,0 +1,287 @@ +import { AvatarEditorUtilities } from './AvatarEditorUtilities'; + +export class FigureData +{ + private static DEFAULT_DIRECTION: number = 4; + + public static MALE: string = 'M'; + public static FEMALE: string = 'F'; + public static UNISEX: string = 'U'; + public static SCALE: string = 'h'; + public static STD: string = 'std'; + public static DEFAULT_FRAME: string = '0'; + public static FACE: string = 'hd'; + public static HAIR: string = 'hr'; + public static HAT: string = 'ha'; + public static HEAD_ACCESSORIES: string = 'he'; + public static EYE_ACCESSORIES: string = 'ea'; + public static FACE_ACCESSORIES: string = 'fa'; + public static JACKET: string = 'cc'; + public static SHIRT: string = 'ch'; + public static CHEST_ACCESSORIES: string = 'ca'; + public static CHEST_PRINTS: string = 'cp'; + public static TROUSERS: string = 'lg'; + public static SHOES: string = 'sh'; + public static TROUSER_ACCESSORIES: string = 'wa'; + public static SET_TYPES = [ FigureData.FACE, FigureData.HAIR, FigureData.HAT, FigureData.HEAD_ACCESSORIES, FigureData.EYE_ACCESSORIES, FigureData.FACE_ACCESSORIES, FigureData.JACKET, FigureData.SHIRT, FigureData.CHEST_ACCESSORIES, FigureData.CHEST_PRINTS, FigureData.TROUSERS, FigureData.SHOES, FigureData.TROUSERS ]; + + private _data: Map; + private _colors: Map; + private _gender: string = 'M'; + private _direction: number = FigureData.DEFAULT_DIRECTION; + private _avatarEffectType: number = -1; + private _notifier: () => void = null; + + public loadAvatarData(figureString: string, gender: string): void + { + this._data = new Map(); + this._colors = new Map(); + this._gender = gender; + + this.parseFigureString(figureString); + this.updateView(); + } + + private parseFigureString(figure: string): void + { + if(!figure) return; + + const sets = figure.split('.'); + + if(!sets || !sets.length) return; + + for(const set of sets) + { + const parts = set.split('-'); + + if(!parts.length) continue; + + const setType = parts[0]; + const setId = parseInt(parts[1]); + const colorIds: number[] = []; + + let offset = 2; + + while(offset < parts.length) + { + colorIds.push(parseInt(parts[offset])); + + offset++; + } + + if(!colorIds.length) colorIds.push(0); + + this.savePartSetId(setType, setId, false); + this.savePartSetColourId(setType, colorIds, false); + } + } + + public getPartSetId(setType: string): number + { + const existing = this._data.get(setType); + + if(existing !== undefined) return existing; + + return -1; + } + + public getColorIds(setType: string): number[] + { + const existing = this._colors.get(setType); + + if(existing !== undefined) return existing; + + return [ AvatarEditorUtilities.avatarSetFirstSelectableColor(setType) ]; + } + + public getFigureString(): string + { + let figureString = ''; + const setParts: string[] = []; + + for(const [ setType, setId ] of this._data.entries()) + { + const colorIds = this._colors.get(setType); + + let setPart = ((setType + '-') + setId); + + if(colorIds && colorIds.length) + { + let i = 0; + + while(i < colorIds.length) + { + setPart = (setPart + ('-' + colorIds[i])); + + i++; + } + } + + setParts.push(setPart); + } + + let i = 0; + + while(i < setParts.length) + { + figureString = (figureString + setParts[i]); + + if(i < (setParts.length - 1)) figureString = (figureString + '.'); + + i++; + } + + return figureString; + } + + public savePartData(setType: string, partId: number, colorIds: number[], update: boolean = false): void + { + this.savePartSetId(setType, partId, update); + this.savePartSetColourId(setType, colorIds, update); + } + + private savePartSetId(setType: string, partId: number, update: boolean = true): void + { + switch(setType) + { + case FigureData.FACE: + case FigureData.HAIR: + case FigureData.HAT: + case FigureData.HEAD_ACCESSORIES: + case FigureData.EYE_ACCESSORIES: + case FigureData.FACE_ACCESSORIES: + case FigureData.SHIRT: + case FigureData.JACKET: + case FigureData.CHEST_ACCESSORIES: + case FigureData.CHEST_PRINTS: + case FigureData.TROUSERS: + case FigureData.SHOES: + case FigureData.TROUSER_ACCESSORIES: + if(partId >= 0) + { + this._data.set(setType, partId); + } + else + { + this._data.delete(setType); + } + break; + } + + if(update) this.updateView(); + } + + public savePartSetColourId(setType: string, colorIds: number[], update: boolean = true): void + { + switch(setType) + { + case FigureData.FACE: + case FigureData.HAIR: + case FigureData.HAT: + case FigureData.HEAD_ACCESSORIES: + case FigureData.EYE_ACCESSORIES: + case FigureData.FACE_ACCESSORIES: + case FigureData.SHIRT: + case FigureData.JACKET: + case FigureData.CHEST_ACCESSORIES: + case FigureData.CHEST_PRINTS: + case FigureData.TROUSERS: + case FigureData.SHOES: + case FigureData.TROUSER_ACCESSORIES: + this._colors.set(setType, colorIds); + break; + } + + if(update) this.updateView(); + } + + public getFigureStringWithFace(k: number, override = true): string + { + let figureString = ''; + + const setTypes: string[] = [ FigureData.FACE ]; + const figureSets: string[] = []; + + for(const setType of setTypes) + { + const colors = this._colors.get(setType); + + if(!colors) continue; + + let setId = this._data.get(setType); + + if((setType === FigureData.FACE) && override) setId = k; + + let figureSet = ((setType + '-') + setId); + + if(setId >= 0) + { + let i = 0; + + while(i < colors.length) + { + figureSet = (figureSet + ('-' + colors[i])); + + i++; + } + } + + figureSets.push(figureSet); + } + + let i = 0; + + while(i < figureSets.length) + { + figureString = (figureString + figureSets[i]); + + if(i < (figureSets.length - 1)) figureString = (figureString + '.'); + + i++; + } + + return figureString; + } + + public updateView(): void + { + if(this.notify) this.notify(); + } + + public get gender(): string + { + return this._gender; + } + + public get direction(): number + { + return this._direction; + } + + public set direction(direction: number) + { + this._direction = direction; + + this.updateView(); + } + + public set avatarEffectType(k: number) + { + this._avatarEffectType = k; + } + + public get avatarEffectType(): number + { + return this._avatarEffectType; + } + + public get notify(): () => void + { + return this._notifier; + } + + public set notify(notifier: () => void) + { + this._notifier = notifier; + } +} diff --git a/src/api/avatar/FigureGenerator.ts b/src/api/avatar/FigureGenerator.ts new file mode 100644 index 0000000..b83a661 --- /dev/null +++ b/src/api/avatar/FigureGenerator.ts @@ -0,0 +1,90 @@ +import { AvatarFigureContainer, IFigurePartSet, IPalette, IPartColor, SetType } from '@nitrots/nitro-renderer'; +import { GetAvatarRenderManager } from '../nitro'; +import { Randomizer } from '../utils'; +import { FigureData } from './FigureData'; + +function getTotalColors(partSet: IFigurePartSet): number +{ + const parts = partSet.parts; + + let totalColors = 0; + + for(const part of parts) totalColors = Math.max(totalColors, part.colorLayerIndex); + + return totalColors; +} + +function getRandomSetTypes(requiredSets: string[], options: string[]): string[] +{ + options = options.filter(option => (requiredSets.indexOf(option) === -1)); + + return [ ...requiredSets, ...Randomizer.getRandomElements(options, (Randomizer.getRandomNumber(options.length) + 1)) ]; +} + +function getRandomPartSet(setType: SetType, gender: string, clubLevel: number = 0, figureSetIds: number[] = []): IFigurePartSet +{ + if(!setType) return null; + + const options = setType.partSets.getValues().filter(option => + { + if(!option.isSelectable || ((option.gender !== 'U') && (option.gender !== gender)) || (option.clubLevel > clubLevel) || (option.isSellable && (figureSetIds.indexOf(option.id) === -1))) return null; + + return option; + }); + + if(!options || !options.length) return null; + + return Randomizer.getRandomElement(options); +} + +function getRandomColors(palette: IPalette, partSet: IFigurePartSet, clubLevel: number = 0): IPartColor[] +{ + if(!palette) return []; + + const options = palette.colors.getValues().filter(option => + { + if(!option.isSelectable || (option.clubLevel > clubLevel)) return null; + + return option; + }); + + if(!options || !options.length) return null; + + return Randomizer.getRandomElements(options, getTotalColors(partSet)); +} + +export function generateRandomFigure(figureData: FigureData, gender: string, clubLevel: number = 0, figureSetIds: number[] = [], ignoredSets: string[] = []): string +{ + const structure = GetAvatarRenderManager().structure; + const figureContainer = new AvatarFigureContainer(''); + const requiredSets = getRandomSetTypes(structure.getMandatorySetTypeIds(gender, clubLevel), FigureData.SET_TYPES); + + for(const setType of ignoredSets) + { + const partSetId = figureData.getPartSetId(setType); + const colors = figureData.getColorIds(setType); + + figureContainer.updatePart(setType, partSetId, colors); + } + + for(const type of requiredSets) + { + if(figureContainer.hasPartType(type)) continue; + + const setType = (structure.figureData.getSetType(type) as SetType); + const selectedSet = getRandomPartSet(setType, gender, clubLevel, figureSetIds); + + if(!selectedSet) continue; + + let selectedColors: number[] = []; + + if(selectedSet.isColorable) + { + selectedColors = getRandomColors(structure.figureData.getPalette(setType.paletteID), selectedSet, clubLevel).map(color => color.id); + } + + figureContainer.updatePart(setType.type, selectedSet.id, selectedColors); + } + + return figureContainer.getFigureString(); +} diff --git a/src/api/avatar/HeadModel.ts b/src/api/avatar/HeadModel.ts new file mode 100644 index 0000000..5dc30cd --- /dev/null +++ b/src/api/avatar/HeadModel.ts @@ -0,0 +1,24 @@ +import { AvatarEditorFigureCategory } from '@nitrots/nitro-renderer'; +import { CategoryBaseModel } from './CategoryBaseModel'; +import { FigureData } from './FigureData'; + +export class HeadModel extends CategoryBaseModel +{ + public init(): void + { + super.init(); + + this.addCategory(FigureData.HAIR); + this.addCategory(FigureData.HAT); + this.addCategory(FigureData.HEAD_ACCESSORIES); + this.addCategory(FigureData.EYE_ACCESSORIES); + this.addCategory(FigureData.FACE_ACCESSORIES); + + this._isInitalized = true; + } + + public get name(): string + { + return AvatarEditorFigureCategory.HEAD; + } +} diff --git a/src/api/avatar/IAvatarEditorCategoryModel.ts b/src/api/avatar/IAvatarEditorCategoryModel.ts new file mode 100644 index 0000000..dc9affa --- /dev/null +++ b/src/api/avatar/IAvatarEditorCategoryModel.ts @@ -0,0 +1,19 @@ +import { CategoryData } from './CategoryData'; + +export interface IAvatarEditorCategoryModel +{ + init(): void; + dispose(): void; + reset(): void; + getCategoryData(category: string): CategoryData; + selectPart(category: string, partIndex: number): void; + selectColor(category: string, colorIndex: number, paletteId: number): void; + hasClubSelectionsOverLevel(level: number): boolean; + hasInvalidSelectedItems(ownedItems: number[]): boolean; + stripClubItemsOverLevel(level: number): boolean; + stripInvalidSellableItems(): boolean; + categories: Map; + canSetGender: boolean; + maxPaletteCount: number; + name: string; +} diff --git a/src/api/avatar/LegModel.ts b/src/api/avatar/LegModel.ts new file mode 100644 index 0000000..5633930 --- /dev/null +++ b/src/api/avatar/LegModel.ts @@ -0,0 +1,22 @@ +import { AvatarEditorFigureCategory } from '@nitrots/nitro-renderer'; +import { CategoryBaseModel } from './CategoryBaseModel'; +import { FigureData } from './FigureData'; + +export class LegModel extends CategoryBaseModel +{ + public init(): void + { + super.init(); + + this.addCategory(FigureData.TROUSERS); + this.addCategory(FigureData.SHOES); + this.addCategory(FigureData.TROUSER_ACCESSORIES); + + this._isInitalized = true; + } + + public get name(): string + { + return AvatarEditorFigureCategory.LEGS; + } +} diff --git a/src/api/avatar/TorsoModel.ts b/src/api/avatar/TorsoModel.ts new file mode 100644 index 0000000..43e48cf --- /dev/null +++ b/src/api/avatar/TorsoModel.ts @@ -0,0 +1,23 @@ +import { AvatarEditorFigureCategory } from '@nitrots/nitro-renderer'; +import { CategoryBaseModel } from './CategoryBaseModel'; +import { FigureData } from './FigureData'; + +export class TorsoModel extends CategoryBaseModel +{ + public init(): void + { + super.init(); + + this.addCategory(FigureData.SHIRT); + this.addCategory(FigureData.CHEST_PRINTS); + this.addCategory(FigureData.JACKET); + this.addCategory(FigureData.CHEST_ACCESSORIES); + + this._isInitalized = true; + } + + public get name(): string + { + return AvatarEditorFigureCategory.TORSO; + } +} diff --git a/src/api/avatar/index.ts b/src/api/avatar/index.ts new file mode 100644 index 0000000..37b3072 --- /dev/null +++ b/src/api/avatar/index.ts @@ -0,0 +1,13 @@ +export * from './AvatarEditorAction'; +export * from './AvatarEditorGridColorItem'; +export * from './AvatarEditorGridPartItem'; +export * from './AvatarEditorUtilities'; +export * from './BodyModel'; +export * from './CategoryBaseModel'; +export * from './CategoryData'; +export * from './FigureData'; +export * from './FigureGenerator'; +export * from './HeadModel'; +export * from './IAvatarEditorCategoryModel'; +export * from './LegModel'; +export * from './TorsoModel'; diff --git a/src/api/camera/CameraEditorTabs.ts b/src/api/camera/CameraEditorTabs.ts new file mode 100644 index 0000000..6e894e7 --- /dev/null +++ b/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/src/api/camera/CameraPicture.ts b/src/api/camera/CameraPicture.ts new file mode 100644 index 0000000..fe8c221 --- /dev/null +++ b/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/src/api/camera/CameraPictureThumbnail.ts b/src/api/camera/CameraPictureThumbnail.ts new file mode 100644 index 0000000..cd12660 --- /dev/null +++ b/src/api/camera/CameraPictureThumbnail.ts @@ -0,0 +1,7 @@ +export class CameraPictureThumbnail +{ + constructor( + public effectName: string, + public thumbnailUrl: string) + {} +} diff --git a/src/api/camera/index.ts b/src/api/camera/index.ts new file mode 100644 index 0000000..93c6ccb --- /dev/null +++ b/src/api/camera/index.ts @@ -0,0 +1,3 @@ +export * from './CameraEditorTabs'; +export * from './CameraPicture'; +export * from './CameraPictureThumbnail'; diff --git a/src/api/campaign/CalendarItem.ts b/src/api/campaign/CalendarItem.ts new file mode 100644 index 0000000..d3634b3 --- /dev/null +++ b/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/src/api/campaign/CalendarItemState.ts b/src/api/campaign/CalendarItemState.ts new file mode 100644 index 0000000..1b91ca3 --- /dev/null +++ b/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/src/api/campaign/ICalendarItem.ts b/src/api/campaign/ICalendarItem.ts new file mode 100644 index 0000000..87dfbd6 --- /dev/null +++ b/src/api/campaign/ICalendarItem.ts @@ -0,0 +1,6 @@ +export interface ICalendarItem +{ + readonly productName: string; + readonly customImage: string; + readonly furnitureClassName: string; +} diff --git a/src/api/campaign/index.ts b/src/api/campaign/index.ts new file mode 100644 index 0000000..a86e40c --- /dev/null +++ b/src/api/campaign/index.ts @@ -0,0 +1,3 @@ +export * from './CalendarItem'; +export * from './CalendarItemState'; +export * from './ICalendarItem'; diff --git a/src/api/catalog/BuilderFurniPlaceableStatus.ts b/src/api/catalog/BuilderFurniPlaceableStatus.ts new file mode 100644 index 0000000..40eb6f6 --- /dev/null +++ b/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/src/api/catalog/CatalogNode.ts b/src/api/catalog/CatalogNode.ts new file mode 100644 index 0000000..893aa32 --- /dev/null +++ b/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/src/api/catalog/CatalogPage.ts b/src/api/catalog/CatalogPage.ts new file mode 100644 index 0000000..1e80609 --- /dev/null +++ b/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/src/api/catalog/CatalogPageName.ts b/src/api/catalog/CatalogPageName.ts new file mode 100644 index 0000000..8e4c7b6 --- /dev/null +++ b/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/src/api/catalog/CatalogPetPalette.ts b/src/api/catalog/CatalogPetPalette.ts new file mode 100644 index 0000000..d92c40d --- /dev/null +++ b/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/src/api/catalog/CatalogPurchaseState.ts b/src/api/catalog/CatalogPurchaseState.ts new file mode 100644 index 0000000..b442f62 --- /dev/null +++ b/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/src/api/catalog/CatalogType.ts b/src/api/catalog/CatalogType.ts new file mode 100644 index 0000000..670ad6f --- /dev/null +++ b/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/src/api/catalog/CatalogUtilities.ts b/src/api/catalog/CatalogUtilities.ts new file mode 100644 index 0000000..5ca8fed --- /dev/null +++ b/src/api/catalog/CatalogUtilities.ts @@ -0,0 +1,125 @@ +import { SellablePetPaletteData } from '@nitrots/nitro-renderer'; +import { GetRoomEngine } from '../nitro'; +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/src/api/catalog/FurnitureOffer.ts b/src/api/catalog/FurnitureOffer.ts new file mode 100644 index 0000000..4c9c9f9 --- /dev/null +++ b/src/api/catalog/FurnitureOffer.ts @@ -0,0 +1,120 @@ +import { GetProductOfferComposer, IFurnitureData } from '@nitrots/nitro-renderer'; +import { GetProductDataForLocalization, SendMessageComposer } from '..'; +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/src/api/catalog/GetImageIconUrlForProduct.ts b/src/api/catalog/GetImageIconUrlForProduct.ts new file mode 100644 index 0000000..1e8d8c0 --- /dev/null +++ b/src/api/catalog/GetImageIconUrlForProduct.ts @@ -0,0 +1,19 @@ +import { GetRoomEngine } from '../nitro'; +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/src/api/catalog/GiftWrappingConfiguration.ts b/src/api/catalog/GiftWrappingConfiguration.ts new file mode 100644 index 0000000..9d29b8c --- /dev/null +++ b/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/src/api/catalog/ICatalogNode.ts b/src/api/catalog/ICatalogNode.ts new file mode 100644 index 0000000..c69f5a6 --- /dev/null +++ b/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/src/api/catalog/ICatalogOptions.ts b/src/api/catalog/ICatalogOptions.ts new file mode 100644 index 0000000..2035694 --- /dev/null +++ b/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/src/api/catalog/ICatalogPage.ts b/src/api/catalog/ICatalogPage.ts new file mode 100644 index 0000000..ed11ba0 --- /dev/null +++ b/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/src/api/catalog/IMarketplaceSearchOptions.ts b/src/api/catalog/IMarketplaceSearchOptions.ts new file mode 100644 index 0000000..9489ef0 --- /dev/null +++ b/src/api/catalog/IMarketplaceSearchOptions.ts @@ -0,0 +1,7 @@ +export interface IMarketplaceSearchOptions +{ + query: string; + type: number; + minPrice: number; + maxPrice: number; +} diff --git a/src/api/catalog/IPageLocalization.ts b/src/api/catalog/IPageLocalization.ts new file mode 100644 index 0000000..ad652e1 --- /dev/null +++ b/src/api/catalog/IPageLocalization.ts @@ -0,0 +1,5 @@ +export interface IPageLocalization +{ + getText(index: number): string + getImage(index: number): string +} diff --git a/src/api/catalog/IProduct.ts b/src/api/catalog/IProduct.ts new file mode 100644 index 0000000..4a1a392 --- /dev/null +++ b/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/src/api/catalog/IPurchasableOffer.ts b/src/api/catalog/IPurchasableOffer.ts new file mode 100644 index 0000000..b182865 --- /dev/null +++ b/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/src/api/catalog/IPurchaseOptions.ts b/src/api/catalog/IPurchaseOptions.ts new file mode 100644 index 0000000..c9fab89 --- /dev/null +++ b/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/src/api/catalog/MarketplaceOfferData.ts b/src/api/catalog/MarketplaceOfferData.ts new file mode 100644 index 0000000..ba1fa88 --- /dev/null +++ b/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/src/api/catalog/MarketplaceOfferState.ts b/src/api/catalog/MarketplaceOfferState.ts new file mode 100644 index 0000000..20c0e45 --- /dev/null +++ b/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/src/api/catalog/MarketplaceSearchType.ts b/src/api/catalog/MarketplaceSearchType.ts new file mode 100644 index 0000000..ac7a701 --- /dev/null +++ b/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/src/api/catalog/Offer.ts b/src/api/catalog/Offer.ts new file mode 100644 index 0000000..c14d6ac --- /dev/null +++ b/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/src/api/catalog/PageLocalization.ts b/src/api/catalog/PageLocalization.ts new file mode 100644 index 0000000..91e3ce6 --- /dev/null +++ b/src/api/catalog/PageLocalization.ts @@ -0,0 +1,36 @@ +import { GetConfiguration } 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 = GetConfiguration('catalog.asset.image.url'); + + assetUrl = assetUrl.replace('%name%', imageName); + + return assetUrl; + } +} diff --git a/src/api/catalog/PlacedObjectPurchaseData.ts b/src/api/catalog/PlacedObjectPurchaseData.ts new file mode 100644 index 0000000..84bad8c --- /dev/null +++ b/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/src/api/catalog/Product.ts b/src/api/catalog/Product.ts new file mode 100644 index 0000000..bfb760f --- /dev/null +++ b/src/api/catalog/Product.ts @@ -0,0 +1,143 @@ +import { IFurnitureData, IObjectData, IProductData } from '@nitrots/nitro-renderer'; +import { GetConfiguration, GetRoomEngine, GetSessionDataManager } 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 = GetConfiguration('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/src/api/catalog/ProductTypeEnum.ts b/src/api/catalog/ProductTypeEnum.ts new file mode 100644 index 0000000..f249081 --- /dev/null +++ b/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/src/api/catalog/RequestedPage.ts b/src/api/catalog/RequestedPage.ts new file mode 100644 index 0000000..8c22730 --- /dev/null +++ b/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/src/api/catalog/SearchResult.ts b/src/api/catalog/SearchResult.ts new file mode 100644 index 0000000..419f3cf --- /dev/null +++ b/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/src/api/catalog/index.ts b/src/api/catalog/index.ts new file mode 100644 index 0000000..6c5b9e2 --- /dev/null +++ b/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/src/api/chat-history/ChatEntryType.ts b/src/api/chat-history/ChatEntryType.ts new file mode 100644 index 0000000..045f00c --- /dev/null +++ b/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/src/api/chat-history/ChatHistoryCurrentDate.ts b/src/api/chat-history/ChatHistoryCurrentDate.ts new file mode 100644 index 0000000..6947bca --- /dev/null +++ b/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/src/api/chat-history/IChatEntry.ts b/src/api/chat-history/IChatEntry.ts new file mode 100644 index 0000000..1bf7a52 --- /dev/null +++ b/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/src/api/chat-history/IRoomHistoryEntry.ts b/src/api/chat-history/IRoomHistoryEntry.ts new file mode 100644 index 0000000..4986154 --- /dev/null +++ b/src/api/chat-history/IRoomHistoryEntry.ts @@ -0,0 +1,5 @@ +export interface IRoomHistoryEntry +{ + id: number; + name: string; +} diff --git a/src/api/chat-history/MessengerHistoryCurrentDate.ts b/src/api/chat-history/MessengerHistoryCurrentDate.ts new file mode 100644 index 0000000..3aeebc5 --- /dev/null +++ b/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/src/api/chat-history/index.ts b/src/api/chat-history/index.ts new file mode 100644 index 0000000..a989374 --- /dev/null +++ b/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/src/api/events/DispatchEvent.ts b/src/api/events/DispatchEvent.ts new file mode 100644 index 0000000..79e2f5c --- /dev/null +++ b/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/src/api/events/DispatchMainEvent.ts b/src/api/events/DispatchMainEvent.ts new file mode 100644 index 0000000..385a888 --- /dev/null +++ b/src/api/events/DispatchMainEvent.ts @@ -0,0 +1,5 @@ +import { NitroEvent } from '@nitrots/nitro-renderer'; +import { GetNitroInstance } from '../nitro'; +import { DispatchEvent } from './DispatchEvent'; + +export const DispatchMainEvent = (event: NitroEvent) => DispatchEvent(GetNitroInstance().events, event); diff --git a/src/api/events/DispatchUiEvent.ts b/src/api/events/DispatchUiEvent.ts new file mode 100644 index 0000000..5200bb4 --- /dev/null +++ b/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/src/api/events/UI_EVENT_DISPATCHER.ts b/src/api/events/UI_EVENT_DISPATCHER.ts new file mode 100644 index 0000000..cb57311 --- /dev/null +++ b/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/src/api/events/index.ts b/src/api/events/index.ts new file mode 100644 index 0000000..b7c22ee --- /dev/null +++ b/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/src/api/friends/GetGroupChatData.ts b/src/api/friends/GetGroupChatData.ts new file mode 100644 index 0000000..75df962 --- /dev/null +++ b/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/src/api/friends/IGroupChatData.ts b/src/api/friends/IGroupChatData.ts new file mode 100644 index 0000000..24a3f9c --- /dev/null +++ b/src/api/friends/IGroupChatData.ts @@ -0,0 +1,6 @@ +export interface IGroupChatData +{ + username: string; + figure: string; + userId: number; +} diff --git a/src/api/friends/MessengerFriend.ts b/src/api/friends/MessengerFriend.ts new file mode 100644 index 0000000..b5cfc88 --- /dev/null +++ b/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/src/api/friends/MessengerGroupType.ts b/src/api/friends/MessengerGroupType.ts new file mode 100644 index 0000000..d46a1b6 --- /dev/null +++ b/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/src/api/friends/MessengerIconState.ts b/src/api/friends/MessengerIconState.ts new file mode 100644 index 0000000..63f8c13 --- /dev/null +++ b/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/src/api/friends/MessengerRequest.ts b/src/api/friends/MessengerRequest.ts new file mode 100644 index 0000000..89ceec5 --- /dev/null +++ b/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/src/api/friends/MessengerSettings.ts b/src/api/friends/MessengerSettings.ts new file mode 100644 index 0000000..e0fc8c2 --- /dev/null +++ b/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/src/api/friends/MessengerThread.ts b/src/api/friends/MessengerThread.ts new file mode 100644 index 0000000..405ea33 --- /dev/null +++ b/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/src/api/friends/MessengerThreadChat.ts b/src/api/friends/MessengerThreadChat.ts new file mode 100644 index 0000000..2927fec --- /dev/null +++ b/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/src/api/friends/MessengerThreadChatGroup.ts b/src/api/friends/MessengerThreadChatGroup.ts new file mode 100644 index 0000000..1668aed --- /dev/null +++ b/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/src/api/friends/OpenMessengerChat.ts b/src/api/friends/OpenMessengerChat.ts new file mode 100644 index 0000000..9270981 --- /dev/null +++ b/src/api/friends/OpenMessengerChat.ts @@ -0,0 +1,7 @@ +import { CreateLinkEvent } from '..'; + +export function OpenMessengerChat(friendId: number = 0): void +{ + if(friendId === 0) CreateLinkEvent('friends-messenger/toggle'); + else CreateLinkEvent(`friends-messenger/${ friendId }`); +} diff --git a/src/api/friends/index.ts b/src/api/friends/index.ts new file mode 100644 index 0000000..ce1ed60 --- /dev/null +++ b/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/src/api/groups/GetGroupInformation.ts b/src/api/groups/GetGroupInformation.ts new file mode 100644 index 0000000..6b4a48c --- /dev/null +++ b/src/api/groups/GetGroupInformation.ts @@ -0,0 +1,7 @@ +import { GroupInformationComposer } from '@nitrots/nitro-renderer'; +import { SendMessageComposer } from '..'; + +export function GetGroupInformation(groupId: number): void +{ + SendMessageComposer(new GroupInformationComposer(groupId, true)); +} diff --git a/src/api/groups/GetGroupManager.ts b/src/api/groups/GetGroupManager.ts new file mode 100644 index 0000000..d372ace --- /dev/null +++ b/src/api/groups/GetGroupManager.ts @@ -0,0 +1,6 @@ +import { CreateLinkEvent } from '..'; + +export function GetGroupManager(groupId: number): void +{ + CreateLinkEvent(`groups/manage/${ groupId }`); +} diff --git a/src/api/groups/GetGroupMembers.ts b/src/api/groups/GetGroupMembers.ts new file mode 100644 index 0000000..27fb6d5 --- /dev/null +++ b/src/api/groups/GetGroupMembers.ts @@ -0,0 +1,7 @@ +import { CreateLinkEvent } from '..'; + +export function GetGroupMembers(groupId: number, levelId?: number): void +{ + if(!levelId) CreateLinkEvent(`group-members/${ groupId }`); + else CreateLinkEvent(`group-members/${ groupId }/${ levelId }`); +} diff --git a/src/api/groups/GroupBadgePart.ts b/src/api/groups/GroupBadgePart.ts new file mode 100644 index 0000000..3b74875 --- /dev/null +++ b/src/api/groups/GroupBadgePart.ts @@ -0,0 +1,31 @@ + +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/src/api/groups/GroupMembershipType.ts b/src/api/groups/GroupMembershipType.ts new file mode 100644 index 0000000..532c836 --- /dev/null +++ b/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/src/api/groups/GroupType.ts b/src/api/groups/GroupType.ts new file mode 100644 index 0000000..58ae72c --- /dev/null +++ b/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/src/api/groups/IGroupCustomize.ts b/src/api/groups/IGroupCustomize.ts new file mode 100644 index 0000000..44fc4ff --- /dev/null +++ b/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/src/api/groups/IGroupData.ts b/src/api/groups/IGroupData.ts new file mode 100644 index 0000000..bb65b49 --- /dev/null +++ b/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/src/api/groups/ToggleFavoriteGroup.ts b/src/api/groups/ToggleFavoriteGroup.ts new file mode 100644 index 0000000..a4b929c --- /dev/null +++ b/src/api/groups/ToggleFavoriteGroup.ts @@ -0,0 +1,7 @@ +import { GroupFavoriteComposer, GroupUnfavoriteComposer, HabboGroupEntryData } from '@nitrots/nitro-renderer'; +import { SendMessageComposer } from '..'; + +export const ToggleFavoriteGroup = (group: HabboGroupEntryData) => +{ + SendMessageComposer(group.favourite ? new GroupUnfavoriteComposer(group.groupId) : new GroupFavoriteComposer(group.groupId)); +} diff --git a/src/api/groups/TryJoinGroup.ts b/src/api/groups/TryJoinGroup.ts new file mode 100644 index 0000000..4fbbcde --- /dev/null +++ b/src/api/groups/TryJoinGroup.ts @@ -0,0 +1,4 @@ +import { GroupJoinComposer } from '@nitrots/nitro-renderer'; +import { SendMessageComposer } from '..'; + +export const TryJoinGroup = (groupId: number) => SendMessageComposer(new GroupJoinComposer(groupId)); diff --git a/src/api/groups/index.ts b/src/api/groups/index.ts new file mode 100644 index 0000000..4842948 --- /dev/null +++ b/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/src/api/guide-tool/GuideSessionState.ts b/src/api/guide-tool/GuideSessionState.ts new file mode 100644 index 0000000..c5e24f3 --- /dev/null +++ b/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/src/api/guide-tool/GuideToolMessage.ts b/src/api/guide-tool/GuideToolMessage.ts new file mode 100644 index 0000000..3810726 --- /dev/null +++ b/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/src/api/guide-tool/GuideToolMessageGroup.ts b/src/api/guide-tool/GuideToolMessageGroup.ts new file mode 100644 index 0000000..bf03c9b --- /dev/null +++ b/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/src/api/guide-tool/index.ts b/src/api/guide-tool/index.ts new file mode 100644 index 0000000..1400adc --- /dev/null +++ b/src/api/guide-tool/index.ts @@ -0,0 +1,3 @@ +export * from './GuideSessionState'; +export * from './GuideToolMessage'; +export * from './GuideToolMessageGroup'; diff --git a/src/api/hc-center/ClubStatus.ts b/src/api/hc-center/ClubStatus.ts new file mode 100644 index 0000000..8200b14 --- /dev/null +++ b/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/src/api/hc-center/GetClubBadge.ts b/src/api/hc-center/GetClubBadge.ts new file mode 100644 index 0000000..6b779e0 --- /dev/null +++ b/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/src/api/hc-center/index.ts b/src/api/hc-center/index.ts new file mode 100644 index 0000000..cee8f69 --- /dev/null +++ b/src/api/hc-center/index.ts @@ -0,0 +1,2 @@ +export * from './ClubStatus'; +export * from './GetClubBadge'; diff --git a/src/api/help/CallForHelpResult.ts b/src/api/help/CallForHelpResult.ts new file mode 100644 index 0000000..37e7ea1 --- /dev/null +++ b/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/src/api/help/GetCloseReasonKey.ts b/src/api/help/GetCloseReasonKey.ts new file mode 100644 index 0000000..520d14f --- /dev/null +++ b/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/src/api/help/IHelpReport.ts b/src/api/help/IHelpReport.ts new file mode 100644 index 0000000..8611707 --- /dev/null +++ b/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/src/api/help/IReportedUser.ts b/src/api/help/IReportedUser.ts new file mode 100644 index 0000000..90a3887 --- /dev/null +++ b/src/api/help/IReportedUser.ts @@ -0,0 +1,5 @@ +export interface IReportedUser +{ + id: number; + username: string; +} diff --git a/src/api/help/ReportState.ts b/src/api/help/ReportState.ts new file mode 100644 index 0000000..ae3a3bd --- /dev/null +++ b/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/src/api/help/ReportType.ts b/src/api/help/ReportType.ts new file mode 100644 index 0000000..24eb7ae --- /dev/null +++ b/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/src/api/help/index.ts b/src/api/help/index.ts new file mode 100644 index 0000000..6fa2045 --- /dev/null +++ b/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/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..af96444 --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,29 @@ +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 './GetRendererVersion'; +export * from './GetUIVersion'; +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/avatar'; +export * from './nitro/camera'; +export * from './nitro/core'; +export * from './nitro/room'; +export * from './nitro/session'; +export * from './notification'; +export * from './purse'; +export * from './room'; +export * from './user'; +export * from './utils'; +export * from './wired'; diff --git a/src/api/inventory/FurniCategory.ts b/src/api/inventory/FurniCategory.ts new file mode 100644 index 0000000..6528947 --- /dev/null +++ b/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/src/api/inventory/FurnitureItem.ts b/src/api/inventory/FurnitureItem.ts new file mode 100644 index 0000000..f5f2f0f --- /dev/null +++ b/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/src/api/inventory/FurnitureUtilities.ts b/src/api/inventory/FurnitureUtilities.ts new file mode 100644 index 0000000..741f1ea --- /dev/null +++ b/src/api/inventory/FurnitureUtilities.ts @@ -0,0 +1,172 @@ +import { FurnitureListItemParser, IObjectData } from '@nitrots/nitro-renderer'; +import { GetRoomEngine } from '../nitro'; +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/src/api/inventory/GroupItem.ts b/src/api/inventory/GroupItem.ts new file mode 100644 index 0000000..8569321 --- /dev/null +++ b/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/src/api/inventory/IBotItem.ts b/src/api/inventory/IBotItem.ts new file mode 100644 index 0000000..0a370ba --- /dev/null +++ b/src/api/inventory/IBotItem.ts @@ -0,0 +1,6 @@ +import { BotData } from '@nitrots/nitro-renderer'; + +export interface IBotItem +{ + botData: BotData; +} diff --git a/src/api/inventory/IFurnitureItem.ts b/src/api/inventory/IFurnitureItem.ts new file mode 100644 index 0000000..435597d --- /dev/null +++ b/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/src/api/inventory/IPetItem.ts b/src/api/inventory/IPetItem.ts new file mode 100644 index 0000000..910d5df --- /dev/null +++ b/src/api/inventory/IPetItem.ts @@ -0,0 +1,6 @@ +import { PetData } from '@nitrots/nitro-renderer'; + +export interface IPetItem +{ + petData: PetData; +} diff --git a/src/api/inventory/IUnseenItemTracker.ts b/src/api/inventory/IUnseenItemTracker.ts new file mode 100644 index 0000000..8a70a16 --- /dev/null +++ b/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/src/api/inventory/InventoryUtilities.ts b/src/api/inventory/InventoryUtilities.ts new file mode 100644 index 0000000..e263b8c --- /dev/null +++ b/src/api/inventory/InventoryUtilities.ts @@ -0,0 +1,117 @@ +import { FurniturePlacePaintComposer, RoomObjectCategory, RoomObjectPlacementSource, RoomObjectType } from '@nitrots/nitro-renderer'; +import { CreateLinkEvent, GetRoomEngine, GetRoomSessionManager, 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/src/api/inventory/PetUtilities.ts b/src/api/inventory/PetUtilities.ts new file mode 100644 index 0000000..881a09e --- /dev/null +++ b/src/api/inventory/PetUtilities.ts @@ -0,0 +1,104 @@ +import { PetData } from '@nitrots/nitro-renderer'; +import { CreateLinkEvent } from '../nitro'; +import { cancelRoomObjectPlacement, getPlacingItemId } from './InventoryUtilities'; +import { IPetItem } from './IPetItem'; +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/src/api/inventory/TradeState.ts b/src/api/inventory/TradeState.ts new file mode 100644 index 0000000..3df418b --- /dev/null +++ b/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/src/api/inventory/TradeUserData.ts b/src/api/inventory/TradeUserData.ts new file mode 100644 index 0000000..452c7ee --- /dev/null +++ b/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/src/api/inventory/TradingNotificationType.ts b/src/api/inventory/TradingNotificationType.ts new file mode 100644 index 0000000..4aed490 --- /dev/null +++ b/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/src/api/inventory/TradingUtilities.ts b/src/api/inventory/TradingUtilities.ts new file mode 100644 index 0000000..28ca8c8 --- /dev/null +++ b/src/api/inventory/TradingUtilities.ts @@ -0,0 +1,71 @@ +import { AdvancedMap, IObjectData, ItemDataStructure, StringDataType } from '@nitrots/nitro-renderer'; +import { GetSessionDataManager } from '../nitro'; +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/src/api/inventory/UnseenItemCategory.ts b/src/api/inventory/UnseenItemCategory.ts new file mode 100644 index 0000000..cbd7e9b --- /dev/null +++ b/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/src/api/inventory/index.ts b/src/api/inventory/index.ts new file mode 100644 index 0000000..76962cf --- /dev/null +++ b/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 './InventoryUtilities'; +export * from './IPetItem'; +export * from './IUnseenItemTracker'; +export * from './PetUtilities'; +export * from './TradeState'; +export * from './TradeUserData'; +export * from './TradingNotificationType'; +export * from './TradingUtilities'; +export * from './UnseenItemCategory'; diff --git a/src/api/mod-tools/GetIssueCategoryName.ts b/src/api/mod-tools/GetIssueCategoryName.ts new file mode 100644 index 0000000..81a3f86 --- /dev/null +++ b/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/src/api/mod-tools/ISelectedUser.ts b/src/api/mod-tools/ISelectedUser.ts new file mode 100644 index 0000000..4f6e76b --- /dev/null +++ b/src/api/mod-tools/ISelectedUser.ts @@ -0,0 +1,5 @@ +export interface ISelectedUser +{ + userId: number; + username: string; +} diff --git a/src/api/mod-tools/IUserInfo.ts b/src/api/mod-tools/IUserInfo.ts new file mode 100644 index 0000000..8d49aa7 --- /dev/null +++ b/src/api/mod-tools/IUserInfo.ts @@ -0,0 +1,6 @@ +export interface IUserInfo +{ + nameKey: string; + nameKeyFallback: string; + value: string; +} diff --git a/src/api/mod-tools/ModActionDefinition.ts b/src/api/mod-tools/ModActionDefinition.ts new file mode 100644 index 0000000..b28aa9c --- /dev/null +++ b/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/src/api/mod-tools/index.ts b/src/api/mod-tools/index.ts new file mode 100644 index 0000000..004bbaa --- /dev/null +++ b/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/src/api/navigator/DoorStateType.ts b/src/api/navigator/DoorStateType.ts new file mode 100644 index 0000000..1f8a8ef --- /dev/null +++ b/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/src/api/navigator/INavigatorData.ts b/src/api/navigator/INavigatorData.ts new file mode 100644 index 0000000..e50b6fe --- /dev/null +++ b/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/src/api/navigator/INavigatorSearchFilter.ts b/src/api/navigator/INavigatorSearchFilter.ts new file mode 100644 index 0000000..179d5d5 --- /dev/null +++ b/src/api/navigator/INavigatorSearchFilter.ts @@ -0,0 +1,5 @@ +export interface INavigatorSearchFilter +{ + name: string; + query: string; +} diff --git a/src/api/navigator/IRoomChatSettings.ts b/src/api/navigator/IRoomChatSettings.ts new file mode 100644 index 0000000..aee426c --- /dev/null +++ b/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/src/api/navigator/IRoomData.ts b/src/api/navigator/IRoomData.ts new file mode 100644 index 0000000..9146314 --- /dev/null +++ b/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/src/api/navigator/IRoomModel.ts b/src/api/navigator/IRoomModel.ts new file mode 100644 index 0000000..73dfe27 --- /dev/null +++ b/src/api/navigator/IRoomModel.ts @@ -0,0 +1,6 @@ +export interface IRoomModel +{ + clubLevel: number; + tileSize: number; + name: string; +} diff --git a/src/api/navigator/IRoomModerationSettings.ts b/src/api/navigator/IRoomModerationSettings.ts new file mode 100644 index 0000000..266fe47 --- /dev/null +++ b/src/api/navigator/IRoomModerationSettings.ts @@ -0,0 +1,6 @@ +export interface IRoomModerationSettings +{ + allowMute: number; + allowKick: number; + allowBan: number; +} diff --git a/src/api/navigator/NavigatorSearchResultViewDisplayMode.ts b/src/api/navigator/NavigatorSearchResultViewDisplayMode.ts new file mode 100644 index 0000000..b532d1a --- /dev/null +++ b/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/src/api/navigator/RoomInfoData.ts b/src/api/navigator/RoomInfoData.ts new file mode 100644 index 0000000..fc0a93b --- /dev/null +++ b/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/src/api/navigator/RoomSettingsUtils.ts b/src/api/navigator/RoomSettingsUtils.ts new file mode 100644 index 0000000..bc611da --- /dev/null +++ b/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/src/api/navigator/SearchFilterOptions.ts b/src/api/navigator/SearchFilterOptions.ts new file mode 100644 index 0000000..aaf1290 --- /dev/null +++ b/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/src/api/navigator/TryVisitRoom.ts b/src/api/navigator/TryVisitRoom.ts new file mode 100644 index 0000000..81138d6 --- /dev/null +++ b/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/src/api/navigator/index.ts b/src/api/navigator/index.ts new file mode 100644 index 0000000..bceb33e --- /dev/null +++ b/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/src/api/nitro/AddLinkEventTracker.ts b/src/api/nitro/AddLinkEventTracker.ts new file mode 100644 index 0000000..8b9f00f --- /dev/null +++ b/src/api/nitro/AddLinkEventTracker.ts @@ -0,0 +1,7 @@ +import { ILinkEventTracker } from '@nitrots/nitro-renderer'; +import { GetNitroInstance } from './GetNitroInstance'; + +export function AddEventLinkTracker(tracker: ILinkEventTracker): void +{ + GetNitroInstance().addLinkEventTracker(tracker); +} diff --git a/src/api/nitro/CreateLinkEvent.ts b/src/api/nitro/CreateLinkEvent.ts new file mode 100644 index 0000000..2acfa86 --- /dev/null +++ b/src/api/nitro/CreateLinkEvent.ts @@ -0,0 +1,8 @@ +import { GetNitroInstance } from './GetNitroInstance'; + +export function CreateLinkEvent(link: string): void +{ + link = (link.startsWith('event:') ? link.substring(6) : link); + + GetNitroInstance().createLinkEvent(link); +} diff --git a/src/api/nitro/GetCommunication.ts b/src/api/nitro/GetCommunication.ts new file mode 100644 index 0000000..5dc5761 --- /dev/null +++ b/src/api/nitro/GetCommunication.ts @@ -0,0 +1,7 @@ +import { INitroCommunicationManager } from '@nitrots/nitro-renderer'; +import { GetNitroInstance } from './GetNitroInstance'; + +export function GetCommunication(): INitroCommunicationManager +{ + return GetNitroInstance()?.communication; +} diff --git a/src/api/nitro/GetConfiguration.ts b/src/api/nitro/GetConfiguration.ts new file mode 100644 index 0000000..800d1f1 --- /dev/null +++ b/src/api/nitro/GetConfiguration.ts @@ -0,0 +1,6 @@ +import { NitroConfiguration } from '@nitrots/nitro-renderer'; + +export function GetConfiguration(key: string, value: T = null): T +{ + return NitroConfiguration.getValue(key, value); +} diff --git a/src/api/nitro/GetConnection.ts b/src/api/nitro/GetConnection.ts new file mode 100644 index 0000000..ec39d8d --- /dev/null +++ b/src/api/nitro/GetConnection.ts @@ -0,0 +1,7 @@ +import { IConnection } from '@nitrots/nitro-renderer'; +import { GetCommunication } from './GetCommunication'; + +export function GetConnection(): IConnection +{ + return GetCommunication()?.connection; +} diff --git a/src/api/nitro/GetLocalization.ts b/src/api/nitro/GetLocalization.ts new file mode 100644 index 0000000..7f105e6 --- /dev/null +++ b/src/api/nitro/GetLocalization.ts @@ -0,0 +1,7 @@ +import { INitroLocalizationManager } from '@nitrots/nitro-renderer'; +import { GetNitroInstance } from './GetNitroInstance'; + +export function GetLocalization(): INitroLocalizationManager +{ + return GetNitroInstance().localization; +} diff --git a/src/api/nitro/GetNitroInstance.ts b/src/api/nitro/GetNitroInstance.ts new file mode 100644 index 0000000..5e64a65 --- /dev/null +++ b/src/api/nitro/GetNitroInstance.ts @@ -0,0 +1,6 @@ +import { INitro, Nitro } from '@nitrots/nitro-renderer'; + +export function GetNitroInstance(): INitro +{ + return Nitro.instance; +} diff --git a/src/api/nitro/OpenUrl.ts b/src/api/nitro/OpenUrl.ts new file mode 100644 index 0000000..096776d --- /dev/null +++ b/src/api/nitro/OpenUrl.ts @@ -0,0 +1,16 @@ +import { HabboWebTools } from '@nitrots/nitro-renderer'; +import { CreateLinkEvent } from './CreateLinkEvent'; + +export const OpenUrl = (url: string) => +{ + if(!url || !url.length) return; + + if(url.startsWith('http')) + { + HabboWebTools.openWebPage(url); + } + else + { + CreateLinkEvent(url); + } +} diff --git a/src/api/nitro/RemoveLinkEventTracker.ts b/src/api/nitro/RemoveLinkEventTracker.ts new file mode 100644 index 0000000..d551c69 --- /dev/null +++ b/src/api/nitro/RemoveLinkEventTracker.ts @@ -0,0 +1,7 @@ +import { ILinkEventTracker } from '@nitrots/nitro-renderer'; +import { GetNitroInstance } from './GetNitroInstance'; + +export function RemoveLinkEventTracker(tracker: ILinkEventTracker): void +{ + GetNitroInstance().removeLinkEventTracker(tracker); +} diff --git a/src/api/nitro/SendMessageComposer.ts b/src/api/nitro/SendMessageComposer.ts new file mode 100644 index 0000000..dd54c02 --- /dev/null +++ b/src/api/nitro/SendMessageComposer.ts @@ -0,0 +1,4 @@ +import { IMessageComposer } from '@nitrots/nitro-renderer'; +import { GetConnection } from './GetConnection'; + +export const SendMessageComposer = (event: IMessageComposer) => GetConnection().send(event); diff --git a/src/api/nitro/avatar/GetAvatarPalette.ts b/src/api/nitro/avatar/GetAvatarPalette.ts new file mode 100644 index 0000000..a46acb2 --- /dev/null +++ b/src/api/nitro/avatar/GetAvatarPalette.ts @@ -0,0 +1,7 @@ +import { IPalette } from '@nitrots/nitro-renderer'; +import { GetAvatarRenderManager } from './GetAvatarRenderManager'; + +export function GetAvatarPalette(paletteId: number): IPalette +{ + return GetAvatarRenderManager().structureData.getPalette(paletteId); +} diff --git a/src/api/nitro/avatar/GetAvatarRenderManager.ts b/src/api/nitro/avatar/GetAvatarRenderManager.ts new file mode 100644 index 0000000..460f010 --- /dev/null +++ b/src/api/nitro/avatar/GetAvatarRenderManager.ts @@ -0,0 +1,7 @@ +import { IAvatarRenderManager } from '@nitrots/nitro-renderer'; +import { GetNitroInstance } from '../GetNitroInstance'; + +export function GetAvatarRenderManager(): IAvatarRenderManager +{ + return GetNitroInstance().avatar; +} diff --git a/src/api/nitro/avatar/GetAvatarSetType.ts b/src/api/nitro/avatar/GetAvatarSetType.ts new file mode 100644 index 0000000..ad95f44 --- /dev/null +++ b/src/api/nitro/avatar/GetAvatarSetType.ts @@ -0,0 +1,7 @@ +import { ISetType } from '@nitrots/nitro-renderer'; +import { GetAvatarRenderManager } from './GetAvatarRenderManager'; + +export function GetAvatarSetType(setType: string): ISetType +{ + return GetAvatarRenderManager().structureData.getSetType(setType); +} diff --git a/src/api/nitro/avatar/index.ts b/src/api/nitro/avatar/index.ts new file mode 100644 index 0000000..258a1ce --- /dev/null +++ b/src/api/nitro/avatar/index.ts @@ -0,0 +1,3 @@ +export * from './GetAvatarPalette'; +export * from './GetAvatarRenderManager'; +export * from './GetAvatarSetType'; diff --git a/src/api/nitro/camera/GetRoomCameraWidgetManager.ts b/src/api/nitro/camera/GetRoomCameraWidgetManager.ts new file mode 100644 index 0000000..392495d --- /dev/null +++ b/src/api/nitro/camera/GetRoomCameraWidgetManager.ts @@ -0,0 +1,7 @@ +import { IRoomCameraWidgetManager } from '@nitrots/nitro-renderer'; +import { GetNitroInstance } from '../GetNitroInstance'; + +export function GetRoomCameraWidgetManager(): IRoomCameraWidgetManager +{ + return GetNitroInstance().cameraManager; +} diff --git a/src/api/nitro/camera/index.ts b/src/api/nitro/camera/index.ts new file mode 100644 index 0000000..3c2707c --- /dev/null +++ b/src/api/nitro/camera/index.ts @@ -0,0 +1 @@ +export * from './GetRoomCameraWidgetManager'; diff --git a/src/api/nitro/core/GetConfigurationManager.ts b/src/api/nitro/core/GetConfigurationManager.ts new file mode 100644 index 0000000..66ce153 --- /dev/null +++ b/src/api/nitro/core/GetConfigurationManager.ts @@ -0,0 +1,7 @@ +import { IConfigurationManager } from '@nitrots/nitro-renderer'; +import { GetNitroCore } from './GetNitroCore'; + +export function GetConfigurationManager(): IConfigurationManager +{ + return GetNitroCore().configuration; +} diff --git a/src/api/nitro/core/GetNitroCore.ts b/src/api/nitro/core/GetNitroCore.ts new file mode 100644 index 0000000..ef34b66 --- /dev/null +++ b/src/api/nitro/core/GetNitroCore.ts @@ -0,0 +1,7 @@ +import { INitroCore } from '@nitrots/nitro-renderer'; +import { GetNitroInstance } from '..'; + +export function GetNitroCore(): INitroCore +{ + return GetNitroInstance().core; +} diff --git a/src/api/nitro/core/index.ts b/src/api/nitro/core/index.ts new file mode 100644 index 0000000..3322c9c --- /dev/null +++ b/src/api/nitro/core/index.ts @@ -0,0 +1,2 @@ +export * from './GetConfigurationManager'; +export * from './GetNitroCore'; diff --git a/src/api/nitro/index.ts b/src/api/nitro/index.ts new file mode 100644 index 0000000..c43e958 --- /dev/null +++ b/src/api/nitro/index.ts @@ -0,0 +1,15 @@ +export * from './AddLinkEventTracker'; +export * from './avatar'; +export * from './camera'; +export * from './core'; +export * from './CreateLinkEvent'; +export * from './GetCommunication'; +export * from './GetConfiguration'; +export * from './GetConnection'; +export * from './GetLocalization'; +export * from './GetNitroInstance'; +export * from './OpenUrl'; +export * from './RemoveLinkEventTracker'; +export * from './room'; +export * from './SendMessageComposer'; +export * from './session'; diff --git a/src/api/nitro/room/DispatchMouseEvent.ts b/src/api/nitro/room/DispatchMouseEvent.ts new file mode 100644 index 0000000..51111ac --- /dev/null +++ b/src/api/nitro/room/DispatchMouseEvent.ts @@ -0,0 +1,55 @@ +import { MouseEventType } from '@nitrots/nitro-renderer'; +import { GetRoomEngine } from './GetRoomEngine'; + +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/src/api/nitro/room/DispatchTouchEvent.ts b/src/api/nitro/room/DispatchTouchEvent.ts new file mode 100644 index 0000000..5017544 --- /dev/null +++ b/src/api/nitro/room/DispatchTouchEvent.ts @@ -0,0 +1,82 @@ +import { MouseEventType, TouchEventType } from '@nitrots/nitro-renderer'; +import { GetRoomEngine } from './GetRoomEngine'; + +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/src/api/nitro/room/GetOwnRoomObject.ts b/src/api/nitro/room/GetOwnRoomObject.ts new file mode 100644 index 0000000..b8d0364 --- /dev/null +++ b/src/api/nitro/room/GetOwnRoomObject.ts @@ -0,0 +1,32 @@ +import { IRoomObjectController, RoomObjectCategory } from '@nitrots/nitro-renderer'; +import { GetRoomSession, GetSessionDataManager } from '../session'; +import { GetRoomEngine } from './GetRoomEngine'; + +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/src/api/nitro/room/GetRoomEngine.ts b/src/api/nitro/room/GetRoomEngine.ts new file mode 100644 index 0000000..4d18ea7 --- /dev/null +++ b/src/api/nitro/room/GetRoomEngine.ts @@ -0,0 +1,7 @@ +import { IRoomEngine } from '@nitrots/nitro-renderer'; +import { GetNitroInstance } from '../GetNitroInstance'; + +export function GetRoomEngine(): IRoomEngine +{ + return GetNitroInstance().roomEngine; +} diff --git a/src/api/nitro/room/GetRoomObjectBounds.ts b/src/api/nitro/room/GetRoomObjectBounds.ts new file mode 100644 index 0000000..0a42ad6 --- /dev/null +++ b/src/api/nitro/room/GetRoomObjectBounds.ts @@ -0,0 +1,13 @@ +import { GetRoomEngine } from './GetRoomEngine'; + +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/src/api/nitro/room/GetRoomObjectScreenLocation.ts b/src/api/nitro/room/GetRoomObjectScreenLocation.ts new file mode 100644 index 0000000..1a8d973 --- /dev/null +++ b/src/api/nitro/room/GetRoomObjectScreenLocation.ts @@ -0,0 +1,13 @@ +import { GetRoomEngine } from './GetRoomEngine'; + +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/src/api/nitro/room/InitializeRoomInstanceRenderingCanvas.ts b/src/api/nitro/room/InitializeRoomInstanceRenderingCanvas.ts new file mode 100644 index 0000000..d85d739 --- /dev/null +++ b/src/api/nitro/room/InitializeRoomInstanceRenderingCanvas.ts @@ -0,0 +1,9 @@ +import { GetRoomEngine } from './GetRoomEngine'; + +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/src/api/nitro/room/IsFurnitureSelectionDisabled.ts b/src/api/nitro/room/IsFurnitureSelectionDisabled.ts new file mode 100644 index 0000000..367f389 --- /dev/null +++ b/src/api/nitro/room/IsFurnitureSelectionDisabled.ts @@ -0,0 +1,24 @@ +import { RoomEngineObjectEvent, RoomObjectVariable } from '@nitrots/nitro-renderer'; +import { GetSessionDataManager } from '../..'; +import { GetRoomEngine } from './GetRoomEngine'; + +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/src/api/nitro/room/ProcessRoomObjectOperation.ts b/src/api/nitro/room/ProcessRoomObjectOperation.ts new file mode 100644 index 0000000..b9187fa --- /dev/null +++ b/src/api/nitro/room/ProcessRoomObjectOperation.ts @@ -0,0 +1,6 @@ +import { GetRoomEngine } from './GetRoomEngine'; + +export function ProcessRoomObjectOperation(objectId: number, category: number, operation: string): void +{ + GetRoomEngine().processRoomObjectOperation(objectId, category, operation); +} diff --git a/src/api/nitro/room/SetActiveRoomId.ts b/src/api/nitro/room/SetActiveRoomId.ts new file mode 100644 index 0000000..2cccd84 --- /dev/null +++ b/src/api/nitro/room/SetActiveRoomId.ts @@ -0,0 +1,6 @@ +import { GetRoomEngine } from './GetRoomEngine'; + +export function SetActiveRoomId(roomId: number): void +{ + GetRoomEngine().setActiveRoomId(roomId); +} diff --git a/src/api/nitro/room/index.ts b/src/api/nitro/room/index.ts new file mode 100644 index 0000000..764b72e --- /dev/null +++ b/src/api/nitro/room/index.ts @@ -0,0 +1,10 @@ +export * from './DispatchMouseEvent'; +export * from './DispatchTouchEvent'; +export * from './GetOwnRoomObject'; +export * from './GetRoomEngine'; +export * from './GetRoomObjectBounds'; +export * from './GetRoomObjectScreenLocation'; +export * from './InitializeRoomInstanceRenderingCanvas'; +export * from './IsFurnitureSelectionDisabled'; +export * from './ProcessRoomObjectOperation'; +export * from './SetActiveRoomId'; diff --git a/src/api/nitro/session/CanManipulateFurniture.ts b/src/api/nitro/session/CanManipulateFurniture.ts new file mode 100644 index 0000000..8655292 --- /dev/null +++ b/src/api/nitro/session/CanManipulateFurniture.ts @@ -0,0 +1,11 @@ +import { IRoomSession, RoomControllerLevel } from '@nitrots/nitro-renderer'; +import { GetSessionDataManager } from '../..'; +import { GetRoomEngine } from '../room/GetRoomEngine'; +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/src/api/nitro/session/CreateRoomSession.ts b/src/api/nitro/session/CreateRoomSession.ts new file mode 100644 index 0000000..3be6a8a --- /dev/null +++ b/src/api/nitro/session/CreateRoomSession.ts @@ -0,0 +1,6 @@ +import { GetRoomSessionManager } from './GetRoomSessionManager'; + +export function CreateRoomSession(roomId: number, password: string = null): void +{ + GetRoomSessionManager().createSession(roomId, password); +} diff --git a/src/api/nitro/session/GetCanStandUp.ts b/src/api/nitro/session/GetCanStandUp.ts new file mode 100644 index 0000000..4915d18 --- /dev/null +++ b/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/src/api/nitro/session/GetCanUseExpression.ts b/src/api/nitro/session/GetCanUseExpression.ts new file mode 100644 index 0000000..c7c7367 --- /dev/null +++ b/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/src/api/nitro/session/GetClubMemberLevel.ts b/src/api/nitro/session/GetClubMemberLevel.ts new file mode 100644 index 0000000..fea75b7 --- /dev/null +++ b/src/api/nitro/session/GetClubMemberLevel.ts @@ -0,0 +1,10 @@ +import { HabboClubLevelEnum } from '@nitrots/nitro-renderer'; +import { GetConfiguration } from '..'; +import { GetSessionDataManager } from './GetSessionDataManager'; + +export function GetClubMemberLevel(): number +{ + if(GetConfiguration('hc.disabled', false)) return HabboClubLevelEnum.VIP; + + return GetSessionDataManager().clubLevel; +} diff --git a/src/api/nitro/session/GetFurnitureData.ts b/src/api/nitro/session/GetFurnitureData.ts new file mode 100644 index 0000000..71afef8 --- /dev/null +++ b/src/api/nitro/session/GetFurnitureData.ts @@ -0,0 +1,20 @@ +import { IFurnitureData } from '@nitrots/nitro-renderer'; +import { GetSessionDataManager } from '.'; +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/src/api/nitro/session/GetFurnitureDataForProductOffer.ts b/src/api/nitro/session/GetFurnitureDataForProductOffer.ts new file mode 100644 index 0000000..0588835 --- /dev/null +++ b/src/api/nitro/session/GetFurnitureDataForProductOffer.ts @@ -0,0 +1,21 @@ +import { CatalogPageMessageProductData, FurnitureType, IFurnitureData } from '@nitrots/nitro-renderer'; +import { GetSessionDataManager } from './GetSessionDataManager'; + +export function GetFurnitureDataForProductOffer(offer: CatalogPageMessageProductData): IFurnitureData +{ + if(!offer) return null; + + let furniData: IFurnitureData = null; + + switch((offer.productType.toUpperCase())) + { + case FurnitureType.FLOOR: + furniData = GetSessionDataManager().getFloorItemData(offer.furniClassId); + break; + case FurnitureType.WALL: + furniData = GetSessionDataManager().getWallItemData(offer.furniClassId); + break; + } + + return furniData; +} diff --git a/src/api/nitro/session/GetFurnitureDataForRoomObject.ts b/src/api/nitro/session/GetFurnitureDataForRoomObject.ts new file mode 100644 index 0000000..360f18d --- /dev/null +++ b/src/api/nitro/session/GetFurnitureDataForRoomObject.ts @@ -0,0 +1,22 @@ +import { IFurnitureData, RoomObjectCategory, RoomObjectVariable } from '@nitrots/nitro-renderer'; +import { GetRoomEngine } from '../room'; +import { GetSessionDataManager } from './GetSessionDataManager'; + +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/src/api/nitro/session/GetOwnPosture.ts b/src/api/nitro/session/GetOwnPosture.ts new file mode 100644 index 0000000..fe0c5f3 --- /dev/null +++ b/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/src/api/nitro/session/GetProductDataForLocalization.ts b/src/api/nitro/session/GetProductDataForLocalization.ts new file mode 100644 index 0000000..692e0ec --- /dev/null +++ b/src/api/nitro/session/GetProductDataForLocalization.ts @@ -0,0 +1,9 @@ +import { IProductData } from '@nitrots/nitro-renderer'; +import { GetSessionDataManager } from './GetSessionDataManager'; + +export function GetProductDataForLocalization(localizationId: string): IProductData +{ + if(!localizationId) return null; + + return GetSessionDataManager().getProductData(localizationId); +} diff --git a/src/api/nitro/session/GetRoomSession.ts b/src/api/nitro/session/GetRoomSession.ts new file mode 100644 index 0000000..37a8b9f --- /dev/null +++ b/src/api/nitro/session/GetRoomSession.ts @@ -0,0 +1,7 @@ +import { IRoomSession } from '@nitrots/nitro-renderer'; +import { GetRoomSessionManager } from './GetRoomSessionManager'; + +export function GetRoomSession(): IRoomSession +{ + return GetRoomSessionManager().getSession(-1); +} diff --git a/src/api/nitro/session/GetRoomSessionManager.ts b/src/api/nitro/session/GetRoomSessionManager.ts new file mode 100644 index 0000000..579342d --- /dev/null +++ b/src/api/nitro/session/GetRoomSessionManager.ts @@ -0,0 +1,7 @@ +import { IRoomSessionManager } from '@nitrots/nitro-renderer'; +import { GetNitroInstance } from '../GetNitroInstance'; + +export function GetRoomSessionManager(): IRoomSessionManager +{ + return GetNitroInstance().roomSessionManager; +} diff --git a/src/api/nitro/session/GetSessionDataManager.ts b/src/api/nitro/session/GetSessionDataManager.ts new file mode 100644 index 0000000..1f3674e --- /dev/null +++ b/src/api/nitro/session/GetSessionDataManager.ts @@ -0,0 +1,7 @@ +import { ISessionDataManager } from '@nitrots/nitro-renderer'; +import { GetNitroInstance } from '../GetNitroInstance'; + +export function GetSessionDataManager(): ISessionDataManager +{ + return GetNitroInstance().sessionDataManager; +} diff --git a/src/api/nitro/session/GoToDesktop.ts b/src/api/nitro/session/GoToDesktop.ts new file mode 100644 index 0000000..1bbe016 --- /dev/null +++ b/src/api/nitro/session/GoToDesktop.ts @@ -0,0 +1,7 @@ +import { DesktopViewComposer } from '@nitrots/nitro-renderer'; +import { SendMessageComposer } from '..'; + +export function GoToDesktop(): void +{ + SendMessageComposer(new DesktopViewComposer()); +} diff --git a/src/api/nitro/session/HasHabboClub.ts b/src/api/nitro/session/HasHabboClub.ts new file mode 100644 index 0000000..4077a0f --- /dev/null +++ b/src/api/nitro/session/HasHabboClub.ts @@ -0,0 +1,7 @@ +import { HabboClubLevelEnum } from '@nitrots/nitro-renderer'; +import { GetSessionDataManager } from './GetSessionDataManager'; + +export function HasHabboClub(): boolean +{ + return (GetSessionDataManager().clubLevel >= HabboClubLevelEnum.CLUB); +} diff --git a/src/api/nitro/session/HasHabboVip.ts b/src/api/nitro/session/HasHabboVip.ts new file mode 100644 index 0000000..87f1156 --- /dev/null +++ b/src/api/nitro/session/HasHabboVip.ts @@ -0,0 +1,7 @@ +import { HabboClubLevelEnum } from '@nitrots/nitro-renderer'; +import { GetSessionDataManager } from './GetSessionDataManager'; + +export function HasHabboVip(): boolean +{ + return (GetSessionDataManager().clubLevel >= HabboClubLevelEnum.VIP); +} diff --git a/src/api/nitro/session/IsOwnerOfFloorFurniture.ts b/src/api/nitro/session/IsOwnerOfFloorFurniture.ts new file mode 100644 index 0000000..65ef7fb --- /dev/null +++ b/src/api/nitro/session/IsOwnerOfFloorFurniture.ts @@ -0,0 +1,16 @@ +import { RoomObjectCategory, RoomObjectVariable } from '@nitrots/nitro-renderer'; +import { GetRoomSession } from '.'; +import { GetRoomEngine } from '..'; +import { GetSessionDataManager } from '../../../api'; + +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/src/api/nitro/session/IsOwnerOfFurniture.ts b/src/api/nitro/session/IsOwnerOfFurniture.ts new file mode 100644 index 0000000..56b7fc3 --- /dev/null +++ b/src/api/nitro/session/IsOwnerOfFurniture.ts @@ -0,0 +1,12 @@ +import { IRoomObject, RoomObjectVariable } from '@nitrots/nitro-renderer'; +import { GetSessionDataManager } from '../../../api'; + +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/src/api/nitro/session/IsRidingHorse.ts b/src/api/nitro/session/IsRidingHorse.ts new file mode 100644 index 0000000..f946b69 --- /dev/null +++ b/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/src/api/nitro/session/StartRoomSession.ts b/src/api/nitro/session/StartRoomSession.ts new file mode 100644 index 0000000..99d9d0b --- /dev/null +++ b/src/api/nitro/session/StartRoomSession.ts @@ -0,0 +1,7 @@ +import { IRoomSession } from '@nitrots/nitro-renderer'; +import { GetRoomSessionManager } from './GetRoomSessionManager'; + +export function StartRoomSession(session: IRoomSession): void +{ + GetRoomSessionManager().startSession(session); +} diff --git a/src/api/nitro/session/VisitDesktop.ts b/src/api/nitro/session/VisitDesktop.ts new file mode 100644 index 0000000..e7416a3 --- /dev/null +++ b/src/api/nitro/session/VisitDesktop.ts @@ -0,0 +1,9 @@ +import { GetRoomSession, GetRoomSessionManager, GoToDesktop } from '.'; + +export const VisitDesktop = () => +{ + if(!GetRoomSession()) return; + + GoToDesktop(); + GetRoomSessionManager().removeSession(-1); +} diff --git a/src/api/nitro/session/index.ts b/src/api/nitro/session/index.ts new file mode 100644 index 0000000..a860d7a --- /dev/null +++ b/src/api/nitro/session/index.ts @@ -0,0 +1,21 @@ +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 './GetRoomSessionManager'; +export * from './GetSessionDataManager'; +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/src/api/notification/NotificationAlertItem.ts b/src/api/notification/NotificationAlertItem.ts new file mode 100644 index 0000000..2d7702c --- /dev/null +++ b/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/src/api/notification/NotificationAlertType.ts b/src/api/notification/NotificationAlertType.ts new file mode 100644 index 0000000..ad804e8 --- /dev/null +++ b/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/src/api/notification/NotificationBubbleItem.ts b/src/api/notification/NotificationBubbleItem.ts new file mode 100644 index 0000000..fe90dab --- /dev/null +++ b/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/src/api/notification/NotificationBubbleType.ts b/src/api/notification/NotificationBubbleType.ts new file mode 100644 index 0000000..cce38f5 --- /dev/null +++ b/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/src/api/notification/NotificationConfirmItem.ts b/src/api/notification/NotificationConfirmItem.ts new file mode 100644 index 0000000..0455662 --- /dev/null +++ b/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/src/api/notification/NotificationConfirmType.ts b/src/api/notification/NotificationConfirmType.ts new file mode 100644 index 0000000..533ca05 --- /dev/null +++ b/src/api/notification/NotificationConfirmType.ts @@ -0,0 +1,4 @@ +export class NotificationConfirmType +{ + public static DEFAULT: string = 'default'; +} diff --git a/src/api/notification/index.ts b/src/api/notification/index.ts new file mode 100644 index 0000000..23476d3 --- /dev/null +++ b/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/src/api/purse/IPurse.ts b/src/api/purse/IPurse.ts new file mode 100644 index 0000000..9fffb18 --- /dev/null +++ b/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/src/api/purse/Purse.ts b/src/api/purse/Purse.ts new file mode 100644 index 0000000..6970e59 --- /dev/null +++ b/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/src/api/purse/index.ts b/src/api/purse/index.ts new file mode 100644 index 0000000..ed34480 --- /dev/null +++ b/src/api/purse/index.ts @@ -0,0 +1,2 @@ +export * from './IPurse'; +export * from './Purse'; diff --git a/src/api/room/events/RoomWidgetPollUpdateEvent.ts b/src/api/room/events/RoomWidgetPollUpdateEvent.ts new file mode 100644 index 0000000..edfb8fd --- /dev/null +++ b/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/src/api/room/events/RoomWidgetUpdateBackgroundColorPreviewEvent.ts b/src/api/room/events/RoomWidgetUpdateBackgroundColorPreviewEvent.ts new file mode 100644 index 0000000..30135a3 --- /dev/null +++ b/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/src/api/room/events/RoomWidgetUpdateChatInputContentEvent.ts b/src/api/room/events/RoomWidgetUpdateChatInputContentEvent.ts new file mode 100644 index 0000000..9352372 --- /dev/null +++ b/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/src/api/room/events/RoomWidgetUpdateEvent.ts b/src/api/room/events/RoomWidgetUpdateEvent.ts new file mode 100644 index 0000000..0ac8ff8 --- /dev/null +++ b/src/api/room/events/RoomWidgetUpdateEvent.ts @@ -0,0 +1,4 @@ +import { NitroEvent } from '@nitrots/nitro-renderer'; + +export class RoomWidgetUpdateEvent extends NitroEvent +{} diff --git a/src/api/room/events/RoomWidgetUpdateRentableBotChatEvent.ts b/src/api/room/events/RoomWidgetUpdateRentableBotChatEvent.ts new file mode 100644 index 0000000..6191e1b --- /dev/null +++ b/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/src/api/room/events/RoomWidgetUpdateRoomObjectEvent.ts b/src/api/room/events/RoomWidgetUpdateRoomObjectEvent.ts new file mode 100644 index 0000000..0660276 --- /dev/null +++ b/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/src/api/room/events/index.ts b/src/api/room/events/index.ts new file mode 100644 index 0000000..e5ed0d8 --- /dev/null +++ b/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/src/api/room/index.ts b/src/api/room/index.ts new file mode 100644 index 0000000..56aea79 --- /dev/null +++ b/src/api/room/index.ts @@ -0,0 +1,2 @@ +export * from './events'; +export * from './widgets'; diff --git a/src/api/room/widgets/AvatarInfoFurni.ts b/src/api/room/widgets/AvatarInfoFurni.ts new file mode 100644 index 0000000..3380282 --- /dev/null +++ b/src/api/room/widgets/AvatarInfoFurni.ts @@ -0,0 +1,38 @@ +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 image: HTMLImageElement = null; + 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/src/api/room/widgets/AvatarInfoName.ts b/src/api/room/widgets/AvatarInfoName.ts new file mode 100644 index 0000000..66a6a7e --- /dev/null +++ b/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/src/api/room/widgets/AvatarInfoPet.ts b/src/api/room/widgets/AvatarInfoPet.ts new file mode 100644 index 0000000..0c0435a --- /dev/null +++ b/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/src/api/room/widgets/AvatarInfoRentableBot.ts b/src/api/room/widgets/AvatarInfoRentableBot.ts new file mode 100644 index 0000000..77fb10c --- /dev/null +++ b/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/src/api/room/widgets/AvatarInfoUser.ts b/src/api/room/widgets/AvatarInfoUser.ts new file mode 100644 index 0000000..270bfbd --- /dev/null +++ b/src/api/room/widgets/AvatarInfoUser.ts @@ -0,0 +1,49 @@ +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 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/src/api/room/widgets/AvatarInfoUtilities.ts b/src/api/room/widgets/AvatarInfoUtilities.ts new file mode 100644 index 0000000..7c48944 --- /dev/null +++ b/src/api/room/widgets/AvatarInfoUtilities.ts @@ -0,0 +1,454 @@ +import { GetTickerTime, IFurnitureData, IRoomModerationSettings, IRoomPetData, IRoomUserData, ObjectDataFactory, PetFigureData, PetType, RoomControllerLevel, RoomModerationSettings, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomTradingLevelEnum, RoomWidgetEnumItemExtradataParameter, Vector3d } from '@nitrots/nitro-renderer'; +import { GetRoomEngine, GetRoomSession, GetSessionDataManager, 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 furniInfo = new AvatarInfoFurni(AvatarInfoFurni.FURNI); + + furniInfo.id = objectId; + furniInfo.category = category; + + const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, objectId, category); + + if(!roomObject) return; + + const model = roomObject.model; + + if(model.getValue(RoomWidgetEnumItemExtradataParameter.INFOSTAND_EXTRA_PARAM)) + { + furniInfo.extraParam = model.getValue(RoomWidgetEnumItemExtradataParameter.INFOSTAND_EXTRA_PARAM); + } + + const dataFormat = model.getValue(RoomObjectVariable.FURNITURE_DATA_FORMAT); + const objectData = ObjectDataFactory.getData(dataFormat); + + 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; + //this.container.connection.send(new GroupInformationComposer(guildId, false)); + } + + if(IsOwnerOfFurniture(roomObject)) furniInfo.isOwner = true; + + return furniInfo; + } + + public static getUserInfo(category: number, userData: IRoomUserData): AvatarInfoUser + { + const roomSession = GetRoomSession(); + + let userInfoType = AvatarInfoUser.OWN_USER; + + if(userData.webID !== GetSessionDataManager().userId) userInfoType = AvatarInfoUser.PEER; + + const userInfo = new AvatarInfoUser(userInfoType); + + userInfo.isSpectatorMode = roomSession.isSpectator; + userInfo.name = userData.name; + userInfo.motto = userData.custom; + 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(userInfoType === 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(userInfoType === AvatarInfoUser.PEER) + { + if(roomObject) + { + const flatControl = roomObject.model.getValue(RoomObjectVariable.FIGURE_FLAT_CONTROL); + + if(flatControl !== null) userInfo.targetRoomControllerLevel = flatControl; + + 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.NO_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/src/api/room/widgets/BotSkillsEnum.ts b/src/api/room/widgets/BotSkillsEnum.ts new file mode 100644 index 0000000..b879cdc --- /dev/null +++ b/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/src/api/room/widgets/ChatBubbleMessage.ts b/src/api/room/widgets/ChatBubbleMessage.ts new file mode 100644 index 0000000..c9cede3 --- /dev/null +++ b/src/api/room/widgets/ChatBubbleMessage.ts @@ -0,0 +1,56 @@ +import { INitroPoint } from '@nitrots/nitro-renderer'; + +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: INitroPoint = 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/src/api/room/widgets/ChatMessageTypeEnum.ts b/src/api/room/widgets/ChatMessageTypeEnum.ts new file mode 100644 index 0000000..1a5296b --- /dev/null +++ b/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/src/api/room/widgets/DimmerFurnitureWidgetPresetItem.ts b/src/api/room/widgets/DimmerFurnitureWidgetPresetItem.ts new file mode 100644 index 0000000..1a2759f --- /dev/null +++ b/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/src/api/room/widgets/DoChatsOverlap.ts b/src/api/room/widgets/DoChatsOverlap.ts new file mode 100644 index 0000000..092ce5d --- /dev/null +++ b/src/api/room/widgets/DoChatsOverlap.ts @@ -0,0 +1,7 @@ +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))); +} + \ No newline at end of file diff --git a/src/api/room/widgets/FurnitureDimmerUtilities.ts b/src/api/room/widgets/FurnitureDimmerUtilities.ts new file mode 100644 index 0000000..9d252d1 --- /dev/null +++ b/src/api/room/widgets/FurnitureDimmerUtilities.ts @@ -0,0 +1,29 @@ +import { GetRoomEngine, 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/src/api/room/widgets/GetDiskColor.ts b/src/api/room/widgets/GetDiskColor.ts new file mode 100644 index 0000000..989f294 --- /dev/null +++ b/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/src/api/room/widgets/IAvatarInfo.ts b/src/api/room/widgets/IAvatarInfo.ts new file mode 100644 index 0000000..23fb47b --- /dev/null +++ b/src/api/room/widgets/IAvatarInfo.ts @@ -0,0 +1,4 @@ +export interface IAvatarInfo +{ + type: string; +} diff --git a/src/api/room/widgets/ICraftingIngredient.ts b/src/api/room/widgets/ICraftingIngredient.ts new file mode 100644 index 0000000..cb2b031 --- /dev/null +++ b/src/api/room/widgets/ICraftingIngredient.ts @@ -0,0 +1,6 @@ +export interface ICraftingIngredient +{ + name: string; + iconUrl: string; + count: number; +} diff --git a/src/api/room/widgets/ICraftingRecipe.ts b/src/api/room/widgets/ICraftingRecipe.ts new file mode 100644 index 0000000..dd99291 --- /dev/null +++ b/src/api/room/widgets/ICraftingRecipe.ts @@ -0,0 +1,6 @@ +export interface ICraftingRecipe +{ + name: string; + localizedName: string; + iconUrl: string; +} diff --git a/src/api/room/widgets/IPhotoData.ts b/src/api/room/widgets/IPhotoData.ts new file mode 100644 index 0000000..9a7b846 --- /dev/null +++ b/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/src/api/room/widgets/MannequinUtilities.ts b/src/api/room/widgets/MannequinUtilities.ts new file mode 100644 index 0000000..5e82b9a --- /dev/null +++ b/src/api/room/widgets/MannequinUtilities.ts @@ -0,0 +1,39 @@ +import { AvatarFigurePartType, IAvatarFigureContainer } from '@nitrots/nitro-renderer'; +import { GetAvatarRenderManager } from '../../nitro'; + +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/src/api/room/widgets/PetSupplementEnum.ts b/src/api/room/widgets/PetSupplementEnum.ts new file mode 100644 index 0000000..eb23687 --- /dev/null +++ b/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/src/api/room/widgets/PostureTypeEnum.ts b/src/api/room/widgets/PostureTypeEnum.ts new file mode 100644 index 0000000..21352d7 --- /dev/null +++ b/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/src/api/room/widgets/RoomDimmerPreset.ts b/src/api/room/widgets/RoomDimmerPreset.ts new file mode 100644 index 0000000..86600d5 --- /dev/null +++ b/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/src/api/room/widgets/RoomObjectItem.ts b/src/api/room/widgets/RoomObjectItem.ts new file mode 100644 index 0000000..f4fb2d6 --- /dev/null +++ b/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/src/api/room/widgets/UseProductItem.ts b/src/api/room/widgets/UseProductItem.ts new file mode 100644 index 0000000..d3e2088 --- /dev/null +++ b/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/src/api/room/widgets/VoteValue.ts b/src/api/room/widgets/VoteValue.ts new file mode 100644 index 0000000..ecf4336 --- /dev/null +++ b/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/src/api/room/widgets/YoutubeVideoPlaybackStateEnum.ts b/src/api/room/widgets/YoutubeVideoPlaybackStateEnum.ts new file mode 100644 index 0000000..3a885d1 --- /dev/null +++ b/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/src/api/room/widgets/index.ts b/src/api/room/widgets/index.ts new file mode 100644 index 0000000..c43d6aa --- /dev/null +++ b/src/api/room/widgets/index.ts @@ -0,0 +1,25 @@ +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 './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/src/api/user/GetUserProfile.ts b/src/api/user/GetUserProfile.ts new file mode 100644 index 0000000..0f2be77 --- /dev/null +++ b/src/api/user/GetUserProfile.ts @@ -0,0 +1,7 @@ +import { UserProfileComposer } from '@nitrots/nitro-renderer'; +import { SendMessageComposer } from '..'; + +export function GetUserProfile(userId: number): void +{ + SendMessageComposer(new UserProfileComposer(userId)); +} diff --git a/src/api/user/index.ts b/src/api/user/index.ts new file mode 100644 index 0000000..1c609ea --- /dev/null +++ b/src/api/user/index.ts @@ -0,0 +1 @@ +export * from './GetUserProfile'; diff --git a/src/api/utils/CloneObject.ts b/src/api/utils/CloneObject.ts new file mode 100644 index 0000000..6cd8d3e --- /dev/null +++ b/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/src/api/utils/ColorUtils.ts b/src/api/utils/ColorUtils.ts new file mode 100644 index 0000000..c32f8fb --- /dev/null +++ b/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/src/api/utils/ConvertSeconds.ts b/src/api/utils/ConvertSeconds.ts new file mode 100644 index 0000000..f559dea --- /dev/null +++ b/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/src/api/utils/GetLocalStorage.ts b/src/api/utils/GetLocalStorage.ts new file mode 100644 index 0000000..769df6d --- /dev/null +++ b/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/src/api/utils/LocalStorageKeys.ts b/src/api/utils/LocalStorageKeys.ts new file mode 100644 index 0000000..6c92279 --- /dev/null +++ b/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/src/api/utils/LocalizeBadgeDescription.ts b/src/api/utils/LocalizeBadgeDescription.ts new file mode 100644 index 0000000..04fd7df --- /dev/null +++ b/src/api/utils/LocalizeBadgeDescription.ts @@ -0,0 +1,10 @@ +import { GetNitroInstance } from '..'; + +export const LocalizeBadgeDescription = (key: string) => +{ + let badgeDesc = GetNitroInstance().localization.getBadgeDesc(key); + + if(!badgeDesc || !badgeDesc.length) badgeDesc = `badge_desc_${ key }`; + + return badgeDesc; +} diff --git a/src/api/utils/LocalizeBageName.ts b/src/api/utils/LocalizeBageName.ts new file mode 100644 index 0000000..d722ab0 --- /dev/null +++ b/src/api/utils/LocalizeBageName.ts @@ -0,0 +1,10 @@ +import { GetNitroInstance } from '..'; + +export const LocalizeBadgeName = (key: string) => +{ + let badgeName = GetNitroInstance().localization.getBadgeName(key); + + if(!badgeName || !badgeName.length) badgeName = `badge_name_${ key }`; + + return badgeName; +} diff --git a/src/api/utils/LocalizeFormattedNumber.ts b/src/api/utils/LocalizeFormattedNumber.ts new file mode 100644 index 0000000..fab30d4 --- /dev/null +++ b/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/src/api/utils/LocalizeShortNumber.ts b/src/api/utils/LocalizeShortNumber.ts new file mode 100644 index 0000000..30975ec --- /dev/null +++ b/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/src/api/utils/LocalizeText.ts b/src/api/utils/LocalizeText.ts new file mode 100644 index 0000000..cf34a1f --- /dev/null +++ b/src/api/utils/LocalizeText.ts @@ -0,0 +1,6 @@ +import { GetNitroInstance } from '..'; + +export function LocalizeText(key: string, parameters: string[] = null, replacements: string[] = null): string +{ + return GetNitroInstance().getLocalizationWithParameters(key, parameters, replacements); +} diff --git a/src/api/utils/PlaySound.ts b/src/api/utils/PlaySound.ts new file mode 100644 index 0000000..0f9a39d --- /dev/null +++ b/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/src/api/utils/ProductImageUtility.ts b/src/api/utils/ProductImageUtility.ts new file mode 100644 index 0000000..59c8c4f --- /dev/null +++ b/src/api/utils/ProductImageUtility.ts @@ -0,0 +1,59 @@ +import { CatalogPageMessageProductData } from '@nitrots/nitro-renderer'; +import { FurniCategory } from '../inventory'; +import { GetRoomEngine } from '../nitro'; + +export class ProductImageUtility +{ + public static getProductImageUrl(productType: string, furniClassId: number, extraParam: string): string + { + let imageUrl: string = null; + + switch(productType) + { + case CatalogPageMessageProductData.S: + imageUrl = GetRoomEngine().getFurnitureFloorIconUrl(furniClassId); + break; + case CatalogPageMessageProductData.I: + 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 CatalogPageMessageProductData.E: + // fx_icon_furniClassId_png + break; + } + + return imageUrl; + } + + public static getProductCategory(productType: string, furniClassId: number): number + { + if(productType === CatalogPageMessageProductData.S) return 1; + + if(productType === CatalogPageMessageProductData.I) + { + if(furniClassId === 3001) return FurniCategory.WALL_PAPER; + + if(furniClassId === 3002) return FurniCategory.FLOOR; + + if(furniClassId === 4057) return FurniCategory.LANDSCAPE; + } + + return 1; + } +} diff --git a/src/api/utils/Randomizer.ts b/src/api/utils/Randomizer.ts new file mode 100644 index 0000000..1f67a12 --- /dev/null +++ b/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/src/api/utils/RoomChatFormatter.ts b/src/api/utils/RoomChatFormatter.ts new file mode 100644 index 0000000..1c9329d --- /dev/null +++ b/src/api/utils/RoomChatFormatter.ts @@ -0,0 +1,102 @@ +import * as joypixels from 'emoji-toolkit'; + +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 = ''; + if (content.toLowerCase().includes("onerror=")) + content = "Error al ejecutar"; + +if (content.toLowerCase().includes("onmouseover=")) + content = "Error al ejecutar"; + + content = encodeHTML(content); + content = (joypixels.shortnameToUnicode(content) as string) + + if(content.includes("giphy.com/media")){ + content = "

"; + } + + if(content.includes("https://int.habbeh.net/audios/")) + { + if(content.includes("https://int.habbeh.net/audios/errorpeso")) + { + content = "El audio es demasiado pesado"; + } + else + { + content = "