From ff80c504266d7cdb11350e56e24924750d4da480 Mon Sep 17 00:00:00 2001 From: Ahmed Khidr Date: Wed, 17 Jan 2024 08:52:08 +0400 Subject: [PATCH] Initial commit --- .browserslistrc | 11 + .eslintrc.json | 110 + .gitignore | 31 + .gitlab-ci.yml-disabled | 29 + .vscode/settings.json | 32 + README.md | 50 + index.html | 35 + package.json | 41 + public/android-chrome-192x192.png | Bin 0 -> 9604 bytes public/android-chrome-512x512.png | Bin 0 -> 29298 bytes public/apple-touch-icon.png | Bin 0 -> 8241 bytes public/browserconfig.xml | 9 + public/favicon-16x16.png | Bin 0 -> 1017 bytes public/favicon-32x32.png | Bin 0 -> 1778 bytes public/favicon.ico | Bin 0 -> 15086 bytes public/mstile-150x150.png | Bin 0 -> 6600 bytes public/robots.txt | 3 + public/safari-pinned-tab.svg | 154 + public/site.webmanifest | 20 + src/App.scss | 105 + src/App.tsx | 141 + src/api/GetRendererVersion.ts | 3 + src/api/GetUIVersion.ts | 1 + src/api/achievements/AchievementCategory.ts | 40 + src/api/achievements/AchievementUtilities.ts | 97 + src/api/achievements/IAchievementCategory.ts | 7 + src/api/achievements/index.ts | 3 + src/api/avatar/AvatarEditorAction.ts | 7 + src/api/avatar/AvatarEditorGridColorItem.ts | 65 + src/api/avatar/AvatarEditorGridPartItem.ts | 337 ++ src/api/avatar/AvatarEditorUtilities.ts | 277 ++ src/api/avatar/BodyModel.ts | 76 + src/api/avatar/CategoryBaseModel.ts | 246 ++ src/api/avatar/CategoryData.ts | 487 +++ src/api/avatar/FigureData.ts | 287 ++ src/api/avatar/FigureGenerator.ts | 90 + src/api/avatar/HeadModel.ts | 24 + src/api/avatar/IAvatarEditorCategoryModel.ts | 19 + src/api/avatar/LegModel.ts | 22 + src/api/avatar/TorsoModel.ts | 23 + src/api/avatar/index.ts | 13 + src/api/camera/CameraEditorTabs.ts | 5 + src/api/camera/CameraPicture.ts | 9 + src/api/camera/CameraPictureThumbnail.ts | 7 + src/api/camera/index.ts | 3 + src/api/campaign/CalendarItem.ts | 30 + src/api/campaign/CalendarItemState.ts | 7 + src/api/campaign/ICalendarItem.ts | 6 + src/api/campaign/index.ts | 3 + .../catalog/BuilderFurniPlaceableStatus.ts | 10 + src/api/catalog/CatalogNode.ts | 124 + src/api/catalog/CatalogPage.ts | 59 + src/api/catalog/CatalogPageName.ts | 26 + src/api/catalog/CatalogPetPalette.ts | 10 + src/api/catalog/CatalogPurchaseState.ts | 10 + src/api/catalog/CatalogType.ts | 5 + src/api/catalog/CatalogUtilities.ts | 125 + src/api/catalog/FurnitureOffer.ts | 120 + src/api/catalog/GetImageIconUrlForProduct.ts | 19 + src/api/catalog/GiftWrappingConfiguration.ts | 51 + src/api/catalog/ICatalogNode.ts | 21 + src/api/catalog/ICatalogOptions.ts | 13 + src/api/catalog/ICatalogPage.ts | 12 + src/api/catalog/IMarketplaceSearchOptions.ts | 7 + src/api/catalog/IPageLocalization.ts | 5 + src/api/catalog/IProduct.ts | 16 + src/api/catalog/IPurchasableOffer.ts | 25 + src/api/catalog/IPurchaseOptions.ts | 9 + src/api/catalog/MarketplaceOfferData.ts | 128 + src/api/catalog/MarketplaceOfferState.ts | 7 + src/api/catalog/MarketplaceSearchType.ts | 6 + src/api/catalog/Offer.ts | 245 ++ src/api/catalog/PageLocalization.ts | 36 + src/api/catalog/PlacedObjectPurchaseData.ts | 41 + src/api/catalog/Product.ts | 143 + src/api/catalog/ProductTypeEnum.ts | 11 + src/api/catalog/RequestedPage.ts | 63 + src/api/catalog/SearchResult.ts | 11 + src/api/catalog/index.ts | 29 + src/api/chat-history/ChatEntryType.ts | 6 + .../chat-history/ChatHistoryCurrentDate.ts | 6 + src/api/chat-history/IChatEntry.ts | 17 + src/api/chat-history/IRoomHistoryEntry.ts | 5 + .../MessengerHistoryCurrentDate.ts | 6 + src/api/chat-history/index.ts | 5 + src/api/events/DispatchEvent.ts | 3 + src/api/events/DispatchMainEvent.ts | 5 + src/api/events/DispatchUiEvent.ts | 5 + src/api/events/UI_EVENT_DISPATCHER.ts | 3 + src/api/events/index.ts | 4 + src/api/friends/GetGroupChatData.ts | 13 + src/api/friends/IGroupChatData.ts | 6 + src/api/friends/MessengerFriend.ts | 43 + src/api/friends/MessengerGroupType.ts | 5 + src/api/friends/MessengerIconState.ts | 6 + src/api/friends/MessengerRequest.ts | 41 + src/api/friends/MessengerSettings.ts | 11 + src/api/friends/MessengerThread.ts | 96 + src/api/friends/MessengerThreadChat.ts | 54 + src/api/friends/MessengerThreadChatGroup.ts | 41 + src/api/friends/OpenMessengerChat.ts | 7 + src/api/friends/index.ts | 11 + src/api/groups/GetGroupInformation.ts | 7 + src/api/groups/GetGroupManager.ts | 6 + src/api/groups/GetGroupMembers.ts | 7 + src/api/groups/GroupBadgePart.ts | 31 + src/api/groups/GroupMembershipType.ts | 6 + src/api/groups/GroupType.ts | 6 + src/api/groups/IGroupCustomize.ts | 8 + src/api/groups/IGroupData.ts | 13 + src/api/groups/ToggleFavoriteGroup.ts | 7 + src/api/groups/TryJoinGroup.ts | 4 + src/api/groups/index.ts | 10 + src/api/guide-tool/GuideSessionState.ts | 23 + src/api/guide-tool/GuideToolMessage.ts | 21 + src/api/guide-tool/GuideToolMessageGroup.ts | 28 + src/api/guide-tool/index.ts | 3 + src/api/hc-center/ClubStatus.ts | 6 + src/api/hc-center/GetClubBadge.ts | 11 + src/api/hc-center/index.ts | 2 + src/api/help/CallForHelpResult.ts | 5 + src/api/help/GetCloseReasonKey.ts | 8 + src/api/help/IHelpReport.ts | 19 + src/api/help/IReportedUser.ts | 5 + src/api/help/ReportState.ts | 8 + src/api/help/ReportType.ts | 11 + src/api/help/index.ts | 6 + src/api/index.ts | 29 + src/api/inventory/FurniCategory.ts | 26 + src/api/inventory/FurnitureItem.ts | 245 ++ src/api/inventory/FurnitureUtilities.ts | 172 + src/api/inventory/GroupItem.ts | 461 +++ src/api/inventory/IBotItem.ts | 6 + src/api/inventory/IFurnitureItem.ts | 17 + src/api/inventory/IPetItem.ts | 6 + src/api/inventory/IUnseenItemTracker.ts | 12 + src/api/inventory/InventoryUtilities.ts | 117 + src/api/inventory/PetUtilities.ts | 104 + src/api/inventory/TradeState.ts | 10 + src/api/inventory/TradeUserData.ts | 15 + src/api/inventory/TradingNotificationType.ts | 12 + src/api/inventory/TradingUtilities.ts | 71 + src/api/inventory/UnseenItemCategory.ts | 9 + src/api/inventory/index.ts | 15 + src/api/mod-tools/GetIssueCategoryName.ts | 35 + src/api/mod-tools/ISelectedUser.ts | 5 + src/api/mod-tools/IUserInfo.ts | 6 + src/api/mod-tools/ModActionDefinition.ts | 49 + src/api/mod-tools/index.ts | 4 + src/api/navigator/DoorStateType.ts | 12 + src/api/navigator/INavigatorData.ts | 17 + src/api/navigator/INavigatorSearchFilter.ts | 5 + src/api/navigator/IRoomChatSettings.ts | 8 + src/api/navigator/IRoomData.ts | 23 + src/api/navigator/IRoomModel.ts | 6 + src/api/navigator/IRoomModerationSettings.ts | 6 + .../NavigatorSearchResultViewDisplayMode.ts | 6 + src/api/navigator/RoomInfoData.ts | 60 + src/api/navigator/RoomSettingsUtils.ts | 10 + src/api/navigator/SearchFilterOptions.ts | 24 + src/api/navigator/TryVisitRoom.ts | 7 + src/api/navigator/index.ts | 12 + src/api/nitro/AddLinkEventTracker.ts | 7 + src/api/nitro/CreateLinkEvent.ts | 8 + src/api/nitro/GetCommunication.ts | 7 + src/api/nitro/GetConfiguration.ts | 6 + src/api/nitro/GetConnection.ts | 7 + src/api/nitro/GetLocalization.ts | 7 + src/api/nitro/GetNitroInstance.ts | 6 + src/api/nitro/OpenUrl.ts | 16 + src/api/nitro/RemoveLinkEventTracker.ts | 7 + src/api/nitro/SendMessageComposer.ts | 4 + src/api/nitro/avatar/GetAvatarPalette.ts | 7 + .../nitro/avatar/GetAvatarRenderManager.ts | 7 + src/api/nitro/avatar/GetAvatarSetType.ts | 7 + src/api/nitro/avatar/index.ts | 3 + .../camera/GetRoomCameraWidgetManager.ts | 7 + src/api/nitro/camera/index.ts | 1 + src/api/nitro/core/GetConfigurationManager.ts | 7 + src/api/nitro/core/GetNitroCore.ts | 7 + src/api/nitro/core/index.ts | 2 + src/api/nitro/index.ts | 15 + src/api/nitro/room/DispatchMouseEvent.ts | 55 + src/api/nitro/room/DispatchTouchEvent.ts | 82 + src/api/nitro/room/GetOwnRoomObject.ts | 32 + src/api/nitro/room/GetRoomEngine.ts | 7 + src/api/nitro/room/GetRoomObjectBounds.ts | 13 + .../nitro/room/GetRoomObjectScreenLocation.ts | 13 + .../InitializeRoomInstanceRenderingCanvas.ts | 9 + .../room/IsFurnitureSelectionDisabled.ts | 24 + .../nitro/room/ProcessRoomObjectOperation.ts | 6 + src/api/nitro/room/SetActiveRoomId.ts | 6 + src/api/nitro/room/index.ts | 10 + .../nitro/session/CanManipulateFurniture.ts | 11 + src/api/nitro/session/CreateRoomSession.ts | 6 + src/api/nitro/session/GetCanStandUp.ts | 13 + src/api/nitro/session/GetCanUseExpression.ts | 14 + src/api/nitro/session/GetClubMemberLevel.ts | 10 + src/api/nitro/session/GetFurnitureData.ts | 20 + .../GetFurnitureDataForProductOffer.ts | 21 + .../session/GetFurnitureDataForRoomObject.ts | 22 + src/api/nitro/session/GetOwnPosture.ts | 13 + .../session/GetProductDataForLocalization.ts | 9 + src/api/nitro/session/GetRoomSession.ts | 7 + .../nitro/session/GetRoomSessionManager.ts | 7 + .../nitro/session/GetSessionDataManager.ts | 7 + src/api/nitro/session/GoToDesktop.ts | 7 + src/api/nitro/session/HasHabboClub.ts | 7 + src/api/nitro/session/HasHabboVip.ts | 7 + .../nitro/session/IsOwnerOfFloorFurniture.ts | 16 + src/api/nitro/session/IsOwnerOfFurniture.ts | 12 + src/api/nitro/session/IsRidingHorse.ts | 14 + src/api/nitro/session/StartRoomSession.ts | 7 + src/api/nitro/session/VisitDesktop.ts | 9 + src/api/nitro/session/index.ts | 21 + src/api/notification/NotificationAlertItem.ts | 67 + src/api/notification/NotificationAlertType.ts | 10 + .../notification/NotificationBubbleItem.ts | 48 + .../notification/NotificationBubbleType.ts | 19 + .../notification/NotificationConfirmItem.ts | 67 + .../notification/NotificationConfirmType.ts | 4 + src/api/notification/index.ts | 6 + src/api/purse/IPurse.ts | 15 + src/api/purse/Purse.ts | 165 + src/api/purse/index.ts | 2 + .../room/events/RoomWidgetPollUpdateEvent.ts | 110 + ...WidgetUpdateBackgroundColorPreviewEvent.ts | 35 + .../RoomWidgetUpdateChatInputContentEvent.ts | 29 + src/api/room/events/RoomWidgetUpdateEvent.ts | 4 + .../RoomWidgetUpdateRentableBotChatEvent.ts | 62 + .../events/RoomWidgetUpdateRoomObjectEvent.ts | 43 + src/api/room/events/index.ts | 6 + src/api/room/index.ts | 2 + src/api/room/widgets/AvatarInfoFurni.ts | 38 + src/api/room/widgets/AvatarInfoName.ts | 11 + src/api/room/widgets/AvatarInfoPet.ts | 46 + src/api/room/widgets/AvatarInfoRentableBot.ts | 23 + src/api/room/widgets/AvatarInfoUser.ts | 49 + src/api/room/widgets/AvatarInfoUtilities.ts | 454 +++ src/api/room/widgets/BotSkillsEnum.ts | 18 + src/api/room/widgets/ChatBubbleMessage.ts | 56 + src/api/room/widgets/ChatMessageTypeEnum.ts | 6 + .../DimmerFurnitureWidgetPresetItem.ts | 9 + src/api/room/widgets/DoChatsOverlap.ts | 7 + .../room/widgets/FurnitureDimmerUtilities.ts | 29 + src/api/room/widgets/GetDiskColor.ts | 37 + src/api/room/widgets/IAvatarInfo.ts | 4 + src/api/room/widgets/ICraftingIngredient.ts | 6 + src/api/room/widgets/ICraftingRecipe.ts | 6 + src/api/room/widgets/IPhotoData.ts | 42 + src/api/room/widgets/MannequinUtilities.ts | 39 + src/api/room/widgets/PetSupplementEnum.ts | 5 + src/api/room/widgets/PostureTypeEnum.ts | 5 + src/api/room/widgets/RoomDimmerPreset.ts | 35 + src/api/room/widgets/RoomObjectItem.ts | 28 + src/api/room/widgets/UseProductItem.ts | 12 + src/api/room/widgets/VoteValue.ts | 8 + .../widgets/YoutubeVideoPlaybackStateEnum.ts | 9 + src/api/room/widgets/index.ts | 25 + src/api/user/GetUserProfile.ts | 7 + src/api/user/index.ts | 1 + src/api/utils/CloneObject.ts | 14 + src/api/utils/ColorUtils.ts | 65 + src/api/utils/ConvertSeconds.ts | 9 + src/api/utils/GetLocalStorage.ts | 11 + src/api/utils/LocalStorageKeys.ts | 5 + src/api/utils/LocalizeBadgeDescription.ts | 10 + src/api/utils/LocalizeBageName.ts | 10 + src/api/utils/LocalizeFormattedNumber.ts | 6 + src/api/utils/LocalizeShortNumber.ts | 36 + src/api/utils/LocalizeText.ts | 6 + src/api/utils/PlaySound.ts | 24 + src/api/utils/ProductImageUtility.ts | 59 + src/api/utils/Randomizer.ts | 28 + src/api/utils/RoomChatFormatter.ts | 102 + src/api/utils/SetLocalStorage.ts | 1 + src/api/utils/SoundNames.ts | 9 + src/api/utils/WindowSaveOptions.ts | 5 + src/api/utils/index.ts | 17 + src/api/wired/GetWiredTimeLocale.ts | 8 + src/api/wired/WiredActionLayoutCode.ts | 29 + src/api/wired/WiredConditionLayoutCode.ts | 29 + src/api/wired/WiredDateToString.ts | 1 + src/api/wired/WiredFurniType.ts | 7 + src/api/wired/WiredSelectionFilter.ts | 95 + src/api/wired/WiredSelectionVisualizer.ts | 68 + src/api/wired/WiredStringDelimeter.ts | 1 + src/api/wired/WiredTriggerLayoutCode.ts | 17 + src/api/wired/index.ts | 9 + src/assets/images/achievements/back-arrow.png | Bin 0 -> 331 bytes .../images/avatareditor/arrow-left-icon.png | Bin 0 -> 198 bytes .../images/avatareditor/arrow-right-icon.png | Bin 0 -> 192 bytes .../avatar-editor-spritesheet.png | Bin 0 -> 23260 bytes src/assets/images/avatareditor/ca-icon.png | Bin 0 -> 259 bytes .../images/avatareditor/ca-selected-icon.png | Bin 0 -> 335 bytes src/assets/images/avatareditor/cc-icon.png | Bin 0 -> 282 bytes .../images/avatareditor/cc-selected-icon.png | Bin 0 -> 338 bytes src/assets/images/avatareditor/ch-icon.png | Bin 0 -> 228 bytes .../images/avatareditor/ch-selected-icon.png | Bin 0 -> 260 bytes src/assets/images/avatareditor/clear-icon.png | Bin 0 -> 272 bytes src/assets/images/avatareditor/cp-icon.png | Bin 0 -> 252 bytes .../images/avatareditor/cp-selected-icon.png | Bin 0 -> 280 bytes src/assets/images/avatareditor/ea-icon.png | Bin 0 -> 251 bytes .../images/avatareditor/ea-selected-icon.png | Bin 0 -> 298 bytes src/assets/images/avatareditor/fa-icon.png | Bin 0 -> 234 bytes .../images/avatareditor/fa-selected-icon.png | Bin 0 -> 286 bytes .../images/avatareditor/female-icon.png | Bin 0 -> 236 bytes .../avatareditor/female-selected-icon.png | Bin 0 -> 270 bytes src/assets/images/avatareditor/ha-icon.png | Bin 0 -> 241 bytes .../images/avatareditor/ha-selected-icon.png | Bin 0 -> 285 bytes src/assets/images/avatareditor/he-icon.png | Bin 0 -> 267 bytes .../images/avatareditor/he-selected-icon.png | Bin 0 -> 338 bytes src/assets/images/avatareditor/hr-icon.png | Bin 0 -> 257 bytes .../images/avatareditor/hr-selected-icon.png | Bin 0 -> 348 bytes src/assets/images/avatareditor/lg-icon.png | Bin 0 -> 196 bytes .../images/avatareditor/lg-selected-icon.png | Bin 0 -> 236 bytes .../images/avatareditor/loading-icon.png | Bin 0 -> 181 bytes src/assets/images/avatareditor/male-icon.png | Bin 0 -> 228 bytes .../avatareditor/male-selected-icon.png | Bin 0 -> 256 bytes .../images/avatareditor/sellable-icon.png | Bin 0 -> 229 bytes src/assets/images/avatareditor/sh-icon.png | Bin 0 -> 208 bytes .../images/avatareditor/sh-selected-icon.png | Bin 0 -> 266 bytes .../images/avatareditor/spotlight-icon.png | Bin 0 -> 11373 bytes src/assets/images/avatareditor/wa-icon.png | Bin 0 -> 257 bytes .../images/avatareditor/wa-selected-icon.png | Bin 0 -> 308 bytes src/assets/images/campaign/available.png | Bin 0 -> 1118 bytes .../campaign/campaign_day_generic_bg.png | Bin 0 -> 36045 bytes .../images/campaign/campaign_opened.png | Bin 0 -> 744 bytes .../images/campaign/campaign_spritesheet.png | Bin 0 -> 83689 bytes src/assets/images/campaign/locked.png | Bin 0 -> 220 bytes src/assets/images/campaign/locked_bg.png | Bin 0 -> 5414 bytes src/assets/images/campaign/next.png | Bin 0 -> 244 bytes src/assets/images/campaign/prev.png | Bin 0 -> 235 bytes src/assets/images/campaign/unavailable.png | Bin 0 -> 448 bytes src/assets/images/campaign/unlocked_bg.png | Bin 0 -> 8798 bytes .../catalog/diamond_info_illustration.gif | Bin 0 -> 3883 bytes src/assets/images/catalog/hc_banner_big.png | Bin 0 -> 3539 bytes src/assets/images/catalog/hc_big.png | Bin 0 -> 2596 bytes src/assets/images/catalog/hc_small.png | Bin 0 -> 2208 bytes src/assets/images/catalog/paint-icon.png | Bin 0 -> 262 bytes src/assets/images/catalog/target-price.png | Bin 0 -> 3562 bytes src/assets/images/catalog/vip.png | Bin 0 -> 521 bytes .../images/chat/chatbubbles/bubble_0.png | Bin 0 -> 5025 bytes .../chatbubbles/bubble_0_1_33_34_pointer.png | Bin 0 -> 127 bytes .../chat/chatbubbles/bubble_0_transparent.png | Bin 0 -> 256 bytes .../images/chat/chatbubbles/bubble_1.png | Bin 0 -> 449 bytes .../images/chat/chatbubbles/bubble_10.png | Bin 0 -> 778 bytes .../chat/chatbubbles/bubble_10_pointer.png | Bin 0 -> 138 bytes .../images/chat/chatbubbles/bubble_11.png | Bin 0 -> 242 bytes .../chat/chatbubbles/bubble_11_pointer.png | Bin 0 -> 104 bytes .../images/chat/chatbubbles/bubble_12.png | Bin 0 -> 242 bytes .../chat/chatbubbles/bubble_12_pointer.png | Bin 0 -> 104 bytes .../images/chat/chatbubbles/bubble_13.png | Bin 0 -> 397 bytes .../chat/chatbubbles/bubble_13_pointer.png | Bin 0 -> 105 bytes .../images/chat/chatbubbles/bubble_14.png | Bin 0 -> 234 bytes .../chat/chatbubbles/bubble_14_pointer.png | Bin 0 -> 105 bytes .../images/chat/chatbubbles/bubble_15.png | Bin 0 -> 247 bytes .../chat/chatbubbles/bubble_15_pointer.png | Bin 0 -> 105 bytes .../images/chat/chatbubbles/bubble_16.png | Bin 0 -> 716 bytes .../chat/chatbubbles/bubble_16_pointer.png | Bin 0 -> 149 bytes .../images/chat/chatbubbles/bubble_17.png | Bin 0 -> 806 bytes .../chat/chatbubbles/bubble_17_pointer.png | Bin 0 -> 105 bytes .../images/chat/chatbubbles/bubble_18.png | Bin 0 -> 247 bytes .../chat/chatbubbles/bubble_18_pointer.png | Bin 0 -> 119 bytes .../images/chat/chatbubbles/bubble_19.png | Bin 0 -> 688 bytes .../chat/chatbubbles/bubble_19_20_pointer.png | Bin 0 -> 110 bytes .../images/chat/chatbubbles/bubble_2.png | Bin 0 -> 436 bytes .../images/chat/chatbubbles/bubble_20.png | Bin 0 -> 417 bytes .../images/chat/chatbubbles/bubble_21.png | Bin 0 -> 584 bytes .../chat/chatbubbles/bubble_21_pointer.png | Bin 0 -> 109 bytes .../images/chat/chatbubbles/bubble_22.png | Bin 0 -> 650 bytes .../chat/chatbubbles/bubble_22_pointer.png | Bin 0 -> 109 bytes .../images/chat/chatbubbles/bubble_23.png | Bin 0 -> 332 bytes .../chat/chatbubbles/bubble_23_37_pointer.png | Bin 0 -> 104 bytes .../images/chat/chatbubbles/bubble_24.png | Bin 0 -> 380 bytes .../chat/chatbubbles/bubble_24_pointer.png | Bin 0 -> 105 bytes .../images/chat/chatbubbles/bubble_25.png | Bin 0 -> 280 bytes .../chat/chatbubbles/bubble_25_pointer.png | Bin 0 -> 131 bytes .../images/chat/chatbubbles/bubble_26.png | Bin 0 -> 557 bytes .../chat/chatbubbles/bubble_26_pointer.png | Bin 0 -> 149 bytes .../images/chat/chatbubbles/bubble_27.png | Bin 0 -> 632 bytes .../chat/chatbubbles/bubble_27_pointer.png | Bin 0 -> 105 bytes .../images/chat/chatbubbles/bubble_28.png | Bin 0 -> 758 bytes .../chat/chatbubbles/bubble_28_pointer.png | Bin 0 -> 105 bytes .../images/chat/chatbubbles/bubble_29.png | Bin 0 -> 476 bytes .../chat/chatbubbles/bubble_29_pointer.png | Bin 0 -> 102 bytes .../chat/chatbubbles/bubble_2_31_pointer.png | Bin 0 -> 178 bytes .../images/chat/chatbubbles/bubble_3.png | Bin 0 -> 236 bytes .../images/chat/chatbubbles/bubble_30.png | Bin 0 -> 427 bytes .../chat/chatbubbles/bubble_30_pointer.png | Bin 0 -> 143 bytes .../images/chat/chatbubbles/bubble_32.png | Bin 0 -> 399 bytes .../chat/chatbubbles/bubble_32_pointer.png | Bin 0 -> 144 bytes .../images/chat/chatbubbles/bubble_33_34.png | Bin 0 -> 344 bytes .../chat/chatbubbles/bubble_33_extra.png | Bin 0 -> 542 bytes .../chat/chatbubbles/bubble_34_extra.png | Bin 0 -> 310 bytes .../images/chat/chatbubbles/bubble_35.png | Bin 0 -> 869 bytes .../chat/chatbubbles/bubble_35_pointer.png | Bin 0 -> 126 bytes .../images/chat/chatbubbles/bubble_36.png | Bin 0 -> 266 bytes .../chat/chatbubbles/bubble_36_extra.png | Bin 0 -> 239 bytes .../chat/chatbubbles/bubble_36_pointer.png | Bin 0 -> 105 bytes .../images/chat/chatbubbles/bubble_37.png | Bin 0 -> 376 bytes .../images/chat/chatbubbles/bubble_38.png | Bin 0 -> 276 bytes .../chat/chatbubbles/bubble_38_extra.png | Bin 0 -> 228 bytes .../chat/chatbubbles/bubble_38_pointer.png | Bin 0 -> 105 bytes .../chat/chatbubbles/bubble_3_pointer.png | Bin 0 -> 105 bytes .../images/chat/chatbubbles/bubble_4.png | Bin 0 -> 242 bytes .../chat/chatbubbles/bubble_4_pointer.png | Bin 0 -> 104 bytes .../images/chat/chatbubbles/bubble_5.png | Bin 0 -> 372 bytes .../chat/chatbubbles/bubble_5_pointer.png | Bin 0 -> 104 bytes .../images/chat/chatbubbles/bubble_6.png | Bin 0 -> 402 bytes .../chat/chatbubbles/bubble_6_pointer.png | Bin 0 -> 105 bytes .../images/chat/chatbubbles/bubble_7.png | Bin 0 -> 231 bytes .../chat/chatbubbles/bubble_7_pointer.png | Bin 0 -> 105 bytes .../images/chat/chatbubbles/bubble_8.png | Bin 0 -> 1206 bytes .../chat/chatbubbles/bubble_8_pointer.png | Bin 0 -> 105 bytes .../images/chat/chatbubbles/bubble_9.png | Bin 0 -> 499 bytes .../chat/chatbubbles/bubble_9_pointer.png | Bin 0 -> 146 bytes src/assets/images/chat/deleteaudio.png | Bin 0 -> 281 bytes src/assets/images/chat/equis.png | Bin 0 -> 2066 bytes src/assets/images/chat/mas.png | Bin 0 -> 669 bytes src/assets/images/chat/microphone-off.png | Bin 0 -> 2051 bytes src/assets/images/chat/microphone-on.png | Bin 0 -> 2060 bytes src/assets/images/chat/sticker.png | Bin 0 -> 265 bytes src/assets/images/chat/stickers-icon.png | Bin 0 -> 2050 bytes src/assets/images/chat/styles-icon.png | Bin 0 -> 314 bytes .../floorplaneditor/door-direction-0.png | Bin 0 -> 742 bytes .../floorplaneditor/door-direction-1.png | Bin 0 -> 738 bytes .../floorplaneditor/door-direction-2.png | Bin 0 -> 750 bytes .../floorplaneditor/door-direction-3.png | Bin 0 -> 697 bytes .../floorplaneditor/door-direction-4.png | Bin 0 -> 756 bytes .../floorplaneditor/door-direction-5.png | Bin 0 -> 754 bytes .../floorplaneditor/door-direction-6.png | Bin 0 -> 747 bytes .../floorplaneditor/door-direction-7.png | Bin 0 -> 698 bytes .../images/floorplaneditor/icon-door.png | Bin 0 -> 806 bytes .../images/floorplaneditor/icon-tile-down.png | Bin 0 -> 609 bytes .../images/floorplaneditor/icon-tile-set.png | Bin 0 -> 525 bytes .../floorplaneditor/icon-tile-unset.png | Bin 0 -> 544 bytes .../images/floorplaneditor/icon-tile-up.png | Bin 0 -> 555 bytes .../images/floorplaneditor/preview_tile.png | Bin 0 -> 146 bytes .../floorplaneditor/selected_height_icon.png | Bin 0 -> 175 bytes .../images/friends/friends-spritesheet.png | Bin 0 -> 3494 bytes src/assets/images/friends/icon-accept.png | Bin 0 -> 174 bytes src/assets/images/friends/icon-add.png | Bin 0 -> 205 bytes src/assets/images/friends/icon-bobba.png | Bin 0 -> 169 bytes src/assets/images/friends/icon-chat.png | Bin 0 -> 199 bytes src/assets/images/friends/icon-deny.png | Bin 0 -> 173 bytes src/assets/images/friends/icon-follow.png | Bin 0 -> 162 bytes .../images/friends/icon-friendbar-chat.png | Bin 0 -> 1740 bytes .../images/friends/icon-friendbar-visit.png | Bin 0 -> 2150 bytes src/assets/images/friends/icon-heart.png | Bin 0 -> 201 bytes .../images/friends/icon-new-message.png | Bin 0 -> 219 bytes src/assets/images/friends/icon-none.png | Bin 0 -> 177 bytes .../images/friends/icon-profile-sm-hover.png | Bin 0 -> 264 bytes src/assets/images/friends/icon-profile-sm.png | Bin 0 -> 257 bytes src/assets/images/friends/icon-profile.png | Bin 0 -> 1819 bytes src/assets/images/friends/icon-smile.png | Bin 0 -> 205 bytes src/assets/images/friends/icon-warning.png | Bin 0 -> 225 bytes .../friends/messenger_notification_icon.png | Bin 0 -> 164 bytes src/assets/images/gamecenter/selectedIcon.png | Bin 0 -> 248 bytes src/assets/images/gift/gift_tag.png | Bin 0 -> 795 bytes src/assets/images/gift/incognito.png | Bin 0 -> 1057 bytes src/assets/images/groups/creator_images.png | Bin 0 -> 7966 bytes src/assets/images/groups/creator_tabs.png | Bin 0 -> 1215 bytes .../groups/icons/group_decorate_icon.png | Bin 0 -> 311 bytes .../images/groups/icons/group_favorite.png | Bin 0 -> 198 bytes .../images/groups/icons/group_icon_admin.png | Bin 0 -> 203 bytes .../groups/icons/group_icon_big_admin.png | Bin 0 -> 429 bytes .../groups/icons/group_icon_big_member.png | Bin 0 -> 447 bytes .../groups/icons/group_icon_big_owner.png | Bin 0 -> 460 bytes .../groups/icons/group_icon_not_admin.png | Bin 0 -> 198 bytes .../groups/icons/group_icon_small_owner.png | Bin 0 -> 215 bytes .../images/groups/icons/group_notfavorite.png | Bin 0 -> 175 bytes .../images/groups/icons/grouptype_icon_0.png | Bin 0 -> 542 bytes .../images/groups/icons/grouptype_icon_1.png | Bin 0 -> 349 bytes .../images/groups/icons/grouptype_icon_2.png | Bin 0 -> 373 bytes src/assets/images/groups/no-group-1.png | Bin 0 -> 5007 bytes src/assets/images/groups/no-group-2.png | Bin 0 -> 4401 bytes src/assets/images/groups/no-group-3.png | Bin 0 -> 4566 bytes .../images/groups/no-group-spritesheet.png | Bin 0 -> 29386 bytes .../guide-tool/guide_tool_duty_switch.png | Bin 0 -> 537 bytes .../guide-tool/guide_tool_info_icon.png | Bin 0 -> 184 bytes src/assets/images/hc-center/benefits.png | Bin 0 -> 14475 bytes src/assets/images/hc-center/clock.png | Bin 0 -> 267 bytes src/assets/images/hc-center/hc_logo.gif | Bin 0 -> 1251 bytes src/assets/images/hc-center/payday.png | Bin 0 -> 721 bytes src/assets/images/help/help_index.png | Bin 0 -> 2569 bytes src/assets/images/icons/arrows.png | Bin 0 -> 222 bytes src/assets/images/icons/builderstool.png | Bin 0 -> 479 bytes .../images/icons/camera-colormatrix.png | Bin 0 -> 249 bytes src/assets/images/icons/camera-composite.png | Bin 0 -> 204 bytes src/assets/images/icons/camera-small.png | Bin 0 -> 296 bytes src/assets/images/icons/chat-history.png | Bin 0 -> 574 bytes src/assets/images/icons/close.png | Bin 0 -> 1163 bytes src/assets/images/icons/cog.png | Bin 0 -> 448 bytes src/assets/images/icons/disablebubble.png | Bin 0 -> 737 bytes src/assets/images/icons/enablebubble.png | Bin 0 -> 734 bytes src/assets/images/icons/help.png | Bin 0 -> 233 bytes src/assets/images/icons/house-small.png | Bin 0 -> 361 bytes src/assets/images/icons/icon_cog.png | Bin 0 -> 218 bytes src/assets/images/icons/like-room.png | Bin 0 -> 481 bytes src/assets/images/icons/loading-icon.png | Bin 0 -> 222 bytes .../icons/room-history-back-disabled.png | Bin 0 -> 267 bytes .../icons/room-history-back-enabled.png | Bin 0 -> 280 bytes .../images/icons/room-history-disabled.png | Bin 0 -> 393 bytes .../images/icons/room-history-enabled.png | Bin 0 -> 402 bytes .../icons/room-history-next-disabled.png | Bin 0 -> 260 bytes .../icons/room-history-next-enabled.png | Bin 0 -> 267 bytes src/assets/images/icons/room-link.png | Bin 0 -> 322 bytes src/assets/images/icons/sign-exclamation.png | Bin 0 -> 236 bytes src/assets/images/icons/sign-heart.png | Bin 0 -> 256 bytes src/assets/images/icons/sign-red.png | Bin 0 -> 324 bytes src/assets/images/icons/sign-skull.png | Bin 0 -> 199 bytes src/assets/images/icons/sign-smile.png | Bin 0 -> 288 bytes src/assets/images/icons/sign-soccer.png | Bin 0 -> 641 bytes src/assets/images/icons/sign-yellow.png | Bin 0 -> 304 bytes src/assets/images/icons/small-room.png | Bin 0 -> 413 bytes src/assets/images/icons/tickets.png | Bin 0 -> 143 bytes src/assets/images/icons/user.png | Bin 0 -> 881 bytes src/assets/images/icons/zoom-less.png | Bin 0 -> 252 bytes src/assets/images/icons/zoom-more.png | Bin 0 -> 475 bytes .../images/infostand/bot_background.png | Bin 0 -> 811 bytes .../images/infostand/countown-timer.png | Bin 0 -> 219 bytes src/assets/images/infostand/disk-creator.png | Bin 0 -> 189 bytes src/assets/images/infostand/disk-icon.png | Bin 0 -> 240 bytes src/assets/images/infostand/pencil-icon.png | Bin 0 -> 288 bytes src/assets/images/infostand/rarity-level.png | Bin 0 -> 273 bytes src/assets/images/inventory/empty.png | Bin 0 -> 6377 bytes src/assets/images/inventory/rarity-level.png | Bin 0 -> 131 bytes .../images/inventory/trading/locked-icon.png | Bin 0 -> 309 bytes .../inventory/trading/unlocked-icon.png | Bin 0 -> 332 bytes src/assets/images/loading/caja.gif | Bin 0 -> 24426 bytes .../loading/connecting-duck-spritesheet.png | Bin 0 -> 34720 bytes .../images/loading/connecting_duck_01.png | Bin 0 -> 3985 bytes .../images/loading/connecting_duck_02.png | Bin 0 -> 4923 bytes .../images/loading/connecting_duck_03.png | Bin 0 -> 5842 bytes .../images/loading/connecting_duck_04.png | Bin 0 -> 3270 bytes .../images/loading/connecting_duck_05.png | Bin 0 -> 4558 bytes .../images/loading/connecting_duck_06.png | Bin 0 -> 5290 bytes .../images/loading/connecting_duck_07.png | Bin 0 -> 2731 bytes src/assets/images/loading/progress_habbos.gif | Bin 0 -> 11805 bytes src/assets/images/modtool/chatlog.gif | Bin 0 -> 109 bytes src/assets/images/modtool/key.gif | Bin 0 -> 214 bytes src/assets/images/modtool/m_icon.png | Bin 0 -> 299 bytes src/assets/images/modtool/reports.png | Bin 0 -> 3551 bytes src/assets/images/modtool/room.gif | Bin 0 -> 169 bytes src/assets/images/modtool/room.png | Bin 0 -> 3102 bytes src/assets/images/modtool/user.gif | Bin 0 -> 1130 bytes src/assets/images/modtool/wrench.gif | Bin 0 -> 117 bytes .../chain_mysterybox_box_overlay.png | Bin 0 -> 369 bytes src/assets/images/mysterybox/key_overlay.png | Bin 0 -> 260 bytes src/assets/images/mysterybox/mystery_box.png | Bin 0 -> 514 bytes .../images/mysterybox/mystery_box_key.png | Bin 0 -> 487 bytes .../mysterytrophy/frank_mystery_trophy.png | Bin 0 -> 1653 bytes src/assets/images/navigator/icons/info.png | Bin 0 -> 256 bytes .../images/navigator/icons/room_group.png | Bin 0 -> 191 bytes .../images/navigator/icons/room_invisible.png | Bin 0 -> 173 bytes .../images/navigator/icons/room_locked.png | Bin 0 -> 221 bytes .../images/navigator/icons/room_password.png | Bin 0 -> 188 bytes .../images/navigator/models/model_0.png | Bin 0 -> 4027 bytes .../images/navigator/models/model_1.png | Bin 0 -> 956 bytes .../images/navigator/models/model_2.png | Bin 0 -> 920 bytes .../images/navigator/models/model_3.png | Bin 0 -> 581 bytes .../images/navigator/models/model_4.png | Bin 0 -> 709 bytes .../images/navigator/models/model_5.png | Bin 0 -> 1730 bytes .../images/navigator/models/model_6.png | Bin 0 -> 2147 bytes .../images/navigator/models/model_7.png | Bin 0 -> 2368 bytes .../images/navigator/models/model_8.png | Bin 0 -> 2123 bytes .../images/navigator/models/model_9.png | Bin 0 -> 1739 bytes .../images/navigator/models/model_a.png | Bin 0 -> 3279 bytes .../images/navigator/models/model_b.png | Bin 0 -> 3254 bytes .../images/navigator/models/model_c.png | Bin 0 -> 3102 bytes .../images/navigator/models/model_d.png | Bin 0 -> 3293 bytes .../images/navigator/models/model_e.png | Bin 0 -> 3267 bytes .../images/navigator/models/model_f.png | Bin 0 -> 3294 bytes .../images/navigator/models/model_g.png | Bin 0 -> 3395 bytes .../images/navigator/models/model_h.png | Bin 0 -> 3354 bytes .../images/navigator/models/model_i.png | Bin 0 -> 3690 bytes .../images/navigator/models/model_j.png | Bin 0 -> 3603 bytes .../images/navigator/models/model_k.png | Bin 0 -> 3785 bytes .../images/navigator/models/model_l.png | Bin 0 -> 3717 bytes .../images/navigator/models/model_m.png | Bin 0 -> 3559 bytes .../images/navigator/models/model_n.png | Bin 0 -> 3778 bytes .../images/navigator/models/model_o.png | Bin 0 -> 3598 bytes .../images/navigator/models/model_p.png | Bin 0 -> 3751 bytes .../images/navigator/models/model_q.png | Bin 0 -> 3874 bytes .../images/navigator/models/model_r.png | Bin 0 -> 4231 bytes .../navigator/models/model_snowwar1.png | Bin 0 -> 19090 bytes .../navigator/models/model_snowwar2.png | Bin 0 -> 19090 bytes .../images/navigator/models/model_t.png | Bin 0 -> 4329 bytes .../images/navigator/models/model_u.png | Bin 0 -> 4126 bytes .../images/navigator/models/model_v.png | Bin 0 -> 4416 bytes .../images/navigator/models/model_w.png | Bin 0 -> 4331 bytes .../images/navigator/models/model_x.png | Bin 0 -> 4103 bytes .../images/navigator/models/model_y.png | Bin 0 -> 4245 bytes .../images/navigator/models/model_z.png | Bin 0 -> 4087 bytes .../navigator/thumbnail_placeholder.png | Bin 0 -> 1801 bytes src/assets/images/nitro/nitro-dark.svg | 43 + src/assets/images/nitro/nitro-light.svg | 43 + src/assets/images/nitro/nitro-n-dark.svg | 28 + src/assets/images/nitro/nitro-n-light.svg | 29 + src/assets/images/notifications/frank.gif | Bin 0 -> 1204 bytes src/assets/images/pets/pet-package/gnome.png | Bin 0 -> 1600 bytes .../pets/pet-package/leprechaun_box.png | Bin 0 -> 1520 bytes .../images/pets/pet-package/petbox_epic.png | Bin 0 -> 2180 bytes .../images/pets/pet-package/pterosaur_egg.png | Bin 0 -> 1631 bytes .../images/pets/pet-package/val11_present.png | Bin 0 -> 2720 bytes .../pets/pet-package/velociraptor_egg.png | Bin 0 -> 1511 bytes src/assets/images/prize/prize_background.png | Bin 0 -> 5861 bytes src/assets/images/profile/icons/offline.png | Bin 0 -> 306 bytes src/assets/images/profile/icons/online.gif | Bin 0 -> 666 bytes src/assets/images/profile/icons/tick.png | Bin 0 -> 129 bytes .../room_spectator_bottom_left.png | Bin 0 -> 461 bytes .../room_spectator_bottom_right.png | Bin 0 -> 456 bytes .../room_spectator_middle_bottom.png | Bin 0 -> 98 bytes .../room_spectator_middle_left.png | Bin 0 -> 94 bytes .../room_spectator_middle_right.png | Bin 0 -> 94 bytes .../room_spectator_middle_top.png | Bin 0 -> 95 bytes .../room_spectator_top_left.png | Bin 0 -> 408 bytes .../room_spectator_top_right.png | Bin 0 -> 412 bytes .../avatar-info/preview-background.png | Bin 0 -> 7756 bytes .../images/room-widgets/camera-widget/btn.png | Bin 0 -> 830 bytes .../room-widgets/camera-widget/btn_down.png | Bin 0 -> 1000 bytes .../room-widgets/camera-widget/btn_hi.png | Bin 0 -> 935 bytes .../room-widgets/camera-widget/cam_bg.png | Bin 0 -> 2166 bytes .../camera-widget/camera-spritesheet.png | Bin 0 -> 15640 bytes .../room-widgets/camera-widget/viewfinder.png | Bin 0 -> 959 bytes .../dimmer-widget/dimmer_banner.png | Bin 0 -> 1041 bytes .../engraving-lock-spritesheet.png | Bin 0 -> 41062 bytes .../exchange-credit/exchange-credit-image.png | Bin 0 -> 9507 bytes .../monsterplant-preview.png | Bin 0 -> 2048 bytes .../mannequin-spritesheet.png | Bin 0 -> 5719 bytes .../room-widgets/playlist-editor/disk_2.png | Bin 0 -> 680 bytes .../playlist-editor/disk_image.png | Bin 0 -> 1441 bytes .../room-widgets/playlist-editor/move.png | Bin 0 -> 164 bytes .../playlist-editor/pause-btn.png | Bin 0 -> 111 bytes .../room-widgets/playlist-editor/pause.png | Bin 0 -> 114 bytes .../room-widgets/playlist-editor/playing.png | Bin 0 -> 309 bytes .../room-widgets/playlist-editor/preview.png | Bin 0 -> 147 bytes .../stickie-widget/stickie-blue.png | Bin 0 -> 401 bytes .../stickie-widget/stickie-christmas.png | Bin 0 -> 1272 bytes .../stickie-widget/stickie-close.png | Bin 0 -> 189 bytes .../stickie-widget/stickie-dreams.png | Bin 0 -> 26556 bytes .../stickie-widget/stickie-green.png | Bin 0 -> 401 bytes .../stickie-widget/stickie-heart.png | Bin 0 -> 945 bytes .../stickie-widget/stickie-juninas.png | Bin 0 -> 28162 bytes .../stickie-widget/stickie-pink.png | Bin 0 -> 758 bytes .../stickie-widget/stickie-shakesp.png | Bin 0 -> 5036 bytes .../stickie-widget/stickie-spritesheet.png | Bin 0 -> 5828 bytes .../stickie-widget/stickie-trash.png | Bin 0 -> 170 bytes .../stickie-widget/stickie-yellow.png | Bin 0 -> 401 bytes .../thumbnail-camera-spritesheet.png | Bin 0 -> 883 bytes .../trophy-widget/trophy-spritesheet.png | Bin 0 -> 6595 bytes .../wordquiz-widget/thumbs-down-small.png | Bin 0 -> 145 bytes .../wordquiz-widget/thumbs-down.png | Bin 0 -> 182 bytes .../wordquiz-widget/thumbs-up-small.png | Bin 0 -> 135 bytes .../wordquiz-widget/thumbs-up.png | Bin 0 -> 174 bytes .../room-widgets/youtube-widget/next.png | Bin 0 -> 164 bytes .../room-widgets/youtube-widget/prev.png | Bin 0 -> 249 bytes .../images/stackhelper/slider-background.png | Bin 0 -> 695 bytes .../images/stackhelper/slider-pointer.png | Bin 0 -> 373 bytes src/assets/images/toolbar/arrow.png | Bin 0 -> 14533 bytes src/assets/images/toolbar/friend-search.png | Bin 0 -> 2213 bytes .../images/toolbar/icons/buildersclub.png | Bin 0 -> 576 bytes src/assets/images/toolbar/icons/camera.png | Bin 0 -> 1732 bytes src/assets/images/toolbar/icons/catalog.png | Bin 0 -> 760 bytes .../images/toolbar/icons/friend_all.png | Bin 0 -> 629 bytes .../images/toolbar/icons/friend_head.png | Bin 0 -> 680 bytes .../images/toolbar/icons/friend_search.png | Bin 0 -> 15241 bytes src/assets/images/toolbar/icons/game.png | Bin 0 -> 740 bytes src/assets/images/toolbar/icons/habbo.png | Bin 0 -> 1304 bytes src/assets/images/toolbar/icons/house.png | Bin 0 -> 399 bytes src/assets/images/toolbar/icons/inventory.png | Bin 0 -> 1494 bytes src/assets/images/toolbar/icons/joinroom.png | Bin 0 -> 1084 bytes .../toolbar/icons/me-menu/achievements.png | Bin 0 -> 2306 bytes .../images/toolbar/icons/me-menu/clothing.png | Bin 0 -> 2255 bytes .../images/toolbar/icons/me-menu/cog.png | Bin 0 -> 657 bytes .../images/toolbar/icons/me-menu/forums.png | Bin 0 -> 416 bytes .../toolbar/icons/me-menu/helper-tool.png | Bin 0 -> 309 bytes .../images/toolbar/icons/me-menu/my-rooms.png | Bin 0 -> 2299 bytes .../images/toolbar/icons/me-menu/profile.png | Bin 0 -> 468 bytes .../images/toolbar/icons/me-menu/rooms.png | Bin 0 -> 1465 bytes .../images/toolbar/icons/me-menu/talents.png | Bin 0 -> 397 bytes src/assets/images/toolbar/icons/message.png | Bin 0 -> 1099 bytes .../images/toolbar/icons/message_unsee.gif | Bin 0 -> 1511 bytes src/assets/images/toolbar/icons/modtools.png | Bin 0 -> 404 bytes src/assets/images/toolbar/icons/rooms.png | Bin 0 -> 508 bytes .../images/toolbar/icons/sendmessage.png | Bin 0 -> 15647 bytes .../images/unique/catalog-info-amount-bg.png | Bin 0 -> 757 bytes .../images/unique/catalog-info-sold-out.png | Bin 0 -> 1100 bytes src/assets/images/unique/grid-bg-glass.png | Bin 0 -> 213 bytes src/assets/images/unique/grid-bg-sold-out.png | Bin 0 -> 332 bytes src/assets/images/unique/grid-bg.png | Bin 0 -> 201 bytes src/assets/images/unique/grid-count-bg.png | Bin 0 -> 155 bytes .../unique/inventory-info-amount-bg.png | Bin 0 -> 521 bytes src/assets/images/unique/numbers.png | Bin 0 -> 146 bytes .../images/wired/card-action-corners.png | Bin 0 -> 1827 bytes src/assets/images/wired/icon_action.png | Bin 0 -> 171 bytes src/assets/images/wired/icon_condition.png | Bin 0 -> 173 bytes src/assets/images/wired/icon_trigger.png | Bin 0 -> 169 bytes src/assets/images/wired/icon_wired_around.png | Bin 0 -> 197 bytes .../images/wired/icon_wired_left_right.png | Bin 0 -> 172 bytes .../images/wired/icon_wired_north_east.png | Bin 0 -> 157 bytes .../images/wired/icon_wired_north_west.png | Bin 0 -> 166 bytes .../wired/icon_wired_rotate_clockwise.png | Bin 0 -> 146 bytes .../icon_wired_rotate_counter_clockwise.png | Bin 0 -> 148 bytes .../images/wired/icon_wired_south_east.png | Bin 0 -> 171 bytes .../images/wired/icon_wired_south_west.png | Bin 0 -> 169 bytes .../images/wired/icon_wired_up_down.png | Bin 0 -> 153 bytes src/assets/styles/bootstrap/_accordion.scss | 118 + src/assets/styles/bootstrap/_alert.scss | 57 + src/assets/styles/bootstrap/_badge.scss | 29 + src/assets/styles/bootstrap/_breadcrumb.scss | 28 + .../styles/bootstrap/_button-group.scss | 139 + src/assets/styles/bootstrap/_buttons.scss | 116 + src/assets/styles/bootstrap/_card.scss | 216 ++ src/assets/styles/bootstrap/_carousel.scss | 229 ++ src/assets/styles/bootstrap/_close.scss | 40 + src/assets/styles/bootstrap/_containers.scss | 41 + src/assets/styles/bootstrap/_dropdown.scss | 240 ++ src/assets/styles/bootstrap/_forms.scss | 9 + src/assets/styles/bootstrap/_functions.scss | 296 ++ src/assets/styles/bootstrap/_grid.scss | 33 + src/assets/styles/bootstrap/_helpers.scss | 9 + src/assets/styles/bootstrap/_images.scss | 42 + src/assets/styles/bootstrap/_list-group.scss | 174 ++ src/assets/styles/bootstrap/_mixins.scss | 43 + src/assets/styles/bootstrap/_modal.scss | 209 ++ src/assets/styles/bootstrap/_nav.scss | 160 + src/assets/styles/bootstrap/_navbar.scss | 335 ++ src/assets/styles/bootstrap/_offcanvas.scss | 83 + src/assets/styles/bootstrap/_pagination.scss | 64 + .../styles/bootstrap/_placeholders.scss | 51 + src/assets/styles/bootstrap/_popover.scss | 158 + src/assets/styles/bootstrap/_progress.scss | 48 + src/assets/styles/bootstrap/_reboot.scss | 609 ++++ src/assets/styles/bootstrap/_root.scss | 53 + src/assets/styles/bootstrap/_spinners.scss | 69 + src/assets/styles/bootstrap/_tables.scss | 151 + src/assets/styles/bootstrap/_toasts.scss | 51 + src/assets/styles/bootstrap/_tooltip.scss | 115 + src/assets/styles/bootstrap/_transitions.scss | 27 + src/assets/styles/bootstrap/_type.scss | 104 + src/assets/styles/bootstrap/_utilities.scss | 638 ++++ src/assets/styles/bootstrap/_variables.scss | 1683 ++++++++++ .../styles/bootstrap/bootstrap-grid.scss | 65 + .../styles/bootstrap/bootstrap-reboot.scss | 15 + .../styles/bootstrap/bootstrap-utilities.scss | 18 + src/assets/styles/bootstrap/bootstrap.scss | 53 + .../bootstrap/forms/_floating-labels.scss | 63 + .../styles/bootstrap/forms/_form-check.scss | 152 + .../styles/bootstrap/forms/_form-control.scss | 219 ++ .../styles/bootstrap/forms/_form-range.scss | 91 + .../styles/bootstrap/forms/_form-select.scss | 70 + .../styles/bootstrap/forms/_form-text.scss | 11 + .../styles/bootstrap/forms/_input-group.scss | 121 + .../styles/bootstrap/forms/_labels.scss | 36 + .../styles/bootstrap/forms/_validation.scss | 12 + .../styles/bootstrap/helpers/_clearfix.scss | 3 + .../bootstrap/helpers/_colored-links.scss | 12 + .../styles/bootstrap/helpers/_position.scss | 30 + .../styles/bootstrap/helpers/_ratio.scss | 26 + .../styles/bootstrap/helpers/_stacks.scss | 15 + .../bootstrap/helpers/_stretched-link.scss | 15 + .../bootstrap/helpers/_text-truncation.scss | 7 + .../bootstrap/helpers/_visually-hidden.scss | 8 + src/assets/styles/bootstrap/helpers/_vr.scss | 8 + .../styles/bootstrap/mixins/_alert.scss | 11 + .../styles/bootstrap/mixins/_backdrop.scss | 14 + .../bootstrap/mixins/_border-radius.scss | 78 + .../styles/bootstrap/mixins/_box-shadow.scss | 18 + .../styles/bootstrap/mixins/_breakpoints.scss | 127 + .../styles/bootstrap/mixins/_buttons.scss | 133 + .../styles/bootstrap/mixins/_caret.scss | 64 + .../styles/bootstrap/mixins/_clearfix.scss | 9 + .../bootstrap/mixins/_color-scheme.scss | 7 + .../styles/bootstrap/mixins/_container.scss | 9 + .../styles/bootstrap/mixins/_deprecate.scss | 10 + .../styles/bootstrap/mixins/_forms.scss | 144 + .../styles/bootstrap/mixins/_gradients.scss | 47 + src/assets/styles/bootstrap/mixins/_grid.scss | 150 + .../styles/bootstrap/mixins/_image.scss | 16 + .../styles/bootstrap/mixins/_list-group.scss | 24 + .../styles/bootstrap/mixins/_lists.scss | 7 + .../styles/bootstrap/mixins/_pagination.scss | 31 + .../styles/bootstrap/mixins/_reset-text.scss | 17 + .../styles/bootstrap/mixins/_resize.scss | 6 + .../bootstrap/mixins/_table-variants.scss | 21 + .../bootstrap/mixins/_text-truncate.scss | 8 + .../styles/bootstrap/mixins/_transition.scss | 26 + .../styles/bootstrap/mixins/_utilities.scss | 89 + .../bootstrap/mixins/_visually-hidden.scss | 29 + .../styles/bootstrap/utilities/_api.scss | 47 + src/assets/styles/bootstrap/vendor/_rfs.scss | 354 +++ src/assets/styles/fonts.scss | 4 + src/assets/styles/icons.scss | 748 +++++ src/assets/styles/index.scss | 118 + src/assets/styles/scrollbars.scss | 53 + src/assets/styles/slider.scss | 54 + src/assets/styles/utils.scss | 134 + src/assets/webfonts/Ubuntu-C.ttf | Bin 0 -> 369840 bytes src/assets/webfonts/Ubuntu-b.ttf | Bin 0 -> 152496 bytes src/assets/webfonts/Ubuntu-i.ttf | Bin 0 -> 160792 bytes src/assets/webfonts/Ubuntu-ib.ttf | Bin 0 -> 148524 bytes src/assets/webfonts/Ubuntu-m.ttf | Bin 0 -> 140976 bytes src/assets/webfonts/Ubuntu.ttf | Bin 0 -> 155908 bytes src/common/AutoGrid.tsx | 28 + src/common/Base.tsx | 84 + src/common/Button.tsx | 35 + src/common/ButtonGroup.tsx | 22 + src/common/Column.tsx | 46 + src/common/Flex.tsx | 50 + src/common/FormGroup.tsx | 22 + src/common/Grid.tsx | 63 + src/common/GridContext.tsx | 17 + src/common/HorizontalRule.tsx | 38 + src/common/InfiniteScroll.tsx | 63 + src/common/Text.tsx | 63 + src/common/card/NitroCardContentView.tsx | 18 + src/common/card/NitroCardContext.tsx | 17 + src/common/card/NitroCardHeaderView.tsx | 48 + src/common/card/NitroCardSubHeaderView.tsx | 23 + src/common/card/NitroCardView.scss | 249 ++ src/common/card/NitroCardView.tsx | 64 + .../accordion/NitroCardAccordionContext.tsx | 21 + .../accordion/NitroCardAccordionItemView.tsx | 18 + .../accordion/NitroCardAccordionSetView.tsx | 84 + .../card/accordion/NitroCardAccordionView.tsx | 25 + src/common/card/accordion/index.ts | 4 + src/common/card/index.ts | 7 + .../card/tabs/NitroCardTabsItemView.tsx | 35 + src/common/card/tabs/NitroCardTabsView.tsx | 22 + src/common/card/tabs/index.ts | 2 + src/common/classNames.ts | 1 + .../draggable-window/DraggableWindow.tsx | 270 ++ .../DraggableWindowPosition.ts | 7 + src/common/draggable-window/index.ts | 2 + src/common/index.scss | 558 ++++ src/common/index.ts | 22 + src/common/layout/LayoutAvatarImageView.tsx | 89 + src/common/layout/LayoutBackgroundImage.tsx | 23 + src/common/layout/LayoutBadgeImageView.tsx | 103 + src/common/layout/LayoutCounterTimeView.tsx | 43 + src/common/layout/LayoutCurrencyIcon.tsx | 44 + .../layout/LayoutFurniIconImageView.tsx | 17 + src/common/layout/LayoutFurniImageView.tsx | 82 + src/common/layout/LayoutGiftTagView.tsx | 39 + src/common/layout/LayoutGridItem.tsx | 75 + src/common/layout/LayoutImage.tsx | 13 + src/common/layout/LayoutItemCountView.tsx | 28 + .../layout/LayoutLoadingSpinnerView.tsx | 15 + src/common/layout/LayoutMiniCameraView.tsx | 44 + .../layout/LayoutNotificationAlertView.tsx | 35 + .../layout/LayoutNotificationBubbleView.tsx | 52 + src/common/layout/LayoutPetImageView.tsx | 121 + .../layout/LayoutPrizeProductImageView.tsx | 30 + src/common/layout/LayoutProgressBar.tsx | 32 + src/common/layout/LayoutRarityLevelView.tsx | 28 + src/common/layout/LayoutRoomPreviewerView.tsx | 97 + src/common/layout/LayoutRoomThumbnailView.tsx | 37 + src/common/layout/LayoutTrophyView.tsx | 39 + src/common/layout/UserProfileIconView.tsx | 29 + src/common/layout/index.ts | 23 + .../LayoutLimitedEditionCompactPlateView.tsx | 35 + .../LayoutLimitedEditionCompletePlateView.tsx | 41 + .../LayoutLimitedEditionStyledNumberView.tsx | 18 + src/common/layout/limited-edition/index.ts | 3 + .../transitions/TransitionAnimation.tsx | 52 + .../transitions/TransitionAnimationStyles.ts | 136 + .../transitions/TransitionAnimationTypes.ts | 11 + src/common/transitions/index.ts | 3 + src/common/types/AlignItemType.ts | 1 + src/common/types/AlignSelfType.ts | 1 + src/common/types/ButtonSizeType.ts | 1 + src/common/types/ColorVariantType.ts | 1 + src/common/types/ColumnSizesType.ts | 1 + src/common/types/DisplayType.ts | 1 + src/common/types/FloatType.ts | 1 + src/common/types/FontSizeType.ts | 1 + src/common/types/FontWeightType.ts | 1 + src/common/types/JustifyContentType.ts | 1 + src/common/types/OverflowType.ts | 1 + src/common/types/PositionType.ts | 1 + src/common/types/SpacingType.ts | 1 + src/common/types/TextAlignType.ts | 1 + src/common/types/index.ts | 14 + src/common/utils/CreateTransitionToIcon.ts | 14 + src/common/utils/FriendlyTimeView.tsx | 28 + src/common/utils/index.ts | 2 + .../achievements/AchievementsView.scss | 15 + .../achievements/AchievementsView.tsx | 72 + .../views/AchievementBadgeView.tsx | 19 + .../views/AchievementCategoryView.tsx | 37 + .../views/AchievementDetailsView.tsx | 53 + .../AchievementListItemView.tsx | 24 + .../achievement-list/AchievementListView.tsx | 20 + .../views/achievement-list/index.ts | 2 + .../AchievementsCategoryListItemView.tsx | 31 + .../AchievementsCategoryListView.tsx | 22 + .../achievements/views/category-list/index.ts | 2 + src/components/achievements/views/index.ts | 5 + .../avatar-editor/AvatarEditorView.scss | 336 ++ .../avatar-editor/AvatarEditorView.tsx | 316 ++ .../views/AvatarEditorFigurePreviewView.tsx | 55 + .../avatar-editor/views/AvatarEditorIcon.tsx | 30 + .../views/AvatarEditorModelView.tsx | 88 + .../views/AvatarEditorWardrobeView.tsx | 79 + .../AvatarEditorFigureSetItemView.tsx | 35 + .../figure-set/AvatarEditorFigureSetView.tsx | 44 + .../AvatarEditorPaletteSetItemView.tsx | 32 + .../AvatarEditorPaletteSetView.tsx | 41 + src/components/camera/CameraWidgetView.scss | 133 + src/components/camera/CameraWidgetView.tsx | 97 + .../camera/views/CameraWidgetCaptureView.tsx | 90 + .../camera/views/CameraWidgetCheckoutView.tsx | 159 + .../views/CameraWidgetShowPhotoView.tsx | 71 + .../views/editor/CameraWidgetEditorView.tsx | 229 ++ .../CameraWidgetEffectListItemView.tsx | 40 + .../CameraWidgetEffectListView.tsx | 31 + src/components/campaign/CalendarItemView.tsx | 52 + src/components/campaign/CalendarView.tsx | 143 + src/components/campaign/CampaignView.scss | 71 + src/components/campaign/CampaignView.tsx | 101 + src/components/catalog/CatalogView.scss | 161 + src/components/catalog/CatalogView.tsx | 111 + .../views/CatalogPurchaseConfirmView.tsx | 10 + .../catalog-header/CatalogHeaderView.tsx | 26 + .../views/catalog-icon/CatalogIconView.tsx | 20 + .../CatalogRoomPreviewerView.tsx | 42 + .../catalog/views/gift/CatalogGiftView.tsx | 289 ++ .../navigation/CatalogNavigationItemView.tsx | 35 + .../navigation/CatalogNavigationSetView.tsx | 25 + .../navigation/CatalogNavigationView.tsx | 34 + .../page/common/CatalogGridOfferView.tsx | 59 + .../page/common/CatalogRedeemVoucherView.tsx | 60 + .../views/page/common/CatalogSearchView.tsx | 96 + .../views/page/layout/CatalogLayout.types.ts | 7 + .../layout/CatalogLayoutBadgeDisplayView.tsx | 54 + .../layout/CatalogLayoutColorGroupingView.tsx | 176 ++ .../page/layout/CatalogLayoutDefaultView.tsx | 61 + .../CatalogLayoutGuildCustomFurniView.tsx | 48 + .../layout/CatalogLayoutGuildForumView.tsx | 49 + .../CatalogLayoutGuildFrontpageView.tsx | 29 + .../layout/CatalogLayoutInfoLoyaltyView.tsx | 15 + .../page/layout/CatalogLayoutPets2View.tsx | 8 + .../page/layout/CatalogLayoutPets3View.tsx | 27 + .../page/layout/CatalogLayoutRoomAdsView.tsx | 113 + .../layout/CatalogLayoutRoomBundleView.tsx | 43 + .../layout/CatalogLayoutSingleBundleView.tsx | 43 + .../layout/CatalogLayoutSoundMachineView.tsx | 113 + .../page/layout/CatalogLayoutSpacesView.tsx | 48 + .../page/layout/CatalogLayoutTrophiesView.tsx | 56 + .../page/layout/CatalogLayoutVipBuyView.tsx | 191 ++ .../views/page/layout/GetCatalogLayout.tsx | 80 + .../CatalogLayoutFrontPageItemView.tsx | 37 + .../CatalogLayoutFrontpage4View.tsx | 50 + .../CatalogLayoutMarketplaceItemView.tsx | 83 + .../CatalogLayoutMarketplaceOwnItemsView.tsx | 102 + ...atalogLayoutMarketplacePublicItemsView.tsx | 161 + ...CatalogLayoutMarketplaceSearchFormView.tsx | 71 + .../marketplace/MarketplacePostOfferView.tsx | 121 + .../page/layout/pets/CatalogLayoutPetView.tsx | 243 ++ .../vip-gifts/CatalogLayoutVipGiftsView.tsx | 62 + .../page/layout/vip-gifts/VipGiftItemView.tsx | 63 + .../widgets/CatalogAddOnBadgeWidgetView.tsx | 18 + .../CatalogBadgeSelectorWidgetView.tsx | 76 + .../widgets/CatalogBundleGridWidgetView.tsx | 29 + .../CatalogFirstProductSelectorWidgetView.tsx | 16 + .../widgets/CatalogGuildBadgeWidgetView.tsx | 31 + .../CatalogGuildSelectorWidgetView.tsx | 75 + .../widgets/CatalogItemGridWidgetView.tsx | 52 + .../widgets/CatalogLimitedItemWidgetView.tsx | 19 + .../widgets/CatalogPriceDisplayWidgetView.tsx | 37 + .../widgets/CatalogPurchaseWidgetView.tsx | 164 + .../widgets/CatalogSimplePriceWidgetView.tsx | 21 + .../widgets/CatalogSingleViewWidgetView.tsx | 7 + .../page/widgets/CatalogSpacesWidgetView.tsx | 115 + .../page/widgets/CatalogSpinnerWidgetView.tsx | 46 + .../page/widgets/CatalogTotalPriceWidget.tsx | 20 + .../widgets/CatalogViewProductWidgetView.tsx | 99 + .../catalog/views/targeted-offer/Offer.scss | 27 + .../views/targeted-offer/OfferBubbleView.tsx | 16 + .../views/targeted-offer/OfferView.tsx | 32 + .../views/targeted-offer/OfferWindowView.tsx | 82 + .../chat-history/ChatHistoryView.scss | 4 + .../chat-history/ChatHistoryView.tsx | 96 + .../FloorplanEditorContext.tsx | 22 + .../floorplan-editor/FloorplanEditorView.scss | 9 + .../floorplan-editor/FloorplanEditorView.tsx | 165 + .../floorplan-editor/common/ActionSettings.ts | 39 + .../floorplan-editor/common/Constants.ts | 44 + .../common/ConvertMapToString.ts | 1 + .../common/FloorplanEditor.ts | 420 +++ .../common/IFloorplanSettings.ts | 8 + .../common/IVisualizationSettings.ts | 7 + .../floorplan-editor/common/Tile.ts | 31 + .../floorplan-editor/common/Utils.ts | 53 + .../views/FloorplanCanvasView.tsx | 156 + .../views/FloorplanImportExportView.tsx | 55 + .../views/FloorplanOptionsView.tsx | 189 ++ src/components/friends/FriendsView.scss | 250 ++ src/components/friends/FriendsView.tsx | 21 + .../views/friends-bar/FriendBarItemView.tsx | 59 + .../views/friends-bar/FriendsBarView.tsx | 26 + .../FriendsListRemoveConfirmationView.tsx | 29 + .../FriendsListRoomInviteView.tsx | 31 + .../friends-list/FriendsListSearchView.tsx | 103 + .../views/friends-list/FriendsListView.tsx | 150 + .../FriendsListGroupItemView.tsx | 85 + .../FriendsListGroupView.tsx | 23 + .../FriendsListRequestItemView.tsx | 25 + .../FriendsListRequestView.tsx | 29 + .../views/messenger/FriendsMessengerView.tsx | 258 ++ .../FriendsMessengerThreadGroup.tsx | 122 + .../FriendsMessengerThreadView.tsx | 16 + .../game-center/GameCenterView.scss | 44 + src/components/game-center/GameCenterView.tsx | 50 + .../game-center/views/GameListView.tsx | 32 + .../game-center/views/GameStageView.tsx | 47 + src/components/game-center/views/GameView.tsx | 58 + src/components/groups/GroupView.scss | 190 ++ src/components/groups/GroupsView.tsx | 63 + .../groups/views/GroupBadgeCreatorView.tsx | 83 + .../groups/views/GroupCreatorView.tsx | 164 + .../views/GroupInformationStandaloneView.tsx | 29 + .../groups/views/GroupInformationView.tsx | 146 + .../groups/views/GroupManagerView.tsx | 119 + .../groups/views/GroupMembersView.tsx | 210 ++ .../groups/views/GroupRoomInformationView.tsx | 132 + .../groups/views/tabs/GroupTabBadgeView.tsx | 120 + .../groups/views/tabs/GroupTabColorsView.tsx | 127 + .../tabs/GroupTabCreatorConfirmationView.tsx | 67 + .../views/tabs/GroupTabIdentityView.tsx | 116 + .../views/tabs/GroupTabSettingsView.tsx | 89 + src/components/guide-tool/GuideToolView.scss | 87 + src/components/guide-tool/GuideToolView.tsx | 356 +++ .../guide-tool/views/GuideToolAcceptView.tsx | 35 + .../guide-tool/views/GuideToolMenuView.tsx | 76 + .../guide-tool/views/GuideToolOngoingView.tsx | 130 + .../views/GuideToolUserCreateRequestView.tsx | 32 + .../views/GuideToolUserFeedbackView.tsx | 43 + .../views/GuideToolUserNoHelpersView.tsx | 13 + .../views/GuideToolUserPendingView.tsx | 33 + .../views/GuideToolUserSomethingWrogView.tsx | 12 + .../views/GuideToolUserThanksView.tsx | 13 + src/components/hc-center/HcCenterView.scss | 44 + src/components/hc-center/HcCenterView.tsx | 204 ++ src/components/help/HelpView.scss | 18 + src/components/help/HelpView.tsx | 116 + .../help/views/DescribeReportView.tsx | 48 + src/components/help/views/HelpIndexView.tsx | 37 + .../help/views/ReportSummaryView.tsx | 57 + .../help/views/SanctionStatusView.tsx | 75 + .../help/views/SelectReportedChatsView.tsx | 90 + .../help/views/SelectReportedUserView.tsx | 85 + src/components/help/views/SelectTopicView.tsx | 53 + .../NameChangeConfirmationView.tsx | 44 + .../views/name-change/NameChangeInitView.tsx | 19 + .../views/name-change/NameChangeInputView.tsx | 97 + .../help/views/name-change/NameChangeView.tsx | 66 + .../views/name-change/NameChangeView.types.ts | 5 + src/components/hotel-view/HotelView.scss | 97 + src/components/hotel-view/HotelView.tsx | 106 + .../views/widgets/GetWidgetLayout.tsx | 29 + .../views/widgets/HotelViewWidgets.scss | 4 + .../views/widgets/WidgetSlotView.tsx | 20 + .../bonus-rare/BonusRareWidgetView.scss | 10 + .../bonus-rare/BonusRareWidgetView.tsx | 42 + .../hall-of-fame-item/HallOfFameItemView.tsx | 27 + .../hall-of-fame/HallOfFameWidgetView.scss | 70 + .../hall-of-fame/HallOfFameWidgetView.tsx | 37 + .../HallOfFameWidgetView.types.ts | 5 + .../promo-article/PromoArticleWidgetView.scss | 27 + .../promo-article/PromoArticleWidgetView.tsx | 46 + .../widget-container/WidgetContainerView.scss | 9 + .../widget-container/WidgetContainerView.tsx | 39 + src/components/index.scss | 26 + src/components/inventory/InventoryView.scss | 17 + src/components/inventory/InventoryView.tsx | 152 + .../views/InventoryCategoryEmptyView.tsx | 26 + .../views/badge/InventoryBadgeItemView.tsx | 19 + .../views/badge/InventoryBadgeView.tsx | 66 + .../views/bot/InventoryBotItemView.tsx | 43 + .../inventory/views/bot/InventoryBotView.tsx | 91 + .../furniture/InventoryFurnitureItemView.tsx | 38 + .../InventoryFurnitureSearchView.tsx | 47 + .../furniture/InventoryFurnitureView.tsx | 146 + .../views/furniture/InventoryTradeView.tsx | 279 ++ .../views/pet/InventoryPetItemView.tsx | 43 + .../inventory/views/pet/InventoryPetView.tsx | 90 + src/components/loading/LoadingView.scss | 26 + src/components/loading/LoadingView.tsx | 35 + src/components/main/MainView.tsx | 114 + src/components/mod-tools/ModToolsView.scss | 70 + src/components/mod-tools/ModToolsView.tsx | 147 + .../mod-tools/views/chatlog/ChatlogRecord.ts | 11 + .../mod-tools/views/chatlog/ChatlogView.tsx | 91 + .../views/room/ModToolsChatlogView.tsx | 44 + .../mod-tools/views/room/ModToolsRoomView.tsx | 117 + .../views/tickets/CfhChatlogView.tsx | 41 + .../views/tickets/ModToolsIssueInfoView.tsx | 86 + .../views/tickets/ModToolsMyIssuesTabView.tsx | 47 + .../tickets/ModToolsOpenIssuesTabView.tsx | 42 + .../tickets/ModToolsPickedIssuesTabView.tsx | 39 + .../views/tickets/ModToolsTicketsView.tsx | 91 + .../views/user/ModToolsUserChatlogView.tsx | 44 + .../views/user/ModToolsUserModActionView.tsx | 176 ++ .../views/user/ModToolsUserRoomVisitsView.tsx | 60 + .../user/ModToolsUserSendMessageView.tsx | 45 + .../mod-tools/views/user/ModToolsUserView.tsx | 156 + src/components/navigator/NavigatorView.scss | 65 + src/components/navigator/NavigatorView.tsx | 233 ++ .../views/NavigatorDoorStateView.tsx | 110 + .../views/NavigatorRoomCreatorView.tsx | 122 + .../navigator/views/NavigatorRoomInfoView.tsx | 180 ++ .../navigator/views/NavigatorRoomLinkView.tsx | 33 + .../NavigatorRoomSettingsAccessTabView.tsx | 88 + .../NavigatorRoomSettingsBasicTabView.tsx | 171 + .../NavigatorRoomSettingsModTabView.tsx | 118 + .../NavigatorRoomSettingsRightsTabView.tsx | 91 + .../NavigatorRoomSettingsView.tsx | 206 ++ .../NavigatorRoomSettingsVipChatTabView.tsx | 76 + .../NavigatorSearchResultItemInfoView.tsx | 81 + .../search/NavigatorSearchResultItemView.tsx | 121 + .../search/NavigatorSearchResultView.tsx | 118 + .../views/search/NavigatorSearchView.tsx | 84 + .../NitrobubbleHiddenView.tsx | 62 + src/components/nitropedia/NitropediaView.scss | 47 + src/components/nitropedia/NitropediaView.tsx | 105 + .../NotificationCenterView.scss | 66 + .../NotificationCenterView.tsx | 77 + .../views/alert-layouts/GetAlertLayout.tsx | 21 + .../alert-layouts/NitroSystemAlertView.tsx | 39 + .../NotificationDefaultAlertView.tsx | 56 + .../NotificationSearchAlertView.tsx | 61 + .../views/bubble-layouts/GetBubbleLayout.tsx | 18 + .../NotificationClubGiftBubbleView.tsx | 26 + .../NotificationDefaultBubbleView.tsx | 25 + .../confirm-layouts/GetConfirmLayout.tsx | 15 + .../NotificationDefaultConfirmView.tsx | 40 + src/components/purse/PurseView.scss | 56 + src/components/purse/PurseView.tsx | 90 + src/components/purse/views/CurrencyView.tsx | 39 + src/components/purse/views/SeasonalView.tsx | 24 + src/components/right-side/RightSideView.scss | 10 + src/components/right-side/RightSideView.tsx | 24 + src/components/room/RoomView.scss | 2 + src/components/room/RoomView.tsx | 45 + .../room/spectator/RoomSpectatorView.scss | 26 + .../room/spectator/RoomSpectatorView.tsx | 8 + src/components/room/widgets/RoomWidgets.scss | 186 ++ .../room/widgets/RoomWidgetsView.tsx | 172 + .../AvatarInfoPetTrainingPanelView.tsx | 59 + .../AvatarInfoRentableBotChatView.tsx | 68 + .../AvatarInfoUseProductConfirmView.tsx | 281 ++ .../avatar-info/AvatarInfoUseProductView.tsx | 135 + .../avatar-info/AvatarInfoWidgetView.scss | 139 + .../avatar-info/AvatarInfoWidgetView.tsx | 139 + .../infostand/InfoStandWidgetBotView.tsx | 55 + .../infostand/InfoStandWidgetFurniView.tsx | 473 +++ .../infostand/InfoStandWidgetPetView.tsx | 209 ++ .../InfoStandWidgetRentableBotView.tsx | 84 + ...nfoStandWidgetUserRelationshipItemView.tsx | 31 + .../InfoStandWidgetUserRelationshipsView.tsx | 23 + .../infostand/InfoStandWidgetUserTagsView.tsx | 31 + .../infostand/InfoStandWidgetUserView.tsx | 215 ++ .../menu/AvatarInfoWidgetAvatarView.tsx | 372 +++ .../menu/AvatarInfoWidgetDecorateView.tsx | 29 + .../menu/AvatarInfoWidgetFurniView.tsx | 67 + .../menu/AvatarInfoWidgetNameView.tsx | 31 + .../menu/AvatarInfoWidgetOwnAvatarView.tsx | 292 ++ .../menu/AvatarInfoWidgetOwnPetView.tsx | 220 ++ .../menu/AvatarInfoWidgetPetView.tsx | 138 + .../menu/AvatarInfoWidgetRentableBotView.tsx | 197 ++ .../ChatInputStickersSelectorConsolaView.tsx | 124 + .../ChatInputStickersSelectorView.tsx | 127 + .../chat-input/ChatInputStyleSelectorView.tsx | 68 + .../widgets/chat-input/ChatInputView.scss | 120 + .../room/widgets/chat-input/ChatInputView.tsx | 330 ++ .../widgets/chat/ChatWidgetMessageView.tsx | 95 + .../room/widgets/chat/ChatWidgetView.scss | 919 ++++++ .../room/widgets/chat/ChatWidgetView.tsx | 160 + .../widgets/choosers/ChooserWidgetView.scss | 4 + .../widgets/choosers/ChooserWidgetView.tsx | 50 + .../choosers/FurniChooserWidgetView.tsx | 31 + .../choosers/UserChooserWidgetView.tsx | 31 + .../widgets/context-menu/ContextMenu.scss | 131 + .../context-menu/ContextMenuCaretView.tsx | 26 + .../context-menu/ContextMenuHeaderView.tsx | 18 + .../context-menu/ContextMenuListItemView.tsx | 32 + .../context-menu/ContextMenuListView.tsx | 18 + .../widgets/context-menu/ContextMenuView.tsx | 170 + .../widgets/doorbell/DoorbellWidgetView.tsx | 51 + .../FriendRequestDialogView.scss | 4 + .../FriendRequestDialogView.tsx | 28 + .../FriendRequestWidgetView.tsx | 17 + .../FurnitureBackgroundColorView.tsx | 30 + .../furniture/FurnitureBadgeDisplayView.tsx | 12 + .../furniture/FurnitureCraftingView.tsx | 115 + .../widgets/furniture/FurnitureDimmerView.tsx | 86 + .../furniture/FurnitureExchangeCreditView.tsx | 33 + .../furniture/FurnitureExternalImageView.tsx | 22 + .../furniture/FurnitureFriendFurniView.tsx | 66 + .../furniture/FurnitureGiftOpeningView.tsx | 72 + .../furniture/FurnitureHighScoreView.tsx | 60 + .../furniture/FurnitureInternalLinkView.tsx | 9 + .../furniture/FurnitureMannequinView.tsx | 144 + .../FurnitureMysteryBoxOpenDialogView.tsx | 81 + .../FurnitureMysteryTrophyOpenDialogView.tsx | 50 + .../furniture/FurnitureRoomLinkView.tsx | 9 + .../furniture/FurnitureSpamWallPostItView.tsx | 46 + .../furniture/FurnitureStackHeightView.tsx | 58 + .../furniture/FurnitureStickieView.tsx | 66 + .../widgets/furniture/FurnitureTrophyView.tsx | 12 + .../widgets/furniture/FurnitureWidgets.scss | 526 ++++ .../furniture/FurnitureWidgetsView.tsx | 48 + .../furniture/FurnitureYoutubeDisplayView.tsx | 109 + .../context-menu/EffectBoxConfirmView.tsx | 40 + .../context-menu/FurnitureContextMenuView.tsx | 130 + .../MonsterPlantSeedConfirmView.tsx | 85 + .../PurchasableClothingConfirmView.tsx | 104 + .../playlist-editor/DiskInventoryView.tsx | 94 + .../FurniturePlaylistEditorWidgetView.tsx | 29 + .../playlist-editor/SongPlaylistView.tsx | 79 + .../mysterybox/MysteryBoxExtensionView.scss | 52 + .../mysterybox/MysteryBoxExtensionView.tsx | 67 + .../object-location/ObjectLocationView.tsx | 61 + .../pet-package/PetPackageWidgetView.scss | 106 + .../pet-package/PetPackageWidgetView.tsx | 42 + .../RoomFilterWordsWidgetView.tsx | 74 + .../room-promotes/RoomPromotesWidgetView.tsx | 56 + .../views/RoomPromoteEditWidgetView.tsx | 44 + .../views/RoomPromoteMyOwnEventWidgetView.tsx | 35 + .../views/RoomPromoteOtherEventWidgetView.tsx | 30 + .../room/widgets/room-promotes/views/index.ts | 3 + .../RoomThumbnailWidgetView.tsx | 42 + .../room-tools/RoomToolsWidgetView.tsx | 205 ++ .../user-location/UserLocationView.tsx | 24 + .../word-quiz/WordQuizQuestionView.tsx | 44 + .../widgets/word-quiz/WordQuizVoteView.tsx | 24 + .../widgets/word-quiz/WordQuizWidgetView.tsx | 19 + src/components/toolbar/ToolbarMeView.tsx | 52 + src/components/toolbar/ToolbarView.scss | 141 + src/components/toolbar/ToolbarView.tsx | 111 + .../user-profile/UserProfileVew.scss | 67 + .../user-profile/UserProfileView.tsx | 122 + .../views/BadgesContainerView.tsx | 25 + .../views/FriendsContainerView.tsx | 28 + .../views/GroupsContainerView.tsx | 90 + .../views/RelationshipsContainerView.tsx | 62 + .../user-profile/views/UserContainerView.tsx | 74 + .../user-settings/UserSettingsView.tsx | 187 ++ src/components/wired/WiredView.scss | 175 ++ src/components/wired/WiredView.tsx | 21 + src/components/wired/views/WiredBaseView.tsx | 113 + .../wired/views/WiredFurniSelectorView.tsx | 16 + .../views/actions/WiredActionBaseView.tsx | 41 + .../WiredActionBotChangeFigureView.tsx | 37 + .../WiredActionBotFollowAvatarView.tsx | 43 + .../WiredActionBotGiveHandItemView.tsx | 42 + .../views/actions/WiredActionBotMoveView.tsx | 27 + .../WiredActionBotTalkToAvatarView.tsx | 52 + .../views/actions/WiredActionBotTalkView.tsx | 52 + .../actions/WiredActionBotTeleportView.tsx | 27 + .../WiredActionCallAnotherStackView.tsx | 8 + .../views/actions/WiredActionChaseView.tsx | 8 + .../views/actions/WiredActionChatView.tsx | 27 + .../views/actions/WiredActionFleeView.tsx | 8 + .../actions/WiredActionGiveRewardView.tsx | 160 + ...redActionGiveScoreToPredefinedTeamView.tsx | 67 + .../actions/WiredActionGiveScoreView.tsx | 52 + .../views/actions/WiredActionJoinTeamView.tsx | 35 + .../actions/WiredActionKickFromRoomView.tsx | 27 + .../views/actions/WiredActionLayoutView.tsx | 85 + .../actions/WiredActionLeaveTeamView.tsx | 8 + .../WiredActionMoveAndRotateFurniView.tsx | 82 + .../actions/WiredActionMoveFurniToView.tsx | 76 + .../actions/WiredActionMoveFurniView.tsx | 100 + .../views/actions/WiredActionMuteUserView.tsx | 43 + .../views/actions/WiredActionResetView.tsx | 8 + .../WiredActionSetFurniStateToView.tsx | 42 + .../views/actions/WiredActionTeleportView.tsx | 8 + .../WiredActionToggleFurniStateView.tsx | 8 + .../WiredConditionActorHasHandItem.tsx | 34 + .../WiredConditionActorIsGroupMemberView.tsx | 8 + .../WiredConditionActorIsOnFurniView.tsx | 8 + .../WiredConditionActorIsTeamMemberView.tsx | 37 + .../WiredConditionActorIsWearingBadgeView.tsx | 27 + ...WiredConditionActorIsWearingEffectView.tsx | 27 + .../conditions/WiredConditionBaseView.tsx | 23 + .../WiredConditionDateRangeView.tsx | 58 + .../WiredConditionFurniHasAvatarOnView.tsx | 8 + .../WiredConditionFurniHasFurniOnView.tsx | 35 + .../WiredConditionFurniHasNotFurniOnView.tsx | 35 + .../WiredConditionFurniIsOfTypeView.tsx | 8 + ...WiredConditionFurniMatchesSnapshotView.tsx | 42 + .../conditions/WiredConditionLayoutView.tsx | 64 + .../WiredConditionTimeElapsedLessView.tsx | 33 + .../WiredConditionTimeElapsedMoreView.tsx | 33 + .../WiredConditionUserCountInRoomView.tsx | 52 + .../WiredTriggerAvatarEnterRoomView.tsx | 38 + .../WiredTriggerAvatarSaysSomethingView.tsx | 44 + .../WiredTriggerAvatarWalksOffFurniView.tsx | 8 + .../WiredTriggerAvatarWalksOnFurni.tsx | 8 + .../views/triggers/WiredTriggerBaseView.tsx | 23 + .../WiredTriggerBotReachedAvatarView.tsx | 27 + .../WiredTriggerBotReachedStuffView.tsx | 27 + .../triggers/WiredTriggerCollisionView.tsx | 8 + .../triggers/WiredTriggerExecuteOnceView.tsx | 33 + ...iredTriggerExecutePeriodicallyLongView.tsx | 34 + .../WiredTriggerExecutePeriodicallyView.tsx | 33 + .../triggers/WiredTriggerGameEndsView.tsx | 8 + .../triggers/WiredTriggerGameStartsView.tsx | 8 + .../views/triggers/WiredTriggerLayoutView.tsx | 52 + .../WiredTriggerScoreAchievedView.tsx | 33 + .../triggers/WiredTriggerToggleFurniView.tsx | 8 + src/events/catalog/CatalogEvent.ts | 14 + src/events/catalog/CatalogInitGiftEvent.ts | 32 + .../CatalogPostMarketplaceOfferEvent.ts | 20 + .../catalog/CatalogPurchaseFailureEvent.ts | 20 + .../catalog/CatalogPurchaseNotAllowedEvent.ts | 20 + .../catalog/CatalogPurchaseOverrideEvent.ts | 19 + .../catalog/CatalogPurchaseSoldOutEvent.ts | 11 + src/events/catalog/CatalogPurchasedEvent.ts | 20 + .../CatalogSetRoomPreviewerStuffDataEvent.ts | 19 + src/events/catalog/CatalogWidgetEvent.ts | 26 + .../catalog/SetRoomPreviewerStuffDataEvent.ts | 28 + src/events/catalog/index.ts | 11 + src/events/guide-tool/GuideToolEvent.ts | 10 + src/events/guide-tool/index.ts | 1 + src/events/help/HelpNameChangeEvent.ts | 6 + src/events/help/index.ts | 1 + src/events/index.ts | 6 + .../inventory/InventoryFurniAddedEvent.ts | 14 + src/events/inventory/index.ts | 1 + src/events/room-widgets/index.ts | 1 + .../thumbnail/RoomWidgetThumbnailEvent.ts | 8 + src/events/room-widgets/thumbnail/index.ts | 1 + src/hooks/UseMountEffect.tsx | 7 + src/hooks/achievements/index.ts | 1 + src/hooks/achievements/useAchievements.ts | 185 ++ src/hooks/camera/index.ts | 1 + src/hooks/camera/useCamera.ts | 42 + src/hooks/catalog/index.ts | 3 + src/hooks/catalog/useCatalog.ts | 913 ++++++ .../catalog/useCatalogPlaceMultipleItems.ts | 7 + .../useCatalogSkipPurchaseConfirmation.ts | 7 + src/hooks/chat-history/index.ts | 1 + src/hooks/chat-history/useChatHistory.ts | 104 + src/hooks/events/core/index.ts | 2 + .../events/core/useCommunicationEvent.tsx | 5 + .../events/core/useConfigurationEvent.tsx | 5 + src/hooks/events/index.ts | 5 + src/hooks/events/nitro/index.ts | 8 + src/hooks/events/nitro/useAvatarEvent.tsx | 5 + src/hooks/events/nitro/useCameraEvent.tsx | 5 + .../events/nitro/useLocalizationEvent.tsx | 5 + src/hooks/events/nitro/useMainEvent.tsx | 5 + src/hooks/events/nitro/useRoomEngineEvent.tsx | 5 + .../nitro/useRoomSessionManagerEvent.tsx | 5 + .../nitro/useSessionDataManagerEvent.tsx | 5 + src/hooks/events/nitro/useSoundEvent.tsx | 5 + src/hooks/events/useEventDispatcher.tsx | 31 + src/hooks/events/useMessageEvent.tsx | 16 + src/hooks/events/useUiEvent.tsx | 5 + src/hooks/friends/index.ts | 2 + src/hooks/friends/useFriends.ts | 266 ++ src/hooks/friends/useMessenger.ts | 187 ++ src/hooks/game-center/index.ts | 1 + src/hooks/game-center/useGameCenter.ts | 83 + src/hooks/groups/index.ts | 1 + src/hooks/groups/useGroup.ts | 55 + src/hooks/help/index.ts | 1 + src/hooks/help/useHelp.ts | 149 + src/hooks/index.ts | 22 + src/hooks/inventory/index.ts | 6 + src/hooks/inventory/useInventoryBadges.ts | 152 + src/hooks/inventory/useInventoryBots.ts | 158 + src/hooks/inventory/useInventoryFurni.ts | 298 ++ src/hooks/inventory/useInventoryPets.ts | 107 + src/hooks/inventory/useInventoryTrade.ts | 288 ++ .../inventory/useInventoryUnseenTracker.ts | 132 + src/hooks/mod-tools/index.ts | 1 + src/hooks/mod-tools/useModTools.ts | 207 ++ src/hooks/navigator/index.ts | 1 + src/hooks/navigator/useNavigator.ts | 442 +++ src/hooks/notification/index.ts | 1 + src/hooks/notification/useNotification.ts | 432 +++ src/hooks/purse/index.ts | 1 + src/hooks/purse/usePurse.ts | 126 + src/hooks/rooms/engine/index.ts | 9 + src/hooks/rooms/engine/useFurniAddedEvent.ts | 19 + .../rooms/engine/useFurniRemovedEvent.ts | 19 + .../rooms/engine/useObjectDeselectedEvent.ts | 7 + .../engine/useObjectDoubleClickedEvent.ts | 7 + .../rooms/engine/useObjectRollOutEvent.ts | 7 + .../rooms/engine/useObjectRollOverEvent.ts | 7 + .../rooms/engine/useObjectSelectedEvent.ts | 7 + src/hooks/rooms/engine/useUserAddedEvent.ts | 19 + src/hooks/rooms/engine/useUserRemovedEvent.ts | 19 + src/hooks/rooms/index.ts | 4 + src/hooks/rooms/promotes/index.ts | 1 + src/hooks/rooms/promotes/useRoomPromote.ts | 23 + src/hooks/rooms/useRoom.ts | 298 ++ src/hooks/rooms/widgets/furniture/index.ts | 19 + .../useFurnitureBackgroundColorWidget.ts | 71 + .../useFurnitureBadgeDisplayWidget.ts | 75 + .../useFurnitureContextMenuWidget.ts | 179 ++ .../furniture/useFurnitureCraftingWidget.ts | 166 + .../furniture/useFurnitureDimmerWidget.ts | 110 + .../furniture/useFurnitureExchangeWidget.ts | 48 + .../useFurnitureExternalImageWidget.ts | 74 + .../useFurnitureFriendFurniWidget.ts | 75 + .../furniture/useFurnitureHighScoreWidget.ts | 56 + .../useFurnitureInternalLinkWidget.ts | 27 + .../furniture/useFurnitureMannequinWidget.ts | 80 + .../useFurniturePlaylistEditorWidget.ts | 108 + .../furniture/useFurniturePresentWidget.ts | 225 ++ .../furniture/useFurnitureRoomLinkWidget.ts | 49 + .../useFurnitureSpamWallPostItWidget.ts | 59 + .../useFurnitureStackHeightWidget.ts | 79 + .../furniture/useFurnitureStickieWidget.ts | 85 + .../furniture/useFurnitureTrophyWidget.ts | 63 + .../furniture/useFurnitureYoutubeWidget.ts | 127 + src/hooks/rooms/widgets/index.ts | 12 + .../rooms/widgets/useAvatarInfoWidget.ts | 355 +++ src/hooks/rooms/widgets/useChatInputWidget.ts | 279 ++ src/hooks/rooms/widgets/useChatWidget.ts | 252 ++ src/hooks/rooms/widgets/useDoorbellWidget.ts | 44 + .../rooms/widgets/useFilterWordsWidget.ts | 23 + .../rooms/widgets/useFriendRequestWidget.ts | 81 + .../rooms/widgets/useFurniChooserWidget.ts | 132 + .../rooms/widgets/usePetPackageWidget.ts | 75 + src/hooks/rooms/widgets/usePollWidget.ts | 52 + .../rooms/widgets/useUserChooserWidget.ts | 80 + src/hooks/rooms/widgets/useWordQuizWidget.ts | 149 + src/hooks/session/index.ts | 1 + src/hooks/session/useSessionInfo.ts | 93 + src/hooks/useLocalStorage.ts | 44 + src/hooks/useSharedVisibility.ts | 44 + src/hooks/wired/index.ts | 1 + src/hooks/wired/useWired.ts | 133 + src/index.scss | 26 + src/index.tsx | 5 + src/react-app-env.d.ts | 1 + src/workers/IntervalWebWorker.ts | 26 + src/workers/WorkerBuilder.ts | 10 + tsconfig.json | 29 + vite.config.js | 31 + yarn.lock | 2757 +++++++++++++++++ 1449 files changed, 66950 insertions(+) create mode 100644 .browserslistrc create mode 100644 .eslintrc.json create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml-disabled create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 100644 index.html create mode 100644 package.json create mode 100644 public/android-chrome-192x192.png create mode 100644 public/android-chrome-512x512.png create mode 100644 public/apple-touch-icon.png create mode 100644 public/browserconfig.xml create mode 100644 public/favicon-16x16.png create mode 100644 public/favicon-32x32.png create mode 100644 public/favicon.ico create mode 100644 public/mstile-150x150.png create mode 100644 public/robots.txt create mode 100644 public/safari-pinned-tab.svg create mode 100644 public/site.webmanifest create mode 100644 src/App.scss create mode 100644 src/App.tsx create mode 100644 src/api/GetRendererVersion.ts create mode 100644 src/api/GetUIVersion.ts create mode 100644 src/api/achievements/AchievementCategory.ts create mode 100644 src/api/achievements/AchievementUtilities.ts create mode 100644 src/api/achievements/IAchievementCategory.ts create mode 100644 src/api/achievements/index.ts create mode 100644 src/api/avatar/AvatarEditorAction.ts create mode 100644 src/api/avatar/AvatarEditorGridColorItem.ts create mode 100644 src/api/avatar/AvatarEditorGridPartItem.ts create mode 100644 src/api/avatar/AvatarEditorUtilities.ts create mode 100644 src/api/avatar/BodyModel.ts create mode 100644 src/api/avatar/CategoryBaseModel.ts create mode 100644 src/api/avatar/CategoryData.ts create mode 100644 src/api/avatar/FigureData.ts create mode 100644 src/api/avatar/FigureGenerator.ts create mode 100644 src/api/avatar/HeadModel.ts create mode 100644 src/api/avatar/IAvatarEditorCategoryModel.ts create mode 100644 src/api/avatar/LegModel.ts create mode 100644 src/api/avatar/TorsoModel.ts create mode 100644 src/api/avatar/index.ts create mode 100644 src/api/camera/CameraEditorTabs.ts create mode 100644 src/api/camera/CameraPicture.ts create mode 100644 src/api/camera/CameraPictureThumbnail.ts create mode 100644 src/api/camera/index.ts create mode 100644 src/api/campaign/CalendarItem.ts create mode 100644 src/api/campaign/CalendarItemState.ts create mode 100644 src/api/campaign/ICalendarItem.ts create mode 100644 src/api/campaign/index.ts create mode 100644 src/api/catalog/BuilderFurniPlaceableStatus.ts create mode 100644 src/api/catalog/CatalogNode.ts create mode 100644 src/api/catalog/CatalogPage.ts create mode 100644 src/api/catalog/CatalogPageName.ts create mode 100644 src/api/catalog/CatalogPetPalette.ts create mode 100644 src/api/catalog/CatalogPurchaseState.ts create mode 100644 src/api/catalog/CatalogType.ts create mode 100644 src/api/catalog/CatalogUtilities.ts create mode 100644 src/api/catalog/FurnitureOffer.ts create mode 100644 src/api/catalog/GetImageIconUrlForProduct.ts create mode 100644 src/api/catalog/GiftWrappingConfiguration.ts create mode 100644 src/api/catalog/ICatalogNode.ts create mode 100644 src/api/catalog/ICatalogOptions.ts create mode 100644 src/api/catalog/ICatalogPage.ts create mode 100644 src/api/catalog/IMarketplaceSearchOptions.ts create mode 100644 src/api/catalog/IPageLocalization.ts create mode 100644 src/api/catalog/IProduct.ts create mode 100644 src/api/catalog/IPurchasableOffer.ts create mode 100644 src/api/catalog/IPurchaseOptions.ts create mode 100644 src/api/catalog/MarketplaceOfferData.ts create mode 100644 src/api/catalog/MarketplaceOfferState.ts create mode 100644 src/api/catalog/MarketplaceSearchType.ts create mode 100644 src/api/catalog/Offer.ts create mode 100644 src/api/catalog/PageLocalization.ts create mode 100644 src/api/catalog/PlacedObjectPurchaseData.ts create mode 100644 src/api/catalog/Product.ts create mode 100644 src/api/catalog/ProductTypeEnum.ts create mode 100644 src/api/catalog/RequestedPage.ts create mode 100644 src/api/catalog/SearchResult.ts create mode 100644 src/api/catalog/index.ts create mode 100644 src/api/chat-history/ChatEntryType.ts create mode 100644 src/api/chat-history/ChatHistoryCurrentDate.ts create mode 100644 src/api/chat-history/IChatEntry.ts create mode 100644 src/api/chat-history/IRoomHistoryEntry.ts create mode 100644 src/api/chat-history/MessengerHistoryCurrentDate.ts create mode 100644 src/api/chat-history/index.ts create mode 100644 src/api/events/DispatchEvent.ts create mode 100644 src/api/events/DispatchMainEvent.ts create mode 100644 src/api/events/DispatchUiEvent.ts create mode 100644 src/api/events/UI_EVENT_DISPATCHER.ts create mode 100644 src/api/events/index.ts create mode 100644 src/api/friends/GetGroupChatData.ts create mode 100644 src/api/friends/IGroupChatData.ts create mode 100644 src/api/friends/MessengerFriend.ts create mode 100644 src/api/friends/MessengerGroupType.ts create mode 100644 src/api/friends/MessengerIconState.ts create mode 100644 src/api/friends/MessengerRequest.ts create mode 100644 src/api/friends/MessengerSettings.ts create mode 100644 src/api/friends/MessengerThread.ts create mode 100644 src/api/friends/MessengerThreadChat.ts create mode 100644 src/api/friends/MessengerThreadChatGroup.ts create mode 100644 src/api/friends/OpenMessengerChat.ts create mode 100644 src/api/friends/index.ts create mode 100644 src/api/groups/GetGroupInformation.ts create mode 100644 src/api/groups/GetGroupManager.ts create mode 100644 src/api/groups/GetGroupMembers.ts create mode 100644 src/api/groups/GroupBadgePart.ts create mode 100644 src/api/groups/GroupMembershipType.ts create mode 100644 src/api/groups/GroupType.ts create mode 100644 src/api/groups/IGroupCustomize.ts create mode 100644 src/api/groups/IGroupData.ts create mode 100644 src/api/groups/ToggleFavoriteGroup.ts create mode 100644 src/api/groups/TryJoinGroup.ts create mode 100644 src/api/groups/index.ts create mode 100644 src/api/guide-tool/GuideSessionState.ts create mode 100644 src/api/guide-tool/GuideToolMessage.ts create mode 100644 src/api/guide-tool/GuideToolMessageGroup.ts create mode 100644 src/api/guide-tool/index.ts create mode 100644 src/api/hc-center/ClubStatus.ts create mode 100644 src/api/hc-center/GetClubBadge.ts create mode 100644 src/api/hc-center/index.ts create mode 100644 src/api/help/CallForHelpResult.ts create mode 100644 src/api/help/GetCloseReasonKey.ts create mode 100644 src/api/help/IHelpReport.ts create mode 100644 src/api/help/IReportedUser.ts create mode 100644 src/api/help/ReportState.ts create mode 100644 src/api/help/ReportType.ts create mode 100644 src/api/help/index.ts create mode 100644 src/api/index.ts create mode 100644 src/api/inventory/FurniCategory.ts create mode 100644 src/api/inventory/FurnitureItem.ts create mode 100644 src/api/inventory/FurnitureUtilities.ts create mode 100644 src/api/inventory/GroupItem.ts create mode 100644 src/api/inventory/IBotItem.ts create mode 100644 src/api/inventory/IFurnitureItem.ts create mode 100644 src/api/inventory/IPetItem.ts create mode 100644 src/api/inventory/IUnseenItemTracker.ts create mode 100644 src/api/inventory/InventoryUtilities.ts create mode 100644 src/api/inventory/PetUtilities.ts create mode 100644 src/api/inventory/TradeState.ts create mode 100644 src/api/inventory/TradeUserData.ts create mode 100644 src/api/inventory/TradingNotificationType.ts create mode 100644 src/api/inventory/TradingUtilities.ts create mode 100644 src/api/inventory/UnseenItemCategory.ts create mode 100644 src/api/inventory/index.ts create mode 100644 src/api/mod-tools/GetIssueCategoryName.ts create mode 100644 src/api/mod-tools/ISelectedUser.ts create mode 100644 src/api/mod-tools/IUserInfo.ts create mode 100644 src/api/mod-tools/ModActionDefinition.ts create mode 100644 src/api/mod-tools/index.ts create mode 100644 src/api/navigator/DoorStateType.ts create mode 100644 src/api/navigator/INavigatorData.ts create mode 100644 src/api/navigator/INavigatorSearchFilter.ts create mode 100644 src/api/navigator/IRoomChatSettings.ts create mode 100644 src/api/navigator/IRoomData.ts create mode 100644 src/api/navigator/IRoomModel.ts create mode 100644 src/api/navigator/IRoomModerationSettings.ts create mode 100644 src/api/navigator/NavigatorSearchResultViewDisplayMode.ts create mode 100644 src/api/navigator/RoomInfoData.ts create mode 100644 src/api/navigator/RoomSettingsUtils.ts create mode 100644 src/api/navigator/SearchFilterOptions.ts create mode 100644 src/api/navigator/TryVisitRoom.ts create mode 100644 src/api/navigator/index.ts create mode 100644 src/api/nitro/AddLinkEventTracker.ts create mode 100644 src/api/nitro/CreateLinkEvent.ts create mode 100644 src/api/nitro/GetCommunication.ts create mode 100644 src/api/nitro/GetConfiguration.ts create mode 100644 src/api/nitro/GetConnection.ts create mode 100644 src/api/nitro/GetLocalization.ts create mode 100644 src/api/nitro/GetNitroInstance.ts create mode 100644 src/api/nitro/OpenUrl.ts create mode 100644 src/api/nitro/RemoveLinkEventTracker.ts create mode 100644 src/api/nitro/SendMessageComposer.ts create mode 100644 src/api/nitro/avatar/GetAvatarPalette.ts create mode 100644 src/api/nitro/avatar/GetAvatarRenderManager.ts create mode 100644 src/api/nitro/avatar/GetAvatarSetType.ts create mode 100644 src/api/nitro/avatar/index.ts create mode 100644 src/api/nitro/camera/GetRoomCameraWidgetManager.ts create mode 100644 src/api/nitro/camera/index.ts create mode 100644 src/api/nitro/core/GetConfigurationManager.ts create mode 100644 src/api/nitro/core/GetNitroCore.ts create mode 100644 src/api/nitro/core/index.ts create mode 100644 src/api/nitro/index.ts create mode 100644 src/api/nitro/room/DispatchMouseEvent.ts create mode 100644 src/api/nitro/room/DispatchTouchEvent.ts create mode 100644 src/api/nitro/room/GetOwnRoomObject.ts create mode 100644 src/api/nitro/room/GetRoomEngine.ts create mode 100644 src/api/nitro/room/GetRoomObjectBounds.ts create mode 100644 src/api/nitro/room/GetRoomObjectScreenLocation.ts create mode 100644 src/api/nitro/room/InitializeRoomInstanceRenderingCanvas.ts create mode 100644 src/api/nitro/room/IsFurnitureSelectionDisabled.ts create mode 100644 src/api/nitro/room/ProcessRoomObjectOperation.ts create mode 100644 src/api/nitro/room/SetActiveRoomId.ts create mode 100644 src/api/nitro/room/index.ts create mode 100644 src/api/nitro/session/CanManipulateFurniture.ts create mode 100644 src/api/nitro/session/CreateRoomSession.ts create mode 100644 src/api/nitro/session/GetCanStandUp.ts create mode 100644 src/api/nitro/session/GetCanUseExpression.ts create mode 100644 src/api/nitro/session/GetClubMemberLevel.ts create mode 100644 src/api/nitro/session/GetFurnitureData.ts create mode 100644 src/api/nitro/session/GetFurnitureDataForProductOffer.ts create mode 100644 src/api/nitro/session/GetFurnitureDataForRoomObject.ts create mode 100644 src/api/nitro/session/GetOwnPosture.ts create mode 100644 src/api/nitro/session/GetProductDataForLocalization.ts create mode 100644 src/api/nitro/session/GetRoomSession.ts create mode 100644 src/api/nitro/session/GetRoomSessionManager.ts create mode 100644 src/api/nitro/session/GetSessionDataManager.ts create mode 100644 src/api/nitro/session/GoToDesktop.ts create mode 100644 src/api/nitro/session/HasHabboClub.ts create mode 100644 src/api/nitro/session/HasHabboVip.ts create mode 100644 src/api/nitro/session/IsOwnerOfFloorFurniture.ts create mode 100644 src/api/nitro/session/IsOwnerOfFurniture.ts create mode 100644 src/api/nitro/session/IsRidingHorse.ts create mode 100644 src/api/nitro/session/StartRoomSession.ts create mode 100644 src/api/nitro/session/VisitDesktop.ts create mode 100644 src/api/nitro/session/index.ts create mode 100644 src/api/notification/NotificationAlertItem.ts create mode 100644 src/api/notification/NotificationAlertType.ts create mode 100644 src/api/notification/NotificationBubbleItem.ts create mode 100644 src/api/notification/NotificationBubbleType.ts create mode 100644 src/api/notification/NotificationConfirmItem.ts create mode 100644 src/api/notification/NotificationConfirmType.ts create mode 100644 src/api/notification/index.ts create mode 100644 src/api/purse/IPurse.ts create mode 100644 src/api/purse/Purse.ts create mode 100644 src/api/purse/index.ts create mode 100644 src/api/room/events/RoomWidgetPollUpdateEvent.ts create mode 100644 src/api/room/events/RoomWidgetUpdateBackgroundColorPreviewEvent.ts create mode 100644 src/api/room/events/RoomWidgetUpdateChatInputContentEvent.ts create mode 100644 src/api/room/events/RoomWidgetUpdateEvent.ts create mode 100644 src/api/room/events/RoomWidgetUpdateRentableBotChatEvent.ts create mode 100644 src/api/room/events/RoomWidgetUpdateRoomObjectEvent.ts create mode 100644 src/api/room/events/index.ts create mode 100644 src/api/room/index.ts create mode 100644 src/api/room/widgets/AvatarInfoFurni.ts create mode 100644 src/api/room/widgets/AvatarInfoName.ts create mode 100644 src/api/room/widgets/AvatarInfoPet.ts create mode 100644 src/api/room/widgets/AvatarInfoRentableBot.ts create mode 100644 src/api/room/widgets/AvatarInfoUser.ts create mode 100644 src/api/room/widgets/AvatarInfoUtilities.ts create mode 100644 src/api/room/widgets/BotSkillsEnum.ts create mode 100644 src/api/room/widgets/ChatBubbleMessage.ts create mode 100644 src/api/room/widgets/ChatMessageTypeEnum.ts create mode 100644 src/api/room/widgets/DimmerFurnitureWidgetPresetItem.ts create mode 100644 src/api/room/widgets/DoChatsOverlap.ts create mode 100644 src/api/room/widgets/FurnitureDimmerUtilities.ts create mode 100644 src/api/room/widgets/GetDiskColor.ts create mode 100644 src/api/room/widgets/IAvatarInfo.ts create mode 100644 src/api/room/widgets/ICraftingIngredient.ts create mode 100644 src/api/room/widgets/ICraftingRecipe.ts create mode 100644 src/api/room/widgets/IPhotoData.ts create mode 100644 src/api/room/widgets/MannequinUtilities.ts create mode 100644 src/api/room/widgets/PetSupplementEnum.ts create mode 100644 src/api/room/widgets/PostureTypeEnum.ts create mode 100644 src/api/room/widgets/RoomDimmerPreset.ts create mode 100644 src/api/room/widgets/RoomObjectItem.ts create mode 100644 src/api/room/widgets/UseProductItem.ts create mode 100644 src/api/room/widgets/VoteValue.ts create mode 100644 src/api/room/widgets/YoutubeVideoPlaybackStateEnum.ts create mode 100644 src/api/room/widgets/index.ts create mode 100644 src/api/user/GetUserProfile.ts create mode 100644 src/api/user/index.ts create mode 100644 src/api/utils/CloneObject.ts create mode 100644 src/api/utils/ColorUtils.ts create mode 100644 src/api/utils/ConvertSeconds.ts create mode 100644 src/api/utils/GetLocalStorage.ts create mode 100644 src/api/utils/LocalStorageKeys.ts create mode 100644 src/api/utils/LocalizeBadgeDescription.ts create mode 100644 src/api/utils/LocalizeBageName.ts create mode 100644 src/api/utils/LocalizeFormattedNumber.ts create mode 100644 src/api/utils/LocalizeShortNumber.ts create mode 100644 src/api/utils/LocalizeText.ts create mode 100644 src/api/utils/PlaySound.ts create mode 100644 src/api/utils/ProductImageUtility.ts create mode 100644 src/api/utils/Randomizer.ts create mode 100644 src/api/utils/RoomChatFormatter.ts create mode 100644 src/api/utils/SetLocalStorage.ts create mode 100644 src/api/utils/SoundNames.ts create mode 100644 src/api/utils/WindowSaveOptions.ts create mode 100644 src/api/utils/index.ts create mode 100644 src/api/wired/GetWiredTimeLocale.ts create mode 100644 src/api/wired/WiredActionLayoutCode.ts create mode 100644 src/api/wired/WiredConditionLayoutCode.ts create mode 100644 src/api/wired/WiredDateToString.ts create mode 100644 src/api/wired/WiredFurniType.ts create mode 100644 src/api/wired/WiredSelectionFilter.ts create mode 100644 src/api/wired/WiredSelectionVisualizer.ts create mode 100644 src/api/wired/WiredStringDelimeter.ts create mode 100644 src/api/wired/WiredTriggerLayoutCode.ts create mode 100644 src/api/wired/index.ts create mode 100644 src/assets/images/achievements/back-arrow.png create mode 100644 src/assets/images/avatareditor/arrow-left-icon.png create mode 100644 src/assets/images/avatareditor/arrow-right-icon.png create mode 100644 src/assets/images/avatareditor/avatar-editor-spritesheet.png create mode 100644 src/assets/images/avatareditor/ca-icon.png create mode 100644 src/assets/images/avatareditor/ca-selected-icon.png create mode 100644 src/assets/images/avatareditor/cc-icon.png create mode 100644 src/assets/images/avatareditor/cc-selected-icon.png create mode 100644 src/assets/images/avatareditor/ch-icon.png create mode 100644 src/assets/images/avatareditor/ch-selected-icon.png create mode 100644 src/assets/images/avatareditor/clear-icon.png create mode 100644 src/assets/images/avatareditor/cp-icon.png create mode 100644 src/assets/images/avatareditor/cp-selected-icon.png create mode 100644 src/assets/images/avatareditor/ea-icon.png create mode 100644 src/assets/images/avatareditor/ea-selected-icon.png create mode 100644 src/assets/images/avatareditor/fa-icon.png create mode 100644 src/assets/images/avatareditor/fa-selected-icon.png create mode 100644 src/assets/images/avatareditor/female-icon.png create mode 100644 src/assets/images/avatareditor/female-selected-icon.png create mode 100644 src/assets/images/avatareditor/ha-icon.png create mode 100644 src/assets/images/avatareditor/ha-selected-icon.png create mode 100644 src/assets/images/avatareditor/he-icon.png create mode 100644 src/assets/images/avatareditor/he-selected-icon.png create mode 100644 src/assets/images/avatareditor/hr-icon.png create mode 100644 src/assets/images/avatareditor/hr-selected-icon.png create mode 100644 src/assets/images/avatareditor/lg-icon.png create mode 100644 src/assets/images/avatareditor/lg-selected-icon.png create mode 100644 src/assets/images/avatareditor/loading-icon.png create mode 100644 src/assets/images/avatareditor/male-icon.png create mode 100644 src/assets/images/avatareditor/male-selected-icon.png create mode 100644 src/assets/images/avatareditor/sellable-icon.png create mode 100644 src/assets/images/avatareditor/sh-icon.png create mode 100644 src/assets/images/avatareditor/sh-selected-icon.png create mode 100644 src/assets/images/avatareditor/spotlight-icon.png create mode 100644 src/assets/images/avatareditor/wa-icon.png create mode 100644 src/assets/images/avatareditor/wa-selected-icon.png create mode 100644 src/assets/images/campaign/available.png create mode 100644 src/assets/images/campaign/campaign_day_generic_bg.png create mode 100644 src/assets/images/campaign/campaign_opened.png create mode 100644 src/assets/images/campaign/campaign_spritesheet.png create mode 100644 src/assets/images/campaign/locked.png create mode 100644 src/assets/images/campaign/locked_bg.png create mode 100644 src/assets/images/campaign/next.png create mode 100644 src/assets/images/campaign/prev.png create mode 100644 src/assets/images/campaign/unavailable.png create mode 100644 src/assets/images/campaign/unlocked_bg.png create mode 100644 src/assets/images/catalog/diamond_info_illustration.gif create mode 100644 src/assets/images/catalog/hc_banner_big.png create mode 100644 src/assets/images/catalog/hc_big.png create mode 100644 src/assets/images/catalog/hc_small.png create mode 100644 src/assets/images/catalog/paint-icon.png create mode 100644 src/assets/images/catalog/target-price.png create mode 100644 src/assets/images/catalog/vip.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_0.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_0_1_33_34_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_0_transparent.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_1.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_10.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_10_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_11.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_11_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_12.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_12_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_13.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_13_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_14.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_14_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_15.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_15_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_16.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_16_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_17.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_17_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_18.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_18_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_19.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_19_20_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_2.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_20.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_21.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_21_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_22.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_22_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_23.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_23_37_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_24.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_24_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_25.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_25_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_26.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_26_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_27.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_27_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_28.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_28_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_29.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_29_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_2_31_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_3.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_30.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_30_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_32.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_32_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_33_34.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_33_extra.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_34_extra.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_35.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_35_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_36.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_36_extra.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_36_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_37.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_38.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_38_extra.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_38_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_3_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_4.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_4_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_5.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_5_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_6.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_6_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_7.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_7_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_8.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_8_pointer.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_9.png create mode 100644 src/assets/images/chat/chatbubbles/bubble_9_pointer.png create mode 100644 src/assets/images/chat/deleteaudio.png create mode 100644 src/assets/images/chat/equis.png create mode 100644 src/assets/images/chat/mas.png create mode 100644 src/assets/images/chat/microphone-off.png create mode 100644 src/assets/images/chat/microphone-on.png create mode 100644 src/assets/images/chat/sticker.png create mode 100644 src/assets/images/chat/stickers-icon.png create mode 100644 src/assets/images/chat/styles-icon.png create mode 100644 src/assets/images/floorplaneditor/door-direction-0.png create mode 100644 src/assets/images/floorplaneditor/door-direction-1.png create mode 100644 src/assets/images/floorplaneditor/door-direction-2.png create mode 100644 src/assets/images/floorplaneditor/door-direction-3.png create mode 100644 src/assets/images/floorplaneditor/door-direction-4.png create mode 100644 src/assets/images/floorplaneditor/door-direction-5.png create mode 100644 src/assets/images/floorplaneditor/door-direction-6.png create mode 100644 src/assets/images/floorplaneditor/door-direction-7.png create mode 100644 src/assets/images/floorplaneditor/icon-door.png create mode 100644 src/assets/images/floorplaneditor/icon-tile-down.png create mode 100644 src/assets/images/floorplaneditor/icon-tile-set.png create mode 100644 src/assets/images/floorplaneditor/icon-tile-unset.png create mode 100644 src/assets/images/floorplaneditor/icon-tile-up.png create mode 100644 src/assets/images/floorplaneditor/preview_tile.png create mode 100644 src/assets/images/floorplaneditor/selected_height_icon.png create mode 100644 src/assets/images/friends/friends-spritesheet.png create mode 100644 src/assets/images/friends/icon-accept.png create mode 100644 src/assets/images/friends/icon-add.png create mode 100644 src/assets/images/friends/icon-bobba.png create mode 100644 src/assets/images/friends/icon-chat.png create mode 100644 src/assets/images/friends/icon-deny.png create mode 100644 src/assets/images/friends/icon-follow.png create mode 100644 src/assets/images/friends/icon-friendbar-chat.png create mode 100644 src/assets/images/friends/icon-friendbar-visit.png create mode 100644 src/assets/images/friends/icon-heart.png create mode 100644 src/assets/images/friends/icon-new-message.png create mode 100644 src/assets/images/friends/icon-none.png create mode 100644 src/assets/images/friends/icon-profile-sm-hover.png create mode 100644 src/assets/images/friends/icon-profile-sm.png create mode 100644 src/assets/images/friends/icon-profile.png create mode 100644 src/assets/images/friends/icon-smile.png create mode 100644 src/assets/images/friends/icon-warning.png create mode 100644 src/assets/images/friends/messenger_notification_icon.png create mode 100644 src/assets/images/gamecenter/selectedIcon.png create mode 100644 src/assets/images/gift/gift_tag.png create mode 100644 src/assets/images/gift/incognito.png create mode 100644 src/assets/images/groups/creator_images.png create mode 100644 src/assets/images/groups/creator_tabs.png create mode 100644 src/assets/images/groups/icons/group_decorate_icon.png create mode 100644 src/assets/images/groups/icons/group_favorite.png create mode 100644 src/assets/images/groups/icons/group_icon_admin.png create mode 100644 src/assets/images/groups/icons/group_icon_big_admin.png create mode 100644 src/assets/images/groups/icons/group_icon_big_member.png create mode 100644 src/assets/images/groups/icons/group_icon_big_owner.png create mode 100644 src/assets/images/groups/icons/group_icon_not_admin.png create mode 100644 src/assets/images/groups/icons/group_icon_small_owner.png create mode 100644 src/assets/images/groups/icons/group_notfavorite.png create mode 100644 src/assets/images/groups/icons/grouptype_icon_0.png create mode 100644 src/assets/images/groups/icons/grouptype_icon_1.png create mode 100644 src/assets/images/groups/icons/grouptype_icon_2.png create mode 100644 src/assets/images/groups/no-group-1.png create mode 100644 src/assets/images/groups/no-group-2.png create mode 100644 src/assets/images/groups/no-group-3.png create mode 100644 src/assets/images/groups/no-group-spritesheet.png create mode 100644 src/assets/images/guide-tool/guide_tool_duty_switch.png create mode 100644 src/assets/images/guide-tool/guide_tool_info_icon.png create mode 100644 src/assets/images/hc-center/benefits.png create mode 100644 src/assets/images/hc-center/clock.png create mode 100644 src/assets/images/hc-center/hc_logo.gif create mode 100644 src/assets/images/hc-center/payday.png create mode 100644 src/assets/images/help/help_index.png create mode 100644 src/assets/images/icons/arrows.png create mode 100644 src/assets/images/icons/builderstool.png create mode 100644 src/assets/images/icons/camera-colormatrix.png create mode 100644 src/assets/images/icons/camera-composite.png create mode 100644 src/assets/images/icons/camera-small.png create mode 100644 src/assets/images/icons/chat-history.png create mode 100644 src/assets/images/icons/close.png create mode 100644 src/assets/images/icons/cog.png create mode 100644 src/assets/images/icons/disablebubble.png create mode 100644 src/assets/images/icons/enablebubble.png create mode 100644 src/assets/images/icons/help.png create mode 100644 src/assets/images/icons/house-small.png create mode 100644 src/assets/images/icons/icon_cog.png create mode 100644 src/assets/images/icons/like-room.png create mode 100644 src/assets/images/icons/loading-icon.png create mode 100644 src/assets/images/icons/room-history-back-disabled.png create mode 100644 src/assets/images/icons/room-history-back-enabled.png create mode 100644 src/assets/images/icons/room-history-disabled.png create mode 100644 src/assets/images/icons/room-history-enabled.png create mode 100644 src/assets/images/icons/room-history-next-disabled.png create mode 100644 src/assets/images/icons/room-history-next-enabled.png create mode 100644 src/assets/images/icons/room-link.png create mode 100644 src/assets/images/icons/sign-exclamation.png create mode 100644 src/assets/images/icons/sign-heart.png create mode 100644 src/assets/images/icons/sign-red.png create mode 100644 src/assets/images/icons/sign-skull.png create mode 100644 src/assets/images/icons/sign-smile.png create mode 100644 src/assets/images/icons/sign-soccer.png create mode 100644 src/assets/images/icons/sign-yellow.png create mode 100644 src/assets/images/icons/small-room.png create mode 100644 src/assets/images/icons/tickets.png create mode 100644 src/assets/images/icons/user.png create mode 100644 src/assets/images/icons/zoom-less.png create mode 100644 src/assets/images/icons/zoom-more.png create mode 100644 src/assets/images/infostand/bot_background.png create mode 100644 src/assets/images/infostand/countown-timer.png create mode 100644 src/assets/images/infostand/disk-creator.png create mode 100644 src/assets/images/infostand/disk-icon.png create mode 100644 src/assets/images/infostand/pencil-icon.png create mode 100644 src/assets/images/infostand/rarity-level.png create mode 100644 src/assets/images/inventory/empty.png create mode 100644 src/assets/images/inventory/rarity-level.png create mode 100644 src/assets/images/inventory/trading/locked-icon.png create mode 100644 src/assets/images/inventory/trading/unlocked-icon.png create mode 100644 src/assets/images/loading/caja.gif create mode 100644 src/assets/images/loading/connecting-duck-spritesheet.png create mode 100644 src/assets/images/loading/connecting_duck_01.png create mode 100644 src/assets/images/loading/connecting_duck_02.png create mode 100644 src/assets/images/loading/connecting_duck_03.png create mode 100644 src/assets/images/loading/connecting_duck_04.png create mode 100644 src/assets/images/loading/connecting_duck_05.png create mode 100644 src/assets/images/loading/connecting_duck_06.png create mode 100644 src/assets/images/loading/connecting_duck_07.png create mode 100644 src/assets/images/loading/progress_habbos.gif create mode 100644 src/assets/images/modtool/chatlog.gif create mode 100644 src/assets/images/modtool/key.gif create mode 100644 src/assets/images/modtool/m_icon.png create mode 100644 src/assets/images/modtool/reports.png create mode 100644 src/assets/images/modtool/room.gif create mode 100644 src/assets/images/modtool/room.png create mode 100644 src/assets/images/modtool/user.gif create mode 100644 src/assets/images/modtool/wrench.gif create mode 100644 src/assets/images/mysterybox/chain_mysterybox_box_overlay.png create mode 100644 src/assets/images/mysterybox/key_overlay.png create mode 100644 src/assets/images/mysterybox/mystery_box.png create mode 100644 src/assets/images/mysterybox/mystery_box_key.png create mode 100644 src/assets/images/mysterytrophy/frank_mystery_trophy.png create mode 100644 src/assets/images/navigator/icons/info.png create mode 100644 src/assets/images/navigator/icons/room_group.png create mode 100644 src/assets/images/navigator/icons/room_invisible.png create mode 100644 src/assets/images/navigator/icons/room_locked.png create mode 100644 src/assets/images/navigator/icons/room_password.png create mode 100644 src/assets/images/navigator/models/model_0.png create mode 100644 src/assets/images/navigator/models/model_1.png create mode 100644 src/assets/images/navigator/models/model_2.png create mode 100644 src/assets/images/navigator/models/model_3.png create mode 100644 src/assets/images/navigator/models/model_4.png create mode 100644 src/assets/images/navigator/models/model_5.png create mode 100644 src/assets/images/navigator/models/model_6.png create mode 100644 src/assets/images/navigator/models/model_7.png create mode 100644 src/assets/images/navigator/models/model_8.png create mode 100644 src/assets/images/navigator/models/model_9.png create mode 100644 src/assets/images/navigator/models/model_a.png create mode 100644 src/assets/images/navigator/models/model_b.png create mode 100644 src/assets/images/navigator/models/model_c.png create mode 100644 src/assets/images/navigator/models/model_d.png create mode 100644 src/assets/images/navigator/models/model_e.png create mode 100644 src/assets/images/navigator/models/model_f.png create mode 100644 src/assets/images/navigator/models/model_g.png create mode 100644 src/assets/images/navigator/models/model_h.png create mode 100644 src/assets/images/navigator/models/model_i.png create mode 100644 src/assets/images/navigator/models/model_j.png create mode 100644 src/assets/images/navigator/models/model_k.png create mode 100644 src/assets/images/navigator/models/model_l.png create mode 100644 src/assets/images/navigator/models/model_m.png create mode 100644 src/assets/images/navigator/models/model_n.png create mode 100644 src/assets/images/navigator/models/model_o.png create mode 100644 src/assets/images/navigator/models/model_p.png create mode 100644 src/assets/images/navigator/models/model_q.png create mode 100644 src/assets/images/navigator/models/model_r.png create mode 100644 src/assets/images/navigator/models/model_snowwar1.png create mode 100644 src/assets/images/navigator/models/model_snowwar2.png create mode 100644 src/assets/images/navigator/models/model_t.png create mode 100644 src/assets/images/navigator/models/model_u.png create mode 100644 src/assets/images/navigator/models/model_v.png create mode 100644 src/assets/images/navigator/models/model_w.png create mode 100644 src/assets/images/navigator/models/model_x.png create mode 100644 src/assets/images/navigator/models/model_y.png create mode 100644 src/assets/images/navigator/models/model_z.png create mode 100644 src/assets/images/navigator/thumbnail_placeholder.png create mode 100644 src/assets/images/nitro/nitro-dark.svg create mode 100644 src/assets/images/nitro/nitro-light.svg create mode 100644 src/assets/images/nitro/nitro-n-dark.svg create mode 100644 src/assets/images/nitro/nitro-n-light.svg create mode 100644 src/assets/images/notifications/frank.gif create mode 100644 src/assets/images/pets/pet-package/gnome.png create mode 100644 src/assets/images/pets/pet-package/leprechaun_box.png create mode 100644 src/assets/images/pets/pet-package/petbox_epic.png create mode 100644 src/assets/images/pets/pet-package/pterosaur_egg.png create mode 100644 src/assets/images/pets/pet-package/val11_present.png create mode 100644 src/assets/images/pets/pet-package/velociraptor_egg.png create mode 100644 src/assets/images/prize/prize_background.png create mode 100644 src/assets/images/profile/icons/offline.png create mode 100644 src/assets/images/profile/icons/online.gif create mode 100644 src/assets/images/profile/icons/tick.png create mode 100644 src/assets/images/room-spectator/room_spectator_bottom_left.png create mode 100644 src/assets/images/room-spectator/room_spectator_bottom_right.png create mode 100644 src/assets/images/room-spectator/room_spectator_middle_bottom.png create mode 100644 src/assets/images/room-spectator/room_spectator_middle_left.png create mode 100644 src/assets/images/room-spectator/room_spectator_middle_right.png create mode 100644 src/assets/images/room-spectator/room_spectator_middle_top.png create mode 100644 src/assets/images/room-spectator/room_spectator_top_left.png create mode 100644 src/assets/images/room-spectator/room_spectator_top_right.png create mode 100644 src/assets/images/room-widgets/avatar-info/preview-background.png create mode 100644 src/assets/images/room-widgets/camera-widget/btn.png create mode 100644 src/assets/images/room-widgets/camera-widget/btn_down.png create mode 100644 src/assets/images/room-widgets/camera-widget/btn_hi.png create mode 100644 src/assets/images/room-widgets/camera-widget/cam_bg.png create mode 100644 src/assets/images/room-widgets/camera-widget/camera-spritesheet.png create mode 100644 src/assets/images/room-widgets/camera-widget/viewfinder.png create mode 100644 src/assets/images/room-widgets/dimmer-widget/dimmer_banner.png create mode 100644 src/assets/images/room-widgets/engraving-lock-widget/engraving-lock-spritesheet.png create mode 100644 src/assets/images/room-widgets/exchange-credit/exchange-credit-image.png create mode 100644 src/assets/images/room-widgets/furni-context-menu/monsterplant-preview.png create mode 100644 src/assets/images/room-widgets/mannequin-widget/mannequin-spritesheet.png create mode 100644 src/assets/images/room-widgets/playlist-editor/disk_2.png create mode 100644 src/assets/images/room-widgets/playlist-editor/disk_image.png create mode 100644 src/assets/images/room-widgets/playlist-editor/move.png create mode 100644 src/assets/images/room-widgets/playlist-editor/pause-btn.png create mode 100644 src/assets/images/room-widgets/playlist-editor/pause.png create mode 100644 src/assets/images/room-widgets/playlist-editor/playing.png create mode 100644 src/assets/images/room-widgets/playlist-editor/preview.png create mode 100644 src/assets/images/room-widgets/stickie-widget/stickie-blue.png create mode 100644 src/assets/images/room-widgets/stickie-widget/stickie-christmas.png create mode 100644 src/assets/images/room-widgets/stickie-widget/stickie-close.png create mode 100644 src/assets/images/room-widgets/stickie-widget/stickie-dreams.png create mode 100644 src/assets/images/room-widgets/stickie-widget/stickie-green.png create mode 100644 src/assets/images/room-widgets/stickie-widget/stickie-heart.png create mode 100644 src/assets/images/room-widgets/stickie-widget/stickie-juninas.png create mode 100644 src/assets/images/room-widgets/stickie-widget/stickie-pink.png create mode 100644 src/assets/images/room-widgets/stickie-widget/stickie-shakesp.png create mode 100644 src/assets/images/room-widgets/stickie-widget/stickie-spritesheet.png create mode 100644 src/assets/images/room-widgets/stickie-widget/stickie-trash.png create mode 100644 src/assets/images/room-widgets/stickie-widget/stickie-yellow.png create mode 100644 src/assets/images/room-widgets/thumbnail-widget/thumbnail-camera-spritesheet.png create mode 100644 src/assets/images/room-widgets/trophy-widget/trophy-spritesheet.png create mode 100644 src/assets/images/room-widgets/wordquiz-widget/thumbs-down-small.png create mode 100644 src/assets/images/room-widgets/wordquiz-widget/thumbs-down.png create mode 100644 src/assets/images/room-widgets/wordquiz-widget/thumbs-up-small.png create mode 100644 src/assets/images/room-widgets/wordquiz-widget/thumbs-up.png create mode 100644 src/assets/images/room-widgets/youtube-widget/next.png create mode 100644 src/assets/images/room-widgets/youtube-widget/prev.png create mode 100644 src/assets/images/stackhelper/slider-background.png create mode 100644 src/assets/images/stackhelper/slider-pointer.png create mode 100644 src/assets/images/toolbar/arrow.png create mode 100644 src/assets/images/toolbar/friend-search.png create mode 100644 src/assets/images/toolbar/icons/buildersclub.png create mode 100644 src/assets/images/toolbar/icons/camera.png create mode 100644 src/assets/images/toolbar/icons/catalog.png create mode 100644 src/assets/images/toolbar/icons/friend_all.png create mode 100644 src/assets/images/toolbar/icons/friend_head.png create mode 100644 src/assets/images/toolbar/icons/friend_search.png create mode 100644 src/assets/images/toolbar/icons/game.png create mode 100644 src/assets/images/toolbar/icons/habbo.png create mode 100644 src/assets/images/toolbar/icons/house.png create mode 100644 src/assets/images/toolbar/icons/inventory.png create mode 100644 src/assets/images/toolbar/icons/joinroom.png create mode 100644 src/assets/images/toolbar/icons/me-menu/achievements.png create mode 100644 src/assets/images/toolbar/icons/me-menu/clothing.png create mode 100644 src/assets/images/toolbar/icons/me-menu/cog.png create mode 100644 src/assets/images/toolbar/icons/me-menu/forums.png create mode 100644 src/assets/images/toolbar/icons/me-menu/helper-tool.png create mode 100644 src/assets/images/toolbar/icons/me-menu/my-rooms.png create mode 100644 src/assets/images/toolbar/icons/me-menu/profile.png create mode 100644 src/assets/images/toolbar/icons/me-menu/rooms.png create mode 100644 src/assets/images/toolbar/icons/me-menu/talents.png create mode 100644 src/assets/images/toolbar/icons/message.png create mode 100644 src/assets/images/toolbar/icons/message_unsee.gif create mode 100644 src/assets/images/toolbar/icons/modtools.png create mode 100644 src/assets/images/toolbar/icons/rooms.png create mode 100644 src/assets/images/toolbar/icons/sendmessage.png create mode 100644 src/assets/images/unique/catalog-info-amount-bg.png create mode 100644 src/assets/images/unique/catalog-info-sold-out.png create mode 100644 src/assets/images/unique/grid-bg-glass.png create mode 100644 src/assets/images/unique/grid-bg-sold-out.png create mode 100644 src/assets/images/unique/grid-bg.png create mode 100644 src/assets/images/unique/grid-count-bg.png create mode 100644 src/assets/images/unique/inventory-info-amount-bg.png create mode 100644 src/assets/images/unique/numbers.png create mode 100644 src/assets/images/wired/card-action-corners.png create mode 100644 src/assets/images/wired/icon_action.png create mode 100644 src/assets/images/wired/icon_condition.png create mode 100644 src/assets/images/wired/icon_trigger.png create mode 100644 src/assets/images/wired/icon_wired_around.png create mode 100644 src/assets/images/wired/icon_wired_left_right.png create mode 100644 src/assets/images/wired/icon_wired_north_east.png create mode 100644 src/assets/images/wired/icon_wired_north_west.png create mode 100644 src/assets/images/wired/icon_wired_rotate_clockwise.png create mode 100644 src/assets/images/wired/icon_wired_rotate_counter_clockwise.png create mode 100644 src/assets/images/wired/icon_wired_south_east.png create mode 100644 src/assets/images/wired/icon_wired_south_west.png create mode 100644 src/assets/images/wired/icon_wired_up_down.png create mode 100644 src/assets/styles/bootstrap/_accordion.scss create mode 100644 src/assets/styles/bootstrap/_alert.scss create mode 100644 src/assets/styles/bootstrap/_badge.scss create mode 100644 src/assets/styles/bootstrap/_breadcrumb.scss create mode 100644 src/assets/styles/bootstrap/_button-group.scss create mode 100644 src/assets/styles/bootstrap/_buttons.scss create mode 100644 src/assets/styles/bootstrap/_card.scss create mode 100644 src/assets/styles/bootstrap/_carousel.scss create mode 100644 src/assets/styles/bootstrap/_close.scss create mode 100644 src/assets/styles/bootstrap/_containers.scss create mode 100644 src/assets/styles/bootstrap/_dropdown.scss create mode 100644 src/assets/styles/bootstrap/_forms.scss create mode 100644 src/assets/styles/bootstrap/_functions.scss create mode 100644 src/assets/styles/bootstrap/_grid.scss create mode 100644 src/assets/styles/bootstrap/_helpers.scss create mode 100644 src/assets/styles/bootstrap/_images.scss create mode 100644 src/assets/styles/bootstrap/_list-group.scss create mode 100644 src/assets/styles/bootstrap/_mixins.scss create mode 100644 src/assets/styles/bootstrap/_modal.scss create mode 100644 src/assets/styles/bootstrap/_nav.scss create mode 100644 src/assets/styles/bootstrap/_navbar.scss create mode 100644 src/assets/styles/bootstrap/_offcanvas.scss create mode 100644 src/assets/styles/bootstrap/_pagination.scss create mode 100644 src/assets/styles/bootstrap/_placeholders.scss create mode 100644 src/assets/styles/bootstrap/_popover.scss create mode 100644 src/assets/styles/bootstrap/_progress.scss create mode 100644 src/assets/styles/bootstrap/_reboot.scss create mode 100644 src/assets/styles/bootstrap/_root.scss create mode 100644 src/assets/styles/bootstrap/_spinners.scss create mode 100644 src/assets/styles/bootstrap/_tables.scss create mode 100644 src/assets/styles/bootstrap/_toasts.scss create mode 100644 src/assets/styles/bootstrap/_tooltip.scss create mode 100644 src/assets/styles/bootstrap/_transitions.scss create mode 100644 src/assets/styles/bootstrap/_type.scss create mode 100644 src/assets/styles/bootstrap/_utilities.scss create mode 100644 src/assets/styles/bootstrap/_variables.scss create mode 100644 src/assets/styles/bootstrap/bootstrap-grid.scss create mode 100644 src/assets/styles/bootstrap/bootstrap-reboot.scss create mode 100644 src/assets/styles/bootstrap/bootstrap-utilities.scss create mode 100644 src/assets/styles/bootstrap/bootstrap.scss create mode 100644 src/assets/styles/bootstrap/forms/_floating-labels.scss create mode 100644 src/assets/styles/bootstrap/forms/_form-check.scss create mode 100644 src/assets/styles/bootstrap/forms/_form-control.scss create mode 100644 src/assets/styles/bootstrap/forms/_form-range.scss create mode 100644 src/assets/styles/bootstrap/forms/_form-select.scss create mode 100644 src/assets/styles/bootstrap/forms/_form-text.scss create mode 100644 src/assets/styles/bootstrap/forms/_input-group.scss create mode 100644 src/assets/styles/bootstrap/forms/_labels.scss create mode 100644 src/assets/styles/bootstrap/forms/_validation.scss create mode 100644 src/assets/styles/bootstrap/helpers/_clearfix.scss create mode 100644 src/assets/styles/bootstrap/helpers/_colored-links.scss create mode 100644 src/assets/styles/bootstrap/helpers/_position.scss create mode 100644 src/assets/styles/bootstrap/helpers/_ratio.scss create mode 100644 src/assets/styles/bootstrap/helpers/_stacks.scss create mode 100644 src/assets/styles/bootstrap/helpers/_stretched-link.scss create mode 100644 src/assets/styles/bootstrap/helpers/_text-truncation.scss create mode 100644 src/assets/styles/bootstrap/helpers/_visually-hidden.scss create mode 100644 src/assets/styles/bootstrap/helpers/_vr.scss create mode 100644 src/assets/styles/bootstrap/mixins/_alert.scss create mode 100644 src/assets/styles/bootstrap/mixins/_backdrop.scss create mode 100644 src/assets/styles/bootstrap/mixins/_border-radius.scss create mode 100644 src/assets/styles/bootstrap/mixins/_box-shadow.scss create mode 100644 src/assets/styles/bootstrap/mixins/_breakpoints.scss create mode 100644 src/assets/styles/bootstrap/mixins/_buttons.scss create mode 100644 src/assets/styles/bootstrap/mixins/_caret.scss create mode 100644 src/assets/styles/bootstrap/mixins/_clearfix.scss create mode 100644 src/assets/styles/bootstrap/mixins/_color-scheme.scss create mode 100644 src/assets/styles/bootstrap/mixins/_container.scss create mode 100644 src/assets/styles/bootstrap/mixins/_deprecate.scss create mode 100644 src/assets/styles/bootstrap/mixins/_forms.scss create mode 100644 src/assets/styles/bootstrap/mixins/_gradients.scss create mode 100644 src/assets/styles/bootstrap/mixins/_grid.scss create mode 100644 src/assets/styles/bootstrap/mixins/_image.scss create mode 100644 src/assets/styles/bootstrap/mixins/_list-group.scss create mode 100644 src/assets/styles/bootstrap/mixins/_lists.scss create mode 100644 src/assets/styles/bootstrap/mixins/_pagination.scss create mode 100644 src/assets/styles/bootstrap/mixins/_reset-text.scss create mode 100644 src/assets/styles/bootstrap/mixins/_resize.scss create mode 100644 src/assets/styles/bootstrap/mixins/_table-variants.scss create mode 100644 src/assets/styles/bootstrap/mixins/_text-truncate.scss create mode 100644 src/assets/styles/bootstrap/mixins/_transition.scss create mode 100644 src/assets/styles/bootstrap/mixins/_utilities.scss create mode 100644 src/assets/styles/bootstrap/mixins/_visually-hidden.scss create mode 100644 src/assets/styles/bootstrap/utilities/_api.scss create mode 100644 src/assets/styles/bootstrap/vendor/_rfs.scss create mode 100644 src/assets/styles/fonts.scss create mode 100644 src/assets/styles/icons.scss create mode 100644 src/assets/styles/index.scss create mode 100644 src/assets/styles/scrollbars.scss create mode 100644 src/assets/styles/slider.scss create mode 100644 src/assets/styles/utils.scss create mode 100644 src/assets/webfonts/Ubuntu-C.ttf create mode 100644 src/assets/webfonts/Ubuntu-b.ttf create mode 100644 src/assets/webfonts/Ubuntu-i.ttf create mode 100644 src/assets/webfonts/Ubuntu-ib.ttf create mode 100644 src/assets/webfonts/Ubuntu-m.ttf create mode 100644 src/assets/webfonts/Ubuntu.ttf create mode 100644 src/common/AutoGrid.tsx create mode 100644 src/common/Base.tsx create mode 100644 src/common/Button.tsx create mode 100644 src/common/ButtonGroup.tsx create mode 100644 src/common/Column.tsx create mode 100644 src/common/Flex.tsx create mode 100644 src/common/FormGroup.tsx create mode 100644 src/common/Grid.tsx create mode 100644 src/common/GridContext.tsx create mode 100644 src/common/HorizontalRule.tsx create mode 100644 src/common/InfiniteScroll.tsx create mode 100644 src/common/Text.tsx create mode 100644 src/common/card/NitroCardContentView.tsx create mode 100644 src/common/card/NitroCardContext.tsx create mode 100644 src/common/card/NitroCardHeaderView.tsx create mode 100644 src/common/card/NitroCardSubHeaderView.tsx create mode 100644 src/common/card/NitroCardView.scss create mode 100644 src/common/card/NitroCardView.tsx create mode 100644 src/common/card/accordion/NitroCardAccordionContext.tsx create mode 100644 src/common/card/accordion/NitroCardAccordionItemView.tsx create mode 100644 src/common/card/accordion/NitroCardAccordionSetView.tsx create mode 100644 src/common/card/accordion/NitroCardAccordionView.tsx create mode 100644 src/common/card/accordion/index.ts create mode 100644 src/common/card/index.ts create mode 100644 src/common/card/tabs/NitroCardTabsItemView.tsx create mode 100644 src/common/card/tabs/NitroCardTabsView.tsx create mode 100644 src/common/card/tabs/index.ts create mode 100644 src/common/classNames.ts create mode 100644 src/common/draggable-window/DraggableWindow.tsx create mode 100644 src/common/draggable-window/DraggableWindowPosition.ts create mode 100644 src/common/draggable-window/index.ts create mode 100644 src/common/index.scss create mode 100644 src/common/index.ts create mode 100644 src/common/layout/LayoutAvatarImageView.tsx create mode 100644 src/common/layout/LayoutBackgroundImage.tsx create mode 100644 src/common/layout/LayoutBadgeImageView.tsx create mode 100644 src/common/layout/LayoutCounterTimeView.tsx create mode 100644 src/common/layout/LayoutCurrencyIcon.tsx create mode 100644 src/common/layout/LayoutFurniIconImageView.tsx create mode 100644 src/common/layout/LayoutFurniImageView.tsx create mode 100644 src/common/layout/LayoutGiftTagView.tsx create mode 100644 src/common/layout/LayoutGridItem.tsx create mode 100644 src/common/layout/LayoutImage.tsx create mode 100644 src/common/layout/LayoutItemCountView.tsx create mode 100644 src/common/layout/LayoutLoadingSpinnerView.tsx create mode 100644 src/common/layout/LayoutMiniCameraView.tsx create mode 100644 src/common/layout/LayoutNotificationAlertView.tsx create mode 100644 src/common/layout/LayoutNotificationBubbleView.tsx create mode 100644 src/common/layout/LayoutPetImageView.tsx create mode 100644 src/common/layout/LayoutPrizeProductImageView.tsx create mode 100644 src/common/layout/LayoutProgressBar.tsx create mode 100644 src/common/layout/LayoutRarityLevelView.tsx create mode 100644 src/common/layout/LayoutRoomPreviewerView.tsx create mode 100644 src/common/layout/LayoutRoomThumbnailView.tsx create mode 100644 src/common/layout/LayoutTrophyView.tsx create mode 100644 src/common/layout/UserProfileIconView.tsx create mode 100644 src/common/layout/index.ts create mode 100644 src/common/layout/limited-edition/LayoutLimitedEditionCompactPlateView.tsx create mode 100644 src/common/layout/limited-edition/LayoutLimitedEditionCompletePlateView.tsx create mode 100644 src/common/layout/limited-edition/LayoutLimitedEditionStyledNumberView.tsx create mode 100644 src/common/layout/limited-edition/index.ts create mode 100644 src/common/transitions/TransitionAnimation.tsx create mode 100644 src/common/transitions/TransitionAnimationStyles.ts create mode 100644 src/common/transitions/TransitionAnimationTypes.ts create mode 100644 src/common/transitions/index.ts create mode 100644 src/common/types/AlignItemType.ts create mode 100644 src/common/types/AlignSelfType.ts create mode 100644 src/common/types/ButtonSizeType.ts create mode 100644 src/common/types/ColorVariantType.ts create mode 100644 src/common/types/ColumnSizesType.ts create mode 100644 src/common/types/DisplayType.ts create mode 100644 src/common/types/FloatType.ts create mode 100644 src/common/types/FontSizeType.ts create mode 100644 src/common/types/FontWeightType.ts create mode 100644 src/common/types/JustifyContentType.ts create mode 100644 src/common/types/OverflowType.ts create mode 100644 src/common/types/PositionType.ts create mode 100644 src/common/types/SpacingType.ts create mode 100644 src/common/types/TextAlignType.ts create mode 100644 src/common/types/index.ts create mode 100644 src/common/utils/CreateTransitionToIcon.ts create mode 100644 src/common/utils/FriendlyTimeView.tsx create mode 100644 src/common/utils/index.ts create mode 100644 src/components/achievements/AchievementsView.scss create mode 100644 src/components/achievements/AchievementsView.tsx create mode 100644 src/components/achievements/views/AchievementBadgeView.tsx create mode 100644 src/components/achievements/views/AchievementCategoryView.tsx create mode 100644 src/components/achievements/views/AchievementDetailsView.tsx create mode 100644 src/components/achievements/views/achievement-list/AchievementListItemView.tsx create mode 100644 src/components/achievements/views/achievement-list/AchievementListView.tsx create mode 100644 src/components/achievements/views/achievement-list/index.ts create mode 100644 src/components/achievements/views/category-list/AchievementsCategoryListItemView.tsx create mode 100644 src/components/achievements/views/category-list/AchievementsCategoryListView.tsx create mode 100644 src/components/achievements/views/category-list/index.ts create mode 100644 src/components/achievements/views/index.ts create mode 100644 src/components/avatar-editor/AvatarEditorView.scss create mode 100644 src/components/avatar-editor/AvatarEditorView.tsx create mode 100644 src/components/avatar-editor/views/AvatarEditorFigurePreviewView.tsx create mode 100644 src/components/avatar-editor/views/AvatarEditorIcon.tsx create mode 100644 src/components/avatar-editor/views/AvatarEditorModelView.tsx create mode 100644 src/components/avatar-editor/views/AvatarEditorWardrobeView.tsx create mode 100644 src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetItemView.tsx create mode 100644 src/components/avatar-editor/views/figure-set/AvatarEditorFigureSetView.tsx create mode 100644 src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetItemView.tsx create mode 100644 src/components/avatar-editor/views/palette-set/AvatarEditorPaletteSetView.tsx create mode 100644 src/components/camera/CameraWidgetView.scss create mode 100644 src/components/camera/CameraWidgetView.tsx create mode 100644 src/components/camera/views/CameraWidgetCaptureView.tsx create mode 100644 src/components/camera/views/CameraWidgetCheckoutView.tsx create mode 100644 src/components/camera/views/CameraWidgetShowPhotoView.tsx create mode 100644 src/components/camera/views/editor/CameraWidgetEditorView.tsx create mode 100644 src/components/camera/views/editor/effect-list/CameraWidgetEffectListItemView.tsx create mode 100644 src/components/camera/views/editor/effect-list/CameraWidgetEffectListView.tsx create mode 100644 src/components/campaign/CalendarItemView.tsx create mode 100644 src/components/campaign/CalendarView.tsx create mode 100644 src/components/campaign/CampaignView.scss create mode 100644 src/components/campaign/CampaignView.tsx create mode 100644 src/components/catalog/CatalogView.scss create mode 100644 src/components/catalog/CatalogView.tsx create mode 100644 src/components/catalog/views/CatalogPurchaseConfirmView.tsx create mode 100644 src/components/catalog/views/catalog-header/CatalogHeaderView.tsx create mode 100644 src/components/catalog/views/catalog-icon/CatalogIconView.tsx create mode 100644 src/components/catalog/views/catalog-room-previewer/CatalogRoomPreviewerView.tsx create mode 100644 src/components/catalog/views/gift/CatalogGiftView.tsx create mode 100644 src/components/catalog/views/navigation/CatalogNavigationItemView.tsx create mode 100644 src/components/catalog/views/navigation/CatalogNavigationSetView.tsx create mode 100644 src/components/catalog/views/navigation/CatalogNavigationView.tsx create mode 100644 src/components/catalog/views/page/common/CatalogGridOfferView.tsx create mode 100644 src/components/catalog/views/page/common/CatalogRedeemVoucherView.tsx create mode 100644 src/components/catalog/views/page/common/CatalogSearchView.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayout.types.ts create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutBadgeDisplayView.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutColorGroupingView.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutDefaultView.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutGuildCustomFurniView.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutGuildForumView.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutGuildFrontpageView.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutInfoLoyaltyView.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutPets2View.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutPets3View.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutRoomAdsView.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutRoomBundleView.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutSingleBundleView.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutSoundMachineView.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutSpacesView.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutTrophiesView.tsx create mode 100644 src/components/catalog/views/page/layout/CatalogLayoutVipBuyView.tsx create mode 100644 src/components/catalog/views/page/layout/GetCatalogLayout.tsx create mode 100644 src/components/catalog/views/page/layout/frontpage4/CatalogLayoutFrontPageItemView.tsx create mode 100644 src/components/catalog/views/page/layout/frontpage4/CatalogLayoutFrontpage4View.tsx create mode 100644 src/components/catalog/views/page/layout/marketplace/CatalogLayoutMarketplaceItemView.tsx create mode 100644 src/components/catalog/views/page/layout/marketplace/CatalogLayoutMarketplaceOwnItemsView.tsx create mode 100644 src/components/catalog/views/page/layout/marketplace/CatalogLayoutMarketplacePublicItemsView.tsx create mode 100644 src/components/catalog/views/page/layout/marketplace/CatalogLayoutMarketplaceSearchFormView.tsx create mode 100644 src/components/catalog/views/page/layout/marketplace/MarketplacePostOfferView.tsx create mode 100644 src/components/catalog/views/page/layout/pets/CatalogLayoutPetView.tsx create mode 100644 src/components/catalog/views/page/layout/vip-gifts/CatalogLayoutVipGiftsView.tsx create mode 100644 src/components/catalog/views/page/layout/vip-gifts/VipGiftItemView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogAddOnBadgeWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogBadgeSelectorWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogBundleGridWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogFirstProductSelectorWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogGuildBadgeWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogGuildSelectorWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogItemGridWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogLimitedItemWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogPriceDisplayWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogSimplePriceWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogSingleViewWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogSpacesWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogSpinnerWidgetView.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogTotalPriceWidget.tsx create mode 100644 src/components/catalog/views/page/widgets/CatalogViewProductWidgetView.tsx create mode 100644 src/components/catalog/views/targeted-offer/Offer.scss create mode 100644 src/components/catalog/views/targeted-offer/OfferBubbleView.tsx create mode 100644 src/components/catalog/views/targeted-offer/OfferView.tsx create mode 100644 src/components/catalog/views/targeted-offer/OfferWindowView.tsx create mode 100644 src/components/chat-history/ChatHistoryView.scss create mode 100644 src/components/chat-history/ChatHistoryView.tsx create mode 100644 src/components/floorplan-editor/FloorplanEditorContext.tsx create mode 100644 src/components/floorplan-editor/FloorplanEditorView.scss create mode 100644 src/components/floorplan-editor/FloorplanEditorView.tsx create mode 100644 src/components/floorplan-editor/common/ActionSettings.ts create mode 100644 src/components/floorplan-editor/common/Constants.ts create mode 100644 src/components/floorplan-editor/common/ConvertMapToString.ts create mode 100644 src/components/floorplan-editor/common/FloorplanEditor.ts create mode 100644 src/components/floorplan-editor/common/IFloorplanSettings.ts create mode 100644 src/components/floorplan-editor/common/IVisualizationSettings.ts create mode 100644 src/components/floorplan-editor/common/Tile.ts create mode 100644 src/components/floorplan-editor/common/Utils.ts create mode 100644 src/components/floorplan-editor/views/FloorplanCanvasView.tsx create mode 100644 src/components/floorplan-editor/views/FloorplanImportExportView.tsx create mode 100644 src/components/floorplan-editor/views/FloorplanOptionsView.tsx create mode 100644 src/components/friends/FriendsView.scss create mode 100644 src/components/friends/FriendsView.tsx create mode 100644 src/components/friends/views/friends-bar/FriendBarItemView.tsx create mode 100644 src/components/friends/views/friends-bar/FriendsBarView.tsx create mode 100644 src/components/friends/views/friends-list/FriendsListRemoveConfirmationView.tsx create mode 100644 src/components/friends/views/friends-list/FriendsListRoomInviteView.tsx create mode 100644 src/components/friends/views/friends-list/FriendsListSearchView.tsx create mode 100644 src/components/friends/views/friends-list/FriendsListView.tsx create mode 100644 src/components/friends/views/friends-list/friends-list-group/FriendsListGroupItemView.tsx create mode 100644 src/components/friends/views/friends-list/friends-list-group/FriendsListGroupView.tsx create mode 100644 src/components/friends/views/friends-list/friends-list-request/FriendsListRequestItemView.tsx create mode 100644 src/components/friends/views/friends-list/friends-list-request/FriendsListRequestView.tsx create mode 100644 src/components/friends/views/messenger/FriendsMessengerView.tsx create mode 100644 src/components/friends/views/messenger/messenger-thread/FriendsMessengerThreadGroup.tsx create mode 100644 src/components/friends/views/messenger/messenger-thread/FriendsMessengerThreadView.tsx create mode 100644 src/components/game-center/GameCenterView.scss create mode 100644 src/components/game-center/GameCenterView.tsx create mode 100644 src/components/game-center/views/GameListView.tsx create mode 100644 src/components/game-center/views/GameStageView.tsx create mode 100644 src/components/game-center/views/GameView.tsx create mode 100644 src/components/groups/GroupView.scss create mode 100644 src/components/groups/GroupsView.tsx create mode 100644 src/components/groups/views/GroupBadgeCreatorView.tsx create mode 100644 src/components/groups/views/GroupCreatorView.tsx create mode 100644 src/components/groups/views/GroupInformationStandaloneView.tsx create mode 100644 src/components/groups/views/GroupInformationView.tsx create mode 100644 src/components/groups/views/GroupManagerView.tsx create mode 100644 src/components/groups/views/GroupMembersView.tsx create mode 100644 src/components/groups/views/GroupRoomInformationView.tsx create mode 100644 src/components/groups/views/tabs/GroupTabBadgeView.tsx create mode 100644 src/components/groups/views/tabs/GroupTabColorsView.tsx create mode 100644 src/components/groups/views/tabs/GroupTabCreatorConfirmationView.tsx create mode 100644 src/components/groups/views/tabs/GroupTabIdentityView.tsx create mode 100644 src/components/groups/views/tabs/GroupTabSettingsView.tsx create mode 100644 src/components/guide-tool/GuideToolView.scss create mode 100644 src/components/guide-tool/GuideToolView.tsx create mode 100644 src/components/guide-tool/views/GuideToolAcceptView.tsx create mode 100644 src/components/guide-tool/views/GuideToolMenuView.tsx create mode 100644 src/components/guide-tool/views/GuideToolOngoingView.tsx create mode 100644 src/components/guide-tool/views/GuideToolUserCreateRequestView.tsx create mode 100644 src/components/guide-tool/views/GuideToolUserFeedbackView.tsx create mode 100644 src/components/guide-tool/views/GuideToolUserNoHelpersView.tsx create mode 100644 src/components/guide-tool/views/GuideToolUserPendingView.tsx create mode 100644 src/components/guide-tool/views/GuideToolUserSomethingWrogView.tsx create mode 100644 src/components/guide-tool/views/GuideToolUserThanksView.tsx create mode 100644 src/components/hc-center/HcCenterView.scss create mode 100644 src/components/hc-center/HcCenterView.tsx create mode 100644 src/components/help/HelpView.scss create mode 100644 src/components/help/HelpView.tsx create mode 100644 src/components/help/views/DescribeReportView.tsx create mode 100644 src/components/help/views/HelpIndexView.tsx create mode 100644 src/components/help/views/ReportSummaryView.tsx create mode 100644 src/components/help/views/SanctionStatusView.tsx create mode 100644 src/components/help/views/SelectReportedChatsView.tsx create mode 100644 src/components/help/views/SelectReportedUserView.tsx create mode 100644 src/components/help/views/SelectTopicView.tsx create mode 100644 src/components/help/views/name-change/NameChangeConfirmationView.tsx create mode 100644 src/components/help/views/name-change/NameChangeInitView.tsx create mode 100644 src/components/help/views/name-change/NameChangeInputView.tsx create mode 100644 src/components/help/views/name-change/NameChangeView.tsx create mode 100644 src/components/help/views/name-change/NameChangeView.types.ts create mode 100644 src/components/hotel-view/HotelView.scss create mode 100644 src/components/hotel-view/HotelView.tsx create mode 100644 src/components/hotel-view/views/widgets/GetWidgetLayout.tsx create mode 100644 src/components/hotel-view/views/widgets/HotelViewWidgets.scss create mode 100644 src/components/hotel-view/views/widgets/WidgetSlotView.tsx create mode 100644 src/components/hotel-view/views/widgets/bonus-rare/BonusRareWidgetView.scss create mode 100644 src/components/hotel-view/views/widgets/bonus-rare/BonusRareWidgetView.tsx create mode 100644 src/components/hotel-view/views/widgets/hall-of-fame-item/HallOfFameItemView.tsx create mode 100644 src/components/hotel-view/views/widgets/hall-of-fame/HallOfFameWidgetView.scss create mode 100644 src/components/hotel-view/views/widgets/hall-of-fame/HallOfFameWidgetView.tsx create mode 100644 src/components/hotel-view/views/widgets/hall-of-fame/HallOfFameWidgetView.types.ts create mode 100644 src/components/hotel-view/views/widgets/promo-article/PromoArticleWidgetView.scss create mode 100644 src/components/hotel-view/views/widgets/promo-article/PromoArticleWidgetView.tsx create mode 100644 src/components/hotel-view/views/widgets/widget-container/WidgetContainerView.scss create mode 100644 src/components/hotel-view/views/widgets/widget-container/WidgetContainerView.tsx create mode 100644 src/components/index.scss create mode 100644 src/components/inventory/InventoryView.scss create mode 100644 src/components/inventory/InventoryView.tsx create mode 100644 src/components/inventory/views/InventoryCategoryEmptyView.tsx create mode 100644 src/components/inventory/views/badge/InventoryBadgeItemView.tsx create mode 100644 src/components/inventory/views/badge/InventoryBadgeView.tsx create mode 100644 src/components/inventory/views/bot/InventoryBotItemView.tsx create mode 100644 src/components/inventory/views/bot/InventoryBotView.tsx create mode 100644 src/components/inventory/views/furniture/InventoryFurnitureItemView.tsx create mode 100644 src/components/inventory/views/furniture/InventoryFurnitureSearchView.tsx create mode 100644 src/components/inventory/views/furniture/InventoryFurnitureView.tsx create mode 100644 src/components/inventory/views/furniture/InventoryTradeView.tsx create mode 100644 src/components/inventory/views/pet/InventoryPetItemView.tsx create mode 100644 src/components/inventory/views/pet/InventoryPetView.tsx create mode 100644 src/components/loading/LoadingView.scss create mode 100644 src/components/loading/LoadingView.tsx create mode 100644 src/components/main/MainView.tsx create mode 100644 src/components/mod-tools/ModToolsView.scss create mode 100644 src/components/mod-tools/ModToolsView.tsx create mode 100644 src/components/mod-tools/views/chatlog/ChatlogRecord.ts create mode 100644 src/components/mod-tools/views/chatlog/ChatlogView.tsx create mode 100644 src/components/mod-tools/views/room/ModToolsChatlogView.tsx create mode 100644 src/components/mod-tools/views/room/ModToolsRoomView.tsx create mode 100644 src/components/mod-tools/views/tickets/CfhChatlogView.tsx create mode 100644 src/components/mod-tools/views/tickets/ModToolsIssueInfoView.tsx create mode 100644 src/components/mod-tools/views/tickets/ModToolsMyIssuesTabView.tsx create mode 100644 src/components/mod-tools/views/tickets/ModToolsOpenIssuesTabView.tsx create mode 100644 src/components/mod-tools/views/tickets/ModToolsPickedIssuesTabView.tsx create mode 100644 src/components/mod-tools/views/tickets/ModToolsTicketsView.tsx create mode 100644 src/components/mod-tools/views/user/ModToolsUserChatlogView.tsx create mode 100644 src/components/mod-tools/views/user/ModToolsUserModActionView.tsx create mode 100644 src/components/mod-tools/views/user/ModToolsUserRoomVisitsView.tsx create mode 100644 src/components/mod-tools/views/user/ModToolsUserSendMessageView.tsx create mode 100644 src/components/mod-tools/views/user/ModToolsUserView.tsx create mode 100644 src/components/navigator/NavigatorView.scss create mode 100644 src/components/navigator/NavigatorView.tsx create mode 100644 src/components/navigator/views/NavigatorDoorStateView.tsx create mode 100644 src/components/navigator/views/NavigatorRoomCreatorView.tsx create mode 100644 src/components/navigator/views/NavigatorRoomInfoView.tsx create mode 100644 src/components/navigator/views/NavigatorRoomLinkView.tsx create mode 100644 src/components/navigator/views/room-settings/NavigatorRoomSettingsAccessTabView.tsx create mode 100644 src/components/navigator/views/room-settings/NavigatorRoomSettingsBasicTabView.tsx create mode 100644 src/components/navigator/views/room-settings/NavigatorRoomSettingsModTabView.tsx create mode 100644 src/components/navigator/views/room-settings/NavigatorRoomSettingsRightsTabView.tsx create mode 100644 src/components/navigator/views/room-settings/NavigatorRoomSettingsView.tsx create mode 100644 src/components/navigator/views/room-settings/NavigatorRoomSettingsVipChatTabView.tsx create mode 100644 src/components/navigator/views/search/NavigatorSearchResultItemInfoView.tsx create mode 100644 src/components/navigator/views/search/NavigatorSearchResultItemView.tsx create mode 100644 src/components/navigator/views/search/NavigatorSearchResultView.tsx create mode 100644 src/components/navigator/views/search/NavigatorSearchView.tsx create mode 100644 src/components/nitrobubblehidden/NitrobubbleHiddenView.tsx create mode 100644 src/components/nitropedia/NitropediaView.scss create mode 100644 src/components/nitropedia/NitropediaView.tsx create mode 100644 src/components/notification-center/NotificationCenterView.scss create mode 100644 src/components/notification-center/NotificationCenterView.tsx create mode 100644 src/components/notification-center/views/alert-layouts/GetAlertLayout.tsx create mode 100644 src/components/notification-center/views/alert-layouts/NitroSystemAlertView.tsx create mode 100644 src/components/notification-center/views/alert-layouts/NotificationDefaultAlertView.tsx create mode 100644 src/components/notification-center/views/alert-layouts/NotificationSearchAlertView.tsx create mode 100644 src/components/notification-center/views/bubble-layouts/GetBubbleLayout.tsx create mode 100644 src/components/notification-center/views/bubble-layouts/NotificationClubGiftBubbleView.tsx create mode 100644 src/components/notification-center/views/bubble-layouts/NotificationDefaultBubbleView.tsx create mode 100644 src/components/notification-center/views/confirm-layouts/GetConfirmLayout.tsx create mode 100644 src/components/notification-center/views/confirm-layouts/NotificationDefaultConfirmView.tsx create mode 100644 src/components/purse/PurseView.scss create mode 100644 src/components/purse/PurseView.tsx create mode 100644 src/components/purse/views/CurrencyView.tsx create mode 100644 src/components/purse/views/SeasonalView.tsx create mode 100644 src/components/right-side/RightSideView.scss create mode 100644 src/components/right-side/RightSideView.tsx create mode 100644 src/components/room/RoomView.scss create mode 100644 src/components/room/RoomView.tsx create mode 100644 src/components/room/spectator/RoomSpectatorView.scss create mode 100644 src/components/room/spectator/RoomSpectatorView.tsx create mode 100644 src/components/room/widgets/RoomWidgets.scss create mode 100644 src/components/room/widgets/RoomWidgetsView.tsx create mode 100644 src/components/room/widgets/avatar-info/AvatarInfoPetTrainingPanelView.tsx create mode 100644 src/components/room/widgets/avatar-info/AvatarInfoRentableBotChatView.tsx create mode 100644 src/components/room/widgets/avatar-info/AvatarInfoUseProductConfirmView.tsx create mode 100644 src/components/room/widgets/avatar-info/AvatarInfoUseProductView.tsx create mode 100644 src/components/room/widgets/avatar-info/AvatarInfoWidgetView.scss create mode 100644 src/components/room/widgets/avatar-info/AvatarInfoWidgetView.tsx create mode 100644 src/components/room/widgets/avatar-info/infostand/InfoStandWidgetBotView.tsx create mode 100644 src/components/room/widgets/avatar-info/infostand/InfoStandWidgetFurniView.tsx create mode 100644 src/components/room/widgets/avatar-info/infostand/InfoStandWidgetPetView.tsx create mode 100644 src/components/room/widgets/avatar-info/infostand/InfoStandWidgetRentableBotView.tsx create mode 100644 src/components/room/widgets/avatar-info/infostand/InfoStandWidgetUserRelationshipItemView.tsx create mode 100644 src/components/room/widgets/avatar-info/infostand/InfoStandWidgetUserRelationshipsView.tsx create mode 100644 src/components/room/widgets/avatar-info/infostand/InfoStandWidgetUserTagsView.tsx create mode 100644 src/components/room/widgets/avatar-info/infostand/InfoStandWidgetUserView.tsx create mode 100644 src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetAvatarView.tsx create mode 100644 src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetDecorateView.tsx create mode 100644 src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetFurniView.tsx create mode 100644 src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetNameView.tsx create mode 100644 src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetOwnAvatarView.tsx create mode 100644 src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetOwnPetView.tsx create mode 100644 src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetPetView.tsx create mode 100644 src/components/room/widgets/avatar-info/menu/AvatarInfoWidgetRentableBotView.tsx create mode 100644 src/components/room/widgets/chat-input/ChatInputStickersSelectorConsolaView.tsx create mode 100644 src/components/room/widgets/chat-input/ChatInputStickersSelectorView.tsx create mode 100644 src/components/room/widgets/chat-input/ChatInputStyleSelectorView.tsx create mode 100644 src/components/room/widgets/chat-input/ChatInputView.scss create mode 100644 src/components/room/widgets/chat-input/ChatInputView.tsx create mode 100644 src/components/room/widgets/chat/ChatWidgetMessageView.tsx create mode 100644 src/components/room/widgets/chat/ChatWidgetView.scss create mode 100644 src/components/room/widgets/chat/ChatWidgetView.tsx create mode 100644 src/components/room/widgets/choosers/ChooserWidgetView.scss create mode 100644 src/components/room/widgets/choosers/ChooserWidgetView.tsx create mode 100644 src/components/room/widgets/choosers/FurniChooserWidgetView.tsx create mode 100644 src/components/room/widgets/choosers/UserChooserWidgetView.tsx create mode 100644 src/components/room/widgets/context-menu/ContextMenu.scss create mode 100644 src/components/room/widgets/context-menu/ContextMenuCaretView.tsx create mode 100644 src/components/room/widgets/context-menu/ContextMenuHeaderView.tsx create mode 100644 src/components/room/widgets/context-menu/ContextMenuListItemView.tsx create mode 100644 src/components/room/widgets/context-menu/ContextMenuListView.tsx create mode 100644 src/components/room/widgets/context-menu/ContextMenuView.tsx create mode 100644 src/components/room/widgets/doorbell/DoorbellWidgetView.tsx create mode 100644 src/components/room/widgets/friend-request/FriendRequestDialogView.scss create mode 100644 src/components/room/widgets/friend-request/FriendRequestDialogView.tsx create mode 100644 src/components/room/widgets/friend-request/FriendRequestWidgetView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureBackgroundColorView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureBadgeDisplayView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureCraftingView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureDimmerView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureExchangeCreditView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureExternalImageView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureFriendFurniView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureGiftOpeningView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureHighScoreView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureInternalLinkView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureMannequinView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureMysteryBoxOpenDialogView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureMysteryTrophyOpenDialogView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureRoomLinkView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureSpamWallPostItView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureStackHeightView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureStickieView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureTrophyView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureWidgets.scss create mode 100644 src/components/room/widgets/furniture/FurnitureWidgetsView.tsx create mode 100644 src/components/room/widgets/furniture/FurnitureYoutubeDisplayView.tsx create mode 100644 src/components/room/widgets/furniture/context-menu/EffectBoxConfirmView.tsx create mode 100644 src/components/room/widgets/furniture/context-menu/FurnitureContextMenuView.tsx create mode 100644 src/components/room/widgets/furniture/context-menu/MonsterPlantSeedConfirmView.tsx create mode 100644 src/components/room/widgets/furniture/context-menu/PurchasableClothingConfirmView.tsx create mode 100644 src/components/room/widgets/furniture/playlist-editor/DiskInventoryView.tsx create mode 100644 src/components/room/widgets/furniture/playlist-editor/FurniturePlaylistEditorWidgetView.tsx create mode 100644 src/components/room/widgets/furniture/playlist-editor/SongPlaylistView.tsx create mode 100644 src/components/room/widgets/mysterybox/MysteryBoxExtensionView.scss create mode 100644 src/components/room/widgets/mysterybox/MysteryBoxExtensionView.tsx create mode 100644 src/components/room/widgets/object-location/ObjectLocationView.tsx create mode 100644 src/components/room/widgets/pet-package/PetPackageWidgetView.scss create mode 100644 src/components/room/widgets/pet-package/PetPackageWidgetView.tsx create mode 100644 src/components/room/widgets/room-filter-words/RoomFilterWordsWidgetView.tsx create mode 100644 src/components/room/widgets/room-promotes/RoomPromotesWidgetView.tsx create mode 100644 src/components/room/widgets/room-promotes/views/RoomPromoteEditWidgetView.tsx create mode 100644 src/components/room/widgets/room-promotes/views/RoomPromoteMyOwnEventWidgetView.tsx create mode 100644 src/components/room/widgets/room-promotes/views/RoomPromoteOtherEventWidgetView.tsx create mode 100644 src/components/room/widgets/room-promotes/views/index.ts create mode 100644 src/components/room/widgets/room-thumbnail/RoomThumbnailWidgetView.tsx create mode 100644 src/components/room/widgets/room-tools/RoomToolsWidgetView.tsx create mode 100644 src/components/room/widgets/user-location/UserLocationView.tsx create mode 100644 src/components/room/widgets/word-quiz/WordQuizQuestionView.tsx create mode 100644 src/components/room/widgets/word-quiz/WordQuizVoteView.tsx create mode 100644 src/components/room/widgets/word-quiz/WordQuizWidgetView.tsx create mode 100644 src/components/toolbar/ToolbarMeView.tsx create mode 100644 src/components/toolbar/ToolbarView.scss create mode 100644 src/components/toolbar/ToolbarView.tsx create mode 100644 src/components/user-profile/UserProfileVew.scss create mode 100644 src/components/user-profile/UserProfileView.tsx create mode 100644 src/components/user-profile/views/BadgesContainerView.tsx create mode 100644 src/components/user-profile/views/FriendsContainerView.tsx create mode 100644 src/components/user-profile/views/GroupsContainerView.tsx create mode 100644 src/components/user-profile/views/RelationshipsContainerView.tsx create mode 100644 src/components/user-profile/views/UserContainerView.tsx create mode 100644 src/components/user-settings/UserSettingsView.tsx create mode 100644 src/components/wired/WiredView.scss create mode 100644 src/components/wired/WiredView.tsx create mode 100644 src/components/wired/views/WiredBaseView.tsx create mode 100644 src/components/wired/views/WiredFurniSelectorView.tsx create mode 100644 src/components/wired/views/actions/WiredActionBaseView.tsx create mode 100644 src/components/wired/views/actions/WiredActionBotChangeFigureView.tsx create mode 100644 src/components/wired/views/actions/WiredActionBotFollowAvatarView.tsx create mode 100644 src/components/wired/views/actions/WiredActionBotGiveHandItemView.tsx create mode 100644 src/components/wired/views/actions/WiredActionBotMoveView.tsx create mode 100644 src/components/wired/views/actions/WiredActionBotTalkToAvatarView.tsx create mode 100644 src/components/wired/views/actions/WiredActionBotTalkView.tsx create mode 100644 src/components/wired/views/actions/WiredActionBotTeleportView.tsx create mode 100644 src/components/wired/views/actions/WiredActionCallAnotherStackView.tsx create mode 100644 src/components/wired/views/actions/WiredActionChaseView.tsx create mode 100644 src/components/wired/views/actions/WiredActionChatView.tsx create mode 100644 src/components/wired/views/actions/WiredActionFleeView.tsx create mode 100644 src/components/wired/views/actions/WiredActionGiveRewardView.tsx create mode 100644 src/components/wired/views/actions/WiredActionGiveScoreToPredefinedTeamView.tsx create mode 100644 src/components/wired/views/actions/WiredActionGiveScoreView.tsx create mode 100644 src/components/wired/views/actions/WiredActionJoinTeamView.tsx create mode 100644 src/components/wired/views/actions/WiredActionKickFromRoomView.tsx create mode 100644 src/components/wired/views/actions/WiredActionLayoutView.tsx create mode 100644 src/components/wired/views/actions/WiredActionLeaveTeamView.tsx create mode 100644 src/components/wired/views/actions/WiredActionMoveAndRotateFurniView.tsx create mode 100644 src/components/wired/views/actions/WiredActionMoveFurniToView.tsx create mode 100644 src/components/wired/views/actions/WiredActionMoveFurniView.tsx create mode 100644 src/components/wired/views/actions/WiredActionMuteUserView.tsx create mode 100644 src/components/wired/views/actions/WiredActionResetView.tsx create mode 100644 src/components/wired/views/actions/WiredActionSetFurniStateToView.tsx create mode 100644 src/components/wired/views/actions/WiredActionTeleportView.tsx create mode 100644 src/components/wired/views/actions/WiredActionToggleFurniStateView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionActorHasHandItem.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionActorIsGroupMemberView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionActorIsOnFurniView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionActorIsTeamMemberView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionActorIsWearingBadgeView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionActorIsWearingEffectView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionBaseView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionDateRangeView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionFurniHasAvatarOnView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionFurniHasFurniOnView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionFurniHasNotFurniOnView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionFurniIsOfTypeView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionFurniMatchesSnapshotView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionLayoutView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionTimeElapsedLessView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionTimeElapsedMoreView.tsx create mode 100644 src/components/wired/views/conditions/WiredConditionUserCountInRoomView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerAvatarEnterRoomView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerAvatarSaysSomethingView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerAvatarWalksOffFurniView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerAvatarWalksOnFurni.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerBaseView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerBotReachedAvatarView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerBotReachedStuffView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerCollisionView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerExecuteOnceView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerExecutePeriodicallyLongView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerExecutePeriodicallyView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerGameEndsView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerGameStartsView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerLayoutView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerScoreAchievedView.tsx create mode 100644 src/components/wired/views/triggers/WiredTriggerToggleFurniView.tsx create mode 100644 src/events/catalog/CatalogEvent.ts create mode 100644 src/events/catalog/CatalogInitGiftEvent.ts create mode 100644 src/events/catalog/CatalogPostMarketplaceOfferEvent.ts create mode 100644 src/events/catalog/CatalogPurchaseFailureEvent.ts create mode 100644 src/events/catalog/CatalogPurchaseNotAllowedEvent.ts create mode 100644 src/events/catalog/CatalogPurchaseOverrideEvent.ts create mode 100644 src/events/catalog/CatalogPurchaseSoldOutEvent.ts create mode 100644 src/events/catalog/CatalogPurchasedEvent.ts create mode 100644 src/events/catalog/CatalogSetRoomPreviewerStuffDataEvent.ts create mode 100644 src/events/catalog/CatalogWidgetEvent.ts create mode 100644 src/events/catalog/SetRoomPreviewerStuffDataEvent.ts create mode 100644 src/events/catalog/index.ts create mode 100644 src/events/guide-tool/GuideToolEvent.ts create mode 100644 src/events/guide-tool/index.ts create mode 100644 src/events/help/HelpNameChangeEvent.ts create mode 100644 src/events/help/index.ts create mode 100644 src/events/index.ts create mode 100644 src/events/inventory/InventoryFurniAddedEvent.ts create mode 100644 src/events/inventory/index.ts create mode 100644 src/events/room-widgets/index.ts create mode 100644 src/events/room-widgets/thumbnail/RoomWidgetThumbnailEvent.ts create mode 100644 src/events/room-widgets/thumbnail/index.ts create mode 100644 src/hooks/UseMountEffect.tsx create mode 100644 src/hooks/achievements/index.ts create mode 100644 src/hooks/achievements/useAchievements.ts create mode 100644 src/hooks/camera/index.ts create mode 100644 src/hooks/camera/useCamera.ts create mode 100644 src/hooks/catalog/index.ts create mode 100644 src/hooks/catalog/useCatalog.ts create mode 100644 src/hooks/catalog/useCatalogPlaceMultipleItems.ts create mode 100644 src/hooks/catalog/useCatalogSkipPurchaseConfirmation.ts create mode 100644 src/hooks/chat-history/index.ts create mode 100644 src/hooks/chat-history/useChatHistory.ts create mode 100644 src/hooks/events/core/index.ts create mode 100644 src/hooks/events/core/useCommunicationEvent.tsx create mode 100644 src/hooks/events/core/useConfigurationEvent.tsx create mode 100644 src/hooks/events/index.ts create mode 100644 src/hooks/events/nitro/index.ts create mode 100644 src/hooks/events/nitro/useAvatarEvent.tsx create mode 100644 src/hooks/events/nitro/useCameraEvent.tsx create mode 100644 src/hooks/events/nitro/useLocalizationEvent.tsx create mode 100644 src/hooks/events/nitro/useMainEvent.tsx create mode 100644 src/hooks/events/nitro/useRoomEngineEvent.tsx create mode 100644 src/hooks/events/nitro/useRoomSessionManagerEvent.tsx create mode 100644 src/hooks/events/nitro/useSessionDataManagerEvent.tsx create mode 100644 src/hooks/events/nitro/useSoundEvent.tsx create mode 100644 src/hooks/events/useEventDispatcher.tsx create mode 100644 src/hooks/events/useMessageEvent.tsx create mode 100644 src/hooks/events/useUiEvent.tsx create mode 100644 src/hooks/friends/index.ts create mode 100644 src/hooks/friends/useFriends.ts create mode 100644 src/hooks/friends/useMessenger.ts create mode 100644 src/hooks/game-center/index.ts create mode 100644 src/hooks/game-center/useGameCenter.ts create mode 100644 src/hooks/groups/index.ts create mode 100644 src/hooks/groups/useGroup.ts create mode 100644 src/hooks/help/index.ts create mode 100644 src/hooks/help/useHelp.ts create mode 100644 src/hooks/index.ts create mode 100644 src/hooks/inventory/index.ts create mode 100644 src/hooks/inventory/useInventoryBadges.ts create mode 100644 src/hooks/inventory/useInventoryBots.ts create mode 100644 src/hooks/inventory/useInventoryFurni.ts create mode 100644 src/hooks/inventory/useInventoryPets.ts create mode 100644 src/hooks/inventory/useInventoryTrade.ts create mode 100644 src/hooks/inventory/useInventoryUnseenTracker.ts create mode 100644 src/hooks/mod-tools/index.ts create mode 100644 src/hooks/mod-tools/useModTools.ts create mode 100644 src/hooks/navigator/index.ts create mode 100644 src/hooks/navigator/useNavigator.ts create mode 100644 src/hooks/notification/index.ts create mode 100644 src/hooks/notification/useNotification.ts create mode 100644 src/hooks/purse/index.ts create mode 100644 src/hooks/purse/usePurse.ts create mode 100644 src/hooks/rooms/engine/index.ts create mode 100644 src/hooks/rooms/engine/useFurniAddedEvent.ts create mode 100644 src/hooks/rooms/engine/useFurniRemovedEvent.ts create mode 100644 src/hooks/rooms/engine/useObjectDeselectedEvent.ts create mode 100644 src/hooks/rooms/engine/useObjectDoubleClickedEvent.ts create mode 100644 src/hooks/rooms/engine/useObjectRollOutEvent.ts create mode 100644 src/hooks/rooms/engine/useObjectRollOverEvent.ts create mode 100644 src/hooks/rooms/engine/useObjectSelectedEvent.ts create mode 100644 src/hooks/rooms/engine/useUserAddedEvent.ts create mode 100644 src/hooks/rooms/engine/useUserRemovedEvent.ts create mode 100644 src/hooks/rooms/index.ts create mode 100644 src/hooks/rooms/promotes/index.ts create mode 100644 src/hooks/rooms/promotes/useRoomPromote.ts create mode 100644 src/hooks/rooms/useRoom.ts create mode 100644 src/hooks/rooms/widgets/furniture/index.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureBackgroundColorWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureBadgeDisplayWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureContextMenuWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureCraftingWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureDimmerWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureExchangeWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureExternalImageWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureFriendFurniWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureHighScoreWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureInternalLinkWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureMannequinWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurniturePlaylistEditorWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurniturePresentWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureRoomLinkWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureSpamWallPostItWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureStackHeightWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureStickieWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureTrophyWidget.ts create mode 100644 src/hooks/rooms/widgets/furniture/useFurnitureYoutubeWidget.ts create mode 100644 src/hooks/rooms/widgets/index.ts create mode 100644 src/hooks/rooms/widgets/useAvatarInfoWidget.ts create mode 100644 src/hooks/rooms/widgets/useChatInputWidget.ts create mode 100644 src/hooks/rooms/widgets/useChatWidget.ts create mode 100644 src/hooks/rooms/widgets/useDoorbellWidget.ts create mode 100644 src/hooks/rooms/widgets/useFilterWordsWidget.ts create mode 100644 src/hooks/rooms/widgets/useFriendRequestWidget.ts create mode 100644 src/hooks/rooms/widgets/useFurniChooserWidget.ts create mode 100644 src/hooks/rooms/widgets/usePetPackageWidget.ts create mode 100644 src/hooks/rooms/widgets/usePollWidget.ts create mode 100644 src/hooks/rooms/widgets/useUserChooserWidget.ts create mode 100644 src/hooks/rooms/widgets/useWordQuizWidget.ts create mode 100644 src/hooks/session/index.ts create mode 100644 src/hooks/session/useSessionInfo.ts create mode 100644 src/hooks/useLocalStorage.ts create mode 100644 src/hooks/useSharedVisibility.ts create mode 100644 src/hooks/wired/index.ts create mode 100644 src/hooks/wired/useWired.ts create mode 100644 src/index.scss create mode 100644 src/index.tsx create mode 100644 src/react-app-env.d.ts create mode 100644 src/workers/IntervalWebWorker.ts create mode 100644 src/workers/WorkerBuilder.ts create mode 100644 tsconfig.json create mode 100644 vite.config.js create mode 100644 yarn.lock 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 0000000000000000000000000000000000000000..634eb063ec992a9de0818597bd6ea3c25820c563 GIT binary patch literal 9604 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 aBw&+;F6aQQkV{elxN-fKZavyL=Klcpaw0GQ literal 0 HcmV?d00001 diff --git a/public/android-chrome-512x512.png b/public/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..33cf6c6ab19d882ccc9e2f05ea256021a12f72ce GIT binary patch literal 29298 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..788a7f12b42a2b1d9b7766676ff5e8e69cc19b89 GIT binary patch literal 1017 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 QGN}Y`BYF~A@YE~+0>ge95&!@I literal 0 HcmV?d00001 diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..34ba1b3d4ace82c0c28e6093993d5d3fe9a47d23 GIT binary patch literal 15086 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 ka>#WtcNG!P^Edoo) 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 literal 0 HcmV?d00001 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 = "