[
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n"
  },
  {
    "path": ".github/workflows/luacheck.yml",
    "content": "name: luacheck\non: [push, pull_request]\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@master\n      - name: lint\n        uses: Roang-zero1/factorio-mod-luacheck@master\n        with:\n          luacheckrc_url: https://raw.githubusercontent.com/Quenty/NevermoreEngine/version2/.luacheckrc"
  },
  {
    "path": ".gitignore",
    "content": "# Compiled Lua sources\nluac.out\n\n# luarocks build files\n*.src.rock\n*.zip\n*.tar.gz\n\n# Object files\n*.o\n*.os\n*.ko\n*.obj\n*.elf\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Libraries\n*.lib\n*.a\n*.la\n*.lo\n*.def\n*.exp\n\n# Shared objects (inc. Windows DLLs)\n*.dll\n*.so\n*.so.*\n*.dylib\n\n# Executables\n*.exe\n*.out\n*.app\n*.i*86\n*.x86_64\n*.hex\n\n"
  },
  {
    "path": ".luacheckrc",
    "content": "-- Quenty/NevermoreEngine https://github.com/Quenty/NevermoreEngine/blob/version2/.luacheckrc\n\nlocal empty = {}\nlocal read_write = { read_only = false }\nlocal read_write_class = { read_only = false, other_fields = true }\nlocal read_only = { read_only = true }\n\nlocal function def_fields(field_list)\n   local fields = {}\n\n   for _, field in ipairs(field_list) do\n      fields[field] = empty\n   end\n\n   return { fields = fields }\nend\n\nlocal enum = def_fields({\"Value\", \"Name\"})\n\nlocal function def_enum(field_list)\n   local fields = {}\n\n   for _, field in ipairs(field_list) do\n      fields[field] = enum\n   end\n\n   fields[\"GetEnumItems\"] = read_only\n\n   return { fields = fields }\nend\n\nstds.roblox = {\n    globals = {\n        script = {\n            other_fields = true,\n            fields = {\n                Source = read_write;\n                GetHash = read_write;\n                Disabled = read_write;\n                LinkedSource = read_write;\n                CurrentEditor = read_write_class;\n                IsDifferentFromFileSystem = read_write;\n                Archivable = read_write;\n                ClassName = read_only;\n                Name = read_write;\n                Parent = read_write_class;\n                RobloxLocked = read_write;\n                ClearAllChildren = read_write;\n                Clone = read_write;\n                Destroy = read_write;\n                FindFirstAncestor = read_write;\n                FindFirstAncestorOfClass = read_write;\n                FindFirstAncestorWhichIsA = read_write;\n                FindFirstChild = read_write;\n                FindFirstChildOfClass = read_write;\n                FindFirstChildWhichIsA = read_write;\n                GetAttribute = read_write;\n                GetAttributeChangedSignal = read_write;\n                GetAttributes = read_write;\n                GetChildren = read_write;\n                GetDebugId = read_write;\n                GetDescendants = read_write;\n                GetFullName = read_write;\n                GetPropertyChangedSignal = read_write;\n                IsA = read_write;\n                IsAncestorOf = read_write;\n                IsDescendantOf = read_write;\n                SetAttribute = read_write;\n                WaitForChild = read_write;\n                AncestryChanged = read_write;\n                AttributeChanged = read_write;\n                Changed = read_write;\n                ChildAdded = read_write;\n                ChildRemoved = read_write;\n                DescendantAdded = read_write;\n                DescendantRemoving = read_write;\n            }\n        },\n        game = {\n            other_fields = true,\n            fields = {\n                CreatorId = read_only;\n                CreatorType = read_only;\n                GameId = read_only;\n                Genre = read_only;\n                IsSFFlagsLoaded = read_only;\n                JobId = read_only;\n                PlaceId = read_only;\n                PlaceVersion = read_only;\n                PrivateServerId = read_only;\n                PrivateServerOwnerId = read_only;\n                Workspace = read_only;\n                BindToClose = read_write;\n                DefineFastFlag = read_write;\n                DefineFastInt = read_write;\n                DefineFastString = read_write;\n                GetEngineFeature = read_write;\n                GetFastFlag = read_write;\n                GetFastInt = read_write;\n                GetFastString = read_write;\n                GetJobIntervalPeakFraction = read_write;\n                GetJobTimePeakFraction = read_write;\n                GetJobsExtendedStats = read_write;\n                GetJobsInfo = read_write;\n                GetObjects = read_write;\n                GetObjectsList = read_write;\n                IsLoaded = read_write;\n                Load = read_write;\n                OpenScreenshotsFolder = read_write;\n                OpenVideosFolder = read_write;\n                ReportInGoogleAnalytics = read_write;\n                SetFastFlagForTesting = read_write;\n                SetFastIntForTesting = read_write;\n                SetFastStringForTesting = read_write;\n                SetPlaceId = read_write;\n                SetUniverseId = read_write;\n                Shutdown = read_write;\n                GetObjectsAsync = read_write;\n                HttpGetAsync = read_write;\n                HttpPostAsync = read_write;\n                InsertObjectsAndJoinIfLegacyAsync = read_write;\n                GraphicsQualityChangeRequest = read_write;\n                Loaded = read_write;\n                ScreenshotReady = read_write;\n                FindService = read_write;\n                GetService = read_write;\n                Close = read_write;\n                CloseLate = read_write;\n                ServiceAdded = read_write;\n                ServiceRemoving = read_write;\n                Archivable = read_write;\n                ClassName = read_only;\n                Name = read_write;\n                Parent = read_write_class;\n                RobloxLocked = read_write;\n                ClearAllChildren = read_write;\n                Clone = read_write;\n                Destroy = read_write;\n                FindFirstAncestor = read_write;\n                FindFirstAncestorOfClass = read_write;\n                FindFirstAncestorWhichIsA = read_write;\n                FindFirstChild = read_write;\n                FindFirstChildOfClass = read_write;\n                FindFirstChildWhichIsA = read_write;\n                GetAttribute = read_write;\n                GetAttributeChangedSignal = read_write;\n                GetAttributes = read_write;\n                GetChildren = read_write;\n                GetDebugId = read_write;\n                GetDescendants = read_write;\n                GetFullName = read_write;\n                GetPropertyChangedSignal = read_write;\n                IsA = read_write;\n                IsAncestorOf = read_write;\n                IsDescendantOf = read_write;\n                SetAttribute = read_write;\n                WaitForChild = read_write;\n                AncestryChanged = read_write;\n                AttributeChanged = read_write;\n                Changed = read_write;\n                ChildAdded = read_write;\n                ChildRemoved = read_write;\n                DescendantAdded = read_write;\n                DescendantRemoving = read_write;\n            }\n        },\n        workspace = {\n            other_fields = true,\n            fields = {\n                AllowThirdPartySales = read_write;\n                CurrentCamera = read_write_class;\n                DistributedGameTime = read_write;\n                FallenPartsDestroyHeight = read_write;\n                FilteringEnabled = read_write;\n                Gravity = read_write;\n                StreamingEnabled = read_write;\n                StreamingMinRadius = read_write;\n                StreamingPauseMode = read_write;\n                StreamingTargetRadius = read_write;\n                TemporaryLegacyPhysicsSolverOverride = read_write;\n                Terrain = read_only;\n                BreakJoints = read_write;\n                CalculateJumpDistance = read_write;\n                CalculateJumpHeight = read_write;\n                CalculateJumpPower = read_write;\n                ExperimentalSolverIsEnabled = read_write;\n                GetNumAwakeParts = read_write;\n                GetPhysicsThrottling = read_write;\n                GetRealPhysicsFPS = read_write;\n                JoinToOutsiders = read_write;\n                MakeJoints = read_write;\n                PGSIsEnabled = read_write;\n                SetPhysicsThrottleEnabled = read_write;\n                UnjoinFromOutsiders = read_write;\n                ZoomToExtents = read_write;\n                ArePartsTouchingOthers = read_write;\n                BulkMoveTo = read_write;\n                FindPartOnRay = read_write;\n                FindPartOnRayWithIgnoreList = read_write;\n                FindPartOnRayWithWhitelist = read_write;\n                FindPartsInRegion3 = read_write;\n                FindPartsInRegion3WithIgnoreList = read_write;\n                FindPartsInRegion3WithWhiteList = read_write;\n                IKMoveTo = read_write;\n                IsRegion3Empty = read_write;\n                IsRegion3EmptyWithIgnoreList = read_write;\n                Raycast = read_write;\n                SetInsertPoint = read_write;\n                PrimaryPart = read_write_class;\n                BreakJoints = read_write;\n                GetBoundingBox = read_write;\n                GetExtentsSize = read_write;\n                GetPrimaryPartCFrame = read_write;\n                MakeJoints = read_write;\n                MoveTo = read_write;\n                SetPrimaryPartCFrame = read_write;\n                TranslateBy = read_write;\n                Archivable = read_write;\n                ClassName = read_only;\n                Name = read_write;\n                Parent = read_write_class;\n                RobloxLocked = read_write;\n                ClearAllChildren = read_write;\n                Clone = read_write;\n                Destroy = read_write;\n                FindFirstAncestor = read_write;\n                FindFirstAncestorOfClass = read_write;\n                FindFirstAncestorWhichIsA = read_write;\n                FindFirstChild = read_write;\n                FindFirstChildOfClass = read_write;\n                FindFirstChildWhichIsA = read_write;\n                GetAttribute = read_write;\n                GetAttributeChangedSignal = read_write;\n                GetAttributes = read_write;\n                GetChildren = read_write;\n                GetDebugId = read_write;\n                GetDescendants = read_write;\n                GetFullName = read_write;\n                GetPropertyChangedSignal = read_write;\n                IsA = read_write;\n                IsAncestorOf = read_write;\n                IsDescendantOf = read_write;\n                SetAttribute = read_write;\n                WaitForChild = read_write;\n                AncestryChanged = read_write;\n                AttributeChanged = read_write;\n                Changed = read_write;\n                ChildAdded = read_write;\n                ChildRemoved = read_write;\n                DescendantAdded = read_write;\n                DescendantRemoving = read_write;\n            }\n        },\n    },\n    read_globals = {\n        -- Methods\n        delay = empty;\n        settings = empty;\n        spawn = empty;\n        tick = empty;\n        time = empty;\n        typeof = empty;\n        version = empty;\n        wait = empty;\n        warn = empty;\n        UserSettings = empty;\n\n        -- Libraries\n        math = def_fields({\"abs\", \"acos\", \"asin\", \"atan\", \"atan2\", \"ceil\", \"clamp\", \"cos\", \"cosh\",\n            \"deg\", \"exp\", \"floor\", \"fmod\", \"frexp\", \"ldexp\", \"log\", \"log10\", \"max\", \"min\", \"modf\",\n            \"noise\", \"pow\", \"rad\", \"random\", \"randomseed\", \"sign\", \"sin\", \"sinh\", \"sqrt\", \"tan\",\n            \"tanh\", \"huge\", \"pi\"}),\n\n        table = def_fields({\"concat\", \"foreach\", \"foreachi\", \"getn\", \"insert\", \"remove\", \"sort\",\n            \"pack\", \"unpack\", \"move\", \"create\", \"find\"}),\n\n        os = def_fields({\"time\", \"difftime\", \"date\"}),\n\n        debug = def_fields({\"traceback\", \"profilebegin\", \"profileend\"}),\n\n        utf8 = def_fields({\"char\", \"codes\", \"codepoint\", \"len\", \"offset\", \"graphemes\",\n            \"nfcnormalize\", \"nfdnormalize\", \"charpattern\"}),\n\n        bit32 = def_fields({\"arshift\", \"band\", \"bnot\", \"bor\", \"btest\", \"bxor\", \"extract\",\n            \"replace\", \"lrotate\", \"lshift\", \"rrotate\", \"rshift\"}),\n\n        string = def_fields({\"byte\", \"char\", \"find\", \"format\", \"gmatch\", \"gsub\", \"len\", \"lower\",\n            \"match\", \"rep\", \"reverse\", \"split\"}),\n\n        -- Types\n        Axes = def_fields({\"new\"}),\n\n        BrickColor = def_fields({\"new\", \"palette\", \"random\", \"White\", \"Gray\", \"DarkGray\", \"Black\",\n            \"Red\", \"Yellow\", \"Green\", \"Blue\"}),\n\n        CFrame = def_fields({\"new\", \"fromEulerAnglesXYZ\", \"Angles\", \"fromOrientation\",\n            \"fromAxisAngle\", \"fromMatrix\"}),\n\n        Color3 = def_fields({\"new\", \"fromRGB\", \"fromHSV\", \"toHSV\"}),\n\n        ColorSequence = def_fields({\"new\"}),\n\n        ColorSequenceKeypoint = def_fields({\"new\"}),\n\n        DockWidgetPluginGuiInfo = def_fields({\"new\"}),\n\n        Enums = def_fields({\"GetEnums\"}),\n\n        Faces = def_fields({\"new\"}),\n\n        Instance = def_fields({\"new\"}),\n\n        NumberRange = def_fields({\"new\"}),\n\n        NumberSequence = def_fields({\"new\"}),\n\n        NumberSequenceKeypoint = def_fields({\"new\"}),\n\n        PhysicalProperties = def_fields({\"new\"}),\n\n        Random = def_fields({\"new\"}),\n\n        Ray = def_fields({\"new\"}),\n\n        RaycastParams = def_fields({\"new\"}),\n\n        Rect = def_fields({\"new\"}),\n\n        Region3 = def_fields({\"new\"}),\n\n        Region3int16 = def_fields({\"new\"}),\n\n        TweenInfo = def_fields({\"new\"}),\n\n        UDim = def_fields({\"new\"}),\n\n        UDim2 = def_fields({\"new\", \"fromScale\", \"fromOffset\"}),\n\n        Vector2 = def_fields({\"new\"}),\n\n        Vector2int16 = def_fields({\"new\"}),\n\n        Vector3 = def_fields({\"new\", \"FromNormalId\", \"FromAxis\"}),\n\n        Vector3int16 = def_fields({\"new\"}),\n\n        -- Enums\n        Enum = {\n            readonly = true,\n            fields = {\n                ABTestLoadingStatus = def_enum({\"None\", \"Pending\", \"Initialized\", \"Error\",\n                    \"TimedOut\", \"ShutOff\"}),\n                ActionType = def_enum({\"Nothing\", \"Pause\", \"Lose\", \"Draw\", \"Win\"}),\n                ActuatorRelativeTo = def_enum({\"Attachment0\", \"Attachment1\", \"World\"}),\n                ActuatorType = def_enum({\"None\", \"Motor\", \"Servo\"}),\n                AlignType = def_enum({\"Parallel\", \"Perpendicular\"}),\n                AlphaMode = def_enum({\"Overlay\", \"Transparency\"}),\n                AnimationPriority = def_enum({\"Idle\", \"Movement\", \"Action\", \"Core\"}),\n                AppShellActionType = def_enum({\"None\", \"OpenApp\", \"TapChatTab\",\n                    \"TapConversationEntry\", \"TapAvatarTab\", \"ReadConversation\", \"TapGamePageTab\",\n                    \"TapHomePageTab\", \"GamePageLoaded\", \"HomePageLoaded\", \"AvatarEditorPageLoaded\"}),\n                AspectType = def_enum({\"FitWithinMaxSize\", \"ScaleWithParentSize\"}),\n                AssetFetchStatus = def_enum({\"Success\", \"Failure\"}),\n                AssetType = def_enum({\"Image\", \"TeeShirt\", \"Audio\", \"Mesh\", \"Lua\", \"Hat\", \"Place\",\n                    \"Model\", \"Shirt\", \"Pants\", \"Decal\", \"Head\", \"Face\", \"Gear\", \"Badge\",\n                    \"Animation\", \"Torso\", \"RightArm\", \"LeftArm\", \"LeftLeg\", \"RightLeg\", \"Package\",\n                    \"GamePass\", \"Plugin\", \"MeshPart\", \"HairAccessory\", \"FaceAccessory\",\n                    \"NeckAccessory\", \"ShoulderAccessory\", \"FrontAccessory\", \"BackAccessory\",\n                    \"WaistAccessory\", \"ClimbAnimation\", \"DeathAnimation\", \"FallAnimation\",\n                    \"IdleAnimation\", \"JumpAnimation\", \"RunAnimation\", \"SwimAnimation\",\n                    \"WalkAnimation\", \"PoseAnimation\", \"EarAccessory\", \"EyeAccessory\",\n                    \"EmoteAnimation\", \"Video\"}),\n                AutoIndentRule = def_enum({\"Off\", \"Absolute\", \"Relative\"}),\n                AvatarContextMenuOption = def_enum({\"Friend\", \"Chat\", \"Emote\", \"InspectMenu\"}),\n                AvatarJointPositionType = def_enum({\"Fixed\", \"ArtistIntent\"}),\n                Axis = def_enum({\"X\", \"Y\", \"Z\"}),\n                BinType = def_enum({\"Script\", \"GameTool\", \"Grab\", \"Clone\", \"Hammer\"}),\n                BodyPart = def_enum({\"Head\", \"Torso\", \"LeftArm\", \"RightArm\", \"LeftLeg\", \"RightLeg\"}),\n                BodyPartR15 = def_enum({\"Head\", \"UpperTorso\", \"LowerTorso\", \"LeftFoot\",\n                    \"LeftLowerLeg\", \"LeftUpperLeg\", \"RightFoot\", \"RightLowerLeg\", \"RightUpperLeg\",\n                    \"LeftHand\", \"LeftLowerArm\", \"LeftUpperArm\", \"RightHand\", \"RightLowerArm\",\n                    \"RightUpperArm\", \"RootPart\", \"Unknown\"}),\n                BorderMode = def_enum({\"Outline\", \"Middle\", \"Inset\"}),\n                BreakReason = def_enum({\"Other\", \"Error\", \"UserBreakpoint\", \"SpecialBreakpoint\"}),\n                BulkMoveMode = def_enum({\"FireAllEvents\", \"FireCFrameChanged\", \"FireNoEvents\"}),\n                Button = def_enum({\"Jump\", \"Dismount\"}),\n                ButtonStyle = def_enum({\"Custom\", \"RobloxButtonDefault\", \"RobloxButton\",\n                    \"RobloxRoundButton\", \"RobloxRoundDefaultButton\", \"RobloxRoundDropdownButton\"}),\n                CameraMode = def_enum({\"Classic\", \"LockFirstPerson\"}),\n                CameraPanMode = def_enum({\"Classic\", \"EdgeBump\"}),\n                CameraType = def_enum({\"Fixed\", \"Watch\", \"Attach\", \"Track\", \"Follow\", \"Custom\",\n                    \"Scriptable\", \"Orbital\"}),\n                CellBlock = def_enum({\"Solid\", \"VerticalWedge\", \"CornerWedge\",\n                    \"InverseCornerWedge\", \"HorizontalWedge\"}),\n                CellMaterial = def_enum({\"Empty\", \"Grass\", \"Sand\", \"Brick\", \"Granite\", \"Asphalt\",\n                    \"Iron\", \"Aluminum\", \"Gold\", \"WoodPlank\", \"WoodLog\", \"Gravel\", \"CinderBlock\",\n                    \"MossyStone\", \"Cement\", \"RedPlastic\", \"BluePlastic\", \"Water\"}),\n                CellOrientation = def_enum({\"NegZ\", \"X\", \"Z\", \"NegX\"}),\n                CenterDialogType = def_enum({\"UnsolicitedDialog\", \"PlayerInitiatedDialog\",\n                    \"ModalDialog\", \"QuitDialog\"}),\n                ChatCallbackType = def_enum({\"OnCreatingChatWindow\", \"OnClientSendingMessage\",\n                    \"OnClientFormattingMessage\", \"OnServerReceivingMessage\"}),\n                ChatColor = def_enum({\"Blue\", \"Green\", \"Red\", \"White\"}),\n                ChatMode = def_enum({\"Menu\", \"TextAndMenu\"}),\n                ChatPrivacyMode = def_enum({\"AllUsers\", \"NoOne\", \"Friends\"}),\n                ChatStyle = def_enum({\"Classic\", \"Bubble\", \"ClassicAndBubble\"}),\n                CollisionFidelity = def_enum({\"Default\", \"Hull\", \"Box\",\n                    \"PreciseConvexDecomposition\"}),\n                ComputerCameraMovementMode = def_enum({\"Default\", \"Follow\", \"Classic\", \"Orbital\",\n                    \"CameraToggle\"}),\n                ComputerMovementMode = def_enum({\"Default\", \"KeyboardMouse\", \"ClickToMove\"}),\n                ConnectionError = def_enum({\"OK\", \"DisconnectErrors\", \"DisconnectBadhash\",\n                    \"DisconnectSecurityKeyMismatch\", \"DisconnectNewSecurityKeyMismatch\",\n                    \"DisconnectProtocolMismatch\", \"DisconnectReceivePacketError\",\n                    \"DisconnectReceivePacketStreamError\", \"DisconnectSendPacketError\",\n                    \"DisconnectIllegalTeleport\", \"DisconnectDuplicatePlayer\",\n                    \"DisconnectDuplicateTicket\", \"DisconnectTimeout\", \"DisconnectLuaKick\",\n                    \"DisconnectOnRemoteSysStats\", \"DisconnectHashTimeout\",\n                    \"DisconnectCloudEditKick\", \"DisconnectPlayerless\", \"DisconnectEvicted\",\n                    \"DisconnectDevMaintenance\", \"DisconnectRobloxMaintenance\", \"DisconnectRejoin\",\n                    \"DisconnectConnectionLost\", \"DisconnectIdle\", \"DisconnectRaknetErrors\",\n                    \"DisconnectWrongVersion\", \"DisconnectBySecurityPolicy\", \"DisconnectBlockedIP\",\n                    \"PlacelaunchErrors\", \"PlacelaunchDisabled\", \"PlacelaunchError\",\n                    \"PlacelaunchGameEnded\", \"PlacelaunchGameFull\", \"PlacelaunchUserLeft\",\n                    \"PlacelaunchRestricted\", \"PlacelaunchUnauthorized\", \"PlacelaunchFlooded\",\n                    \"PlacelaunchHashExpired\", \"PlacelaunchHashException\",\n                    \"PlacelaunchPartyCannotFit\", \"PlacelaunchHttpError\",\n                    \"PlacelaunchCustomMessage\", \"PlacelaunchOtherError\", \"TeleportErrors\",\n                    \"TeleportFailure\", \"TeleportGameNotFound\", \"TeleportGameEnded\",\n                    \"TeleportGameFull\", \"TeleportUnauthorized\", \"TeleportFlooded\",\n                    \"TeleportIsTeleporting\"}),\n                ConnectionState = def_enum({\"Connected\", \"Disconnected\"}),\n                ContextActionPriority = def_enum({\"Low\", \"Medium\", \"Default\", \"High\"}),\n                ContextActionResult = def_enum({\"Pass\", \"Sink\"}),\n                ControlMode = def_enum({\"MouseLockSwitch\", \"Classic\"}),\n                CoreGuiType = def_enum({\"PlayerList\", \"Health\", \"Backpack\", \"Chat\", \"All\",\n                    \"EmotesMenu\"}),\n                CreatorType = def_enum({\"User\", \"Group\"}),\n                CurrencyType = def_enum({\"Default\", \"Robux\", \"Tix\"}),\n                CustomCameraMode = def_enum({\"Default\", \"Follow\", \"Classic\"}),\n                DataStoreRequestType = def_enum({\"GetAsync\", \"SetIncrementAsync\", \"UpdateAsync\",\n                    \"GetSortedAsync\", \"SetIncrementSortedAsync\", \"OnUpdate\"}),\n                DevCameraOcclusionMode = def_enum({\"Zoom\", \"Invisicam\"}),\n                DevComputerCameraMovementMode = def_enum({\"UserChoice\", \"Classic\", \"Follow\",\n                    \"Orbital\", \"CameraToggle\"}),\n                DevComputerMovementMode = def_enum({\"UserChoice\", \"KeyboardMouse\", \"ClickToMove\",\n                    \"Scriptable\"}),\n                DevTouchCameraMovementMode = def_enum({\"UserChoice\", \"Classic\", \"Follow\",\n                    \"Orbital\"}),\n                DevTouchMovementMode = def_enum({\"UserChoice\", \"Thumbstick\", \"DPad\", \"Thumbpad\",\n                    \"ClickToMove\", \"Scriptable\", \"DynamicThumbstick\"}),\n                DeveloperMemoryTag = def_enum({\"Internal\", \"HttpCache\", \"Instances\", \"Signals\",\n                    \"LuaHeap\", \"Script\", \"PhysicsCollision\", \"PhysicsParts\", \"GraphicsSolidModels\",\n                    \"GraphicsMeshParts\", \"GraphicsParticles\", \"GraphicsParts\",\n                    \"GraphicsSpatialHash\", \"GraphicsTerrain\", \"GraphicsTexture\",\n                    \"GraphicsTextureCharacter\", \"Sounds\", \"StreamingSounds\", \"TerrainVoxels\",\n                    \"Gui\", \"Animation\", \"Navigation\"}),\n                DeviceType = def_enum({\"Unknown\", \"Desktop\", \"Tablet\", \"Phone\"}),\n                DialogBehaviorType = def_enum({\"SinglePlayer\", \"MultiplePlayers\"}),\n                DialogPurpose = def_enum({\"Quest\", \"Help\", \"Shop\"}),\n                DialogTone = def_enum({\"Neutral\", \"Friendly\", \"Enemy\"}),\n                DominantAxis = def_enum({\"Width\", \"Height\"}),\n                DraftStatusCode = def_enum({\"OK\", \"DraftOutdated\", \"ScriptRemoved\",\n                    \"DraftCommitted\"}),\n                EasingDirection = def_enum({\"In\", \"Out\", \"InOut\"}),\n                EasingStyle = def_enum({\"Linear\", \"Sine\", \"Back\", \"Quad\", \"Quart\", \"Quint\",\n                    \"Bounce\", \"Elastic\", \"Exponential\", \"Circular\", \"Cubic\"}),\n                ElasticBehavior = def_enum({\"WhenScrollable\", \"Always\", \"Never\"}),\n                EnviromentalPhysicsThrottle = def_enum({\"DefaultAuto\", \"Disabled\", \"Always\",\n                    \"Skip2\", \"Skip4\", \"Skip8\", \"Skip16\"}),\n                ExplosionType = def_enum({\"NoCraters\", \"Craters\"}),\n                FillDirection = def_enum({\"Horizontal\", \"Vertical\"}),\n                FilterResult = def_enum({\"Rejected\", \"Accepted\"}),\n                Font = def_enum({\"Legacy\", \"Arial\", \"ArialBold\", \"SourceSans\", \"SourceSansBold\",\n                    \"SourceSansSemibold\", \"SourceSansLight\", \"SourceSansItalic\", \"Bodoni\",\n                    \"Garamond\", \"Cartoon\", \"Code\", \"Highway\", \"SciFi\", \"Arcade\", \"Fantasy\",\n                    \"Antique\", \"Gotham\", \"GothamSemibold\", \"GothamBold\", \"GothamBlack\"}),\n                FontSize = def_enum({\"Size8\", \"Size9\", \"Size10\", \"Size11\", \"Size12\", \"Size14\",\n                    \"Size18\", \"Size24\", \"Size36\", \"Size48\", \"Size28\", \"Size32\", \"Size42\", \"Size60\",\n                    \"Size96\"}),\n                FormFactor = def_enum({\"Symmetric\", \"Brick\", \"Plate\", \"Custom\"}),\n                FrameStyle = def_enum({\"Custom\", \"ChatBlue\", \"RobloxSquare\", \"RobloxRound\",\n                    \"ChatGreen\", \"ChatRed\", \"DropShadow\"}),\n                FramerateManagerMode = def_enum({\"Automatic\", \"On\", \"Off\"}),\n                FriendRequestEvent = def_enum({\"Issue\", \"Revoke\", \"Accept\", \"Deny\"}),\n                FriendStatus = def_enum({\"Unknown\", \"NotFriend\", \"Friend\", \"FriendRequestSent\",\n                    \"FriendRequestReceived\"}),\n                FunctionalTestResult = def_enum({\"Passed\", \"Warning\", \"Error\"}),\n                GameAvatarType = def_enum({\"R6\", \"R15\", \"PlayerChoice\"}),\n                GearGenreSetting = def_enum({\"AllGenres\", \"MatchingGenreOnly\"}),\n                GearType = def_enum({\"MeleeWeapons\", \"RangedWeapons\", \"Explosives\", \"PowerUps\",\n                    \"NavigationEnhancers\", \"MusicalInstruments\", \"SocialItems\", \"BuildingTools\",\n                    \"Transport\"}),\n                Genre = def_enum({\"All\", \"TownAndCity\", \"Fantasy\", \"SciFi\", \"Ninja\", \"Scary\",\n                    \"Pirate\", \"Adventure\", \"Sports\", \"Funny\", \"WildWest\", \"War\", \"SkatePark\",\n                    \"Tutorial\"}),\n                GraphicsMode = def_enum({\"Automatic\", \"Direct3D9\", \"Direct3D11\", \"OpenGL\", \"Metal\",\n                    \"Vulkan\", \"NoGraphics\"}),\n                HandlesStyle = def_enum({\"Resize\", \"Movement\"}),\n                HorizontalAlignment = def_enum({\"Center\", \"Left\", \"Right\"}),\n                HoverAnimateSpeed = def_enum({\"VerySlow\", \"Slow\", \"Medium\", \"Fast\", \"VeryFast\"}),\n                HttpCachePolicy = def_enum({\"None\", \"Full\", \"DataOnly\", \"Default\",\n                    \"InternalRedirectRefresh\"}),\n                HttpContentType = def_enum({\"ApplicationJson\", \"ApplicationXml\",\n                    \"ApplicationUrlEncoded\", \"TextPlain\", \"TextXml\"}),\n                HttpError = def_enum({\"OK\", \"InvalidUrl\", \"DnsResolve\", \"ConnectFail\",\n                    \"OutOfMemory\", \"TimedOut\", \"TooManyRedirects\", \"InvalidRedirect\", \"NetFail\",\n                    \"Aborted\", \"SslConnectFail\", \"SslVerificationFail\", \"Unknown\"}),\n                HttpRequestType = def_enum({\"Default\", \"MarketplaceService\", \"Players\", \"Chat\",\n                    \"Avatar\", \"Analytics\", \"Localization\"}),\n                HumanoidCollisionType = def_enum({\"OuterBox\", \"InnerBox\"}),\n                HumanoidDisplayDistanceType = def_enum({\"Viewer\", \"Subject\", \"None\"}),\n                HumanoidHealthDisplayType = def_enum({\"DisplayWhenDamaged\", \"AlwaysOn\",\n                    \"AlwaysOff\"}),\n                HumanoidRigType = def_enum({\"R6\", \"R15\"}),\n                HumanoidStateType = def_enum({\"FallingDown\", \"Running\", \"RunningNoPhysics\",\n                    \"Climbing\", \"StrafingNoPhysics\", \"Ragdoll\", \"GettingUp\", \"Jumping\", \"Landed\",\n                    \"Flying\", \"Freefall\", \"Seated\", \"PlatformStanding\", \"Dead\", \"Swimming\",\n                    \"Physics\", \"None\"}),\n                IKCollisionsMode = def_enum({\"NoCollisions\", \"OtherMechanismsAnchored\",\n                    \"IncludeContactedMechanisms\"}),\n                InOut = def_enum({\"Edge\", \"Inset\", \"Center\"}),\n                InfoType = def_enum({\"Asset\", \"Product\", \"GamePass\", \"Subscription\", \"Bundle\"}),\n                InitialDockState = def_enum({\"Top\", \"Bottom\", \"Left\", \"Right\", \"Float\"}),\n                InlineAlignment = def_enum({\"Bottom\", \"Center\", \"Top\"}),\n                InputType = def_enum({\"NoInput\", \"Constant\", \"Sin\"}),\n                JointCreationMode = def_enum({\"All\", \"Surface\", \"None\"}),\n                KeyCode = def_enum({\"Unknown\", \"Backspace\", \"Tab\", \"Clear\", \"Return\", \"Pause\",\n                    \"Escape\", \"Space\", \"QuotedDouble\", \"Hash\", \"Dollar\", \"Percent\", \"Ampersand\",\n                    \"Quote\", \"LeftParenthesis\", \"RightParenthesis\", \"Asterisk\", \"Plus\", \"Comma\",\n                    \"Minus\", \"Period\", \"Slash\", \"Zero\", \"One\", \"Two\", \"Three\", \"Four\", \"Five\",\n                    \"Six\", \"Seven\", \"Eight\", \"Nine\", \"Colon\", \"Semicolon\", \"LessThan\", \"Equals\",\n                    \"GreaterThan\", \"Question\", \"At\", \"LeftBracket\", \"BackSlash\", \"RightBracket\",\n                    \"Caret\", \"Underscore\", \"Backquote\", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\",\n                    \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\",\n                    \"Y\", \"Z\", \"LeftCurly\", \"Pipe\", \"RightCurly\", \"Tilde\", \"Delete\", \"KeypadZero\",\n                    \"KeypadOne\", \"KeypadTwo\", \"KeypadThree\", \"KeypadFour\", \"KeypadFive\",\n                    \"KeypadSix\", \"KeypadSeven\", \"KeypadEight\", \"KeypadNine\", \"KeypadPeriod\",\n                    \"KeypadDivide\", \"KeypadMultiply\", \"KeypadMinus\", \"KeypadPlus\", \"KeypadEnter\",\n                    \"KeypadEquals\", \"Up\", \"Down\", \"Right\", \"Left\", \"Insert\", \"Home\", \"End\",\n                    \"PageUp\", \"PageDown\", \"LeftShift\", \"RightShift\", \"LeftMeta\", \"RightMeta\",\n                    \"LeftAlt\", \"RightAlt\", \"LeftControl\", \"RightControl\", \"CapsLock\", \"NumLock\",\n                    \"ScrollLock\", \"LeftSuper\", \"RightSuper\", \"Mode\", \"Compose\", \"Help\", \"Print\",\n                    \"SysReq\", \"Break\", \"Menu\", \"Power\", \"Euro\", \"Undo\", \"F1\", \"F2\", \"F3\", \"F4\",\n                    \"F5\", \"F6\", \"F7\", \"F8\", \"F9\", \"F10\", \"F11\", \"F12\", \"F13\", \"F14\", \"F15\",\n                    \"World0\", \"World1\", \"World2\", \"World3\", \"World4\", \"World5\", \"World6\", \"World7\",\n                    \"World8\", \"World9\", \"World10\", \"World11\", \"World12\", \"World13\", \"World14\",\n                    \"World15\", \"World16\", \"World17\", \"World18\", \"World19\", \"World20\", \"World21\",\n                    \"World22\", \"World23\", \"World24\", \"World25\", \"World26\", \"World27\", \"World28\",\n                    \"World29\", \"World30\", \"World31\", \"World32\", \"World33\", \"World34\", \"World35\",\n                    \"World36\", \"World37\", \"World38\", \"World39\", \"World40\", \"World41\", \"World42\",\n                    \"World43\", \"World44\", \"World45\", \"World46\", \"World47\", \"World48\", \"World49\",\n                    \"World50\", \"World51\", \"World52\", \"World53\", \"World54\", \"World55\", \"World56\",\n                    \"World57\", \"World58\", \"World59\", \"World60\", \"World61\", \"World62\", \"World63\",\n                    \"World64\", \"World65\", \"World66\", \"World67\", \"World68\", \"World69\", \"World70\",\n                    \"World71\", \"World72\", \"World73\", \"World74\", \"World75\", \"World76\", \"World77\",\n                    \"World78\", \"World79\", \"World80\", \"World81\", \"World82\", \"World83\", \"World84\",\n                    \"World85\", \"World86\", \"World87\", \"World88\", \"World89\", \"World90\", \"World91\",\n                    \"World92\", \"World93\", \"World94\", \"World95\", \"ButtonX\", \"ButtonY\", \"ButtonA\",\n                    \"ButtonB\", \"ButtonR1\", \"ButtonL1\", \"ButtonR2\", \"ButtonL2\", \"ButtonR3\",\n                    \"ButtonL3\", \"ButtonStart\", \"ButtonSelect\", \"DPadLeft\", \"DPadRight\", \"DPadUp\",\n                    \"DPadDown\", \"Thumbstick1\", \"Thumbstick2\"}),\n                KeywordFilterType = def_enum({\"Include\", \"Exclude\"}),\n                Language = def_enum({\"Default\"}),\n                LanguagePreference = def_enum({\"SystemDefault\", \"English\", \"SimplifiedChinese\",\n                    \"Korean\"}),\n                LeftRight = def_enum({\"Left\", \"Center\", \"Right\"}),\n                LevelOfDetailSetting = def_enum({\"High\", \"Medium\", \"Low\"}),\n                Limb = def_enum({\"Head\", \"Torso\", \"LeftArm\", \"RightArm\", \"LeftLeg\", \"RightLeg\",\n                    \"Unknown\"}),\n                ListDisplayMode = def_enum({\"Horizontal\", \"Vertical\"}),\n                ListenerType = def_enum({\"Camera\", \"CFrame\", \"ObjectPosition\", \"ObjectCFrame\"}),\n                Material = def_enum({\"Plastic\", \"Wood\", \"Slate\", \"Concrete\", \"CorrodedMetal\",\n                    \"DiamondPlate\", \"Foil\", \"Grass\", \"Ice\", \"Marble\", \"Granite\", \"Brick\", \"Pebble\",\n                    \"Sand\", \"Fabric\", \"SmoothPlastic\", \"Metal\", \"WoodPlanks\", \"Cobblestone\", \"Air\",\n                    \"Water\", \"Rock\", \"Glacier\", \"Snow\", \"Sandstone\", \"Mud\", \"Basalt\", \"Ground\",\n                    \"CrackedLava\", \"Neon\", \"Glass\", \"Asphalt\", \"LeafyGrass\", \"Salt\", \"Limestone\",\n                    \"Pavement\", \"ForceField\"}),\n                MembershipType = def_enum({\"None\", \"BuildersClub\", \"TurboBuildersClub\",\n                    \"OutrageousBuildersClub\", \"Premium\"}),\n                MeshType = def_enum({\"Head\", \"Torso\", \"Wedge\", \"Prism\", \"Pyramid\", \"ParallelRamp\",\n                    \"RightAngleRamp\", \"CornerWedge\", \"Brick\", \"Sphere\", \"Cylinder\", \"FileMesh\"}),\n                MessageType = def_enum({\"MessageOutput\", \"MessageInfo\", \"MessageWarning\",\n                    \"MessageError\"}),\n                ModifierKey = def_enum({\"Alt\", \"Ctrl\", \"Meta\", \"Shift\"}),\n                MouseBehavior = def_enum({\"Default\", \"LockCenter\", \"LockCurrentPosition\"}),\n                MoveState = def_enum({\"Stopped\", \"Coasting\", \"Pushing\", \"Stopping\", \"AirFree\"}),\n                NameOcclusion = def_enum({\"OccludeAll\", \"EnemyOcclusion\", \"NoOcclusion\"}),\n                NetworkOwnership = def_enum({\"Automatic\", \"Manual\", \"OnContact\"}),\n                NormalId = def_enum({\"Top\", \"Bottom\", \"Back\", \"Front\", \"Right\", \"Left\"}),\n                OutputLayoutMode = def_enum({\"Horizontal\", \"Vertical\"}),\n                OverrideMouseIconBehavior = def_enum({\"None\", \"ForceShow\", \"ForceHide\"}),\n                PacketPriority = def_enum({\"IMMEDIATE_PRIORITY\", \"HIGH_PRIORITY\",\n                    \"MEDIUM_PRIORITY\", \"LOW_PRIORITY\"}),\n                PartType = def_enum({\"Ball\", \"Block\", \"Cylinder\"}),\n                PathStatus = def_enum({\"Success\", \"ClosestNoPath\", \"ClosestOutOfRange\",\n                    \"FailStartNotEmpty\", \"FailFinishNotEmpty\", \"NoPath\"}),\n                PathWaypointAction = def_enum({\"Walk\", \"Jump\"}),\n                PermissionLevelShown = def_enum({\"Game\", \"RobloxGame\", \"RobloxScript\", \"Studio\",\n                    \"Roblox\"}),\n                Platform = def_enum({\"Windows\", \"OSX\", \"IOS\", \"Android\", \"XBoxOne\", \"PS4\", \"PS3\",\n                    \"XBox360\", \"WiiU\", \"NX\", \"Ouya\", \"AndroidTV\", \"Chromecast\", \"Linux\", \"SteamOS\",\n                    \"WebOS\", \"DOS\", \"BeOS\", \"UWP\", \"None\"}),\n                PlaybackState = def_enum({\"Begin\", \"Delayed\", \"Playing\", \"Paused\", \"Completed\",\n                    \"Cancelled\"}),\n                PlayerActions = def_enum({\"CharacterForward\", \"CharacterBackward\", \"CharacterLeft\",\n                    \"CharacterRight\", \"CharacterJump\"}),\n                PlayerChatType = def_enum({\"All\", \"Team\", \"Whisper\"}),\n                PoseEasingDirection = def_enum({\"Out\", \"InOut\", \"In\"}),\n                PoseEasingStyle = def_enum({\"Linear\", \"Constant\", \"Elastic\", \"Cubic\", \"Bounce\"}),\n                PrivilegeType = def_enum({\"Owner\", \"Admin\", \"Member\", \"Visitor\", \"Banned\"}),\n                ProductPurchaseDecision = def_enum({\"NotProcessedYet\", \"PurchaseGranted\"}),\n                QualityLevel = def_enum({\"Automatic\", \"Level01\", \"Level02\", \"Level03\", \"Level04\",\n                    \"Level05\", \"Level06\", \"Level07\", \"Level08\", \"Level09\", \"Level10\", \"Level11\",\n                    \"Level12\", \"Level13\", \"Level14\", \"Level15\", \"Level16\", \"Level17\", \"Level18\",\n                    \"Level19\", \"Level20\", \"Level21\"}),\n                R15CollisionType = def_enum({\"OuterBox\", \"InnerBox\"}),\n                RaycastFilterType = def_enum({\"Blacklist\", \"Whitelist\"}),\n                RenderFidelity = def_enum({\"Automatic\", \"Precise\"}),\n                RenderPriority = def_enum({\"First\", \"Input\", \"Camera\", \"Character\", \"Last\"}),\n                RenderingTestComparisonMethod = def_enum({\"psnr\", \"diff\"}),\n                ReturnKeyType = def_enum({\"Default\", \"Done\", \"Go\", \"Next\", \"Search\", \"Send\"}),\n                ReverbType = def_enum({\"NoReverb\", \"GenericReverb\", \"PaddedCell\", \"Room\",\n                    \"Bathroom\", \"LivingRoom\", \"StoneRoom\", \"Auditorium\", \"ConcertHall\", \"Cave\",\n                    \"Arena\", \"Hangar\", \"CarpettedHallway\", \"Hallway\", \"StoneCorridor\", \"Alley\",\n                    \"Forest\", \"City\", \"Mountains\", \"Quarry\", \"Plain\", \"ParkingLot\", \"SewerPipe\",\n                    \"UnderWater\"}),\n                RibbonTool = def_enum({\"Select\", \"Scale\", \"Rotate\", \"Move\", \"Transform\",\n                    \"ColorPicker\", \"MaterialPicker\", \"Group\", \"Ungroup\", \"None\"}),\n                RollOffMode = def_enum({\"Inverse\", \"Linear\", \"InverseTapered\", \"LinearSquare\"}),\n                RotationType = def_enum({\"MovementRelative\", \"CameraRelative\"}),\n                RuntimeUndoBehavior = def_enum({\"Aggregate\", \"Snapshot\", \"Hybrid\"}),\n                SaveFilter = def_enum({\"SaveAll\", \"SaveWorld\", \"SaveGame\"}),\n                SavedQualitySetting = def_enum({\"Automatic\", \"QualityLevel1\", \"QualityLevel2\",\n                    \"QualityLevel3\", \"QualityLevel4\", \"QualityLevel5\", \"QualityLevel6\",\n                    \"QualityLevel7\", \"QualityLevel8\", \"QualityLevel9\", \"QualityLevel10\"}),\n                ScaleType = def_enum({\"Stretch\", \"Slice\", \"Tile\", \"Fit\", \"Crop\"}),\n                ScreenOrientation = def_enum({\"LandscapeLeft\", \"LandscapeRight\", \"LandscapeSensor\",\n                    \"Portrait\", \"Sensor\"}),\n                ScrollBarInset = def_enum({\"None\", \"ScrollBar\", \"Always\"}),\n                ScrollingDirection = def_enum({\"X\", \"Y\", \"XY\"}),\n                ServerAudioBehavior = def_enum({\"Enabled\", \"Muted\", \"OnlineGame\"}),\n                SizeConstraint = def_enum({\"RelativeXY\", \"RelativeXX\", \"RelativeYY\"}),\n                SortOrder = def_enum({\"LayoutOrder\", \"Name\", \"Custom\"}),\n                SoundType = def_enum({\"NoSound\", \"Boing\", \"Bomb\", \"Break\", \"Click\", \"Clock\",\n                    \"Slingshot\", \"Page\", \"Ping\", \"Snap\", \"Splat\", \"Step\", \"StepOn\", \"Swoosh\",\n                    \"Victory\"}),\n                SpecialKey = def_enum({\"Insert\", \"Home\", \"End\", \"PageUp\", \"PageDown\", \"ChatHotkey\"}),\n                StartCorner = def_enum({\"TopLeft\", \"TopRight\", \"BottomLeft\", \"BottomRight\"}),\n                Status = def_enum({\"Poison\", \"Confusion\"}),\n                StreamingPauseMode = def_enum({\"Default\", \"Disabled\", \"ClientPhysicsPause\"}),\n                StudioDataModelType = def_enum({\"Edit\", \"PlayClient\", \"PlayServer\", \"RobloxPlugin\",\n                    \"UserPlugin\", \"None\"}),\n                StudioStyleGuideColor = def_enum({\"MainBackground\", \"Titlebar\", \"Dropdown\",\n                    \"Tooltip\", \"Notification\", \"ScrollBar\", \"ScrollBarBackground\", \"TabBar\", \"Tab\",\n                    \"RibbonTab\", \"RibbonTabTopBar\", \"Button\", \"MainButton\", \"RibbonButton\",\n                    \"ViewPortBackground\", \"InputFieldBackground\", \"Item\", \"TableItem\",\n                    \"CategoryItem\", \"GameSettingsTableItem\", \"GameSettingsTooltip\", \"EmulatorBar\",\n                    \"EmulatorDropDown\", \"ColorPickerFrame\", \"CurrentMarker\", \"Border\", \"Shadow\",\n                    \"Light\", \"Dark\", \"Mid\", \"MainText\", \"SubText\", \"TitlebarText\", \"BrightText\",\n                    \"DimmedText\", \"LinkText\", \"WarningText\", \"ErrorText\", \"InfoText\",\n                    \"SensitiveText\", \"ScriptSideWidget\", \"ScriptBackground\", \"ScriptText\",\n                    \"ScriptSelectionText\", \"ScriptSelectionBackground\",\n                    \"ScriptFindSelectionBackground\", \"ScriptMatchingWordSelectionBackground\",\n                    \"ScriptOperator\", \"ScriptNumber\", \"ScriptString\", \"ScriptComment\",\n                    \"ScriptPreprocessor\", \"ScriptKeyword\", \"ScriptBuiltInFunction\",\n                    \"ScriptWarning\", \"ScriptError\", \"ScriptWhitespace\", \"ScriptRuler\",\n                    \"DebuggerCurrentLine\", \"DebuggerErrorLine\", \"DiffFilePathText\",\n                    \"DiffTextHunkInfo\", \"DiffTextNoChange\", \"DiffTextAddition\", \"DiffTextDeletion\",\n                    \"DiffTextSeparatorBackground\", \"DiffTextNoChangeBackground\",\n                    \"DiffTextAdditionBackground\", \"DiffTextDeletionBackground\", \"DiffLineNum\",\n                    \"DiffLineNumSeparatorBackground\", \"DiffLineNumNoChangeBackground\",\n                    \"DiffLineNumAdditionBackground\", \"DiffLineNumDeletionBackground\",\n                    \"DiffFilePathBackground\", \"DiffFilePathBorder\", \"Separator\", \"ButtonBorder\",\n                    \"ButtonText\", \"InputFieldBorder\", \"CheckedFieldBackground\",\n                    \"CheckedFieldBorder\", \"CheckedFieldIndicator\", \"HeaderSection\", \"Midlight\",\n                    \"StatusBar\", \"DialogButton\", \"DialogButtonText\", \"DialogButtonBorder\",\n                    \"DialogMainButton\", \"DialogMainButtonText\"}),\n                StudioStyleGuideModifier = def_enum({\"Default\", \"Selected\", \"Pressed\", \"Disabled\",\n                    \"Hover\"}),\n                Style = def_enum({\"AlternatingSupports\", \"BridgeStyleSupports\", \"NoSupports\"}),\n                SurfaceConstraint = def_enum({\"None\", \"Hinge\", \"SteppingMotor\", \"Motor\"}),\n                SurfaceGuiSizingMode = def_enum({\"FixedSize\", \"PixelsPerStud\"}),\n                SurfaceType = def_enum({\"Smooth\", \"Glue\", \"Weld\", \"Studs\", \"Inlet\", \"Universal\",\n                    \"Hinge\", \"Motor\", \"SteppingMotor\", \"SmoothNoOutlines\"}),\n                SwipeDirection = def_enum({\"Right\", \"Left\", \"Up\", \"Down\", \"None\"}),\n                TableMajorAxis = def_enum({\"RowMajor\", \"ColumnMajor\"}),\n                Technology = def_enum({\"Compatibility\", \"Voxel\", \"ShadowMap\", \"Legacy\"}),\n                TeleportResult = def_enum({\"Success\", \"Failure\", \"GameNotFound\", \"GameEnded\",\n                    \"GameFull\", \"Unauthorized\", \"Flooded\", \"IsTeleporting\"}),\n                TeleportState = def_enum({\"RequestedFromServer\", \"Started\", \"WaitingForServer\",\n                    \"Failed\", \"InProgress\"}),\n                TeleportType = def_enum({\"ToPlace\", \"ToInstance\", \"ToReservedServer\"}),\n                TextFilterContext = def_enum({\"PublicChat\", \"PrivateChat\"}),\n                TextInputType = def_enum({\"Default\", \"NoSuggestions\", \"Number\", \"Email\", \"Phone\",\n                    \"Password\"}),\n                TextTruncate = def_enum({\"None\", \"AtEnd\"}),\n                TextXAlignment = def_enum({\"Left\", \"Center\", \"Right\"}),\n                TextYAlignment = def_enum({\"Top\", \"Center\", \"Bottom\"}),\n                TextureMode = def_enum({\"Stretch\", \"Wrap\", \"Static\"}),\n                TextureQueryType = def_enum({\"NonHumanoid\", \"NonHumanoidOrphaned\", \"Humanoid\",\n                    \"HumanoidOrphaned\"}),\n                ThreadPoolConfig = def_enum({\"Auto\", \"PerCore1\", \"PerCore2\", \"PerCore3\",\n                    \"PerCore4\", \"Threads1\", \"Threads2\", \"Threads3\", \"Threads4\", \"Threads8\",\n                    \"Threads16\"}),\n                ThrottlingPriority = def_enum({\"Extreme\", \"ElevatedOnServer\", \"Default\"}),\n                ThumbnailSize = def_enum({\"Size48x48\", \"Size180x180\", \"Size420x420\", \"Size60x60\",\n                    \"Size100x100\", \"Size150x150\", \"Size352x352\"}),\n                ThumbnailType = def_enum({\"HeadShot\", \"AvatarBust\", \"AvatarThumbnail\"}),\n                TickCountSampleMethod = def_enum({\"Fast\", \"Benchmark\", \"Precise\"}),\n                TopBottom = def_enum({\"Top\", \"Center\", \"Bottom\"}),\n                TouchCameraMovementMode = def_enum({\"Default\", \"Follow\", \"Classic\", \"Orbital\"}),\n                TouchMovementMode = def_enum({\"Default\", \"Thumbstick\", \"DPad\", \"Thumbpad\",\n                    \"ClickToMove\", \"DynamicThumbstick\"}),\n                TweenStatus = def_enum({\"Canceled\", \"Completed\"}),\n                UITheme = def_enum({\"Light\", \"Dark\"}),\n                UiMessageType = def_enum({\"UiMessageError\", \"UiMessageInfo\"}),\n                UploadSetting = def_enum({\"Never\", \"Ask\", \"Always\"}),\n                UserCFrame = def_enum({\"Head\", \"LeftHand\", \"RightHand\"}),\n                UserInputState = def_enum({\"Begin\", \"Change\", \"End\", \"Cancel\", \"None\"}),\n                UserInputType = def_enum({\"MouseButton1\", \"MouseButton2\", \"MouseButton3\",\n                    \"MouseWheel\", \"MouseMovement\", \"Touch\", \"Keyboard\", \"Focus\", \"Accelerometer\",\n                    \"Gyro\", \"Gamepad1\", \"Gamepad2\", \"Gamepad3\", \"Gamepad4\", \"Gamepad5\", \"Gamepad6\",\n                    \"Gamepad7\", \"Gamepad8\", \"TextInput\", \"InputMethod\", \"None\"}),\n                VRTouchpad = def_enum({\"Left\", \"Right\"}),\n                VRTouchpadMode = def_enum({\"Touch\", \"VirtualThumbstick\", \"ABXY\"}),\n                VerticalAlignment = def_enum({\"Center\", \"Top\", \"Bottom\"}),\n                VerticalScrollBarPosition = def_enum({\"Left\", \"Right\"}),\n                VibrationMotor = def_enum({\"Large\", \"Small\", \"LeftTrigger\", \"RightTrigger\",\n                    \"LeftHand\", \"RightHand\"}),\n                VideoQualitySettings = def_enum({\"LowResolution\", \"MediumResolution\",\n                    \"HighResolution\"}),\n                VirtualInputMode = def_enum({\"Recording\", \"Playing\", \"None\"}),\n                WaterDirection = def_enum({\"NegX\", \"X\", \"NegY\", \"Y\", \"NegZ\", \"Z\"}),\n                WaterForce = def_enum({\"None\", \"Small\", \"Medium\", \"Strong\", \"Max\"}),\n                ZIndexBehavior = def_enum({\"Global\", \"Sibling\"}),\n            }\n        }\n    },\n}\n\nstds.testez = {\n    read_globals = {\n        \"describe\",\n        \"it\", \"itFOCUS\", \"itSKIP\",\n        \"FOCUS\", \"SKIP\", \"HACK_NO_XPCALL\",\n        \"expect\",\n    }\n}\n\nstds.plugin = {\n    read_globals = {\n        \"plugin\",\n        \"DebuggerManager\",\n    }\n}\n\nignore = {\n    \"212\", -- unused arguments\n}\n\nstd = \"lua51+roblox\"\n\nfiles[\"**/*.spec.lua\"] = {\n    std = \"+testez\",\n}"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"Lua.completion.callSnippet\": \"Both\",\n    \"Lua.workspace.ignoreSubmodules\": false\n}"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2020 Vesteria, Inc.                                \n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "assets/readme.md",
    "content": "Vesteria Image & Other Assets\n"
  },
  {
    "path": "default.project.json",
    "content": "{\n  \"name\": \"project\",\n  \"tree\": {\n    \"$className\": \"DataModel\",\n    \"Chat\": {\n      \"$className\": \"Chat\",\n      \"$ignoreUnknownInstances\": true,\n      \"$path\": \"src/Chat\"\n    },\n    \"ReplicatedStorage\": {\n      \"$className\": \"ReplicatedStorage\",\n      \"$ignoreUnknownInstances\": true,\n      \"$path\": \"src/ReplicatedStorage\"\n    },\n    \"ServerScriptService\": {\n      \"$className\": \"ServerScriptService\",\n      \"$ignoreUnknownInstances\": false,\n      \"$path\": \"src/ServerScriptService\"\n    },\n    \"StarterGui\": {\n      \"$className\": \"StarterGui\",\n      \"$ignoreUnknownInstances\": true,\n      \"$path\": \"src/StarterGui\"\n    },\n    \"StarterPlayer\": {\n      \"$className\": \"StarterPlayer\",\n      \"StarterCharacterScripts\": {\n        \"$className\": \"StarterCharacterScripts\",\n        \"$ignoreUnknownInstances\": true,\n        \"$path\": \"src/StarterPlayer/StarterCharacterScripts\"\n      },\n      \"StarterPlayerScripts\": {\n        \"$className\": \"StarterPlayerScripts\",\n        \"$ignoreUnknownInstances\": false,\n        \"$path\": \"src/StarterPlayer/StarterPlayerScripts\"\n      },\n      \"$ignoreUnknownInstances\": true\n    }\n  }\n}"
  },
  {
    "path": "readme.md",
    "content": "<div align=\"center\">\n    <a href=\"https://playvesteria.com\">\n        <img src=\"assets/logo-512.png\" alt=\"Vesteria\" height=\"217\" />\n    </a>\n</div>\n\nVesteria is a fantasy MMORPG on Roblox- originally founded by berezaa, sk3let0n and Polymorphic in the Roblox Incubator program.\n\nIn early 2020, Vesteria shut down and the team underwent a huge effort to convert the game to support Rojo and VSCode. Multiple global refactors followed, hackily attempting to salvage the game's codebase. Simulatnously, the game went through a signifcant creative overhaul to simplify and refine the gameplay in an attempt to return to our roots. While the project was never completed and the game has reverted back to the original version, there is still great value to be found in sharing the lessons we learned with the public. \n\nDevForum Announcement: https://devforum.roblox.com/t/vesteria-open-source/842436\n\n## Play Vesteria \nhttps://www.roblox.com/games/2376885433/Vesteria/\n\n## The Vesteria Team\nhttps://www.roblox.com/groups/4238824/The-Vesteria-Team/\n\nCopyright (c) 2020, Vesteria, Inc.\n<br>\nProvided for public use in educational, recreational or commercial purposes under the Apache 2 License.\n"
  },
  {
    "path": "roblox.toml",
    "content": "# This file was @generated by generate-roblox-std at 2020-07-16 00:32:31.501799700 -06:00\n[selene]\nbase = \"lua51\"\nname = \"roblox\"\n[selene.structs.BasePart.\"*\"]\nstruct = \"Instance\"\n\n[selene.structs.BasePart.AncestryChanged]\nstruct = \"Event\"\n\n[selene.structs.BasePart.Anchored]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.Archivable]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.AttributeChanged]\nstruct = \"Event\"\n\n[selene.structs.BasePart.BackParamA]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.BackParamB]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.BackSurface]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.BackSurfaceInput]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.BottomParamA]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.BottomParamB]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.BottomSurface]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.BottomSurfaceInput]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.BreakJoints]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.BrickColor]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.CFrame]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.CanCollide]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.CanCollideWith]\nmethod = true\n\n[[selene.structs.BasePart.CanCollideWith.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.CanSetNetworkOwnership]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.CastShadow]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.CenterOfMass]\nproperty = true\n\n[selene.structs.BasePart.Changed]\nstruct = \"Event\"\n\n[selene.structs.BasePart.ChildAdded]\nstruct = \"Event\"\n\n[selene.structs.BasePart.ChildRemoved]\nstruct = \"Event\"\n\n[selene.structs.BasePart.ClassName]\nproperty = true\n\n[selene.structs.BasePart.ClearAllChildren]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.Clone]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.CollisionGroupId]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.Color]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.CustomPhysicalProperties]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.DescendantAdded]\nstruct = \"Event\"\n\n[selene.structs.BasePart.DescendantRemoving]\nstruct = \"Event\"\n\n[selene.structs.BasePart.Destroy]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.FindFirstAncestor]\nmethod = true\n\n[[selene.structs.BasePart.FindFirstAncestor.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.FindFirstAncestorOfClass]\nmethod = true\n\n[[selene.structs.BasePart.FindFirstAncestorOfClass.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.FindFirstAncestorWhichIsA]\nmethod = true\n\n[[selene.structs.BasePart.FindFirstAncestorWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.FindFirstChild]\nmethod = true\n\n[[selene.structs.BasePart.FindFirstChild.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.BasePart.FindFirstChild.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.FindFirstChildOfClass]\nmethod = true\n\n[[selene.structs.BasePart.FindFirstChildOfClass.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.FindFirstChildWhichIsA]\nmethod = true\n\n[[selene.structs.BasePart.FindFirstChildWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.BasePart.FindFirstChildWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.FrontParamA]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.FrontParamB]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.FrontSurface]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.FrontSurfaceInput]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.GetAttribute]\nmethod = true\n\n[[selene.structs.BasePart.GetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.GetAttributeChangedSignal]\nmethod = true\n\n[[selene.structs.BasePart.GetAttributeChangedSignal.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.GetAttributes]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.GetChildren]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.GetConnectedParts]\nmethod = true\n\n[[selene.structs.BasePart.GetConnectedParts.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.GetDebugId]\nmethod = true\n\n[[selene.structs.BasePart.GetDebugId.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.GetDescendants]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.GetFullName]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.GetJoints]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.GetMass]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.GetNetworkOwner]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.GetNetworkOwnershipAuto]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.GetPropertyChangedSignal]\nmethod = true\n\n[[selene.structs.BasePart.GetPropertyChangedSignal.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.GetRootPart]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.GetTouchingParts]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.IsA]\nmethod = true\n\n[[selene.structs.BasePart.IsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.IsAncestorOf]\nmethod = true\n\n[[selene.structs.BasePart.IsAncestorOf.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.IsDescendantOf]\nmethod = true\n\n[[selene.structs.BasePart.IsDescendantOf.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.IsGrounded]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.LeftParamA]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.LeftParamB]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.LeftSurface]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.LeftSurfaceInput]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.LocalTransparencyModifier]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.Locked]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.MakeJoints]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.Mass]\nproperty = true\n\n[selene.structs.BasePart.Massless]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.Material]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.Name]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.Orientation]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.Parent]\nstruct = \"Instance\"\n\n[selene.structs.BasePart.Position]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.ReceiveAge]\nproperty = true\n\n[selene.structs.BasePart.Reflectance]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.Resize]\nmethod = true\n\n[[selene.structs.BasePart.Resize.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.BasePart.Resize.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.ResizeIncrement]\nproperty = true\n\n[selene.structs.BasePart.ResizeableFaces]\nproperty = true\n\n[selene.structs.BasePart.RightParamA]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.RightParamB]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.RightSurface]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.RightSurfaceInput]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.RootPriority]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.RotVelocity]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.Rotation]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.SetAttribute]\nmethod = true\n\n[[selene.structs.BasePart.SetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.BasePart.SetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.SetNetworkOwner]\nmethod = true\n\n[[selene.structs.BasePart.SetNetworkOwner.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.SetNetworkOwnershipAuto]\nmethod = true\nargs = []\n\n[selene.structs.BasePart.Size]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.SubtractAsync]\nmethod = true\n\n[[selene.structs.BasePart.SubtractAsync.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.BasePart.SubtractAsync.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.BasePart.SubtractAsync.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.TopParamA]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.TopParamB]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.TopSurface]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.TopSurfaceInput]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.TouchEnded]\nstruct = \"Event\"\n\n[selene.structs.BasePart.Touched]\nstruct = \"Event\"\n\n[selene.structs.BasePart.Transparency]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.UnionAsync]\nmethod = true\n\n[[selene.structs.BasePart.UnionAsync.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.BasePart.UnionAsync.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.BasePart.UnionAsync.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.BasePart.Velocity]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.BasePart.WaitForChild]\nmethod = true\n\n[[selene.structs.BasePart.WaitForChild.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.BasePart.WaitForChild.args]]\nrequired = false\ntype = \"any\"\n[selene.structs.Camera.\"*\"]\nstruct = \"Instance\"\n\n[selene.structs.Camera.AncestryChanged]\nstruct = \"Event\"\n\n[selene.structs.Camera.Archivable]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Camera.AttributeChanged]\nstruct = \"Event\"\n\n[selene.structs.Camera.CFrame]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Camera.CameraSubject]\nstruct = \"Instance\"\n\n[selene.structs.Camera.CameraType]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Camera.Changed]\nstruct = \"Event\"\n\n[selene.structs.Camera.ChildAdded]\nstruct = \"Event\"\n\n[selene.structs.Camera.ChildRemoved]\nstruct = \"Event\"\n\n[selene.structs.Camera.ClassName]\nproperty = true\n\n[selene.structs.Camera.ClearAllChildren]\nmethod = true\nargs = []\n\n[selene.structs.Camera.Clone]\nmethod = true\nargs = []\n\n[selene.structs.Camera.DescendantAdded]\nstruct = \"Event\"\n\n[selene.structs.Camera.DescendantRemoving]\nstruct = \"Event\"\n\n[selene.structs.Camera.Destroy]\nmethod = true\nargs = []\n\n[selene.structs.Camera.FieldOfView]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Camera.FindFirstAncestor]\nmethod = true\n\n[[selene.structs.Camera.FindFirstAncestor.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.FindFirstAncestorOfClass]\nmethod = true\n\n[[selene.structs.Camera.FindFirstAncestorOfClass.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.FindFirstAncestorWhichIsA]\nmethod = true\n\n[[selene.structs.Camera.FindFirstAncestorWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.FindFirstChild]\nmethod = true\n\n[[selene.structs.Camera.FindFirstChild.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Camera.FindFirstChild.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.FindFirstChildOfClass]\nmethod = true\n\n[[selene.structs.Camera.FindFirstChildOfClass.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.FindFirstChildWhichIsA]\nmethod = true\n\n[[selene.structs.Camera.FindFirstChildWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Camera.FindFirstChildWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.FirstPersonTransition]\nstruct = \"Event\"\n\n[selene.structs.Camera.Focus]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Camera.GetAttribute]\nmethod = true\n\n[[selene.structs.Camera.GetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.GetAttributeChangedSignal]\nmethod = true\n\n[[selene.structs.Camera.GetAttributeChangedSignal.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.GetAttributes]\nmethod = true\nargs = []\n\n[selene.structs.Camera.GetChildren]\nmethod = true\nargs = []\n\n[selene.structs.Camera.GetDebugId]\nmethod = true\n\n[[selene.structs.Camera.GetDebugId.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.GetDescendants]\nmethod = true\nargs = []\n\n[selene.structs.Camera.GetFullName]\nmethod = true\nargs = []\n\n[selene.structs.Camera.GetPanSpeed]\nmethod = true\nargs = []\n\n[selene.structs.Camera.GetPartsObscuringTarget]\nmethod = true\n\n[[selene.structs.Camera.GetPartsObscuringTarget.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Camera.GetPartsObscuringTarget.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.GetPropertyChangedSignal]\nmethod = true\n\n[[selene.structs.Camera.GetPropertyChangedSignal.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.GetRenderCFrame]\nmethod = true\nargs = []\n\n[selene.structs.Camera.GetRoll]\nmethod = true\nargs = []\n\n[selene.structs.Camera.GetTiltSpeed]\nmethod = true\nargs = []\n\n[selene.structs.Camera.HeadLocked]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Camera.HeadScale]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Camera.InterpolationFinished]\nstruct = \"Event\"\n\n[selene.structs.Camera.IsA]\nmethod = true\n\n[[selene.structs.Camera.IsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.IsAncestorOf]\nmethod = true\n\n[[selene.structs.Camera.IsAncestorOf.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.IsDescendantOf]\nmethod = true\n\n[[selene.structs.Camera.IsDescendantOf.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.Name]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Camera.NearPlaneZ]\nproperty = true\n\n[selene.structs.Camera.Parent]\nstruct = \"Instance\"\n\n[selene.structs.Camera.ScreenPointToRay]\nmethod = true\n\n[[selene.structs.Camera.ScreenPointToRay.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Camera.ScreenPointToRay.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Camera.ScreenPointToRay.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.SetAttribute]\nmethod = true\n\n[[selene.structs.Camera.SetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Camera.SetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.SetCameraPanMode]\nmethod = true\n\n[[selene.structs.Camera.SetCameraPanMode.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.SetImageServerView]\nmethod = true\n\n[[selene.structs.Camera.SetImageServerView.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.SetRoll]\nmethod = true\n\n[[selene.structs.Camera.SetRoll.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.ViewportPointToRay]\nmethod = true\n\n[[selene.structs.Camera.ViewportPointToRay.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Camera.ViewportPointToRay.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Camera.ViewportPointToRay.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.ViewportSize]\nproperty = true\n\n[selene.structs.Camera.WaitForChild]\nmethod = true\n\n[[selene.structs.Camera.WaitForChild.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Camera.WaitForChild.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.WorldToScreenPoint]\nmethod = true\n\n[[selene.structs.Camera.WorldToScreenPoint.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.WorldToViewportPoint]\nmethod = true\n\n[[selene.structs.Camera.WorldToViewportPoint.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Camera.Zoom]\nmethod = true\n\n[[selene.structs.Camera.Zoom.args]]\nrequired = false\ntype = \"any\"\n[selene.structs.DataModel.\"*\"]\nstruct = \"Instance\"\n\n[selene.structs.DataModel.AncestryChanged]\nstruct = \"Event\"\n\n[selene.structs.DataModel.Archivable]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.DataModel.AttributeChanged]\nstruct = \"Event\"\n\n[selene.structs.DataModel.BindToClose]\nmethod = true\n\n[[selene.structs.DataModel.BindToClose.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.Changed]\nstruct = \"Event\"\n\n[selene.structs.DataModel.ChildAdded]\nstruct = \"Event\"\n\n[selene.structs.DataModel.ChildRemoved]\nstruct = \"Event\"\n\n[selene.structs.DataModel.ClassName]\nproperty = true\n\n[selene.structs.DataModel.ClearAllChildren]\nmethod = true\nargs = []\n\n[selene.structs.DataModel.Clone]\nmethod = true\nargs = []\n\n[selene.structs.DataModel.Close]\nstruct = \"Event\"\n\n[selene.structs.DataModel.CloseLate]\nstruct = \"Event\"\n\n[selene.structs.DataModel.CreatorId]\nproperty = true\n\n[selene.structs.DataModel.CreatorType]\nproperty = true\n\n[selene.structs.DataModel.DefineFastFlag]\nmethod = true\n\n[[selene.structs.DataModel.DefineFastFlag.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.DefineFastFlag.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.DefineFastInt]\nmethod = true\n\n[[selene.structs.DataModel.DefineFastInt.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.DefineFastInt.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.DefineFastString]\nmethod = true\n\n[[selene.structs.DataModel.DefineFastString.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.DefineFastString.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.DescendantAdded]\nstruct = \"Event\"\n\n[selene.structs.DataModel.DescendantRemoving]\nstruct = \"Event\"\n\n[selene.structs.DataModel.Destroy]\nmethod = true\nargs = []\n\n[selene.structs.DataModel.FindFirstAncestor]\nmethod = true\n\n[[selene.structs.DataModel.FindFirstAncestor.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.FindFirstAncestorOfClass]\nmethod = true\n\n[[selene.structs.DataModel.FindFirstAncestorOfClass.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.FindFirstAncestorWhichIsA]\nmethod = true\n\n[[selene.structs.DataModel.FindFirstAncestorWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.FindFirstChild]\nmethod = true\n\n[[selene.structs.DataModel.FindFirstChild.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.FindFirstChild.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.FindFirstChildOfClass]\nmethod = true\n\n[[selene.structs.DataModel.FindFirstChildOfClass.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.FindFirstChildWhichIsA]\nmethod = true\n\n[[selene.structs.DataModel.FindFirstChildWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.FindFirstChildWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.FindService]\nmethod = true\n\n[[selene.structs.DataModel.FindService.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.GameId]\nproperty = true\n\n[selene.structs.DataModel.Genre]\nproperty = true\n\n[selene.structs.DataModel.GetAttribute]\nmethod = true\n\n[[selene.structs.DataModel.GetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.GetAttributeChangedSignal]\nmethod = true\n\n[[selene.structs.DataModel.GetAttributeChangedSignal.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.GetAttributes]\nmethod = true\nargs = []\n\n[selene.structs.DataModel.GetChildren]\nmethod = true\nargs = []\n\n[selene.structs.DataModel.GetDebugId]\nmethod = true\n\n[[selene.structs.DataModel.GetDebugId.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.GetDescendants]\nmethod = true\nargs = []\n\n[selene.structs.DataModel.GetEngineFeature]\nmethod = true\n\n[[selene.structs.DataModel.GetEngineFeature.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.GetFastFlag]\nmethod = true\n\n[[selene.structs.DataModel.GetFastFlag.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.GetFastInt]\nmethod = true\n\n[[selene.structs.DataModel.GetFastInt.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.GetFastString]\nmethod = true\n\n[[selene.structs.DataModel.GetFastString.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.GetFullName]\nmethod = true\nargs = []\n\n[selene.structs.DataModel.GetJobIntervalPeakFraction]\nmethod = true\n\n[[selene.structs.DataModel.GetJobIntervalPeakFraction.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.GetJobIntervalPeakFraction.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.GetJobTimePeakFraction]\nmethod = true\n\n[[selene.structs.DataModel.GetJobTimePeakFraction.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.GetJobTimePeakFraction.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.GetJobsExtendedStats]\nmethod = true\nargs = []\n\n[selene.structs.DataModel.GetJobsInfo]\nmethod = true\nargs = []\n\n[selene.structs.DataModel.GetObjects]\nmethod = true\n\n[[selene.structs.DataModel.GetObjects.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.GetObjectsAsync]\nmethod = true\n\n[[selene.structs.DataModel.GetObjectsAsync.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.GetObjectsList]\nmethod = true\n\n[[selene.structs.DataModel.GetObjectsList.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.GetPropertyChangedSignal]\nmethod = true\n\n[[selene.structs.DataModel.GetPropertyChangedSignal.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.GetService]\nmethod = true\n\n[[selene.structs.DataModel.GetService.args]]\ntype = [\"ABTestService\", \"AdService\", \"AnalyticsService\", \"AssetManagerService\", \"AssetService\", \"BadgeService\", \"CoreGui\", \"StarterGui\", \"BrowserService\", \"BulkImportService\", \"CacheableContentProvider\", \"MeshContentProvider\", \"SolidModelContentProvider\", \"ChangeHistoryService\", \"Chat\", \"ClusterPacketCache\", \"CollectionService\", \"ContentProvider\", \"ContextActionService\", \"ControllerService\", \"CookiesService\", \"CorePackages\", \"CoreScriptSyncService\", \"DataStoreService\", \"Debris\", \"DraftsService\", \"EventIngestService\", \"FlagStandService\", \"FlyweightService\", \"CSGDictionaryService\", \"NonReplicatedCSGDictionaryService\", \"FriendService\", \"GamePassService\", \"GamepadService\", \"Geometry\", \"GoogleAnalyticsConfiguration\", \"GroupService\", \"GuiService\", \"GuidRegistryService\", \"HapticService\", \"Hopper\", \"HttpRbxApiService\", \"HttpService\", \"InsertService\", \"InternalContainer\", \"JointsService\", \"KeyboardService\", \"KeyframeSequenceProvider\", \"LanguageService\", \"Lighting\", \"LocalStorageService\", \"AppStorageService\", \"UserStorageService\", \"LocalizationService\", \"LogService\", \"LoginService\", \"LuaWebService\", \"MarketplaceService\", \"MemStorageService\", \"MessagingService\", \"MouseService\", \"NetworkClient\", \"NetworkServer\", \"NetworkSettings\", \"NotificationService\", \"Workspace\", \"PackageService\", \"PathfindingService\", \"PermissionsService\", \"PhysicsService\", \"PlayerEmulatorService\", \"Players\", \"PluginDebugService\", \"PluginGuiService\", \"PointsService\", \"PolicyService\", \"RbxAnalyticsService\", \"RenderSettings\", \"ReplicatedFirst\", \"ReplicatedScriptService\", \"ReplicatedStorage\", \"RobloxPluginGuiService\", \"RobloxReplicatedStorage\", \"RunService\", \"RuntimeScriptService\", \"ScriptContext\", \"ScriptService\", \"Selection\", \"ServerScriptService\", \"ServerStorage\", \"SessionService\", \"SocialService\", \"SoundService\", \"SpawnerService\", \"StarterPack\", \"StarterPlayer\", \"Stats\", \"StopWatchReporter\", \"Studio\", \"StudioData\", \"StudioService\", \"TaskScheduler\", \"Teams\", \"TeleportService\", \"TestService\", \"TextService\", \"ThirdPartyUserService\", \"TimerService\", \"TouchInputService\", \"TweenService\", \"UGCValidationService\", \"UserInputService\", \"UserService\", \"VRService\", \"VersionControlService\", \"VirtualInputManager\", \"VirtualUser\", \"Visit\"]\n\n[selene.structs.DataModel.GraphicsQualityChangeRequest]\nstruct = \"Event\"\n\n[selene.structs.DataModel.HttpGetAsync]\nmethod = true\n\n[[selene.structs.DataModel.HttpGetAsync.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.HttpGetAsync.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.HttpPostAsync]\nmethod = true\n\n[[selene.structs.DataModel.HttpPostAsync.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.HttpPostAsync.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.HttpPostAsync.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.HttpPostAsync.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.InsertObjectsAndJoinIfLegacyAsync]\nmethod = true\n\n[[selene.structs.DataModel.InsertObjectsAndJoinIfLegacyAsync.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.IsA]\nmethod = true\n\n[[selene.structs.DataModel.IsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.IsAncestorOf]\nmethod = true\n\n[[selene.structs.DataModel.IsAncestorOf.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.IsDescendantOf]\nmethod = true\n\n[[selene.structs.DataModel.IsDescendantOf.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.IsLoaded]\nmethod = true\nargs = []\n\n[selene.structs.DataModel.JobId]\nproperty = true\n\n[selene.structs.DataModel.Load]\nmethod = true\n\n[[selene.structs.DataModel.Load.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.Loaded]\nstruct = \"Event\"\n\n[selene.structs.DataModel.Name]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.DataModel.OpenScreenshotsFolder]\nmethod = true\nargs = []\n\n[selene.structs.DataModel.OpenVideosFolder]\nmethod = true\nargs = []\n\n[selene.structs.DataModel.Parent]\nstruct = \"Instance\"\n\n[selene.structs.DataModel.PlaceId]\nproperty = true\n\n[selene.structs.DataModel.PlaceVersion]\nproperty = true\n\n[selene.structs.DataModel.PrivateServerId]\nproperty = true\n\n[selene.structs.DataModel.PrivateServerOwnerId]\nproperty = true\n\n[selene.structs.DataModel.ReportInGoogleAnalytics]\nmethod = true\n\n[[selene.structs.DataModel.ReportInGoogleAnalytics.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.ReportInGoogleAnalytics.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.ReportInGoogleAnalytics.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.ReportInGoogleAnalytics.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.ScreenshotReady]\nstruct = \"Event\"\n\n[selene.structs.DataModel.ServiceAdded]\nstruct = \"Event\"\n\n[selene.structs.DataModel.ServiceRemoving]\nstruct = \"Event\"\n\n[selene.structs.DataModel.SetAttribute]\nmethod = true\n\n[[selene.structs.DataModel.SetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.SetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.SetFastFlagForTesting]\nmethod = true\n\n[[selene.structs.DataModel.SetFastFlagForTesting.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.SetFastFlagForTesting.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.SetFastIntForTesting]\nmethod = true\n\n[[selene.structs.DataModel.SetFastIntForTesting.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.SetFastIntForTesting.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.SetFastStringForTesting]\nmethod = true\n\n[[selene.structs.DataModel.SetFastStringForTesting.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.SetFastStringForTesting.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.SetPlaceId]\nmethod = true\n\n[[selene.structs.DataModel.SetPlaceId.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.SetUniverseId]\nmethod = true\n\n[[selene.structs.DataModel.SetUniverseId.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.Shutdown]\nmethod = true\nargs = []\n\n[selene.structs.DataModel.WaitForChild]\nmethod = true\n\n[[selene.structs.DataModel.WaitForChild.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.DataModel.WaitForChild.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.DataModel.Workspace]\nstruct = \"Workspace\"\n[selene.structs.EnumItem.Name]\nproperty = true\n\n[selene.structs.EnumItem.Value]\nproperty = true\n[selene.structs.Event.Connect]\nmethod = true\n\n[[selene.structs.Event.Connect.args]]\ntype = \"function\"\n\n[selene.structs.Event.Wait]\nmethod = true\nargs = []\n[selene.structs.Instance.\"*\"]\nany = true\n[selene.structs.Plugin.\"*\"]\nstruct = \"Instance\"\n\n[selene.structs.Plugin.Activate]\nmethod = true\n\n[[selene.structs.Plugin.Activate.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.AncestryChanged]\nstruct = \"Event\"\n\n[selene.structs.Plugin.Archivable]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Plugin.AttributeChanged]\nstruct = \"Event\"\n\n[selene.structs.Plugin.Changed]\nstruct = \"Event\"\n\n[selene.structs.Plugin.ChildAdded]\nstruct = \"Event\"\n\n[selene.structs.Plugin.ChildRemoved]\nstruct = \"Event\"\n\n[selene.structs.Plugin.ClassName]\nproperty = true\n\n[selene.structs.Plugin.ClearAllChildren]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.Clone]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.CollisionEnabled]\nproperty = true\n\n[selene.structs.Plugin.CreateDockWidgetPluginGui]\nmethod = true\n\n[[selene.structs.Plugin.CreateDockWidgetPluginGui.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.CreateDockWidgetPluginGui.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.CreatePluginAction]\nmethod = true\n\n[[selene.structs.Plugin.CreatePluginAction.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.CreatePluginAction.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.CreatePluginAction.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.CreatePluginAction.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.CreatePluginAction.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.CreatePluginMenu]\nmethod = true\n\n[[selene.structs.Plugin.CreatePluginMenu.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.CreatePluginMenu.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.CreatePluginMenu.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.CreateQWidgetPluginGui]\nmethod = true\n\n[[selene.structs.Plugin.CreateQWidgetPluginGui.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.CreateQWidgetPluginGui.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.CreateToolbar]\nmethod = true\n\n[[selene.structs.Plugin.CreateToolbar.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.Deactivate]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.Deactivation]\nstruct = \"Event\"\n\n[selene.structs.Plugin.DescendantAdded]\nstruct = \"Event\"\n\n[selene.structs.Plugin.DescendantRemoving]\nstruct = \"Event\"\n\n[selene.structs.Plugin.Destroy]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.FindFirstAncestor]\nmethod = true\n\n[[selene.structs.Plugin.FindFirstAncestor.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.FindFirstAncestorOfClass]\nmethod = true\n\n[[selene.structs.Plugin.FindFirstAncestorOfClass.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.FindFirstAncestorWhichIsA]\nmethod = true\n\n[[selene.structs.Plugin.FindFirstAncestorWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.FindFirstChild]\nmethod = true\n\n[[selene.structs.Plugin.FindFirstChild.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.FindFirstChild.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.FindFirstChildOfClass]\nmethod = true\n\n[[selene.structs.Plugin.FindFirstChildOfClass.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.FindFirstChildWhichIsA]\nmethod = true\n\n[[selene.structs.Plugin.FindFirstChildWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.FindFirstChildWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.GetAttribute]\nmethod = true\n\n[[selene.structs.Plugin.GetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.GetAttributeChangedSignal]\nmethod = true\n\n[[selene.structs.Plugin.GetAttributeChangedSignal.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.GetAttributes]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.GetChildren]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.GetDebugId]\nmethod = true\n\n[[selene.structs.Plugin.GetDebugId.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.GetDescendants]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.GetFullName]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.GetItem]\nmethod = true\n\n[[selene.structs.Plugin.GetItem.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.GetItem.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.GetJoinMode]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.GetMouse]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.GetPropertyChangedSignal]\nmethod = true\n\n[[selene.structs.Plugin.GetPropertyChangedSignal.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.GetSelectedRibbonTool]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.GetSetting]\nmethod = true\n\n[[selene.structs.Plugin.GetSetting.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.GridSize]\nproperty = true\n\n[selene.structs.Plugin.ImportFbxAnimation]\nmethod = true\n\n[[selene.structs.Plugin.ImportFbxAnimation.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.ImportFbxAnimation.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.ImportFbxRig]\nmethod = true\n\n[[selene.structs.Plugin.ImportFbxRig.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.Invoke]\nmethod = true\n\n[[selene.structs.Plugin.Invoke.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.Invoke.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.IsA]\nmethod = true\n\n[[selene.structs.Plugin.IsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.IsActivated]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.IsActivatedWithExclusiveMouse]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.IsAncestorOf]\nmethod = true\n\n[[selene.structs.Plugin.IsAncestorOf.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.IsDescendantOf]\nmethod = true\n\n[[selene.structs.Plugin.IsDescendantOf.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.Name]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Plugin.Negate]\nmethod = true\n\n[[selene.structs.Plugin.Negate.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.OnInvoke]\nmethod = true\n\n[[selene.structs.Plugin.OnInvoke.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.OnInvoke.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.OnSetItem]\nmethod = true\n\n[[selene.structs.Plugin.OnSetItem.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.OnSetItem.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.OpenScript]\nmethod = true\n\n[[selene.structs.Plugin.OpenScript.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.OpenScript.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.OpenWikiPage]\nmethod = true\n\n[[selene.structs.Plugin.OpenWikiPage.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.Parent]\nstruct = \"Instance\"\n\n[selene.structs.Plugin.PauseSound]\nmethod = true\n\n[[selene.structs.Plugin.PauseSound.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.PlaySound]\nmethod = true\n\n[[selene.structs.Plugin.PlaySound.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.PromptForExistingAssetId]\nmethod = true\n\n[[selene.structs.Plugin.PromptForExistingAssetId.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.PromptSaveSelection]\nmethod = true\n\n[[selene.structs.Plugin.PromptSaveSelection.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.ResumeSound]\nmethod = true\n\n[[selene.structs.Plugin.ResumeSound.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.SaveSelectedToRoblox]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.SelectRibbonTool]\nmethod = true\n\n[[selene.structs.Plugin.SelectRibbonTool.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.SelectRibbonTool.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.Separate]\nmethod = true\n\n[[selene.structs.Plugin.Separate.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.SetAttribute]\nmethod = true\n\n[[selene.structs.Plugin.SetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.SetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.SetItem]\nmethod = true\n\n[[selene.structs.Plugin.SetItem.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.SetItem.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.SetSetting]\nmethod = true\n\n[[selene.structs.Plugin.SetSetting.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.SetSetting.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.StartDecalDrag]\nmethod = true\n\n[[selene.structs.Plugin.StartDecalDrag.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.StartDrag]\nmethod = true\n\n[[selene.structs.Plugin.StartDrag.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.StopAllSounds]\nmethod = true\nargs = []\n\n[selene.structs.Plugin.Union]\nmethod = true\n\n[[selene.structs.Plugin.Union.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Plugin.Unloading]\nstruct = \"Event\"\n\n[selene.structs.Plugin.WaitForChild]\nmethod = true\n\n[[selene.structs.Plugin.WaitForChild.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Plugin.WaitForChild.args]]\nrequired = false\ntype = \"any\"\n[selene.structs.Script.\"*\"]\nstruct = \"Instance\"\n\n[selene.structs.Script.AncestryChanged]\nstruct = \"Event\"\n\n[selene.structs.Script.Archivable]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Script.AttributeChanged]\nstruct = \"Event\"\n\n[selene.structs.Script.Changed]\nstruct = \"Event\"\n\n[selene.structs.Script.ChildAdded]\nstruct = \"Event\"\n\n[selene.structs.Script.ChildRemoved]\nstruct = \"Event\"\n\n[selene.structs.Script.ClassName]\nproperty = true\n\n[selene.structs.Script.ClearAllChildren]\nmethod = true\nargs = []\n\n[selene.structs.Script.Clone]\nmethod = true\nargs = []\n\n[selene.structs.Script.CurrentEditor]\nstruct = \"Instance\"\n\n[selene.structs.Script.DescendantAdded]\nstruct = \"Event\"\n\n[selene.structs.Script.DescendantRemoving]\nstruct = \"Event\"\n\n[selene.structs.Script.Destroy]\nmethod = true\nargs = []\n\n[selene.structs.Script.Disabled]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Script.FindFirstAncestor]\nmethod = true\n\n[[selene.structs.Script.FindFirstAncestor.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Script.FindFirstAncestorOfClass]\nmethod = true\n\n[[selene.structs.Script.FindFirstAncestorOfClass.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Script.FindFirstAncestorWhichIsA]\nmethod = true\n\n[[selene.structs.Script.FindFirstAncestorWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Script.FindFirstChild]\nmethod = true\n\n[[selene.structs.Script.FindFirstChild.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Script.FindFirstChild.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Script.FindFirstChildOfClass]\nmethod = true\n\n[[selene.structs.Script.FindFirstChildOfClass.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Script.FindFirstChildWhichIsA]\nmethod = true\n\n[[selene.structs.Script.FindFirstChildWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Script.FindFirstChildWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Script.GetAttribute]\nmethod = true\n\n[[selene.structs.Script.GetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Script.GetAttributeChangedSignal]\nmethod = true\n\n[[selene.structs.Script.GetAttributeChangedSignal.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Script.GetAttributes]\nmethod = true\nargs = []\n\n[selene.structs.Script.GetChildren]\nmethod = true\nargs = []\n\n[selene.structs.Script.GetDebugId]\nmethod = true\n\n[[selene.structs.Script.GetDebugId.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Script.GetDescendants]\nmethod = true\nargs = []\n\n[selene.structs.Script.GetFullName]\nmethod = true\nargs = []\n\n[selene.structs.Script.GetHash]\nmethod = true\nargs = []\n\n[selene.structs.Script.GetPropertyChangedSignal]\nmethod = true\n\n[[selene.structs.Script.GetPropertyChangedSignal.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Script.IsA]\nmethod = true\n\n[[selene.structs.Script.IsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Script.IsAncestorOf]\nmethod = true\n\n[[selene.structs.Script.IsAncestorOf.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Script.IsDescendantOf]\nmethod = true\n\n[[selene.structs.Script.IsDescendantOf.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Script.LinkedSource]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Script.Name]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Script.Parent]\nstruct = \"Instance\"\n\n[selene.structs.Script.SetAttribute]\nmethod = true\n\n[[selene.structs.Script.SetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Script.SetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Script.WaitForChild]\nmethod = true\n\n[[selene.structs.Script.WaitForChild.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Script.WaitForChild.args]]\nrequired = false\ntype = \"any\"\n[selene.structs.Workspace.\"*\"]\nstruct = \"Instance\"\n\n[selene.structs.Workspace.AllowThirdPartySales]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Workspace.AncestryChanged]\nstruct = \"Event\"\n\n[selene.structs.Workspace.Archivable]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Workspace.ArePartsTouchingOthers]\nmethod = true\n\n[[selene.structs.Workspace.ArePartsTouchingOthers.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.ArePartsTouchingOthers.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.AttributeChanged]\nstruct = \"Event\"\n\n[selene.structs.Workspace.BreakJoints]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.BulkMoveTo]\nmethod = true\n\n[[selene.structs.Workspace.BulkMoveTo.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.BulkMoveTo.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.BulkMoveTo.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.CalculateJumpDistance]\nmethod = true\n\n[[selene.structs.Workspace.CalculateJumpDistance.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.CalculateJumpDistance.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.CalculateJumpDistance.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.CalculateJumpHeight]\nmethod = true\n\n[[selene.structs.Workspace.CalculateJumpHeight.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.CalculateJumpHeight.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.CalculateJumpPower]\nmethod = true\n\n[[selene.structs.Workspace.CalculateJumpPower.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.CalculateJumpPower.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.Changed]\nstruct = \"Event\"\n\n[selene.structs.Workspace.ChildAdded]\nstruct = \"Event\"\n\n[selene.structs.Workspace.ChildRemoved]\nstruct = \"Event\"\n\n[selene.structs.Workspace.ClassName]\nproperty = true\n\n[selene.structs.Workspace.ClearAllChildren]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.Clone]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.CurrentCamera]\nstruct = \"Camera\"\n\n[selene.structs.Workspace.DescendantAdded]\nstruct = \"Event\"\n\n[selene.structs.Workspace.DescendantRemoving]\nstruct = \"Event\"\n\n[selene.structs.Workspace.Destroy]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.DistributedGameTime]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Workspace.ExperimentalSolverIsEnabled]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.FindFirstAncestor]\nmethod = true\n\n[[selene.structs.Workspace.FindFirstAncestor.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.FindFirstAncestorOfClass]\nmethod = true\n\n[[selene.structs.Workspace.FindFirstAncestorOfClass.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.FindFirstAncestorWhichIsA]\nmethod = true\n\n[[selene.structs.Workspace.FindFirstAncestorWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.FindFirstChild]\nmethod = true\n\n[[selene.structs.Workspace.FindFirstChild.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindFirstChild.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.FindFirstChildOfClass]\nmethod = true\n\n[[selene.structs.Workspace.FindFirstChildOfClass.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.FindFirstChildWhichIsA]\nmethod = true\n\n[[selene.structs.Workspace.FindFirstChildWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindFirstChildWhichIsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.FindPartOnRay]\nmethod = true\n\n[[selene.structs.Workspace.FindPartOnRay.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindPartOnRay.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindPartOnRay.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindPartOnRay.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.FindPartOnRayWithIgnoreList]\nmethod = true\n\n[[selene.structs.Workspace.FindPartOnRayWithIgnoreList.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindPartOnRayWithIgnoreList.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindPartOnRayWithIgnoreList.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindPartOnRayWithIgnoreList.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.FindPartOnRayWithWhitelist]\nmethod = true\n\n[[selene.structs.Workspace.FindPartOnRayWithWhitelist.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindPartOnRayWithWhitelist.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindPartOnRayWithWhitelist.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.FindPartsInRegion3]\nmethod = true\n\n[[selene.structs.Workspace.FindPartsInRegion3.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindPartsInRegion3.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindPartsInRegion3.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.FindPartsInRegion3WithIgnoreList]\nmethod = true\n\n[[selene.structs.Workspace.FindPartsInRegion3WithIgnoreList.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindPartsInRegion3WithIgnoreList.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindPartsInRegion3WithIgnoreList.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.FindPartsInRegion3WithWhiteList]\nmethod = true\n\n[[selene.structs.Workspace.FindPartsInRegion3WithWhiteList.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindPartsInRegion3WithWhiteList.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.FindPartsInRegion3WithWhiteList.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.GetAttribute]\nmethod = true\n\n[[selene.structs.Workspace.GetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.GetAttributeChangedSignal]\nmethod = true\n\n[[selene.structs.Workspace.GetAttributeChangedSignal.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.GetAttributes]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.GetBoundingBox]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.GetChildren]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.GetDebugId]\nmethod = true\n\n[[selene.structs.Workspace.GetDebugId.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.GetDescendants]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.GetExtentsSize]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.GetFullName]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.GetNumAwakeParts]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.GetPhysicsThrottling]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.GetPrimaryPartCFrame]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.GetPropertyChangedSignal]\nmethod = true\n\n[[selene.structs.Workspace.GetPropertyChangedSignal.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.GetRealPhysicsFPS]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.Gravity]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Workspace.IKMoveTo]\nmethod = true\n\n[[selene.structs.Workspace.IKMoveTo.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.IKMoveTo.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.IKMoveTo.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.IKMoveTo.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.IKMoveTo.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.IsA]\nmethod = true\n\n[[selene.structs.Workspace.IsA.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.IsAncestorOf]\nmethod = true\n\n[[selene.structs.Workspace.IsAncestorOf.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.IsDescendantOf]\nmethod = true\n\n[[selene.structs.Workspace.IsDescendantOf.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.IsRegion3Empty]\nmethod = true\n\n[[selene.structs.Workspace.IsRegion3Empty.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.IsRegion3Empty.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.IsRegion3EmptyWithIgnoreList]\nmethod = true\n\n[[selene.structs.Workspace.IsRegion3EmptyWithIgnoreList.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.IsRegion3EmptyWithIgnoreList.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.JoinToOutsiders]\nmethod = true\n\n[[selene.structs.Workspace.JoinToOutsiders.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.JoinToOutsiders.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.MakeJoints]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.MoveTo]\nmethod = true\n\n[[selene.structs.Workspace.MoveTo.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.Name]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Workspace.PGSIsEnabled]\nmethod = true\nargs = []\n\n[selene.structs.Workspace.Parent]\nstruct = \"Instance\"\n\n[selene.structs.Workspace.PhysicsSimulationRate]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Workspace.PrimaryPart]\nstruct = \"BasePart\"\n\n[selene.structs.Workspace.Raycast]\nmethod = true\n\n[[selene.structs.Workspace.Raycast.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.Raycast.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.Raycast.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.SetAttribute]\nmethod = true\n\n[[selene.structs.Workspace.SetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.SetAttribute.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.SetInsertPoint]\nmethod = true\n\n[[selene.structs.Workspace.SetInsertPoint.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.SetInsertPoint.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.SetPhysicsThrottleEnabled]\nmethod = true\n\n[[selene.structs.Workspace.SetPhysicsThrottleEnabled.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.SetPrimaryPartCFrame]\nmethod = true\n\n[[selene.structs.Workspace.SetPrimaryPartCFrame.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.SkinnedMeshEnabled]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Workspace.StreamingMinRadius]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Workspace.StreamingPauseMode]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Workspace.StreamingTargetRadius]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Workspace.TemporaryLegacyPhysicsSolverOverride]\nproperty = true\nwritable = \"overridden\"\n\n[selene.structs.Workspace.Terrain]\nstruct = \"Instance\"\n\n[selene.structs.Workspace.TranslateBy]\nmethod = true\n\n[[selene.structs.Workspace.TranslateBy.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.UnjoinFromOutsiders]\nmethod = true\n\n[[selene.structs.Workspace.UnjoinFromOutsiders.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.WaitForChild]\nmethod = true\n\n[[selene.structs.Workspace.WaitForChild.args]]\nrequired = false\ntype = \"any\"\n\n[[selene.structs.Workspace.WaitForChild.args]]\nrequired = false\ntype = \"any\"\n\n[selene.structs.Workspace.ZoomToExtents]\nmethod = true\nargs = []\n[[Axes.new.args]]\ntype = \"...\"\n[BrickColor.Black]\nargs = []\n\n[BrickColor.Blue]\nargs = []\n\n[BrickColor.DarkGray]\nargs = []\n\n[BrickColor.Gray]\nargs = []\n\n[BrickColor.Green]\nargs = []\n\n[BrickColor.Red]\nargs = []\n\n[BrickColor.White]\nargs = []\n\n[BrickColor.Yellow]\nargs = []\n[[BrickColor.new.args]]\ntype = \"any\"\n\n[[BrickColor.new.args]]\nrequired = false\ntype = \"number\"\n\n[[BrickColor.new.args]]\nrequired = false\ntype = \"number\"\n[[BrickColor.palette.args]]\ntype = \"number\"\n\n[BrickColor.random]\nargs = []\n[[CFrame.Angles.args]]\nrequired = false\ntype = \"number\"\n\n[[CFrame.Angles.args]]\nrequired = false\ntype = \"number\"\n\n[[CFrame.Angles.args]]\nrequired = false\ntype = \"number\"\n[[CFrame.fromAxisAngle.args]]\n[CFrame.fromAxisAngle.args.type]\ndisplay = \"Vector3\"\n\n[[CFrame.fromAxisAngle.args]]\ntype = \"number\"\n[[CFrame.fromEulerAnglesXYZ.args]]\ntype = \"number\"\n\n[[CFrame.fromEulerAnglesXYZ.args]]\ntype = \"number\"\n\n[[CFrame.fromEulerAnglesXYZ.args]]\ntype = \"number\"\n[[CFrame.fromEulerAnglesYXZ.args]]\ntype = \"number\"\n\n[[CFrame.fromEulerAnglesYXZ.args]]\ntype = \"number\"\n\n[[CFrame.fromEulerAnglesYXZ.args]]\ntype = \"number\"\n[[CFrame.fromMatrix.args]]\n[CFrame.fromMatrix.args.type]\ndisplay = \"Vector3\"\n\n[[CFrame.fromMatrix.args]]\n[CFrame.fromMatrix.args.type]\ndisplay = \"Vector3\"\n\n[[CFrame.fromMatrix.args]]\n[CFrame.fromMatrix.args.type]\ndisplay = \"Vector3\"\n\n[[CFrame.fromMatrix.args]]\nrequired = false\n\n[CFrame.fromMatrix.args.type]\ndisplay = \"Vector3\"\n[[CFrame.fromOrientation.args]]\ntype = \"number\"\n\n[[CFrame.fromOrientation.args]]\ntype = \"number\"\n\n[[CFrame.fromOrientation.args]]\ntype = \"number\"\n[[CFrame.new.args]]\nrequired = false\ntype = \"any\"\n\n[[CFrame.new.args]]\nrequired = false\ntype = \"any\"\n\n[[CFrame.new.args]]\nrequired = false\ntype = \"number\"\n\n[[CFrame.new.args]]\nrequired = false\ntype = \"number\"\n\n[[CFrame.new.args]]\nrequired = false\ntype = \"number\"\n\n[[CFrame.new.args]]\nrequired = false\ntype = \"number\"\n\n[[CFrame.new.args]]\nrequired = false\ntype = \"number\"\n\n[[CFrame.new.args]]\nrequired = false\ntype = \"number\"\n\n[[CFrame.new.args]]\nrequired = false\ntype = \"number\"\n\n[[CFrame.new.args]]\nrequired = false\ntype = \"number\"\n\n[[CFrame.new.args]]\nrequired = false\ntype = \"number\"\n\n[[CFrame.new.args]]\nrequired = false\ntype = \"number\"\n[[Color3.fromHSV.args]]\ntype = \"number\"\n\n[[Color3.fromHSV.args]]\ntype = \"number\"\n\n[[Color3.fromHSV.args]]\ntype = \"number\"\n[[Color3.fromRGB.args]]\ntype = \"number\"\n\n[[Color3.fromRGB.args]]\ntype = \"number\"\n\n[[Color3.fromRGB.args]]\ntype = \"number\"\n[[Color3.new.args]]\nrequired = false\ntype = \"number\"\n\n[[Color3.new.args]]\nrequired = false\ntype = \"number\"\n\n[[Color3.new.args]]\nrequired = false\ntype = \"number\"\n[[Color3.toHSV.args]]\n[Color3.toHSV.args.type]\ndisplay = \"Color3\"\n[[ColorSequence.new.args]]\ntype = \"any\"\n\n[[ColorSequence.new.args]]\nrequired = false\n\n[ColorSequence.new.args.type]\ndisplay = \"Color3\"\n[[ColorSequenceKeypoint.new.args]]\ntype = \"number\"\n\n[[ColorSequenceKeypoint.new.args]]\n[ColorSequenceKeypoint.new.args.type]\ndisplay = \"Color3\"\n\n[DebuggerManager]\nargs = []\n[[DockWidgetPluginGuiInfo.new.args]]\nrequired = false\n\n[DockWidgetPluginGuiInfo.new.args.type]\ndisplay = \"InitialDockState\"\n\n[[DockWidgetPluginGuiInfo.new.args]]\nrequired = false\ntype = \"bool\"\n\n[[DockWidgetPluginGuiInfo.new.args]]\nrequired = false\ntype = \"bool\"\n\n[[DockWidgetPluginGuiInfo.new.args]]\nrequired = false\ntype = \"number\"\n\n[[DockWidgetPluginGuiInfo.new.args]]\nrequired = false\ntype = \"number\"\n\n[[DockWidgetPluginGuiInfo.new.args]]\nrequired = false\ntype = \"number\"\n\n[[DockWidgetPluginGuiInfo.new.args]]\nrequired = false\ntype = \"number\"\n[Enum.ABTestLoadingStatus.Error]\nstruct = \"EnumItem\"\n\n[Enum.ABTestLoadingStatus.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ABTestLoadingStatus.Initialized]\nstruct = \"EnumItem\"\n\n[Enum.ABTestLoadingStatus.None]\nstruct = \"EnumItem\"\n\n[Enum.ABTestLoadingStatus.Pending]\nstruct = \"EnumItem\"\n\n[Enum.ABTestLoadingStatus.ShutOff]\nstruct = \"EnumItem\"\n\n[Enum.ABTestLoadingStatus.TimedOut]\nstruct = \"EnumItem\"\n[Enum.ActionType.Draw]\nstruct = \"EnumItem\"\n\n[Enum.ActionType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ActionType.Lose]\nstruct = \"EnumItem\"\n\n[Enum.ActionType.Nothing]\nstruct = \"EnumItem\"\n\n[Enum.ActionType.Pause]\nstruct = \"EnumItem\"\n\n[Enum.ActionType.Win]\nstruct = \"EnumItem\"\n[Enum.ActuatorRelativeTo.Attachment0]\nstruct = \"EnumItem\"\n\n[Enum.ActuatorRelativeTo.Attachment1]\nstruct = \"EnumItem\"\n\n[Enum.ActuatorRelativeTo.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ActuatorRelativeTo.World]\nstruct = \"EnumItem\"\n[Enum.ActuatorType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ActuatorType.Motor]\nstruct = \"EnumItem\"\n\n[Enum.ActuatorType.None]\nstruct = \"EnumItem\"\n\n[Enum.ActuatorType.Servo]\nstruct = \"EnumItem\"\n[Enum.AlignType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.AlignType.Parallel]\nstruct = \"EnumItem\"\n\n[Enum.AlignType.Perpendicular]\nstruct = \"EnumItem\"\n[Enum.AlphaMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.AlphaMode.Overlay]\nstruct = \"EnumItem\"\n\n[Enum.AlphaMode.Transparency]\nstruct = \"EnumItem\"\n[Enum.AnimationPriority.Action]\nstruct = \"EnumItem\"\n\n[Enum.AnimationPriority.Core]\nstruct = \"EnumItem\"\n\n[Enum.AnimationPriority.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.AnimationPriority.Idle]\nstruct = \"EnumItem\"\n\n[Enum.AnimationPriority.Movement]\nstruct = \"EnumItem\"\n[Enum.AppShellActionType.AvatarEditorPageLoaded]\nstruct = \"EnumItem\"\n\n[Enum.AppShellActionType.GamePageLoaded]\nstruct = \"EnumItem\"\n\n[Enum.AppShellActionType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.AppShellActionType.HomePageLoaded]\nstruct = \"EnumItem\"\n\n[Enum.AppShellActionType.None]\nstruct = \"EnumItem\"\n\n[Enum.AppShellActionType.OpenApp]\nstruct = \"EnumItem\"\n\n[Enum.AppShellActionType.ReadConversation]\nstruct = \"EnumItem\"\n\n[Enum.AppShellActionType.TapAvatarTab]\nstruct = \"EnumItem\"\n\n[Enum.AppShellActionType.TapChatTab]\nstruct = \"EnumItem\"\n\n[Enum.AppShellActionType.TapConversationEntry]\nstruct = \"EnumItem\"\n\n[Enum.AppShellActionType.TapGamePageTab]\nstruct = \"EnumItem\"\n\n[Enum.AppShellActionType.TapHomePageTab]\nstruct = \"EnumItem\"\n[Enum.AspectType.FitWithinMaxSize]\nstruct = \"EnumItem\"\n\n[Enum.AspectType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.AspectType.ScaleWithParentSize]\nstruct = \"EnumItem\"\n[Enum.AssetFetchStatus.Failure]\nstruct = \"EnumItem\"\n\n[Enum.AssetFetchStatus.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.AssetFetchStatus.Success]\nstruct = \"EnumItem\"\n[Enum.AssetType.Animation]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Audio]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.BackAccessory]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Badge]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.ClimbAnimation]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.DeathAnimation]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Decal]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.EarAccessory]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.EmoteAnimation]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.EyeAccessory]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Face]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.FaceAccessory]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.FallAnimation]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.FrontAccessory]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.GamePass]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Gear]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.AssetType.HairAccessory]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Hat]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Head]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.IdleAnimation]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Image]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.JumpAnimation]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.LeftArm]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.LeftLeg]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Lua]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Mesh]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.MeshPart]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Model]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.NeckAccessory]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Package]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Pants]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Place]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Plugin]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.PoseAnimation]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.RightArm]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.RightLeg]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.RunAnimation]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Shirt]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.ShoulderAccessory]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.SwimAnimation]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.TeeShirt]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Torso]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.Video]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.WaistAccessory]\nstruct = \"EnumItem\"\n\n[Enum.AssetType.WalkAnimation]\nstruct = \"EnumItem\"\n[Enum.AutoIndentRule.Absolute]\nstruct = \"EnumItem\"\n\n[Enum.AutoIndentRule.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.AutoIndentRule.Off]\nstruct = \"EnumItem\"\n\n[Enum.AutoIndentRule.Relative]\nstruct = \"EnumItem\"\n[Enum.AvatarContextMenuOption.Chat]\nstruct = \"EnumItem\"\n\n[Enum.AvatarContextMenuOption.Emote]\nstruct = \"EnumItem\"\n\n[Enum.AvatarContextMenuOption.Friend]\nstruct = \"EnumItem\"\n\n[Enum.AvatarContextMenuOption.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.AvatarContextMenuOption.InspectMenu]\nstruct = \"EnumItem\"\n[Enum.AvatarJointPositionType.ArtistIntent]\nstruct = \"EnumItem\"\n\n[Enum.AvatarJointPositionType.Fixed]\nstruct = \"EnumItem\"\n\n[Enum.AvatarJointPositionType.GetEnumItems]\nargs = []\nmethod = true\n[Enum.Axis.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.Axis.X]\nstruct = \"EnumItem\"\n\n[Enum.Axis.Y]\nstruct = \"EnumItem\"\n\n[Enum.Axis.Z]\nstruct = \"EnumItem\"\n[Enum.BinType.Clone]\nstruct = \"EnumItem\"\n\n[Enum.BinType.GameTool]\nstruct = \"EnumItem\"\n\n[Enum.BinType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.BinType.Grab]\nstruct = \"EnumItem\"\n\n[Enum.BinType.Hammer]\nstruct = \"EnumItem\"\n\n[Enum.BinType.Script]\nstruct = \"EnumItem\"\n[Enum.BodyPart.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.BodyPart.Head]\nstruct = \"EnumItem\"\n\n[Enum.BodyPart.LeftArm]\nstruct = \"EnumItem\"\n\n[Enum.BodyPart.LeftLeg]\nstruct = \"EnumItem\"\n\n[Enum.BodyPart.RightArm]\nstruct = \"EnumItem\"\n\n[Enum.BodyPart.RightLeg]\nstruct = \"EnumItem\"\n\n[Enum.BodyPart.Torso]\nstruct = \"EnumItem\"\n[Enum.BodyPartR15.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.BodyPartR15.Head]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.LeftFoot]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.LeftHand]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.LeftLowerArm]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.LeftLowerLeg]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.LeftUpperArm]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.LeftUpperLeg]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.LowerTorso]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.RightFoot]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.RightHand]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.RightLowerArm]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.RightLowerLeg]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.RightUpperArm]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.RightUpperLeg]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.RootPart]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.Unknown]\nstruct = \"EnumItem\"\n\n[Enum.BodyPartR15.UpperTorso]\nstruct = \"EnumItem\"\n[Enum.BorderMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.BorderMode.Inset]\nstruct = \"EnumItem\"\n\n[Enum.BorderMode.Middle]\nstruct = \"EnumItem\"\n\n[Enum.BorderMode.Outline]\nstruct = \"EnumItem\"\n[Enum.BreakReason.Error]\nstruct = \"EnumItem\"\n\n[Enum.BreakReason.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.BreakReason.Other]\nstruct = \"EnumItem\"\n\n[Enum.BreakReason.SpecialBreakpoint]\nstruct = \"EnumItem\"\n\n[Enum.BreakReason.UserBreakpoint]\nstruct = \"EnumItem\"\n[Enum.BulkMoveMode.FireAllEvents]\nstruct = \"EnumItem\"\n\n[Enum.BulkMoveMode.FireCFrameChanged]\nstruct = \"EnumItem\"\n\n[Enum.BulkMoveMode.GetEnumItems]\nargs = []\nmethod = true\n[Enum.Button.Dismount]\nstruct = \"EnumItem\"\n\n[Enum.Button.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.Button.Jump]\nstruct = \"EnumItem\"\n[Enum.ButtonStyle.Custom]\nstruct = \"EnumItem\"\n\n[Enum.ButtonStyle.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ButtonStyle.RobloxButton]\nstruct = \"EnumItem\"\n\n[Enum.ButtonStyle.RobloxButtonDefault]\nstruct = \"EnumItem\"\n\n[Enum.ButtonStyle.RobloxRoundButton]\nstruct = \"EnumItem\"\n\n[Enum.ButtonStyle.RobloxRoundDefaultButton]\nstruct = \"EnumItem\"\n\n[Enum.ButtonStyle.RobloxRoundDropdownButton]\nstruct = \"EnumItem\"\n[Enum.CameraMode.Classic]\nstruct = \"EnumItem\"\n\n[Enum.CameraMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.CameraMode.LockFirstPerson]\nstruct = \"EnumItem\"\n[Enum.CameraPanMode.Classic]\nstruct = \"EnumItem\"\n\n[Enum.CameraPanMode.EdgeBump]\nstruct = \"EnumItem\"\n\n[Enum.CameraPanMode.GetEnumItems]\nargs = []\nmethod = true\n[Enum.CameraType.Attach]\nstruct = \"EnumItem\"\n\n[Enum.CameraType.Custom]\nstruct = \"EnumItem\"\n\n[Enum.CameraType.Fixed]\nstruct = \"EnumItem\"\n\n[Enum.CameraType.Follow]\nstruct = \"EnumItem\"\n\n[Enum.CameraType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.CameraType.Orbital]\nstruct = \"EnumItem\"\n\n[Enum.CameraType.Scriptable]\nstruct = \"EnumItem\"\n\n[Enum.CameraType.Track]\nstruct = \"EnumItem\"\n\n[Enum.CameraType.Watch]\nstruct = \"EnumItem\"\n[Enum.CellBlock.CornerWedge]\nstruct = \"EnumItem\"\n\n[Enum.CellBlock.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.CellBlock.HorizontalWedge]\nstruct = \"EnumItem\"\n\n[Enum.CellBlock.InverseCornerWedge]\nstruct = \"EnumItem\"\n\n[Enum.CellBlock.Solid]\nstruct = \"EnumItem\"\n\n[Enum.CellBlock.VerticalWedge]\nstruct = \"EnumItem\"\n[Enum.CellMaterial.Aluminum]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.Asphalt]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.BluePlastic]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.Brick]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.Cement]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.CinderBlock]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.Empty]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.CellMaterial.Gold]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.Granite]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.Grass]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.Gravel]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.Iron]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.MossyStone]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.RedPlastic]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.Sand]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.Water]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.WoodLog]\nstruct = \"EnumItem\"\n\n[Enum.CellMaterial.WoodPlank]\nstruct = \"EnumItem\"\n[Enum.CellOrientation.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.CellOrientation.NegX]\nstruct = \"EnumItem\"\n\n[Enum.CellOrientation.NegZ]\nstruct = \"EnumItem\"\n\n[Enum.CellOrientation.X]\nstruct = \"EnumItem\"\n\n[Enum.CellOrientation.Z]\nstruct = \"EnumItem\"\n[Enum.CenterDialogType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.CenterDialogType.ModalDialog]\nstruct = \"EnumItem\"\n\n[Enum.CenterDialogType.PlayerInitiatedDialog]\nstruct = \"EnumItem\"\n\n[Enum.CenterDialogType.QuitDialog]\nstruct = \"EnumItem\"\n\n[Enum.CenterDialogType.UnsolicitedDialog]\nstruct = \"EnumItem\"\n[Enum.ChatCallbackType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ChatCallbackType.OnClientFormattingMessage]\nstruct = \"EnumItem\"\n\n[Enum.ChatCallbackType.OnClientSendingMessage]\nstruct = \"EnumItem\"\n\n[Enum.ChatCallbackType.OnCreatingChatWindow]\nstruct = \"EnumItem\"\n\n[Enum.ChatCallbackType.OnServerReceivingMessage]\nstruct = \"EnumItem\"\n[Enum.ChatColor.Blue]\nstruct = \"EnumItem\"\n\n[Enum.ChatColor.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ChatColor.Green]\nstruct = \"EnumItem\"\n\n[Enum.ChatColor.Red]\nstruct = \"EnumItem\"\n\n[Enum.ChatColor.White]\nstruct = \"EnumItem\"\n[Enum.ChatMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ChatMode.Menu]\nstruct = \"EnumItem\"\n\n[Enum.ChatMode.TextAndMenu]\nstruct = \"EnumItem\"\n[Enum.ChatPrivacyMode.AllUsers]\nstruct = \"EnumItem\"\n\n[Enum.ChatPrivacyMode.Friends]\nstruct = \"EnumItem\"\n\n[Enum.ChatPrivacyMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ChatPrivacyMode.NoOne]\nstruct = \"EnumItem\"\n[Enum.ChatStyle.Bubble]\nstruct = \"EnumItem\"\n\n[Enum.ChatStyle.Classic]\nstruct = \"EnumItem\"\n\n[Enum.ChatStyle.ClassicAndBubble]\nstruct = \"EnumItem\"\n\n[Enum.ChatStyle.GetEnumItems]\nargs = []\nmethod = true\n[Enum.CollisionFidelity.Box]\nstruct = \"EnumItem\"\n\n[Enum.CollisionFidelity.Default]\nstruct = \"EnumItem\"\n\n[Enum.CollisionFidelity.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.CollisionFidelity.Hull]\nstruct = \"EnumItem\"\n\n[Enum.CollisionFidelity.PreciseConvexDecomposition]\nstruct = \"EnumItem\"\n[Enum.ComputerCameraMovementMode.CameraToggle]\nstruct = \"EnumItem\"\n\n[Enum.ComputerCameraMovementMode.Classic]\nstruct = \"EnumItem\"\n\n[Enum.ComputerCameraMovementMode.Default]\nstruct = \"EnumItem\"\n\n[Enum.ComputerCameraMovementMode.Follow]\nstruct = \"EnumItem\"\n\n[Enum.ComputerCameraMovementMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ComputerCameraMovementMode.Orbital]\nstruct = \"EnumItem\"\n[Enum.ComputerMovementMode.ClickToMove]\nstruct = \"EnumItem\"\n\n[Enum.ComputerMovementMode.Default]\nstruct = \"EnumItem\"\n\n[Enum.ComputerMovementMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ComputerMovementMode.KeyboardMouse]\nstruct = \"EnumItem\"\n[Enum.ConnectionError.DisconnectBadhash]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectBlockedIP]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectBySecurityPolicy]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectCloudEditKick]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectConnectionLost]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectDevMaintenance]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectDuplicatePlayer]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectDuplicateTicket]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectErrors]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectEvicted]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectHashTimeout]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectIdle]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectIllegalTeleport]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectLuaKick]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectNewSecurityKeyMismatch]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectOnRemoteSysStats]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectPlayerless]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectProtocolMismatch]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectRaknetErrors]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectReceivePacketError]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectReceivePacketStreamError]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectRejoin]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectRobloxMaintenance]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectSecurityKeyMismatch]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectSendPacketError]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectTimeout]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.DisconnectWrongVersion]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ConnectionError.OK]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchCustomMessage]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchDisabled]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchError]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchErrors]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchFlooded]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchGameEnded]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchGameFull]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchHashException]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchHashExpired]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchHttpError]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchOtherError]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchPartyCannotFit]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchRestricted]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchUnauthorized]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.PlacelaunchUserLeft]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.TeleportErrors]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.TeleportFailure]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.TeleportFlooded]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.TeleportGameEnded]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.TeleportGameFull]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.TeleportGameNotFound]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.TeleportIsTeleporting]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionError.TeleportUnauthorized]\nstruct = \"EnumItem\"\n[Enum.ConnectionState.Connected]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionState.Disconnected]\nstruct = \"EnumItem\"\n\n[Enum.ConnectionState.GetEnumItems]\nargs = []\nmethod = true\n[Enum.ContextActionPriority.Default]\nstruct = \"EnumItem\"\n\n[Enum.ContextActionPriority.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ContextActionPriority.High]\nstruct = \"EnumItem\"\n\n[Enum.ContextActionPriority.Low]\nstruct = \"EnumItem\"\n\n[Enum.ContextActionPriority.Medium]\nstruct = \"EnumItem\"\n[Enum.ContextActionResult.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ContextActionResult.Pass]\nstruct = \"EnumItem\"\n\n[Enum.ContextActionResult.Sink]\nstruct = \"EnumItem\"\n[Enum.ControlMode.Classic]\nstruct = \"EnumItem\"\n\n[Enum.ControlMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ControlMode.MouseLockSwitch]\nstruct = \"EnumItem\"\n[Enum.CoreGuiType.All]\nstruct = \"EnumItem\"\n\n[Enum.CoreGuiType.Backpack]\nstruct = \"EnumItem\"\n\n[Enum.CoreGuiType.Chat]\nstruct = \"EnumItem\"\n\n[Enum.CoreGuiType.EmotesMenu]\nstruct = \"EnumItem\"\n\n[Enum.CoreGuiType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.CoreGuiType.Health]\nstruct = \"EnumItem\"\n\n[Enum.CoreGuiType.PlayerList]\nstruct = \"EnumItem\"\n[Enum.CreatorType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.CreatorType.Group]\nstruct = \"EnumItem\"\n\n[Enum.CreatorType.User]\nstruct = \"EnumItem\"\n[Enum.CurrencyType.Default]\nstruct = \"EnumItem\"\n\n[Enum.CurrencyType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.CurrencyType.Robux]\nstruct = \"EnumItem\"\n\n[Enum.CurrencyType.Tix]\nstruct = \"EnumItem\"\n[Enum.CustomCameraMode.Classic]\nstruct = \"EnumItem\"\n\n[Enum.CustomCameraMode.Default]\nstruct = \"EnumItem\"\n\n[Enum.CustomCameraMode.Follow]\nstruct = \"EnumItem\"\n\n[Enum.CustomCameraMode.GetEnumItems]\nargs = []\nmethod = true\n[Enum.DataStoreRequestType.GetAsync]\nstruct = \"EnumItem\"\n\n[Enum.DataStoreRequestType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.DataStoreRequestType.GetSortedAsync]\nstruct = \"EnumItem\"\n\n[Enum.DataStoreRequestType.OnUpdate]\nstruct = \"EnumItem\"\n\n[Enum.DataStoreRequestType.SetIncrementAsync]\nstruct = \"EnumItem\"\n\n[Enum.DataStoreRequestType.SetIncrementSortedAsync]\nstruct = \"EnumItem\"\n\n[Enum.DataStoreRequestType.UpdateAsync]\nstruct = \"EnumItem\"\n[Enum.DevCameraOcclusionMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.DevCameraOcclusionMode.Invisicam]\nstruct = \"EnumItem\"\n\n[Enum.DevCameraOcclusionMode.Zoom]\nstruct = \"EnumItem\"\n[Enum.DevComputerCameraMovementMode.CameraToggle]\nstruct = \"EnumItem\"\n\n[Enum.DevComputerCameraMovementMode.Classic]\nstruct = \"EnumItem\"\n\n[Enum.DevComputerCameraMovementMode.Follow]\nstruct = \"EnumItem\"\n\n[Enum.DevComputerCameraMovementMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.DevComputerCameraMovementMode.Orbital]\nstruct = \"EnumItem\"\n\n[Enum.DevComputerCameraMovementMode.UserChoice]\nstruct = \"EnumItem\"\n[Enum.DevComputerMovementMode.ClickToMove]\nstruct = \"EnumItem\"\n\n[Enum.DevComputerMovementMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.DevComputerMovementMode.KeyboardMouse]\nstruct = \"EnumItem\"\n\n[Enum.DevComputerMovementMode.Scriptable]\nstruct = \"EnumItem\"\n\n[Enum.DevComputerMovementMode.UserChoice]\nstruct = \"EnumItem\"\n[Enum.DevTouchCameraMovementMode.Classic]\nstruct = \"EnumItem\"\n\n[Enum.DevTouchCameraMovementMode.Follow]\nstruct = \"EnumItem\"\n\n[Enum.DevTouchCameraMovementMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.DevTouchCameraMovementMode.Orbital]\nstruct = \"EnumItem\"\n\n[Enum.DevTouchCameraMovementMode.UserChoice]\nstruct = \"EnumItem\"\n[Enum.DevTouchMovementMode.ClickToMove]\nstruct = \"EnumItem\"\n\n[Enum.DevTouchMovementMode.DPad]\nstruct = \"EnumItem\"\n\n[Enum.DevTouchMovementMode.DynamicThumbstick]\nstruct = \"EnumItem\"\n\n[Enum.DevTouchMovementMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.DevTouchMovementMode.Scriptable]\nstruct = \"EnumItem\"\n\n[Enum.DevTouchMovementMode.Thumbpad]\nstruct = \"EnumItem\"\n\n[Enum.DevTouchMovementMode.Thumbstick]\nstruct = \"EnumItem\"\n\n[Enum.DevTouchMovementMode.UserChoice]\nstruct = \"EnumItem\"\n[Enum.DeveloperMemoryTag.Animation]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.DeveloperMemoryTag.GraphicsMeshParts]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.GraphicsParticles]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.GraphicsParts]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.GraphicsSolidModels]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.GraphicsSpatialHash]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.GraphicsTerrain]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.GraphicsTexture]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.GraphicsTextureCharacter]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.Gui]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.HttpCache]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.Instances]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.Internal]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.LuaHeap]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.Navigation]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.PhysicsCollision]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.PhysicsParts]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.Script]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.Signals]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.Sounds]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.StreamingSounds]\nstruct = \"EnumItem\"\n\n[Enum.DeveloperMemoryTag.TerrainVoxels]\nstruct = \"EnumItem\"\n[Enum.DeviceType.Desktop]\nstruct = \"EnumItem\"\n\n[Enum.DeviceType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.DeviceType.Phone]\nstruct = \"EnumItem\"\n\n[Enum.DeviceType.Tablet]\nstruct = \"EnumItem\"\n\n[Enum.DeviceType.Unknown]\nstruct = \"EnumItem\"\n[Enum.DialogBehaviorType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.DialogBehaviorType.MultiplePlayers]\nstruct = \"EnumItem\"\n\n[Enum.DialogBehaviorType.SinglePlayer]\nstruct = \"EnumItem\"\n[Enum.DialogPurpose.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.DialogPurpose.Help]\nstruct = \"EnumItem\"\n\n[Enum.DialogPurpose.Quest]\nstruct = \"EnumItem\"\n\n[Enum.DialogPurpose.Shop]\nstruct = \"EnumItem\"\n[Enum.DialogTone.Enemy]\nstruct = \"EnumItem\"\n\n[Enum.DialogTone.Friendly]\nstruct = \"EnumItem\"\n\n[Enum.DialogTone.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.DialogTone.Neutral]\nstruct = \"EnumItem\"\n[Enum.DominantAxis.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.DominantAxis.Height]\nstruct = \"EnumItem\"\n\n[Enum.DominantAxis.Width]\nstruct = \"EnumItem\"\n[Enum.DraftStatusCode.DraftCommitted]\nstruct = \"EnumItem\"\n\n[Enum.DraftStatusCode.DraftOutdated]\nstruct = \"EnumItem\"\n\n[Enum.DraftStatusCode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.DraftStatusCode.OK]\nstruct = \"EnumItem\"\n\n[Enum.DraftStatusCode.ScriptRemoved]\nstruct = \"EnumItem\"\n[Enum.EasingDirection.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.EasingDirection.In]\nstruct = \"EnumItem\"\n\n[Enum.EasingDirection.InOut]\nstruct = \"EnumItem\"\n\n[Enum.EasingDirection.Out]\nstruct = \"EnumItem\"\n[Enum.EasingStyle.Back]\nstruct = \"EnumItem\"\n\n[Enum.EasingStyle.Bounce]\nstruct = \"EnumItem\"\n\n[Enum.EasingStyle.Circular]\nstruct = \"EnumItem\"\n\n[Enum.EasingStyle.Cubic]\nstruct = \"EnumItem\"\n\n[Enum.EasingStyle.Elastic]\nstruct = \"EnumItem\"\n\n[Enum.EasingStyle.Exponential]\nstruct = \"EnumItem\"\n\n[Enum.EasingStyle.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.EasingStyle.Linear]\nstruct = \"EnumItem\"\n\n[Enum.EasingStyle.Quad]\nstruct = \"EnumItem\"\n\n[Enum.EasingStyle.Quart]\nstruct = \"EnumItem\"\n\n[Enum.EasingStyle.Quint]\nstruct = \"EnumItem\"\n\n[Enum.EasingStyle.Sine]\nstruct = \"EnumItem\"\n[Enum.ElasticBehavior.Always]\nstruct = \"EnumItem\"\n\n[Enum.ElasticBehavior.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ElasticBehavior.Never]\nstruct = \"EnumItem\"\n\n[Enum.ElasticBehavior.WhenScrollable]\nstruct = \"EnumItem\"\n[Enum.EnviromentalPhysicsThrottle.Always]\nstruct = \"EnumItem\"\n\n[Enum.EnviromentalPhysicsThrottle.DefaultAuto]\nstruct = \"EnumItem\"\n\n[Enum.EnviromentalPhysicsThrottle.Disabled]\nstruct = \"EnumItem\"\n\n[Enum.EnviromentalPhysicsThrottle.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.EnviromentalPhysicsThrottle.Skip16]\nstruct = \"EnumItem\"\n\n[Enum.EnviromentalPhysicsThrottle.Skip2]\nstruct = \"EnumItem\"\n\n[Enum.EnviromentalPhysicsThrottle.Skip4]\nstruct = \"EnumItem\"\n\n[Enum.EnviromentalPhysicsThrottle.Skip8]\nstruct = \"EnumItem\"\n[Enum.ExplosionType.Craters]\nstruct = \"EnumItem\"\n\n[Enum.ExplosionType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ExplosionType.NoCraters]\nstruct = \"EnumItem\"\n[Enum.FillDirection.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.FillDirection.Horizontal]\nstruct = \"EnumItem\"\n\n[Enum.FillDirection.Vertical]\nstruct = \"EnumItem\"\n[Enum.FilterResult.Accepted]\nstruct = \"EnumItem\"\n\n[Enum.FilterResult.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.FilterResult.Rejected]\nstruct = \"EnumItem\"\n[Enum.Font.Antique]\nstruct = \"EnumItem\"\n\n[Enum.Font.Arcade]\nstruct = \"EnumItem\"\n\n[Enum.Font.Arial]\nstruct = \"EnumItem\"\n\n[Enum.Font.ArialBold]\nstruct = \"EnumItem\"\n\n[Enum.Font.Bodoni]\nstruct = \"EnumItem\"\n\n[Enum.Font.Cartoon]\nstruct = \"EnumItem\"\n\n[Enum.Font.Code]\nstruct = \"EnumItem\"\n\n[Enum.Font.Fantasy]\nstruct = \"EnumItem\"\n\n[Enum.Font.Garamond]\nstruct = \"EnumItem\"\n\n[Enum.Font.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.Font.Gotham]\nstruct = \"EnumItem\"\n\n[Enum.Font.GothamBlack]\nstruct = \"EnumItem\"\n\n[Enum.Font.GothamBold]\nstruct = \"EnumItem\"\n\n[Enum.Font.GothamSemibold]\nstruct = \"EnumItem\"\n\n[Enum.Font.Highway]\nstruct = \"EnumItem\"\n\n[Enum.Font.Legacy]\nstruct = \"EnumItem\"\n\n[Enum.Font.SciFi]\nstruct = \"EnumItem\"\n\n[Enum.Font.SourceSans]\nstruct = \"EnumItem\"\n\n[Enum.Font.SourceSansBold]\nstruct = \"EnumItem\"\n\n[Enum.Font.SourceSansItalic]\nstruct = \"EnumItem\"\n\n[Enum.Font.SourceSansLight]\nstruct = \"EnumItem\"\n\n[Enum.Font.SourceSansSemibold]\nstruct = \"EnumItem\"\n[Enum.FontSize.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.FontSize.Size10]\nstruct = \"EnumItem\"\n\n[Enum.FontSize.Size11]\nstruct = \"EnumItem\"\n\n[Enum.FontSize.Size12]\nstruct = \"EnumItem\"\n\n[Enum.FontSize.Size14]\nstruct = \"EnumItem\"\n\n[Enum.FontSize.Size18]\nstruct = \"EnumItem\"\n\n[Enum.FontSize.Size24]\nstruct = \"EnumItem\"\n\n[Enum.FontSize.Size28]\nstruct = \"EnumItem\"\n\n[Enum.FontSize.Size32]\nstruct = \"EnumItem\"\n\n[Enum.FontSize.Size36]\nstruct = \"EnumItem\"\n\n[Enum.FontSize.Size42]\nstruct = \"EnumItem\"\n\n[Enum.FontSize.Size48]\nstruct = \"EnumItem\"\n\n[Enum.FontSize.Size60]\nstruct = \"EnumItem\"\n\n[Enum.FontSize.Size8]\nstruct = \"EnumItem\"\n\n[Enum.FontSize.Size9]\nstruct = \"EnumItem\"\n\n[Enum.FontSize.Size96]\nstruct = \"EnumItem\"\n[Enum.FormFactor.Brick]\nstruct = \"EnumItem\"\n\n[Enum.FormFactor.Custom]\nstruct = \"EnumItem\"\n\n[Enum.FormFactor.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.FormFactor.Plate]\nstruct = \"EnumItem\"\n\n[Enum.FormFactor.Symmetric]\nstruct = \"EnumItem\"\n[Enum.FrameStyle.ChatBlue]\nstruct = \"EnumItem\"\n\n[Enum.FrameStyle.ChatGreen]\nstruct = \"EnumItem\"\n\n[Enum.FrameStyle.ChatRed]\nstruct = \"EnumItem\"\n\n[Enum.FrameStyle.Custom]\nstruct = \"EnumItem\"\n\n[Enum.FrameStyle.DropShadow]\nstruct = \"EnumItem\"\n\n[Enum.FrameStyle.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.FrameStyle.RobloxRound]\nstruct = \"EnumItem\"\n\n[Enum.FrameStyle.RobloxSquare]\nstruct = \"EnumItem\"\n[Enum.FramerateManagerMode.Automatic]\nstruct = \"EnumItem\"\n\n[Enum.FramerateManagerMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.FramerateManagerMode.Off]\nstruct = \"EnumItem\"\n\n[Enum.FramerateManagerMode.On]\nstruct = \"EnumItem\"\n[Enum.FriendRequestEvent.Accept]\nstruct = \"EnumItem\"\n\n[Enum.FriendRequestEvent.Deny]\nstruct = \"EnumItem\"\n\n[Enum.FriendRequestEvent.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.FriendRequestEvent.Issue]\nstruct = \"EnumItem\"\n\n[Enum.FriendRequestEvent.Revoke]\nstruct = \"EnumItem\"\n[Enum.FriendStatus.Friend]\nstruct = \"EnumItem\"\n\n[Enum.FriendStatus.FriendRequestReceived]\nstruct = \"EnumItem\"\n\n[Enum.FriendStatus.FriendRequestSent]\nstruct = \"EnumItem\"\n\n[Enum.FriendStatus.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.FriendStatus.NotFriend]\nstruct = \"EnumItem\"\n\n[Enum.FriendStatus.Unknown]\nstruct = \"EnumItem\"\n[Enum.FunctionalTestResult.Error]\nstruct = \"EnumItem\"\n\n[Enum.FunctionalTestResult.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.FunctionalTestResult.Passed]\nstruct = \"EnumItem\"\n\n[Enum.FunctionalTestResult.Warning]\nstruct = \"EnumItem\"\n[Enum.GameAvatarType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.GameAvatarType.PlayerChoice]\nstruct = \"EnumItem\"\n\n[Enum.GameAvatarType.R15]\nstruct = \"EnumItem\"\n\n[Enum.GameAvatarType.R6]\nstruct = \"EnumItem\"\n[Enum.GearGenreSetting.AllGenres]\nstruct = \"EnumItem\"\n\n[Enum.GearGenreSetting.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.GearGenreSetting.MatchingGenreOnly]\nstruct = \"EnumItem\"\n[Enum.GearType.BuildingTools]\nstruct = \"EnumItem\"\n\n[Enum.GearType.Explosives]\nstruct = \"EnumItem\"\n\n[Enum.GearType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.GearType.MeleeWeapons]\nstruct = \"EnumItem\"\n\n[Enum.GearType.MusicalInstruments]\nstruct = \"EnumItem\"\n\n[Enum.GearType.NavigationEnhancers]\nstruct = \"EnumItem\"\n\n[Enum.GearType.PowerUps]\nstruct = \"EnumItem\"\n\n[Enum.GearType.RangedWeapons]\nstruct = \"EnumItem\"\n\n[Enum.GearType.SocialItems]\nstruct = \"EnumItem\"\n\n[Enum.GearType.Transport]\nstruct = \"EnumItem\"\n[Enum.Genre.Adventure]\nstruct = \"EnumItem\"\n\n[Enum.Genre.All]\nstruct = \"EnumItem\"\n\n[Enum.Genre.Fantasy]\nstruct = \"EnumItem\"\n\n[Enum.Genre.Funny]\nstruct = \"EnumItem\"\n\n[Enum.Genre.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.Genre.Ninja]\nstruct = \"EnumItem\"\n\n[Enum.Genre.Pirate]\nstruct = \"EnumItem\"\n\n[Enum.Genre.Scary]\nstruct = \"EnumItem\"\n\n[Enum.Genre.SciFi]\nstruct = \"EnumItem\"\n\n[Enum.Genre.SkatePark]\nstruct = \"EnumItem\"\n\n[Enum.Genre.Sports]\nstruct = \"EnumItem\"\n\n[Enum.Genre.TownAndCity]\nstruct = \"EnumItem\"\n\n[Enum.Genre.Tutorial]\nstruct = \"EnumItem\"\n\n[Enum.Genre.War]\nstruct = \"EnumItem\"\n\n[Enum.Genre.WildWest]\nstruct = \"EnumItem\"\n[Enum.GraphicsMode.Automatic]\nstruct = \"EnumItem\"\n\n[Enum.GraphicsMode.Direct3D11]\nstruct = \"EnumItem\"\n\n[Enum.GraphicsMode.Direct3D9]\nstruct = \"EnumItem\"\n\n[Enum.GraphicsMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.GraphicsMode.Metal]\nstruct = \"EnumItem\"\n\n[Enum.GraphicsMode.NoGraphics]\nstruct = \"EnumItem\"\n\n[Enum.GraphicsMode.OpenGL]\nstruct = \"EnumItem\"\n\n[Enum.GraphicsMode.Vulkan]\nstruct = \"EnumItem\"\n[Enum.HandlesStyle.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.HandlesStyle.Movement]\nstruct = \"EnumItem\"\n\n[Enum.HandlesStyle.Resize]\nstruct = \"EnumItem\"\n[Enum.HorizontalAlignment.Center]\nstruct = \"EnumItem\"\n\n[Enum.HorizontalAlignment.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.HorizontalAlignment.Left]\nstruct = \"EnumItem\"\n\n[Enum.HorizontalAlignment.Right]\nstruct = \"EnumItem\"\n[Enum.HoverAnimateSpeed.Fast]\nstruct = \"EnumItem\"\n\n[Enum.HoverAnimateSpeed.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.HoverAnimateSpeed.Medium]\nstruct = \"EnumItem\"\n\n[Enum.HoverAnimateSpeed.Slow]\nstruct = \"EnumItem\"\n\n[Enum.HoverAnimateSpeed.VeryFast]\nstruct = \"EnumItem\"\n\n[Enum.HoverAnimateSpeed.VerySlow]\nstruct = \"EnumItem\"\n[Enum.HttpCachePolicy.DataOnly]\nstruct = \"EnumItem\"\n\n[Enum.HttpCachePolicy.Default]\nstruct = \"EnumItem\"\n\n[Enum.HttpCachePolicy.Full]\nstruct = \"EnumItem\"\n\n[Enum.HttpCachePolicy.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.HttpCachePolicy.InternalRedirectRefresh]\nstruct = \"EnumItem\"\n\n[Enum.HttpCachePolicy.None]\nstruct = \"EnumItem\"\n[Enum.HttpContentType.ApplicationJson]\nstruct = \"EnumItem\"\n\n[Enum.HttpContentType.ApplicationUrlEncoded]\nstruct = \"EnumItem\"\n\n[Enum.HttpContentType.ApplicationXml]\nstruct = \"EnumItem\"\n\n[Enum.HttpContentType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.HttpContentType.TextPlain]\nstruct = \"EnumItem\"\n\n[Enum.HttpContentType.TextXml]\nstruct = \"EnumItem\"\n[Enum.HttpError.Aborted]\nstruct = \"EnumItem\"\n\n[Enum.HttpError.ConnectFail]\nstruct = \"EnumItem\"\n\n[Enum.HttpError.DnsResolve]\nstruct = \"EnumItem\"\n\n[Enum.HttpError.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.HttpError.InvalidRedirect]\nstruct = \"EnumItem\"\n\n[Enum.HttpError.InvalidUrl]\nstruct = \"EnumItem\"\n\n[Enum.HttpError.NetFail]\nstruct = \"EnumItem\"\n\n[Enum.HttpError.OK]\nstruct = \"EnumItem\"\n\n[Enum.HttpError.OutOfMemory]\nstruct = \"EnumItem\"\n\n[Enum.HttpError.SslConnectFail]\nstruct = \"EnumItem\"\n\n[Enum.HttpError.SslVerificationFail]\nstruct = \"EnumItem\"\n\n[Enum.HttpError.TimedOut]\nstruct = \"EnumItem\"\n\n[Enum.HttpError.TooManyRedirects]\nstruct = \"EnumItem\"\n\n[Enum.HttpError.Unknown]\nstruct = \"EnumItem\"\n[Enum.HttpRequestType.Analytics]\nstruct = \"EnumItem\"\n\n[Enum.HttpRequestType.Avatar]\nstruct = \"EnumItem\"\n\n[Enum.HttpRequestType.Chat]\nstruct = \"EnumItem\"\n\n[Enum.HttpRequestType.Default]\nstruct = \"EnumItem\"\n\n[Enum.HttpRequestType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.HttpRequestType.Localization]\nstruct = \"EnumItem\"\n\n[Enum.HttpRequestType.MarketplaceService]\nstruct = \"EnumItem\"\n\n[Enum.HttpRequestType.Players]\nstruct = \"EnumItem\"\n[Enum.HumanoidCollisionType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.HumanoidCollisionType.InnerBox]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidCollisionType.OuterBox]\nstruct = \"EnumItem\"\n[Enum.HumanoidDisplayDistanceType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.HumanoidDisplayDistanceType.None]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidDisplayDistanceType.Subject]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidDisplayDistanceType.Viewer]\nstruct = \"EnumItem\"\n[Enum.HumanoidHealthDisplayType.AlwaysOff]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidHealthDisplayType.AlwaysOn]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidHealthDisplayType.DisplayWhenDamaged]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidHealthDisplayType.GetEnumItems]\nargs = []\nmethod = true\n[Enum.HumanoidRigType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.HumanoidRigType.R15]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidRigType.R6]\nstruct = \"EnumItem\"\n[Enum.HumanoidStateType.Climbing]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.Dead]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.FallingDown]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.Flying]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.Freefall]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.HumanoidStateType.GettingUp]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.Jumping]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.Landed]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.None]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.Physics]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.PlatformStanding]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.Ragdoll]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.Running]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.RunningNoPhysics]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.Seated]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.StrafingNoPhysics]\nstruct = \"EnumItem\"\n\n[Enum.HumanoidStateType.Swimming]\nstruct = \"EnumItem\"\n[Enum.IKCollisionsMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.IKCollisionsMode.IncludeContactedMechanisms]\nstruct = \"EnumItem\"\n\n[Enum.IKCollisionsMode.NoCollisions]\nstruct = \"EnumItem\"\n\n[Enum.IKCollisionsMode.OtherMechanismsAnchored]\nstruct = \"EnumItem\"\n[Enum.InOut.Center]\nstruct = \"EnumItem\"\n\n[Enum.InOut.Edge]\nstruct = \"EnumItem\"\n\n[Enum.InOut.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.InOut.Inset]\nstruct = \"EnumItem\"\n[Enum.InfoType.Asset]\nstruct = \"EnumItem\"\n\n[Enum.InfoType.Bundle]\nstruct = \"EnumItem\"\n\n[Enum.InfoType.GamePass]\nstruct = \"EnumItem\"\n\n[Enum.InfoType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.InfoType.Product]\nstruct = \"EnumItem\"\n\n[Enum.InfoType.Subscription]\nstruct = \"EnumItem\"\n[Enum.InitialDockState.Bottom]\nstruct = \"EnumItem\"\n\n[Enum.InitialDockState.Float]\nstruct = \"EnumItem\"\n\n[Enum.InitialDockState.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.InitialDockState.Left]\nstruct = \"EnumItem\"\n\n[Enum.InitialDockState.Right]\nstruct = \"EnumItem\"\n\n[Enum.InitialDockState.Top]\nstruct = \"EnumItem\"\n[Enum.InlineAlignment.Bottom]\nstruct = \"EnumItem\"\n\n[Enum.InlineAlignment.Center]\nstruct = \"EnumItem\"\n\n[Enum.InlineAlignment.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.InlineAlignment.Top]\nstruct = \"EnumItem\"\n[Enum.InputType.Constant]\nstruct = \"EnumItem\"\n\n[Enum.InputType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.InputType.NoInput]\nstruct = \"EnumItem\"\n\n[Enum.InputType.Sin]\nstruct = \"EnumItem\"\n[Enum.JointCreationMode.All]\nstruct = \"EnumItem\"\n\n[Enum.JointCreationMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.JointCreationMode.None]\nstruct = \"EnumItem\"\n\n[Enum.JointCreationMode.Surface]\nstruct = \"EnumItem\"\n[Enum.KeyCode.A]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Ampersand]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Asterisk]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.At]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.B]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.BackSlash]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Backquote]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Backspace]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Break]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.ButtonA]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.ButtonB]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.ButtonL1]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.ButtonL2]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.ButtonL3]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.ButtonR1]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.ButtonR2]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.ButtonR3]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.ButtonSelect]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.ButtonStart]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.ButtonX]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.ButtonY]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.C]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.CapsLock]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Caret]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Clear]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Colon]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Comma]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Compose]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.D]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.DPadDown]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.DPadLeft]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.DPadRight]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.DPadUp]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Delete]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Dollar]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Down]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.E]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Eight]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.End]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Equals]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Escape]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Euro]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F1]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F10]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F11]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F12]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F13]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F14]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F15]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F2]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F3]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F4]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F5]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F6]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F7]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F8]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.F9]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Five]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Four]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.G]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.KeyCode.GreaterThan]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.H]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Hash]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Help]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Home]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.I]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Insert]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.J]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.K]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadDivide]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadEight]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadEnter]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadEquals]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadFive]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadFour]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadMinus]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadMultiply]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadNine]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadOne]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadPeriod]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadPlus]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadSeven]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadSix]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadThree]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadTwo]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.KeypadZero]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.L]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Left]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.LeftAlt]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.LeftBracket]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.LeftControl]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.LeftCurly]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.LeftMeta]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.LeftParenthesis]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.LeftShift]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.LeftSuper]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.LessThan]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.M]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Menu]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Minus]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Mode]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.N]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Nine]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.NumLock]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.O]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.One]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.P]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.PageDown]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.PageUp]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Pause]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Percent]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Period]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Pipe]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Plus]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Power]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Print]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Q]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Question]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Quote]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.QuotedDouble]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.R]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Return]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Right]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.RightAlt]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.RightBracket]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.RightControl]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.RightCurly]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.RightMeta]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.RightParenthesis]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.RightShift]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.RightSuper]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.S]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.ScrollLock]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Semicolon]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Seven]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Six]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Slash]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Space]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.SysReq]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.T]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Tab]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Three]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Thumbstick1]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Thumbstick2]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Tilde]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Two]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.U]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Underscore]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Undo]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Unknown]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Up]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.V]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.W]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World0]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World1]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World10]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World11]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World12]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World13]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World14]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World15]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World16]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World17]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World18]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World19]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World2]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World20]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World21]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World22]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World23]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World24]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World25]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World26]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World27]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World28]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World29]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World3]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World30]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World31]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World32]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World33]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World34]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World35]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World36]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World37]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World38]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World39]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World4]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World40]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World41]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World42]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World43]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World44]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World45]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World46]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World47]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World48]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World49]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World5]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World50]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World51]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World52]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World53]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World54]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World55]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World56]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World57]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World58]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World59]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World6]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World60]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World61]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World62]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World63]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World64]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World65]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World66]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World67]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World68]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World69]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World7]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World70]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World71]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World72]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World73]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World74]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World75]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World76]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World77]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World78]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World79]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World8]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World80]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World81]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World82]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World83]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World84]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World85]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World86]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World87]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World88]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World89]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World9]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World90]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World91]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World92]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World93]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World94]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.World95]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.X]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Y]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Z]\nstruct = \"EnumItem\"\n\n[Enum.KeyCode.Zero]\nstruct = \"EnumItem\"\n[Enum.KeywordFilterType.Exclude]\nstruct = \"EnumItem\"\n\n[Enum.KeywordFilterType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.KeywordFilterType.Include]\nstruct = \"EnumItem\"\n[Enum.Language.Default]\nstruct = \"EnumItem\"\n\n[Enum.Language.GetEnumItems]\nargs = []\nmethod = true\n[Enum.LanguagePreference.English]\nstruct = \"EnumItem\"\n\n[Enum.LanguagePreference.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.LanguagePreference.Korean]\nstruct = \"EnumItem\"\n\n[Enum.LanguagePreference.SimplifiedChinese]\nstruct = \"EnumItem\"\n\n[Enum.LanguagePreference.SystemDefault]\nstruct = \"EnumItem\"\n[Enum.LeftRight.Center]\nstruct = \"EnumItem\"\n\n[Enum.LeftRight.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.LeftRight.Left]\nstruct = \"EnumItem\"\n\n[Enum.LeftRight.Right]\nstruct = \"EnumItem\"\n[Enum.LevelOfDetailSetting.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.LevelOfDetailSetting.High]\nstruct = \"EnumItem\"\n\n[Enum.LevelOfDetailSetting.Low]\nstruct = \"EnumItem\"\n\n[Enum.LevelOfDetailSetting.Medium]\nstruct = \"EnumItem\"\n[Enum.Limb.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.Limb.Head]\nstruct = \"EnumItem\"\n\n[Enum.Limb.LeftArm]\nstruct = \"EnumItem\"\n\n[Enum.Limb.LeftLeg]\nstruct = \"EnumItem\"\n\n[Enum.Limb.RightArm]\nstruct = \"EnumItem\"\n\n[Enum.Limb.RightLeg]\nstruct = \"EnumItem\"\n\n[Enum.Limb.Torso]\nstruct = \"EnumItem\"\n\n[Enum.Limb.Unknown]\nstruct = \"EnumItem\"\n[Enum.ListDisplayMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ListDisplayMode.Horizontal]\nstruct = \"EnumItem\"\n\n[Enum.ListDisplayMode.Vertical]\nstruct = \"EnumItem\"\n[Enum.ListenerType.CFrame]\nstruct = \"EnumItem\"\n\n[Enum.ListenerType.Camera]\nstruct = \"EnumItem\"\n\n[Enum.ListenerType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ListenerType.ObjectCFrame]\nstruct = \"EnumItem\"\n\n[Enum.ListenerType.ObjectPosition]\nstruct = \"EnumItem\"\n[Enum.Material.Air]\nstruct = \"EnumItem\"\n\n[Enum.Material.Asphalt]\nstruct = \"EnumItem\"\n\n[Enum.Material.Basalt]\nstruct = \"EnumItem\"\n\n[Enum.Material.Brick]\nstruct = \"EnumItem\"\n\n[Enum.Material.Cobblestone]\nstruct = \"EnumItem\"\n\n[Enum.Material.Concrete]\nstruct = \"EnumItem\"\n\n[Enum.Material.CorrodedMetal]\nstruct = \"EnumItem\"\n\n[Enum.Material.CrackedLava]\nstruct = \"EnumItem\"\n\n[Enum.Material.DiamondPlate]\nstruct = \"EnumItem\"\n\n[Enum.Material.Fabric]\nstruct = \"EnumItem\"\n\n[Enum.Material.Foil]\nstruct = \"EnumItem\"\n\n[Enum.Material.ForceField]\nstruct = \"EnumItem\"\n\n[Enum.Material.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.Material.Glacier]\nstruct = \"EnumItem\"\n\n[Enum.Material.Glass]\nstruct = \"EnumItem\"\n\n[Enum.Material.Granite]\nstruct = \"EnumItem\"\n\n[Enum.Material.Grass]\nstruct = \"EnumItem\"\n\n[Enum.Material.Ground]\nstruct = \"EnumItem\"\n\n[Enum.Material.Ice]\nstruct = \"EnumItem\"\n\n[Enum.Material.LeafyGrass]\nstruct = \"EnumItem\"\n\n[Enum.Material.Limestone]\nstruct = \"EnumItem\"\n\n[Enum.Material.Marble]\nstruct = \"EnumItem\"\n\n[Enum.Material.Metal]\nstruct = \"EnumItem\"\n\n[Enum.Material.Mud]\nstruct = \"EnumItem\"\n\n[Enum.Material.Neon]\nstruct = \"EnumItem\"\n\n[Enum.Material.Pavement]\nstruct = \"EnumItem\"\n\n[Enum.Material.Pebble]\nstruct = \"EnumItem\"\n\n[Enum.Material.Plastic]\nstruct = \"EnumItem\"\n\n[Enum.Material.Rock]\nstruct = \"EnumItem\"\n\n[Enum.Material.Salt]\nstruct = \"EnumItem\"\n\n[Enum.Material.Sand]\nstruct = \"EnumItem\"\n\n[Enum.Material.Sandstone]\nstruct = \"EnumItem\"\n\n[Enum.Material.Slate]\nstruct = \"EnumItem\"\n\n[Enum.Material.SmoothPlastic]\nstruct = \"EnumItem\"\n\n[Enum.Material.Snow]\nstruct = \"EnumItem\"\n\n[Enum.Material.Water]\nstruct = \"EnumItem\"\n\n[Enum.Material.Wood]\nstruct = \"EnumItem\"\n\n[Enum.Material.WoodPlanks]\nstruct = \"EnumItem\"\n[Enum.MembershipType.BuildersClub]\nstruct = \"EnumItem\"\n\n[Enum.MembershipType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.MembershipType.None]\nstruct = \"EnumItem\"\n\n[Enum.MembershipType.OutrageousBuildersClub]\nstruct = \"EnumItem\"\n\n[Enum.MembershipType.Premium]\nstruct = \"EnumItem\"\n\n[Enum.MembershipType.TurboBuildersClub]\nstruct = \"EnumItem\"\n[Enum.MeshType.Brick]\nstruct = \"EnumItem\"\n\n[Enum.MeshType.CornerWedge]\nstruct = \"EnumItem\"\n\n[Enum.MeshType.Cylinder]\nstruct = \"EnumItem\"\n\n[Enum.MeshType.FileMesh]\nstruct = \"EnumItem\"\n\n[Enum.MeshType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.MeshType.Head]\nstruct = \"EnumItem\"\n\n[Enum.MeshType.ParallelRamp]\nstruct = \"EnumItem\"\n\n[Enum.MeshType.Prism]\nstruct = \"EnumItem\"\n\n[Enum.MeshType.Pyramid]\nstruct = \"EnumItem\"\n\n[Enum.MeshType.RightAngleRamp]\nstruct = \"EnumItem\"\n\n[Enum.MeshType.Sphere]\nstruct = \"EnumItem\"\n\n[Enum.MeshType.Torso]\nstruct = \"EnumItem\"\n\n[Enum.MeshType.Wedge]\nstruct = \"EnumItem\"\n[Enum.MessageType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.MessageType.MessageError]\nstruct = \"EnumItem\"\n\n[Enum.MessageType.MessageInfo]\nstruct = \"EnumItem\"\n\n[Enum.MessageType.MessageOutput]\nstruct = \"EnumItem\"\n\n[Enum.MessageType.MessageWarning]\nstruct = \"EnumItem\"\n[Enum.ModifierKey.Alt]\nstruct = \"EnumItem\"\n\n[Enum.ModifierKey.Ctrl]\nstruct = \"EnumItem\"\n\n[Enum.ModifierKey.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ModifierKey.Meta]\nstruct = \"EnumItem\"\n\n[Enum.ModifierKey.Shift]\nstruct = \"EnumItem\"\n[Enum.MouseBehavior.Default]\nstruct = \"EnumItem\"\n\n[Enum.MouseBehavior.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.MouseBehavior.LockCenter]\nstruct = \"EnumItem\"\n\n[Enum.MouseBehavior.LockCurrentPosition]\nstruct = \"EnumItem\"\n[Enum.MoveState.AirFree]\nstruct = \"EnumItem\"\n\n[Enum.MoveState.Coasting]\nstruct = \"EnumItem\"\n\n[Enum.MoveState.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.MoveState.Pushing]\nstruct = \"EnumItem\"\n\n[Enum.MoveState.Stopped]\nstruct = \"EnumItem\"\n\n[Enum.MoveState.Stopping]\nstruct = \"EnumItem\"\n[Enum.NameOcclusion.EnemyOcclusion]\nstruct = \"EnumItem\"\n\n[Enum.NameOcclusion.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.NameOcclusion.NoOcclusion]\nstruct = \"EnumItem\"\n\n[Enum.NameOcclusion.OccludeAll]\nstruct = \"EnumItem\"\n[Enum.NetworkOwnership.Automatic]\nstruct = \"EnumItem\"\n\n[Enum.NetworkOwnership.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.NetworkOwnership.Manual]\nstruct = \"EnumItem\"\n\n[Enum.NetworkOwnership.OnContact]\nstruct = \"EnumItem\"\n[Enum.NormalId.Back]\nstruct = \"EnumItem\"\n\n[Enum.NormalId.Bottom]\nstruct = \"EnumItem\"\n\n[Enum.NormalId.Front]\nstruct = \"EnumItem\"\n\n[Enum.NormalId.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.NormalId.Left]\nstruct = \"EnumItem\"\n\n[Enum.NormalId.Right]\nstruct = \"EnumItem\"\n\n[Enum.NormalId.Top]\nstruct = \"EnumItem\"\n[Enum.OutputLayoutMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.OutputLayoutMode.Horizontal]\nstruct = \"EnumItem\"\n\n[Enum.OutputLayoutMode.Vertical]\nstruct = \"EnumItem\"\n[Enum.OverrideMouseIconBehavior.ForceHide]\nstruct = \"EnumItem\"\n\n[Enum.OverrideMouseIconBehavior.ForceShow]\nstruct = \"EnumItem\"\n\n[Enum.OverrideMouseIconBehavior.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.OverrideMouseIconBehavior.None]\nstruct = \"EnumItem\"\n[Enum.PacketPriority.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.PacketPriority.HIGH_PRIORITY]\nstruct = \"EnumItem\"\n\n[Enum.PacketPriority.IMMEDIATE_PRIORITY]\nstruct = \"EnumItem\"\n\n[Enum.PacketPriority.LOW_PRIORITY]\nstruct = \"EnumItem\"\n\n[Enum.PacketPriority.MEDIUM_PRIORITY]\nstruct = \"EnumItem\"\n[Enum.PartType.Ball]\nstruct = \"EnumItem\"\n\n[Enum.PartType.Block]\nstruct = \"EnumItem\"\n\n[Enum.PartType.Cylinder]\nstruct = \"EnumItem\"\n\n[Enum.PartType.GetEnumItems]\nargs = []\nmethod = true\n[Enum.PathStatus.ClosestNoPath]\nstruct = \"EnumItem\"\n\n[Enum.PathStatus.ClosestOutOfRange]\nstruct = \"EnumItem\"\n\n[Enum.PathStatus.FailFinishNotEmpty]\nstruct = \"EnumItem\"\n\n[Enum.PathStatus.FailStartNotEmpty]\nstruct = \"EnumItem\"\n\n[Enum.PathStatus.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.PathStatus.NoPath]\nstruct = \"EnumItem\"\n\n[Enum.PathStatus.Success]\nstruct = \"EnumItem\"\n[Enum.PathWaypointAction.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.PathWaypointAction.Jump]\nstruct = \"EnumItem\"\n\n[Enum.PathWaypointAction.Walk]\nstruct = \"EnumItem\"\n[Enum.PermissionLevelShown.Game]\nstruct = \"EnumItem\"\n\n[Enum.PermissionLevelShown.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.PermissionLevelShown.Roblox]\nstruct = \"EnumItem\"\n\n[Enum.PermissionLevelShown.RobloxGame]\nstruct = \"EnumItem\"\n\n[Enum.PermissionLevelShown.RobloxScript]\nstruct = \"EnumItem\"\n\n[Enum.PermissionLevelShown.Studio]\nstruct = \"EnumItem\"\n[Enum.PhysicsSimulationRate.Fixed120Hz]\nstruct = \"EnumItem\"\n\n[Enum.PhysicsSimulationRate.Fixed240Hz]\nstruct = \"EnumItem\"\n\n[Enum.PhysicsSimulationRate.Fixed60Hz]\nstruct = \"EnumItem\"\n\n[Enum.PhysicsSimulationRate.GetEnumItems]\nargs = []\nmethod = true\n[Enum.Platform.Android]\nstruct = \"EnumItem\"\n\n[Enum.Platform.AndroidTV]\nstruct = \"EnumItem\"\n\n[Enum.Platform.BeOS]\nstruct = \"EnumItem\"\n\n[Enum.Platform.Chromecast]\nstruct = \"EnumItem\"\n\n[Enum.Platform.DOS]\nstruct = \"EnumItem\"\n\n[Enum.Platform.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.Platform.IOS]\nstruct = \"EnumItem\"\n\n[Enum.Platform.Linux]\nstruct = \"EnumItem\"\n\n[Enum.Platform.NX]\nstruct = \"EnumItem\"\n\n[Enum.Platform.None]\nstruct = \"EnumItem\"\n\n[Enum.Platform.OSX]\nstruct = \"EnumItem\"\n\n[Enum.Platform.Ouya]\nstruct = \"EnumItem\"\n\n[Enum.Platform.PS3]\nstruct = \"EnumItem\"\n\n[Enum.Platform.PS4]\nstruct = \"EnumItem\"\n\n[Enum.Platform.SteamOS]\nstruct = \"EnumItem\"\n\n[Enum.Platform.UWP]\nstruct = \"EnumItem\"\n\n[Enum.Platform.WebOS]\nstruct = \"EnumItem\"\n\n[Enum.Platform.WiiU]\nstruct = \"EnumItem\"\n\n[Enum.Platform.Windows]\nstruct = \"EnumItem\"\n\n[Enum.Platform.XBox360]\nstruct = \"EnumItem\"\n\n[Enum.Platform.XBoxOne]\nstruct = \"EnumItem\"\n[Enum.PlaybackState.Begin]\nstruct = \"EnumItem\"\n\n[Enum.PlaybackState.Cancelled]\nstruct = \"EnumItem\"\n\n[Enum.PlaybackState.Completed]\nstruct = \"EnumItem\"\n\n[Enum.PlaybackState.Delayed]\nstruct = \"EnumItem\"\n\n[Enum.PlaybackState.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.PlaybackState.Paused]\nstruct = \"EnumItem\"\n\n[Enum.PlaybackState.Playing]\nstruct = \"EnumItem\"\n[Enum.PlayerActions.CharacterBackward]\nstruct = \"EnumItem\"\n\n[Enum.PlayerActions.CharacterForward]\nstruct = \"EnumItem\"\n\n[Enum.PlayerActions.CharacterJump]\nstruct = \"EnumItem\"\n\n[Enum.PlayerActions.CharacterLeft]\nstruct = \"EnumItem\"\n\n[Enum.PlayerActions.CharacterRight]\nstruct = \"EnumItem\"\n\n[Enum.PlayerActions.GetEnumItems]\nargs = []\nmethod = true\n[Enum.PlayerChatType.All]\nstruct = \"EnumItem\"\n\n[Enum.PlayerChatType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.PlayerChatType.Team]\nstruct = \"EnumItem\"\n\n[Enum.PlayerChatType.Whisper]\nstruct = \"EnumItem\"\n[Enum.PoseEasingDirection.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.PoseEasingDirection.In]\nstruct = \"EnumItem\"\n\n[Enum.PoseEasingDirection.InOut]\nstruct = \"EnumItem\"\n\n[Enum.PoseEasingDirection.Out]\nstruct = \"EnumItem\"\n[Enum.PoseEasingStyle.Bounce]\nstruct = \"EnumItem\"\n\n[Enum.PoseEasingStyle.Constant]\nstruct = \"EnumItem\"\n\n[Enum.PoseEasingStyle.Cubic]\nstruct = \"EnumItem\"\n\n[Enum.PoseEasingStyle.Elastic]\nstruct = \"EnumItem\"\n\n[Enum.PoseEasingStyle.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.PoseEasingStyle.Linear]\nstruct = \"EnumItem\"\n[Enum.PrivilegeType.Admin]\nstruct = \"EnumItem\"\n\n[Enum.PrivilegeType.Banned]\nstruct = \"EnumItem\"\n\n[Enum.PrivilegeType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.PrivilegeType.Member]\nstruct = \"EnumItem\"\n\n[Enum.PrivilegeType.Owner]\nstruct = \"EnumItem\"\n\n[Enum.PrivilegeType.Visitor]\nstruct = \"EnumItem\"\n[Enum.ProductPurchaseDecision.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ProductPurchaseDecision.NotProcessedYet]\nstruct = \"EnumItem\"\n\n[Enum.ProductPurchaseDecision.PurchaseGranted]\nstruct = \"EnumItem\"\n[Enum.QualityLevel.Automatic]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.QualityLevel.Level01]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level02]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level03]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level04]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level05]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level06]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level07]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level08]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level09]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level10]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level11]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level12]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level13]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level14]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level15]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level16]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level17]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level18]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level19]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level20]\nstruct = \"EnumItem\"\n\n[Enum.QualityLevel.Level21]\nstruct = \"EnumItem\"\n[Enum.R15CollisionType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.R15CollisionType.InnerBox]\nstruct = \"EnumItem\"\n\n[Enum.R15CollisionType.OuterBox]\nstruct = \"EnumItem\"\n[Enum.RaycastFilterType.Blacklist]\nstruct = \"EnumItem\"\n\n[Enum.RaycastFilterType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.RaycastFilterType.Whitelist]\nstruct = \"EnumItem\"\n[Enum.RenderFidelity.Automatic]\nstruct = \"EnumItem\"\n\n[Enum.RenderFidelity.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.RenderFidelity.Precise]\nstruct = \"EnumItem\"\n[Enum.RenderPriority.Camera]\nstruct = \"EnumItem\"\n\n[Enum.RenderPriority.Character]\nstruct = \"EnumItem\"\n\n[Enum.RenderPriority.First]\nstruct = \"EnumItem\"\n\n[Enum.RenderPriority.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.RenderPriority.Input]\nstruct = \"EnumItem\"\n\n[Enum.RenderPriority.Last]\nstruct = \"EnumItem\"\n[Enum.RenderingTestComparisonMethod.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.RenderingTestComparisonMethod.diff]\nstruct = \"EnumItem\"\n\n[Enum.RenderingTestComparisonMethod.psnr]\nstruct = \"EnumItem\"\n[Enum.ReturnKeyType.Default]\nstruct = \"EnumItem\"\n\n[Enum.ReturnKeyType.Done]\nstruct = \"EnumItem\"\n\n[Enum.ReturnKeyType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ReturnKeyType.Go]\nstruct = \"EnumItem\"\n\n[Enum.ReturnKeyType.Next]\nstruct = \"EnumItem\"\n\n[Enum.ReturnKeyType.Search]\nstruct = \"EnumItem\"\n\n[Enum.ReturnKeyType.Send]\nstruct = \"EnumItem\"\n[Enum.ReverbType.Alley]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.Arena]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.Auditorium]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.Bathroom]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.CarpettedHallway]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.Cave]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.City]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.ConcertHall]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.Forest]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.GenericReverb]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ReverbType.Hallway]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.Hangar]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.LivingRoom]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.Mountains]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.NoReverb]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.PaddedCell]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.ParkingLot]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.Plain]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.Quarry]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.Room]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.SewerPipe]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.StoneCorridor]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.StoneRoom]\nstruct = \"EnumItem\"\n\n[Enum.ReverbType.UnderWater]\nstruct = \"EnumItem\"\n[Enum.RibbonTool.ColorPicker]\nstruct = \"EnumItem\"\n\n[Enum.RibbonTool.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.RibbonTool.Group]\nstruct = \"EnumItem\"\n\n[Enum.RibbonTool.MaterialPicker]\nstruct = \"EnumItem\"\n\n[Enum.RibbonTool.Move]\nstruct = \"EnumItem\"\n\n[Enum.RibbonTool.None]\nstruct = \"EnumItem\"\n\n[Enum.RibbonTool.Rotate]\nstruct = \"EnumItem\"\n\n[Enum.RibbonTool.Scale]\nstruct = \"EnumItem\"\n\n[Enum.RibbonTool.Select]\nstruct = \"EnumItem\"\n\n[Enum.RibbonTool.Transform]\nstruct = \"EnumItem\"\n\n[Enum.RibbonTool.Ungroup]\nstruct = \"EnumItem\"\n[Enum.RollOffMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.RollOffMode.Inverse]\nstruct = \"EnumItem\"\n\n[Enum.RollOffMode.InverseTapered]\nstruct = \"EnumItem\"\n\n[Enum.RollOffMode.Linear]\nstruct = \"EnumItem\"\n\n[Enum.RollOffMode.LinearSquare]\nstruct = \"EnumItem\"\n[Enum.RotationType.CameraRelative]\nstruct = \"EnumItem\"\n\n[Enum.RotationType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.RotationType.MovementRelative]\nstruct = \"EnumItem\"\n[Enum.RuntimeUndoBehavior.Aggregate]\nstruct = \"EnumItem\"\n\n[Enum.RuntimeUndoBehavior.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.RuntimeUndoBehavior.Hybrid]\nstruct = \"EnumItem\"\n\n[Enum.RuntimeUndoBehavior.Snapshot]\nstruct = \"EnumItem\"\n[Enum.SaveFilter.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.SaveFilter.SaveAll]\nstruct = \"EnumItem\"\n\n[Enum.SaveFilter.SaveGame]\nstruct = \"EnumItem\"\n\n[Enum.SaveFilter.SaveWorld]\nstruct = \"EnumItem\"\n[Enum.SavedQualitySetting.Automatic]\nstruct = \"EnumItem\"\n\n[Enum.SavedQualitySetting.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.SavedQualitySetting.QualityLevel1]\nstruct = \"EnumItem\"\n\n[Enum.SavedQualitySetting.QualityLevel10]\nstruct = \"EnumItem\"\n\n[Enum.SavedQualitySetting.QualityLevel2]\nstruct = \"EnumItem\"\n\n[Enum.SavedQualitySetting.QualityLevel3]\nstruct = \"EnumItem\"\n\n[Enum.SavedQualitySetting.QualityLevel4]\nstruct = \"EnumItem\"\n\n[Enum.SavedQualitySetting.QualityLevel5]\nstruct = \"EnumItem\"\n\n[Enum.SavedQualitySetting.QualityLevel6]\nstruct = \"EnumItem\"\n\n[Enum.SavedQualitySetting.QualityLevel7]\nstruct = \"EnumItem\"\n\n[Enum.SavedQualitySetting.QualityLevel8]\nstruct = \"EnumItem\"\n\n[Enum.SavedQualitySetting.QualityLevel9]\nstruct = \"EnumItem\"\n[Enum.ScaleType.Crop]\nstruct = \"EnumItem\"\n\n[Enum.ScaleType.Fit]\nstruct = \"EnumItem\"\n\n[Enum.ScaleType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ScaleType.Slice]\nstruct = \"EnumItem\"\n\n[Enum.ScaleType.Stretch]\nstruct = \"EnumItem\"\n\n[Enum.ScaleType.Tile]\nstruct = \"EnumItem\"\n[Enum.ScreenOrientation.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ScreenOrientation.LandscapeLeft]\nstruct = \"EnumItem\"\n\n[Enum.ScreenOrientation.LandscapeRight]\nstruct = \"EnumItem\"\n\n[Enum.ScreenOrientation.LandscapeSensor]\nstruct = \"EnumItem\"\n\n[Enum.ScreenOrientation.Portrait]\nstruct = \"EnumItem\"\n\n[Enum.ScreenOrientation.Sensor]\nstruct = \"EnumItem\"\n[Enum.ScrollBarInset.Always]\nstruct = \"EnumItem\"\n\n[Enum.ScrollBarInset.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ScrollBarInset.None]\nstruct = \"EnumItem\"\n\n[Enum.ScrollBarInset.ScrollBar]\nstruct = \"EnumItem\"\n[Enum.ScrollingDirection.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ScrollingDirection.X]\nstruct = \"EnumItem\"\n\n[Enum.ScrollingDirection.XY]\nstruct = \"EnumItem\"\n\n[Enum.ScrollingDirection.Y]\nstruct = \"EnumItem\"\n[Enum.ServerAudioBehavior.Enabled]\nstruct = \"EnumItem\"\n\n[Enum.ServerAudioBehavior.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ServerAudioBehavior.Muted]\nstruct = \"EnumItem\"\n\n[Enum.ServerAudioBehavior.OnlineGame]\nstruct = \"EnumItem\"\n[Enum.SizeConstraint.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.SizeConstraint.RelativeXX]\nstruct = \"EnumItem\"\n\n[Enum.SizeConstraint.RelativeXY]\nstruct = \"EnumItem\"\n\n[Enum.SizeConstraint.RelativeYY]\nstruct = \"EnumItem\"\n[Enum.SkinnedMeshAllowType.Default]\nstruct = \"EnumItem\"\n\n[Enum.SkinnedMeshAllowType.Disabled]\nstruct = \"EnumItem\"\n\n[Enum.SkinnedMeshAllowType.Enabled]\nstruct = \"EnumItem\"\n\n[Enum.SkinnedMeshAllowType.GetEnumItems]\nargs = []\nmethod = true\n[Enum.SortOrder.Custom]\nstruct = \"EnumItem\"\n\n[Enum.SortOrder.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.SortOrder.LayoutOrder]\nstruct = \"EnumItem\"\n\n[Enum.SortOrder.Name]\nstruct = \"EnumItem\"\n[Enum.SoundType.Boing]\nstruct = \"EnumItem\"\n\n[Enum.SoundType.Bomb]\nstruct = \"EnumItem\"\n\n[Enum.SoundType.Break]\nstruct = \"EnumItem\"\n\n[Enum.SoundType.Click]\nstruct = \"EnumItem\"\n\n[Enum.SoundType.Clock]\nstruct = \"EnumItem\"\n\n[Enum.SoundType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.SoundType.NoSound]\nstruct = \"EnumItem\"\n\n[Enum.SoundType.Page]\nstruct = \"EnumItem\"\n\n[Enum.SoundType.Ping]\nstruct = \"EnumItem\"\n\n[Enum.SoundType.Slingshot]\nstruct = \"EnumItem\"\n\n[Enum.SoundType.Snap]\nstruct = \"EnumItem\"\n\n[Enum.SoundType.Splat]\nstruct = \"EnumItem\"\n\n[Enum.SoundType.Step]\nstruct = \"EnumItem\"\n\n[Enum.SoundType.StepOn]\nstruct = \"EnumItem\"\n\n[Enum.SoundType.Swoosh]\nstruct = \"EnumItem\"\n\n[Enum.SoundType.Victory]\nstruct = \"EnumItem\"\n[Enum.SpecialKey.ChatHotkey]\nstruct = \"EnumItem\"\n\n[Enum.SpecialKey.End]\nstruct = \"EnumItem\"\n\n[Enum.SpecialKey.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.SpecialKey.Home]\nstruct = \"EnumItem\"\n\n[Enum.SpecialKey.Insert]\nstruct = \"EnumItem\"\n\n[Enum.SpecialKey.PageDown]\nstruct = \"EnumItem\"\n\n[Enum.SpecialKey.PageUp]\nstruct = \"EnumItem\"\n[Enum.StartCorner.BottomLeft]\nstruct = \"EnumItem\"\n\n[Enum.StartCorner.BottomRight]\nstruct = \"EnumItem\"\n\n[Enum.StartCorner.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.StartCorner.TopLeft]\nstruct = \"EnumItem\"\n\n[Enum.StartCorner.TopRight]\nstruct = \"EnumItem\"\n[Enum.Status.Confusion]\nstruct = \"EnumItem\"\n\n[Enum.Status.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.Status.Poison]\nstruct = \"EnumItem\"\n[Enum.StreamingPauseMode.ClientPhysicsPause]\nstruct = \"EnumItem\"\n\n[Enum.StreamingPauseMode.Default]\nstruct = \"EnumItem\"\n\n[Enum.StreamingPauseMode.Disabled]\nstruct = \"EnumItem\"\n\n[Enum.StreamingPauseMode.GetEnumItems]\nargs = []\nmethod = true\n[Enum.StudioDataModelType.Edit]\nstruct = \"EnumItem\"\n\n[Enum.StudioDataModelType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.StudioDataModelType.None]\nstruct = \"EnumItem\"\n\n[Enum.StudioDataModelType.PlayClient]\nstruct = \"EnumItem\"\n\n[Enum.StudioDataModelType.PlayServer]\nstruct = \"EnumItem\"\n\n[Enum.StudioDataModelType.RobloxPlugin]\nstruct = \"EnumItem\"\n\n[Enum.StudioDataModelType.UserPlugin]\nstruct = \"EnumItem\"\n[Enum.StudioStyleGuideColor.Border]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.BrightText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.Button]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ButtonBorder]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ButtonText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.CategoryItem]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ChatIncomingBgColor]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ChatIncomingTextColor]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ChatModeratedMessageColor]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ChatOutgoingBgColor]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ChatOutgoingTextColor]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.CheckedFieldBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.CheckedFieldBorder]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.CheckedFieldIndicator]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ColorPickerFrame]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.CurrentMarker]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.Dark]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DebuggerCurrentLine]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DebuggerErrorLine]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DialogButton]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DialogButtonBorder]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DialogButtonText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DialogMainButton]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DialogMainButtonText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffFilePathBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffFilePathBorder]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffFilePathText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffLineNum]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffLineNumAdditionBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffLineNumDeletionBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffLineNumNoChangeBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffLineNumSeparatorBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffTextAddition]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffTextAdditionBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffTextDeletion]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffTextDeletionBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffTextHunkInfo]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffTextNoChange]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffTextNoChangeBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DiffTextSeparatorBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.DimmedText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.Dropdown]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.EmulatorBar]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.EmulatorDropDown]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ErrorText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.GameSettingsTableItem]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.GameSettingsTooltip]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.StudioStyleGuideColor.HeaderSection]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.InfoBarWarningBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.InfoBarWarningText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.InfoText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.InputFieldBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.InputFieldBorder]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.Item]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.Light]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.LinkText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.MainBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.MainButton]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.MainText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.Mid]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.Midlight]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.Notification]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.RibbonButton]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.RibbonTab]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.RibbonTabTopBar]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptBuiltInFunction]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptComment]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptEditorCurrentLine]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptError]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptFindSelectionBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptKeyword]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptMatchingWordSelectionBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptNumber]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptOperator]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptPreprocessor]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptRuler]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptSelectionBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptSelectionText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptSideWidget]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptString]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptWarning]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScriptWhitespace]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScrollBar]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ScrollBarBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.SensitiveText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.Separator]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.Shadow]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.StatusBar]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.SubText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.Tab]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.TabBar]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.TableItem]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.Titlebar]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.TitlebarText]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.Tooltip]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.ViewPortBackground]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideColor.WarningText]\nstruct = \"EnumItem\"\n[Enum.StudioStyleGuideModifier.Default]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideModifier.Disabled]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideModifier.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.StudioStyleGuideModifier.Hover]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideModifier.Pressed]\nstruct = \"EnumItem\"\n\n[Enum.StudioStyleGuideModifier.Selected]\nstruct = \"EnumItem\"\n[Enum.Style.AlternatingSupports]\nstruct = \"EnumItem\"\n\n[Enum.Style.BridgeStyleSupports]\nstruct = \"EnumItem\"\n\n[Enum.Style.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.Style.NoSupports]\nstruct = \"EnumItem\"\n[Enum.SurfaceConstraint.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.SurfaceConstraint.Hinge]\nstruct = \"EnumItem\"\n\n[Enum.SurfaceConstraint.Motor]\nstruct = \"EnumItem\"\n\n[Enum.SurfaceConstraint.None]\nstruct = \"EnumItem\"\n\n[Enum.SurfaceConstraint.SteppingMotor]\nstruct = \"EnumItem\"\n[Enum.SurfaceGuiSizingMode.FixedSize]\nstruct = \"EnumItem\"\n\n[Enum.SurfaceGuiSizingMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.SurfaceGuiSizingMode.PixelsPerStud]\nstruct = \"EnumItem\"\n[Enum.SurfaceType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.SurfaceType.Glue]\nstruct = \"EnumItem\"\n\n[Enum.SurfaceType.Hinge]\nstruct = \"EnumItem\"\n\n[Enum.SurfaceType.Inlet]\nstruct = \"EnumItem\"\n\n[Enum.SurfaceType.Motor]\nstruct = \"EnumItem\"\n\n[Enum.SurfaceType.Smooth]\nstruct = \"EnumItem\"\n\n[Enum.SurfaceType.SmoothNoOutlines]\nstruct = \"EnumItem\"\n\n[Enum.SurfaceType.SteppingMotor]\nstruct = \"EnumItem\"\n\n[Enum.SurfaceType.Studs]\nstruct = \"EnumItem\"\n\n[Enum.SurfaceType.Universal]\nstruct = \"EnumItem\"\n\n[Enum.SurfaceType.Weld]\nstruct = \"EnumItem\"\n[Enum.SwipeDirection.Down]\nstruct = \"EnumItem\"\n\n[Enum.SwipeDirection.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.SwipeDirection.Left]\nstruct = \"EnumItem\"\n\n[Enum.SwipeDirection.None]\nstruct = \"EnumItem\"\n\n[Enum.SwipeDirection.Right]\nstruct = \"EnumItem\"\n\n[Enum.SwipeDirection.Up]\nstruct = \"EnumItem\"\n[Enum.TableMajorAxis.ColumnMajor]\nstruct = \"EnumItem\"\n\n[Enum.TableMajorAxis.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TableMajorAxis.RowMajor]\nstruct = \"EnumItem\"\n[Enum.Technology.Compatibility]\nstruct = \"EnumItem\"\n\n[Enum.Technology.Future]\nstruct = \"EnumItem\"\n\n[Enum.Technology.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.Technology.Legacy]\nstruct = \"EnumItem\"\n\n[Enum.Technology.ShadowMap]\nstruct = \"EnumItem\"\n\n[Enum.Technology.Voxel]\nstruct = \"EnumItem\"\n[Enum.TeleportMethod.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TeleportMethod.TeleportToPartyAsync]\nstruct = \"EnumItem\"\n\n[Enum.TeleportMethod.TeleportToPlaceInstance]\nstruct = \"EnumItem\"\n\n[Enum.TeleportMethod.TeleportToPrivateServer]\nstruct = \"EnumItem\"\n\n[Enum.TeleportMethod.TeleportToSpawnByName]\nstruct = \"EnumItem\"\n[Enum.TeleportResult.Failure]\nstruct = \"EnumItem\"\n\n[Enum.TeleportResult.Flooded]\nstruct = \"EnumItem\"\n\n[Enum.TeleportResult.GameEnded]\nstruct = \"EnumItem\"\n\n[Enum.TeleportResult.GameFull]\nstruct = \"EnumItem\"\n\n[Enum.TeleportResult.GameNotFound]\nstruct = \"EnumItem\"\n\n[Enum.TeleportResult.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TeleportResult.IsTeleporting]\nstruct = \"EnumItem\"\n\n[Enum.TeleportResult.Success]\nstruct = \"EnumItem\"\n\n[Enum.TeleportResult.Unauthorized]\nstruct = \"EnumItem\"\n[Enum.TeleportState.Failed]\nstruct = \"EnumItem\"\n\n[Enum.TeleportState.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TeleportState.InProgress]\nstruct = \"EnumItem\"\n\n[Enum.TeleportState.RequestedFromServer]\nstruct = \"EnumItem\"\n\n[Enum.TeleportState.Started]\nstruct = \"EnumItem\"\n\n[Enum.TeleportState.WaitingForServer]\nstruct = \"EnumItem\"\n[Enum.TeleportType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TeleportType.ToInstance]\nstruct = \"EnumItem\"\n\n[Enum.TeleportType.ToPlace]\nstruct = \"EnumItem\"\n\n[Enum.TeleportType.ToReservedServer]\nstruct = \"EnumItem\"\n[Enum.TextFilterContext.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TextFilterContext.PrivateChat]\nstruct = \"EnumItem\"\n\n[Enum.TextFilterContext.PublicChat]\nstruct = \"EnumItem\"\n[Enum.TextInputType.Default]\nstruct = \"EnumItem\"\n\n[Enum.TextInputType.Email]\nstruct = \"EnumItem\"\n\n[Enum.TextInputType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TextInputType.NoSuggestions]\nstruct = \"EnumItem\"\n\n[Enum.TextInputType.Number]\nstruct = \"EnumItem\"\n\n[Enum.TextInputType.Password]\nstruct = \"EnumItem\"\n\n[Enum.TextInputType.Phone]\nstruct = \"EnumItem\"\n[Enum.TextTruncate.AtEnd]\nstruct = \"EnumItem\"\n\n[Enum.TextTruncate.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TextTruncate.None]\nstruct = \"EnumItem\"\n[Enum.TextXAlignment.Center]\nstruct = \"EnumItem\"\n\n[Enum.TextXAlignment.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TextXAlignment.Left]\nstruct = \"EnumItem\"\n\n[Enum.TextXAlignment.Right]\nstruct = \"EnumItem\"\n[Enum.TextYAlignment.Bottom]\nstruct = \"EnumItem\"\n\n[Enum.TextYAlignment.Center]\nstruct = \"EnumItem\"\n\n[Enum.TextYAlignment.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TextYAlignment.Top]\nstruct = \"EnumItem\"\n[Enum.TextureMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TextureMode.Static]\nstruct = \"EnumItem\"\n\n[Enum.TextureMode.Stretch]\nstruct = \"EnumItem\"\n\n[Enum.TextureMode.Wrap]\nstruct = \"EnumItem\"\n[Enum.TextureQueryType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TextureQueryType.Humanoid]\nstruct = \"EnumItem\"\n\n[Enum.TextureQueryType.HumanoidOrphaned]\nstruct = \"EnumItem\"\n\n[Enum.TextureQueryType.NonHumanoid]\nstruct = \"EnumItem\"\n\n[Enum.TextureQueryType.NonHumanoidOrphaned]\nstruct = \"EnumItem\"\n[Enum.ThreadPoolConfig.Auto]\nstruct = \"EnumItem\"\n\n[Enum.ThreadPoolConfig.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ThreadPoolConfig.PerCore1]\nstruct = \"EnumItem\"\n\n[Enum.ThreadPoolConfig.PerCore2]\nstruct = \"EnumItem\"\n\n[Enum.ThreadPoolConfig.PerCore3]\nstruct = \"EnumItem\"\n\n[Enum.ThreadPoolConfig.PerCore4]\nstruct = \"EnumItem\"\n\n[Enum.ThreadPoolConfig.Threads1]\nstruct = \"EnumItem\"\n\n[Enum.ThreadPoolConfig.Threads16]\nstruct = \"EnumItem\"\n\n[Enum.ThreadPoolConfig.Threads2]\nstruct = \"EnumItem\"\n\n[Enum.ThreadPoolConfig.Threads3]\nstruct = \"EnumItem\"\n\n[Enum.ThreadPoolConfig.Threads4]\nstruct = \"EnumItem\"\n\n[Enum.ThreadPoolConfig.Threads8]\nstruct = \"EnumItem\"\n[Enum.ThrottlingPriority.Default]\nstruct = \"EnumItem\"\n\n[Enum.ThrottlingPriority.ElevatedOnServer]\nstruct = \"EnumItem\"\n\n[Enum.ThrottlingPriority.Extreme]\nstruct = \"EnumItem\"\n\n[Enum.ThrottlingPriority.GetEnumItems]\nargs = []\nmethod = true\n[Enum.ThumbnailSize.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ThumbnailSize.Size100x100]\nstruct = \"EnumItem\"\n\n[Enum.ThumbnailSize.Size150x150]\nstruct = \"EnumItem\"\n\n[Enum.ThumbnailSize.Size180x180]\nstruct = \"EnumItem\"\n\n[Enum.ThumbnailSize.Size352x352]\nstruct = \"EnumItem\"\n\n[Enum.ThumbnailSize.Size420x420]\nstruct = \"EnumItem\"\n\n[Enum.ThumbnailSize.Size48x48]\nstruct = \"EnumItem\"\n\n[Enum.ThumbnailSize.Size60x60]\nstruct = \"EnumItem\"\n[Enum.ThumbnailType.AvatarBust]\nstruct = \"EnumItem\"\n\n[Enum.ThumbnailType.AvatarThumbnail]\nstruct = \"EnumItem\"\n\n[Enum.ThumbnailType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ThumbnailType.HeadShot]\nstruct = \"EnumItem\"\n[Enum.TickCountSampleMethod.Benchmark]\nstruct = \"EnumItem\"\n\n[Enum.TickCountSampleMethod.Fast]\nstruct = \"EnumItem\"\n\n[Enum.TickCountSampleMethod.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TickCountSampleMethod.Precise]\nstruct = \"EnumItem\"\n[Enum.TopBottom.Bottom]\nstruct = \"EnumItem\"\n\n[Enum.TopBottom.Center]\nstruct = \"EnumItem\"\n\n[Enum.TopBottom.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TopBottom.Top]\nstruct = \"EnumItem\"\n[Enum.TouchCameraMovementMode.Classic]\nstruct = \"EnumItem\"\n\n[Enum.TouchCameraMovementMode.Default]\nstruct = \"EnumItem\"\n\n[Enum.TouchCameraMovementMode.Follow]\nstruct = \"EnumItem\"\n\n[Enum.TouchCameraMovementMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TouchCameraMovementMode.Orbital]\nstruct = \"EnumItem\"\n[Enum.TouchMovementMode.ClickToMove]\nstruct = \"EnumItem\"\n\n[Enum.TouchMovementMode.DPad]\nstruct = \"EnumItem\"\n\n[Enum.TouchMovementMode.Default]\nstruct = \"EnumItem\"\n\n[Enum.TouchMovementMode.DynamicThumbstick]\nstruct = \"EnumItem\"\n\n[Enum.TouchMovementMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.TouchMovementMode.Thumbpad]\nstruct = \"EnumItem\"\n\n[Enum.TouchMovementMode.Thumbstick]\nstruct = \"EnumItem\"\n[Enum.TweenStatus.Canceled]\nstruct = \"EnumItem\"\n\n[Enum.TweenStatus.Completed]\nstruct = \"EnumItem\"\n\n[Enum.TweenStatus.GetEnumItems]\nargs = []\nmethod = true\n[Enum.UITheme.Dark]\nstruct = \"EnumItem\"\n\n[Enum.UITheme.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.UITheme.Light]\nstruct = \"EnumItem\"\n[Enum.UiMessageType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.UiMessageType.UiMessageError]\nstruct = \"EnumItem\"\n\n[Enum.UiMessageType.UiMessageInfo]\nstruct = \"EnumItem\"\n[Enum.UploadSetting.Always]\nstruct = \"EnumItem\"\n\n[Enum.UploadSetting.Ask]\nstruct = \"EnumItem\"\n\n[Enum.UploadSetting.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.UploadSetting.Never]\nstruct = \"EnumItem\"\n[Enum.UserCFrame.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.UserCFrame.Head]\nstruct = \"EnumItem\"\n\n[Enum.UserCFrame.LeftHand]\nstruct = \"EnumItem\"\n\n[Enum.UserCFrame.RightHand]\nstruct = \"EnumItem\"\n[Enum.UserInputState.Begin]\nstruct = \"EnumItem\"\n\n[Enum.UserInputState.Cancel]\nstruct = \"EnumItem\"\n\n[Enum.UserInputState.Change]\nstruct = \"EnumItem\"\n\n[Enum.UserInputState.End]\nstruct = \"EnumItem\"\n\n[Enum.UserInputState.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.UserInputState.None]\nstruct = \"EnumItem\"\n[Enum.UserInputType.Accelerometer]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.Focus]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.Gamepad1]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.Gamepad2]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.Gamepad3]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.Gamepad4]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.Gamepad5]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.Gamepad6]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.Gamepad7]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.Gamepad8]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.UserInputType.Gyro]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.InputMethod]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.Keyboard]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.MouseButton1]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.MouseButton2]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.MouseButton3]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.MouseMovement]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.MouseWheel]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.None]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.TextInput]\nstruct = \"EnumItem\"\n\n[Enum.UserInputType.Touch]\nstruct = \"EnumItem\"\n[Enum.VRTouchpad.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.VRTouchpad.Left]\nstruct = \"EnumItem\"\n\n[Enum.VRTouchpad.Right]\nstruct = \"EnumItem\"\n[Enum.VRTouchpadMode.ABXY]\nstruct = \"EnumItem\"\n\n[Enum.VRTouchpadMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.VRTouchpadMode.Touch]\nstruct = \"EnumItem\"\n\n[Enum.VRTouchpadMode.VirtualThumbstick]\nstruct = \"EnumItem\"\n[Enum.VerticalAlignment.Bottom]\nstruct = \"EnumItem\"\n\n[Enum.VerticalAlignment.Center]\nstruct = \"EnumItem\"\n\n[Enum.VerticalAlignment.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.VerticalAlignment.Top]\nstruct = \"EnumItem\"\n[Enum.VerticalScrollBarPosition.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.VerticalScrollBarPosition.Left]\nstruct = \"EnumItem\"\n\n[Enum.VerticalScrollBarPosition.Right]\nstruct = \"EnumItem\"\n[Enum.VibrationMotor.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.VibrationMotor.Large]\nstruct = \"EnumItem\"\n\n[Enum.VibrationMotor.LeftHand]\nstruct = \"EnumItem\"\n\n[Enum.VibrationMotor.LeftTrigger]\nstruct = \"EnumItem\"\n\n[Enum.VibrationMotor.RightHand]\nstruct = \"EnumItem\"\n\n[Enum.VibrationMotor.RightTrigger]\nstruct = \"EnumItem\"\n\n[Enum.VibrationMotor.Small]\nstruct = \"EnumItem\"\n[Enum.VideoQualitySettings.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.VideoQualitySettings.HighResolution]\nstruct = \"EnumItem\"\n\n[Enum.VideoQualitySettings.LowResolution]\nstruct = \"EnumItem\"\n\n[Enum.VideoQualitySettings.MediumResolution]\nstruct = \"EnumItem\"\n[Enum.VirtualInputMode.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.VirtualInputMode.None]\nstruct = \"EnumItem\"\n\n[Enum.VirtualInputMode.Playing]\nstruct = \"EnumItem\"\n\n[Enum.VirtualInputMode.Recording]\nstruct = \"EnumItem\"\n[Enum.WaterDirection.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.WaterDirection.NegX]\nstruct = \"EnumItem\"\n\n[Enum.WaterDirection.NegY]\nstruct = \"EnumItem\"\n\n[Enum.WaterDirection.NegZ]\nstruct = \"EnumItem\"\n\n[Enum.WaterDirection.X]\nstruct = \"EnumItem\"\n\n[Enum.WaterDirection.Y]\nstruct = \"EnumItem\"\n\n[Enum.WaterDirection.Z]\nstruct = \"EnumItem\"\n[Enum.WaterForce.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.WaterForce.Max]\nstruct = \"EnumItem\"\n\n[Enum.WaterForce.Medium]\nstruct = \"EnumItem\"\n\n[Enum.WaterForce.None]\nstruct = \"EnumItem\"\n\n[Enum.WaterForce.Small]\nstruct = \"EnumItem\"\n\n[Enum.WaterForce.Strong]\nstruct = \"EnumItem\"\n[Enum.ZIndexBehavior.GetEnumItems]\nargs = []\nmethod = true\n\n[Enum.ZIndexBehavior.Global]\nstruct = \"EnumItem\"\n\n[Enum.ZIndexBehavior.Sibling]\nstruct = \"EnumItem\"\n[[Faces.new.args]]\ntype = \"...\"\n[[Instance.new.args]]\ntype = [\"Accoutrement\", \"Accessory\", \"Hat\", \"AdvancedDragger\", \"Animation\", \"AnimationController\", \"Animator\", \"Atmosphere\", \"Attachment\", \"Bone\", \"Backpack\", \"HopperBin\", \"Tool\", \"Flag\", \"Beam\", \"BindableEvent\", \"BindableFunction\", \"BodyAngularVelocity\", \"BodyForce\", \"BodyGyro\", \"BodyPosition\", \"BodyThrust\", \"BodyVelocity\", \"RocketPropulsion\", \"Camera\", \"BodyColors\", \"CharacterMesh\", \"Pants\", \"Shirt\", \"ShirtGraphic\", \"Skin\", \"ClickDetector\", \"Configuration\", \"AlignOrientation\", \"AlignPosition\", \"AngularVelocity\", \"BallSocketConstraint\", \"HingeConstraint\", \"LineForce\", \"RodConstraint\", \"RopeConstraint\", \"CylindricalConstraint\", \"PrismaticConstraint\", \"SpringConstraint\", \"Torque\", \"VectorForce\", \"HumanoidController\", \"SkateboardController\", \"VehicleController\", \"CustomEvent\", \"CustomEventReceiver\", \"BlockMesh\", \"CylinderMesh\", \"FileMesh\", \"SpecialMesh\", \"DebuggerWatch\", \"Dialog\", \"DialogChoice\", \"Dragger\", \"Explosion\", \"Decal\", \"Texture\", \"Hole\", \"MotorFeature\", \"Fire\", \"FlyweightService\", \"CSGDictionaryService\", \"NonReplicatedCSGDictionaryService\", \"Folder\", \"ForceField\", \"FunctionalTest\", \"Frame\", \"ImageButton\", \"TextButton\", \"ImageLabel\", \"TextLabel\", \"ScrollingFrame\", \"TextBox\", \"VideoFrame\", \"ViewportFrame\", \"BillboardGui\", \"ScreenGui\", \"GuiMain\", \"SurfaceGui\", \"FloorWire\", \"SelectionBox\", \"BoxHandleAdornment\", \"ConeHandleAdornment\", \"CylinderHandleAdornment\", \"ImageHandleAdornment\", \"LineHandleAdornment\", \"SphereHandleAdornment\", \"ParabolaAdornment\", \"SelectionSphere\", \"ArcHandles\", \"Handles\", \"SurfaceSelection\", \"SelectionPartLasso\", \"SelectionPointLasso\", \"Humanoid\", \"HumanoidDescription\", \"RotateP\", \"RotateV\", \"Glue\", \"ManualGlue\", \"ManualWeld\", \"Motor\", \"Motor6D\", \"Rotate\", \"Snap\", \"VelocityMotor\", \"Weld\", \"Keyframe\", \"KeyframeMarker\", \"KeyframeSequence\", \"PointLight\", \"SpotLight\", \"SurfaceLight\", \"LocalizationTable\", \"Script\", \"LocalScript\", \"ModuleScript\", \"Message\", \"Hint\", \"NoCollisionConstraint\", \"CornerWedgePart\", \"Part\", \"FlagStand\", \"Seat\", \"SkateboardPlatform\", \"SpawnLocation\", \"WedgePart\", \"MeshPart\", \"PartOperation\", \"NegateOperation\", \"UnionOperation\", \"TrussPart\", \"VehicleSeat\", \"Model\", \"WorldModel\", \"PartOperationAsset\", \"ParticleEmitter\", \"Player\", \"PluginAction\", \"Pose\", \"BloomEffect\", \"BlurEffect\", \"ColorCorrectionEffect\", \"DepthOfFieldEffect\", \"SunRaysEffect\", \"ReflectionMetadata\", \"ReflectionMetadataCallbacks\", \"ReflectionMetadataClasses\", \"ReflectionMetadataEnums\", \"ReflectionMetadataEvents\", \"ReflectionMetadataFunctions\", \"ReflectionMetadataClass\", \"ReflectionMetadataEnum\", \"ReflectionMetadataEnumItem\", \"ReflectionMetadataMember\", \"ReflectionMetadataProperties\", \"ReflectionMetadataYieldFunctions\", \"RemoteEvent\", \"RemoteFunction\", \"RenderingTest\", \"Sky\", \"Smoke\", \"Sound\", \"ChorusSoundEffect\", \"CompressorSoundEffect\", \"DistortionSoundEffect\", \"EchoSoundEffect\", \"EqualizerSoundEffect\", \"FlangeSoundEffect\", \"PitchShiftSoundEffect\", \"ReverbSoundEffect\", \"TremoloSoundEffect\", \"SoundGroup\", \"Sparkles\", \"StandalonePluginScripts\", \"StarterGear\", \"SurfaceAppearance\", \"Team\", \"TerrainRegion\", \"TestService\", \"Trail\", \"Tween\", \"UIAspectRatioConstraint\", \"UISizeConstraint\", \"UITextSizeConstraint\", \"UICorner\", \"UIGradient\", \"UIGridLayout\", \"UIInlineLayout\", \"UIListLayout\", \"UIPageLayout\", \"UITableLayout\", \"UIPadding\", \"UIScale\", \"BinaryStringValue\", \"BoolValue\", \"BrickColorValue\", \"CFrameValue\", \"Color3Value\", \"DoubleConstrainedValue\", \"IntConstrainedValue\", \"IntValue\", \"NumberValue\", \"ObjectValue\", \"RayValue\", \"StringValue\", \"Vector3Value\", \"VirtualInputManager\", \"WeldConstraint\"]\n[[NumberRange.new.args]]\ntype = \"number\"\n\n[[NumberRange.new.args]]\nrequired = false\ntype = \"number\"\n[[NumberSequence.new.args]]\ntype = \"any\"\n\n[[NumberSequence.new.args]]\nrequired = false\ntype = \"number\"\n[[NumberSequenceKeypoint.new.args]]\ntype = \"number\"\n\n[[NumberSequenceKeypoint.new.args]]\ntype = \"number\"\n\n[[NumberSequenceKeypoint.new.args]]\nrequired = false\ntype = \"number\"\n[[PathWaypoint.new.args]]\nrequired = false\n\n[PathWaypoint.new.args.type]\ndisplay = \"Vector3\"\n\n[[PathWaypoint.new.args]]\nrequired = false\n\n[PathWaypoint.new.args.type]\ndisplay = \"PathWaypointAction\"\n[[PhysicalProperties.new.args]]\ntype = \"any\"\n\n[[PhysicalProperties.new.args]]\nrequired = false\ntype = \"number\"\n\n[[PhysicalProperties.new.args]]\nrequired = false\ntype = \"number\"\n\n[[PhysicalProperties.new.args]]\nrequired = false\ntype = \"number\"\n\n[[PhysicalProperties.new.args]]\nrequired = false\ntype = \"number\"\n[[Random.new.args]]\nrequired = false\ntype = \"number\"\n[[Ray.new.args]]\n[Ray.new.args.type]\ndisplay = \"Vector3\"\n\n[[Ray.new.args]]\n[Ray.new.args.type]\ndisplay = \"Vector3\"\n[[Rect.new.args]]\ntype = \"any\"\n\n[[Rect.new.args]]\ntype = \"any\"\n\n[[Rect.new.args]]\nrequired = false\ntype = \"number\"\n\n[[Rect.new.args]]\nrequired = false\ntype = \"number\"\n[[Region3.new.args]]\n[Region3.new.args.type]\ndisplay = \"Vector3\"\n\n[[Region3.new.args]]\n[Region3.new.args.type]\ndisplay = \"Vector3\"\n[[Region3int16.new.args]]\n[Region3int16.new.args.type]\ndisplay = \"Vector3\"\n\n[[Region3int16.new.args]]\n[Region3int16.new.args.type]\ndisplay = \"Vector3\"\n[[TweenInfo.new.args]]\nrequired = false\ntype = \"number\"\n\n[[TweenInfo.new.args]]\nrequired = false\n\n[TweenInfo.new.args.type]\ndisplay = \"EasingStyle\"\n\n[[TweenInfo.new.args]]\nrequired = false\n\n[TweenInfo.new.args.type]\ndisplay = \"EasingDirection\"\n\n[[TweenInfo.new.args]]\nrequired = false\ntype = \"number\"\n\n[[TweenInfo.new.args]]\nrequired = false\ntype = \"bool\"\n\n[[TweenInfo.new.args]]\nrequired = false\ntype = \"number\"\n[[UDim.new.args]]\ntype = \"number\"\n\n[[UDim.new.args]]\ntype = \"number\"\n[[UDim2.fromOffset.args]]\ntype = \"number\"\n\n[[UDim2.fromOffset.args]]\ntype = \"number\"\n[[UDim2.fromScale.args]]\ntype = \"number\"\n\n[[UDim2.fromScale.args]]\ntype = \"number\"\n[[UDim2.new.args]]\nrequired = false\ntype = \"any\"\n\n[[UDim2.new.args]]\nrequired = false\ntype = \"any\"\n\n[[UDim2.new.args]]\nrequired = false\ntype = \"number\"\n\n[[UDim2.new.args]]\nrequired = false\ntype = \"number\"\n\n[UserSettings]\nargs = []\n[[Vector2.new.args]]\nrequired = false\ntype = \"number\"\n\n[[Vector2.new.args]]\nrequired = false\ntype = \"number\"\n[[Vector2int16.new.args]]\nrequired = false\ntype = \"number\"\n\n[[Vector2int16.new.args]]\nrequired = false\ntype = \"number\"\n[[Vector3.FromAxis.args]]\n[Vector3.FromAxis.args.type]\ndisplay = \"Axis\"\n[[Vector3.FromNormalId.args]]\n[Vector3.FromNormalId.args.type]\ndisplay = \"NormalId\"\n[[Vector3.new.args]]\nrequired = false\ntype = \"number\"\n\n[[Vector3.new.args]]\nrequired = false\ntype = \"number\"\n\n[[Vector3.new.args]]\nrequired = false\ntype = \"number\"\n[[Vector3int16.new.args]]\nrequired = false\ntype = \"number\"\n\n[[Vector3int16.new.args]]\nrequired = false\ntype = \"number\"\n\n[[Vector3int16.new.args]]\nrequired = false\ntype = \"number\"\n[[bit32.arshift.args]]\ntype = \"number\"\n\n[[bit32.arshift.args]]\ntype = \"number\"\n[[bit32.band.args]]\ntype = \"...\"\n[[bit32.bnot.args]]\ntype = \"number\"\n[[bit32.bor.args]]\ntype = \"...\"\n[[bit32.btest.args]]\ntype = \"...\"\n[[bit32.bxor.args]]\ntype = \"...\"\n[[bit32.extract.args]]\ntype = \"number\"\n\n[[bit32.extract.args]]\ntype = \"number\"\n\n[[bit32.extract.args]]\nrequired = false\ntype = \"number\"\n[[bit32.lrotate.args]]\ntype = \"number\"\n\n[[bit32.lrotate.args]]\ntype = \"number\"\n[[bit32.lshift.args]]\ntype = \"number\"\n\n[[bit32.lshift.args]]\ntype = \"number\"\n[[bit32.replace.args]]\ntype = \"number\"\n\n[[bit32.replace.args]]\ntype = \"number\"\n\n[[bit32.replace.args]]\nrequired = false\ntype = \"number\"\n[[bit32.rrotate.args]]\ntype = \"number\"\n\n[[bit32.rrotate.args]]\ntype = \"number\"\n[[bit32.rshift.args]]\ntype = \"number\"\n\n[[bit32.rshift.args]]\ntype = \"number\"\n[[collectgarbage.args]]\ntype = [\"count\"]\n[coroutine.isyieldable]\nargs = []\n[debug.debug]\nremoved = true\n\n[debug.getfenv]\nremoved = true\n\n[debug.gethook]\nremoved = true\n\n[debug.getinfo]\nremoved = true\n\n[debug.getlocal]\nremoved = true\n\n[debug.getmetatable]\nremoved = true\n\n[debug.getregistry]\nremoved = true\n\n[debug.getupvalue]\nremoved = true\n[[debug.profilebegin.args]]\ntype = \"string\"\n\n[debug.profileend]\nargs = []\n\n[debug.setfenv]\nremoved = true\n\n[debug.sethook]\nremoved = true\n\n[debug.setlocal]\nremoved = true\n\n[debug.setmetatable]\nremoved = true\n\n[debug.setupvalue]\nremoved = true\n[[delay.args]]\ntype = \"number\"\n\n[[delay.args]]\ntype = \"function\"\n\n[dofile]\nremoved = true\n\n[elapsedTime]\nargs = []\n\n[game]\nstruct = \"DataModel\"\n\n[io]\nremoved = true\n\n[load]\nremoved = true\n\n[loadfile]\nremoved = true\n[[math.clamp.args]]\ntype = \"number\"\n\n[[math.clamp.args]]\ntype = \"number\"\n\n[[math.clamp.args]]\ntype = \"number\"\n[[math.log.args]]\ntype = \"number\"\n\n[[math.log.args]]\nrequired = false\ntype = \"number\"\n[[math.noise.args]]\ntype = \"number\"\n\n[[math.noise.args]]\nrequired = false\ntype = \"number\"\n\n[[math.noise.args]]\nrequired = false\ntype = \"number\"\n[[math.sign.args]]\ntype = \"number\"\n\n[module]\nremoved = true\n[os.clock]\nremoved = true\n\n[os.execute]\nremoved = true\n\n[os.exit]\nremoved = true\n\n[os.getenv]\nremoved = true\n\n[os.remove]\nremoved = true\n\n[os.rename]\nremoved = true\n\n[os.setlocale]\nremoved = true\n\n[os.tmpname]\nremoved = true\n\n[package]\nremoved = true\n\n[plugin]\nstruct = \"Plugin\"\n[[require.args]]\ntype = \"number\"\n\n[script]\nstruct = \"Script\"\n\n[settings]\nargs = []\n\n[shared]\nproperty = true\nwritable = \"new-fields\"\n[[spawn.args]]\ntype = \"function\"\n[string.dump]\nremoved = true\n[[string.split.args]]\ntype = \"string\"\n\n[[string.split.args]]\nrequired = false\ntype = \"string\"\n[[table.create.args]]\ntype = \"number\"\n\n[[table.create.args]]\nrequired = false\ntype = \"any\"\n[[table.find.args]]\ntype = \"table\"\n\n[[table.find.args]]\ntype = \"any\"\n\n[[table.find.args]]\nrequired = false\ntype = \"number\"\n[[table.move.args]]\ntype = \"table\"\n\n[[table.move.args]]\ntype = \"number\"\n\n[[table.move.args]]\ntype = \"number\"\n\n[[table.move.args]]\ntype = \"number\"\n\n[[table.move.args]]\nrequired = false\ntype = \"table\"\n[[table.pack.args]]\ntype = \"...\"\n[[table.unpack.args]]\ntype = \"table\"\n\n[[table.unpack.args]]\nrequired = false\ntype = \"number\"\n\n[[table.unpack.args]]\nrequired = false\ntype = \"number\"\n\n[tick]\nargs = []\n\n[time]\nargs = []\n[[typeof.args]]\ntype = \"any\"\n[[utf8.char.args]]\nrequired = \"utf8.char should be used with an argument despite it not throwing\"\ntype = \"number\"\n\n[[utf8.char.args]]\nrequired = false\ntype = \"...\"\n\n[utf8.charpattern]\nproperty = true\n[[utf8.codepoint.args]]\ntype = \"string\"\n\n[[utf8.codepoint.args]]\nrequired = false\ntype = \"number\"\n\n[[utf8.codepoint.args]]\nrequired = false\ntype = \"number\"\n[[utf8.codes.args]]\ntype = \"string\"\n[[utf8.graphemes.args]]\ntype = \"string\"\n\n[[utf8.graphemes.args]]\nrequired = false\ntype = \"number\"\n\n[[utf8.graphemes.args]]\nrequired = false\ntype = \"number\"\n[[utf8.len.args]]\ntype = \"string\"\n\n[[utf8.len.args]]\nrequired = false\ntype = \"number\"\n\n[[utf8.len.args]]\nrequired = false\ntype = \"number\"\n[[utf8.nfcnormalize.args]]\ntype = \"string\"\n[[utf8.nfdnormalize.args]]\ntype = \"string\"\n[[utf8.offset.args]]\ntype = \"string\"\n\n[[utf8.offset.args]]\nrequired = false\ntype = \"number\"\n\n[[utf8.offset.args]]\nrequired = false\ntype = \"number\"\n[[wait.args]]\nrequired = false\ntype = \"number\"\n[[warn.args]]\ntype = \"string\"\n\n[[warn.args]]\nrequired = false\ntype = \"...\"\n\n[workspace]\nstruct = \"Workspace\"\n"
  },
  {
    "path": "selene.toml",
    "content": "std = \"roblox\"\n\n[config]\nempty_if = { comments_count = true }\n"
  },
  {
    "path": "src/Chat/BubbleChat.client.lua",
    "content": "--[[\n\t// FileName: BubbleChat.lua\n\t// Written by: jeditkacheff, TheGamer101\n\t// Description: Code for rendering bubble chat\n]]\n\n--[[ SERVICES ]]\nlocal PlayersService = game:GetService('Players')\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal ChatService = game:GetService(\"Chat\")\nlocal TextService = game:GetService(\"TextService\")\n--[[ END OF SERVICES ]]\n\nlocal LocalPlayer = PlayersService.LocalPlayer\nwhile LocalPlayer == nil do\n\tPlayersService.ChildAdded:wait()\n\tLocalPlayer = PlayersService.LocalPlayer\nend\n\nlocal PlayerGui = LocalPlayer:WaitForChild(\"PlayerGui\")\n\nlocal okShouldClipInGameChat, valueShouldClipInGameChat = pcall(function() return UserSettings():IsUserFeatureEnabled(\"UserShouldClipInGameChat\") end)\nlocal shouldClipInGameChat = okShouldClipInGameChat and valueShouldClipInGameChat\n\n--[[ SCRIPT VARIABLES ]]\nlocal CHAT_BUBBLE_FONT = Enum.Font.SourceSans\nlocal CHAT_BUBBLE_FONT_SIZE = Enum.FontSize.Size24 -- if you change CHAT_BUBBLE_FONT_SIZE_INT please change this to match\nlocal CHAT_BUBBLE_FONT_SIZE_INT = 24 -- if you change CHAT_BUBBLE_FONT_SIZE please change this to match\nlocal CHAT_BUBBLE_LINE_HEIGHT = CHAT_BUBBLE_FONT_SIZE_INT + 10\nlocal CHAT_BUBBLE_TAIL_HEIGHT = 14\nlocal CHAT_BUBBLE_WIDTH_PADDING = 30\nlocal CHAT_BUBBLE_FADE_SPEED = 1.5\n\nlocal BILLBOARD_MAX_WIDTH = 400\nlocal BILLBOARD_MAX_HEIGHT = 250\t--This limits the number of bubble chats that you see above characters\n\nlocal ELIPSES = \"...\"\nlocal MaxChatMessageLength = 128 -- max chat message length, including null terminator and elipses.\nlocal MaxChatMessageLengthExclusive = MaxChatMessageLength - string.len(ELIPSES) - 1\n\nlocal NEAR_BUBBLE_DISTANCE = 65\t--previously 45\nlocal MAX_BUBBLE_DISTANCE = 100\t--previously 80\n\n--[[ END OF SCRIPT VARIABLES ]]\n\n\n-- [[ SCRIPT ENUMS ]]\nlocal BubbleColor = {\tWHITE = \"dub\",\n\t\t\t\t\tBLUE = \"blu\",\n\t\t\t\t\tGREEN = \"gre\",\n\t\t\t\t\tRED = \"red\" }\n\n--[[ END OF SCRIPT ENUMS ]]\n\n-- This screenGui exists so that the billboardGui is not deleted when the PlayerGui is reset.\nlocal BubbleChatScreenGui = Instance.new(\"ScreenGui\")\nBubbleChatScreenGui.Name = \"BubbleChat\"\nBubbleChatScreenGui.ResetOnSpawn = false\nBubbleChatScreenGui.Parent = PlayerGui\n\n--[[ FUNCTIONS ]]\n\nlocal function lerpLength(msg, min, max)\n\treturn min + (max-min) * math.min(string.len(msg)/75.0, 1.0)\nend\n\nlocal function createFifo()\n\tlocal this = {}\n\tthis.data = {}\n\n\tlocal emptyEvent = Instance.new(\"BindableEvent\")\n\tthis.Emptied = emptyEvent.Event\n\n\tfunction this:Size()\n\t\treturn #this.data\n\tend\n\n\tfunction this:Empty()\n\t\treturn this:Size() <= 0\n\tend\n\n\tfunction this:PopFront()\n\t\ttable.remove(this.data, 1)\n\t\tif this:Empty() then emptyEvent:Fire() end\n\tend\n\n\tfunction this:Front()\n\t\treturn this.data[1]\n\tend\n\n\tfunction this:Get(index)\n\t\treturn this.data[index]\n\tend\n\n\tfunction this:PushBack(value)\n\t\ttable.insert(this.data, value)\n\tend\n\n\tfunction this:GetData()\n\t\treturn this.data\n\tend\n\n\treturn this\nend\n\nlocal function createCharacterChats()\n\tlocal this = {}\n\n\tthis.Fifo = createFifo()\n\tthis.BillboardGui = nil\n\n\treturn this\nend\n\nlocal function createMap()\n\tlocal this = {}\n\tthis.data = {}\n\tlocal count = 0\n\n\tfunction this:Size()\n\t\treturn count\n\tend\n\n\tfunction this:Erase(key)\n\t\tif this.data[key] then count = count - 1 end\n\t\tthis.data[key] = nil\n\tend\n\n\tfunction this:Set(key, value)\n\t\tthis.data[key] = value\n\t\tif value then count = count + 1 end\n\tend\n\n\tfunction this:Get(key)\n\t\tif not key then return end\n\t\tif not this.data[key] then\n\t\t\tthis.data[key] = createCharacterChats()\n\t\t\tlocal emptiedCon = nil\n\t\t\temptiedCon = this.data[key].Fifo.Emptied:connect(function()\n\t\t\t\temptiedCon:disconnect()\n\t\t\t\tthis:Erase(key)\n\t\t\tend)\n\t\tend\n\t\treturn this.data[key]\n\tend\n\n\tfunction this:GetData()\n\t\treturn this.data\n\tend\n\n\treturn this\nend\n\nlocal function createChatLine(message, bubbleColor, isLocalPlayer)\n\tlocal this = {}\n\n\tfunction this:ComputeBubbleLifetime(msg, isSelf)\n\t\tif isSelf then\n\t\t\treturn lerpLength(msg,8,15)\n\t\telse\n\t\t\treturn lerpLength(msg,12,20)\n\t\tend\n\tend\n\n\tthis.Origin = nil\n\tthis.RenderBubble = nil\n\tthis.Message = message\n\tthis.BubbleDieDelay = this:ComputeBubbleLifetime(message, isLocalPlayer)\n\tthis.BubbleColor = bubbleColor\n\tthis.IsLocalPlayer = isLocalPlayer\n\n\treturn this\nend\n\nlocal function createPlayerChatLine(player, message, isLocalPlayer)\n\tlocal this = createChatLine(message, BubbleColor.WHITE, isLocalPlayer)\n\n\tif player then\n\t\tthis.User = player.Name\n\t\tthis.Origin = player.Character\n\tend\n\n\treturn this\nend\n\nlocal function createGameChatLine(origin, message, isLocalPlayer, bubbleColor)\n\tlocal this = createChatLine(message, bubbleColor, isLocalPlayer)\n\tthis.Origin = origin\n\n\treturn this\nend\n\nfunction createChatBubbleMain(filePrefix, sliceRect)\n\tlocal chatBubbleMain = Instance.new(\"ImageLabel\")\n\tchatBubbleMain.Name = \"ChatBubble\"\n\tchatBubbleMain.ScaleType = Enum.ScaleType.Slice\n\tchatBubbleMain.SliceCenter = sliceRect\n\tchatBubbleMain.Image = \"rbxasset://textures/\" .. tostring(filePrefix) .. \".png\"\n\tchatBubbleMain.BackgroundTransparency = 1\n\tchatBubbleMain.BorderSizePixel = 0\n\tchatBubbleMain.Size = UDim2.new(1.0, 0, 1.0, 0)\n\tchatBubbleMain.Position = UDim2.new(0,0,0,0)\n\n\treturn chatBubbleMain\nend\n\nfunction createChatBubbleTail(position, size)\n\tlocal chatBubbleTail = Instance.new(\"ImageLabel\")\n\tchatBubbleTail.Name = \"ChatBubbleTail\"\n\tchatBubbleTail.Image = \"rbxasset://textures/ui/dialog_tail.png\"\n\tchatBubbleTail.BackgroundTransparency = 1\n\tchatBubbleTail.BorderSizePixel = 0\n\tchatBubbleTail.Position = position\n\tchatBubbleTail.Size = size\n\n\treturn chatBubbleTail\nend\n\nfunction createChatBubbleWithTail(filePrefix, position, size, sliceRect)\n\tlocal chatBubbleMain = createChatBubbleMain(filePrefix, sliceRect)\n\n\tlocal chatBubbleTail = createChatBubbleTail(position, size)\n\tchatBubbleTail.Parent = chatBubbleMain\n\n\treturn chatBubbleMain\nend\n\nfunction createScaledChatBubbleWithTail(filePrefix, frameScaleSize, position, sliceRect)\n\tlocal chatBubbleMain = createChatBubbleMain(filePrefix, sliceRect)\n\n\tlocal frame = Instance.new(\"Frame\")\n\tframe.Name = \"ChatBubbleTailFrame\"\n\tframe.BackgroundTransparency = 1\n\tframe.SizeConstraint = Enum.SizeConstraint.RelativeXX\n\tframe.Position = UDim2.new(0.5, 0, 1, 0)\n\tframe.Size = UDim2.new(frameScaleSize, 0, frameScaleSize, 0)\n\tframe.Parent = chatBubbleMain\n\n\tlocal chatBubbleTail = createChatBubbleTail(position, UDim2.new(1,0,0.5,0))\n\tchatBubbleTail.Parent = frame\n\n\treturn chatBubbleMain\nend\n\nfunction createChatImposter(filePrefix, dotDotDot, yOffset)\n\tlocal result = Instance.new(\"ImageLabel\")\n\tresult.Name = \"DialogPlaceholder\"\n\tresult.Image = \"rbxasset://textures/\" .. tostring(filePrefix) .. \".png\"\n\tresult.BackgroundTransparency = 1\n\tresult.BorderSizePixel = 0\n\tresult.Position = UDim2.new(0, 0, -1.25, 0)\n\tresult.Size = UDim2.new(1, 0, 1, 0)\n\n\tlocal image = Instance.new(\"ImageLabel\")\n\timage.Name = \"DotDotDot\"\n\timage.Image = \"rbxasset://textures/\" .. tostring(dotDotDot) .. \".png\"\n\timage.BackgroundTransparency = 1\n\timage.BorderSizePixel = 0\n\timage.Position = UDim2.new(0.001, 0, yOffset, 0)\n\timage.Size = UDim2.new(1, 0, 0.7, 0)\n\timage.Parent = result\n\n\treturn result\nend\n\n\nlocal this = {}\nthis.ChatBubble = {}\nthis.ChatBubbleWithTail = {}\nthis.ScalingChatBubbleWithTail = {}\nthis.CharacterSortedMsg = createMap()\n\n-- init chat bubble tables\nlocal function initChatBubbleType(chatBubbleType, fileName, imposterFileName, isInset, sliceRect)\n\tthis.ChatBubble[chatBubbleType] = createChatBubbleMain(fileName, sliceRect)\n\tthis.ChatBubbleWithTail[chatBubbleType] = createChatBubbleWithTail(fileName, UDim2.new(0.5, -CHAT_BUBBLE_TAIL_HEIGHT, 1, isInset and -1 or 0), UDim2.new(0, 30, 0, CHAT_BUBBLE_TAIL_HEIGHT), sliceRect)\n\tthis.ScalingChatBubbleWithTail[chatBubbleType] = createScaledChatBubbleWithTail(fileName, 0.5, UDim2.new(-0.5, 0, 0, isInset and -1 or 0), sliceRect)\nend\n\ninitChatBubbleType(BubbleColor.WHITE,\t\"ui/dialog_white\",\t\"ui/chatBubble_white_notify_bkg\", \tfalse,\tRect.new(5,5,15,15))\ninitChatBubbleType(BubbleColor.BLUE,\t\"ui/dialog_blue\",\t\"ui/chatBubble_blue_notify_bkg\",\ttrue, \tRect.new(7,7,33,33))\ninitChatBubbleType(BubbleColor.RED,\t\t\"ui/dialog_red\",\t\"ui/chatBubble_red_notify_bkg\",\t\ttrue,\tRect.new(7,7,33,33))\ninitChatBubbleType(BubbleColor.GREEN,\t\"ui/dialog_green\",\t\"ui/chatBubble_green_notify_bkg\",\ttrue,\tRect.new(7,7,33,33))\n\nfunction this:SanitizeChatLine(msg)\n\tif string.len(msg) > MaxChatMessageLengthExclusive then\n\t\treturn string.sub(msg, 1, MaxChatMessageLengthExclusive + string.len(ELIPSES))\n\telse\n\t\treturn msg\n\tend\nend\n\nlocal function createBillboardInstance(adornee)\n\tlocal billboardGui = Instance.new(\"BillboardGui\")\n\tbillboardGui.Adornee = adornee\n\tbillboardGui.Size = UDim2.new(0,BILLBOARD_MAX_WIDTH,0,BILLBOARD_MAX_HEIGHT)\n\tbillboardGui.StudsOffset = Vector3.new(0, 1.5, 2)\n\tbillboardGui.Parent = BubbleChatScreenGui\n\n\tlocal billboardFrame = Instance.new(\"Frame\")\n\tbillboardFrame.Name = \"BillboardFrame\"\n\tbillboardFrame.Size = UDim2.new(1,0,1,0)\n\tbillboardFrame.Position = UDim2.new(0,0,-0.5,0)\n\tbillboardFrame.BackgroundTransparency = 1\n\tbillboardFrame.Parent = billboardGui\n\n\tlocal billboardChildRemovedCon = nil\n\tbillboardChildRemovedCon = billboardFrame.ChildRemoved:connect(function()\n\t\tif #billboardFrame:GetChildren() <= 1 then\n\t\t\tbillboardChildRemovedCon:disconnect()\n\t\t\tbillboardGui:Destroy()\n\t\tend\n\tend)\n\n\tthis:CreateSmallTalkBubble(BubbleColor.WHITE).Parent = billboardFrame\n\n\treturn billboardGui\nend\n\nfunction this:CreateBillboardGuiHelper(instance, onlyCharacter)\n\tif instance and not this.CharacterSortedMsg:Get(instance)[\"BillboardGui\"] then\n\t\tif not onlyCharacter then\n\t\t\tif instance:IsA(\"BasePart\") then\n\t\t\t\t-- Create a new billboardGui object attached to this player\n\t\t\t\tlocal billboardGui = createBillboardInstance(instance)\n\t\t\t\tthis.CharacterSortedMsg:Get(instance)[\"BillboardGui\"] = billboardGui\n\t\t\t\treturn\n\t\t\tend\n\t\tend\n\n\t\tif instance:IsA(\"Model\") then\n\t\t\tlocal head = instance:FindFirstChild(\"Head\")\n\t\t\tif head and head:IsA(\"BasePart\") then\n\t\t\t\t-- Create a new billboardGui object attached to this player\n\t\t\t\tlocal billboardGui = createBillboardInstance(head)\n\t\t\t\tthis.CharacterSortedMsg:Get(instance)[\"BillboardGui\"] = billboardGui\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function distanceToBubbleOrigin(origin)\n\tif not origin then return 100000 end\n\n\treturn (origin.Position - game.Workspace.CurrentCamera.CoordinateFrame.p).magnitude\nend\n\nlocal function isPartOfLocalPlayer(adornee)\n\tif adornee and PlayersService.LocalPlayer.Character then\n\t\treturn adornee:IsDescendantOf(PlayersService.LocalPlayer.Character)\n\tend\nend\n\nfunction this:SetBillboardLODNear(billboardGui)\n\tlocal isLocalPlayer = isPartOfLocalPlayer(billboardGui.Adornee)\n\tbillboardGui.Size = UDim2.new(0, BILLBOARD_MAX_WIDTH, 0, BILLBOARD_MAX_HEIGHT)\n\tbillboardGui.StudsOffset = Vector3.new(0, isLocalPlayer and 1.5 or 2.5, isLocalPlayer and 2 or 0.1)\n\tbillboardGui.Enabled = true\n\tlocal billChildren = billboardGui.BillboardFrame:GetChildren()\n\tfor i = 1, #billChildren do\n\t\tbillChildren[i].Visible = true\n\tend\n\tbillboardGui.BillboardFrame.SmallTalkBubble.Visible = false\nend\n\nfunction this:SetBillboardLODDistant(billboardGui)\n\tlocal isLocalPlayer = isPartOfLocalPlayer(billboardGui.Adornee)\n\tbillboardGui.Size = UDim2.new(4,0,3,0)\n\tbillboardGui.StudsOffset = Vector3.new(0, 3, isLocalPlayer and 2 or 0.1)\n\tbillboardGui.Enabled = true\n\tlocal billChildren = billboardGui.BillboardFrame:GetChildren()\n\tfor i = 1, #billChildren do\n\t\tbillChildren[i].Visible = false\n\tend\n\tbillboardGui.BillboardFrame.SmallTalkBubble.Visible = true\nend\n\nfunction this:SetBillboardLODVeryFar(billboardGui)\n\tbillboardGui.Enabled = false\nend\n\nfunction this:SetBillboardGuiLOD(billboardGui, origin)\n\tif not origin then return end\n\n\tif origin:IsA(\"Model\") then\n\t\tlocal head = origin:FindFirstChild(\"Head\")\n\t\tif not head then origin = origin.PrimaryPart\n\t\telse origin = head end\n\tend\n\n\tlocal bubbleDistance = distanceToBubbleOrigin(origin)\n\n\tif bubbleDistance < NEAR_BUBBLE_DISTANCE then\n\t\tthis:SetBillboardLODNear(billboardGui)\n\telseif bubbleDistance >= NEAR_BUBBLE_DISTANCE and bubbleDistance < MAX_BUBBLE_DISTANCE then\n\t\tthis:SetBillboardLODDistant(billboardGui)\n\telse\n\t\tthis:SetBillboardLODVeryFar(billboardGui)\n\tend\nend\n\nfunction this:CameraCFrameChanged()\n\tfor index, value in pairs(this.CharacterSortedMsg:GetData()) do\n\t\tlocal playerBillboardGui = value[\"BillboardGui\"]\n\t\tif playerBillboardGui then this:SetBillboardGuiLOD(playerBillboardGui, index) end\n\tend\nend\n\nfunction this:CreateBubbleText(message)\n\tlocal bubbleText = Instance.new(\"TextLabel\")\n\tbubbleText.Name = \"BubbleText\"\n\tbubbleText.BackgroundTransparency = 1\n\tbubbleText.Position = UDim2.new(0,CHAT_BUBBLE_WIDTH_PADDING/2,0,0)\n\tbubbleText.Size = UDim2.new(1,-CHAT_BUBBLE_WIDTH_PADDING,1,0)\n\tbubbleText.Font = CHAT_BUBBLE_FONT\n\tif shouldClipInGameChat then\n\t\tbubbleText.ClipsDescendants = true\n\tend\n\tbubbleText.TextWrapped = true\n\tbubbleText.FontSize = CHAT_BUBBLE_FONT_SIZE\n\tbubbleText.Text = message\n\tbubbleText.Visible = false\n\tbubbleText.AutoLocalize = false\n\n\treturn bubbleText\nend\n\nfunction this:CreateSmallTalkBubble(chatBubbleType)\n\tlocal smallTalkBubble = this.ScalingChatBubbleWithTail[chatBubbleType]:Clone()\n\tsmallTalkBubble.Name = \"SmallTalkBubble\"\n\tsmallTalkBubble.AnchorPoint = Vector2.new(0, 0.5)\n\tsmallTalkBubble.Position = UDim2.new(0,0,0.5,0)\n\tsmallTalkBubble.Visible = false\n\tlocal text = this:CreateBubbleText(\"...\")\n\ttext.TextScaled = true\n\ttext.TextWrapped = false\n\ttext.Visible = true\n\ttext.Parent = smallTalkBubble\n\n\treturn smallTalkBubble\nend\n\nfunction this:UpdateChatLinesForOrigin(origin, currentBubbleYPos)\n\tlocal bubbleQueue = this.CharacterSortedMsg:Get(origin).Fifo\n\tlocal bubbleQueueSize = bubbleQueue:Size()\n\tlocal bubbleQueueData = bubbleQueue:GetData()\n\tif #bubbleQueueData <= 1 then return end\n\n\tfor index = (#bubbleQueueData - 1), 1, -1 do\n\t\tlocal value = bubbleQueueData[index]\n\t\tlocal bubble = value.RenderBubble\n\t\tif not bubble then return end\n\t\tlocal bubblePos = bubbleQueueSize - index + 1\n\n\t\tif bubblePos > 1 then\n\t\t\tlocal tail = bubble:FindFirstChild(\"ChatBubbleTail\")\n\t\t\tif tail then tail:Destroy() end\n\t\t\tlocal bubbleText = bubble:FindFirstChild(\"BubbleText\")\n\t\t\tif bubbleText then bubbleText.TextTransparency = 0.5 end\n\t\tend\n\n\t\tlocal udimValue = UDim2.new( bubble.Position.X.Scale, bubble.Position.X.Offset,\n\t\t\t\t\t\t\t\t\t1, currentBubbleYPos - bubble.Size.Y.Offset - CHAT_BUBBLE_TAIL_HEIGHT )\n\t\tbubble:TweenPosition(udimValue, Enum.EasingDirection.Out, Enum.EasingStyle.Bounce, 0.1, true)\n\t\tcurrentBubbleYPos = currentBubbleYPos - bubble.Size.Y.Offset - CHAT_BUBBLE_TAIL_HEIGHT\n\tend\nend\n\nfunction this:DestroyBubble(bubbleQueue, bubbleToDestroy)\n\tif not bubbleQueue then return end\n\tif bubbleQueue:Empty() then return end\n\n\tlocal bubble = bubbleQueue:Front().RenderBubble\n\tif not bubble then\n\t\tbubbleQueue:PopFront()\n\t \treturn\n\tend\n\n\tspawn(function()\n\t\twhile bubbleQueue:Front().RenderBubble ~= bubbleToDestroy do\n\t\t\twait()\n\t\tend\n\n\t\tbubble = bubbleQueue:Front().RenderBubble\n\n\t\tlocal timeBetween = 0\n\t\tlocal bubbleText = bubble:FindFirstChild(\"BubbleText\")\n\t\tlocal bubbleTail = bubble:FindFirstChild(\"ChatBubbleTail\")\n\n\t\twhile bubble and bubble.ImageTransparency < 1 do\n\t\t\ttimeBetween = wait()\n\t\t\tif bubble then\n\t\t\t\tlocal fadeAmount = timeBetween * CHAT_BUBBLE_FADE_SPEED\n\t\t\t\tbubble.ImageTransparency = bubble.ImageTransparency + fadeAmount\n\t\t\t\tif bubbleText then bubbleText.TextTransparency = bubbleText.TextTransparency + fadeAmount end\n\t\t\t\tif bubbleTail then bubbleTail.ImageTransparency = bubbleTail.ImageTransparency + fadeAmount end\n\t\t\tend\n\t\tend\n\n\t\tif bubble then\n\t\t\tbubble:Destroy()\n\t\t\tbubbleQueue:PopFront()\n\t\tend\n\tend)\nend\n\nfunction this:CreateChatLineRender(instance, line, onlyCharacter, fifo)\n\tif not instance then return end\n\n\tif not this.CharacterSortedMsg:Get(instance)[\"BillboardGui\"] then\n\t\tthis:CreateBillboardGuiHelper(instance, onlyCharacter)\n\tend\n\n\tlocal billboardGui = this.CharacterSortedMsg:Get(instance)[\"BillboardGui\"]\n\tif billboardGui then\n\t\tlocal chatBubbleRender = this.ChatBubbleWithTail[line.BubbleColor]:Clone()\n\t\tchatBubbleRender.Visible = false\n\t\tlocal bubbleText = this:CreateBubbleText(line.Message)\n\n\t\tbubbleText.Parent = chatBubbleRender\n\t\tchatBubbleRender.Parent = billboardGui.BillboardFrame\n\n\t\tline.RenderBubble = chatBubbleRender\n\n\t\tlocal currentTextBounds = TextService:GetTextSize(\n\t\t\t\tbubbleText.Text, CHAT_BUBBLE_FONT_SIZE_INT, CHAT_BUBBLE_FONT,\n\t\t\t\tVector2.new(BILLBOARD_MAX_WIDTH, BILLBOARD_MAX_HEIGHT))\n\t\tlocal bubbleWidthScale = math.max((currentTextBounds.X + CHAT_BUBBLE_WIDTH_PADDING)/BILLBOARD_MAX_WIDTH, 0.1)\n\t\tlocal numOflines = (currentTextBounds.Y/CHAT_BUBBLE_FONT_SIZE_INT)\n\n\t\t-- prep chat bubble for tween\n\t\tchatBubbleRender.Size = UDim2.new(0,0,0,0)\n\t\tchatBubbleRender.Position = UDim2.new(0.5,0,1,0)\n\n\t\tlocal newChatBubbleOffsetSizeY = numOflines * CHAT_BUBBLE_LINE_HEIGHT\n\n\t\tchatBubbleRender:TweenSizeAndPosition(UDim2.new(bubbleWidthScale, 0, 0, newChatBubbleOffsetSizeY),\n\t\t\t\t\t\t\t\t\t\t\t \tUDim2.new( (1-bubbleWidthScale)/2, 0, 1, -newChatBubbleOffsetSizeY),\n\t\t\t\t\t\t\t\t\t\t\t \tEnum.EasingDirection.Out, Enum.EasingStyle.Elastic, 0.1, true,\n\t\t\t\t\t\t\t\t\t\t\t \tfunction() bubbleText.Visible = true end)\n\n\t\t-- todo: remove when over max bubbles\n\t\tthis:SetBillboardGuiLOD(billboardGui, line.Origin)\n\t\tthis:UpdateChatLinesForOrigin(line.Origin, -newChatBubbleOffsetSizeY)\n\n\t\tdelay(line.BubbleDieDelay, function()\n\t\t\tthis:DestroyBubble(fifo, chatBubbleRender)\n\t\tend)\n\tend\nend\n\nfunction this:OnPlayerChatMessage(sourcePlayer, message, targetPlayer)\n\n\tif not this:BubbleChatEnabled() then return end\n\n\tlocal localPlayer = PlayersService.LocalPlayer\n\tlocal fromOthers = localPlayer ~= nil and sourcePlayer ~= localPlayer\n\n\tlocal safeMessage = this:SanitizeChatLine(message)\n\n\tlocal line = createPlayerChatLine(sourcePlayer, safeMessage, not fromOthers)\n\n\tif sourcePlayer and line.Origin then\n\t\tlocal fifo = this.CharacterSortedMsg:Get(line.Origin).Fifo\n\t\tfifo:PushBack(line)\n\t\t--Game chat (badges) won't show up here\n\t\tthis:CreateChatLineRender(sourcePlayer.Character, line, true, fifo)\n\tend\nend\n\nfunction this:OnGameChatMessage(origin, message, color)\n\tlocal localPlayer = PlayersService.LocalPlayer\n\tlocal fromOthers = localPlayer ~= nil and (localPlayer.Character ~= origin)\n\n\tlocal bubbleColor = BubbleColor.WHITE\n\n\tif color == Enum.ChatColor.Blue then bubbleColor = BubbleColor.BLUE\n\telseif color == Enum.ChatColor.Green then bubbleColor = BubbleColor.GREEN\n\telseif color == Enum.ChatColor.Red then bubbleColor = BubbleColor.RED end\n\n\tlocal safeMessage = this:SanitizeChatLine(message)\n\tlocal line = createGameChatLine(origin, safeMessage, not fromOthers, bubbleColor)\n\n\tthis.CharacterSortedMsg:Get(line.Origin).Fifo:PushBack(line)\n\tthis:CreateChatLineRender(origin, line, false, this.CharacterSortedMsg:Get(line.Origin).Fifo)\nend\n\nfunction this:BubbleChatEnabled()\n\tlocal clientChatModules = ChatService:FindFirstChild(\"ClientChatModules\")\n\tif clientChatModules then\n\t\tlocal chatSettings = clientChatModules:FindFirstChild(\"ChatSettings\")\n\t\tif chatSettings then\n\t\t\tlocal chatSettings = require(chatSettings)\n\t\t\tif chatSettings.BubbleChatEnabled ~= nil then\n\t\t\t\treturn chatSettings.BubbleChatEnabled\n\t\t\tend\n\t\tend\n\tend\n\treturn PlayersService.BubbleChat\nend\n\nfunction this:ShowOwnFilteredMessage()\n\tlocal clientChatModules = ChatService:FindFirstChild(\"ClientChatModules\")\n\tif clientChatModules then\n\t\tlocal chatSettings = clientChatModules:FindFirstChild(\"ChatSettings\")\n\t\tif chatSettings then\n\t\t\tchatSettings = require(chatSettings)\n\t\t\treturn chatSettings.ShowUserOwnFilteredMessage\n\t\tend\n\tend\n\treturn false\nend\n\nfunction findPlayer(playerName)\n\tfor i,v in pairs(PlayersService:GetPlayers()) do\n\t\tif v.Name == playerName then\n\t\t\treturn v\n\t\tend\n\tend\nend\n\nChatService.Chatted:connect(function(origin, message, color) this:OnGameChatMessage(origin, message, color) end)\n\nlocal cameraChangedCon = nil\nif game.Workspace.CurrentCamera then\n\tcameraChangedCon = game.Workspace.CurrentCamera:GetPropertyChangedSignal(\"CFrame\"):connect(function(prop) this:CameraCFrameChanged() end)\nend\n\ngame.Workspace.Changed:connect(function(prop)\n\tif prop == \"CurrentCamera\" then\n\t\tif cameraChangedCon then cameraChangedCon:disconnect() end\n\t\tif game.Workspace.CurrentCamera then\n\t\t\tcameraChangedCon = game.Workspace.CurrentCamera:GetPropertyChangedSignal(\"CFrame\"):connect(function(prop) this:CameraCFrameChanged() end)\n\t\tend\n\tend\nend)\n\n\nlocal AllowedMessageTypes = nil\n\nfunction getAllowedMessageTypes()\n\tif AllowedMessageTypes then\n\t\treturn AllowedMessageTypes\n\tend\n\tlocal clientChatModules = ChatService:FindFirstChild(\"ClientChatModules\")\n\tif clientChatModules then\n\t\tlocal chatSettings = clientChatModules:FindFirstChild(\"ChatSettings\")\n\t\tif chatSettings then\n\t\t\tchatSettings = require(chatSettings)\n\t\t\tif chatSettings.BubbleChatMessageTypes then\n\t\t\t\tAllowedMessageTypes = chatSettings.BubbleChatMessageTypes\n\t\t\t\treturn AllowedMessageTypes\n\t\t\tend\n\t\tend\n\t\tlocal chatConstants = clientChatModules:FindFirstChild(\"ChatConstants\")\n\t\tif chatConstants then\n\t\t\tchatConstants = require(chatConstants)\n\t\t\tAllowedMessageTypes = {chatConstants.MessageTypeDefault, chatConstants.MessageTypeWhisper}\n\t\tend\n\t\treturn AllowedMessageTypes\n\tend\n\treturn {\"Message\", \"Whisper\"}\nend\n\nfunction checkAllowedMessageType(messageData)\n\tlocal allowedMessageTypes = getAllowedMessageTypes()\n\tfor i = 1, #allowedMessageTypes do\n\t\tif allowedMessageTypes[i] == messageData.MessageType then\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\nlocal ChatEvents = ReplicatedStorage:WaitForChild(\"DefaultChatSystemChatEvents\")\nlocal OnMessageDoneFiltering = ChatEvents:WaitForChild(\"OnMessageDoneFiltering\")\nlocal OnNewMessage = ChatEvents:WaitForChild(\"OnNewMessage\")\n\nOnNewMessage.OnClientEvent:connect(function(messageData, channelName)\n\tif not checkAllowedMessageType(messageData) then\n\t\treturn\n\tend\n\n\tlocal sender = findPlayer(messageData.FromSpeaker)\n\tif not sender then\n\t\treturn\n\tend\n\n\tif not messageData.IsFiltered or messageData.FromSpeaker == LocalPlayer.Name then\n\t\tif messageData.FromSpeaker ~= LocalPlayer.Name or this:ShowOwnFilteredMessage() then\n\t\t\treturn\n\t\tend\n\tend\n\n\tthis:OnPlayerChatMessage(sender, messageData.Message, nil)\nend)\n\nOnMessageDoneFiltering.OnClientEvent:connect(function(messageData, channelName)\n\tif not checkAllowedMessageType(messageData) then\n\t\treturn\n\tend\n\n\tlocal sender = findPlayer(messageData.FromSpeaker)\n\tif not sender then\n\t\treturn\n\tend\n\n\tif messageData.FromSpeaker == LocalPlayer.Name and not this:ShowOwnFilteredMessage() then\n\t\treturn\n\tend\n\n\tthis:OnPlayerChatMessage(sender, messageData.Message, nil)\nend)\n"
  },
  {
    "path": "src/Chat/ChatModules/ChatCommandsTeller.lua",
    "content": "--\t// FileName: ChatCommandsTeller.lua\n--\t// Written by: Xsitsu\n--\t// Description: Module that provides information on default chat commands to players.\n\nlocal Chat = game:GetService(\"Chat\")\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatSettings = require(ReplicatedModules:WaitForChild(\"ChatSettings\"))\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = { Get = function(key,default) return default end } end\n\nlocal function Run(ChatService)\n\n\tlocal function ShowJoinAndLeaveCommands()\n\t\tif ChatSettings.ShowJoinAndLeaveHelpText ~= nil then\n\t\t\treturn ChatSettings.ShowJoinAndLeaveHelpText\n\t\tend\n\t\treturn false\n\tend\n\n\tlocal function ProcessCommandsFunction(fromSpeaker, message, channel)\n\t\tif (message:lower() == \"/?\" or message:lower() == \"/help\") then\n\t\t\tlocal speaker = ChatService:GetSpeaker(fromSpeaker)\n\t\t\t--[[\n\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_ChatCommandsTeller_Desc\",\"Vesteria chat commands:\"), channel)\n\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_ChatCommandsTeller_MeCommand\",\"/me <text> : roleplaying command for doing actions.\"), channel)\n\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_ChatCommandsTeller_WhisperCommand\",\"/whisper <speaker> or /w <speaker> : open private message channel with speaker.\"), channel)\n\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_ChatCommandsTeller_MuteCommand\",\"/mute <speaker> : mute a speaker.\"), channel)\n\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_ChatCommandsTeller_UnMuteCommand\",\"/unmute <speaker> : unmute a speaker.\"), channel)\n\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_ChatCommandsTeller_ToggleCommand\",\"/toggle <tags/color> : toggles chat tags or chat color.\"), channel)\n\t\t\t]]\n\t\t\t\n\t\t\tspeaker:SendSystemMessage(\"Vesteria chat commands:\", channel)\n\t\t\tspeaker:SendSystemMessage(\"    /me <text> :: roleplaying command for doing actions.\", channel)\t\n\t\t\tspeaker:SendSystemMessage(\"    /whisper <player> (/w) :: whisper a private message to a player.\", channel)\t\n\t\t\tspeaker:SendSystemMessage(\"    /mute <player> :: stop seeing chats from a player.\", channel)\n\t\t\tspeaker:SendSystemMessage(\"    /unmute <player> :: unmute a muted player.\", channel)\n\t\t\tspeaker:SendSystemMessage(\"    /party <text> (/p) :: send a message to party members.\", channel)\n\t\t\tspeaker:SendSystemMessage(\"    /invite <player> (/i) :: invite a player to your party.\", channel)\n\t\t\tspeaker:SendSystemMessage(\"    /duel <player> (/d) :: challenge a player to a duel.\", channel)\n\t\t\tspeaker:SendSystemMessage(\"    /trade <player> (/i) :: request a trade with a player.\", channel)\n\t\t\t\n\t\t\t\n\t\t\tlocal player = speaker:GetPlayer()\n\t\t\tif player and player.Team then\n\t\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_ChatCommandsTeller_TeamCommand\",\"/team <message> or /t <message> : send a team chat to players on your team.\"), channel)\n\t\t\tend\n\n\t\t\treturn true\n\t\tend\n\n\t\treturn false\n\tend\n\n\tChatService:RegisterProcessCommandsFunction(\"chat_commands_inquiry\", ProcessCommandsFunction, ChatConstants.StandardPriority)\n\n\tif ChatSettings.GeneralChannelName then\n\t\tlocal allChannel = ChatService:GetChannel(ChatSettings.GeneralChannelName)\n\t\tif (allChannel) then\n--\t\t\tallChannel.WelcomeMessage = \"\"\n\t\tend\n\tend\nend\n\nreturn Run\n"
  },
  {
    "path": "src/Chat/ChatModules/ChatFloodDetector.lua",
    "content": "--\t// FileName: ChatFloodDetector.lua\n--\t// Written by: Xsitsu\n--\t// Description: Module that limits the number of messages a speaker can send in a given period of time.\n\nlocal Chat = game:GetService(\"Chat\")\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\n\nlocal doFloodCheckByChannel = true\nlocal informSpeakersOfWaitTimes = true\nlocal chatBotsBypassFloodCheck = true\nlocal numberMessagesAllowed = 7\nlocal decayTimePeriod = 15\n\nlocal floodCheckTable = {}\nlocal whitelistedSpeakers = {}\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\nlocal function EnterTimeIntoLog(tbl)\n\ttable.insert(tbl, tick() + decayTimePeriod)\nend\n\nlocal function Run(ChatService)\n\tlocal function FloodDetectionProcessCommandsFunction(speakerName, message, channel)\n\t\tif (whitelistedSpeakers[speakerName]) then return false end\n\n\t\tlocal speakerObj = ChatService:GetSpeaker(speakerName)\n\t\tif (not speakerObj) then return false end\n\t\tif (chatBotsBypassFloodCheck and not speakerObj:GetPlayer()) then return false end\n\n\t\tif (not floodCheckTable[speakerName]) then\n\t\t\tfloodCheckTable[speakerName] = {}\n\t\tend\n\n\t\tlocal t = nil\n\n\t\tif (doFloodCheckByChannel) then\n\t\t\tif (not floodCheckTable[speakerName][channel]) then\n\t\t\t\tfloodCheckTable[speakerName][channel] = {}\n\t\t\tend\n\n\t\t\tt = floodCheckTable[speakerName][channel]\n\t\telse\n\t\t\tt = floodCheckTable[speakerName]\n\t\tend\n\n\t\tlocal now = tick()\n\t\twhile (#t > 0 and t[1] < now) do\n\t\t\ttable.remove(t, 1)\n\t\tend\n\n\t\tif (#t < numberMessagesAllowed) then\n\t\t\tEnterTimeIntoLog(t)\n\t\t\treturn false\n\t\telse\n\n\t\t\tlocal timeDiff = math.ceil(t[1] - now)\n\t\t\t\n\t\t\tif (informSpeakersOfWaitTimes) then\n\t\t\t\tlocal msg = string.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatFloodDetector_MessageDisplaySeconds\",\n\t\t\t\t\t\tstring.format(\"You must wait %d %s before sending another message!\", timeDiff, (timeDiff > 1) and \"seconds\" or \"second\")\n\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NUMBER}\",\n\t\t\t\t\ttostring(timeDiff)\n\t\t\t\t)\n\t\t\t\tspeakerObj:SendSystemMessage(msg, channel)\n\t\t\telse\n\t\t\t\tspeakerObj:SendSystemMessage(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatFloodDetector_Message\",\n\t\t\t\t\t\t\"You must wait before sending another message!\"\n\t\t\t\t\t)\n\t\t\t\t,channel)\n\t\t\tend\n\n\t\t\treturn true\n\t\tend\n\tend\n\n\tChatService:RegisterProcessCommandsFunction(\"flood_detection\", FloodDetectionProcessCommandsFunction, ChatConstants.LowPriority)\n\n\tChatService.SpeakerRemoved:connect(function(speakerName)\n\t\tfloodCheckTable[speakerName] = nil\n\tend)\nend\n\nreturn Run\n"
  },
  {
    "path": "src/Chat/ChatModules/ChatMessageValidator.lua",
    "content": "--\t// FileName: ChatMessageValidator.lua\n--\t// Written by: TheGamer101\n--\t// Description: Validate things such as no disallowed whitespace and chat message length on the server.\n\nlocal Chat = game:GetService(\"Chat\")\nlocal RunService = game:GetService(\"RunService\")\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatSettings = require(ReplicatedModules:WaitForChild(\"ChatSettings\"))\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\nlocal DISALLOWED_WHITESPACE = {\"\\n\", \"\\r\", \"\\t\", \"\\v\", \"\\f\"}\n\nif ChatSettings.DisallowedWhiteSpace then\n\tDISALLOWED_WHITESPACE = ChatSettings.DisallowedWhiteSpace\nend\n\nlocal function Run(ChatService)\n\n\tlocal function CanUserChat(playerObj)\n\t\tif RunService:IsStudio() then\n\t\t\treturn true\n\t\tend\n\t\tlocal success, canChat = pcall(function()\n\t\t\treturn Chat:CanUserChatAsync(playerObj.UserId)\n\t\tend)\n\t\treturn success and canChat\n\tend\n\n\tlocal function ValidateChatFunction(speakerName, message, channel)\n\t\tlocal speakerObj = ChatService:GetSpeaker(speakerName)\n\t\tlocal playerObj = speakerObj:GetPlayer()\n\t\tif not speakerObj then return false end\n\t\tif not playerObj then return false end\n\n\t\tif not RunService:IsStudio() and playerObj.UserId < 1 then\n\t\t\treturn true\n\t\tend\n\n\t\tif not CanUserChat(playerObj) then\n\t\t\tspeakerObj:SendSystemMessage(ChatLocalization:Get(\"GameChat_ChatMessageValidator_SettingsError\",\"Your chat settings prevent you from sending messages.\"), channel)\n\t\t\treturn true\n\t\tend\n\n\t\tif message:len() > ChatSettings.MaximumMessageLength + 1 then\n\t\t\tspeakerObj:SendSystemMessage(ChatLocalization:Get(\"GameChat_ChatMessageValidator_MaxLengthError\",\"Your message exceeds the maximum message length.\"), channel)\n\t\t\treturn true\n\t\tend\n\n\t\tfor i = 1, #DISALLOWED_WHITESPACE do\n\t\t\tif string.find(message, DISALLOWED_WHITESPACE[i]) then\n\t\t\t\tspeakerObj:SendSystemMessage(ChatLocalization:Get(\"GameChat_ChatMessageValidator_WhitespaceError\",\"Your message contains whitespace that is not allowed.\"), channel)\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\t\treturn false\n\tend\n\n\tChatService:RegisterProcessCommandsFunction(\"message_validation\", ValidateChatFunction, ChatSettings.LowPriority)\nend\n\nreturn Run\n"
  },
  {
    "path": "src/Chat/ChatModules/CustomCommands.lua",
    "content": "function matches(newText, pattern)\n\tnewText = string.lower(newText)\n\tpattern = string.lower(pattern)\n\tif string.sub(newText, 1, pattern:len()) == pattern then\n\t\treturn true\n\tend\nend\n\n\nfunction isCommand(newText)\n\tif matches(newText, \"/invite \") or  matches(newText, \"/i \") then\n\t\treturn \"invite\"\n\telseif matches(newText, \"/duel \") or  matches(newText, \"/d \") then\n\t\treturn \"duel\"\n\telseif matches(newText, \"/trade \") or  matches(newText, \"/t \") then\n\t\treturn \"trade\"\n\tend\nend\n\n\nlocal function Run(ChatService)\n\tlocal function commandRecieved(speakerName, message, channelName)\n\n\t\tlocal speaker = ChatService:GetSpeaker(speakerName)\t\n\t\tlocal player = speaker:GetPlayer()\n\t\t\n\t\t\n\t\tif isCommand(message) then\n\t\t\tlocal targetPlayerName = string.gsub(message, \"^[^%s]+ \", \"\")\n\n\t\t\t\n\t\tend\n\t\treturn false\n\tend\n \n\tChatService:RegisterProcessCommandsFunction(\"customCommands\", commandRecieved)\nend\n \nreturn Run"
  },
  {
    "path": "src/Chat/ChatModules/ExtraDataInitializer.lua",
    "content": "--\t// FileName: ExtraDataInitializer.lua\n--\t// Written by: Xsitsu\n--\t// Description: Module that sets some basic ExtraData such as name color, and chat color.\n--\n--  // Editted by: Nicholas_Foreman\n--  // ^ I'm not a Roblox admin nor an intern. I'm just a bored guy who has OCD and likes a neat chat. :)\n\n\nlocal stackTags = false\n--[[\n\tValue\tEffect\t\t\t\t\t\t\t\tExample\n\ttrue\tA player can have multiple tags\t\t[Owner] [Admin] [God] [Nicholas_Foreman]: hi\n\tfalse\tA player can only have one tag\t\t[Owner] [Nicholas_Foreman]: hi\n--]]\n\nlocal groupRankComparison = \">=\"\n--[[\n\tValue\tEffect\n\t\">=\"\tIf the player's rank is greater than or equal to the GroupId Rank\n\t\">\"\t\tIf the player's rank is greater than to the GroupId Rank\n\t\"<\"\t\tIf the player's rank is less than the GroupId Rank\n\t\">=\"\tIf the player's rank is less than or equal to the GroupId Rank\n--]]\n\n--[[\n\tThis is where you put all possible tags and chat colors. So, the index, like in my examples, is the name of the tag that you will later refer to.\n\n\tPriority is the order in which the tags are added. I have random values for examples.\n\n\tThe higher the number, the more it will be seen first.\n\n\tExample given what is below, if you have all the tags:\n\t[Owner] [Developer] [Moderator] [Tester] [VIP] [Roblox Staff] [Roblox Star] [Roblox QA] [Roblox DevForum] [Nicholas_Foreman]: hi\n\tLet's just say, it's reaaaaally long.\n--]]\n\nlocal possibleTags = {\n\n\t[\"Developer\"] = {\n\t\tTagText = \"Developer\",\n\t\tTagColor = Color3.fromRGB(255, 174, 11),\n\t\tPriority = 9,\n\t},\n\t[\"Moderator\"] = {\n\t\tTagText = \"Moderator\",\n\t\tTagColor = Color3.fromRGB(0, 170, 0),\n\t\tPriority = 7,\n\t},\n\t[\"Contributor\"] = {\n\t\tTagText = \"Contributor\",\n\t\tTagColor = Color3.fromRGB(255, 0, 100),\n\t\tPriority = 8,\n\t},\n\t[\"Tester\"] = {\n\t\tTagText = \"Tester\",\n\t\tTagColor = Color3.fromRGB(225, 203, 255),\n\t\tPriority = 7,\n\t},\n\t[\"Roblox Staff\"] = {\n\t\tTagText = \"Roblox\",\n\t\tTagColor = Color3.fromRGB(255, 85, 85),\n\t\tPriority = 6,\n\t},\n\t[\"Roblox Star\"] = {\n\t\tTagText = \"Star\",\n\t\tTagColor = Color3.fromRGB(255, 230, 101),\n\t\tPriority = 5,\n\t},\n\t[\"Alpha\"] = {\n\t\tTagText = \"α\",\n\t\tTagColor = Color3.fromRGB(234, 55, 58),\n\t\tPriority = 2,\n\t},\n\t[\"Beta\"] = {\n\t\tTagText = \"β\",\n\t\tTagColor = Color3.fromRGB(140,140,140),\n\t\tPriority = 1,\n\t}\n}\n\nlocal possibleChatColors = {\n\t[\"Dev Orange\"] = {\n\t\tChatColor = Color3.fromRGB(255, 154, 103),\n\t\tPriority = 5,\n\t},\n\t[\"Admin Yellow\"] = {\n\t\tChatColor = Color3.fromRGB(255, 215, 0),\n\t\tPriority = 4,\n\t},\n\t[\"Contributor Blue\"] = {\n\t\tChatColor = Color3.fromRGB(156, 247, 255),\n\t\tPriority = 3,\n\t},\n\t[\"Tester Purple\"] = {\n\t\tChatColor = Color3.fromRGB(225, 203, 255),\n\t\tPriority = 2,\n\t},\n\t[\"Intern Blue\"] = {\n\t\tChatColor = Color3.fromRGB(175, 221, 255),\n\t\tPriority = 1,\n\t}\n}\n\n-- Set the value of Chat Colors\nlocal SpecialChatColors = {\n\tGamepasses = {\n\t\t--[[{\n\t\t\t--- VIP Gamepass\n\t\t\tGamepassId = 718077,\n\t\t\tChatColor = possibleChatColors[\"Admin Yellow\"],\n\t\t},]]\n\t},\n\tBadges = {\n\t\t--[[{\n\t\t\t--- Tester Badge\n\t\t\tBadgeId = 336132652,\n\t\t\tChatColor = possibleChatColors[\"Admin Yellow\"],\n\t\t},]]\n\t},\n\tTeams = {\n\t\t--[[{\n\t\t\t--- Example Team\n\t\t\tTeam = \"Example\",\n\t\t\tChatColor = possibleChatColors[\"Admin Yellow\"],\n\t\t},]]\n\t},\n\tGroups = {\n\t\t--\"Contributor Blue\"\n\t\t{\n\t\t\t---Devs\n\t\t\tGroupId = 4238824,\n\t\t\tRank = 254,\n\t\t\tChatColor = possibleChatColors[\"Dev Orange\"],\n\t\t},\n\t\t{\n\t\t\t---Contributors\n\t\t\tGroupId = 4238824,\n\t\t\tRank = 5,\n\t\t\tChatColor = possibleChatColors[\"Contributor Blue\"],\n\t\t},\n\t\t{\n\t\t\t---Contributors\n\t\t\tGroupId = 4238824,\n\t\t\tRank = 3,\n\t\t\tChatColor = possibleChatColors[\"Tester Purple\"],\n\t\t},\n\n\t\t{\n\n\t\t\t--- Roblox Admins group\n\t\t\tGroupId = 1200769,\n\t\t\tChatColor = possibleChatColors[\"Admin Yellow\"],\n\t\t},\n\t\t{\n\t\t\t--- Roblox Interns group\n\t\t\tGroupId = 2868472,\n\t\t\tRank = 100,\n\t\t\tChatColor = possibleChatColors[\"Intern Blue\"],\n\t\t},\n\t\t{\n\t\t\t--- Roblox Stars group\n\t\t\tGroupId = 4199740,\n\t\t\tChatColor = possibleChatColors[\"Intern Blue\"],\n\t\t},\n\n\t},\n\tPlayers = {\n\n\t\t{\n\t\t\t--- Player Id -1, useful for testing.\n\t\t\tUserId = -1,\n\t\t\tChatColor = possibleChatColors[\"Admin Yellow\"],\n\t\t},\n\t}\n}\n\n-- Here is where you refer to the actual tags that you listed above. This saves time and simplicity. Also helps with mass editting. :?\nlocal SpecialTags = {\n\tGamepasses = {\n\t\t--[[{\n\t\t\t--- VIP Gamepass\n\t\t\tGamepassId = 718077,\n\t\t\tTags = {\"VIP\"}\n\t\t},]]\n\t},\n\tBadges = {\n\t\t{\n\t\t\t--- Tester Badge?\n\t\t\tBadgeId = 2124445897,\n\t\t\tTags = {\"Alpha\"}\n\t\t},\n\t\t{\n\t\t\t--- Tester Badge?\n\t\t\tBadgeId = 2124469268,\n\t\t\tTags = {\"Beta\"}\n\t\t},\n\t},\n\tTeams = {\n\t\t--[[{\n\t\t\t--- Example Team\n\t\t\tTeam = \"Example\",\n\t\t\tTags = {\"Owner\"}\n\t\t},]]\n\t},\n\tGroups = {\n\n\t\t{\n\t\t\t--- Roblox Admins group\n\t\t\tGroupId = 1200769,\n\t\t\tTags = {\"Roblox Staff\"}\n\t\t},\n\n\t\t{\n\t\t\t--- Roblox Stars group\n\t\t\tGroupId = 4199740,\n\t\t\tTags = {\"Roblox Star\"}\n\t\t},\n\n\n\t\t{\n\t\t\t---Devs\n\t\t\tGroupId = 4238824,\n\t\t\tRank = 254,\n\t\t\tTags = {\"Developer\"}\n\t\t},\n\t\t{\n\t\t\t---Contributors\n\t\t\tGroupId = 4238824,\n\t\t\tRank = 5,\n\t\t\tTags = {\"Contributor\"}\n\t\t},\n\t\t{\n\t\t\t---Tester\n\t\t\tGroupId = 4238824,\n\t\t\tRank = 3,\n\t\t\tTags = {\"Tester\"}\n\t\t},\n\n\t},\n\tPlayers = {\n\t\t{\n\t\t\t--- Nicholas_Foreman, editor and adder of chat tags.\n\t\t\tUserId = 9221415,\n\t\t\tTags = {\"Contributor\"}\n\t\t},\n\t\t{\n\t\t\t--- Player Id -1, useful for testing.\n\t\t\tUserId = -1,\n\t\t\tTags = {\"Contributor\", \"VIP\"}\n\t\t},\n\t}\n}\n\nlocal function MakeIsInGroup(groupId, requiredRank)\n\tassert(type(requiredRank) == \"nil\" or type(requiredRank) == \"number\", \"requiredRank must be a number or nil\")\n\n\treturn function(player)\n\t\tif player and player.UserId then\n\t\t\tlocal userId = player.UserId\n\n\t\t\tlocal inGroup = false\n\t\t\tlocal success, err = pcall(function() -- Many things can error is the IsInGroup check\n\t\t\t\tif requiredRank then\n\t\t\t\t\tif groupRankComparison == \">=\" then\n\t\t\t\t\t\tinGroup = player:GetRankInGroup(groupId) >= requiredRank\n\t\t\t\t\telseif groupRankComparison == \">\" then\n\t\t\t\t\t\tinGroup = player:GetRankInGroup(groupId) > requiredRank\n\t\t\t\t\telseif groupRankComparison == \"<\" then\n\t\t\t\t\t\tinGroup = player:GetRankInGroup(groupId) < requiredRank\n\t\t\t\t\telseif groupRankComparison == \"<=\" then\n\t\t\t\t\t\tinGroup = player:GetRankInGroup(groupId) <= requiredRank\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tinGroup = player:IsInGroup(groupId)\n\t\t\t\tend\n\t\t\tend)\n\t\t\tif not success and err then\n\t\t\t\tprint(\"Error checking in group: \" ..err)\n\t\t\tend\n\n\t\t\treturn inGroup\n\t\tend\n\n\t\treturn false\n\tend\nend\n\nlocal function ConstructIsInGroups()\n\tif SpecialChatColors.Groups then\n\t\tfor _, group in pairs(SpecialChatColors.Groups) do\n\t\t\tgroup.IsInGroup = MakeIsInGroup(group.GroupId, group.Rank)\n\t\tend\n\tend\n\tif SpecialTags.Groups then\n\t\tfor _, group in pairs(SpecialTags.Groups) do\n\t\t\tgroup.IsInGroup = MakeIsInGroup(group.GroupId, group.Rank)\n\t\tend\n\tend\nend\nConstructIsInGroups()\n\nlocal Players = game:GetService(\"Players\")\n\n\nlocal devColors = {\n\t[\"berezaa\"] = {Color3.fromRGB(255, 0, 64), Color3.fromRGB(255, 130, 147)},\n\t[\"lmaginationBurst\"] = {Color3.fromRGB(106, 165, 125), Color3.fromRGB(165, 112, 96)},\n\t[\"Rocky28447\"] = {Color3.fromRGB(0, 255, 255), Color3.fromRGB(240, 150, 255)},\n\t[\"Legitmanp\"] = {Color3.fromRGB( 33, 94, 253), Color3.fromRGB(103, 192, 253)},\n\t[\"10_MinuteAdRevenue\"] = {Color3.fromRGB(0, 255, 149), Color3.fromRGB(0, 244, 214)},\n\t[\"\"] = {Color3.fromRGB(0, 0, 0), Color3.fromRGB(0, 0, 0)},\n\t[\"\"] = {Color3.fromRGB(0, 0, 0), Color3.fromRGB(0, 0, 0)},\n\t[\"\"] = {Color3.fromRGB(0, 0, 0), Color3.fromRGB(0, 0, 0)},\n\t[\"\"] = {Color3.fromRGB(0, 0, 0), Color3.fromRGB(0, 0, 0)},\n}\n\n--[[\n\tTHIS IS A MESS. IF YOU WANT TO CLEAN IT UP, GO FOR IT. SORRY FOR ALL OF YOU SCRIPTERS OUT THERE. LMAO.\n--]]\nfunction GetSpecialChatColor(speakerName)\n\tif devColors[speakerName] then\n\t\treturn devColors[speakerName][2]\n\tend\n\tlocal chatColor = Color3.new(1,1,1)\n\tlocal currentPriority = 0\n\tif SpecialChatColors.Players then\n\t\tlocal playerFromSpeaker = Players:FindFirstChild(speakerName)\n\t\tif playerFromSpeaker then\n\t\t\tfor _, player in pairs(SpecialChatColors.Players) do\n\t\t\t\tif playerFromSpeaker.UserId == player.UserId then\n\t\t\t\t\tif player[\"ChatColor\"][\"Priority\"] > currentPriority then\n\t\t\t\t\t\tcurrentPriority = player[\"ChatColor\"][\"Priority\"]\n\t\t\t\t\t\tchatColor = player[\"ChatColor\"][\"ChatColor\"]\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\tif SpecialChatColors.Groups then\n\t\tfor _, group in pairs(SpecialChatColors.Groups) do\n\t\t\tif group.IsInGroup(Players:FindFirstChild(speakerName)) then\n\t\t\t\tif group[\"ChatColor\"][\"Priority\"] > currentPriority then\n\t\t\t\t\tcurrentPriority = group[\"ChatColor\"][\"Priority\"]\n\t\t\t\t\tchatColor = group[\"ChatColor\"][\"ChatColor\"]\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\tif SpecialChatColors.Teams then\n\t\tlocal playerFromSpeaker = Players:FindFirstChild(speakerName)\n\t\tif playerFromSpeaker then\n\t\t\tfor _, team in pairs(SpecialChatColors.Teams) do\n\t\t\t\tlocal actualTeam = game:GetService(\"Teams\"):FindFirstChild(team.Team)\n\t\t\t\tif playerFromSpeaker.Team == actualTeam then\n\t\t\t\t\tif team[\"ChatColor\"][\"Priority\"] > currentPriority then\n\t\t\t\t\t\tcurrentPriority = team[\"ChatColor\"][\"Priority\"]\n\t\t\t\t\t\tchatColor = team[\"ChatColor\"][\"ChatColor\"]\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\tif SpecialChatColors.Gamepasses then\n\t\tfor _, gamepass in pairs(SpecialChatColors.Gamepasses) do\n\t\t\tlocal playerFromSpeaker = Players:FindFirstChild(speakerName)\n\t\t\tif game:GetService(\"MarketplaceService\"):UserOwnsGamePassAsync(playerFromSpeaker.UserId, gamepass.GamepassId) then\n\t\t\t\tif gamepass[\"ChatColor\"][\"Priority\"] > currentPriority then\n\t\t\t\t\tcurrentPriority = gamepass[\"ChatColor\"][\"Priority\"]\n\t\t\t\t\tchatColor = gamepass[\"ChatColor\"][\"ChatColor\"]\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\tif SpecialChatColors.Badges then\n\t\tfor _, badge in pairs(SpecialChatColors.Badges) do\n\t\t\tlocal playerFromSpeaker = Players:FindFirstChild(speakerName)\n\t\t\tif game:GetService(\"BadgeService\"):UserHasBadgeAsync(playerFromSpeaker.UserId, badge.BadgeId)  then\n\t\t\t\tif badge[\"ChatColor\"][\"Priority\"] > currentPriority then\n\t\t\t\t\tcurrentPriority = badge[\"ChatColor\"][\"Priority\"]\n\t\t\t\t\tchatColor = badge[\"ChatColor\"][\"ChatColor\"]\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\treturn chatColor\nend\nfunction GetSpecialTags(speakerName)\n\tif devColors[speakerName] then\n\t\tlocal tag = {\n\t\t\tTagText = \"Developer\",\n\t\t\tTagColor = devColors[speakerName][1],\n\t\t\tPriority = 10,\n\t\t}\n\t\treturn {tag}\n\tend\n\tlocal tags = {}\n\tlocal currentPriority = 0\n\tif SpecialTags.Players then\n\t\tlocal playerFromSpeaker = Players:FindFirstChild(speakerName)\n\t\tif playerFromSpeaker then\n\t\t\tfor _, player in pairs(SpecialTags.Players) do\n\t\t\t\tif playerFromSpeaker.UserId == player.UserId then\n\t\t\t\t\tfor possibleTagName,possibleTagValue in pairs(possibleTags) do\n\t\t\t\t\t\tfor i,playerTagName in pairs(player.Tags) do\n\t\t\t\t\t\t\tif playerTagName == possibleTagName then\n\t\t\t\t\t\t\t\tif stackTags then\n\t\t\t\t\t\t\t\t\ttable.insert(tags,possibleTagValue)\n\t\t\t\t\t\t\t\t\tif possibleTagValue[\"Priority\"] > currentPriority then\n\t\t\t\t\t\t\t\t\t\tcurrentPriority = possibleTagValue[\"Priority\"]\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tif possibleTagValue[\"Priority\"] > currentPriority then\n\t\t\t\t\t\t\t\t\t\ttags = {possibleTagValue}\n\t\t\t\t\t\t\t\t\t\tcurrentPriority = possibleTagValue[\"Priority\"]\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\tif SpecialTags.Groups then\n\t\tfor _, group in pairs(SpecialTags.Groups) do\n\t\t\tif group.IsInGroup(Players:FindFirstChild(speakerName)) then\n\t\t\t\tfor possibleTagName,possibleTagValue in pairs(possibleTags) do\n\t\t\t\t\tfor i,groupTagName in pairs(group.Tags) do\n\t\t\t\t\t\tif groupTagName == possibleTagName then\n\t\t\t\t\t\t\tif stackTags then\n\t\t\t\t\t\t\t\ttable.insert(tags,possibleTagValue)\n\t\t\t\t\t\t\t\tif possibleTagValue[\"Priority\"] > currentPriority then\n\t\t\t\t\t\t\t\t\tcurrentPriority = possibleTagValue[\"Priority\"]\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tif possibleTagValue[\"Priority\"] > currentPriority then\n\t\t\t\t\t\t\t\t\ttags = {possibleTagValue}\n\t\t\t\t\t\t\t\t\tcurrentPriority = possibleTagValue[\"Priority\"]\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\tif SpecialTags.Teams then\n\t\tlocal playerFromSpeaker = Players:FindFirstChild(speakerName)\n\t\tif playerFromSpeaker then\n\t\t\tfor _, team in pairs(SpecialTags.Teams) do\n\t\t\t\tlocal actualTeam = game:GetService(\"Teams\"):FindFirstChild(team.Team)\n\t\t\t\tif playerFromSpeaker.Team == actualTeam then\n\t\t\t\t\tfor possibleTagName,possibleTagValue in pairs(possibleTags) do\n\t\t\t\t\t\tfor i,playerTagName in pairs(team.Tags) do\n\t\t\t\t\t\t\tif playerTagName == possibleTagName then\n\t\t\t\t\t\t\t\tif stackTags then\n\t\t\t\t\t\t\t\t\ttable.insert(tags,possibleTagValue)\n\t\t\t\t\t\t\t\t\tif possibleTagValue[\"Priority\"] > currentPriority then\n\t\t\t\t\t\t\t\t\t\tcurrentPriority = possibleTagValue[\"Priority\"]\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tif possibleTagValue[\"Priority\"] > currentPriority then\n\t\t\t\t\t\t\t\t\t\ttags = {possibleTagValue}\n\t\t\t\t\t\t\t\t\t\tcurrentPriority = possibleTagValue[\"Priority\"]\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\tif SpecialTags.Gamepasses then\n\t\tfor _, gamepass in pairs(SpecialTags.Gamepasses) do\n\t\t\tlocal playerFromSpeaker = Players:FindFirstChild(speakerName)\n\t\t\tif game:GetService(\"MarketplaceService\"):UserOwnsGamePassAsync(playerFromSpeaker.UserId, gamepass.GamepassId) then\n\t\t\t\tlocal playerTags = {}\n\t\t\t\tfor possibleTagName,possibleTagValue in pairs(possibleTags) do\n\t\t\t\t\tfor i,gamepassTagName in pairs(gamepass.Tags) do\n\t\t\t\t\t\tif gamepassTagName == possibleTagName then\n\t\t\t\t\t\t\tif stackTags then\n\t\t\t\t\t\t\t\ttable.insert(tags,possibleTagValue)\n\t\t\t\t\t\t\t\tif possibleTagValue[\"Priority\"] > currentPriority then\n\t\t\t\t\t\t\t\t\tcurrentPriority = possibleTagValue[\"Priority\"]\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tif possibleTagValue[\"Priority\"] > currentPriority then\n\t\t\t\t\t\t\t\t\ttags = {possibleTagValue}\n\t\t\t\t\t\t\t\t\tcurrentPriority = possibleTagValue[\"Priority\"]\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\tif SpecialTags.Badges then\n\t\tfor _, badge in pairs(SpecialTags.Badges) do\n\t\t\tlocal playerFromSpeaker = Players:FindFirstChild(speakerName)\n\t\t\tif  game:GetService(\"BadgeService\"):UserHasBadgeAsync(playerFromSpeaker.UserId, badge.BadgeId) then\n\t\t\t\tlocal playerTags = {}\n\t\t\t\tfor possibleTagName,possibleTagValue in pairs(possibleTags) do\n\t\t\t\t\tfor i,badgeTagName in pairs(badge.Tags) do\n\t\t\t\t\t\tif badgeTagName == possibleTagName then\n\t\t\t\t\t\t\tif stackTags then\n\t\t\t\t\t\t\t\ttable.insert(tags,possibleTagValue)\n\t\t\t\t\t\t\t\tif possibleTagValue[\"Priority\"] > currentPriority then\n\t\t\t\t\t\t\t\t\tcurrentPriority = possibleTagValue[\"Priority\"]\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tif possibleTagValue[\"Priority\"] > currentPriority then\n\t\t\t\t\t\t\t\t\ttags = {possibleTagValue}\n\t\t\t\t\t\t\t\t\tcurrentPriority = possibleTagValue[\"Priority\"]\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\tlocal returnTags = {}\n\tif #tags > 1 then\n\t\tfor i = currentPriority, 1, -1 do\n\t\t\tfor tagIndex,tagValue in pairs(tags) do\n\t\t\t\tif tagValue[\"Priority\"] == i then\n\t\t\t\t\ttable.insert(returnTags, tagValue)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\telse\n\t\treturnTags = tags\n\tend\n\treturn returnTags\nend\n\nlocal function Run(ChatService)\n\tlocal NAME_COLORS =\n\t{\n\t\tColor3.new(253/255, 41/255, 67/255), -- BrickColor.new(\"Bright red\").Color,\n\t\tColor3.new(1/255, 162/255, 255/255), -- BrickColor.new(\"Bright blue\").Color,\n\t\tColor3.new(2/255, 184/255, 87/255), -- BrickColor.new(\"Earth green\").Color,\n\t\tBrickColor.new(\"Bright violet\").Color,\n\t\tBrickColor.new(\"Bright orange\").Color,\n\t\tBrickColor.new(\"Bright yellow\").Color,\n\t\tBrickColor.new(\"Light reddish violet\").Color,\n\t\tBrickColor.new(\"Brick yellow\").Color,\n\t}\n\n\tlocal function GetNameValue(pName)\n\t\tlocal value = 0\n\t\tfor index = 1, #pName do\n\t\t\tlocal cValue = string.byte(string.sub(pName, index, index))\n\t\t\tlocal reverseIndex = #pName - index + 1\n\t\t\tif #pName%2 == 1 then\n\t\t\t\treverseIndex = reverseIndex - 1\n\t\t\tend\n\t\t\tif reverseIndex%4 >= 2 then\n\t\t\t\tcValue = -cValue\n\t\t\tend\n\t\t\tvalue = value + cValue\n\t\tend\n\t\treturn value\n\tend\n\n\tlocal color_offset = 0\n\tlocal function ComputeNameColor(pName)\n\t\treturn NAME_COLORS[((GetNameValue(pName) + color_offset) % #NAME_COLORS) + 1]\n\tend\n\n\tlocal function GetNameColor(speaker)\n\t\tlocal player = speaker:GetPlayer()\n\t\tif player then\n\t\t\tif player.Team ~= nil then\n\t\t\t\treturn player.TeamColor.Color\n\t\t\tend\n\t\tend\n\t\treturn ComputeNameColor(speaker.Name)\n\tend\n\n\tChatService.SpeakerAdded:connect(function(speakerName)\n\t\tlocal speaker = ChatService:GetSpeaker(speakerName)\n\t\tif not speaker:GetExtraData(\"NameColor\") then\n\t\t\tspeaker:SetExtraData(\"NameColor\", GetNameColor(speaker))\n\t\tend\n\t\tif not speaker:GetExtraData(\"ChatColor\") then\n\t\t\tlocal specialChatColor = GetSpecialChatColor(speakerName)\n\t\t\tif specialChatColor then\n\t\t\t\tspeaker:SetExtraData(\"ChatColor\", specialChatColor)\n\t\t\tend\n\t\tend\n\t\tif not speaker:GetExtraData(\"Tags\") then\n\t\t\tlocal specialTags = GetSpecialTags(speakerName)\n\t\t\tspeaker:SetExtraData(\"Tags\", specialTags)\n\t\tend\n\tend)\n\n\tlocal PlayerChangedConnections = {}\n\tPlayers.PlayerAdded:connect(function(player)\n\t\tlocal changedConn = player.Changed:connect(function(property)\n\t\t\tlocal speaker = ChatService:GetSpeaker(player.Name)\n\t\t\tif speaker then\n\t\t\t\tif property == \"TeamColor\" or property == \"Neutral\" or property == \"Team\" then\n\t\t\t\t\tspeaker:SetExtraData(\"NameColor\", GetNameColor(speaker))\n\t\t\t\t\tlocal specialTags = GetSpecialTags(player.Name)\n\t\t\t\t\tspeaker:SetExtraData(\"Tags\", specialTags)\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\t\tPlayerChangedConnections[player] = changedConn\n\tend)\n\n\tPlayers.PlayerRemoving:connect(function(player)\n\t\tlocal changedConn = PlayerChangedConnections[player]\n\t\tif changedConn then\n\t\t\tchangedConn:Disconnect()\n\t\tend\n\t\tPlayerChangedConnections[player] = nil\n\tend)\n\n\tlocal DefaultChatSystemChatEvents = game:GetService(\"ReplicatedStorage\"):WaitForChild(\"DefaultChatSystemChatEvents\");\n\tlocal event = Instance.new(\"RemoteEvent\");\n\t\tevent.Name = \"Toggle\"\n\t\tevent.Parent = DefaultChatSystemChatEvents\n\tevent.OnServerEvent:Connect(function(player, eventType)\n\t\tlocal speaker = ChatService:GetSpeaker(player.Name)\n\t\tif eventType == \"Tags\" then\n\t\t\tif not speaker:GetExtraData(\"Tags\") or not speaker:GetExtraData(\"Tags\")[1] then\n\t\t\t\tlocal specialTags = GetSpecialTags(player.Name)\n\t\t\t\tspeaker:SetExtraData(\"Tags\", specialTags)\n\t\t\telse\n\t\t\t\tspeaker:SetExtraData(\"Tags\", {})\n\t\t\tend\n\t\telseif eventType == \"Color\" then\n\t\t\tif not speaker:GetExtraData(\"ChatColor\") then\n\t\t\t\tlocal specialChatColor = GetSpecialChatColor(player.Name)\n\t\t\t\tif specialChatColor then\n\t\t\t\t\tspeaker:SetExtraData(\"ChatColor\", specialChatColor)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tspeaker:SetExtraData(\"ChatColor\", false)\n\t\t\tend\n\t\tend\n\tend)\nend\n\nreturn Run"
  },
  {
    "path": "src/Chat/ChatModules/FriendJoinNotifier.lua",
    "content": "--\t// FileName: FriendJoinNotifer.lua\n--\t// Written by: TheGamer101\n--\t// Description: Module that adds a message to the chat whenever a friend joins the game.\n\nlocal Chat = game:GetService(\"Chat\")\nlocal Players = game:GetService(\"Players\")\nlocal FriendService = game:GetService(\"FriendService\")\n\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatSettings = require(ReplicatedModules:WaitForChild(\"ChatSettings\"))\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\nlocal FriendMessageTextColor = Color3.fromRGB(255, 255, 255)\nlocal FriendMessageExtraData = {ChatColor = FriendMessageTextColor}\n\nlocal function Run(ChatService)\n\n\tlocal function ShowFriendJoinNotification()\n\t\tif ChatSettings.ShowFriendJoinNotification ~= nil then\n\t\t\treturn ChatSettings.ShowFriendJoinNotification\n\t\tend\n\t\treturn false\n\tend\n\n\tlocal function SendFriendJoinNotification(player, joinedFriend)\n\t\tlocal speakerObj = ChatService:GetSpeaker(player.Name)\n\t\tif speakerObj then\n\t\t\tspeakerObj:SendSystemMessage(\n\t\t\t\tstring.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_FriendChatNotifier_JoinMessage\",\n\t\t\t\t\t\tstring.format(\"Your friend %s has joined the game.\", joinedFriend.Name)\n\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\",\n\t\t\t\t\tjoinedFriend.Name\n\t\t\t\t),\n\t\t\t\t\"System\",\n\t\t\t\tFriendMessageExtraData\n\t\t\t)\n\t\tend\n\tend\n\n\tlocal function TrySendFriendNotification(player, joinedPlayer)\n\t\tif player ~= joinedPlayer then\n\t\t\tcoroutine.wrap(function()\n\t\t\t\tif player:IsFriendsWith(joinedPlayer.UserId) then\n\t\t\t\t\tSendFriendJoinNotification(player, joinedPlayer)\n\t\t\t\tend\n\t\t\tend)()\n\t\tend\n\tend\n\n\tif ShowFriendJoinNotification() then\n\t\tPlayers.PlayerAdded:connect(function(player)\n\t\t\tlocal possibleFriends = Players:GetPlayers()\n\t\t\tfor i = 1, #possibleFriends do\n\t\t\t\tTrySendFriendNotification(possibleFriends[i], player)\n\t\t\tend\n\t\tend)\n\tend\nend\n\nreturn Run\n"
  },
  {
    "path": "src/Chat/ChatModules/GuildChat.lua",
    "content": "--\t// FileName: GuildChat.lua\n--\t// Written by: Xsitsu\n--\t// Description: Module that handles all guild chat.\n\nlocal Chat = game:GetService(\"Chat\")\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatSettings = require(ReplicatedModules:WaitForChild(\"ChatSettings\"))\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network\t\t= modules.load(\"network\")\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\nlocal errorTextColor = ChatSettings.ErrorMessageTextColor or Color3.fromRGB(245, 50, 50)\nlocal errorExtraData = {ChatColor = errorTextColor}\n\nlocal function Run(ChatService)\n\n\tlocal Players = game:GetService(\"Players\")\n\n\tlocal channel = ChatService:AddChannel(\"Guild\")\n\tchannel.Joinable = false\n\tchannel.Leavable = false\n\tchannel.AutoJoin = false\n\tchannel.Private = true\n\n\tlocal function GuildChatReplicationFunction(fromSpeaker, message, channelName)\n\t\tlocal speakerObj = ChatService:GetSpeaker(fromSpeaker)\n\t\tlocal channelObj = ChatService:GetChannel(channelName)\n\t\t\n\t\tif channelName == \"Guild\" then\n\t\t\n\t\t\tif (speakerObj and channelObj) then\n\t\t\t\tlocal player = speakerObj:GetPlayer()\n\t\t\t\tif (player) then\n\t\t\t\t\t\n\t\t\t\t\tlocal success, reason = network:invoke(\"sendGuildChat\", player, message)\n\t\t\t\t\treturn success, reason\n\t\t\t\t\t\n\t\t\t\t\t--[[\n\t\t\t\t\tlocal speakerGuild = network:invoke(\"getGuildDataByPlayer\", player)\n\t\t\t\t\tif speakerGuild and speakerGuild.members and #speakerGuild.members > 0 then\n\t\n\t\t\t\t\t\tfor i, guildMember in pairs(speakerGuild.members) do\n\t\t\t\t\t\t\tif guildMember and guildMember.player then\n\t\t\t\t\t\t\t\tlocal speakerName = guildMember.player.Name\n\t\t\t\t\t\t\t\tlocal otherSpeaker = ChatService:GetSpeaker(speakerName)\n\t\t\t\t\t\t\t\tif (otherSpeaker) then\n\t\t\t\t\t\t\t\t\tlocal otherPlayer = otherSpeaker:GetPlayer()\n\t\t\t\t\t\t\t\t\tif (otherPlayer) then\n\t\t\t\n\t\t\n\t\t\t\t\t\t\t\t\t\tlocal extraData = {\n\t\t\t\t\t\t\t\t\t\t\tChannelColor = Color3.fromRGB(0, 240, 244);\n\t\t\t\t\t\t\t\t\t\t\tChatColor = Color3.fromRGB(0, 240, 244)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\totherSpeaker:SendMessage(message, channelName, fromSpeaker, extraData)\n\t\t\t\t\t\t\n\t\t\t\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\t]]\n\t\t\t\tend\n\t\t\tend\n\t\n\t\t\treturn true\n\t\tend\n\tend\n\n\tchannel:RegisterProcessCommandsFunction(\"replication_function_guild\", GuildChatReplicationFunction, ChatConstants.LowPriority)\n\n\tlocal function DoGuildCommand(fromSpeaker, message, channel)\n\t\tif message == nil then\n\t\t\tmessage = \"\"\n\t\tend\n\n\t\tlocal speaker = ChatService:GetSpeaker(fromSpeaker)\n\t\tif speaker then\n\t\t\tlocal player = speaker:GetPlayer()\n\n\t\t\tif player then\n\t\t\t\t\n\t\t\t\tlocal speakerGuildId = player:FindFirstChild(\"guildId\") and player.guildId.Value\n\t\t\t\t\n\t\t\t\tif speakerGuildId == nil or speakerGuildId == \"\" then\n\t\t\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_GuildChat_CannotGuildChatIfNotInGuild\",\"You cannot guild chat if you are not on a guild!\"), channel, errorExtraData)\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\tlocal channelObj = ChatService:GetChannel(\"Guild\")\n\t\t\t\tif channelObj then\n\t\t\t\t\tif not speaker:IsInChannel(channelObj.Name) then\n\t\t\t\t\t\tspeaker:JoinChannel(channelObj.Name)\n\t\t\t\t\tend\n\t\t\t\t\tif message and string.len(message) > 0 then\n\t\t\t\t\t\tspeaker:SayMessage(message, channelObj.Name)\n\t\t\t\t\tend\n\t\t\t\t\tspeaker:SetMainChannel(channelObj.Name)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function GuildCommandsFunction(fromSpeaker, message, channel)\n\t\tlocal processedCommand = false\n\n\t\tif message == nil then\n\t\t\terror(\"Message is nil\")\n\t\tend\n\n\t\tif channel == \"Guild\" then\n\t\t\treturn false\n\t\tend\n                                          \n\t\tif string.sub(message, 1, 6):lower() == \"/guild \" or message:lower() == \"/guild\" then\n\t\t\tDoGuildCommand(fromSpeaker, string.sub(message, 7), channel)\n\t\t\tprocessedCommand = true\n\t\telseif string.sub(message, 1, 3):lower() == \"/g \" or message:lower() == \"/g\" then\n\t\t\tDoGuildCommand(fromSpeaker, string.sub(message, 4), channel)\n\t\t\tprocessedCommand = true\n\n\t\tend\n\n\t\treturn processedCommand\n\tend\n\n\tChatService:RegisterProcessCommandsFunction(\"guild_commands\", GuildCommandsFunction, ChatConstants.StandardPriority)\n\n\tlocal function GetDefaultChannelNameColor()\n\t\treturn Color3.fromRGB(145, 71, 255)\n\tend\n\n\tlocal function PutSpeakerInCorrectGuildChatState(speakerObj, playerObj)\n\n\t\tspeakerObj:UpdateChannelNameColor(channel.Name, GetDefaultChannelNameColor())\n\n\t\tif not speakerObj:IsInChannel(channel.Name) then\n\t\t\tspeakerObj:JoinChannel(channel.Name)\n\t\tend\n\t\n\tend\n\n\tChatService.SpeakerAdded:connect(function(speakerName)\n\t\tlocal speakerObj = ChatService:GetSpeaker(speakerName)\n\t\tif speakerObj then\n\t\t\tlocal player = speakerObj:GetPlayer()\n\t\t\tif player then\n\t\t\t\tPutSpeakerInCorrectGuildChatState(speakerObj, player)\n\t\t\tend\n\t\tend\n\tend)\n\n\tlocal PlayerChangedConnections = {}\n\tnetwork:connect(\"playerDataLoaded\", \"Event\", function(player)\n\t\tlocal speakerObj = ChatService:GetSpeaker(player.Name)\n\t\tif speakerObj then\n\t\t\tPutSpeakerInCorrectGuildChatState(speakerObj, player)\n\t\tend\n\n\n\t\tPlayerChangedConnections[player] = nil\n\tend)\n\n\tPlayers.PlayerRemoving:connect(function(player)\n\t\tlocal changedConn = PlayerChangedConnections[player]\n\t\tif changedConn then\n\t\t\tchangedConn:Disconnect()\n\t\tend\n\t\tPlayerChangedConnections[player] = nil\n\tend)\nend\n\nreturn Run\n"
  },
  {
    "path": "src/Chat/ChatModules/MeCommand.lua",
    "content": "--\t// FileName: MeCommand.lua\n--\t// Written by: TheGamer101\n--\t// Description: Sets the type of /me messages.\n\nlocal Chat = game:GetService(\"Chat\")\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\n\nlocal function Run(ChatService)\n\n\tlocal function MeCommandFilterFunction(speakerName, messageObj, channelName)\n\t\tlocal message = messageObj.Message\n\t\tif message and string.sub(message, 1, 4):lower() == \"/me \" then\n\t\t\t-- Set a different message type so that clients can render the message differently.\n\t\t\tmessageObj.MessageType = ChatConstants.MessageTypeMeCommand\n\t\tend\n\tend\n\n\tChatService:RegisterFilterMessageFunction(\"me_command\", MeCommandFilterFunction)\nend\n\nreturn Run\n"
  },
  {
    "path": "src/Chat/ChatModules/MuteSpeaker.lua",
    "content": "--\t// FileName: MuteSpeaker.lua\n--\t// Written by: TheGamer101\n--\t// Description: Module that handles all the mute and unmute commands.\n\nlocal Chat = game:GetService(\"Chat\")\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\nlocal ChatSettings = require(ReplicatedModules:WaitForChild(\"ChatSettings\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\nlocal errorTextColor = ChatSettings.ErrorMessageTextColor or Color3.fromRGB(245, 50, 50)\nlocal errorExtraData = {ChatColor = errorTextColor}\n\nlocal function Run(ChatService)\n\n\tlocal function GetSpeakerNameFromMessage(message)\n\t\tlocal speakerName = message\n\t\tif string.sub(message, 1, 1) == \"\\\"\" then\n\t\t\tlocal pos = string.find(message, \"\\\"\", 2)\n\t\t\tif pos then\n\t\t\t\tspeakerName = string.sub(message, 2, pos - 1)\n\t\t\tend\n\t\telse\n\t\t\tlocal first = string.match(message, \"^[^%s]+\")\n\t\t\tif first then\n\t\t\t\tspeakerName = first\n\t\t\tend\n\t\tend\n\t\treturn speakerName\n\tend\n\n\tlocal function DoMuteCommand(speakerName, message, channel)\n\t\tlocal muteSpeakerName = GetSpeakerNameFromMessage(message)\n\t\tlocal speaker = ChatService:GetSpeaker(speakerName)\n\t\tif speaker then\n\t\t\tif muteSpeakerName:lower() == speakerName:lower() then\n\t\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_DoMuteCommand_CannotMuteSelf\",\"You cannot mute yourself.\"), channel, errorExtraData)\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tlocal muteSpeaker = ChatService:GetSpeaker(muteSpeakerName)\n\t\t\tif muteSpeaker then\n\t\t\t\tspeaker:AddMutedSpeaker(muteSpeaker.Name)\n\t\t\t\tspeaker:SendSystemMessage(\n\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\"GameChat_ChatMain_SpeakerHasBeenMuted\", \n\t\t\t\t\t\t\tstring.format(\"Speaker '%s' has been muted.\", muteSpeaker.Name)\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"{RBX_NAME}\",muteSpeaker.Name\n\t\t\t\t\t),\n\t\t\t\t\tchannel\n\t\t\t\t)\n\t\t\telse\n\t\t\t\tspeaker:SendSystemMessage(\n\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\"GameChat_MuteSpeaker_SpeakerDoesNotExist\", \n\t\t\t\t\t\t\tstring.format(\"Speaker '%s' does not exist.\", tostring(muteSpeakerName))\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"{RBX_NAME}\",tostring(muteSpeakerName)\n\t\t\t\t\t),\n\t\t\t\t\tchannel,\n\t\t\t\t\terrorExtraData\n\t\t\t\t)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function DoUnmuteCommand(speakerName, message, channel)\n\t\tlocal unmuteSpeakerName = GetSpeakerNameFromMessage(message)\n\t\tlocal speaker = ChatService:GetSpeaker(speakerName)\n\t\tif speaker then\n\t\t\tif unmuteSpeakerName:lower() == speakerName:lower() then\n\t\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_DoMuteCommand_CannotMuteSelf\",\"You cannot mute yourself.\"), channel, errorExtraData)\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tlocal unmuteSpeaker = ChatService:GetSpeaker(unmuteSpeakerName)\n\t\t\tif unmuteSpeaker then\n\t\t\t\tspeaker:RemoveMutedSpeaker(unmuteSpeaker.Name)\n\t\t\t\tspeaker:SendSystemMessage(\n\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\"GameChat_ChatMain_SpeakerHasBeenUnMuted\", \n\t\t\t\t\t\t\tstring.format(\"Speaker '%s' has been unmuted.\", unmuteSpeaker.Name)\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"{RBX_NAME}\",unmuteSpeaker.Name\n\t\t\t\t\t),\n\t\t\t\t\tchannel\n\t\t\t\t)\n\t\t\telse\n\t\t\t\tspeaker:SendSystemMessage(\n\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\"GameChat_MuteSpeaker_SpeakerDoesNotExist\", \n\t\t\t\t\t\t\tstring.format(\"Speaker '%s' does not exist.\", tostring(unmuteSpeakerName))\n\t\t\t\t\t\t),\n\t\t\t\t\t\t\"{RBX_NAME}\",tostring(unmuteSpeakerName)\n\t\t\t\t\t),\n\t\t\t\t\tchannel,\n\t\t\t\t\terrorExtraData\n\t\t\t\t)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function MuteCommandsFunction(fromSpeaker, message, channel)\n\t\tlocal processedCommand = false\n\n\t\tif string.sub(message, 1, 6):lower() == \"/mute \" then\n\t\t\tDoMuteCommand(fromSpeaker, string.sub(message, 7), channel)\n\t\t\tprocessedCommand = true\n\t\telseif string.sub(message, 1, 8):lower() == \"/unmute \" then\n\t\t\tDoUnmuteCommand(fromSpeaker, string.sub(message, 9), channel)\n\t\t\tprocessedCommand = true\n\t\tend\n\t\treturn processedCommand\n\tend\n\n\tChatService:RegisterProcessCommandsFunction(\"mute_commands\", MuteCommandsFunction, ChatConstants.StandardPriority)\nend\n\nreturn Run\n"
  },
  {
    "path": "src/Chat/ChatModules/PartyChat.lua",
    "content": "--\t// FileName: PartyChat.lua\n--\t// Written by: Xsitsu\n--\t// Description: Module that handles all party chat.\n\nlocal Chat = game:GetService(\"Chat\")\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatSettings = require(ReplicatedModules:WaitForChild(\"ChatSettings\"))\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network\t\t= modules.load(\"network\")\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\nlocal errorTextColor = ChatSettings.ErrorMessageTextColor or Color3.fromRGB(245, 50, 50)\nlocal errorExtraData = {ChatColor = errorTextColor}\n\nlocal function Run(ChatService)\n\n\tlocal Players = game:GetService(\"Players\")\n\n\tlocal channel = ChatService:AddChannel(\"Party\")\n\tchannel.Joinable = false\n\tchannel.Leavable = false\n\tchannel.AutoJoin = false\n\tchannel.Private = true\n\n\tlocal function PartyChatReplicationFunction(fromSpeaker, message, channelName)\n\t\tlocal speakerObj = ChatService:GetSpeaker(fromSpeaker)\n\t\tlocal channelObj = ChatService:GetChannel(channelName)\n\t\t\n\t\tif channelName == \"Party\" then\n\t\t\n\t\t\tif (speakerObj and channelObj) then\n\t\t\t\tlocal player = speakerObj:GetPlayer()\n\t\t\t\tif (player) then\n\t\t\t\t\t\n\t\t\t\t\tlocal speakerParty = network:invoke(\"getPartyDataByPlayer\", player)\n\t\t\t\t\tif speakerParty and speakerParty.members and #speakerParty.members > 0 then\n\t\n\t\t\t\t\t\tfor i, partyMember in pairs(speakerParty.members) do\n\t\t\t\t\t\t\tif partyMember and partyMember.player then\n\t\t\t\t\t\t\t\tlocal speakerName = partyMember.player.Name\n\t\t\t\t\t\t\t\tlocal otherSpeaker = ChatService:GetSpeaker(speakerName)\n\t\t\t\t\t\t\t\tif (otherSpeaker) then\n\t\t\t\t\t\t\t\t\tlocal otherPlayer = otherSpeaker:GetPlayer()\n\t\t\t\t\t\t\t\t\tif (otherPlayer) then\n\t\t\t\n\t\t\n\t\t\t\t\t\t\t\t\t\tlocal extraData = {\n\t\t\t\t\t\t\t\t\t\t\tChannelColor = Color3.fromRGB(0, 240, 244);\n\t\t\t\t\t\t\t\t\t\t\tChatColor = Color3.fromRGB(0, 240, 244)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\totherSpeaker:SendMessage(message, channelName, fromSpeaker, extraData)\n\t\t\t\t\t\t\n\t\t\t\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\n\t\t\t\tend\n\t\t\tend\n\t\n\t\t\treturn true\n\t\tend\n\tend\n\n\tchannel:RegisterProcessCommandsFunction(\"replication_function_party\", PartyChatReplicationFunction, ChatConstants.LowPriority)\n\n\tlocal function DoPartyCommand(fromSpeaker, message, channel)\n\t\tif message == nil then\n\t\t\tmessage = \"\"\n\t\tend\n\n\t\tlocal speaker = ChatService:GetSpeaker(fromSpeaker)\n\t\tif speaker then\n\t\t\tlocal player = speaker:GetPlayer()\n\n\t\t\tif player then\n\t\t\t\t\n\t\t\t\tlocal speakerParty = network:invoke(\"getPartyDataByPlayer\", player)\n\t\t\t\t\n\t\t\t\tif speakerParty == nil or speakerParty.members == nil then\n\t\t\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_PartyChat_CannotPartyChatIfNotInParty\",\"You cannot party chat if you are not on a party!\"), channel, errorExtraData)\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\tlocal channelObj = ChatService:GetChannel(\"Party\")\n\t\t\t\tif channelObj then\n\t\t\t\t\tif not speaker:IsInChannel(channelObj.Name) then\n\t\t\t\t\t\tspeaker:JoinChannel(channelObj.Name)\n\t\t\t\t\tend\n\t\t\t\t\tif message and string.len(message) > 0 then\n\t\t\t\t\t\tspeaker:SayMessage(message, channelObj.Name)\n\t\t\t\t\tend\n\t\t\t\t\tspeaker:SetMainChannel(channelObj.Name)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function PartyCommandsFunction(fromSpeaker, message, channel)\n\t\tlocal processedCommand = false\n\n\t\tif message == nil then\n\t\t\terror(\"Message is nil\")\n\t\tend\n\n\t\tif channel == \"Party\" then\n\t\t\treturn false\n\t\tend\n\n\t\tif string.sub(message, 1, 6):lower() == \"/party \" or message:lower() == \"/party\" then\n\t\t\tDoPartyCommand(fromSpeaker, string.sub(message, 7), channel)\n\t\t\tprocessedCommand = true\n\t\telseif string.sub(message, 1, 3):lower() == \"/p \" or message:lower() == \"/p\" then\n\t\t\tDoPartyCommand(fromSpeaker, string.sub(message, 4), channel)\n\t\t\tprocessedCommand = true\n\n\t\tend\n\n\t\treturn processedCommand\n\tend\n\n\tChatService:RegisterProcessCommandsFunction(\"party_commands\", PartyCommandsFunction, ChatConstants.StandardPriority)\n\n\tlocal function GetDefaultChannelNameColor()\n\t\treturn Color3.fromRGB(0, 240, 244)\n\tend\n\n\tlocal function PutSpeakerInCorrectPartyChatState(speakerObj, playerObj)\n\n\t\tspeakerObj:UpdateChannelNameColor(channel.Name, GetDefaultChannelNameColor())\n\n\t\tif not speakerObj:IsInChannel(channel.Name) then\n\t\t\tspeakerObj:JoinChannel(channel.Name)\n\t\tend\n\t\n\tend\n\n\tChatService.SpeakerAdded:connect(function(speakerName)\n\t\tlocal speakerObj = ChatService:GetSpeaker(speakerName)\n\t\tif speakerObj then\n\t\t\tlocal player = speakerObj:GetPlayer()\n\t\t\tif player then\n\t\t\t\tPutSpeakerInCorrectPartyChatState(speakerObj, player)\n\t\t\tend\n\t\tend\n\tend)\n\n\tlocal PlayerChangedConnections = {}\n\tnetwork:connect(\"playerDataLoaded\", \"Event\", function(player)\n\t\tlocal speakerObj = ChatService:GetSpeaker(player.Name)\n\t\tif speakerObj then\n\t\t\tPutSpeakerInCorrectPartyChatState(speakerObj, player)\n\t\tend\n\n\n\t\tPlayerChangedConnections[player] = nil\n\tend)\n\n\tPlayers.PlayerRemoving:connect(function(player)\n\t\tlocal changedConn = PlayerChangedConnections[player]\n\t\tif changedConn then\n\t\t\tchangedConn:Disconnect()\n\t\tend\n\t\tPlayerChangedConnections[player] = nil\n\tend)\nend\n\nreturn Run\n"
  },
  {
    "path": "src/Chat/ChatModules/PrivateMessaging.lua",
    "content": "--\t// FileName: PrivateMessaging.lua\n--\t// Written by: Xsitsu\n--\t// Description: Module that handles all private messaging.\n\nlocal Chat = game:GetService(\"Chat\")\nlocal RunService = game:GetService(\"RunService\")\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\nlocal ChatSettings = require(ReplicatedModules:WaitForChild(\"ChatSettings\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\nlocal errorTextColor = ChatSettings.ErrorMessageTextColor or Color3.fromRGB(245, 50, 50)\nlocal errorExtraData = {ChatColor = errorTextColor}\n\nfunction GetWhisperChannelPrefix()\n\tif ChatConstants.WhisperChannelPrefix then\n\t\treturn ChatConstants.WhisperChannelPrefix\n\tend\n\treturn \"To \"\nend\n\nlocal function Run(ChatService)\n\n\tlocal function CanCommunicate(fromSpeaker, toSpeaker)\n\t\tif RunService:IsStudio() then\n\t\t\treturn true\n\t\tend\n\t\tlocal fromPlayer = fromSpeaker:GetPlayer()\n\t\tlocal toPlayer = toSpeaker:GetPlayer()\n\t\tif fromPlayer and toPlayer then\n\t\t\tlocal success, canChat = pcall(function()\n\t\t\t\treturn Chat:CanUsersChatAsync(fromPlayer.UserId, toPlayer.UserId)\n\t\t\tend)\n\t\t\treturn success and canChat\n\t\tend\n\t\treturn false\n\tend\n\n\tlocal function DoWhisperCommand(fromSpeaker, message, channel)\n\t\tlocal otherSpeakerName = message\n\t\tlocal sendMessage = nil\n\n\t\tif (string.sub(message, 1, 1) == \"\\\"\") then\n\t\t\tlocal pos = string.find(message, \"\\\"\", 2)\n\t\t\tif (pos) then\n\t\t\t\totherSpeakerName = string.sub(message, 2, pos - 1)\n\t\t\t\tsendMessage = string.sub(message, pos + 2)\n\t\t\tend\n\t\telse\n\t\t\tlocal first = string.match(message, \"^[^%s]+\")\n\t\t\tif (first) then\n\t\t\t\totherSpeakerName = first\n\t\t\t\tsendMessage = string.sub(message, string.len(otherSpeakerName) + 2)\n\t\t\tend\n\t\tend\n\n\t\tlocal speaker = ChatService:GetSpeaker(fromSpeaker)\n\t\tlocal otherSpeaker = ChatService:GetSpeaker(otherSpeakerName)\n\t\tlocal channelObj = ChatService:GetChannel(GetWhisperChannelPrefix() .. otherSpeakerName)\n\t\tif channelObj and otherSpeaker then\n\t\t\tif not CanCommunicate(speaker, otherSpeaker) then\n\t\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_PrivateMessaging_CannotChat\",\"You are not able to chat with this player.\"), channel, errorExtraData)\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tif (channelObj.Name == GetWhisperChannelPrefix() .. speaker.Name) then\n\t\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_PrivateMessaging_CannotWhisperToSelf\",\"You cannot whisper to yourself.\"), channel, errorExtraData)\n\t\t\telse\n\t\t\t\tif (not speaker:IsInChannel(channelObj.Name)) then\n\t\t\t\t\tspeaker:JoinChannel(channelObj.Name)\n\t\t\t\tend\n\n\t\t\t\tif (sendMessage and (string.len(sendMessage) > 0) ) then\n\t\t\t\t\tspeaker:SayMessage(sendMessage, channelObj.Name)\n\t\t\t\tend\n\n\t\t\t\tspeaker:SetMainChannel(channelObj.Name)\n\n\t\t\tend\n\n\t\telse\n\t\t\tspeaker:SendSystemMessage(\n\t\t\t\tstring.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_MuteSpeaker_SpeakerDoesNotExist\", \n\t\t\t\t\t\tstring.format(\"Speaker '%s' does not exist.\", tostring(otherSpeakerName))\n\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\",tostring(otherSpeakerName)\n\t\t\t\t),\n\t\t\t\tchannel,\n\t\t\t\terrorExtraData\n\t\t\t)\n\t\tend\n\tend\n\n\tlocal function WhisperCommandsFunction(fromSpeaker, message, channel)\n\t\tlocal processedCommand = false\n\n\t\tif (string.sub(message, 1, 3):lower() == \"/w \") then\n\t\t\tDoWhisperCommand(fromSpeaker, string.sub(message, 4), channel)\n\t\t\tprocessedCommand = true\n\n\t\telseif (string.sub(message, 1, 9):lower() == \"/whisper \") then\n\t\t\tDoWhisperCommand(fromSpeaker, string.sub(message, 10), channel)\n\t\t\tprocessedCommand = true\n\n\t\tend\n\n\t\treturn processedCommand\n\tend\n\n\tlocal function PrivateMessageReplicationFunction(fromSpeaker, message, channelName)\n\t\tlocal sendingSpeaker = ChatService:GetSpeaker(fromSpeaker)\n\t\tlocal extraData = sendingSpeaker.ExtraData\n\t\tsendingSpeaker:SendMessage(message, channelName, fromSpeaker, extraData)\n\n\t\tlocal toSpeaker = ChatService:GetSpeaker(string.sub(channelName, 4))\n\t\tif (toSpeaker) then\n\t\t\tif (not toSpeaker:IsInChannel(GetWhisperChannelPrefix() .. fromSpeaker)) then\n\t\t\t\ttoSpeaker:JoinChannel(GetWhisperChannelPrefix() .. fromSpeaker)\n\t\t\tend\n\t\t\ttoSpeaker:SendMessage(message, GetWhisperChannelPrefix() .. fromSpeaker, fromSpeaker, extraData)\n\t\tend\n\n\t\treturn true\n\tend\n\n\tlocal function PrivateMessageAddTypeFunction(speakerName, messageObj, channelName)\n\t\tif ChatConstants.MessageTypeWhisper then\n\t\t\tmessageObj.MessageType = ChatConstants.MessageTypeWhisper\n\t\tend\n\tend\n\n\tChatService:RegisterProcessCommandsFunction(\"whisper_commands\", WhisperCommandsFunction, ChatConstants.StandardPriority)\n\n\tlocal function GetWhisperChanneNameColor()\n\t\tif ChatSettings.WhisperChannelNameColor then\n\t\t\treturn ChatSettings.WhisperChannelNameColor\n\t\tend\n\t\treturn Color3.fromRGB(102, 14, 102)\n\tend\n\n\tChatService.SpeakerAdded:connect(function(speakerName)\n\t\tif (ChatService:GetChannel(GetWhisperChannelPrefix() .. speakerName)) then\n\t\t\tChatService:RemoveChannel(GetWhisperChannelPrefix() .. speakerName)\n\t\tend\n\n\t\tlocal channel = ChatService:AddChannel(GetWhisperChannelPrefix() .. speakerName)\n\t\tchannel.Joinable = false\n\t\tchannel.Leavable = true\n\t\tchannel.AutoJoin = false\n\t\tchannel.Private = true\n\n\t\tchannel.WelcomeMessage = string.gsub(\n\t\t\tChatLocalization:Get(\n\t\t\t\t\"GameChat_PrivateMessaging_NowChattingWith\", \n\t\t\t\t\"You are now privately chatting with \" .. speakerName .. \".\"\n\t\t\t),\n\t\t\t\"{RBX_NAME}\",tostring(speakerName)\n\t\t)\n\t\tchannel.ChannelNameColor = GetWhisperChanneNameColor()\n\n\t\tchannel:RegisterProcessCommandsFunction(\"replication_function\", PrivateMessageReplicationFunction, ChatConstants.LowPriority)\n\t\tchannel:RegisterFilterMessageFunction(\"message_type_function\", PrivateMessageAddTypeFunction)\n\tend)\n\n\tChatService.SpeakerRemoved:connect(function(speakerName)\n\t\tif (ChatService:GetChannel(GetWhisperChannelPrefix() .. speakerName)) then\n\t\t\tChatService:RemoveChannel(GetWhisperChannelPrefix() .. speakerName)\n\t\tend\n\tend)\nend\n\nreturn Run\n"
  },
  {
    "path": "src/Chat/ChatModules/TeamChat.lua",
    "content": "--\t// FileName: TeamChat.lua\n--\t// Written by: Xsitsu\n--\t// Description: Module that handles all team chat.\n\nlocal Chat = game:GetService(\"Chat\")\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatSettings = require(ReplicatedModules:WaitForChild(\"ChatSettings\"))\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\nlocal errorTextColor = ChatSettings.ErrorMessageTextColor or Color3.fromRGB(245, 50, 50)\nlocal errorExtraData = {ChatColor = errorTextColor}\n\nlocal function Run(ChatService)\n\n\tlocal Players = game:GetService(\"Players\")\n\n\tlocal channel = ChatService:AddChannel(\"Team\")\n\tchannel.WelcomeMessage = ChatLocalization:Get(\"GameChat_TeamChat_WelcomeMessage\",\"This is a private channel between you and your team members.\")\n\tchannel.Joinable = false\n\tchannel.Leavable = false\n\tchannel.AutoJoin = false\n\tchannel.Private = true\n\n\tlocal function TeamChatReplicationFunction(fromSpeaker, message, channelName)\n\t\tlocal speakerObj = ChatService:GetSpeaker(fromSpeaker)\n\t\tlocal channelObj = ChatService:GetChannel(channelName)\n\t\t\n\t\tif channelName == \"Team\" then\t\n\t\t\tif (speakerObj and channelObj) then\n\t\t\t\tlocal player = speakerObj:GetPlayer()\n\t\t\t\tif (player) then\n\t\n\t\t\t\t\tfor i, speakerName in pairs(channelObj:GetSpeakerList()) do\n\t\t\t\t\t\tlocal otherSpeaker = ChatService:GetSpeaker(speakerName)\n\t\t\t\t\t\tif (otherSpeaker) then\n\t\t\t\t\t\t\tlocal otherPlayer = otherSpeaker:GetPlayer()\n\t\t\t\t\t\t\tif (otherPlayer) then\n\t\n\t\t\t\t\t\t\t\tif (player.Team == otherPlayer.Team) then\n\t\t\t\t\t\t\t\t\tlocal extraData = {\n\t\t\t\t\t\t\t\t\t\tNameColor = player.TeamColor.Color,\n\t\t\t\t\t\t\t\t\t\tChannelColor = player.TeamColor.Color\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\totherSpeaker:SendMessage(message, channelName, fromSpeaker, extraData)\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t--// Could use this line to obfuscate message for cool effects\n\t\t\t\t\t\t\t\t\t--otherSpeaker:SendMessage(message, channelName, fromSpeaker)\n\t\t\t\t\t\t\t\tend\n\t\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\n\t\t\t\tend\n\t\t\tend\n\t\n\t\t\treturn true\n\t\tend\n\tend\n\n\tchannel:RegisterProcessCommandsFunction(\"replication_function\", TeamChatReplicationFunction, ChatConstants.LowPriority)\n\n\tlocal function DoTeamCommand(fromSpeaker, message, channel)\n\t\tif message == nil then\n\t\t\tmessage = \"\"\n\t\tend\n\n\t\tlocal speaker = ChatService:GetSpeaker(fromSpeaker)\n\t\tif speaker then\n\t\t\tlocal player = speaker:GetPlayer()\n\n\t\t\tif player then\n\t\t\t\tif player.Team == nil then\n\t\t\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_TeamChat_CannotTeamChatIfNotInTeam\",\"You cannot team chat if you are not on a team!\"), channel, errorExtraData)\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\tlocal channelObj = ChatService:GetChannel(\"Team\")\n\t\t\t\tif channelObj then\n\t\t\t\t\tif not speaker:IsInChannel(channelObj.Name) then\n\t\t\t\t\t\tspeaker:JoinChannel(channelObj.Name)\n\t\t\t\t\tend\n\t\t\t\t\tif message and string.len(message) > 0 then\n\t\t\t\t\t\tspeaker:SayMessage(message, channelObj.Name)\n\t\t\t\t\tend\n\t\t\t\t\tspeaker:SetMainChannel(channelObj.Name)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function TeamCommandsFunction(fromSpeaker, message, channel)\n\t\tlocal processedCommand = false\n\n\t\tif message == nil then\n\t\t\terror(\"Message is nil\")\n\t\tend\n\n\t\tif channel == \"Team\" then\n\t\t\treturn false\n\t\tend\n\n\t\tif string.sub(message, 1, 6):lower() == \"/team \" or message:lower() == \"/team\" then\n\t\t\tDoTeamCommand(fromSpeaker, string.sub(message, 7), channel)\n\t\t\tprocessedCommand = true\n\t\telseif string.sub(message, 1, 3):lower() == \"/t \" or message:lower() == \"/t\" then\n\t\t\tDoTeamCommand(fromSpeaker, string.sub(message, 4), channel)\n\t\t\tprocessedCommand = true\n--[[\telseif string.sub(message, 1, 2):lower() == \"% \" or message:lower() == \"%\" then\n\t\t\tDoTeamCommand(fromSpeaker, string.sub(message, 3), channel)\n\t\t\tprocessedCommand = true ]]\n\t\tend\n\n\t\treturn processedCommand\n\tend\n\n\tChatService:RegisterProcessCommandsFunction(\"team_commands\", TeamCommandsFunction, ChatConstants.StandardPriority)\n\n\tlocal function GetDefaultChannelNameColor()\n\t\tif ChatSettings.DefaultChannelNameColor then\n\t\t\treturn ChatSettings.DefaultChannelNameColor\n\t\tend\n\t\treturn Color3.fromRGB(35, 76, 142)\n\tend\n\n\tlocal function PutSpeakerInCorrectTeamChatState(speakerObj, playerObj)\n\t\tif playerObj.Neutral or playerObj.Team == nil then\n\t\t\tspeakerObj:UpdateChannelNameColor(channel.Name, GetDefaultChannelNameColor())\n\n\t\t\tif speakerObj:IsInChannel(channel.Name) then\n\t\t\t\tspeakerObj:LeaveChannel(channel.Name)\n\t\t\tend\n\t\telseif not playerObj.Neutral and playerObj.Team then\n\t\t\tspeakerObj:UpdateChannelNameColor(channel.Name, playerObj.Team.TeamColor.Color)\n\n\t\t\tif not speakerObj:IsInChannel(channel.Name) then\n\t\t\t\tspeakerObj:JoinChannel(channel.Name)\n\t\t\tend\n\t\tend\n\tend\n\n\tChatService.SpeakerAdded:connect(function(speakerName)\n\t\tlocal speakerObj = ChatService:GetSpeaker(speakerName)\n\t\tif speakerObj then\n\t\t\tlocal player = speakerObj:GetPlayer()\n\t\t\tif player then\n\t\t\t\tPutSpeakerInCorrectTeamChatState(speakerObj, player)\n\t\t\tend\n\t\tend\n\tend)\n\n\tlocal PlayerChangedConnections = {}\n\tPlayers.PlayerAdded:connect(function(player)\n\t\tlocal changedConn = player.Changed:connect(function(property)\n\t\t\tlocal speakerObj = ChatService:GetSpeaker(player.Name)\n\t\t\tif speakerObj then\n\t\t\t\tif property == \"Neutral\" then\n\t\t\t\t\tPutSpeakerInCorrectTeamChatState(speakerObj, player)\n\t\t\t\telseif property == \"Team\" then\n\t\t\t\t\tPutSpeakerInCorrectTeamChatState(speakerObj, player)\n\t\t\t\t\tif speakerObj:IsInChannel(channel.Name) then\n\t\t\t\t\t\tspeakerObj:SendSystemMessage(\n\t\t\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\t\t\"GameChat_TeamChat_NowInTeam\", \n\t\t\t\t\t\t\t\t\tstring.format(\"You are now on the '%s' team.\", player.Team.Name)\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\"{RBX_NAME}\",player.Team.Name\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tchannel.Name\n\t\t\t\t\t\t)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\t\tPlayerChangedConnections[player] = changedConn\n\tend)\n\n\tPlayers.PlayerRemoving:connect(function(player)\n\t\tlocal changedConn = PlayerChangedConnections[player]\n\t\tif changedConn then\n\t\t\tchangedConn:Disconnect()\n\t\tend\n\t\tPlayerChangedConnections[player] = nil\n\tend)\nend\n\nreturn Run\n"
  },
  {
    "path": "src/Chat/ChatModules/Toggle.lua",
    "content": "--[[--\t// FileName: Toggle.lua\n--\t// Written by: Nicholas_Foreman\n--\t// Description: Allows for toggling chat tags/chat color.\n\nlocal Chat = game:GetService(\"Chat\")\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatSettings = require(ReplicatedModules:WaitForChild(\"ChatSettings\"))\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = { Get = function(key,default) return default end } end\n\nlocal cache = {}\n\nlocal function Run(ChatService)\n\tlocal function ProcessCommandsFunction(fromSpeaker, message, channel)\n\t\tlocal speaker = ChatService:GetSpeaker(fromSpeaker)\n\t\tif string.sub(message, 1, 7) == \"/toggle\" then\n\t\t\tif not cache[fromSpeaker] then\n\t\t\t\tcache[fromSpeaker] = {\n\t\t\t\t\tActive = true,\n\t\t\t\t\tTags = speaker:GetExtraData(\"Tags\"),\n\t\t\t\t\tColor = speaker:GetExtraData(\"ChatColor\")\n\t\t\t\t}\n\t\t\tend\n\t\t\tif message:lower() == \"/toggle\" then\n\t\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"ToggleSuccess\",\"/toggle <tags/color> : toggles chat tags or chat color.\"), channel)\n\t\t\t\treturn true\n\t\t\telseif message:lower() == \"/toggle tags\" then\n\t\t\t\tif cache[fromSpeaker][\"Active\"] then\n\t\t\t\t\t\n\t\t\t\tend\n\t\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"ToggleTagsSuccess\",\"Successfully toggled chat tags.\"), channel)\n\t\t\t\treturn true\n\t\t\telseif message:lower() == \"/toggle color\" then\n\t\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"ToggleChatSuccess\",\"Successfully toggled chat tags.\"), channel)\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\t\treturn false\n\tend\n\n\tChatService:RegisterProcessCommandsFunction(\"chat_toggler\", ProcessCommandsFunction, ChatConstants.StandardPriority)\nend\n\nreturn Run]]\n\t\nlocal function Run(ChatService)\n\t\nend\nreturn Run;"
  },
  {
    "path": "src/Chat/ChatModules/init.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/Chat/ChatScript/ChatMain/ChannelsBar.lua",
    "content": "--\t// FileName: ChannelsBar.lua\n--\t// Written by: Xsitsu\n--\t// Description: Manages creating, destroying, and displaying ChannelTabs.\n\nlocal module = {}\n\nlocal PlayerGui = game:GetService(\"Players\").LocalPlayer:WaitForChild(\"PlayerGui\")\n\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal modulesFolder = script.Parent\nlocal moduleChannelsTab = require(modulesFolder:WaitForChild(\"ChannelsTab\"))\nlocal MessageSender = require(modulesFolder:WaitForChild(\"MessageSender\"))\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal CurveUtil = require(modulesFolder:WaitForChild(\"CurveUtil\"))\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:CreateGuiObjects(targetParent)\n\tlocal BaseFrame = Instance.new(\"Frame\")\n\tBaseFrame.Selectable = false\n\tBaseFrame.Size = UDim2.new(1, 0, 1, 0)\n\tBaseFrame.BackgroundTransparency = 1\n\tBaseFrame.Parent = targetParent\n\n\tlocal ScrollingBase = Instance.new(\"Frame\")\n\tScrollingBase.Selectable = false\n\tScrollingBase.Name = \"ScrollingBase\"\n\tScrollingBase.BackgroundTransparency = 1\n\tScrollingBase.ClipsDescendants = true\n\tScrollingBase.Size = UDim2.new(1, 0, 1, 0)\n\tScrollingBase.Position = UDim2.new(0, 0, 0, 0)\n\tScrollingBase.Parent = BaseFrame\n\n\tlocal ScrollerSizer = Instance.new(\"Frame\")\n\tScrollerSizer.Selectable = false\n\tScrollerSizer.Name = \"ScrollerSizer\"\n\tScrollerSizer.BackgroundTransparency = 1\n\tScrollerSizer.Size = UDim2.new(1, 0, 1, 0)\n\tScrollerSizer.Position = UDim2.new(0, 0, 0, 0)\n\tScrollerSizer.Parent = ScrollingBase\n\n\tlocal ScrollerFrame = Instance.new(\"Frame\")\n\tScrollerFrame.Selectable = false\n\tScrollerFrame.Name = \"ScrollerFrame\"\n\tScrollerFrame.BackgroundTransparency = 1\n\tScrollerFrame.Size = UDim2.new(1, 0, 1, 0)\n\tScrollerFrame.Position = UDim2.new(0, 0, 0, 0)\n\tScrollerFrame.Parent = ScrollerSizer\n\n\tlocal LeaveConfirmationFrameBase = Instance.new(\"Frame\")\n\tLeaveConfirmationFrameBase.Selectable = false\n\tLeaveConfirmationFrameBase.Size = UDim2.new(1, 0, 1, 0)\n\tLeaveConfirmationFrameBase.Position = UDim2.new(0, 0, 0, 0)\n\tLeaveConfirmationFrameBase.ClipsDescendants = true\n\tLeaveConfirmationFrameBase.BackgroundTransparency = 1\n\tLeaveConfirmationFrameBase.Parent = BaseFrame\n\n\tlocal LeaveConfirmationFrame = Instance.new(\"Frame\")\n\tLeaveConfirmationFrame.Selectable = false\n\tLeaveConfirmationFrame.Name = \"LeaveConfirmationFrame\"\n\tLeaveConfirmationFrame.Size = UDim2.new(1, 0, 1, 0)\n\tLeaveConfirmationFrame.Position = UDim2.new(0, 0, 1, 0)\n\tLeaveConfirmationFrame.BackgroundTransparency = 0.6\n\tLeaveConfirmationFrame.BorderSizePixel = 0\n\tLeaveConfirmationFrame.BackgroundColor3 = Color3.new(0, 0, 0)\n\tLeaveConfirmationFrame.Parent = LeaveConfirmationFrameBase\n\n\tlocal InputBlocker = Instance.new(\"TextButton\")\n\tInputBlocker.Selectable = false\n\tInputBlocker.Size = UDim2.new(1, 0, 1, 0)\n\tInputBlocker.BackgroundTransparency = 1\n\tInputBlocker.Text = \"\"\n\tInputBlocker.Parent = LeaveConfirmationFrame\n\n\tlocal LeaveConfirmationButtonYes = Instance.new(\"TextButton\")\n\tLeaveConfirmationButtonYes.Selectable = false\n\tLeaveConfirmationButtonYes.Size = UDim2.new(0.25, 0, 1, 0)\n\tLeaveConfirmationButtonYes.BackgroundTransparency = 1\n\tLeaveConfirmationButtonYes.Font = ChatSettings.DefaultFont\n\tLeaveConfirmationButtonYes.TextSize = 18\n\tLeaveConfirmationButtonYes.TextStrokeTransparency = 0.75\n\tLeaveConfirmationButtonYes.Position = UDim2.new(0, 0, 0, 0)\n\tLeaveConfirmationButtonYes.TextColor3 = Color3.new(0, 1, 0)\n\tLeaveConfirmationButtonYes.Text = \"Confirm\"\n\tLeaveConfirmationButtonYes.Parent = LeaveConfirmationFrame\n\n\tlocal LeaveConfirmationButtonNo = LeaveConfirmationButtonYes:Clone()\n\tLeaveConfirmationButtonNo.Parent = LeaveConfirmationFrame\n\tLeaveConfirmationButtonNo.Position = UDim2.new(0.75, 0, 0, 0)\n\tLeaveConfirmationButtonNo.TextColor3 = Color3.new(1, 0, 0)\n\tLeaveConfirmationButtonNo.Text = \"Cancel\"\n\n\tlocal LeaveConfirmationNotice = Instance.new(\"TextLabel\")\n\tLeaveConfirmationNotice.Selectable = false\n\tLeaveConfirmationNotice.Size = UDim2.new(0.5, 0, 1, 0)\n\tLeaveConfirmationNotice.Position = UDim2.new(0.25, 0, 0, 0)\n\tLeaveConfirmationNotice.BackgroundTransparency = 1\n\tLeaveConfirmationNotice.TextColor3 = Color3.new(1, 1, 1)\n\tLeaveConfirmationNotice.TextStrokeTransparency = 0.75\n\tLeaveConfirmationNotice.Text = \"Leave channel <XX>?\"\n\tLeaveConfirmationNotice.Font = ChatSettings.DefaultFont\n\tLeaveConfirmationNotice.TextSize = 18\n\tLeaveConfirmationNotice.Parent = LeaveConfirmationFrame\n\n\tlocal LeaveTarget = Instance.new(\"StringValue\")\n\tLeaveTarget.Name = \"LeaveTarget\"\n\tLeaveTarget.Parent = LeaveConfirmationFrame\n\n\tlocal outPos = LeaveConfirmationFrame.Position\n\tLeaveConfirmationButtonYes.MouseButton1Click:connect(function()\n\t\tMessageSender:SendMessage(string.format(\"/leave %s\", LeaveTarget.Value), nil)\n\t\tLeaveConfirmationFrame:TweenPosition(outPos, Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.2, true)\n\tend)\n\tLeaveConfirmationButtonNo.MouseButton1Click:connect(function()\n\t\tLeaveConfirmationFrame:TweenPosition(outPos, Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.2, true)\n\tend)\n\n\n\n\tlocal scale = 0.7\n\tlocal scaleOther = (1 - scale) / 2\n\tlocal pageButtonImage = \"rbxasset://textures/ui/Chat/TabArrowBackground.png\"\n\tlocal pageButtonArrowImage = \"rbxasset://textures/ui/Chat/TabArrow.png\"\n\n\t--// ToDo: Remove these lines when the assets are put into trunk.\n\t--// These grab unchanging versions hosted on the site, and not from the content folder.\n\tpageButtonImage = \"rbxassetid://471630199\"\n\tpageButtonArrowImage = \"rbxassetid://471630112\"\n\n\n\tlocal PageLeftButton = Instance.new(\"ImageButton\", BaseFrame)\n\tPageLeftButton.Selectable = ChatSettings.GamepadNavigationEnabled\n\tPageLeftButton.Name = \"PageLeftButton\"\n\tPageLeftButton.SizeConstraint = Enum.SizeConstraint.RelativeYY\n\tPageLeftButton.Size = UDim2.new(scale, 0, scale, 0)\n\tPageLeftButton.BackgroundTransparency = 1\n\tPageLeftButton.Position = UDim2.new(0, 4, scaleOther, 0)\n\tPageLeftButton.Visible = false\n\tPageLeftButton.Image = pageButtonImage\n\tlocal ArrowLabel = Instance.new(\"ImageLabel\", PageLeftButton)\n\tArrowLabel.Name = \"ArrowLabel\"\n\tArrowLabel.BackgroundTransparency = 1\n\tArrowLabel.Size = UDim2.new(0.4, 0, 0.4, 0)\n\tArrowLabel.Image = pageButtonArrowImage\n\n\tlocal PageRightButtonPositionalHelper = Instance.new(\"Frame\", BaseFrame)\n\tPageRightButtonPositionalHelper.Selectable = false\n\tPageRightButtonPositionalHelper.BackgroundTransparency = 1\n\tPageRightButtonPositionalHelper.Name = \"PositionalHelper\"\n\tPageRightButtonPositionalHelper.Size = PageLeftButton.Size\n\tPageRightButtonPositionalHelper.SizeConstraint = PageLeftButton.SizeConstraint\n\tPageRightButtonPositionalHelper.Position = UDim2.new(1, 0, scaleOther, 0)\n\n\tlocal PageRightButton = PageLeftButton:Clone()\n\tPageRightButton.Parent = PageRightButtonPositionalHelper\n\tPageRightButton.Name = \"PageRightButton\"\n\tPageRightButton.Size = UDim2.new(1, 0, 1, 0)\n\tPageRightButton.SizeConstraint = Enum.SizeConstraint.RelativeXY\n\tPageRightButton.Position = UDim2.new(-1, -4, 0, 0)\n\n\tlocal positionOffset = UDim2.new(0.05, 0, 0, 0)\n\n\tPageRightButton.ArrowLabel.Position = UDim2.new(0.3, 0, 0.3, 0) + positionOffset\n\tPageLeftButton.ArrowLabel.Position = UDim2.new(0.3, 0, 0.3, 0) - positionOffset\n\tPageLeftButton.ArrowLabel.Rotation = 180\n\n\n\tself.GuiObject = BaseFrame\n\n\tself.GuiObjects.BaseFrame = BaseFrame\n\tself.GuiObjects.ScrollerSizer = ScrollerSizer\n\tself.GuiObjects.ScrollerFrame = ScrollerFrame\n\tself.GuiObjects.PageLeftButton = PageLeftButton\n\tself.GuiObjects.PageRightButton = PageRightButton\n\tself.GuiObjects.LeaveConfirmationFrame = LeaveConfirmationFrame\n\tself.GuiObjects.LeaveConfirmationNotice = LeaveConfirmationNotice\n\n\tself.GuiObjects.PageLeftButtonArrow = PageLeftButton.ArrowLabel\n\tself.GuiObjects.PageRightButtonArrow = PageRightButton.ArrowLabel\n\tself:AnimGuiObjects()\n\n\tPageLeftButton.MouseButton1Click:connect(function() self:ScrollChannelsFrame(-1) end)\n\tPageRightButton.MouseButton1Click:connect(function() self:ScrollChannelsFrame(1) end)\n\n\tself:ScrollChannelsFrame(0)\nend\n\n\nfunction methods:UpdateMessagePostedInChannel(channelName)\n\tlocal tab = self:GetChannelTab(channelName)\n\tif (tab) then\n\t\ttab:UpdateMessagePostedInChannel()\n\telse\n\t\twarn(\"ChannelsTab '\" .. channelName .. \"' does not exist!\")\n\tend\nend\n\nfunction methods:AddChannelTab(channelName)\n\tif (self:GetChannelTab(channelName)) then\n\t\terror(\"Channel tab '\" .. channelName .. \"'already exists!\")\n\tend\n\n\tlocal tab = moduleChannelsTab.new(channelName)\n\ttab.GuiObject.Parent = self.GuiObjects.ScrollerFrame\n\tself.ChannelTabs[channelName:lower()] = tab\n\n\tself.NumTabs = self.NumTabs + 1\n\tself:OrganizeChannelTabs()\n\n\tif (ChatSettings.RightClickToLeaveChannelEnabled) then\n\t\ttab.NameTag.MouseButton2Click:connect(function()\n\t\t\tself.LeaveConfirmationNotice.Text = string.format(\"Leave channel %s?\", tab.ChannelName)\n\t\t\tself.LeaveConfirmationFrame.LeaveTarget.Value = tab.ChannelName\n\t\t\tself.LeaveConfirmationFrame:TweenPosition(UDim2.new(0, 0, 0, 0), Enum.EasingDirection.In, Enum.EasingStyle.Quad, 0.2, true)\n\t\tend)\n\tend\n\n\treturn tab\nend\n\nfunction methods:RemoveChannelTab(channelName)\n\tif (not self:GetChannelTab(channelName)) then\n\t\terror(\"Channel tab '\" .. channelName .. \"'does not exist!\")\n\tend\n\n\tlocal indexName = channelName:lower()\n\tself.ChannelTabs[indexName]:Destroy()\n\tself.ChannelTabs[indexName] = nil\n\n\tself.NumTabs = self.NumTabs - 1\n\tself:OrganizeChannelTabs()\nend\n\nfunction methods:GetChannelTab(channelName)\n\treturn self.ChannelTabs[channelName:lower()]\nend\n\nfunction methods:OrganizeChannelTabs()\n\tlocal order = {}\n\n\ttable.insert(order, self:GetChannelTab(ChatSettings.GeneralChannelName))\n\ttable.insert(order, self:GetChannelTab(\"System\"))\n\n\tfor tabIndexName, tab in pairs(self.ChannelTabs) do\n\t\tif (tab.ChannelName ~= ChatSettings.GeneralChannelName and tab.ChannelName ~= \"System\") then\n\t\t\ttable.insert(order, tab)\n\t\tend\n\tend\n\n\tfor index, tab in pairs(order) do\n\t\ttab.GuiObject.Position = UDim2.new(index - 1, 0, 0, 0)\n\tend\n\n\t--// Dynamic tab resizing\n\tself.GuiObjects.ScrollerSizer.Size = UDim2.new(1 / math.max(1, math.min(ChatSettings.ChannelsBarFullTabSize, self.NumTabs)), 0, 1, 0)\n\n\tself:ScrollChannelsFrame(0)\nend\n\nfunction methods:ResizeChannelTabText(textSize)\n\tfor i, tab in pairs(self.ChannelTabs) do\n\t\ttab:SetTextSize(textSize)\n\tend\nend\n\nfunction methods:ScrollChannelsFrame(dir)\n\tif (self.ScrollChannelsFrameLock) then return end\n\tself.ScrollChannelsFrameLock = true\n\n\tlocal tabNumber = ChatSettings.ChannelsBarFullTabSize\n\n\tlocal newPageNum = self.CurPageNum + dir\n\tif (newPageNum < 0) then\n\t\tnewPageNum = 0\n\telseif (newPageNum > 0 and newPageNum + tabNumber > self.NumTabs) then\n\t\tnewPageNum = self.NumTabs - tabNumber\n\tend\n\n\tself.CurPageNum = newPageNum\n\n\tlocal tweenTime = 0.15\n\tlocal endPos = UDim2.new(-self.CurPageNum, 0, 0, 0)\n\n\tself.GuiObjects.PageLeftButton.Visible = (self.CurPageNum > 0)\n\tself.GuiObjects.PageRightButton.Visible = (self.CurPageNum + tabNumber < self.NumTabs)\n\n\tif dir == 0 then\n\t\tself.ScrollChannelsFrameLock = false\n\t\treturn\n\tend\n\n\tlocal function UnlockFunc()\n\t\tself.ScrollChannelsFrameLock = false\n\tend\n\n\tself:WaitUntilParentedCorrectly()\n\n\tself.GuiObjects.ScrollerFrame:TweenPosition(endPos, Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, tweenTime, true, UnlockFunc)\nend\n\nfunction methods:FadeOutBackground(duration)\n\tfor channelName, channelObj in pairs(self.ChannelTabs) do\n\t\tchannelObj:FadeOutBackground(duration)\n\tend\n\n\tself.AnimParams.Background_TargetTransparency = 1\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeInBackground(duration)\n\tfor channelName, channelObj in pairs(self.ChannelTabs) do\n\t\tchannelObj:FadeInBackground(duration)\n\tend\n\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeOutText(duration)\n\tfor channelName, channelObj in pairs(self.ChannelTabs) do\n\t\tchannelObj:FadeOutText(duration)\n\tend\nend\n\nfunction methods:FadeInText(duration)\n\tfor channelName, channelObj in pairs(self.ChannelTabs) do\n\t\tchannelObj:FadeInText(duration)\n\tend\nend\n\nfunction methods:AnimGuiObjects()\n\tself.GuiObjects.PageLeftButton.ImageTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.GuiObjects.PageRightButton.ImageTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.GuiObjects.PageLeftButtonArrow.ImageTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.GuiObjects.PageRightButtonArrow.ImageTransparency = self.AnimParams.Background_CurrentTransparency\nend\n\nfunction methods:InitializeAnimParams()\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_CurrentTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(0)\nend\n\nfunction methods:Update(dtScale)\n\tfor channelName, channelObj in pairs(self.ChannelTabs) do\n\t\tchannelObj:Update(dtScale)\n\tend\n\n\tself.AnimParams.Background_CurrentTransparency = CurveUtil:Expt(\n\t\t\tself.AnimParams.Background_CurrentTransparency,\n\t\t\tself.AnimParams.Background_TargetTransparency,\n\t\t\tself.AnimParams.Background_NormalizedExptValue,\n\t\t\tdtScale\n\t)\n\n\tself:AnimGuiObjects()\nend\n\n--// ToDo: Move to common modules\nfunction methods:WaitUntilParentedCorrectly()\n\twhile (not self.GuiObject:IsDescendantOf(game:GetService(\"Players\").LocalPlayer)) do\n\t\tself.GuiObject.AncestryChanged:wait()\n\tend\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.GuiObject = nil\n\tobj.GuiObjects = {}\n\n\tobj.ChannelTabs = {}\n\tobj.NumTabs = 0\n\tobj.CurPageNum = 0\n\n\tobj.ScrollChannelsFrameLock = false\n\n\tobj.AnimParams = {}\n\n\tobj:InitializeAnimParams()\n\n\tChatSettings.SettingsChanged:connect(function(setting, value)\n\t\tif (setting == \"ChatChannelsTabTextSize\") then\n\t\t\tobj:ResizeChannelTabText(value)\n\t\tend\n\tend)\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/Chat/ChatScript/ChatMain/ChannelsTab.lua",
    "content": "--\t// FileName: ChannelsTab.lua\n--\t// Written by: Xsitsu\n--\t// Description: Channel tab button for selecting current channel and also displaying if currently selected.\n\nlocal module = {}\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal modulesFolder = script.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal CurveUtil = require(modulesFolder:WaitForChild(\"CurveUtil\"))\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nlocal function CreateGuiObjects()\n\tlocal BaseFrame = Instance.new(\"Frame\")\n\tBaseFrame.Selectable = false\n\tBaseFrame.Size = UDim2.new(1, 0, 1, 0)\n\tBaseFrame.BackgroundTransparency = 1\n\n\tlocal gapOffsetX = 1\n\tlocal gapOffsetY = 1\n\n\tlocal BackgroundFrame = Instance.new(\"Frame\")\n\tBackgroundFrame.Selectable = false\n\tBackgroundFrame.Name = \"BackgroundFrame\"\n\tBackgroundFrame.Size = UDim2.new(1, -gapOffsetX * 2, 1, -gapOffsetY * 2)\n\tBackgroundFrame.Position = UDim2.new(0, gapOffsetX, 0, gapOffsetY)\n\tBackgroundFrame.BackgroundTransparency = 1\n\tBackgroundFrame.Parent = BaseFrame\n\n\tlocal UnselectedFrame = Instance.new(\"Frame\")\n\tUnselectedFrame.Selectable = false\n\tUnselectedFrame.Name = \"UnselectedFrame\"\n\tUnselectedFrame.Size = UDim2.new(1, 0, 1, 0)\n\tUnselectedFrame.Position = UDim2.new(0, 0, 0, 0)\n\tUnselectedFrame.BorderSizePixel = 0\n\tUnselectedFrame.BackgroundColor3 = ChatSettings.ChannelsTabUnselectedColor\n\tUnselectedFrame.BackgroundTransparency = 0.6\n\tUnselectedFrame.Parent = BackgroundFrame\n\n\tlocal SelectedFrame = Instance.new(\"Frame\")\n\tSelectedFrame.Selectable = false\n\tSelectedFrame.Name = \"SelectedFrame\"\n\tSelectedFrame.Size = UDim2.new(1, 0, 1, 0)\n\tSelectedFrame.Position = UDim2.new(0, 0, 0, 0)\n\tSelectedFrame.BorderSizePixel = 0\n\tSelectedFrame.BackgroundColor3 = ChatSettings.ChannelsTabSelectedColor\n\tSelectedFrame.BackgroundTransparency = 1\n\tSelectedFrame.Parent = BackgroundFrame\n\n\tlocal SelectedFrameBackgroundImage = Instance.new(\"ImageLabel\")\n\tSelectedFrameBackgroundImage.Selectable = false\n\tSelectedFrameBackgroundImage.Name = \"BackgroundImage\"\n\tSelectedFrameBackgroundImage.BackgroundTransparency = 1\n\tSelectedFrameBackgroundImage.BorderSizePixel = 0\n\tSelectedFrameBackgroundImage.Size = UDim2.new(1, 0, 1, 0)\n\tSelectedFrameBackgroundImage.Position = UDim2.new(0, 0, 0, 0)\n\tSelectedFrameBackgroundImage.ScaleType = Enum.ScaleType.Slice\n\tSelectedFrameBackgroundImage.Parent = SelectedFrame\n\n\tSelectedFrameBackgroundImage.BackgroundTransparency = 0.6 - 1\n\tlocal rate = 1.2 * 1\n\tSelectedFrameBackgroundImage.BackgroundColor3 = Color3.fromRGB(78 * rate, 84 * rate, 96 * rate)\n\n\tlocal borderXOffset = 2\n\tlocal blueBarYSize = 4\n\tlocal BlueBarLeft = Instance.new(\"ImageLabel\")\n\tBlueBarLeft.Selectable = false\n\tBlueBarLeft.Size = UDim2.new(0.5, -borderXOffset, 0, blueBarYSize)\n\tBlueBarLeft.BackgroundTransparency = 1\n\tBlueBarLeft.ScaleType = Enum.ScaleType.Slice\n\tBlueBarLeft.SliceCenter = Rect.new(3,3,32,21)\n\tBlueBarLeft.Parent = SelectedFrame\n\n\tlocal BlueBarRight = BlueBarLeft:Clone()\n\tBlueBarRight.Parent = SelectedFrame\n\n\tBlueBarLeft.Position = UDim2.new(0, borderXOffset, 1, -blueBarYSize)\n\tBlueBarRight.Position = UDim2.new(0.5, 0, 1, -blueBarYSize)\n\tBlueBarLeft.Image = \"rbxasset://textures/ui/Settings/Slider/SelectedBarLeft.png\"\n\tBlueBarRight.Image = \"rbxasset://textures/ui/Settings/Slider/SelectedBarRight.png\"\n\n\tBlueBarLeft.Name = \"BlueBarLeft\"\n\tBlueBarRight.Name = \"BlueBarRight\"\n\n\tlocal NameTag = Instance.new(\"TextButton\")\n\tNameTag.Selectable = ChatSettings.GamepadNavigationEnabled\n\tNameTag.Size = UDim2.new(1, 0, 1, 0)\n\tNameTag.Position = UDim2.new(0, 0, 0, 0)\n\tNameTag.BackgroundTransparency = 1\n\tNameTag.Font = ChatSettings.DefaultFont\n\tNameTag.TextSize = ChatSettings.ChatChannelsTabTextSize\n\tNameTag.TextColor3 = Color3.new(1, 1, 1)\n\tNameTag.TextStrokeTransparency = 0.75\n\tNameTag.Parent = BackgroundFrame\n\n\tlocal NameTagNonSelect = NameTag:Clone()\n\tlocal NameTagSelect = NameTag:Clone()\n\tNameTagNonSelect.Parent = UnselectedFrame\n\tNameTagSelect.Parent = SelectedFrame\n\tNameTagNonSelect.Font = Enum.Font.SourceSans\n\tNameTagNonSelect.Active = false\n\tNameTagSelect.Active = false\n\n\tlocal NewMessageIconFrame = Instance.new(\"Frame\")\n\tNewMessageIconFrame.Selectable = false\n\tNewMessageIconFrame.Size = UDim2.new(0, 18, 0, 18)\n\tNewMessageIconFrame.Position = UDim2.new(0.8, -9, 0.5, -9)\n\tNewMessageIconFrame.BackgroundTransparency = 1\n\tNewMessageIconFrame.Parent = BackgroundFrame\n\n\tlocal NewMessageIcon = Instance.new(\"ImageLabel\")\n\tNewMessageIcon.Selectable = false\n\tNewMessageIcon.Size = UDim2.new(1, 0, 1, 0)\n\tNewMessageIcon.BackgroundTransparency = 1\n\tNewMessageIcon.Image = \"rbxasset://textures/ui/Chat/MessageCounter.png\"\n\tNewMessageIcon.Visible = false\n\tNewMessageIcon.Parent = NewMessageIconFrame\n\n\tlocal NewMessageIconText = Instance.new(\"TextLabel\")\n\tNewMessageIconText.Selectable = false\n\tNewMessageIconText.BackgroundTransparency = 1\n\tNewMessageIconText.Size = UDim2.new(0, 13, 0, 9)\n\tNewMessageIconText.Position = UDim2.new(0.5, -7, 0.5, -7)\n\tNewMessageIconText.Font = ChatSettings.DefaultFont\n\tNewMessageIconText.TextSize = 14\n\tNewMessageIconText.TextColor3 = Color3.new(1, 1, 1)\n\tNewMessageIconText.Text = \"\"\n\tNewMessageIconText.Parent = NewMessageIcon\n\n\treturn BaseFrame, NameTag, NameTagNonSelect, NameTagSelect, NewMessageIcon, UnselectedFrame, SelectedFrame\nend\n\nfunction methods:Destroy()\n\tself.GuiObject:Destroy()\nend\n\nfunction methods:UpdateMessagePostedInChannel(ignoreActive)\n\tif (self.Active and (ignoreActive ~= true)) then return end\n\n\tlocal count = self.UnreadMessageCount + 1\n\tself.UnreadMessageCount = count\n\n\tlocal label = self.NewMessageIcon\n\tlabel.Visible = true\n\tlabel.TextLabel.Text = (count < 100) and tostring(count) or \"!\"\n\n\tlocal tweenTime = 0.15\n\tlocal tweenPosOffset = UDim2.new(0, 0, -0.1, 0)\n\n\tlocal curPos = label.Position\n\tlocal outPos = curPos + tweenPosOffset\n\tlocal easingDirection = Enum.EasingDirection.Out\n\tlocal easingStyle = Enum.EasingStyle.Quad\n\n\tlabel.Position = UDim2.new(0, 0, -0.15, 0)\n\tlabel:TweenPosition(UDim2.new(0, 0, 0, 0), easingDirection, easingStyle, tweenTime, true)\n\nend\n\nfunction methods:SetActive(active)\n\tself.Active = active\n\tself.UnselectedFrame.Visible = not active\n\tself.SelectedFrame.Visible = active\n\n\tif (active) then\n\t\tself.UnreadMessageCount = 0\n\t\tself.NewMessageIcon.Visible = false\n\n\t\tself.NameTag.Font = Enum.Font.SourceSansBold\n\telse\n\t\tself.NameTag.Font = Enum.Font.SourceSans\n\n\tend\nend\n\nfunction methods:SetTextSize(textSize)\n\tself.NameTag.TextSize = textSize\nend\n\nfunction methods:FadeOutBackground(duration)\n\tself.AnimParams.Background_TargetTransparency = 1\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeInBackground(duration)\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeOutText(duration)\n\tself.AnimParams.Text_TargetTransparency = 1\n\tself.AnimParams.Text_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\n\tself.AnimParams.TextStroke_TargetTransparency = 1\n\tself.AnimParams.TextStroke_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeInText(duration)\n\tself.AnimParams.Text_TargetTransparency = 0\n\tself.AnimParams.Text_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\n\tself.AnimParams.TextStroke_TargetTransparency = 0.75\n\tself.AnimParams.TextStroke_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:AnimGuiObjects()\n\tself.UnselectedFrame.BackgroundTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.SelectedFrame.BackgroundImage.BackgroundTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.SelectedFrame.BlueBarLeft.ImageTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.SelectedFrame.BlueBarRight.ImageTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.NameTagNonSelect.TextTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.NameTagNonSelect.TextStrokeTransparency = self.AnimParams.Background_CurrentTransparency\n\n\tself.NameTag.TextTransparency = self.AnimParams.Text_CurrentTransparency\n\tself.NewMessageIcon.ImageTransparency = self.AnimParams.Text_CurrentTransparency\n\tself.WhiteTextNewMessageNotification.TextTransparency = self.AnimParams.Text_CurrentTransparency\n\tself.NameTagSelect.TextTransparency = self.AnimParams.Text_CurrentTransparency\n\n\tself.NameTag.TextStrokeTransparency = self.AnimParams.TextStroke_CurrentTransparency\n\tself.WhiteTextNewMessageNotification.TextStrokeTransparency = self.AnimParams.TextStroke_CurrentTransparency\n\tself.NameTagSelect.TextStrokeTransparency = self.AnimParams.TextStroke_CurrentTransparency\nend\n\nfunction methods:InitializeAnimParams()\n\tself.AnimParams.Text_TargetTransparency = 0\n\tself.AnimParams.Text_CurrentTransparency = 0\n\tself.AnimParams.Text_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(0)\n\n\tself.AnimParams.TextStroke_TargetTransparency = 0.75\n\tself.AnimParams.TextStroke_CurrentTransparency = 0.75\n\tself.AnimParams.TextStroke_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(0)\n\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_CurrentTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(0)\nend\n\nfunction methods:Update(dtScale)\n\tself.AnimParams.Background_CurrentTransparency = CurveUtil:Expt(\n\t\t\tself.AnimParams.Background_CurrentTransparency,\n\t\t\tself.AnimParams.Background_TargetTransparency,\n\t\t\tself.AnimParams.Background_NormalizedExptValue,\n\t\t\tdtScale\n\t)\n\tself.AnimParams.Text_CurrentTransparency = CurveUtil:Expt(\n\t\t\tself.AnimParams.Text_CurrentTransparency,\n\t\t\tself.AnimParams.Text_TargetTransparency,\n\t\t\tself.AnimParams.Text_NormalizedExptValue,\n\t\t\tdtScale\n\t)\n\tself.AnimParams.TextStroke_CurrentTransparency = CurveUtil:Expt(\n\t\t\tself.AnimParams.TextStroke_CurrentTransparency,\n\t\t\tself.AnimParams.TextStroke_TargetTransparency,\n\t\t\tself.AnimParams.TextStroke_NormalizedExptValue,\n\t\t\tdtScale\n\t)\n\n\tself:AnimGuiObjects()\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new(channelName)\n\tlocal obj = setmetatable({}, methods)\n\n\tlocal BaseFrame, NameTag, NameTagNonSelect, NameTagSelect, NewMessageIcon, UnselectedFrame, SelectedFrame = CreateGuiObjects()\n\tobj.GuiObject = BaseFrame\n\tobj.NameTag = NameTag\n\tobj.NameTagNonSelect = NameTagNonSelect\n\tobj.NameTagSelect = NameTagSelect\n\tobj.NewMessageIcon = NewMessageIcon\n\tobj.UnselectedFrame = UnselectedFrame\n\tobj.SelectedFrame = SelectedFrame\n\n\tobj.BlueBarLeft = SelectedFrame.BlueBarLeft\n\tobj.BlueBarRight = SelectedFrame.BlueBarRight\n\tobj.BackgroundImage = SelectedFrame.BackgroundImage\n\tobj.WhiteTextNewMessageNotification = obj.NewMessageIcon.TextLabel\n\n\tobj.ChannelName = channelName\n\tobj.UnreadMessageCount = 0\n\tobj.Active = false\n\n\tobj.GuiObject.Name = \"Frame_\" .. obj.ChannelName\n\n\tif (string.len(channelName) > ChatSettings.MaxChannelNameLength) then\n\t\tchannelName = string.sub(channelName, 1, ChatSettings.MaxChannelNameLength - 3) .. \"...\"\n\tend\n\n\t--obj.NameTag.Text = channelName\n\n\tobj.NameTag.Text = \"\"\n\tobj.NameTagNonSelect.Text = channelName\n\tobj.NameTagSelect.Text = channelName\n\n\tobj.AnimParams = {}\n\n\tobj:InitializeAnimParams()\n\tobj:AnimGuiObjects()\n\tobj:SetActive(false)\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/Chat/ChatScript/ChatMain/ChatBar.lua",
    "content": "--\t// FileName: ChatBar.lua\n--\t// Written by: Xsitsu\n--\t// Description: Manages text typing and typing state.\n\nlocal module = {}\n\nlocal UserInputService = game:GetService(\"UserInputService\")\nlocal RunService = game:GetService(\"RunService\")\nlocal Players = game:GetService(\"Players\")\nlocal TextService = game:GetService(\"TextService\")\nlocal LocalPlayer = Players.LocalPlayer\n\nwhile not LocalPlayer do\n\tPlayers.PlayerAdded:wait()\n\tLocalPlayer = Players.LocalPlayer\nend\n\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal modulesFolder = script.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal CurveUtil = require(modulesFolder:WaitForChild(\"CurveUtil\"))\n\nlocal commandModules = clientChatModules:WaitForChild(\"CommandModules\")\nlocal WhisperModule = require(commandModules:WaitForChild(\"Whisper\"))\n\nlocal MessageSender = require(modulesFolder:WaitForChild(\"MessageSender\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:CreateGuiObjects(targetParent)\n\tself.ChatBarParentFrame = targetParent\n\n\tlocal backgroundImagePixelOffset = 7\n\tlocal textBoxPixelOffset = 5\n\n\tlocal BaseFrame = Instance.new(\"Frame\")\n\tBaseFrame.Selectable = false\n\tBaseFrame.Size = UDim2.new(1, 0, 1, 0)\n\tBaseFrame.BackgroundTransparency = 0.6\n\tBaseFrame.BorderSizePixel = 0\n\tBaseFrame.BackgroundColor3 = ChatSettings.ChatBarBackGroundColor\n\tBaseFrame.Parent = targetParent\n\n\tlocal BoxFrame = Instance.new(\"Frame\")\n\tBoxFrame.Selectable = false\n\tBoxFrame.Name = \"BoxFrame\"\n\tBoxFrame.BackgroundTransparency = 0.6\n\tBoxFrame.BorderSizePixel = 0\n\tBoxFrame.BackgroundColor3 = ChatSettings.ChatBarBoxColor\n\tBoxFrame.Size = UDim2.new(1, -backgroundImagePixelOffset * 2, 1, -backgroundImagePixelOffset * 2)\n\tBoxFrame.Position = UDim2.new(0, backgroundImagePixelOffset, 0, backgroundImagePixelOffset)\n\tBoxFrame.Parent = BaseFrame\n\n\tlocal TextBoxHolderFrame = Instance.new(\"Frame\")\n\tTextBoxHolderFrame.BackgroundTransparency = 1\n\tTextBoxHolderFrame.Size = UDim2.new(1, -textBoxPixelOffset * 2, 1, -textBoxPixelOffset * 2)\n\tTextBoxHolderFrame.Position = UDim2.new(0, textBoxPixelOffset, 0, textBoxPixelOffset)\n\tTextBoxHolderFrame.Parent = BoxFrame\n\n\tlocal TextBox = Instance.new(\"TextBox\")\n\tTextBox.Selectable = ChatSettings.GamepadNavigationEnabled\n\tTextBox.Name = \"ChatBar\"\n\tTextBox.BackgroundTransparency = 1\n\tTextBox.Size = UDim2.new(1, 0, 1, 0)\n\tTextBox.Position = UDim2.new(0, 0, 0, 0)\n\tTextBox.TextSize = ChatSettings.ChatBarTextSize\n\tTextBox.Font = ChatSettings.ChatBarFont\n\tTextBox.TextColor3 = ChatSettings.ChatBarTextColor\n\tTextBox.TextTransparency = 0.4\n\tTextBox.TextStrokeTransparency = 1\n\tTextBox.ClearTextOnFocus = false\n\tTextBox.TextXAlignment = Enum.TextXAlignment.Left\n\tTextBox.TextYAlignment = Enum.TextYAlignment.Top\n\tTextBox.TextWrapped = true\n\tTextBox.Text = \"\"\n\tTextBox.Parent = TextBoxHolderFrame\n\n\tlocal MessageModeTextButton = Instance.new(\"TextButton\")\n\tMessageModeTextButton.Selectable = false\n\tMessageModeTextButton.Name = \"MessageMode\"\n\tMessageModeTextButton.BackgroundTransparency = 1\n\tMessageModeTextButton.Position = UDim2.new(0, 0, 0, 0)\n\tMessageModeTextButton.TextSize = ChatSettings.ChatBarTextSize\n\tMessageModeTextButton.Font = ChatSettings.ChatBarFont\n\tMessageModeTextButton.TextXAlignment = Enum.TextXAlignment.Left\n\tMessageModeTextButton.TextWrapped = true\n\tMessageModeTextButton.Text = \"\"\n\tMessageModeTextButton.Size = UDim2.new(0, 0, 0, 0)\n\tMessageModeTextButton.TextYAlignment = Enum.TextYAlignment.Center\n\tMessageModeTextButton.TextColor3 = self:GetDefaultChannelNameColor()\n\tMessageModeTextButton.Visible = true\n\tMessageModeTextButton.Parent = TextBoxHolderFrame\n\n\tlocal TextLabel = Instance.new(\"TextLabel\")\n\tTextLabel.Selectable = false\n\tTextLabel.TextWrapped = true\n\tTextLabel.BackgroundTransparency = 1\n\tTextLabel.Size = TextBox.Size\n\tTextLabel.Position = TextBox.Position\n\tTextLabel.TextSize = TextBox.TextSize\n\tTextLabel.Font = TextBox.Font\n\tTextLabel.TextColor3 = TextBox.TextColor3\n\tTextLabel.TextTransparency = TextBox.TextTransparency\n\tTextLabel.TextStrokeTransparency = TextBox.TextStrokeTransparency\n\tTextLabel.TextXAlignment = TextBox.TextXAlignment\n\tTextLabel.TextYAlignment = TextBox.TextYAlignment\n\tTextLabel.Text = \"...\"\n\tTextLabel.Parent = TextBoxHolderFrame\n\n\tself.GuiObject = BaseFrame\n\tself.TextBox = TextBox\n\tself.TextLabel  = TextLabel\n\n\tself.GuiObjects.BaseFrame = BaseFrame\n\tself.GuiObjects.TextBoxFrame = BoxFrame\n\tself.GuiObjects.TextBox = TextBox\n\tself.GuiObjects.TextLabel = TextLabel\n\tself.GuiObjects.MessageModeTextButton = MessageModeTextButton\n\n\tself:AnimGuiObjects()\n\tself:SetUpTextBoxEvents(TextBox, TextLabel, MessageModeTextButton)\n\tif self.UserHasChatOff then\n\t\tself:DoLockChatBar()\n\tend\n\tself.eGuiObjectsChanged:Fire()\nend\n\n-- Used to lock the chat bar when the user has chat turned off.\nfunction methods:DoLockChatBar()\n\tif self.TextLabel then\n\t\tif LocalPlayer.UserId > 0 then\n\t\t\tself.TextLabel.Text = ChatLocalization:Get(\n\t\t\t\t\"GameChat_ChatMessageValidator_SettingsError\",\n\t\t\t\t\"To chat in game, turn on chat in your Privacy Settings.\"\n\t\t\t)\n\t\telse\n\t\t\tself.TextLabel.Text = ChatLocalization:Get(\n\t\t\t\t\"GameChat_SwallowGuestChat_Message\",\n\t\t\t\t\"Sign up to chat in game.\"\n\t\t\t)\n\t\tend\n\t\tself:CalculateSize()\n\tend\n\tif self.TextBox then\n\t\tself.TextBox.Active = false\n\t\tself.TextBox.Focused:connect(function()\n\t\t\tself.TextBox:ReleaseFocus()\n\t\tend)\n\tend\nend\n\nfunction methods:SetUpTextBoxEvents(TextBox, TextLabel, MessageModeTextButton)\n\t-- Clean up events from a previous setup.\n\tfor name, conn in pairs(self.TextBoxConnections) do\n\t\tconn:disconnect()\n\t\tself.TextBoxConnections[name] = nil\n\tend\n\n\t--// Code for getting back into general channel from other target channel when pressing backspace.\n\tself.TextBoxConnections.UserInputBegan = UserInputService.InputBegan:connect(function(inputObj, gpe)\n\t\tif (inputObj.KeyCode == Enum.KeyCode.Backspace) then\n\t\t\tif (self:IsFocused() and TextBox.Text == \"\") then\n\t\t\t\tself:SetChannelTarget(ChatSettings.GeneralChannelName)\n\t\t\tend\n\t\tend\n\tend)\n\n\tself.TextBoxConnections.TextBoxChanged = TextBox.Changed:connect(function(prop)\n\t\tif prop == \"AbsoluteSize\" then\n\t\t\tself:CalculateSize()\n\t\t\treturn\n\t\tend\n\n\t\tif prop ~= \"Text\" then\n\t\t\treturn\n\t\tend\n\n\t\tself:CalculateSize()\n\n\t\tif (string.len(TextBox.Text) > ChatSettings.MaximumMessageLength) then\n\t\t\tTextBox.Text = string.sub(TextBox.Text, 1, ChatSettings.MaximumMessageLength)\n\t\t\treturn\n\t\tend\n\n\t\tif not self.InCustomState then\n\t\t\tlocal customState = self.CommandProcessor:ProcessInProgressChatMessage(TextBox.Text, self.ChatWindow, self)\n\t\t\tif customState then\n\t\t\t\tself.InCustomState = true\n\t\t\t\tself.CustomState = customState\n\t\t\tend\n\t\telse\n\t\t\tself.CustomState:TextUpdated()\n\t\tend\n\tend)\n\n\tlocal function UpdateOnFocusStatusChanged(isFocused)\n\t\tif isFocused or TextBox.Text ~= \"\" then\n\t\t\tTextLabel.Visible = false\n\t\telse\n\t\t\tTextLabel.Visible = true\n\t\tend\n\tend\n\n\tself.TextBoxConnections.MessageModeClick = MessageModeTextButton.MouseButton1Click:connect(function()\n\t\tif MessageModeTextButton.Text ~= \"\" then\n\t\t\tself:SetChannelTarget(ChatSettings.GeneralChannelName)\n\t\tend\n\tend)\n\n\tself.TextBoxConnections.TextBoxFocused = TextBox.Focused:connect(function()\n\t\tif not self.UserHasChatOff then\n\t\t\tself:CalculateSize()\n\t\t\tUpdateOnFocusStatusChanged(true)\n\t\tend\n\tend)\n\n\tself.TextBoxConnections.TextBoxFocusLost = TextBox.FocusLost:connect(function(enterPressed, inputObject)\n\t\tself:CalculateSize()\n\t\tif (inputObject and inputObject.KeyCode == Enum.KeyCode.Escape) then\n\t\t\tTextBox.Text = \"\"\n\t\tend\n\t\tUpdateOnFocusStatusChanged(false)\n\tend)\nend\n\nfunction methods:GetTextBox()\n\treturn self.TextBox\nend\n\nfunction methods:GetMessageModeTextButton()\n\treturn self.GuiObjects.MessageModeTextButton\nend\n\n-- Deprecated in favour of GetMessageModeTextButton\n-- Retained for compatibility reasons.\nfunction methods:GetMessageModeTextLabel()\n\treturn self:GetMessageModeTextButton()\nend\n\nfunction methods:IsFocused()\n\tif self.UserHasChatOff then\n\t\treturn false\n\tend\n\n\treturn self:GetTextBox():IsFocused()\nend\n\nfunction methods:GetVisible()\n\treturn self.GuiObject.Visible\nend\n\nfunction methods:CaptureFocus()\n\tif not self.UserHasChatOff then\n\t\tself:GetTextBox():CaptureFocus()\n\tend\nend\n\nfunction methods:ReleaseFocus(didRelease)\n\tself:GetTextBox():ReleaseFocus(didRelease)\nend\n\nfunction methods:ResetText()\n\tself:GetTextBox().Text = \"\"\nend\n\nfunction methods:SetText(text)\n\tself:GetTextBox().Text = text\nend\n\nfunction methods:GetEnabled()\n\treturn self.GuiObject.Visible\nend\n\nfunction methods:SetEnabled(enabled)\n\tif self.UserHasChatOff then\n\t\t-- The chat bar can not be removed if a user has chat turned off so that\n\t\t-- the chat bar can display a message explaining that chat is turned off.\n\t\tself.GuiObject.Visible = true\n\telse\n\t\tself.GuiObject.Visible = enabled\n\tend\nend\n\nfunction methods:SetTextLabelText(text)\n\tif not self.UserHasChatOff then\n\t\tself.TextLabel.Text = text\n\tend\nend\n\nfunction methods:SetTextBoxText(text)\n\tself.TextBox.Text = text\nend\n\nfunction methods:GetTextBoxText()\n\treturn self.TextBox.Text\nend\n\nfunction methods:ResetSize()\n\tself.TargetYSize = 0\n\tself:TweenToTargetYSize()\nend\n\nlocal function measureSize(textObj)\n\treturn TextService:GetTextSize(\n\t\ttextObj.Text,\n\t\ttextObj.TextSize,\n\t\ttextObj.Font,\n\t\tVector2.new(textObj.AbsoluteSize.X, 10000)\n\t)\nend\n\nfunction methods:CalculateSize()\n\tif self.CalculatingSizeLock then\n\t\treturn\n\tend\n\tself.CalculatingSizeLock = true\n\n\tlocal textSize = nil\n\tlocal bounds = nil\n\n\tif self:IsFocused() or self.TextBox.Text ~= \"\" then\n\t\ttextSize = self.TextBox.TextSize\n\t\tbounds = measureSize(self.TextBox).Y\n\telse\n\t\ttextSize = self.TextLabel.TextSize\n\t\tbounds = measureSize(self.TextLabel).Y\n\tend\n\n\tlocal newTargetYSize = bounds - textSize\n\tif (self.TargetYSize ~= newTargetYSize) then\n\t\tself.TargetYSize = newTargetYSize\n\t\tself:TweenToTargetYSize()\n\tend\n\n\tself.CalculatingSizeLock = false\nend\n\nfunction methods:TweenToTargetYSize()\n\tlocal endSize = UDim2.new(1, 0, 1, self.TargetYSize)\n\tlocal curSize = self.GuiObject.Size\n\n\tlocal curAbsoluteSizeY = self.GuiObject.AbsoluteSize.Y\n\tself.GuiObject.Size = endSize\n\tlocal endAbsoluteSizeY = self.GuiObject.AbsoluteSize.Y\n\tself.GuiObject.Size = curSize\n\n\tlocal pixelDistance = math.abs(endAbsoluteSizeY - curAbsoluteSizeY)\n\tlocal tweeningTime = math.min(1, (pixelDistance * (1 / self.TweenPixelsPerSecond))) -- pixelDistance * (seconds per pixels)\n\n\tlocal success = pcall(function() self.GuiObject:TweenSize(endSize, Enum.EasingDirection.Out, Enum.EasingStyle.Quad, tweeningTime, true) end)\n\tif (not success) then\n\t\tself.GuiObject.Size = endSize\n\tend\nend\n\nfunction methods:SetTextSize(textSize)\n\tif not self:IsInCustomState() then\n\t\tif self.TextBox then\n\t\t\tself.TextBox.TextSize = textSize\n\t\tend\n\t\tif self.TextLabel then\n\t\t\tself.TextLabel.TextSize = textSize\n\t\tend\n\tend\nend\n\nfunction methods:GetDefaultChannelNameColor()\n\tif ChatSettings.DefaultChannelNameColor then\n\t\treturn ChatSettings.DefaultChannelNameColor\n\tend\n\treturn Color3.fromRGB(35, 76, 142)\nend\n\nfunction methods:SetChannelTarget(targetChannel)\n\tlocal messageModeTextButton = self.GuiObjects.MessageModeTextButton\n\tlocal textBox = self.TextBox\n\tlocal textLabel = self.TextLabel\n\n\tself.TargetChannel = targetChannel\n\n\tif not self:IsInCustomState() then\n\t\tif targetChannel ~= ChatSettings.GeneralChannelName then\n\t\t\tmessageModeTextButton.Size = UDim2.new(0, 1000, 1, 0)\n\t\t\tmessageModeTextButton.Text = string.format(\"[%s] \", targetChannel)\n\n\t\t\tlocal channelNameColor = self:GetChannelNameColor(targetChannel)\n\t\t\tif channelNameColor then\n\t\t\t\tmessageModeTextButton.TextColor3 = channelNameColor\n\t\t\telse\n\t\t\t\tmessageModeTextButton.TextColor3 = self:GetDefaultChannelNameColor()\n\t\t\tend\n\n\t\t\tlocal xSize = messageModeTextButton.TextBounds.X\n\t\t\tmessageModeTextButton.Size = UDim2.new(0, xSize, 1, 0)\n\t\t\ttextBox.Size = UDim2.new(1, -xSize, 1, 0)\n\t\t\ttextBox.Position = UDim2.new(0, xSize, 0, 0)\n\t\t\ttextLabel.Size = UDim2.new(1, -xSize, 1, 0)\n\t\t\ttextLabel.Position = UDim2.new(0, xSize, 0, 0)\n\t\telse\n\t\t\tmessageModeTextButton.Text = \"\"\n\t\t\tmessageModeTextButton.Size = UDim2.new(0, 0, 0, 0)\n\t\t\ttextBox.Size = UDim2.new(1, 0, 1, 0)\n\t\t\ttextBox.Position = UDim2.new(0, 0, 0, 0)\n\t\t\ttextLabel.Size = UDim2.new(1, 0, 1, 0)\n\t\t\ttextLabel.Position = UDim2.new(0, 0, 0, 0)\n\t\tend\n\tend\nend\n\nfunction methods:IsInCustomState()\n\treturn self.InCustomState\nend\n\nfunction methods:ResetCustomState()\n\tif self.InCustomState then\n\t\tself.CustomState:Destroy()\n\t\tself.CustomState = nil\n\t\tself.InCustomState = false\n\n\t\tself.ChatBarParentFrame:ClearAllChildren()\n\t\tself:CreateGuiObjects(self.ChatBarParentFrame)\n\t\tself:SetTextLabelText(\n\t\t\tChatLocalization:Get(\n\t\t\t\t\"GameChat_ChatMain_ChatBarText\",\n\t\t\t\t'To chat click here or press \"/\" key'\n\t\t\t)\n\t\t)\n\tend\nend\n\nfunction methods:EnterWhisperState(player)\n\tself:ResetCustomState()\n\tself:CaptureFocus()\n\tif WhisperModule.CustomStateCreator then\n\t\tself.CustomState = WhisperModule.CustomStateCreator(\n\t\t\tplayer,\n\t\t\tself.ChatWindow,\n\t\t\tself,\n\t\t\tChatSettings\n\t\t)\n\t\tself.InCustomState = true\n\telse\n\t\tself:SetText(\"/w \" .. player.Name)\n\tend\nend\n\nfunction methods:GetCustomMessage()\n\tif self.InCustomState then\n\t\treturn self.CustomState:GetMessage()\n\tend\n\treturn nil\nend\n\nfunction methods:CustomStateProcessCompletedMessage(message)\n\tif self.InCustomState then\n\t\treturn self.CustomState:ProcessCompletedMessage()\n\tend\n\treturn false\nend\n\nfunction methods:FadeOutBackground(duration)\n\tself.AnimParams.Background_TargetTransparency = 1\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\n\tself:FadeOutText(duration)\nend\n\nfunction methods:FadeInBackground(duration)\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\n\tself:FadeInText(duration)\nend\n\nfunction methods:FadeOutText(duration)\n\tself.AnimParams.Text_TargetTransparency = 1\n\tself.AnimParams.Text_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeInText(duration)\n\tself.AnimParams.Text_TargetTransparency = 0.4\n\tself.AnimParams.Text_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:AnimGuiObjects()\n\tself.GuiObject.BackgroundTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.GuiObjects.TextBoxFrame.BackgroundTransparency = self.AnimParams.Background_CurrentTransparency\n\n\tself.GuiObjects.TextLabel.TextTransparency = self.AnimParams.Text_CurrentTransparency\n\tself.GuiObjects.TextBox.TextTransparency = self.AnimParams.Text_CurrentTransparency\n\tself.GuiObjects.MessageModeTextButton.TextTransparency = self.AnimParams.Text_CurrentTransparency\nend\n\nfunction methods:InitializeAnimParams()\n\tself.AnimParams.Text_TargetTransparency = 0.4\n\tself.AnimParams.Text_CurrentTransparency = 0.4\n\tself.AnimParams.Text_NormalizedExptValue = 1\n\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_CurrentTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = 1\nend\n\nfunction methods:Update(dtScale)\n\tself.AnimParams.Text_CurrentTransparency = CurveUtil:Expt(\n\t\t\tself.AnimParams.Text_CurrentTransparency,\n\t\t\tself.AnimParams.Text_TargetTransparency,\n\t\t\tself.AnimParams.Text_NormalizedExptValue,\n\t\t\tdtScale\n\t)\n\tself.AnimParams.Background_CurrentTransparency = CurveUtil:Expt(\n\t\t\tself.AnimParams.Background_CurrentTransparency,\n\t\t\tself.AnimParams.Background_TargetTransparency,\n\t\t\tself.AnimParams.Background_NormalizedExptValue,\n\t\t\tdtScale\n\t)\n\n\tself:AnimGuiObjects()\nend\n\nfunction methods:SetChannelNameColor(channelName, channelNameColor)\n\tself.ChannelNameColors[channelName] = channelNameColor\n\tif self.GuiObjects.MessageModeTextButton.Text == channelName then\n\t\tself.GuiObjects.MessageModeTextButton.TextColor3 = channelNameColor\n\tend\nend\n\nfunction methods:GetChannelNameColor(channelName)\n\treturn self.ChannelNameColors[channelName]\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new(CommandProcessor, ChatWindow)\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.GuiObject = nil\n\tobj.ChatBarParentFrame = nil\n\tobj.TextBox = nil\n\tobj.TextLabel = nil\n\tobj.GuiObjects = {}\n\tobj.eGuiObjectsChanged = Instance.new(\"BindableEvent\")\n\tobj.GuiObjectsChanged = obj.eGuiObjectsChanged.Event\n\tobj.TextBoxConnections = {}\n\n\tobj.InCustomState = false\n\tobj.CustomState = nil\n\n\tobj.TargetChannel = nil\n\tobj.CommandProcessor = CommandProcessor\n\tobj.ChatWindow = ChatWindow\n\n\tobj.TweenPixelsPerSecond = 500\n\tobj.TargetYSize = 0\n\n\tobj.AnimParams = {}\n\tobj.CalculatingSizeLock = false\n\n\tobj.ChannelNameColors = {}\n\n\tobj.UserHasChatOff = false\n\n\tobj:InitializeAnimParams()\n\n\tChatSettings.SettingsChanged:connect(function(setting, value)\n\t\tif (setting == \"ChatBarTextSize\") then\n\t\t\tobj:SetTextSize(value)\n\t\tend\n\tend)\n\n\tcoroutine.wrap(function()\n\t\tlocal success, canLocalUserChat = pcall(function()\n\t\t\treturn Chat:CanUserChatAsync(LocalPlayer.UserId)\n\t\tend)\n\t\tlocal canChat = success and (RunService:IsStudio() or canLocalUserChat)\n\t\tif canChat == false then\n\t\t\tobj.UserHasChatOff = true\n\t\t\tobj:DoLockChatBar()\n\t\tend\n\tend)()\n\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/Chat/ChatScript/ChatMain/ChatChannel.lua",
    "content": "--\t// FileName: ChatChannel.lua\n--\t// Written by: Xsitsu\n--\t// Description: ChatChannel class for handling messages being added and removed from the chat channel.\n\nlocal module = {}\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal modulesFolder = script.Parent\n\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:Destroy()\n\tself.Destroyed = true\nend\n\nfunction methods:SetActive(active)\n\tif active == self.Active then\n\t\treturn\n\tend\n\tif active == false then\n\t\tself.MessageLogDisplay:Clear()\n\telse\n\t\tself.MessageLogDisplay:SetCurrentChannelName(self.Name)\n\t\tfor i = 1, #self.MessageLog do\n\t\t\tself.MessageLogDisplay:AddMessage(self.MessageLog[i])\n\t\tend\n\tend\n\tself.Active = active\nend\n\nfunction methods:UpdateMessageFiltered(messageData)\n\tlocal searchIndex = 1\n\tlocal searchTable = self.MessageLog\n\tlocal messageObj = nil\n\twhile (#searchTable >= searchIndex) do\n\t\tlocal obj = searchTable[searchIndex]\n\n\t\tif (obj.ID == messageData.ID) then\n\t\t\tmessageObj = obj\n\t\t\tbreak\n\t\tend\n\n\t\tsearchIndex = searchIndex + 1\n\tend\n\n\tif messageObj then\n\t\tmessageObj.Message = messageData.Message\n\t\tmessageObj.IsFiltered = true\n\t\tif self.Active then\n\t\t\tself.MessageLogDisplay:UpdateMessageFiltered(messageObj)\n\t\tend\n\telse\n\t\t-- We have not seen this filtered message before, but we should still add it to our log.\n\t\tself:AddMessageToChannelByTimeStamp(messageData)\n\tend\nend\n\nfunction methods:AddMessageToChannel(messageData)\n\ttable.insert(self.MessageLog, messageData)\n\tif self.Active then\n\t\tself.MessageLogDisplay:AddMessage(messageData)\n\tend\n\tif #self.MessageLog > ChatSettings.MessageHistoryLengthPerChannel then\n\t\tself:RemoveLastMessageFromChannel()\n\tend\nend\n\nfunction methods:InternalAddMessageAtTimeStamp(messageData)\n\tfor i = 1, #self.MessageLog do\n\t\tif messageData.Time < self.MessageLog[i].Time then\n\t\t\ttable.insert(self.MessageLog, i, messageData)\n\t\t\treturn\n\t\tend\n\tend\n\ttable.insert(self.MessageLog, messageData)\nend\n\nfunction methods:AddMessagesToChannelByTimeStamp(messageLog, startIndex)\n\tfor i = startIndex, #messageLog do\n\t\tself:InternalAddMessageAtTimeStamp(messageLog[i])\n\tend\n\twhile #self.MessageLog > ChatSettings.MessageHistoryLengthPerChannel do\n\t\ttable.remove(self.MessageLog, 1)\n\tend\n\tif self.Active then\n\t\tself.MessageLogDisplay:Clear()\n\t\tfor i = 1, #self.MessageLog do\n\t\t\tself.MessageLogDisplay:AddMessage(self.MessageLog[i])\n\t\tend\n\tend\nend\n\nfunction methods:AddMessageToChannelByTimeStamp(messageData)\n\tif #self.MessageLog >= 1 then\n\t\t-- These are the fast cases to evalutate.\n\t\tif self.MessageLog[1].Time > messageData.Time then\n\t\t\treturn\n\t\telseif messageData.Time >= self.MessageLog[#self.MessageLog].Time then\n\t\t\tself:AddMessageToChannel(messageData)\n\t\t\treturn\n\t\tend\n\n\t\tfor i = 1, #self.MessageLog do\n\t\t\tif messageData.Time < self.MessageLog[i].Time then\n\t\t\t\ttable.insert(self.MessageLog, i, messageData)\n\n\t\t\t\tif #self.MessageLog > ChatSettings.MessageHistoryLengthPerChannel then\n\t\t\t\t\tself:RemoveLastMessageFromChannel()\n\t\t\t\tend\n\n\t\t\t\tif self.Active then\n\t\t\t\t\tself.MessageLogDisplay:AddMessageAtIndex(messageData, i)\n\t\t\t\tend\n\n\t\t\t\treturn\n\t\t\tend\n\t\tend\n\telse\n\t\tself:AddMessageToChannel(messageData)\n\tend\nend\n\nfunction methods:RemoveLastMessageFromChannel()\n\ttable.remove(self.MessageLog, 1)\n\n\tif self.Active then\n\t\tself.MessageLogDisplay:RemoveLastMessage()\n\tend\nend\n\nfunction methods:ClearMessageLog()\n\tself.MessageLog = {}\n\n\tif self.Active then\n\t\tself.MessageLogDisplay:Clear()\n\tend\nend\n\nfunction methods:RegisterChannelTab(tab)\n\tself.ChannelTab = tab\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new(channelName, messageLogDisplay)\n\tlocal obj = setmetatable({}, methods)\n\tobj.Destroyed = false\n\tobj.Active = false\n\n\tobj.MessageLog = {}\n\tobj.MessageLogDisplay = messageLogDisplay\n\tobj.ChannelTab = nil\n\tobj.Name = channelName\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/Chat/ChatScript/ChatMain/ChatWindow.lua",
    "content": "--\t// FileName: ChatWindow.lua\n--\t// Written by: Xsitsu\n--\t// Description: Main GUI window piece. Manages ChatBar, ChannelsBar, and ChatChannels.\n\nlocal module = {}\n\nlocal Players = game:GetService(\"Players\")\nlocal Chat = game:GetService(\"Chat\")\nlocal LocalPlayer = Players.LocalPlayer\nlocal PlayerGui = LocalPlayer:WaitForChild(\"PlayerGui\")\n\nlocal PHONE_SCREEN_WIDTH = 640\nlocal TABLET_SCREEN_WIDTH = 1024\n\nlocal DEVICE_PHONE = 1\nlocal DEVICE_TABLET = 2\nlocal DEVICE_DESKTOP = 3\n\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal modulesFolder = script.Parent\nlocal moduleChatChannel = require(modulesFolder:WaitForChild(\"ChatChannel\"))\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal CurveUtil = require(modulesFolder:WaitForChild(\"CurveUtil\"))\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nfunction getClassicChatEnabled()\n\tif ChatSettings.ClassicChatEnabled ~= nil then\n\t\treturn ChatSettings.ClassicChatEnabled\n\tend\n\treturn Players.ClassicChat\nend\n\nfunction getBubbleChatEnabled()\n\tif ChatSettings.BubbleChatEnabled ~= nil then\n\t\treturn ChatSettings.BubbleChatEnabled\n\tend\n\treturn Players.BubbleChat\nend\n\nfunction bubbleChatOnly()\n \treturn not getClassicChatEnabled() and getBubbleChatEnabled()\nend\n\n-- only merge property defined on target\nfunction mergeProps(source, target)\n\tif not source or not target then return end\n\tfor prop, value in pairs(source) do\n\t\tif target[prop] ~= nil then\n\t\t\ttarget[prop] = value\n\t\tend\n\tend\nend\n\nfunction methods:CreateGuiObjects(targetParent)\n\tlocal userDefinedChatWindowStyle \n\tpcall(function()\n\t\tuserDefinedChatWindowStyle= Chat:InvokeChatCallback(Enum.ChatCallbackType.OnCreatingChatWindow, nil)\n\tend)\n\n\t-- merge the userdefined settings with the ChatSettings\n\tmergeProps(userDefinedChatWindowStyle, ChatSettings)\n\n\tlocal BaseFrame = Instance.new(\"Frame\")\n\tBaseFrame.BackgroundTransparency = 1\n\tBaseFrame.Active = ChatSettings.WindowDraggable\n\tBaseFrame.Parent = targetParent\n    BaseFrame.AutoLocalize = false\n\n\tlocal ChatBarParentFrame = Instance.new(\"Frame\")\n\tChatBarParentFrame.Selectable = false\n\tChatBarParentFrame.Name = \"ChatBarParentFrame\"\n\tChatBarParentFrame.BackgroundTransparency = 1\n\tChatBarParentFrame.Parent = BaseFrame\n\n\tlocal ChannelsBarParentFrame = Instance.new(\"Frame\")\n\tChannelsBarParentFrame.Selectable = false\n\tChannelsBarParentFrame.Name = \"ChannelsBarParentFrame\"\n\tChannelsBarParentFrame.BackgroundTransparency = 1\n\tChannelsBarParentFrame.Position = UDim2.new(0, 0, 0, 0)\n\tChannelsBarParentFrame.Parent = BaseFrame\n\n\tlocal ChatChannelParentFrame = Instance.new(\"Frame\")\n\tChatChannelParentFrame.Selectable = false\n\tChatChannelParentFrame.Name = \"ChatChannelParentFrame\"\n\tChatChannelParentFrame.BackgroundTransparency = 1\n\tChatChannelParentFrame.BackgroundColor3 = ChatSettings.BackGroundColor\n\tChatChannelParentFrame.BackgroundTransparency = 0.6\n\tChatChannelParentFrame.BorderSizePixel = 0\n\tChatChannelParentFrame.Parent = BaseFrame\n\n\tlocal ChatResizerFrame = Instance.new(\"ImageButton\")\n\tChatResizerFrame.Selectable = false\n\tChatResizerFrame.Image = \"\"\n\tChatResizerFrame.BackgroundTransparency = 0.6\n\tChatResizerFrame.BorderSizePixel = 0\n\tChatResizerFrame.Visible = false\n\tChatResizerFrame.BackgroundColor3 = ChatSettings.BackGroundColor\n\tChatResizerFrame.Active = true\n\tif bubbleChatOnly() then\n\t\tChatResizerFrame.Position = UDim2.new(1, -ChatResizerFrame.AbsoluteSize.X, 0, 0)\n\telse\n\t\tChatResizerFrame.Position = UDim2.new(1, -ChatResizerFrame.AbsoluteSize.X, 1, -ChatResizerFrame.AbsoluteSize.Y)\n\tend\n\tChatResizerFrame.Parent = BaseFrame\n\n\tlocal ResizeIcon = Instance.new(\"ImageLabel\")\n\tResizeIcon.Selectable = false\n\tResizeIcon.Size = UDim2.new(0.8, 0, 0.8, 0)\n\tResizeIcon.Position = UDim2.new(0.2, 0, 0.2, 0)\n\tResizeIcon.BackgroundTransparency = 1\n\tResizeIcon.Image = \"rbxassetid://261880743\"\n\tResizeIcon.Parent = ChatResizerFrame\n\n\tlocal function GetScreenGuiParent()\n\t\t--// Travel up parent list until you find the ScreenGui that the chat window is parented to\n\t\tlocal screenGuiParent = BaseFrame\n\t\twhile (screenGuiParent and not screenGuiParent:IsA(\"ScreenGui\")) do\n\t\t\tscreenGuiParent = screenGuiParent.Parent\n\t\tend\n\n\t\treturn screenGuiParent\n\tend\n\n\n\tlocal deviceType = DEVICE_DESKTOP\n\n\tlocal screenGuiParent = GetScreenGuiParent()\n\tif (screenGuiParent.AbsoluteSize.X <= PHONE_SCREEN_WIDTH) then\n\t\tdeviceType = DEVICE_PHONE\n\n\telseif (screenGuiParent.AbsoluteSize.X <= TABLET_SCREEN_WIDTH) then\n\t\tdeviceType = DEVICE_TABLET\n\n\tend\n\n\tlocal checkSizeLock = false\n\tlocal function doCheckSizeBounds()\n\t\tif (checkSizeLock) then return end\n\t\tcheckSizeLock = true\n\n\t\tif (not BaseFrame:IsDescendantOf(PlayerGui)) then return end\n\n\t\tlocal screenGuiParent = GetScreenGuiParent()\n\n\t\tlocal minWinSize = ChatSettings.MinimumWindowSize\n\t\tlocal maxWinSize = ChatSettings.MaximumWindowSize\n\n\t\tlocal forceMinY = ChannelsBarParentFrame.AbsoluteSize.Y + ChatBarParentFrame.AbsoluteSize.Y\n\n\t\tlocal minSizePixelX = (minWinSize.X.Scale * screenGuiParent.AbsoluteSize.X) + minWinSize.X.Offset\n\t\tlocal minSizePixelY = math.max((minWinSize.Y.Scale * screenGuiParent.AbsoluteSize.Y) + minWinSize.Y.Offset, forceMinY)\n\n\t\tlocal maxSizePixelX = (maxWinSize.X.Scale * screenGuiParent.AbsoluteSize.X) + maxWinSize.X.Offset\n\t\tlocal maxSizePixelY = (maxWinSize.Y.Scale * screenGuiParent.AbsoluteSize.Y) + maxWinSize.Y.Offset\n\n\t\tlocal absSizeX = BaseFrame.AbsoluteSize.X\n\t\tlocal absSizeY = BaseFrame.AbsoluteSize.Y\n\n\t\tif (absSizeX < minSizePixelX) then\n\t\t\tlocal offset = UDim2.new(0, minSizePixelX - absSizeX, 0, 0)\n\t\t\tBaseFrame.Size = BaseFrame.Size + offset\n\n\t\telseif (absSizeX > maxSizePixelX) then\n\t\t\tlocal offset = UDim2.new(0, maxSizePixelX - absSizeX, 0, 0)\n\t\t\tBaseFrame.Size = BaseFrame.Size + offset\n\n\t\tend\n\n\t\tif (absSizeY < minSizePixelY) then\n\t\t\tlocal offset = UDim2.new(0, 0, 0, minSizePixelY - absSizeY)\n\t\t\tBaseFrame.Size = BaseFrame.Size + offset\n\n\t\telseif (absSizeY > maxSizePixelY) then\n\t\t\tlocal offset = UDim2.new(0, 0, 0, maxSizePixelY - absSizeY)\n\t\t\tBaseFrame.Size = BaseFrame.Size + offset\n\n\t\tend\n\n\t\tlocal xScale = BaseFrame.AbsoluteSize.X / screenGuiParent.AbsoluteSize.X\n\t\tlocal yScale = BaseFrame.AbsoluteSize.Y / screenGuiParent.AbsoluteSize.Y\n\t\tBaseFrame.Size = UDim2.new(xScale, 0, yScale, 0)\n\n\t\tcheckSizeLock = false\n\tend\n\n\n\tBaseFrame.Changed:connect(function(prop)\n\t\tif (prop == \"AbsoluteSize\") then\n\t\t\tdoCheckSizeBounds()\n\t\tend\n\tend)\n\n\n\n\tChatResizerFrame.DragBegin:connect(function(startUdim)\n\t\tBaseFrame.Draggable = false\n\tend)\n\n\tlocal function UpdatePositionFromDrag(atPos)\n\t\tif ChatSettings.WindowDraggable == false and ChatSettings.WindowResizable == false then\n\t\t\treturn\n\t\tend\n\t\tlocal newSize = atPos - BaseFrame.AbsolutePosition + ChatResizerFrame.AbsoluteSize\n\t\tBaseFrame.Size = UDim2.new(0, newSize.X, 0, newSize.Y)\n\t\tif bubbleChatOnly() then\n\t\t\tChatResizerFrame.Position = UDim2.new(1, -ChatResizerFrame.AbsoluteSize.X, 0, 0)\n\t\telse\n\t\t\tChatResizerFrame.Position = UDim2.new(1, -ChatResizerFrame.AbsoluteSize.X, 1, -ChatResizerFrame.AbsoluteSize.Y)\n\t\tend\n\tend\n\n\tChatResizerFrame.DragStopped:connect(function(endX, endY)\n\t\tBaseFrame.Draggable = ChatSettings.WindowDraggable\n\t\t--UpdatePositionFromDrag(Vector2.new(endX, endY))\n\tend)\n\n\tlocal resizeLock = false\n\tChatResizerFrame.Changed:connect(function(prop)\n\t\tif (prop == \"AbsolutePosition\" and not BaseFrame.Draggable) then\n\t\t\tif (resizeLock) then return end\n\t\t\tresizeLock = true\n\n\t\t\tUpdatePositionFromDrag(ChatResizerFrame.AbsolutePosition)\n\n\t\t\tresizeLock = false\n\t\tend\n\tend)\n\n\tlocal function CalculateChannelsBarPixelSize(textSize)\n\t\tif (deviceType == DEVICE_PHONE) then\n\t\t\ttextSize = textSize or ChatSettings.ChatChannelsTabTextSizePhone\n\t\telse\n\t\t\ttextSize = textSize or ChatSettings.ChatChannelsTabTextSize\n\t\tend\n\n\t\tlocal channelsBarTextYSize = textSize\n\t\tlocal chatChannelYSize = math.max(32, channelsBarTextYSize + 8) + 2\n\n\t\treturn chatChannelYSize\n\tend\n\n\tlocal function CalculateChatBarPixelSize(textSize)\n\t\tif (deviceType == DEVICE_PHONE) then\n\t\t\ttextSize = textSize or ChatSettings.ChatBarTextSizePhone\n\t\telse\n\t\t\ttextSize = textSize or ChatSettings.ChatBarTextSize\n\t\tend\n\n\t\tlocal chatBarTextSizeY = textSize\n\t\tlocal chatBarYSize = chatBarTextSizeY + (7 * 2) + (5 * 2)\n\n\t\treturn chatBarYSize\n\tend\n\n\tif bubbleChatOnly() then\n\t\tChatBarParentFrame.Position = UDim2.new(0, 0, 0, 0)\n\t\tChannelsBarParentFrame.Visible = false\n\t\tChannelsBarParentFrame.Active = false\n\t\tChatChannelParentFrame.Visible = false\n\t\tChatChannelParentFrame.Active = false\n\n\t\tlocal useXScale = 0\n\t\tlocal useXOffset = 0\n\n\t\tlocal screenGuiParent = GetScreenGuiParent()\n\n\t\tif (deviceType == DEVICE_PHONE) then\n\t\t\tuseXScale = ChatSettings.DefaultWindowSizePhone.X.Scale\n\t\t\tuseXOffset = ChatSettings.DefaultWindowSizePhone.X.Offset\n\n\t\telseif (deviceType == DEVICE_TABLET) then\n\t\t\tuseXScale = ChatSettings.DefaultWindowSizeTablet.X.Scale\n\t\t\tuseXOffset = ChatSettings.DefaultWindowSizeTablet.X.Offset\n\n\t\telse\n\t\t\tuseXScale = ChatSettings.DefaultWindowSizeTablet.X.Scale\n\t\t\tuseXOffset = ChatSettings.DefaultWindowSizeTablet.X.Offset\n\n\t\tend\n\n\t\tlocal chatBarYSize = CalculateChatBarPixelSize()\n\n\t\tBaseFrame.Size = UDim2.new(useXScale, useXOffset, 0, chatBarYSize)\n\t\tBaseFrame.Position = ChatSettings.DefaultWindowPosition\n\n\telse\n\n\t\tlocal screenGuiParent = GetScreenGuiParent()\n\n\t\tif (deviceType == DEVICE_PHONE) then\n\t\t\tBaseFrame.Size = ChatSettings.DefaultWindowSizePhone\n\n\t\telseif (deviceType == DEVICE_TABLET) then\n\t\t\tBaseFrame.Size = ChatSettings.DefaultWindowSizeTablet\n\n\t\telse\n\t\t\tBaseFrame.Size = ChatSettings.DefaultWindowSizeDesktop\n\n\t\tend\n\n\t\tBaseFrame.Position = ChatSettings.DefaultWindowPosition\n\n\tend\n\n\tif (deviceType == DEVICE_PHONE) then\n\t\tChatSettings.ChatWindowTextSize = ChatSettings.ChatWindowTextSizePhone\n\t\tChatSettings.ChatChannelsTabTextSize = ChatSettings.ChatChannelsTabTextSizePhone\n\t\tChatSettings.ChatBarTextSize = ChatSettings.ChatBarTextSizePhone\n\tend\n\n\tlocal function UpdateDraggable(enabled)\n\t\tBaseFrame.Active = enabled\n\t\tBaseFrame.Draggable = enabled\n\tend\n\n\tlocal function UpdateResizable(enabled)\n\t\tChatResizerFrame.Visible = enabled\n\t\tChatResizerFrame.Draggable = enabled\n\n\t\tlocal frameSizeY = ChatBarParentFrame.Size.Y.Offset\n\n\t\tif (enabled) then\n\t\t\tChatBarParentFrame.Size = UDim2.new(1, -frameSizeY - 2, 0, frameSizeY)\n\t\t\tif not bubbleChatOnly() then\n\t\t\t\tChatBarParentFrame.Position = UDim2.new(0, 0, 1, -frameSizeY)\n\t\t\tend\n\t\telse\n\t\t\tChatBarParentFrame.Size = UDim2.new(1, 0, 0, frameSizeY)\n\t\t\tif not bubbleChatOnly() then\n\t\t\t\tChatBarParentFrame.Position = UDim2.new(0, 0, 1, -frameSizeY)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function UpdateChatChannelParentFrameSize()\n\t\tlocal channelsBarSize = CalculateChannelsBarPixelSize()\n\t\tlocal chatBarSize = CalculateChatBarPixelSize()\n\n\t\tif (ChatSettings.ShowChannelsBar) then\n\t\t\tChatChannelParentFrame.Size = UDim2.new(1, 0, 1, -(channelsBarSize + chatBarSize + 2 + 2))\n\t\t\tChatChannelParentFrame.Position = UDim2.new(0, 0, 0, channelsBarSize + 2)\n\n\t\telse\n\t\t\tChatChannelParentFrame.Size = UDim2.new(1, 0, 1, -(chatBarSize + 2 + 2))\n\t\t\tChatChannelParentFrame.Position = UDim2.new(0, 0, 0, 2)\n\n\t\tend\n\tend\n\n\tlocal function UpdateChatChannelsTabTextSize(size)\n\t\tlocal channelsBarSize = CalculateChannelsBarPixelSize(size)\n\t\tChannelsBarParentFrame.Size = UDim2.new(1, 0, 0, channelsBarSize)\n\n\t\tUpdateChatChannelParentFrameSize()\n\tend\n\n\tlocal function UpdateChatBarTextSize(size)\n\t\tlocal chatBarSize = CalculateChatBarPixelSize(size)\n\n\t\tChatBarParentFrame.Size = UDim2.new(1, 0, 0, chatBarSize)\n\t\tif not bubbleChatOnly() then\n\t\t\tChatBarParentFrame.Position = UDim2.new(0, 0, 1, -chatBarSize)\n\t\tend\n\n\t\tChatResizerFrame.Size = UDim2.new(0, chatBarSize, 0, chatBarSize)\n\t\tChatResizerFrame.Position = UDim2.new(1, -chatBarSize, 1, -chatBarSize)\n\n\t\tUpdateChatChannelParentFrameSize()\n\t\tUpdateResizable(ChatSettings.WindowResizable)\n\tend\n\n\tlocal function UpdateShowChannelsBar(enabled)\n\t\tChannelsBarParentFrame.Visible = enabled\n\t\tUpdateChatChannelParentFrameSize()\n\tend\n\n\tUpdateChatChannelsTabTextSize(ChatSettings.ChatChannelsTabTextSize)\n\tUpdateChatBarTextSize(ChatSettings.ChatBarTextSize)\n\tUpdateDraggable(ChatSettings.WindowDraggable)\n\tUpdateResizable(ChatSettings.WindowResizable)\n\tUpdateShowChannelsBar(ChatSettings.ShowChannelsBar)\n\n\tChatSettings.SettingsChanged:connect(function(setting, value)\n\t\tif (setting == \"WindowDraggable\") then\n\t\t\tUpdateDraggable(value)\n\n\t\telseif (setting == \"WindowResizable\") then\n\t\t\tUpdateResizable(value)\n\n\t\telseif (setting == \"ChatChannelsTabTextSize\") then\n\t\t\tUpdateChatChannelsTabTextSize(value)\n\n\t\telseif (setting == \"ChatBarTextSize\") then\n\t\t\tUpdateChatBarTextSize(value)\n\n\t\telseif (setting == \"ShowChannelsBar\") then\n\t\t\tUpdateShowChannelsBar(value)\n\n\t\tend\n\tend)\n\n\tself.GuiObject = BaseFrame\n\n\tself.GuiObjects.BaseFrame = BaseFrame\n\tself.GuiObjects.ChatBarParentFrame = ChatBarParentFrame\n\tself.GuiObjects.ChannelsBarParentFrame = ChannelsBarParentFrame\n\tself.GuiObjects.ChatChannelParentFrame = ChatChannelParentFrame\n\tself.GuiObjects.ChatResizerFrame = ChatResizerFrame\n\tself.GuiObjects.ResizeIcon = ResizeIcon\n\tself:AnimGuiObjects()\nend\n\nfunction methods:GetChatBar()\n\treturn self.ChatBar\nend\n\nfunction methods:RegisterChatBar(ChatBar)\n\tself.ChatBar = ChatBar\n\tself.ChatBar:CreateGuiObjects(self.GuiObjects.ChatBarParentFrame)\nend\n\nfunction methods:RegisterChannelsBar(ChannelsBar)\n\tself.ChannelsBar = ChannelsBar\n\tself.ChannelsBar:CreateGuiObjects(self.GuiObjects.ChannelsBarParentFrame)\nend\n\nfunction methods:RegisterMessageLogDisplay(MessageLogDisplay)\n\tself.MessageLogDisplay = MessageLogDisplay\n\tself.MessageLogDisplay.GuiObject.Parent = self.GuiObjects.ChatChannelParentFrame\nend\n\nfunction methods:AddChannel(channelName)\n\tif (self:GetChannel(channelName)) then\n\t\terror(\"Channel '\" .. channelName .. \"' already exists!\")\n\t\treturn\n\tend\n\n\tlocal channel = moduleChatChannel.new(channelName, self.MessageLogDisplay)\n\tself.Channels[channelName:lower()] = channel\n\n\tchannel:SetActive(false)\n\n\tlocal tab = self.ChannelsBar:AddChannelTab(channelName)\n\ttab.NameTag.MouseButton1Click:connect(function()\n\t\tself:SwitchCurrentChannel(channelName)\n\tend)\n\n\tchannel:RegisterChannelTab(tab)\n\n\treturn channel\nend\n\nfunction methods:GetFirstChannel()\n\t--// Channels are not indexed numerically, so this function is necessary.\n\t--// Grabs and returns the first channel it happens to, or nil if none exist.\n\tfor i, v in pairs(self.Channels) do\n\t\treturn v\n\tend\n\treturn nil\nend\n\nfunction methods:RemoveChannel(channelName)\n\tif (not self:GetChannel(channelName)) then\n\t\terror(\"Channel '\" .. channelName .. \"' does not exist!\")\n\tend\n\n\tlocal indexName = channelName:lower()\n\n\tlocal needsChannelSwitch = false\n\tif (self.Channels[indexName] == self:GetCurrentChannel()) then\n\t\tneedsChannelSwitch = true\n\n\t\tself:SwitchCurrentChannel(nil)\n\tend\n\n\tself.Channels[indexName]:Destroy()\n\tself.Channels[indexName] = nil\n\n\tself.ChannelsBar:RemoveChannelTab(channelName)\n\n\tif (needsChannelSwitch) then\n\t\tlocal generalChannelExists = (self:GetChannel(ChatSettings.GeneralChannelName) ~= nil)\n\t\tlocal removingGeneralChannel = (indexName == ChatSettings.GeneralChannelName:lower())\n\n\t\tlocal targetSwitchChannel = nil\n\n\t\tif (generalChannelExists and not removingGeneralChannel) then\n\t\t\ttargetSwitchChannel = ChatSettings.GeneralChannelName\n\t\telse\n\t\t\tlocal firstChannel = self:GetFirstChannel()\n\t\t\ttargetSwitchChannel = (firstChannel and firstChannel.Name or nil)\n\t\tend\n\n\t\tself:SwitchCurrentChannel(targetSwitchChannel)\n\tend\n\n\tif not ChatSettings.ShowChannelsBar then\n\t\tif self.ChatBar.TargetChannel == channelName then\n\t\t\tself.ChatBar:SetChannelTarget(ChatSettings.GeneralChannelName)\n\t\tend\n\tend\nend\n\nfunction methods:GetChannel(channelName)\n\treturn channelName and self.Channels[channelName:lower()] or nil\nend\n\nfunction methods:GetTargetMessageChannel()\n\tif (not ChatSettings.ShowChannelsBar) then\n\t\treturn self.ChatBar.TargetChannel\n\telse\n\t\tlocal curChannel = self:GetCurrentChannel()\n\t\treturn curChannel and curChannel.Name\n\tend\nend\n\nfunction methods:GetCurrentChannel()\n\treturn self.CurrentChannel\nend\n\nfunction methods:SwitchCurrentChannel(channelName)\n\tif (not ChatSettings.ShowChannelsBar) then\n\t\tlocal targ = self:GetChannel(channelName)\n\t\tif (targ) then\n\t\t\tself.ChatBar:SetChannelTarget(targ.Name)\n\t\tend\n\n\t\tchannelName = ChatSettings.GeneralChannelName\n\tend\n\n\tlocal cur = self:GetCurrentChannel()\n\tlocal new = self:GetChannel(channelName)\n\tif new == nil then\n\t\terror(string.format(\"Channel '%s' does not exist.\", channelName))\n\tend\n\n\tif (new ~= cur) then\n\t\tif (cur) then\n\t\t\tcur:SetActive(false)\n\t\t\tlocal tab = self.ChannelsBar:GetChannelTab(cur.Name)\n\t\t\ttab:SetActive(false)\n\t\tend\n\n\t\tif (new) then\n\t\t\tnew:SetActive(true)\n\t\t\tlocal tab = self.ChannelsBar:GetChannelTab(new.Name)\n\t\t\ttab:SetActive(true)\n\t\tend\n\n\t\tself.CurrentChannel = new\n\tend\n\nend\n\nfunction methods:UpdateFrameVisibility()\n\tself.GuiObject.Visible = (self.Visible and self.CoreGuiEnabled)\nend\n\nfunction methods:GetVisible()\n\treturn self.Visible\nend\n\nfunction methods:SetVisible(visible)\n\tself.Visible = visible\n\tself:UpdateFrameVisibility()\nend\n\nfunction methods:GetCoreGuiEnabled()\n\treturn self.CoreGuiEnabled\nend\n\nfunction methods:SetCoreGuiEnabled(enabled)\n\tself.CoreGuiEnabled = enabled\n\tself:UpdateFrameVisibility()\nend\n\nfunction methods:EnableResizable()\n\tself.GuiObjects.ChatResizerFrame.Active = true\nend\n\nfunction methods:DisableResizable()\n\tself.GuiObjects.ChatResizerFrame.Active = false\nend\n\nfunction methods:FadeOutBackground(duration)\n\tself.ChannelsBar:FadeOutBackground(duration)\n\tself.MessageLogDisplay:FadeOutBackground(duration)\n\tself.ChatBar:FadeOutBackground(duration)\n\n\tself.AnimParams.Background_TargetTransparency = 1\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeInBackground(duration)\n\tself.ChannelsBar:FadeInBackground(duration)\n\tself.MessageLogDisplay:FadeInBackground(duration)\n\tself.ChatBar:FadeInBackground(duration)\n\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeOutText(duration)\n\tself.MessageLogDisplay:FadeOutText(duration)\n\tself.ChannelsBar:FadeOutText(duration)\nend\n\nfunction methods:FadeInText(duration)\n\tself.MessageLogDisplay:FadeInText(duration)\n\tself.ChannelsBar:FadeInText(duration)\nend\n\nfunction methods:AnimGuiObjects()\n\tself.GuiObjects.ChatChannelParentFrame.BackgroundTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.GuiObjects.ChatResizerFrame.BackgroundTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.GuiObjects.ResizeIcon.ImageTransparency = self.AnimParams.Background_CurrentTransparency\nend\n\nfunction methods:InitializeAnimParams()\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_CurrentTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(0)\nend\n\nfunction methods:Update(dtScale)\n\tself.ChatBar:Update(dtScale)\n\tself.ChannelsBar:Update(dtScale)\n\tself.MessageLogDisplay:Update(dtScale)\n\n\tself.AnimParams.Background_CurrentTransparency = CurveUtil:Expt(\n\t\t\tself.AnimParams.Background_CurrentTransparency,\n\t\t\tself.AnimParams.Background_TargetTransparency,\n\t\t\tself.AnimParams.Background_NormalizedExptValue,\n\t\t\tdtScale\n\t)\n\n\tself:AnimGuiObjects()\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.GuiObject = nil\n\tobj.GuiObjects = {}\n\n\tobj.ChatBar = nil\n\tobj.ChannelsBar = nil\n\tobj.MessageLogDisplay = nil\n\n\tobj.Channels = {}\n\tobj.CurrentChannel = nil\n\n\tobj.Visible = true\n\tobj.CoreGuiEnabled = true\n\n\tobj.AnimParams = {}\n\n\tobj:InitializeAnimParams()\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/Chat/ChatScript/ChatMain/CommandProcessor.lua",
    "content": "--\t// FileName: ProcessCommands.lua\n--\t// Written by: TheGamer101\n--\t// Description: Module for processing commands using the client CommandModules\n\nlocal module = {}\nlocal methods = {}\nmethods.__index = methods\n\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal commandModules = clientChatModules:WaitForChild(\"CommandModules\")\nlocal commandUtil = require(commandModules:WaitForChild(\"Util\"))\nlocal modulesFolder = script.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\n\nfunction methods:SetupCommandProcessors()\n\tlocal commands = commandModules:GetChildren()\n\tfor i = 1, #commands do\n\t\tif commands[i]:IsA(\"ModuleScript\") then\n\t\t\tif commands[i].Name ~= \"Util\" then\n\t\t\t\tlocal commandProcessor = require(commands[i])\n\t\t\t\tlocal processorType = commandProcessor[commandUtil.KEY_COMMAND_PROCESSOR_TYPE]\n\t\t\t\tlocal processorFunction = commandProcessor[commandUtil.KEY_PROCESSOR_FUNCTION]\n\t\t\t\tif processorType == commandUtil.IN_PROGRESS_MESSAGE_PROCESSOR then\n\t\t\t\t\ttable.insert(self.InProgressMessageProcessors, processorFunction)\n\t\t\t\telseif processorType == commandUtil.COMPLETED_MESSAGE_PROCESSOR then\n\t\t\t\t\ttable.insert(self.CompletedMessageProcessors, processorFunction)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction methods:ProcessCompletedChatMessage(message, ChatWindow)\n\tfor i = 1, #self.CompletedMessageProcessors do\n\t\tlocal processedCommand = self.CompletedMessageProcessors[i](message, ChatWindow, ChatSettings)\n\t\tif processedCommand then\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\nfunction methods:ProcessInProgressChatMessage(message, ChatWindow, ChatBar)\n\tfor i = 1, #self.InProgressMessageProcessors do\n\t\tlocal customState = self.InProgressMessageProcessors[i](message, ChatWindow, ChatBar, ChatSettings)\n\t\tif customState then\n\t\t\treturn customState\n\t\tend\n\tend\n\treturn nil\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.CompletedMessageProcessors = {}\n\tobj.InProgressMessageProcessors = {}\n\n\tobj:SetupCommandProcessors()\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/Chat/ChatScript/ChatMain/CurveUtil.lua",
    "content": "local CurveUtil = {\t}\nlocal DEFAULT_THRESHOLD = 0.01\n\nfunction CurveUtil:Expt(start, to, pct, dt_scale)\n\tif math.abs(to - start) < DEFAULT_THRESHOLD then\n\t\treturn to\n\tend\n\n\tlocal y = CurveUtil:Expty(start,to,pct,dt_scale)\n\n\t--rtv = start + (to - start) * timescaled_friction--\n\tlocal delta = (to - start) * y\n\treturn start + delta\nend\n\nfunction CurveUtil:Expty(start, to, pct, dt_scale)\n\t--y = e ^ (-a * timescale)--\n\tlocal friction = 1 - pct\n\tlocal a = -math.log(friction)\n\treturn 1 - math.exp(-a * dt_scale)\nend\n\nfunction CurveUtil:Sign(val)\n\tif val > 0 then\n\t\treturn 1\n\telseif val < 0 then\n\t\treturn -1\n\telse\n\t\treturn 0\n\tend\nend\n\nfunction CurveUtil:BezierValForT(p0, p1, p2, p3, t)\n\tlocal cp0 = (1 - t) * (1 - t) * (1 - t)\n\tlocal cp1 = 3 * t * (1-t)*(1-t)\n\tlocal cp2 = 3 * t * t * (1 - t)\n\tlocal cp3 = t * t * t\n\treturn cp0 * p0 + cp1 * p1 + cp2 * p2 + cp3 * p3\nend\n\nCurveUtil._BezierPt2ForT = { x = 0; y = 0 }\nfunction CurveUtil:BezierPt2ForT(\n\tp0x, p0y,\n\tp1x, p1y,\n\tp2x, p2y,\n\tp3x, p3y,\n\tt)\n\n\tCurveUtil._BezierPt2ForT.x = CurveUtil:BezierValForT(p0x,p1x,p2x,p3x,t)\n\tCurveUtil._BezierPt2ForT.y = CurveUtil:BezierValForT(p0y,p1y,p2y,p3y,t)\n\treturn CurveUtil._BezierPt2ForT\nend\n\nfunction CurveUtil:YForPointOf2PtLine(pt1, pt2, x)\n\t--(y - y1\u0010)/(x - x1) = m--\n\tlocal m = (pt1.y - pt2.y) / (pt1.x - pt2.x)\n\t--y - mx = b--\n\tlocal b = pt1.y - m * pt1.x\n\treturn m * x + b\nend\n\nfunction CurveUtil:DeltaTimeToTimescale(s_frame_delta_time)\n\treturn s_frame_delta_time / (1.0 / 60.0)\nend\n\nfunction CurveUtil:SecondsToTick(sec)\n\treturn (1 / 60.0) / sec\nend\n\nfunction CurveUtil:ExptValueInSeconds(threshold, start, seconds)\n\t\treturn 1 - math.pow((threshold / start), 1 / (60.0 * seconds))\nend\n\nfunction CurveUtil:NormalizedDefaultExptValueInSeconds(seconds)\n\t\treturn self:ExptValueInSeconds(DEFAULT_THRESHOLD, 1, seconds)\nend\n\nreturn CurveUtil\n"
  },
  {
    "path": "src/Chat/ChatScript/ChatMain/MessageLabelCreator.lua",
    "content": "--\t// FileName: MessageLabelCreator.lua\n--\t// Written by: Xsitsu\n--\t// Description: Module to handle taking text and creating stylized GUI objects for display in ChatWindow.\n\nlocal OBJECT_POOL_SIZE = 50\n\nlocal module = {}\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal messageCreatorModules = clientChatModules:WaitForChild(\"MessageCreatorModules\")\nlocal messageCreatorUtil = require(messageCreatorModules:WaitForChild(\"Util\"))\nlocal modulesFolder = script.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal moduleObjectPool = require(modulesFolder:WaitForChild(\"ObjectPool\"))\nlocal MessageSender = require(modulesFolder:WaitForChild(\"MessageSender\"))\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\n-- merge properties on both table to target\nfunction mergeProps(source, target)\n\tif not source then return end\n\tfor prop, value in pairs(source) do\n\t\ttarget[prop] = value\n\tend\nend\n\nfunction ReturnToObjectPoolRecursive(instance, objectPool)\n\tlocal children = instance:GetChildren()\n\tfor i = 1, #children do\n\t\tReturnToObjectPoolRecursive(children[i], objectPool)\n\tend\n\tinstance.Parent = nil\n\tobjectPool:ReturnInstance(instance)\nend\n\nfunction GetMessageCreators()\n\tlocal typeToFunction = {}\n\tlocal creators = messageCreatorModules:GetChildren()\n\tfor i = 1, #creators do\n\t\tif creators[i]:IsA(\"ModuleScript\") then\n\t\t\tif creators[i].Name ~= \"Util\" then\n\t\t\t\tlocal creator = require(creators[i])\n\t\t\t\ttypeToFunction[creator[messageCreatorUtil.KEY_MESSAGE_TYPE]] = creator[messageCreatorUtil.KEY_CREATOR_FUNCTION]\n\t\t\tend\n\t\tend\n\tend\n\treturn typeToFunction\nend\n\nfunction methods:WrapIntoMessageObject(messageData, createdMessageObject)\n\tlocal BaseFrame = createdMessageObject[messageCreatorUtil.KEY_BASE_FRAME]\n\tlocal BaseMessage = nil\n\tif messageCreatorUtil.KEY_BASE_MESSAGE then\n\t\tBaseMessage = createdMessageObject[messageCreatorUtil.KEY_BASE_MESSAGE]\n\tend\n\tlocal UpdateTextFunction = createdMessageObject[messageCreatorUtil.KEY_UPDATE_TEXT_FUNC]\n\tlocal GetHeightFunction = createdMessageObject[messageCreatorUtil.KEY_GET_HEIGHT]\n\tlocal FadeInFunction = createdMessageObject[messageCreatorUtil.KEY_FADE_IN]\n\tlocal FadeOutFunction = createdMessageObject[messageCreatorUtil.KEY_FADE_OUT]\n\tlocal UpdateAnimFunction = createdMessageObject[messageCreatorUtil.KEY_UPDATE_ANIMATION]\n\n\tlocal obj = {}\n\n\tobj.ID = messageData.ID\n\tobj.BaseFrame = BaseFrame\n\tobj.BaseMessage = BaseMessage\n\tobj.UpdateTextFunction = UpdateTextFunction or function() warn(\"NO MESSAGE RESIZE FUNCTION\") end\n\tobj.GetHeightFunction = GetHeightFunction\n\tobj.FadeInFunction = FadeInFunction\n\tobj.FadeOutFunction = FadeOutFunction\n\tobj.UpdateAnimFunction = UpdateAnimFunction\n\tobj.ObjectPool = self.ObjectPool\n\tobj.Destroyed = false\n\n\tfunction obj:Destroy()\n\t\tReturnToObjectPoolRecursive(self.BaseFrame, self.ObjectPool)\n\t\tself.Destroyed = true\n\tend\n\n\treturn obj\nend\n\nfunction methods:CreateMessageLabel(messageData, currentChannelName)\n\n\tmessageData.Channel = currentChannelName\n\tlocal extraDeveloperFormatTable\n\tpcall(function()\n\t\textraDeveloperFormatTable = Chat:InvokeChatCallback(Enum.ChatCallbackType.OnClientFormattingMessage, messageData)\n\tend)\n\tmessageData.ExtraData = messageData.ExtraData or {}\n\tmergeProps(extraDeveloperFormatTable, messageData.ExtraData)\n\n\tlocal messageType = messageData.MessageType\n\tif self.MessageCreators[messageType] then\n\t\tlocal createdMessageObject = self.MessageCreators[messageType](messageData, currentChannelName)\n\t\tif createdMessageObject then\n\t\t\treturn self:WrapIntoMessageObject(messageData, createdMessageObject)\n\t\tend\n\telseif self.DefaultCreatorType then\n\t\tlocal createdMessageObject = self.MessageCreators[self.DefaultCreatorType](messageData, currentChannelName)\n\t\tif createdMessageObject then\n\t\t\treturn self:WrapIntoMessageObject(messageData, createdMessageObject)\n\t\tend\n\telse\n\t\terror(\"No message creator available for message type: \" ..messageType)\n\tend\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.ObjectPool = moduleObjectPool.new(OBJECT_POOL_SIZE)\n\tobj.MessageCreators = GetMessageCreators()\n\tobj.DefaultCreatorType = messageCreatorUtil.DEFAULT_MESSAGE_CREATOR\n\n\tmessageCreatorUtil:RegisterObjectPool(obj.ObjectPool)\n\n\treturn obj\nend\n\nfunction module:GetStringTextBounds(text, font, textSize, sizeBounds)\n\treturn messageCreatorUtil:GetStringTextBounds(text, font, textSize, sizeBounds)\nend\n\nreturn module\n"
  },
  {
    "path": "src/Chat/ChatScript/ChatMain/MessageLogDisplay.lua",
    "content": "--\t// FileName: MessageLogDisplay.lua\n--\t// Written by: Xsitsu, TheGamer101\n--\t// Description: ChatChannel window for displaying messages.\n\nlocal module = {}\nmodule.ScrollBarThickness = 4\n\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal modulesFolder = script.Parent\nlocal moduleMessageLabelCreator = require(modulesFolder:WaitForChild(\"MessageLabelCreator\"))\nlocal CurveUtil = require(modulesFolder:WaitForChild(\"CurveUtil\"))\n\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\n\nlocal FlagFixChatMessageLogPerformance = false do\n\tlocal ok, value = pcall(function()\n\t\treturn UserSettings():IsUserFeatureEnabled(\"UserFixChatMessageLogPerformance\")\n\tend)\n\tif ok then\n\t\tFlagFixChatMessageLogPerformance = value\n\tend\nend\n\nlocal MessageLabelCreator = moduleMessageLabelCreator.new()\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nlocal function CreateGuiObjects()\n\tlocal BaseFrame = Instance.new(\"Frame\")\n\tBaseFrame.Selectable = false\n\tBaseFrame.Size = UDim2.new(1, 0, 1, 0)\n\tBaseFrame.BackgroundTransparency = 1\n\n\tlocal Scroller = Instance.new(\"ScrollingFrame\")\n\tScroller.Selectable = ChatSettings.GamepadNavigationEnabled\n\tScroller.Name = \"Scroller\"\n\tScroller.BackgroundTransparency = 1\n\tScroller.BorderSizePixel = 0\n\tScroller.Position = UDim2.new(0, 0, 0, 3)\n\tScroller.Size = UDim2.new(1, -4, 1, -6)\n\tScroller.CanvasSize = UDim2.new(0, 0, 0, 0)\n\tScroller.ScrollBarThickness = module.ScrollBarThickness\n\tScroller.Active = false\n\tScroller.Parent = BaseFrame\n\n\tlocal Layout\n\tif FlagFixChatMessageLogPerformance then\n\t\tLayout = Instance.new(\"UIListLayout\")\n\t\tLayout.SortOrder = Enum.SortOrder.LayoutOrder\n\t\tLayout.Parent = Scroller\n\tend\n\n\treturn BaseFrame, Scroller, Layout\nend\n\nfunction methods:Destroy()\n\tself.GuiObject:Destroy()\n\tself.Destroyed = true\nend\n\nfunction methods:SetActive(active)\n\tself.GuiObject.Visible = active\nend\n\nfunction methods:UpdateMessageFiltered(messageData)\n\tlocal messageObject = nil\n\tlocal searchIndex = 1\n\tlocal searchTable = self.MessageObjectLog\n\n\twhile (#searchTable >= searchIndex) do\n\t\tlocal obj = searchTable[searchIndex]\n\n\t\tif obj.ID == messageData.ID then\n\t\t\tmessageObject = obj\n\t\t\tbreak\n\t\tend\n\n\t\tsearchIndex = searchIndex + 1\n\tend\n\n\tif messageObject then\n\t\tlocal isScrolledDown = self:IsScrolledDown()\n\t\tmessageObject.UpdateTextFunction(messageData)\n\t\tif FlagFixChatMessageLogPerformance then\n\t\t\tself:PositionMessageLabelInWindow(messageObject, isScrolledDown)\n\t\telse\n\t\t\tself:ReorderAllMessages()\n\t\tend\n\tend\nend\n\nfunction methods:AddMessage(messageData)\n\tself:WaitUntilParentedCorrectly()\n\n\tlocal messageObject = MessageLabelCreator:CreateMessageLabel(messageData, self.CurrentChannelName)\n\tif messageObject == nil then\n\t\treturn\n\tend\n\n\ttable.insert(self.MessageObjectLog, messageObject)\n\tself:PositionMessageLabelInWindow(messageObject)\nend\n\nfunction methods:AddMessageAtIndex(messageData, index)\n\tlocal messageObject = MessageLabelCreator:CreateMessageLabel(messageData, self.CurrentChannelName)\n\tif messageObject == nil then\n\t\treturn\n\tend\n\n\ttable.insert(self.MessageObjectLog, index, messageObject)\n\n\tlocal wasScrolledToBottom = self:IsScrolledDown()\n\tself:ReorderAllMessages()\n\tif wasScrolledToBottom then\n\t\tself.Scroller.CanvasPosition = Vector2.new(0, math.max(0, self.Scroller.CanvasSize.Y.Offset - self.Scroller.AbsoluteSize.Y))\n\tend\nend\n\nfunction methods:RemoveLastMessage()\n\tself:WaitUntilParentedCorrectly()\n\n\tlocal lastMessage = self.MessageObjectLog[1]\n\t-- remove with FlagFixChatMessageLogPerformance\n\tlocal posOffset = UDim2.new(0, 0, 0, lastMessage.BaseFrame.AbsoluteSize.Y)\n\n\tlastMessage:Destroy()\n\ttable.remove(self.MessageObjectLog, 1)\n\n\tif not FlagFixChatMessageLogPerformance then\n\t\tfor i, messageObject in pairs(self.MessageObjectLog) do\n\t\t\tmessageObject.BaseFrame.Position = messageObject.BaseFrame.Position - posOffset\n\t\tend\n\n\t\tself.Scroller.CanvasSize = self.Scroller.CanvasSize - posOffset\n\tend\nend\n\nfunction methods:IsScrolledDown()\n\tlocal yCanvasSize = self.Scroller.CanvasSize.Y.Offset\n\tlocal yContainerSize = self.Scroller.AbsoluteWindowSize.Y\n\tlocal yScrolledPosition = self.Scroller.CanvasPosition.Y\n\n\tif FlagFixChatMessageLogPerformance then\n\t\treturn\n\t\t\tyCanvasSize < yContainerSize or\n\t\t\tyCanvasSize + yScrolledPosition >= yContainerSize - 5\n\telse\n\t\treturn (yCanvasSize < yContainerSize or\n\t\t\tyCanvasSize - yScrolledPosition <= yContainerSize + 5)\n\tend\nend\n\nfunction methods:PositionMessageLabelInWindow(messageObject, isScrolledDown)\n\tself:WaitUntilParentedCorrectly()\n\n\tlocal baseFrame = messageObject.BaseFrame\n\n\tif FlagFixChatMessageLogPerformance then\n\t\tif isScrolledDown == nil then\n\t\t\tisScrolledDown = self:IsScrolledDown()\n\t\tend\n\t\tbaseFrame.LayoutOrder = messageObject.ID\n\telse\n\t\tbaseFrame.Parent = self.Scroller\n\t\tbaseFrame.Position = UDim2.new(0, 0, 0, self.Scroller.CanvasSize.Y.Offset)\n\tend\n\n\tbaseFrame.Size = UDim2.new(1, 0, 0, messageObject.GetHeightFunction(self.Scroller.AbsoluteSize.X))\n\tif FlagFixChatMessageLogPerformance then\n\t\tbaseFrame.Parent = self.Scroller\n\tend\n\n\tif messageObject.BaseMessage then\n\t\tif FlagFixChatMessageLogPerformance then\n\t\t\tfor i = 1, 10 do\n\t\t\t\tif messageObject.BaseMessage.TextFits then\n\t\t\t\t\tbreak\n\t\t\t\tend\n\n\t\t\t\tlocal trySize = self.Scroller.AbsoluteSize.X - i\n\t\t\t\tbaseFrame.Size = UDim2.new(1, 0, 0, messageObject.GetHeightFunction(trySize))\n\t\t\tend\n\t\telse\n\t\t\tlocal trySize = self.Scroller.AbsoluteSize.X\n\t\t\tlocal minTrySize = math.min(self.Scroller.AbsoluteSize.X - 10, 0)\n\t\t\twhile not messageObject.BaseMessage.TextFits do\n\t\t\t\ttrySize = trySize - 1\n\t\t\t\tif trySize < minTrySize then\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\t\tbaseFrame.Size = UDim2.new(1, 0, 0, messageObject.GetHeightFunction(trySize))\n\t\t\tend\n\t\tend\n\tend\n\n\tif FlagFixChatMessageLogPerformance then\n\t\tif isScrolledDown then\n\t\t\tlocal scrollValue = self.Scroller.CanvasSize.Y.Offset - self.Scroller.AbsoluteSize.Y\n\t\t\tself.Scroller.CanvasPosition = Vector2.new(0, math.max(0, scrollValue))\n\t\tend\n\telse\n\t\tisScrolledDown = self:IsScrolledDown()\n\n\t\tlocal add = UDim2.new(0, 0, 0, baseFrame.Size.Y.Offset)\n\t\tself.Scroller.CanvasSize = self.Scroller.CanvasSize + add\n\n\t\tif isScrolledDown then\n\t\t\tself.Scroller.CanvasPosition = Vector2.new(0, math.max(0, self.Scroller.CanvasSize.Y.Offset - self.Scroller.AbsoluteSize.Y))\n\t\tend\n\tend\nend\n\nfunction methods:ReorderAllMessages()\n\tself:WaitUntilParentedCorrectly()\n\n\t--// Reordering / reparenting with a size less than 1 causes weird glitches to happen with scrolling as repositioning happens.\n\tif self.GuiObject.AbsoluteSize.Y < 1 then return end\n\n\tlocal oldCanvasPositon = self.Scroller.CanvasPosition\n\tlocal wasScrolledDown = self:IsScrolledDown()\n\n\tself.Scroller.CanvasSize = UDim2.new(0, 0, 0, 0)\n\tfor i, messageObject in pairs(self.MessageObjectLog) do\n\t\tself:PositionMessageLabelInWindow(messageObject)\n\tend\n\n\tif not wasScrolledDown then\n\t\tself.Scroller.CanvasPosition = oldCanvasPositon\n\tend\nend\n\nfunction methods:Clear()\n\tfor i, v in pairs(self.MessageObjectLog) do\n\t\tv:Destroy()\n\tend\n\tself.MessageObjectLog = {}\n\n\tif not FlagFixChatMessageLogPerformance then\n\t\tself.Scroller.CanvasSize = UDim2.new(0, 0, 0, 0)\n\tend\nend\n\nfunction methods:SetCurrentChannelName(name)\n\tself.CurrentChannelName = name\nend\n\nfunction methods:FadeOutBackground(duration)\n\t--// Do nothing\nend\n\nfunction methods:FadeInBackground(duration)\n\t--// Do nothing\nend\n\nfunction methods:FadeOutText(duration)\n\tfor i = 1, #self.MessageObjectLog do\n\t\tif self.MessageObjectLog[i].FadeOutFunction then\n\t\t\tself.MessageObjectLog[i].FadeOutFunction(duration, CurveUtil)\n\t\tend\n\tend\nend\n\nfunction methods:FadeInText(duration)\n\tfor i = 1, #self.MessageObjectLog do\n\t\tif self.MessageObjectLog[i].FadeInFunction then\n\t\t\tself.MessageObjectLog[i].FadeInFunction(duration, CurveUtil)\n\t\tend\n\tend\nend\n\nfunction methods:Update(dtScale)\n\tfor i = 1, #self.MessageObjectLog do\n\t\tif self.MessageObjectLog[i].UpdateAnimFunction then\n\t\t\tself.MessageObjectLog[i].UpdateAnimFunction(dtScale, CurveUtil)\n\t\tend\n\tend\nend\n\n--// ToDo: Move to common modules\nfunction methods:WaitUntilParentedCorrectly()\n\twhile (not self.GuiObject:IsDescendantOf(game:GetService(\"Players\").LocalPlayer)) do\n\t\tself.GuiObject.AncestryChanged:wait()\n\tend\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\tobj.Destroyed = false\n\n\tlocal BaseFrame, Scroller, Layout = CreateGuiObjects()\n\tobj.GuiObject = BaseFrame\n\tobj.Scroller = Scroller\n\tobj.Layout = Layout\n\n\tobj.MessageObjectLog = {}\n\n\tobj.Name = \"MessageLogDisplay\"\n\tobj.GuiObject.Name = \"Frame_\" .. obj.Name\n\n\tobj.CurrentChannelName = \"\"\n\n\tobj.GuiObject:GetPropertyChangedSignal(\"AbsoluteSize\"):Connect(function()\n\t\tspawn(function() obj:ReorderAllMessages() end)\n\tend)\n\n\tif FlagFixChatMessageLogPerformance then\n\t\tlocal wasScrolledDown = true\n\n\t\tobj.Layout:GetPropertyChangedSignal(\"AbsoluteContentSize\"):Connect(function()\n\t\t\tlocal size = obj.Layout.AbsoluteContentSize\n\t\t\tobj.Scroller.CanvasSize = UDim2.new(0, 0, 0, size.Y)\n\t\t\tif wasScrolledDown then\n\t\t\t\tlocal windowSize = obj.Scroller.AbsoluteWindowSize\n\t\t\t\tobj.Scroller.CanvasPosition = Vector2.new(0, size.Y - windowSize.Y)\n\t\t\tend\n\t\tend)\n\n\t\tobj.Scroller:GetPropertyChangedSignal(\"CanvasPosition\"):Connect(function()\n\t\t\twasScrolledDown = obj:IsScrolledDown()\n\t\tend)\n\tend\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/Chat/ChatScript/ChatMain/MessageSender.lua",
    "content": "--\t// FileName: MessageSender.lua\n--\t// Written by: Xsitsu\n--\t// Description: Module to centralize sending message functionality.\n\nlocal module = {}\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal modulesFolder = script.Parent\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:SendMessage(message, toChannel)\n\tself.SayMessageRequest:FireServer(message, toChannel)\nend\n\nfunction methods:RegisterSayMessageFunction(func)\n\tself.SayMessageRequest = func\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\tobj.SayMessageRequest = nil\n\n\treturn obj\nend\n\nreturn module.new()\n"
  },
  {
    "path": "src/Chat/ChatScript/ChatMain/ObjectPool.lua",
    "content": "--\t// FileName: ObjectPool.lua\n--\t// Written by: TheGamer101\n--\t// Description: An object pool class used to avoid unnecessarily instantiating Instances.\n\nlocal module = {}\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal modulesFolder = script.Parent\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:GetInstance(className)\n  if self.InstancePoolsByClass[className] == nil then\n    self.InstancePoolsByClass[className] = {}\n  end\n  local availableInstances = #self.InstancePoolsByClass[className]\n  if availableInstances > 0 then\n    local instance = self.InstancePoolsByClass[className][availableInstances]\n    table.remove(self.InstancePoolsByClass[className])\n    return instance\n  end\n  return Instance.new(className)\nend\n\nfunction methods:ReturnInstance(instance)\n  if self.InstancePoolsByClass[instance.ClassName] == nil then\n    self.InstancePoolsByClass[instance.ClassName] = {}\n  end\n  if #self.InstancePoolsByClass[instance.ClassName] < self.PoolSizePerType then\n    table.insert(self.InstancePoolsByClass[instance.ClassName], instance)\n  else\n    instance:Destroy()\n  end\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new(poolSizePerType)\n\tlocal obj = setmetatable({}, methods)\n\tobj.InstancePoolsByClass = {}\n\tobj.Name = \"ObjectPool\"\n  obj.PoolSizePerType = poolSizePerType\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/Chat/ChatScript/ChatMain/init.lua",
    "content": "--\t// FileName: ChatMain.lua\n--\t// Written by: Xsitsu\n--\t// Description: Main module to handle initializing chat window UI and hooking up events to individual UI pieces.\n\nlocal moduleApiTable = {}\n\n--// This section of code waits until all of the necessary RemoteEvents are found in EventFolder.\n--// I have to do some weird stuff since people could potentially already have pre-existing\n--// things in a folder with the same name, and they may have different class types.\n--// I do the useEvents thing and set EventFolder to useEvents so I can have a pseudo folder that\n--// the rest of the code can interface with and have the guarantee that the RemoteEvents they want\n--// exist with their desired names.\n\nlocal FILTER_MESSAGE_TIMEOUT = 60\n\nlocal RunService = game:GetService(\"RunService\")\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal Chat = game:GetService(\"Chat\")\nlocal StarterGui = game:GetService(\"StarterGui\")\n\nlocal DefaultChatSystemChatEvents = ReplicatedStorage:WaitForChild(\"DefaultChatSystemChatEvents\")\nlocal EventFolder = ReplicatedStorage:WaitForChild(\"DefaultChatSystemChatEvents\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatConstants = require(clientChatModules:WaitForChild(\"ChatConstants\"))\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal messageCreatorModules = clientChatModules:WaitForChild(\"MessageCreatorModules\")\nlocal MessageCreatorUtil = require(messageCreatorModules:WaitForChild(\"Util\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal configuration = modules.load(\"configuration\")\n\nlocal numChildrenRemaining = 10 -- #waitChildren returns 0 because it's a dictionary\nlocal waitChildren =\n{\n\tOnNewMessage = \"RemoteEvent\",\n\tOnMessageDoneFiltering = \"RemoteEvent\",\n\tOnNewSystemMessage = \"RemoteEvent\",\n\tOnChannelJoined = \"RemoteEvent\",\n\tOnChannelLeft = \"RemoteEvent\",\n\tOnMuted = \"RemoteEvent\",\n\tOnUnmuted = \"RemoteEvent\",\n\tOnMainChannelSet = \"RemoteEvent\",\n\n\tSayMessageRequest = \"RemoteEvent\",\n\tGetInitDataRequest = \"RemoteFunction\",\n}\n-- waitChildren/EventFolder does not contain all the remote events, because the server version could be older than the client version.\n-- In that case it would not create the new events.\n-- These events are accessed directly from DefaultChatSystemChatEvents\n\nlocal useEvents = {}\n\nlocal FoundAllEventsEvent = Instance.new(\"BindableEvent\")\n\nfunction TryRemoveChildWithVerifyingIsCorrectType(child)\n\tif (waitChildren[child.Name] and child:IsA(waitChildren[child.Name])) then\n\t\twaitChildren[child.Name] = nil\n\t\tuseEvents[child.Name] = child\n\t\tnumChildrenRemaining = numChildrenRemaining - 1\n\tend\nend\n\nfor i, child in pairs(EventFolder:GetChildren()) do\n\tTryRemoveChildWithVerifyingIsCorrectType(child)\nend\n\nif (numChildrenRemaining > 0) then\n\tlocal con = EventFolder.ChildAdded:connect(function(child)\n\t\tTryRemoveChildWithVerifyingIsCorrectType(child)\n\t\tif (numChildrenRemaining < 1) then\n\t\t\tFoundAllEventsEvent:Fire()\n\t\tend\n\tend)\n\n\tFoundAllEventsEvent.Event:wait()\n\tcon:disconnect()\n\n\tFoundAllEventsEvent:Destroy()\nend\n\nEventFolder = useEvents\n\n\n\n--// Rest of code after waiting for correct events.\n\nlocal UserInputService = game:GetService(\"UserInputService\")\nlocal RunService = game:GetService(\"RunService\")\n\nlocal Players = game:GetService(\"Players\")\nlocal LocalPlayer = Players.LocalPlayer\n\nwhile not LocalPlayer do\n\tPlayers.ChildAdded:wait()\n\tLocalPlayer = Players.LocalPlayer\nend\n\nlocal canChat = true\n\nlocal ChatDisplayOrder = 6\nif ChatSettings.ScreenGuiDisplayOrder ~= nil then\n\tChatDisplayOrder = ChatSettings.ScreenGuiDisplayOrder\nend\n\nlocal PlayerGui = LocalPlayer:WaitForChild(\"PlayerGui\")\nlocal GuiParent = Instance.new(\"ScreenGui\")\nGuiParent.Name = \"Chat\"\nGuiParent.ResetOnSpawn = false\nGuiParent.DisplayOrder = ChatDisplayOrder\nGuiParent.Parent = PlayerGui\n\nlocal DidFirstChannelsLoads = false\n\nlocal modulesFolder = script\n\nlocal moduleChatWindow = require(modulesFolder:WaitForChild(\"ChatWindow\"))\nlocal moduleChatBar = require(modulesFolder:WaitForChild(\"ChatBar\"))\nlocal moduleChannelsBar = require(modulesFolder:WaitForChild(\"ChannelsBar\"))\nlocal moduleMessageLabelCreator = require(modulesFolder:WaitForChild(\"MessageLabelCreator\"))\nlocal moduleMessageLogDisplay = require(modulesFolder:WaitForChild(\"MessageLogDisplay\"))\nlocal moduleChatChannel = require(modulesFolder:WaitForChild(\"ChatChannel\"))\nlocal moduleCommandProcessor = require(modulesFolder:WaitForChild(\"CommandProcessor\"))\n\nlocal ChatWindow = moduleChatWindow.new()\nlocal ChannelsBar = moduleChannelsBar.new()\nlocal MessageLogDisplay = moduleMessageLogDisplay.new()\nlocal CommandProcessor = moduleCommandProcessor.new()\nlocal ChatBar = moduleChatBar.new(CommandProcessor, ChatWindow)\n\nChatWindow:CreateGuiObjects(GuiParent)\n\nChatWindow:RegisterChatBar(ChatBar)\nChatWindow:RegisterChannelsBar(ChannelsBar)\nChatWindow:RegisterMessageLogDisplay(MessageLogDisplay)\n\nMessageCreatorUtil:RegisterChatWindow(ChatWindow)\n\nlocal MessageSender = require(modulesFolder:WaitForChild(\"MessageSender\"))\nMessageSender:RegisterSayMessageFunction(EventFolder.SayMessageRequest)\n\n\n\nif (UserInputService.TouchEnabled) then\n\tChatBar:SetTextLabelText(ChatLocalization:Get(\"GameChat_ChatMain_ChatBarText\",'Tap here to chat'))\nelse\n\tChatBar:SetTextLabelText(ChatLocalization:Get(\"GameChat_ChatMain_ChatBarTextTouch\",'To chat click here or press \"/\" key'))\nend\n\nspawn(function()\n\tlocal CurveUtil = require(modulesFolder:WaitForChild(\"CurveUtil\"))\n\tlocal animationFps = ChatSettings.ChatAnimationFPS or 20.0\n\n\tlocal updateWaitTime = 1.0 / animationFps\n\tlocal lastTick = tick()\n\twhile true do\n\t\tlocal currentTick = tick()\n\t\tlocal tickDelta = currentTick - lastTick\n\t\tlocal dtScale = CurveUtil:DeltaTimeToTimescale(tickDelta)\n\n\t\tif dtScale ~= 0 then\n\t\t\tChatWindow:Update(dtScale)\n\t\tend\n\n\t\tlastTick = currentTick\n\t\twait(updateWaitTime)\n\tend\nend)\n\n\n\n\n--////////////////////////////////////////////////////////////////////////////////////////////\n--////////////////////////////////////////////////////////////// Code to do chat window fading\n--////////////////////////////////////////////////////////////////////////////////////////////\nfunction CheckIfPointIsInSquare(checkPos, topLeft, bottomRight)\n\treturn (topLeft.X <= checkPos.X and checkPos.X <= bottomRight.X and\n\t\ttopLeft.Y <= checkPos.Y and checkPos.Y <= bottomRight.Y)\nend\n\nlocal backgroundIsFaded = false\nlocal textIsFaded = false\nlocal lastTextFadeTime = 0\nlocal lastBackgroundFadeTime = 0\n\nlocal fadedChanged = Instance.new(\"BindableEvent\")\nlocal mouseStateChanged = Instance.new(\"BindableEvent\")\nlocal chatBarFocusChanged = Instance.new(\"BindableEvent\")\n\nfunction DoBackgroundFadeIn(setFadingTime)\n\tlastBackgroundFadeTime = tick()\n\tbackgroundIsFaded = false\n\tfadedChanged:Fire()\n\tChatWindow:FadeInBackground((setFadingTime or ChatSettings.ChatDefaultFadeDuration))\n\n\tlocal currentChannelObject = ChatWindow:GetCurrentChannel()\n\tif (currentChannelObject) then\n\n\t\tlocal Scroller = MessageLogDisplay.Scroller\n\t\tScroller.ScrollingEnabled = true\n\t\tScroller.ScrollBarThickness = moduleMessageLogDisplay.ScrollBarThickness\n\tend\nend\n\nfunction DoBackgroundFadeOut(setFadingTime)\n\tlastBackgroundFadeTime = tick()\n\tbackgroundIsFaded = true\n\tfadedChanged:Fire()\n\tChatWindow:FadeOutBackground((setFadingTime or ChatSettings.ChatDefaultFadeDuration))\n\n\tlocal currentChannelObject = ChatWindow:GetCurrentChannel()\n\tif (currentChannelObject) then\n\n\t\tlocal Scroller = MessageLogDisplay.Scroller\n\t\tScroller.ScrollingEnabled = false\n\t\tScroller.ScrollBarThickness = 0\n\tend\nend\n\nfunction DoTextFadeIn(setFadingTime)\n\tlastTextFadeTime = tick()\n\ttextIsFaded = false\n\tfadedChanged:Fire()\n\tChatWindow:FadeInText((setFadingTime or ChatSettings.ChatDefaultFadeDuration) * 0)\nend\n\nfunction DoTextFadeOut(setFadingTime)\n\tlastTextFadeTime = tick()\n\ttextIsFaded = true\n\tfadedChanged:Fire()\n\tChatWindow:FadeOutText((setFadingTime or ChatSettings.ChatDefaultFadeDuration))\nend\n\nfunction DoFadeInFromNewInformation()\n\tDoTextFadeIn()\n\tif ChatSettings.ChatShouldFadeInFromNewInformation then\n\t\tDoBackgroundFadeIn()\n\tend\nend\n\nfunction InstantFadeIn()\n\tDoBackgroundFadeIn(0)\n\tDoTextFadeIn(0)\nend\n\nfunction InstantFadeOut()\n\tDoBackgroundFadeOut(0)\n\tDoTextFadeOut(0)\nend\n\nlocal mouseIsInWindow = nil\nfunction UpdateFadingForMouseState(mouseState)\n\tmouseIsInWindow = mouseState\n\n\tmouseStateChanged:Fire()\n\n\tif (ChatBar:IsFocused()) then return end\n\n\tif (mouseState) then\n\t\tDoBackgroundFadeIn()\n\t\tDoTextFadeIn()\n\telse\n\t\tDoBackgroundFadeIn()\n\tend\nend\n\n\nspawn(function()\n\twhile true do\n\t\tRunService.RenderStepped:wait()\n\n\t\twhile (mouseIsInWindow or ChatBar:IsFocused()) do\n\t\t\tif (mouseIsInWindow) then\n\t\t\t\tmouseStateChanged.Event:wait()\n\t\t\tend\n\t\t\tif (ChatBar:IsFocused()) then\n\t\t\t\tchatBarFocusChanged.Event:wait()\n\t\t\tend\n\t\tend\n\n\t\tif (not backgroundIsFaded) then\n\t\t\tlocal timeDiff = tick() - lastBackgroundFadeTime\n\t\t\tif (timeDiff > ChatSettings.ChatWindowBackgroundFadeOutTime) then\n\t\t\t\tDoBackgroundFadeOut()\n\t\t\tend\n\n\t\telseif (not textIsFaded) then\n\t\t\tlocal timeDiff = tick() - lastTextFadeTime\n\t\t\tif (timeDiff > ChatSettings.ChatWindowTextFadeOutTime) then\n\t\t\t\tDoTextFadeOut()\n\t\t\tend\n\n\t\telse\n\t\t\tfadedChanged.Event:wait()\n\n\t\tend\n\n\tend\nend)\n\nfunction getClassicChatEnabled()\n\tif ChatSettings.ClassicChatEnabled ~= nil then\n\t\treturn ChatSettings.ClassicChatEnabled\n\tend\n\treturn Players.ClassicChat\nend\n\nfunction getBubbleChatEnabled()\n\tif ChatSettings.BubbleChatEnabled ~= nil then\n\t\treturn ChatSettings.BubbleChatEnabled\n\tend\n\treturn Players.BubbleChat\nend\n\nfunction bubbleChatOnly()\n \treturn not getClassicChatEnabled() and getBubbleChatEnabled()\nend\n\nfunction UpdateMousePosition(mousePos)\n\tif not (moduleApiTable.Visible and moduleApiTable.IsCoreGuiEnabled and (moduleApiTable.TopbarEnabled or ChatSettings.ChatOnWithTopBarOff)) then return end\n\n\tif bubbleChatOnly() then\n\t\treturn\n\tend\n\n\tlocal windowPos = ChatWindow.GuiObject.AbsolutePosition\n\tlocal windowSize = ChatWindow.GuiObject.AbsoluteSize\n\n\tlocal newMouseState = CheckIfPointIsInSquare(mousePos, windowPos, windowPos + windowSize)\n\tif (newMouseState ~= mouseIsInWindow) then\n\t\tUpdateFadingForMouseState(newMouseState)\n\tend\nend\n\nUserInputService.InputChanged:connect(function(inputObject)\n\tif (inputObject.UserInputType == Enum.UserInputType.MouseMovement) then\n\t\tlocal mousePos = Vector2.new(inputObject.Position.X, inputObject.Position.Y)\n\t\tUpdateMousePosition(mousePos)\n\tend\nend)\n\nUserInputService.TouchTap:connect(function(tapPos, gameProcessedEvent)\n\tUpdateMousePosition(tapPos[1])\nend)\n\nUserInputService.TouchMoved:connect(function(inputObject, gameProcessedEvent)\n\tlocal tapPos = Vector2.new(inputObject.Position.X, inputObject.Position.Y)\n\tUpdateMousePosition(tapPos)\nend)\n--[[\n\tUserInputService.Changed:connect(function(prop)\n\t\tif prop == \"MouseBehavior\" then\n\t\t\tif UserInputService.MouseBehavior == Enum.MouseBehavior.LockCenter then\n\t\t\t\tlocal windowPos = ChatWindow.GuiObject.AbsolutePosition\n\t\t\t\tlocal windowSize = ChatWindow.GuiObject.AbsoluteSize\n\t\t\t\tlocal screenSize = GuiParent.AbsoluteSize\n\t\n\t\t\t\tlocal centerScreenIsInWindow = CheckIfPointIsInSquare(screenSize/2, windowPos, windowPos + windowSize)\n\t\t\t\tif centerScreenIsInWindow then\n\t\t\t\t\tUserInputService.MouseBehavior = Enum.MouseBehavior.Default\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\n\n]]\n--// Start and stop fading sequences / timers\nUpdateFadingForMouseState(true)\nUpdateFadingForMouseState(false)\n\n\n--////////////////////////////////////////////////////////////////////////////////////////////\n--///////////// Code to talk to topbar and maintain set/get core backwards compatibility stuff\n--////////////////////////////////////////////////////////////////////////////////////////////\nlocal Util = {}\ndo\n\tfunction Util.Signal()\n\t\tlocal sig = {}\n\n\t\tlocal mSignaler = Instance.new('BindableEvent')\n\n\t\tlocal mArgData = nil\n\t\tlocal mArgDataCount = nil\n\n\t\tfunction sig:fire(...)\n\t\t\tmArgData = {...}\n\t\t\tmArgDataCount = select('#', ...)\n\t\t\tmSignaler:Fire()\n\t\tend\n\n\t\tfunction sig:connect(f)\n\t\t\tif not f then error(\"connect(nil)\", 2) end\n\t\t\treturn mSignaler.Event:connect(function()\n\t\t\t\tf(unpack(mArgData, 1, mArgDataCount))\n\t\t\tend)\n\t\tend\n\n\t\tfunction sig:wait()\n\t\t\tmSignaler.Event:wait()\n\t\t\tassert(mArgData, \"Missing arg data, likely due to :TweenSize/Position corrupting threadrefs.\")\n\t\t\treturn unpack(mArgData, 1, mArgDataCount)\n\t\tend\n\n\t\treturn sig\n\tend\nend\n\n\nfunction SetVisibility(val)\n\tChatWindow:SetVisible(val)\n\tmoduleApiTable.VisibilityStateChanged:fire(val)\n\tmoduleApiTable.Visible = val\n\n\tif (moduleApiTable.IsCoreGuiEnabled) then\n\t\tif (val) then\n\t\t\tInstantFadeIn()\n\t\telse\n\t\t\tInstantFadeOut()\n\t\tend\n\tend\nend\n\ndo\n\tmoduleApiTable.TopbarEnabled = true\n\tmoduleApiTable.MessageCount = 0\n\tmoduleApiTable.Visible = true\n\tmoduleApiTable.IsCoreGuiEnabled = true\n\n\tfunction moduleApiTable:ToggleVisibility()\n\t\tSetVisibility(not ChatWindow:GetVisible())\n\tend\n\n\tfunction moduleApiTable:SetVisible(visible)\n\t\tif (ChatWindow:GetVisible() ~= visible) then\n\t\t\tSetVisibility(visible)\n\t\tend\n\tend\n\n\tfunction moduleApiTable:FocusChatBar()\n\t\tChatBar:CaptureFocus()\n\tend\n\n\tfunction moduleApiTable:EnterWhisperState(player)\n\t\tChatBar:EnterWhisperState(player)\n\tend\n\n\tfunction moduleApiTable:GetVisibility()\n\t\treturn ChatWindow:GetVisible()\n\tend\n\n\tfunction moduleApiTable:GetMessageCount()\n\t\treturn self.MessageCount\n\tend\n\n\tfunction moduleApiTable:TopbarEnabledChanged(enabled)\n\t\tself.TopbarEnabled = enabled\n\t\tself.CoreGuiEnabled:fire(game:GetService(\"StarterGui\"):GetCoreGuiEnabled(Enum.CoreGuiType.Chat))\n\tend\n\n\tfunction moduleApiTable:IsFocused(useWasFocused)\n\t\treturn ChatBar:IsFocused()\n\tend\n\n\tmoduleApiTable.ChatBarFocusChanged = Util.Signal()\n\tmoduleApiTable.VisibilityStateChanged = Util.Signal()\n\tmoduleApiTable.MessagesChanged = Util.Signal()\n\n\n\tmoduleApiTable.MessagePosted = Util.Signal()\n\tmoduleApiTable.CoreGuiEnabled = Util.Signal()\n\n\tmoduleApiTable.ChatMakeSystemMessageEvent = Util.Signal()\n\tmoduleApiTable.ChatWindowPositionEvent = Util.Signal()\n\tmoduleApiTable.ChatWindowSizeEvent = Util.Signal()\n\tmoduleApiTable.ChatBarDisabledEvent = Util.Signal()\n\n\n\tfunction moduleApiTable:fChatWindowPosition()\n\t\treturn ChatWindow.GuiObject.Position\n\tend\n\n\tfunction moduleApiTable:fChatWindowSize()\n\t\treturn ChatWindow.GuiObject.Size\n\tend\n\n\tfunction moduleApiTable:fChatBarDisabled()\n\t\treturn not ChatBar:GetEnabled()\n\tend\n\n\n\n\tfunction moduleApiTable:SpecialKeyPressed(key, modifiers)\n\t\tif (key == Enum.SpecialKey.ChatHotkey) then\n\t\t\tif canChat then\n\t\t\t\tDoChatBarFocus()\n\t\t\tend\n\t\tend\n\tend\nend\n\nmoduleApiTable.CoreGuiEnabled:connect(function(enabled)\n\tmoduleApiTable.IsCoreGuiEnabled = enabled\n\n\tenabled = enabled and (moduleApiTable.TopbarEnabled or ChatSettings.ChatOnWithTopBarOff)\n\n\tChatWindow:SetCoreGuiEnabled(enabled)\n\n\tif (not enabled) then\n\t\tChatBar:ReleaseFocus()\n\t\tInstantFadeOut()\n\telse\n\t\tInstantFadeIn()\n\tend\nend)\n\nfunction trimTrailingSpaces(str)\n\tlocal lastSpace = #str\n\twhile lastSpace > 0 do\n\t\t--- The pattern ^%s matches whitespace at the start of the string. (Starting from lastSpace)\n\t\tif str:find(\"^%s\", lastSpace) then\n\t\t\tlastSpace = lastSpace - 1\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\treturn str:sub(1, lastSpace)\nend\n\nmoduleApiTable.ChatMakeSystemMessageEvent:connect(function(valueTable)\n\tif (valueTable[\"Text\"] and type(valueTable[\"Text\"]) == \"string\") then\n\t\twhile (not DidFirstChannelsLoads) do wait() end\n\n\t\tlocal channel = ChatSettings.GeneralChannelName\n\t\tlocal channelObj = ChatWindow:GetChannel(channel)\n\n\t\tif (channelObj) then\n\t\t\tlocal messageObject = {\n\t\t\t\tID = -1,\n\t\t\t\tFromSpeaker = nil,\n\t\t\t\tSpeakerUserId = 0,\n\t\t\t\tOriginalChannel = channel,\n\t\t\t\tIsFiltered = true,\n\t\t\t\tMessageLength = string.len(valueTable.Text),\n\t\t\t\tMessage = trimTrailingSpaces(valueTable.Text),\n\t\t\t\tMessageType = ChatConstants.MessageTypeSetCore,\n\t\t\t\tTime = os.time(),\n\t\t\t\tExtraData = valueTable,\n\t\t\t}\n\t\t\tchannelObj:AddMessageToChannel(messageObject)\n\t\t\tChannelsBar:UpdateMessagePostedInChannel(channel)\n\n\t\t\tmoduleApiTable.MessageCount = moduleApiTable.MessageCount + 1\n\t\t\tmoduleApiTable.MessagesChanged:fire(moduleApiTable.MessageCount)\n\t\tend\n\tend\nend)\n\nmoduleApiTable.ChatBarDisabledEvent:connect(function(disabled)\n\tif canChat then\n\t\tChatBar:SetEnabled(not disabled)\n\t\tif (disabled) then\n\t\t\tChatBar:ReleaseFocus()\n\t\tend\n\tend\nend)\n\nmoduleApiTable.ChatWindowSizeEvent:connect(function(size)\n\tChatWindow.GuiObject.Size = size\nend)\n\nmoduleApiTable.ChatWindowPositionEvent:connect(function(position)\n\tChatWindow.GuiObject.Position = position\nend)\n\n--////////////////////////////////////////////////////////////////////////////////////////////\n--///////////////////////////////////////////////// Code to hook client UI up to server events\n--////////////////////////////////////////////////////////////////////////////////////////////\n\nfunction DoChatBarFocus()\n\tif (not ChatWindow:GetCoreGuiEnabled()) then return end\n\tif (not ChatBar:GetEnabled()) then return end\n\n\tif (not ChatBar:IsFocused() and ChatBar:GetVisible()) then\n\t\tmoduleApiTable:SetVisible(true)\n\t\tInstantFadeIn()\n\t\tChatBar:CaptureFocus()\n\t\tmoduleApiTable.ChatBarFocusChanged:fire(true)\n\tend\nend\n\nchatBarFocusChanged.Event:connect(function(focused)\n\tmoduleApiTable.ChatBarFocusChanged:fire(focused)\nend)\n\nfunction DoSwitchCurrentChannel(targetChannel)\n\tif (ChatWindow:GetChannel(targetChannel)) then\n\t\tChatWindow:SwitchCurrentChannel(targetChannel)\n\tend\nend\n\nfunction SendMessageToSelfInTargetChannel(message, channelName, extraData)\n\tlocal channelObj = ChatWindow:GetChannel(channelName)\n\tif (channelObj) then\n\t\tlocal messageData =\n\t\t{\n\t\t\tID = -1,\n\t\t\tFromSpeaker = nil,\n\t\t\tSpeakerUserId = 0,\n\t\t\tOriginalChannel = channelName,\n\t\t\tIsFiltered = true,\n\t\t\tMessageLength = string.len(message),\n\t\t\tMessage = trimTrailingSpaces(message),\n\t\t\tMessageType = ChatConstants.MessageTypeSystem,\n\t\t\tTime = os.time(),\n\t\t\tExtraData = extraData,\n\t\t}\n\n\t\tchannelObj:AddMessageToChannel(messageData)\n\tend\nend\n\nfunction chatBarFocused()\n\tif (not mouseIsInWindow) then\n\t\tDoBackgroundFadeIn()\n\t\tif (textIsFaded) then\n\t\t\tDoTextFadeIn()\n\t\tend\n\tend\n\n\tchatBarFocusChanged:Fire(true)\nend\n\n--// Event for making player say chat message.\nfunction chatBarFocusLost(enterPressed, inputObject)\n\tDoBackgroundFadeIn()\n\tchatBarFocusChanged:Fire(false)\n\n\tif (enterPressed) then\n\t\tlocal message = ChatBar:GetTextBox().Text\n\n\t\tif ChatBar:IsInCustomState() then\n\t\t\tlocal customMessage = ChatBar:GetCustomMessage()\n\t\t\tif customMessage then\n\t\t\t\tmessage = customMessage\n\t\t\tend\n\t\t\tlocal messageSunk = ChatBar:CustomStateProcessCompletedMessage(message)\n\t\t\tChatBar:ResetCustomState()\n\t\t\tif messageSunk then\n\t\t\t\treturn\n\t\t\tend\n\t\tend\n\n\t\tmessage = string.sub(message, 1, ChatSettings.MaximumMessageLength)\n\n\t\tChatBar:GetTextBox().Text = \"\"\n\n\t\tif message ~= \"\" then\n\t\t\t--// Sends signal to eventually call Player:Chat() to handle C++ side legacy stuff.\n\t\t\tmoduleApiTable.MessagePosted:fire(message)\n\n\t\t\tif not CommandProcessor:ProcessCompletedChatMessage(message, ChatWindow) then\n\t\t\t\tif ChatSettings.DisallowedWhiteSpace then\n\t\t\t\t\tfor i = 1, #ChatSettings.DisallowedWhiteSpace do\n\t\t\t\t\t\tif ChatSettings.DisallowedWhiteSpace[i] == \"\\t\" then\n\t\t\t\t\t\t\tmessage = string.gsub(message, ChatSettings.DisallowedWhiteSpace[i], \" \")\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tmessage = string.gsub(message, ChatSettings.DisallowedWhiteSpace[i], \"\")\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tmessage = string.gsub(message, \"\\n\", \"\")\n\t\t\t\tmessage = string.gsub(message, \"[ ]+\", \" \")\n\n\t\t\t\tlocal targetChannel = ChatWindow:GetTargetMessageChannel()\n\t\t\t\tif targetChannel then\n\t\t\t\t\tMessageSender:SendMessage(message, targetChannel)\n\t\t\t\telse\n\t\t\t\t\tMessageSender:SendMessage(message, nil)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\tend\nend\n\nlocal ChatBarConnections = {}\nfunction setupChatBarConnections()\n\tfor i = 1, #ChatBarConnections do\n\t\tChatBarConnections[i]:Disconnect()\n\tend\n\tChatBarConnections = {}\n\n\tlocal focusLostConnection = ChatBar:GetTextBox().FocusLost:connect(chatBarFocusLost)\n\ttable.insert(ChatBarConnections, focusLostConnection)\n\n\tlocal focusGainedConnection = ChatBar:GetTextBox().Focused:connect(chatBarFocused)\n\ttable.insert(ChatBarConnections, focusGainedConnection)\nend\n\nsetupChatBarConnections()\nChatBar.GuiObjectsChanged:connect(setupChatBarConnections)\n\nfunction getEchoMessagesInGeneral()\n\tif ChatSettings.EchoMessagesInGeneralChannel == nil then\n\t\treturn true\n\tend\n\treturn ChatSettings.EchoMessagesInGeneralChannel\nend\n\nEventFolder.OnMessageDoneFiltering.OnClientEvent:connect(function(messageData)\n\tif not ChatSettings.ShowUserOwnFilteredMessage then\n\t\tif messageData.FromSpeaker == LocalPlayer.Name then\n\t\t\treturn\n\t\tend\n\tend\n\n\tlocal channelName = messageData.OriginalChannel\n\tlocal channelObj = ChatWindow:GetChannel(channelName)\n\tif channelObj then\n\t\tchannelObj:UpdateMessageFiltered(messageData)\n\tend\n\n\tif getEchoMessagesInGeneral() and ChatSettings.GeneralChannelName and channelName ~= ChatSettings.GeneralChannelName then\n\t\tlocal generalChannel = ChatWindow:GetChannel(ChatSettings.GeneralChannelName)\n\t\tif generalChannel then\n\t\t\tgeneralChannel:UpdateMessageFiltered(messageData)\n\t\tend\n\tend\nend)\n\nEventFolder.OnNewMessage.OnClientEvent:connect(function(messageData, channelName)\n\tlocal channelObj = ChatWindow:GetChannel(channelName)\n\tif (channelObj) then\n\t\tchannelObj:AddMessageToChannel(messageData)\n\n\t\tif (messageData.FromSpeaker ~= LocalPlayer.Name) then\n\t\t\tChannelsBar:UpdateMessagePostedInChannel(channelName)\n\t\tend\n\n\t\tif getEchoMessagesInGeneral() and ChatSettings.GeneralChannelName and channelName ~= ChatSettings.GeneralChannelName then\n\t\t\tlocal generalChannel = ChatWindow:GetChannel(ChatSettings.GeneralChannelName)\n\t\t\tif generalChannel then\n\t\t\t\tgeneralChannel:AddMessageToChannel(messageData)\n\t\t\tend\n\t\tend\n\n\t\tmoduleApiTable.MessageCount = moduleApiTable.MessageCount + 1\n\t\tmoduleApiTable.MessagesChanged:fire(moduleApiTable.MessageCount)\n\n\t\tDoFadeInFromNewInformation()\n\tend\nend)\n\nEventFolder.OnNewSystemMessage.OnClientEvent:connect(function(messageData, channelName)\n\tchannelName = channelName or \"System\"\n\n\tlocal channelObj = ChatWindow:GetChannel(channelName)\n\tif (channelObj) then\n\t\tchannelObj:AddMessageToChannel(messageData)\n\n\t\tChannelsBar:UpdateMessagePostedInChannel(channelName)\n\n\t\tmoduleApiTable.MessageCount = moduleApiTable.MessageCount + 1\n\t\tmoduleApiTable.MessagesChanged:fire(moduleApiTable.MessageCount)\n\n\t\tDoFadeInFromNewInformation()\n\n\t\tif getEchoMessagesInGeneral() and ChatSettings.GeneralChannelName and channelName ~= ChatSettings.GeneralChannelName then\n\t\t\tlocal generalChannel = ChatWindow:GetChannel(ChatSettings.GeneralChannelName)\n\t\t\tif generalChannel then\n\t\t\t\tgeneralChannel:AddMessageToChannel(messageData)\n\t\t\tend\n\t\tend\n\telse\n\t\twarn(string.format(\"Just received system message for channel I'm not in [%s]\", channelName))\n\tend\nend)\n\n\nfunction HandleChannelJoined(channel, welcomeMessage, messageLog, channelNameColor, addHistoryToGeneralChannel,\n\taddWelcomeMessageToGeneralChannel)\n\tif ChatWindow:GetChannel(channel) then\n\t\t--- If the channel has already been added, remove it first.\n\t\tChatWindow:RemoveChannel(channel)\n\tend\n\n\tif (channel == ChatSettings.GeneralChannelName) then\n\t\tDidFirstChannelsLoads = true\n\tend\n\n\tif channelNameColor then\n\t\tChatBar:SetChannelNameColor(channel, channelNameColor)\n\tend\n\n\tlocal channelObj = ChatWindow:AddChannel(channel)\n\n\tif (channelObj) then\n\t\tif (channel == ChatSettings.GeneralChannelName) then\n\t\t\tDoSwitchCurrentChannel(channel)\n\t\tend\n\n\t\tif (messageLog) then\n\t\t\tlocal startIndex = 1\n\t\t\tif #messageLog > ChatSettings.MessageHistoryLengthPerChannel then\n\t\t\t\tstartIndex = #messageLog - ChatSettings.MessageHistoryLengthPerChannel\n\t\t\tend\n\n\t\t\tfor i = startIndex, #messageLog do\n\t\t\t\tchannelObj:AddMessageToChannel(messageLog[i])\n\t\t\tend\n\n\t\t\tif getEchoMessagesInGeneral() and addHistoryToGeneralChannel then\n\t\t\t\tif ChatSettings.GeneralChannelName and channel ~= ChatSettings.GeneralChannelName then\n\t\t\t\t\tlocal generalChannel = ChatWindow:GetChannel(ChatSettings.GeneralChannelName)\n\t\t\t\t\tif generalChannel then\n\t\t\t\t\t\tgeneralChannel:AddMessagesToChannelByTimeStamp(messageLog, startIndex)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif (welcomeMessage ~= \"\") then\n\t\t\tlocal welcomeMessageObject = {\n\t\t\t\tID = -1,\n\t\t\t\tFromSpeaker = nil,\n\t\t\t\tSpeakerUserId = 0,\n\t\t\t\tOriginalChannel = channel,\n\t\t\t\tIsFiltered = true,\n\t\t\t\tMessageLength = string.len(welcomeMessage),\n\t\t\t\tMessage = trimTrailingSpaces(welcomeMessage),\n\t\t\t\tMessageType = ChatConstants.MessageTypeWelcome,\n\t\t\t\tTime = os.time(),\n\t\t\t\tExtraData = nil,\n\t\t\t}\n\t\t\tchannelObj:AddMessageToChannel(welcomeMessageObject)\n\n\t\t\tif getEchoMessagesInGeneral() and addWelcomeMessageToGeneralChannel and not ChatSettings.ShowChannelsBar then\n\t\t\t\tif channel ~= ChatSettings.GeneralChannelName then\n\t\t\t\t\tlocal generalChannel = ChatWindow:GetChannel(ChatSettings.GeneralChannelName)\n\t\t\t\t\tif generalChannel then\n\t\t\t\t\t\tgeneralChannel:AddMessageToChannel(welcomeMessageObject)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tDoFadeInFromNewInformation()\n\tend\n\nend\n\nEventFolder.OnChannelJoined.OnClientEvent:connect(function(channel, welcomeMessage, messageLog, channelNameColor)\n\tHandleChannelJoined(channel, welcomeMessage, messageLog, channelNameColor, false, true)\nend)\n\nEventFolder.OnChannelLeft.OnClientEvent:connect(function(channel)\n\tChatWindow:RemoveChannel(channel)\n\n\tDoFadeInFromNewInformation()\nend)\n\nEventFolder.OnMuted.OnClientEvent:connect(function(channel)\n\t--// Do something eventually maybe?\n\t--// This used to take away the chat bar in channels the player was muted in.\n\t--// We found out this behavior was inconvenient for doing chat commands though.\nend)\n\nEventFolder.OnUnmuted.OnClientEvent:connect(function(channel)\n\t--// Same as above.\nend)\n\nEventFolder.OnMainChannelSet.OnClientEvent:connect(function(channel)\n\tDoSwitchCurrentChannel(channel)\nend)\n\ncoroutine.wrap(function()\n\t-- ChannelNameColorUpdated may not exist if the client version is older than the server version.\n\tlocal ChannelNameColorUpdated = DefaultChatSystemChatEvents:WaitForChild(\"ChannelNameColorUpdated\", 5)\n\tif ChannelNameColorUpdated then\n\t\tChannelNameColorUpdated.OnClientEvent:connect(function(channelName, channelNameColor)\n\t\t\tChatBar:SetChannelNameColor(channelName, channelNameColor)\n\t\tend)\n\tend\nend)()\n\n\n--- Interaction with SetCore Player events.\n\nlocal PlayerBlockedEvent = nil\nlocal PlayerMutedEvent = nil\nlocal PlayerUnBlockedEvent = nil\nlocal PlayerUnMutedEvent = nil\n\n\n-- This is pcalled because the SetCore methods may not be released yet.\npcall(function()\n\tPlayerBlockedEvent = StarterGui:GetCore(\"PlayerBlockedEvent\")\n\tPlayerMutedEvent = StarterGui:GetCore(\"PlayerMutedEvent\")\n\tPlayerUnBlockedEvent = StarterGui:GetCore(\"PlayerUnblockedEvent\")\n\tPlayerUnMutedEvent = StarterGui:GetCore(\"PlayerUnmutedEvent\")\nend)\n\nfunction SendSystemMessageToSelf(message)\n\tlocal currentChannel = ChatWindow:GetCurrentChannel()\n\n\tif currentChannel then\n\t\tlocal messageData =\n\t\t{\n\t\t\tID = -1,\n\t\t\tFromSpeaker = nil,\n\t\t\tSpeakerUserId = 0,\n\t\t\tOriginalChannel = currentChannel.Name,\n\t\t\tIsFiltered = true,\n\t\t\tMessageLength = string.len(message),\n\t\t\tMessage = trimTrailingSpaces(message),\n\t\t\tMessageType = ChatConstants.MessageTypeSystem,\n\t\t\tTime = os.time(),\n\t\t\tExtraData = nil,\n\t\t}\n\n\t\tcurrentChannel:AddMessageToChannel(messageData)\n\tend\nend\n\nfunction MutePlayer(player)\n\tlocal mutePlayerRequest = DefaultChatSystemChatEvents:FindFirstChild(\"MutePlayerRequest\")\n\tif mutePlayerRequest then\n\t\treturn mutePlayerRequest:InvokeServer(player.Name)\n\tend\n\treturn false\nend\n\nif PlayerBlockedEvent then\n\tPlayerBlockedEvent.Event:connect(function(player)\n\t\tif MutePlayer(player) then\n\t\t\tSendSystemMessageToSelf(\n\t\t\t\tstring.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatMain_SpeakerHasBeenBlocked\",\n\t\t\t\t\t\tstring.format(\"Speaker '%s' has been blocked.\", player.Name)\n\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\",player.Name\n\t\t\t\t)\n\t\t\t)\n\t\tend\n\tend)\nend\n\nif PlayerMutedEvent then\n\tPlayerMutedEvent.Event:connect(function(player)\n\t\tif MutePlayer(player) then\n\t\t\tSendSystemMessageToSelf(\n\t\t\t\tstring.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatMain_SpeakerHasBeenMuted\",\n\t\t\t\t\t\tstring.format(\"Speaker '%s' has been muted.\", player.Name)\n\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\", player.Name\n\t\t\t\t)\n\t\t\t)\n\t\tend\n\tend)\nend\n\nfunction UnmutePlayer(player)\n\tlocal unmutePlayerRequest = DefaultChatSystemChatEvents:FindFirstChild(\"UnMutePlayerRequest\")\n\tif unmutePlayerRequest then\n\t\treturn unmutePlayerRequest:InvokeServer(player.Name)\n\tend\n\treturn false\nend\n\nif PlayerUnBlockedEvent then\n\tPlayerUnBlockedEvent.Event:connect(function(player)\n\t\tif UnmutePlayer(player) then\n\t\t\tSendSystemMessageToSelf(\n\t\t\t\tstring.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatMain_SpeakerHasBeenUnBlocked\",\n\t\t\t\t\t\tstring.format(\"Speaker '%s' has been unblocked.\", player.Name)\n\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\",player.Name\n\t\t\t\t)\n\t\t\t)\n\t\tend\n\tend)\nend\n\nif PlayerUnMutedEvent then\n\tPlayerUnMutedEvent.Event:connect(function(player)\n\t\tif UnmutePlayer(player) then\n\t\t\tSendSystemMessageToSelf(\n\t\t\t\tstring.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatMain_SpeakerHasBeenUnMuted\",\n\t\t\t\t\t\tstring.format(\"Speaker '%s' has been unmuted.\", player.Name)\n\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\",player.Name\n\t\t\t\t)\n\t\t\t)\n\t\tend\n\tend)\nend\n\n-- Get a list of blocked users from the corescripts.\n-- Spawned because this method can yeild.\nspawn(function()\n\t-- Pcalled because this method is not released on all platforms yet.\n\tif LocalPlayer.UserId > 0 then\n\t\tpcall(function()\n\t\t\tlocal blockedUserIds = StarterGui:GetCore(\"GetBlockedUserIds\")\n\t\t\tif #blockedUserIds > 0 then\n\t\t\t\tlocal setInitalBlockedUserIds = DefaultChatSystemChatEvents:FindFirstChild(\"SetBlockedUserIdsRequest\")\n\t\t\t\tif setInitalBlockedUserIds then\n\t\t\t\t\tsetInitalBlockedUserIds:FireServer(blockedUserIds)\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\tend\nend)\n\nspawn(function()\n\tlocal success, canLocalUserChat = pcall(function()\n\t\treturn Chat:CanUserChatAsync(LocalPlayer.UserId)\n\tend)\n\tif success then\n\t\tcanChat = RunService:IsStudio() or canLocalUserChat\n\tend\nend)\n\nlocal initData = EventFolder.GetInitDataRequest:InvokeServer()\n\n-- Handle joining general channel first.\nfor i, channelData in pairs(initData.Channels) do\n\tif channelData[1] == ChatSettings.GeneralChannelName then\n\t\tHandleChannelJoined(channelData[1], channelData[2], channelData[3], channelData[4], true, false)\n\tend\nend\n\nfor i, channelData in pairs(initData.Channels) do\n\tif channelData[1] ~= ChatSettings.GeneralChannelName then\n\t\tHandleChannelJoined(channelData[1], channelData[2], channelData[3], channelData[4], true, false)\n\tend\nend\n\nreturn moduleApiTable\n"
  },
  {
    "path": "src/Chat/ChatScript/init.client.lua",
    "content": "--\t// FileName: ChatScript.lua\n--\t// Written by: Xsitsu\n--\t// Description: Hooks main chat module up to Topbar in corescripts.\n\nlocal StarterGui = game:GetService(\"StarterGui\")\nlocal GuiService = game:GetService(\"GuiService\")\nlocal ChatService = game:GetService(\"Chat\")\n\nlocal MAX_COREGUI_CONNECTION_ATTEMPTS = 10\n\nlocal ClientChatModules = ChatService:WaitForChild(\"ClientChatModules\")\nlocal ChatSettings = require(ClientChatModules:WaitForChild(\"ChatSettings\"))\n\nlocal function DoEverything()\n\tlocal Chat = require(script:WaitForChild(\"ChatMain\"))\n\n\tlocal containerTable = {}\n\tcontainerTable.ChatWindow = {}\n\tcontainerTable.SetCore = {}\n\tcontainerTable.GetCore = {}\n\n\tcontainerTable.ChatWindow.ChatTypes = {}\n\tcontainerTable.ChatWindow.ChatTypes.BubbleChatEnabled = ChatSettings.BubbleChatEnabled\n\tcontainerTable.ChatWindow.ChatTypes.ClassicChatEnabled = ChatSettings.ClassicChatEnabled\n\n\t--// Connection functions\n\tlocal function ConnectEvent(name)\n\t\tlocal event = Instance.new(\"BindableEvent\")\n\t\tevent.Name = name\n\t\tcontainerTable.ChatWindow[name] = event\n\n\t\tevent.Event:connect(function(...) Chat[name](Chat, ...) end)\n\tend\n\n\tlocal function ConnectFunction(name)\n\t\tlocal func = Instance.new(\"BindableFunction\")\n\t\tfunc.Name = name\n\t\tcontainerTable.ChatWindow[name] = func\n\n\t\tfunc.OnInvoke = function(...) return Chat[name](Chat, ...) end\n\tend\n\n\tlocal function ReverseConnectEvent(name)\n\t\tlocal event = Instance.new(\"BindableEvent\")\n\t\tevent.Name = name\n\t\tcontainerTable.ChatWindow[name] = event\n\n\t\tChat[name]:connect(function(...) event:Fire(...) end)\n\tend\n\n\tlocal function ConnectSignal(name)\n\t\tlocal event = Instance.new(\"BindableEvent\")\n\t\tevent.Name = name\n\t\tcontainerTable.ChatWindow[name] = event\n\n\t\tevent.Event:connect(function(...) Chat[name]:fire(...) end)\n\tend\n\n\tlocal function ConnectSetCore(name)\n\t\tlocal event = Instance.new(\"BindableEvent\")\n\t\tevent.Name = name\n\t\tcontainerTable.SetCore[name] = event\n\n\t\tevent.Event:connect(function(...) Chat[name..\"Event\"]:fire(...) end)\n\tend\n\n\tlocal function ConnectGetCore(name)\n\t\tlocal func = Instance.new(\"BindableFunction\")\n\t\tfunc.Name = name\n\t\tcontainerTable.GetCore[name] = func\n\n\t\tfunc.OnInvoke = function(...) return Chat[\"f\"..name](...) end\n\tend\n\n\t--// Do connections\n\tConnectEvent(\"ToggleVisibility\")\n\tConnectEvent(\"SetVisible\")\n\tConnectEvent(\"FocusChatBar\")\n\tConnectEvent(\"EnterWhisperState\")\n\tConnectFunction(\"GetVisibility\")\n\tConnectFunction(\"GetMessageCount\")\n\tConnectEvent(\"TopbarEnabledChanged\")\n\tConnectFunction(\"IsFocused\")\n\n\tReverseConnectEvent(\"ChatBarFocusChanged\")\n\tReverseConnectEvent(\"VisibilityStateChanged\")\n\tReverseConnectEvent(\"MessagesChanged\")\n\tReverseConnectEvent(\"MessagePosted\")\n\n\tConnectSignal(\"CoreGuiEnabled\")\n\n\tConnectSetCore(\"ChatMakeSystemMessage\")\n\tConnectSetCore(\"ChatWindowPosition\")\n\tConnectSetCore(\"ChatWindowSize\")\n\tConnectGetCore(\"ChatWindowPosition\")\n\tConnectGetCore(\"ChatWindowSize\")\n\tConnectSetCore(\"ChatBarDisabled\")\n\tConnectGetCore(\"ChatBarDisabled\")\n\n\tConnectEvent(\"SpecialKeyPressed\")\n\n\tSetCoreGuiChatConnections(containerTable)\nend\n\nfunction SetCoreGuiChatConnections(containerTable)\n\tlocal tries = 0\n\twhile tries < MAX_COREGUI_CONNECTION_ATTEMPTS do\n\t\ttries = tries + 1\n\t\tlocal success, ret = pcall(function() StarterGui:SetCore(\"CoreGuiChatConnections\", containerTable) end)\n\t\tif success then\n\t\t\tbreak\n\t\tend\n\t\tif not success and tries == MAX_COREGUI_CONNECTION_ATTEMPTS then\n\t\t\terror(\"Error calling SetCore CoreGuiChatConnections: \" .. ret)\n\t\tend\n\t\twait()\n\tend\nend\n\nfunction checkBothChatTypesDisabled()\n\tif ChatSettings.BubbleChatEnabled ~= nil then\n\t\tif ChatSettings.ClassicChatEnabled ~= nil then\n\t\t\treturn not (ChatSettings.BubbleChatEnabled or ChatSettings.ClassicChatEnabled)\n\t\tend\n\tend\n\treturn false\nend\n\nif (not GuiService:IsTenFootInterface()) and (not game:GetService('UserInputService').VREnabled) then\n\tif not checkBothChatTypesDisabled() then\n\t\tDoEverything()\n\telse\n\t\tlocal containerTable = {}\n\t\tcontainerTable.ChatWindow = {}\n\n\t\tcontainerTable.ChatWindow.ChatTypes = {}\n\t\tcontainerTable.ChatWindow.ChatTypes.BubbleChatEnabled = false\n\t\tcontainerTable.ChatWindow.ChatTypes.ClassicChatEnabled = false\n\t\tSetCoreGuiChatConnections(containerTable)\n\tend\nend\n"
  },
  {
    "path": "src/Chat/ChatServiceRunner/ChatChannel.lua",
    "content": "--\t// FileName: ChatChannel.lua\n--\t// Written by: Xsitsu\n--\t// Description: A representation of one channel that speakers can chat in.\n\nlocal forceNewFilterAPI = false\nlocal IN_GAME_CHAT_USE_NEW_FILTER_API\ndo\n\tlocal textServiceExists = (game:GetService(\"TextService\") ~= nil)\n\tlocal success, enabled = pcall(function() return UserSettings():IsUserFeatureEnabled(\"UserInGameChatUseNewFilterAPIV2\") end)\n\tlocal flagEnabled = (success and enabled)\n\tIN_GAME_CHAT_USE_NEW_FILTER_API = (forceNewFilterAPI or flagEnabled) and textServiceExists\nend\n\nlocal module = {}\n\nlocal modulesFolder = script.Parent\nlocal Chat = game:GetService(\"Chat\")\nlocal RunService = game:GetService(\"RunService\")\nlocal replicatedModules = Chat:WaitForChild(\"ClientChatModules\")\n\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal ChatConstants = require(replicatedModules:WaitForChild(\"ChatConstants\"))\nlocal Util = require(modulesFolder:WaitForChild(\"Util\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = { Get = function(key,default) return default end } end\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\n\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:SendSystemMessage(message, extraData)\n\tlocal messageObj = self:InternalCreateMessageObject(message, nil, true, extraData)\n\n\tlocal success, err = pcall(function() self.eMessagePosted:Fire(messageObj) end)\n\tif not success and err then\n\t\tprint(\"Error posting message: \" ..err)\n\tend\n\n\tself:InternalAddMessageToHistoryLog(messageObj)\n\n\tfor i, speaker in pairs(self.Speakers) do\n\t\tspeaker:InternalSendSystemMessage(messageObj, self.Name)\n\tend\n\n\treturn messageObj\nend\n\nfunction methods:SendSystemMessageToSpeaker(message, speakerName, extraData)\n\tlocal speaker = self.Speakers[speakerName]\n\tif (speaker) then\n\t\tlocal messageObj = self:InternalCreateMessageObject(message, nil, true, extraData)\n\t\tspeaker:InternalSendSystemMessage(messageObj, self.Name)\n\telse\n\t\twarn(string.format(\"Speaker '%s' is not in channel '%s' and cannot be sent a system message\", speakerName, self.Name))\n\tend\nend\n\nfunction methods:SendMessageObjToFilters(message, messageObj, fromSpeaker)\n\tlocal oldMessage = messageObj.Message\n\tmessageObj.Message = message\n\tself:InternalDoMessageFilter(fromSpeaker.Name, messageObj, self.Name)\n\tself.ChatService:InternalDoMessageFilter(fromSpeaker.Name, messageObj, self.Name)\n\tlocal newMessage = messageObj.Message\n\tmessageObj.Message = oldMessage\n\treturn newMessage\nend\n\nfunction methods:CanCommunicateByUserId(userId1, userId2)\n\tif RunService:IsStudio() then\n\t\treturn true\n\tend\n\t-- UserId is set as 0 for non player speakers.\n\tif userId1 == 0 or userId2 == 0 then\n\t\treturn true\n\tend\n\tlocal success, canCommunicate = pcall(function()\n\t\treturn Chat:CanUsersChatAsync(userId1, userId2)\n\tend)\n\treturn success and canCommunicate\nend\n\nfunction methods:CanCommunicate(speakerObj1, speakerObj2)\n\tlocal player1 = speakerObj1:GetPlayer()\n\tlocal player2 = speakerObj2:GetPlayer()\n\tif player1 and player2 then\n\t\treturn self:CanCommunicateByUserId(player1.UserId, player2.UserId)\n\tend\n\treturn true\nend\n\nfunction methods:SendMessageToSpeaker(message, speakerName, fromSpeakerName, extraData)\n\tlocal speakerTo = self.Speakers[speakerName]\n\tlocal speakerFrom = self.ChatService:GetSpeaker(fromSpeakerName)\n\tif speakerTo and speakerFrom then\n\t\tlocal isMuted = speakerTo:IsSpeakerMuted(fromSpeakerName)\n\t\tif isMuted then\n\t\t\treturn\n\t\tend\n\n\t\tif not self:CanCommunicate(speakerTo, speakerFrom) then\n\t\t\treturn\n\t\tend\n\n\t\t-- We need to claim the message is filtered even if it not in this case for compatibility with legacy client side code.\n\t\tlocal isFiltered = speakerName == fromSpeakerName\n\t\tlocal messageObj = self:InternalCreateMessageObject(message, fromSpeakerName, isFiltered, extraData)\n\t\tmessage = self:SendMessageObjToFilters(message, messageObj, fromSpeakerName)\n\t\tspeakerTo:InternalSendMessage(messageObj, self.Name)\n\n\t\t--// START FFLAG\n\t\tif (not IN_GAME_CHAT_USE_NEW_FILTER_API) then --// USES FFLAG\n\t\t--// OLD BEHAVIOR\n\t\tlocal filteredMessage = self.ChatService:InternalApplyRobloxFilter(messageObj.FromSpeaker, message, speakerName)\n\t\tif filteredMessage then\n\t\t\tmessageObj.Message = filteredMessage\n\t\t\tmessageObj.IsFiltered = true\n\t\t\tspeakerTo:InternalSendFilteredMessage(messageObj, self.Name)\n\t\tend\n\t\t--// OLD BEHAVIOR\n\t\telse\n\t\t--// NEW BEHAVIOR\n\t\t\tlocal textContext = self.Private and Enum.TextFilterContext.PrivateChat or Enum.TextFilterContext.PublicChat\n\t\t\tlocal filterSuccess, isFilterResult, filteredMessage = self.ChatService:InternalApplyRobloxFilterNewAPI(\n\t\t\t\tmessageObj.FromSpeaker,\n\t\t\t\tmessage,\n\t\t\t\ttextContext\n\t\t\t)\n\t\t\tif (filterSuccess) then\n\t\t\t\tmessageObj.FilterResult = filteredMessage\n\t\t\t\tmessageObj.IsFilterResult = isFilterResult\n\t\t\t\tmessageObj.IsFiltered = true\n\t\t\t\tspeakerTo:InternalSendFilteredMessageWithFilterResult(messageObj, self.Name)\n\t\t\tend\n\t\t--// NEW BEHAVIOR\n\t\tend\n\t\t--// END FFLAG\n\telse\n\t\twarn(string.format(\"Speaker '%s' is not in channel '%s' and cannot be sent a message\", speakerName, self.Name))\n\tend\nend\n\nfunction methods:KickSpeaker(speakerName, reason)\n\tlocal speaker = self.ChatService:GetSpeaker(speakerName)\n\tif (not speaker) then\n\t\terror(\"Speaker \\\"\" .. speakerName .. \"\\\" does not exist!\")\n\tend\n\n\tlocal messageToSpeaker = \"\"\n\tlocal messageToChannel = \"\"\n\n\tif (reason) then\n\t\tmessageToSpeaker = string.format(\"You were kicked from '%s' for the following reason(s): %s\", self.Name, reason)\n\t\tmessageToChannel = string.format(\"%s was kicked for the following reason(s): %s\", speakerName, reason)\n\telse\n\t\tmessageToSpeaker = string.format(\"You were kicked from '%s'\", self.Name)\n\t\tmessageToChannel = string.format(\"%s was kicked\", speakerName)\n\tend\n\n\tself:SendSystemMessageToSpeaker(messageToSpeaker, speakerName)\n\tspeaker:LeaveChannel(self.Name)\n\tself:SendSystemMessage(messageToChannel)\nend\n\nfunction methods:MuteSpeaker(speakerName, reason, length)\n\tlocal speaker = self.ChatService:GetSpeaker(speakerName)\n\tif (not speaker) then\n\t\terror(\"Speaker \\\"\" .. speakerName .. \"\\\" does not exist!\")\n\tend\n\n\tself.Mutes[speakerName:lower()] = (length == 0 or length == nil) and 0 or (os.time() + length)\n\n\tif (reason) then\n\t\tself:SendSystemMessage(string.format(\"%s was muted for the following reason(s): %s\", speakerName, reason))\n\tend\n\n\tlocal success, err = pcall(function() self.eSpeakerMuted:Fire(speakerName, reason, length) end)\n\tif not success and err then\n\t\tprint(\"Error mutting speaker: \" ..err)\n\tend\n\n\tlocal spkr = self.ChatService:GetSpeaker(speakerName)\n\tif (spkr) then\n\t\tlocal success, err = pcall(function() spkr.eMuted:Fire(self.Name, reason, length) end)\n\t\tif not success and err then\n\t\t\tprint(\"Error mutting speaker: \" ..err)\n\t\tend\n\tend\n\nend\n\nfunction methods:UnmuteSpeaker(speakerName)\n\tlocal speaker = self.ChatService:GetSpeaker(speakerName)\n\tif (not speaker) then\n\t\terror(\"Speaker \\\"\" .. speakerName .. \"\\\" does not exist!\")\n\tend\n\n\tself.Mutes[speakerName:lower()] = nil\n\n\tlocal success, err = pcall(function() self.eSpeakerUnmuted:Fire(speakerName) end)\n\tif not success and err then\n\t\tprint(\"Error unmuting speaker: \" ..err)\n\tend\n\n\tlocal spkr = self.ChatService:GetSpeaker(speakerName)\n\tif (spkr) then\n\t\tlocal success, err = pcall(function() spkr.eUnmuted:Fire(self.Name) end)\n\t\tif not success and err then\n\t\t\tprint(\"Error unmuting speaker: \" ..err)\n\t\tend\n\tend\nend\n\nfunction methods:IsSpeakerMuted(speakerName)\n\treturn (self.Mutes[speakerName:lower()] ~= nil)\nend\n\nfunction methods:GetSpeakerList()\n\tlocal list = {}\n\tfor i, speaker in pairs(self.Speakers) do\n\t\ttable.insert(list, speaker.Name)\n\tend\n\treturn list\nend\n\nfunction methods:RegisterFilterMessageFunction(funcId, func, priority)\n\tif self.FilterMessageFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"FilterMessageFunction '%s' already exists\", funcId))\n\tend\n\tself.FilterMessageFunctions:AddFunction(funcId, func, priority)\nend\n\nfunction methods:FilterMessageFunctionExists(funcId)\n\treturn self.FilterMessageFunctions:HasFunction(funcId)\nend\n\nfunction methods:UnregisterFilterMessageFunction(funcId)\n\tif not self.FilterMessageFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"FilterMessageFunction '%s' does not exists\", funcId))\n\tend\n\tself.FilterMessageFunctions:RemoveFunction(funcId)\nend\n\nfunction methods:RegisterProcessCommandsFunction(funcId, func, priority)\n\tif self.ProcessCommandsFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"ProcessCommandsFunction '%s' already exists\", funcId))\n\tend\n\tself.ProcessCommandsFunctions:AddFunction(funcId, func, priority)\nend\n\nfunction methods:ProcessCommandsFunctionExists(funcId)\n\treturn self.ProcessCommandsFunctions:HasFunction(funcId)\nend\n\nfunction methods:UnregisterProcessCommandsFunction(funcId)\n\tif not self.ProcessCommandsFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"ProcessCommandsFunction '%s' does not exist\", funcId))\n\tend\n\tself.ProcessCommandsFunctions:RemoveFunction(funcId)\nend\n\nlocal function ShallowCopy(table)\n\tlocal copy = {}\n\tfor i, v in pairs(table) do\n\t\tcopy[i] = v\n\tend\n\treturn copy\nend\n\nfunction methods:GetHistoryLog()\n\treturn ShallowCopy(self.ChatHistory)\nend\n\nfunction methods:GetHistoryLogForSpeaker(speaker)\n\tlocal userId = -1\n\tlocal player = speaker:GetPlayer()\n\tif player then\n\t\tuserId = player.UserId\n\tend\n\tlocal chatlog = {}\n\t--// START FFLAG\n\tif (not IN_GAME_CHAT_USE_NEW_FILTER_API) then --// USES FFLAG\n\t--// OLD BEHAVIOR\n\tfor i = 1, #self.ChatHistory do\n\t\tlocal logUserId = self.ChatHistory[i].SpeakerUserId\n\t\tif self:CanCommunicateByUserId(userId, logUserId) then\n\t\t\ttable.insert(chatlog, ShallowCopy(self.ChatHistory[i]))\n\t\tend\n\tend\n\t--// OLD BEHAVIOR\n\telse\n\t--// NEW BEHAVIOR\n\t\tfor i = 1, #self.ChatHistory do\n\t\t\tlocal logUserId = self.ChatHistory[i].SpeakerUserId\n\t\t\tif self:CanCommunicateByUserId(userId, logUserId) then\n\t\t\t\tlocal messageObj = ShallowCopy(self.ChatHistory[i])\n\n\t\t\t\t--// Since we're using the new filter API, we need to convert the stored filter result\n\t\t\t\t--// into an actual string message to send to players for their chat history.\n\t\t\t\t--// System messages aren't filtered the same way, so they just have a regular \n\t\t\t\t--// text value in the Message field.\n\t\t\t\tif (messageObj.MessageType == ChatConstants.MessageTypeDefault or messageObj.MessageType == ChatConstants.MessageTypeMeCommand) then\n\t\t\t\t\tlocal filterResult = messageObj.FilterResult\n\t\t\t\t\tif (messageObj.IsFilterResult) then\n\t\t\t\t\t\tif (player) then\n\t\t\t\t\t\t\tmessageObj.Message = filterResult:GetChatForUserAsync(player.UserId)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tmessageObj.Message = filterResult:GetNonChatStringForBroadcastAsync()\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\tmessageObj.Message = filterResult\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\ttable.insert(chatlog, messageObj)\n\t\t\tend\n\t\tend\n\t--// NEW BEHAVIOR\n\tend\n\t--// END FFLAG\n\treturn chatlog\nend\n\n--///////////////// Internal-Use Methods\n--//////////////////////////////////////\nfunction methods:InternalDestroy()\n\tfor i, speaker in pairs(self.Speakers) do\n\t\tspeaker:LeaveChannel(self.Name)\n\tend\n\n\tself.eDestroyed:Fire()\n\n\tself.eDestroyed:Destroy()\n\tself.eMessagePosted:Destroy()\n\tself.eSpeakerJoined:Destroy()\n\tself.eSpeakerLeft:Destroy()\n\tself.eSpeakerMuted:Destroy()\n\tself.eSpeakerUnmuted:Destroy()\nend\n\nfunction methods:InternalDoMessageFilter(speakerName, messageObj, channel)\n\tlocal filtersIterator = self.FilterMessageFunctions:GetIterator()\n\tfor funcId, func, priority in filtersIterator do\n\t\tlocal success, errorMessage = pcall(function()\n\t\t\tfunc(speakerName, messageObj, channel)\n\t\tend)\n\n\t\tif not success then\n\t\t\twarn(string.format(\"DoMessageFilter Function '%s' failed for reason: %s\", funcId, errorMessage))\n\t\tend\n\tend\nend\n\nfunction methods:InternalDoProcessCommands(speakerName, message, channel)\n\tlocal commandsIterator = self.ProcessCommandsFunctions:GetIterator()\n\tfor funcId, func, priority in commandsIterator do\n\t\tlocal success, returnValue = pcall(function()\n\t\t\tlocal ret = func(speakerName, message, channel)\n\t\t\tif type(ret) ~= \"boolean\" then\n\t\t\t\terror(\"Process command functions must return a bool\")\n\t\t\tend\n\t\t\treturn ret\n\t\tend)\n\n\t\tif not success then\n\t\t\twarn(string.format(\"DoProcessCommands Function '%s' failed for reason: %s\", funcId, returnValue))\n\t\telseif returnValue then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nfunction methods:InternalPostMessage(fromSpeaker, message, extraData)\n\tif (self:InternalDoProcessCommands(fromSpeaker.Name, message, self.Name)) then return false end\n\n\tif (self.Mutes[fromSpeaker.Name:lower()] ~= nil) then\n\t\tlocal t = self.Mutes[fromSpeaker.Name:lower()]\n\t\tif (t > 0 and os.time() > t) then\n\t\t\tself:UnmuteSpeaker(fromSpeaker.Name)\n\t\telse\n\t\t\tself:SendSystemMessageToSpeaker(ChatLocalization:Get(\"GameChat_ChatChannel_MutedInChannel\",\"You are muted and cannot talk in this channel\"), fromSpeaker.Name)\n\t\t\treturn false\n\t\tend\n\tend\n\n\tlocal messageObj = self:InternalCreateMessageObject(message, fromSpeaker.Name, false, extraData)\n\n\t-- allow server to process the unfiltered message string\n\tmessageObj.Message = message\n\tlocal processedMessage\n\tpcall(function()\n\t\tprocessedMessage = Chat:InvokeChatCallback(Enum.ChatCallbackType.OnServerReceivingMessage, messageObj)\n\tend)\n\tmessageObj.Message = nil\n\n\tif processedMessage then\n\n\t\t-- developer server code's choice to mute the message\n\t\tif processedMessage.ShouldDeliver == false then\n\t\t\treturn false\n\t\tend\n\t\tmessageObj = processedMessage\n\tend\n\n\tmessage = self:SendMessageObjToFilters(message, messageObj, fromSpeaker)\n\n\tlocal sentToList = {}\n\tfor i, speaker in pairs(self.Speakers) do\n\t\tlocal isMuted = speaker:IsSpeakerMuted(fromSpeaker.Name)\n\t\tif not isMuted and self:CanCommunicate(fromSpeaker, speaker) then\n\t\t\ttable.insert(sentToList, speaker.Name)\n\t\t\tif speaker.Name == fromSpeaker.Name then\n\t\t\t\t-- Send unfiltered message to speaker who sent the message.\n\t\t\t\tlocal cMessageObj = ShallowCopy(messageObj)\n\t\t\t\tcMessageObj.Message = message\n\t\t\t\tcMessageObj.IsFiltered = true\n\t\t\t\t-- We need to claim the message is filtered even if it not in this case for compatibility with legacy client side code.\n\t\t\t\tspeaker:InternalSendMessage(cMessageObj, self.Name)\n\t\t\telse\n\t\t\t\tspeaker:InternalSendMessage(messageObj, self.Name)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal success, err = pcall(function() self.eMessagePosted:Fire(messageObj) end)\n\tif not success and err then\n\t\tprint(\"Error posting message: \" ..err)\n\tend\n\n\t--// START FFLAG\n\tif (not IN_GAME_CHAT_USE_NEW_FILTER_API) then --// USES FFLAG\n\t--// OLD BEHAVIOR\n\tlocal filteredMessages = {}\n\tfor i, speakerName in pairs(sentToList) do\n\t\tlocal filteredMessage = self.ChatService:InternalApplyRobloxFilter(messageObj.FromSpeaker, message, speakerName)\n\t\tif filteredMessage then\n\t\t\tfilteredMessages[speakerName] = filteredMessage\n\t\telse\n\t\t\treturn false\n\t\tend\n\tend\n\n\tfor i, speakerName in pairs(sentToList) do\n\t\tlocal speaker = self.Speakers[speakerName]\n\t\tif (speaker) then\n\t\t\tlocal cMessageObj = ShallowCopy(messageObj)\n\t\t\tcMessageObj.Message = filteredMessages[speakerName]\n\t\t\tcMessageObj.IsFiltered = true\n\t\t\tspeaker:InternalSendFilteredMessage(cMessageObj, self.Name)\n\t\tend\n\tend\n\n\tlocal filteredMessage = self.ChatService:InternalApplyRobloxFilter(messageObj.FromSpeaker, message)\n\tif filteredMessage then\n\t\tmessageObj.Message = filteredMessage\n\telse\n\t\treturn false\n\tend\n\tmessageObj.IsFiltered = true\n\tself:InternalAddMessageToHistoryLog(messageObj)\n\t--// OLD BEHAVIOR\n\telse\n\t--// NEW BEHAVIOR\n\t\tlocal textFilterContext = self.Private and Enum.TextFilterContext.PrivateChat or Enum.TextFilterContext.PublicChat\n\t\tlocal filterSuccess, isFilterResult, filteredMessage = self.ChatService:InternalApplyRobloxFilterNewAPI(\n\t\t\tmessageObj.FromSpeaker,\n\t\t\tmessage,\n\t\t\ttextFilterContext\n\t\t)\n\t\tif (filterSuccess) then\n\t\t\tmessageObj.FilterResult = filteredMessage\n\t\t\tmessageObj.IsFilterResult = isFilterResult\n\t\telse\n\t\t\treturn false\n\t\tend\n\t\tmessageObj.IsFiltered = true\n\t\tself:InternalAddMessageToHistoryLog(messageObj)\n\n\t\tfor _, speakerName in pairs(sentToList) do\n\t\t\tlocal speaker = self.Speakers[speakerName]\n\t\t\tif (speaker) then\n\t\t\t\tspeaker:InternalSendFilteredMessageWithFilterResult(messageObj, self.Name)\n\t\t\tend\n\t\tend\n\t--// NEW BEHAVIOR\n\tend\n\t--// END FFLAG\n\n\t-- One more pass is needed to ensure that no speakers do not recieve the message.\n\t-- Otherwise a user could join while the message is being filtered who had not originally been sent the message.\n\tlocal speakersMissingMessage = {}\n\tfor _, speaker in pairs(self.Speakers) do\n\t\tlocal isMuted = speaker:IsSpeakerMuted(fromSpeaker.Name)\n\t\tif not isMuted and self:CanCommunicate(fromSpeaker, speaker) then\n\t\t\tlocal wasSentMessage = false\n\t\t\tfor _, sentSpeakerName in pairs(sentToList) do\n\t\t\t\tif speaker.Name == sentSpeakerName then\n\t\t\t\t\twasSentMessage = true\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\t\tif not wasSentMessage then\n\t\t\t\ttable.insert(speakersMissingMessage, speaker.Name)\n\t\t\tend\n\t\tend\n\tend\n\n\t--// START FFLAG\n\tif (not IN_GAME_CHAT_USE_NEW_FILTER_API) then --// USES FFLAG\n\t--// OLD BEHAVIOR\n\tfor _, speakerName in pairs(speakersMissingMessage) do\n\t\tlocal speaker = self.Speakers[speakerName]\n\t\tif speaker then\n\t\t\tlocal filteredMessage = self.ChatService:InternalApplyRobloxFilter(messageObj.FromSpeaker, message, speakerName)\n\t\t\tif filteredMessage == nil then\n\t\t\t\treturn false\n\t\t\tend\n\t\t\tlocal cMessageObj = ShallowCopy(messageObj)\n\t\t\tcMessageObj.Message = filteredMessage\n\t\t\tcMessageObj.IsFiltered = true\n\t\t\tspeaker:InternalSendFilteredMessage(cMessageObj, self.Name)\n\t\tend\n\tend\n\t--// OLD BEHAVIOR\n\telse\n\t--// NEW BEHAVIOR\n\t\tfor _, speakerName in pairs(speakersMissingMessage) do\n\t\t\tlocal speaker = self.Speakers[speakerName]\n\t\t\tif speaker then\n\t\t\t\tspeaker:InternalSendFilteredMessageWithFilterResult(messageObj, self.Name)\n\t\t\tend\n\t\tend\n\t--// NEW BEHAVIOR\n\tend\n\t--// END FFLAG\n\n\treturn messageObj\nend\n\nfunction methods:InternalAddSpeaker(speaker)\n\tif (self.Speakers[speaker.Name]) then\n\t\twarn(\"Speaker \\\"\" .. speaker.name .. \"\\\" is already in the channel!\")\n\t\treturn\n\tend\n\n\tself.Speakers[speaker.Name] = speaker\n\tlocal success, err = pcall(function() self.eSpeakerJoined:Fire(speaker.Name) end)\n\tif not success and err then\n\t\tprint(\"Error removing channel: \" ..err)\n\tend\nend\n\nfunction methods:InternalRemoveSpeaker(speaker)\n\tif (not self.Speakers[speaker.Name]) then\n\t\twarn(\"Speaker \\\"\" .. speaker.name .. \"\\\" is not in the channel!\")\n\t\treturn\n\tend\n\n\tself.Speakers[speaker.Name] = nil\n\tlocal success, err = pcall(function() self.eSpeakerLeft:Fire(speaker.Name) end)\n\tif not success and err then\n\t\tprint(\"Error removing speaker: \" ..err)\n\tend\nend\n\nfunction methods:InternalRemoveExcessMessagesFromLog()\n\tlocal remove = table.remove\n\twhile (#self.ChatHistory > self.MaxHistory) do\n\t\tremove(self.ChatHistory, 1)\n\tend\nend\n\nfunction methods:InternalAddMessageToHistoryLog(messageObj)\n\ttable.insert(self.ChatHistory, messageObj)\n\n\tself:InternalRemoveExcessMessagesFromLog()\nend\n\nfunction methods:GetMessageType(message, fromSpeaker)\n\tif fromSpeaker == nil then\n\t\treturn ChatConstants.MessageTypeSystem\n\tend\n\treturn ChatConstants.MessageTypeDefault\nend\n\nfunction methods:InternalCreateMessageObject(message, fromSpeaker, isFiltered, extraData)\n\tlocal messageType = self:GetMessageType(message, fromSpeaker)\n\n\tlocal speakerUserId = -1\n\tlocal speaker = nil\n\n\tif fromSpeaker then\n\t\tspeaker = self.Speakers[fromSpeaker]\n\t\tif speaker then\n\t\t\tlocal player = speaker:GetPlayer()\n\t\t\tif player then\n\t\t\t\tspeakerUserId = player.UserId\n\t\t\telse\n\t\t\t\tspeakerUserId = 0\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal messageObj =\n\t{\n\t\tID = self.ChatService:InternalGetUniqueMessageId(),\n\t\tFromSpeaker = fromSpeaker,\n\t\tSpeakerUserId = speakerUserId,\n\t\tOriginalChannel = self.Name,\n\t\tMessageLength = string.len(message),\n\t\tMessageType = messageType,\n\t\tIsFiltered = isFiltered,\n\t\tMessage = isFiltered and message or nil,\n\t\t--// These two get set by the new API. The comments are just here\n\t\t--// to remind readers that they will exist so it's not super\n\t\t--// confusing if they find them in the code but cannot find them\n\t\t--// here.\n\t\t--FilterResult = nil,\n\t\t--IsFilterResult = false,\n\t\tTime = os.time(),\n\t\tExtraData = {},\n\t}\n\n\tif speaker then\n\t\tfor k, v in pairs(speaker.ExtraData) do\n\t\t\tmessageObj.ExtraData[k] = v\n\t\tend\n\tend\n\n\tif (extraData) then\n\t\tfor k, v in pairs(extraData) do\n\t\t\tmessageObj.ExtraData[k] = v\n\t\tend\n\tend\n\n\treturn messageObj\nend\n\nfunction methods:SetChannelNameColor(color)\n\tself.ChannelNameColor = color\n\tfor i, speaker in pairs(self.Speakers) do\n\t\tspeaker:UpdateChannelNameColor(self.Name, color)\n\tend\nend\n\nfunction methods:GetWelcomeMessageForSpeaker(speaker)\n\tif self.GetWelcomeMessageFunction then\n\t\tlocal welcomeMessage = self.GetWelcomeMessageFunction(speaker)\n\t\tif welcomeMessage then\n\t\t\treturn welcomeMessage\n\t\tend\n\tend\n\treturn self.WelcomeMessage\nend\n\nfunction methods:RegisterGetWelcomeMessageFunction(func)\n\tif type(func) ~= \"function\" then\n\t\terror(\"RegisterGetWelcomeMessageFunction must be called with a function.\")\n\tend\n\tself.GetWelcomeMessageFunction = func\nend\n\nfunction methods:UnRegisterGetWelcomeMessageFunction()\n\tself.GetWelcomeMessageFunction = nil\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new(vChatService, name, welcomeMessage, channelNameColor)\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.ChatService = vChatService\n\n\tobj.Name = name\n\tobj.WelcomeMessage = welcomeMessage or \"\"\n\tobj.GetWelcomeMessageFunction = nil\n\tobj.ChannelNameColor = channelNameColor\n\n\tobj.Joinable = true\n\tobj.Leavable = true\n\tobj.AutoJoin = false\n\tobj.Private = false\n\n\tobj.Speakers = {}\n\tobj.Mutes = {}\n\n\tobj.MaxHistory = 200\n\tobj.HistoryIndex = 0\n\tobj.ChatHistory = {}\n\n\tobj.FilterMessageFunctions = Util:NewSortedFunctionContainer()\n\tobj.ProcessCommandsFunctions = Util:NewSortedFunctionContainer()\n\n\t-- Make sure to destroy added binadable events in the InternalDestroy method.\n\tobj.eDestroyed = Instance.new(\"BindableEvent\")\n\tobj.eMessagePosted = Instance.new(\"BindableEvent\")\n\tobj.eSpeakerJoined = Instance.new(\"BindableEvent\")\n\tobj.eSpeakerLeft = Instance.new(\"BindableEvent\")\n\tobj.eSpeakerMuted = Instance.new(\"BindableEvent\")\n\tobj.eSpeakerUnmuted = Instance.new(\"BindableEvent\")\n\n\tobj.MessagePosted = obj.eMessagePosted.Event\n\tobj.SpeakerJoined = obj.eSpeakerJoined.Event\n\tobj.SpeakerLeft = obj.eSpeakerLeft.Event\n\tobj.SpeakerMuted = obj.eSpeakerMuted.Event\n\tobj.SpeakerUnmuted = obj.eSpeakerUnmuted.Event\n\tobj.Destroyed = obj.eDestroyed.Event\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/Chat/ChatServiceRunner/ChatService.lua",
    "content": "--\t// FileName: ChatService.lua\n--\t// Written by: Xsitsu\n--\t// Description: Manages creating and destroying ChatChannels and Speakers.\n\nlocal MAX_FILTER_RETRIES = 3\nlocal FILTER_BACKOFF_INTERVALS = {50/1000, 100/1000, 200/1000}\nlocal MAX_FILTER_DURATION = 60\n\n--- Constants used to decide when to notify that the chat filter is having issues filtering messages.\nlocal FILTER_NOTIFCATION_THRESHOLD = 3 --Number of notifcation failures before an error message is output.\nlocal FILTER_NOTIFCATION_INTERVAL = 60 --Time between error messages.\nlocal FILTER_THRESHOLD_TIME = 60 --If there has not been an issue in this many seconds, the count of issues resets.\n\nlocal module = {}\n\nlocal RunService = game:GetService(\"RunService\")\nlocal Chat = game:GetService(\"Chat\")\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\n\nlocal modulesFolder = script.Parent\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatSettings = require(ReplicatedModules:WaitForChild(\"ChatSettings\"))\n\nlocal errorTextColor = ChatSettings.ErrorMessageTextColor or Color3.fromRGB(245, 50, 50)\nlocal errorExtraData = {ChatColor = errorTextColor}\n\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\n\nlocal ChatChannel = require(modulesFolder:WaitForChild(\"ChatChannel\"))\nlocal Speaker = require(modulesFolder:WaitForChild(\"Speaker\"))\nlocal Util = require(modulesFolder:WaitForChild(\"Util\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:AddChannel(channelName, autoJoin)\n\tif (self.ChatChannels[channelName:lower()]) then\n\t\terror(string.format(\"Channel %q alrady exists.\", channelName))\n\tend\n\n\tlocal function DefaultChannelCommands(fromSpeaker, message)\n\t\tif (message:lower() == \"/leave\") then\n\t\t\tlocal channel = self:GetChannel(channelName)\n\t\t\tlocal speaker = self:GetSpeaker(fromSpeaker)\n\t\t\tif (channel and speaker) then\n\t\t\t\tif (channel.Leavable) then\n\t\t\t\t\tspeaker:LeaveChannel(channelName)\n\t\t\t\t\tspeaker:SendSystemMessage(\n\t\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\t\"GameChat_ChatService_YouHaveLeftChannel\",\n\t\t\t\t\t\t\t\tstring.format(\"You have left channel '%s'\", channelName)\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\"{RBX_NAME}\",channelName),\n\t\t\t\t\t\t\"System\"\n\t\t\t\t\t)\n\t\t\t\telse\n\t\t\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_ChatService_CannotLeaveChannel\",\"You cannot leave this channel.\"), channelName)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\treturn true\n\t\tend\n\t\treturn false\n\tend\n\n\n\tlocal channel = ChatChannel.new(self, channelName)\n\tself.ChatChannels[channelName:lower()] = channel\n\n\tchannel:RegisterProcessCommandsFunction(\"default_commands\", DefaultChannelCommands, ChatConstants.HighPriority)\n\n\tlocal success, err = pcall(function() self.eChannelAdded:Fire(channelName) end)\n\tif not success and err then\n\t\tprint(\"Error addding channel: \" ..err)\n\tend\n\n\tif autoJoin ~= nil then\n\t\tchannel.AutoJoin = autoJoin\n\t\tif autoJoin then\n\t\t\tfor _, speaker in pairs(self.Speakers) do\n\t\t\t\tspeaker:JoinChannel(channelName)\n\t\t\tend\n\t\tend\n\tend\n\n\treturn channel\nend\n\nfunction methods:RemoveChannel(channelName)\n\tif (self.ChatChannels[channelName:lower()]) then\n\t\tlocal n = self.ChatChannels[channelName:lower()].Name\n\n\t\tself.ChatChannels[channelName:lower()]:InternalDestroy()\n\t\tself.ChatChannels[channelName:lower()] = nil\n\n\t\tlocal success, err = pcall(function() self.eChannelRemoved:Fire(n) end)\n\t\tif not success and err then\n\t\t\tprint(\"Error removing channel: \" ..err)\n\t\tend\n\telse\n\t\twarn(string.format(\"Channel %q does not exist.\", channelName))\n\tend\nend\n\nfunction methods:GetChannel(channelName)\n\treturn self.ChatChannels[channelName:lower()]\nend\n\n\nfunction methods:AddSpeaker(speakerName)\n\tif (self.Speakers[speakerName:lower()]) then\n\t\terror(\"Speaker \\\"\" .. speakerName .. \"\\\" already exists!\")\n\tend\n\n\tlocal speaker = Speaker.new(self, speakerName)\n\tself.Speakers[speakerName:lower()] = speaker\n\n\tlocal success, err = pcall(function() self.eSpeakerAdded:Fire(speakerName) end)\n\tif not success and err then\n\t\tprint(\"Error adding speaker: \" ..err)\n\tend\n\n\treturn speaker\nend\n\nfunction methods:InternalUnmuteSpeaker(speakerName)\n\tfor channelName, channel in pairs(self.ChatChannels) do\n\t\tif channel:IsSpeakerMuted(speakerName) then\n\t\t\tchannel:UnmuteSpeaker(speakerName)\n\t\tend\n\tend\nend\n\nfunction methods:RemoveSpeaker(speakerName)\n\tif (self.Speakers[speakerName:lower()]) then\n\t\tlocal n = self.Speakers[speakerName:lower()].Name\n\t\tself:InternalUnmuteSpeaker(n)\n\n\t\tself.Speakers[speakerName:lower()]:InternalDestroy()\n\t\tself.Speakers[speakerName:lower()] = nil\n\n\t\tlocal success, err = pcall(function() self.eSpeakerRemoved:Fire(n) end)\n\t\tif not success and err then\n\t\t\tprint(\"Error removing speaker: \" ..err)\n\t\tend\n\n\telse\n\t\twarn(\"Speaker \\\"\" .. speakerName .. \"\\\" does not exist!\")\n\tend\nend\n\nfunction methods:GetSpeaker(speakerName)\n\treturn self.Speakers[speakerName:lower()]\nend\n\nfunction methods:GetChannelList()\n\tlocal list = {}\n\tfor i, channel in pairs(self.ChatChannels) do\n\t\tif (not channel.Private) then\n\t\t\ttable.insert(list, channel.Name)\n\t\tend\n\tend\n\treturn list\nend\n\nfunction methods:GetAutoJoinChannelList()\n\tlocal list = {}\n\tfor i, channel in pairs(self.ChatChannels) do\n\t\tif channel.AutoJoin then\n\t\t\ttable.insert(list, channel)\n\t\tend\n\tend\n\treturn list\nend\n\nfunction methods:GetSpeakerList()\n\tlocal list = {}\n\tfor i, speaker in pairs(self.Speakers) do\n\t\ttable.insert(list, speaker.Name)\n\tend\n\treturn list\nend\n\nfunction methods:SendGlobalSystemMessage(message)\n\tfor i, speaker in pairs(self.Speakers) do\n\t\tspeaker:SendSystemMessage(message, nil)\n\tend\nend\n\nfunction methods:RegisterFilterMessageFunction(funcId, func, priority)\n\tif self.FilterMessageFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"FilterMessageFunction '%s' already exists\", funcId))\n\tend\n\tself.FilterMessageFunctions:AddFunction(funcId, func, priority)\nend\n\nfunction methods:FilterMessageFunctionExists(funcId)\n\treturn self.FilterMessageFunctions:HasFunction(funcId)\nend\n\nfunction methods:UnregisterFilterMessageFunction(funcId)\n\tif not self.FilterMessageFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"FilterMessageFunction '%s' does not exists\", funcId))\n\tend\n\tself.FilterMessageFunctions:RemoveFunction(funcId)\nend\n\nfunction methods:RegisterProcessCommandsFunction(funcId, func, priority)\n\tif self.ProcessCommandsFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"ProcessCommandsFunction '%s' already exists\", funcId))\n\tend\n\tself.ProcessCommandsFunctions:AddFunction(funcId, func, priority)\nend\n\nfunction methods:ProcessCommandsFunctionExists(funcId)\n\treturn self.ProcessCommandsFunctions:HasFunction(funcId)\nend\n\nfunction methods:UnregisterProcessCommandsFunction(funcId)\n\tif not self.ProcessCommandsFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"ProcessCommandsFunction '%s' does not exist\", funcId))\n\tend\n\tself.ProcessCommandsFunctions:RemoveFunction(funcId)\nend\n\nlocal LastFilterNoficationTime = 0\nlocal LastFilterIssueTime = 0\nlocal FilterIssueCount = 0\nfunction methods:InternalNotifyFilterIssue()\n\tif (tick() - LastFilterIssueTime) > FILTER_THRESHOLD_TIME then\n\t\tFilterIssueCount = 0\n\tend\n\tFilterIssueCount = FilterIssueCount + 1\n\tLastFilterIssueTime = tick()\n\tif FilterIssueCount >= FILTER_NOTIFCATION_THRESHOLD then\n\t\tif (tick() - LastFilterNoficationTime) > FILTER_NOTIFCATION_INTERVAL then\n\t\t\tLastFilterNoficationTime = tick()\n\t\t\tlocal systemChannel = self:GetChannel(\"System\")\n\t\t\tif systemChannel then\n\t\t\t\tsystemChannel:SendSystemMessage(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatService_ChatFilterIssues\",\n\t\t\t\t\t\t\"The chat filter is currently experiencing issues and messages may be slow to appear.\"\n\t\t\t\t\t),\n\t\t\t\t\terrorExtraData\n\t\t\t\t)\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal StudioMessageFilteredCache = {}\n\n--///////////////// Internal-Use Methods\n--//////////////////////////////////////\n--DO NOT REMOVE THIS. Chat must be filtered or your game will face\n--moderation.\nfunction methods:InternalApplyRobloxFilter(speakerName, message, toSpeakerName) --// USES FFLAG\n\tif (RunService:IsServer() and not RunService:IsStudio()) then\n\t\tlocal fromSpeaker = self:GetSpeaker(speakerName)\n\t\tlocal toSpeaker = toSpeakerName and self:GetSpeaker(toSpeakerName)\n\n\t\tif fromSpeaker == nil then\n\t\t\treturn nil\n\t\tend\n\n\t\tlocal fromPlayerObj = fromSpeaker:GetPlayer()\n\t\tlocal toPlayerObj = toSpeaker and toSpeaker:GetPlayer()\n\n\t\tif fromPlayerObj == nil then\n\t\t\treturn message\n\t\tend\n\n\t\tlocal filterStartTime = tick()\n\t\tlocal filterRetries = 0\n\t\twhile true do\n\t\t\tlocal success, message = pcall(function()\n\t\t\t\tif toPlayerObj then\n\t\t\t\t\treturn Chat:FilterStringAsync(message, fromPlayerObj, toPlayerObj)\n\t\t\t\telse\n\t\t\t\t\treturn Chat:FilterStringForBroadcast(message, fromPlayerObj)\n\t\t\t\tend\n\t\t\tend)\n\t\t\tif success then\n\t\t\t\treturn message\n\t\t\telse\n\t\t\t\twarn(\"Error filtering message:\", message)\n\t\t\tend\n\t\t\tfilterRetries = filterRetries + 1\n\t\t\tif filterRetries > MAX_FILTER_RETRIES or (tick() - filterStartTime) > MAX_FILTER_DURATION then\n\t\t\t\tself:InternalNotifyFilterIssue()\n\t\t\t\treturn nil\n\t\t\tend\n\t\t\tlocal backoffInterval = FILTER_BACKOFF_INTERVALS[math.min(#FILTER_BACKOFF_INTERVALS, filterRetries)]\n\t\t\t-- backoffWait = backoffInterval +/- (0 -> backoffInterval)\n\t\t\tlocal backoffWait = backoffInterval + ((math.random()*2 - 1) * backoffInterval)\n\t\t\twait(backoffWait)\n\t\tend\n\telse\n\t\t--// Simulate filtering latency.\n\t\t--// There is only latency the first time the message is filtered, all following calls will be instant.\n\t\tif not StudioMessageFilteredCache[message] then\n\t\t\tStudioMessageFilteredCache[message] = true\n\t\t\twait()\n\t\tend\n\t\treturn message\n\tend\n\n\treturn nil\nend\n\n--// Return values: bool filterSuccess, bool resultIsFilterObject, variant result\nfunction methods:InternalApplyRobloxFilterNewAPI(speakerName, message, textFilterContext) --// USES FFLAG\n\tlocal alwaysRunFilter = false\n\tlocal runFilter = RunService:IsServer() and not RunService:IsStudio()\n\tif (alwaysRunFilter or runFilter) then\n\t\tlocal fromSpeaker = self:GetSpeaker(speakerName)\n\t\tif fromSpeaker == nil then\n\t\t\treturn false, nil, nil\n\t\tend\n\n\t\tlocal fromPlayerObj = fromSpeaker:GetPlayer()\n\t\tif fromPlayerObj == nil then\n\t\t\treturn true, false, message\n\t\tend\n\n\t\tlocal success, filterResult = pcall(function()\n\t\t\tlocal ts = game:GetService(\"TextService\")\n\t\t\tlocal result = ts:FilterStringAsync(message, fromPlayerObj.UserId, textFilterContext)\n\t\t\treturn result\n\t\tend)\n\t\tif (success) then\n\t\t\treturn true, true, filterResult\n\t\telse\n\t\t\twarn(\"Error filtering message:\", message, filterResult)\n\t\t\tself:InternalNotifyFilterIssue()\n\t\t\treturn false, nil, nil\n\t\tend\n\tend\n\n\t--// Simulate filtering latency.\n\twait()\n\treturn true, false, message\nend\n\nfunction methods:InternalDoMessageFilter(speakerName, messageObj, channel)\n\tlocal filtersIterator = self.FilterMessageFunctions:GetIterator()\n\n\tfor funcId, func, priority in filtersIterator do\n\t\tlocal success, errorMessage = pcall(function()\n\t\t\tfunc(speakerName, messageObj, channel)\n\t\tend)\n\n\t\tif not success then\n\t\t\twarn(string.format(\"DoMessageFilter Function '%s' failed for reason: %s\", funcId, errorMessage))\n\t\tend\n\tend\nend\n\nfunction methods:InternalDoProcessCommands(speakerName, message, channel)\n\tlocal commandsIterator = self.ProcessCommandsFunctions:GetIterator()\n\n\tfor funcId, func, priority in commandsIterator do\n\t\tlocal success, returnValue = pcall(function()\n\t\t\tlocal ret = func(speakerName, message, channel)\n\t\t\tif type(ret) ~= \"boolean\" then\n\t\t\t\terror(\"Process command functions must return a bool\")\n\t\t\tend\n\t\t\treturn ret\n\t\tend)\n\n\t\tif not success then\n\t\t\twarn(string.format(\"DoProcessCommands Function '%s' failed for reason: %s\", funcId, returnValue))\n\t\telseif returnValue then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nfunction methods:InternalGetUniqueMessageId()\n\tlocal id = self.MessageIdCounter\n\tself.MessageIdCounter = id + 1\n\treturn id\nend\n\nfunction methods:InternalAddSpeakerWithPlayerObject(speakerName, playerObj, fireSpeakerAdded)\n\tif (self.Speakers[speakerName:lower()]) then\n\t\terror(\"Speaker \\\"\" .. speakerName .. \"\\\" already exists!\")\n\tend\n\n\tlocal speaker = Speaker.new(self, speakerName)\n\tspeaker:InternalAssignPlayerObject(playerObj)\n\tself.Speakers[speakerName:lower()] = speaker\n\n\tif fireSpeakerAdded then\n\t\tlocal success, err = pcall(function() self.eSpeakerAdded:Fire(speakerName) end)\n\t\tif not success and err then\n\t\t\tprint(\"Error adding speaker: \" ..err)\n\t\tend\n\tend\n\n\treturn speaker\nend\n\nfunction methods:InternalFireSpeakerAdded(speakerName)\n\tlocal success, err = pcall(function() self.eSpeakerAdded:Fire(speakerName) end)\n\tif not success and err then\n\t\tprint(\"Error firing speaker added: \" ..err)\n\tend\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.MessageIdCounter = 0\n\n\tobj.ChatChannels = {}\n\tobj.Speakers = {}\n\n\tobj.FilterMessageFunctions = Util:NewSortedFunctionContainer()\n\tobj.ProcessCommandsFunctions = Util:NewSortedFunctionContainer()\n\n\tobj.eChannelAdded = Instance.new(\"BindableEvent\")\n\tobj.eChannelRemoved = Instance.new(\"BindableEvent\")\n\tobj.eSpeakerAdded = Instance.new(\"BindableEvent\")\n\tobj.eSpeakerRemoved = Instance.new(\"BindableEvent\")\n\n\tobj.ChannelAdded = obj.eChannelAdded.Event\n\tobj.ChannelRemoved = obj.eChannelRemoved.Event\n\tobj.SpeakerAdded = obj.eSpeakerAdded.Event\n\tobj.SpeakerRemoved = obj.eSpeakerRemoved.Event\n\n\tobj.ChatServiceMajorVersion = 0\n\tobj.ChatServiceMinorVersion = 5\n\n\treturn obj\nend\n\nreturn module.new()\n"
  },
  {
    "path": "src/Chat/ChatServiceRunner/Speaker.lua",
    "content": "--\t// FileName: Speaker.lua\n--\t// Written by: Xsitsu\n--\t// Description: A representation of one entity that can chat in different ChatChannels.\n\nlocal module = {}\n\nlocal modulesFolder = script.Parent\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal function ShallowCopy(table)\n\tlocal copy = {}\n\tfor i, v in pairs(table) do\n\t\tcopy[i] = v\n\tend\n\treturn copy\nend\n\nlocal methods = {}\n\nlocal lazyEventNames = \n{\n    eDestroyed = true,\n\teSaidMessage = true,\n\teReceivedMessage = true,\n\teReceivedUnfilteredMessage = true,\n\teMessageDoneFiltering = true,\n\teReceivedSystemMessage = true,\n\teChannelJoined = true,\n\teChannelLeft = true,\n\teMuted = true,\n\teUnmuted = true,\n\teExtraDataUpdated = true,\n\teMainChannelSet = true,\n\teChannelNameColorUpdated = true,\n}\nlocal lazySignalNames =\n{\n\tDestroyed = \"eDestroyed\",\n\tSaidMessage = \"eSaidMessage\",\n\tReceivedMessage = \"eReceivedMessage\",\n\tReceivedUnfilteredMessage = \"eReceivedUnfilteredMessage\",\n    RecievedUnfilteredMessage = \"eReceivedUnfilteredMessage\", -- legacy mispelling\n\tMessageDoneFiltering = \"eMessageDoneFiltering\",\n\tReceivedSystemMessage = \"eReceivedSystemMessage\",\n\tChannelJoined = \"eChannelJoined\",\n\tChannelLeft = \"eChannelLeft\",\n\tMuted = \"eMuted\",\n\tUnmuted = \"eUnmuted\",\n\tExtraDataUpdated = \"eExtraDataUpdated\",\n\tMainChannelSet = \"eMainChannelSet\",\n\tChannelNameColorUpdated = \"eChannelNameColorUpdated\"\n}\n\nmethods.__index = function (self, k)\n\tlocal fromMethods = rawget(methods, k)\n\tif fromMethods then return fromMethods end\n\t\n    if lazyEventNames[k] and not rawget(self, k) then\n        rawset(self, k, Instance.new(\"BindableEvent\"))\n    end\n    local lazySignalEventName = lazySignalNames[k]\n    if lazySignalEventName and not rawget(self, k) then\n        if not rawget(self, lazySignalEventName) then\n            rawset(self, lazySignalEventName, Instance.new(\"BindableEvent\"))\n        end\n        rawset(self, k, rawget(self, lazySignalEventName).Event)\n    end\n    return rawget(self, k)\nend\n\nfunction methods:LazyFire(eventName, ...)\n\tlocal createdEvent = rawget(self, eventName)\n\tif createdEvent then\n\t\tcreatedEvent:Fire(...)\n\tend\nend\n\nfunction methods:SayMessage(message, channelName, extraData)\n\tif (self.ChatService:InternalDoProcessCommands(self.Name, message, channelName)) then return end\n\tif (not channelName) then return end\n\n\tlocal channel = self.Channels[channelName:lower()]\n\tif (not channel) then\n\t\terror(\"Speaker is not in channel \\\"\" .. channelName .. \"\\\"\")\n\tend\n\n\tlocal messageObj = channel:InternalPostMessage(self, message, extraData)\n\tif (messageObj) then\n\t\tlocal success, err = pcall(function() self:LazyFire(\"eSaidMessage\", messageObj, channelName) end)\n\t\tif not success and err then\n\t\t\tprint(\"Error saying message: \" ..err)\n\t\tend\n\tend\n\n\treturn messageObj\nend\n\nfunction methods:JoinChannel(channelName)\n\tif (self.Channels[channelName:lower()]) then\n\t\twarn(\"Speaker is already in channel \\\"\" .. channelName .. \"\\\"\")\n\t\treturn\n\tend\n\n\tlocal channel = self.ChatService:GetChannel(channelName)\n\tif (not channel) then\n\t\terror(\"Channel \\\"\" .. channelName .. \"\\\" does not exist!\")\n\tend\n\n\tself.Channels[channelName:lower()] = channel\n\tchannel:InternalAddSpeaker(self)\n\tlocal success, err = pcall(function()\n\t\tself.eChannelJoined:Fire(channel.Name, channel:GetWelcomeMessageForSpeaker(self))\n\tend)\n\tif not success and err then\n\t\tprint(\"Error joining channel: \" ..err)\n\tend\nend\n\nfunction methods:LeaveChannel(channelName)\n\tif (not self.Channels[channelName:lower()]) then\n\t\twarn(\"Speaker is not in channel \\\"\" .. channelName .. \"\\\"\")\n\t\treturn\n\tend\n\n\tlocal channel = self.Channels[channelName:lower()]\n\n\tself.Channels[channelName:lower()] = nil\n\tchannel:InternalRemoveSpeaker(self)\n\tlocal success, err = pcall(function()\n\t\tself:LazyFire(\"eChannelLeft\", channel.Name)\n\t\tself.EventFolder.OnChannelLeft:FireClient(self.PlayerObj, channel.Name)\n\tend)\n\tif not success and err then\n\t\tprint(\"Error leaving channel: \" ..err)\n\tend\nend\n\nfunction methods:IsInChannel(channelName)\n\treturn (self.Channels[channelName:lower()] ~= nil)\nend\n\nfunction methods:GetChannelList()\n\tlocal list = {}\n\tfor i, channel in pairs(self.Channels) do\n\t\ttable.insert(list, channel.Name)\n\tend\n\treturn list\nend\n\nfunction methods:SendMessage(message, channelName, fromSpeaker, extraData)\n\tlocal channel = self.Channels[channelName:lower()]\n\tif (channel) then\n\t\tchannel:SendMessageToSpeaker(message, self.Name, fromSpeaker, extraData)\n\n\telse\n\t\twarn(string.format(\"Speaker '%s' is not in channel '%s' and cannot receive a message in it.\", self.Name, channelName))\n\n\tend\nend\n\nfunction methods:SendSystemMessage(message, channelName, extraData)\n\tlocal channel = self.Channels[channelName:lower()]\n\tif (channel) then\n\t\tchannel:SendSystemMessageToSpeaker(message, self.Name, extraData)\n\n\telse\n\t\twarn(string.format(\"Speaker '%s' is not in channel '%s' and cannot receive a system message in it.\", self.Name, channelName))\n\n\tend\nend\n\nfunction methods:GetPlayer()\n\treturn self.PlayerObj\nend\n\nfunction methods:SetExtraData(key, value)\n\tself.ExtraData[key] = value\n\tself:LazyFire(\"eExtraDataUpdated\", key, value)\nend\n\nfunction methods:GetExtraData(key)\n\treturn self.ExtraData[key]\nend\n\nfunction methods:SetMainChannel(channelName)\n\tlocal success, err = pcall(function()\n\t\tself:LazyFire(\"eMainChannelSet\", channelName)\n\t\tself.EventFolder.OnMainChannelSet:FireClient(self.PlayerObj, channelName)\n\tend)\n\tif not success and err then\n\t\tprint(\"Error setting main channel: \" ..err)\n\tend\nend\n\n--- Used to mute a speaker so that this speaker does not see their messages.\nfunction methods:AddMutedSpeaker(speakerName)\n\tself.MutedSpeakers[speakerName:lower()] = true\nend\n\nfunction methods:RemoveMutedSpeaker(speakerName)\n\tself.MutedSpeakers[speakerName:lower()] = false\nend\n\nfunction methods:IsSpeakerMuted(speakerName)\n\treturn self.MutedSpeakers[speakerName:lower()]\nend\n\n--///////////////// Internal-Use Methods\n--//////////////////////////////////////\nfunction methods:InternalDestroy()\n\tfor i, channel in pairs(self.Channels) do\n\t\tchannel:InternalRemoveSpeaker(self)\n\tend\n\n\tself.eDestroyed:Fire()\n\n\tself.EventFolder = nil\n\tself.eDestroyed:Destroy()\n\tself.eSaidMessage:Destroy()\n\tself.eReceivedMessage:Destroy()\n\tself.eReceivedUnfilteredMessage:Destroy()\n\tself.eMessageDoneFiltering:Destroy()\n\tself.eReceivedSystemMessage:Destroy()\n\tself.eChannelJoined:Destroy()\n\tself.eChannelLeft:Destroy()\n\tself.eMuted:Destroy()\n\tself.eUnmuted:Destroy()\n\tself.eExtraDataUpdated:Destroy()\n\tself.eMainChannelSet:Destroy()\n\tself.eChannelNameColorUpdated:Destroy()\nend\n\nfunction methods:InternalAssignPlayerObject(playerObj)\n\tself.PlayerObj = playerObj\nend\n\nfunction methods:InternalAssignEventFolder(eventFolder)\n\tself.EventFolder = eventFolder\nend\n\nfunction methods:InternalSendMessage(messageObj, channelName)\n\tlocal success, err = pcall(function()\n\t\tself:LazyFire(\"eReceivedUnfilteredMessage\", messageObj, channelName)\n\t\tself.EventFolder.OnNewMessage:FireClient(self.PlayerObj, messageObj, channelName)\n\tend)\n\tif not success and err then\n\t\tprint(\"Error sending internal message: \" ..err)\n\tend\nend\n\nfunction methods:InternalSendFilteredMessage(messageObj, channelName)\n\tlocal success, err = pcall(function()\n\t\tself:LazyFire(\"eReceivedMessage\", messageObj, channelName)\n\t\tself:LazyFire(\"eMessageDoneFiltering\", messageObj, channelName)\n\t\tself.EventFolder.OnMessageDoneFiltering:FireClient(self.PlayerObj, messageObj, channelName)\n\tend)\n\tif not success and err then\n\t\tprint(\"Error sending internal filtered message: \" ..err)\n\tend\nend\n\n--// This method is to be used with the new filter API. This method takes the \n--// TextFilterResult objects and converts them into the appropriate string\n--// messages for each player.\nfunction methods:InternalSendFilteredMessageWithFilterResult(inMessageObj, channelName)\n\tlocal messageObj = ShallowCopy(inMessageObj)\n\n\tlocal oldFilterResult = messageObj.FilterResult\n\tlocal player = self:GetPlayer()\n\n\tlocal msg = \"\"\n\tpcall(function()\n\t\tif (messageObj.IsFilterResult) then\n\t\t\tif (player) then\n\t\t\t\tmsg = oldFilterResult:GetChatForUserAsync(player.UserId)\n\t\t\telse\n\t\t\t\tmsg = oldFilterResult:GetNonChatStringForBroadcastAsync()\n\t\t\tend\n\t\telse\n\t\t\tmsg = oldFilterResult\n\t\tend\n\tend)\n\n\t--// Messages of 0 length are the result of two users not being allowed\n\t--// to chat, or GetChatForUserAsync() failing. In both of these situations,\n\t--// messages with length of 0 should not be sent.\n\tif (#msg > 0) then\n\t\tmessageObj.Message = msg\n\t\tmessageObj.FilterResult = nil\n\t\tself:InternalSendFilteredMessage(messageObj, channelName)\n\tend\nend\n\nfunction methods:InternalSendSystemMessage(messageObj, channelName)\n\tlocal success, err = pcall(function()\n\t\tself:LazyFire(\"eReceivedSystemMessage\", messageObj, channelName)\n\t\tself.EventFolder.OnNewSystemMessage:FireClient(self.PlayerObj, messageObj, channelName)\n\tend)\n\tif not success and err then\n\t\tprint(\"Error sending internal system message: \" ..err)\n\tend\nend\n\nfunction methods:UpdateChannelNameColor(channelName, channelNameColor)\n\tself:LazyFire(\"eChannelNameColorUpdated\", channelName, channelNameColor)\n\tself.EventFolder.ChannelNameColorUpdated:FireClient(self.PlayerObj, channelName, channelNameColor)\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new(vChatService, name)\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.ChatService = vChatService\n\n\tobj.PlayerObj = nil\n\n\tobj.Name = name\n\tobj.ExtraData = {}\n\n\tobj.Channels = {}\n\tobj.MutedSpeakers = {}\n\tobj.EventFolder = nil\n\t\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/Chat/ChatServiceRunner/Util.lua",
    "content": "--\t// FileName: Util.lua\n--\t// Written by: TheGamer101\n--\t// Description: Utility code used by the server side chat implementation.\n\nlocal Chat = game:GetService(\"Chat\")\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\n\nlocal DEFAULT_PRIORITY = ChatConstants.StandardPriority\nif DEFAULT_PRIORITY == nil then\n\tDEFAULT_PRIORITY = 10\nend\n\nlocal Util = {}\nUtil.__index = Util\n\nlocal SortedFunctionContainer = {}; do\n\t-- This sorted function container is used to handle the logic around storing filter functions and\n\t-- command processors by priority.\n\n\tlocal methods = {}\n\tmethods.__index = methods\n\n\tfunction methods:RebuildProcessCommandsPriorities()\n\t\tself.RegisteredPriorites = {}\n\t\tfor priority, functions in pairs(self.FunctionMap) do\n\t\t\tlocal functionsEmpty = true\n\t\t\tfor funcId, funciton in pairs(functions) do\n\t\t\t\tfunctionsEmpty = false\n\t\t\t\tbreak\n\t\t\tend\n\t\t\tif not functionsEmpty then\n\t\t\t\ttable.insert(self.RegisteredPriorites, priority)\n\t\t\tend\n\t\tend\n\t\ttable.sort(self.RegisteredPriorites, function(a, b)\n\t\t\treturn a > b\n\t\tend)\n\tend\n\n\tfunction methods:HasFunction(funcId)\n\t\tif self.RegisteredFunctions[funcId] == nil then\n\t\t\treturn false\n\t\tend\n\t\treturn true\n\tend\n\n\tfunction methods:RemoveFunction(funcId)\n\t\tlocal functionPriority = self.RegisteredFunctions[funcId]\n\t\tself.RegisteredFunctions[funcId] = nil\n\t\tself.FunctionMap[functionPriority][funcId] = nil\n\t\tself:RebuildProcessCommandsPriorities()\n\tend\n\n\tfunction methods:AddFunction(funcId, func, priority)\n\t\tif priority == nil then\n\t\t\tpriority = DEFAULT_PRIORITY\n\t\tend\n\n\t\tif self.RegisteredFunctions[funcId] then\n\t\t\terror(funcId .. \" is already in use!\")\n\t\tend\n\n\t\tself.RegisteredFunctions[funcId] = priority\n\n\t\tif self.FunctionMap[priority] == nil then\n\t\t\tself.FunctionMap[priority] = {}\n\t\tend\n\n\t\tself.FunctionMap[priority][funcId] = func\n\t\tself:RebuildProcessCommandsPriorities()\n\tend\n\n\tfunction methods:GetIterator()\n\t\tlocal priorityIndex = 1\n\t\tlocal funcId = nil\n\t\tlocal func = nil\n\n\t\treturn function()\n\t\t\twhile true do\n\t\t\t\tif priorityIndex > #self.RegisteredPriorites then\n\t\t\t\t\treturn\n\t\t\t\tend\n\t\t\t\tlocal priority = self.RegisteredPriorites[priorityIndex]\n\t\t\t\tfuncId, func = next(self.FunctionMap[priority], funcId)\n\t\t\t\tif funcId == nil then\n\t\t\t\t\tpriorityIndex = priorityIndex + 1\n\t\t\t\telse\n\t\t\t\t\treturn funcId, func, priority\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction SortedFunctionContainer.new()\n\t\tlocal obj = setmetatable({}, methods)\n\n\t\tobj.RegisteredFunctions = {}\n\t\tobj.RegisteredPriorites = {}\n\t\tobj.FunctionMap = {}\n\n\t\treturn obj\n\tend\nend\n\nfunction Util:NewSortedFunctionContainer()\n\treturn SortedFunctionContainer.new()\nend\n\nreturn Util\n"
  },
  {
    "path": "src/Chat/ChatServiceRunner/init.server.lua",
    "content": "--\t// FileName: ChatServiceRunner.lua\n--\t// Written by: Xsitsu\n--\t// Description: Main script to initialize ChatService and run ChatModules.\n\nlocal EventFolderName = \"DefaultChatSystemChatEvents\"\nlocal EventFolderParent = game:GetService(\"ReplicatedStorage\")\nlocal modulesFolder = script\n\nlocal PlayersService = game:GetService(\"Players\")\nlocal RunService = game:GetService(\"RunService\")\nlocal Chat = game:GetService(\"Chat\")\n\nlocal ChatService = require(modulesFolder:WaitForChild(\"ChatService\"))\n\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatSettings = require(ReplicatedModules:WaitForChild(\"ChatSettings\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(Chat.ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = { Get = function(key,default) return default end } end\n\nlocal useEvents = {}\n\nlocal EventFolder = EventFolderParent:FindFirstChild(EventFolderName)\nif (not EventFolder) then\n\tEventFolder = Instance.new(\"Folder\")\n\tEventFolder.Name = EventFolderName\n\tEventFolder.Archivable = false\n\tEventFolder.Parent = EventFolderParent\nend\n\n--// No-opt connect Server>Client RemoteEvents to ensure they cannot be called\n--// to fill the remote event queue.\nlocal function emptyFunction()\n\t--intentially empty\nend\n\nlocal function GetObjectWithNameAndType(parentObject, objectName, objectType)\n\tfor _, child in pairs(parentObject:GetChildren()) do\n\t\tif (child:IsA(objectType) and child.Name == objectName) then\n\t\t\treturn child\n\t\tend\n\tend\n\n\treturn nil\nend\n\nlocal function CreateIfDoesntExist(parentObject, objectName, objectType)\n\tlocal obj = GetObjectWithNameAndType(parentObject, objectName, objectType)\n\tif (not obj) then\n\t\tobj = Instance.new(objectType)\n\t\tobj.Name = objectName\n\t\tobj.Parent = parentObject\n\tend\n\tuseEvents[objectName] = obj\n\n\treturn obj\nend\n\n--// All remote events will have a no-opt OnServerEvent connecdted on construction\nlocal function CreateEventIfItDoesntExist(parentObject, objectName)\n\tlocal obj = CreateIfDoesntExist(parentObject, objectName, \"RemoteEvent\")\n\tobj.OnServerEvent:Connect(emptyFunction)\n\treturn obj\nend\n\nCreateEventIfItDoesntExist(EventFolder, \"OnNewMessage\")\nCreateEventIfItDoesntExist(EventFolder, \"OnMessageDoneFiltering\")\nCreateEventIfItDoesntExist(EventFolder, \"OnNewSystemMessage\")\nCreateEventIfItDoesntExist(EventFolder, \"OnChannelJoined\")\nCreateEventIfItDoesntExist(EventFolder, \"OnChannelLeft\")\nCreateEventIfItDoesntExist(EventFolder, \"OnMuted\")\nCreateEventIfItDoesntExist(EventFolder, \"OnUnmuted\")\nCreateEventIfItDoesntExist(EventFolder, \"OnMainChannelSet\")\nCreateEventIfItDoesntExist(EventFolder, \"ChannelNameColorUpdated\")\n\nCreateEventIfItDoesntExist(EventFolder, \"SayMessageRequest\")\nCreateEventIfItDoesntExist(EventFolder, \"SetBlockedUserIdsRequest\")\nCreateIfDoesntExist(EventFolder, \"GetInitDataRequest\", \"RemoteFunction\")\nCreateIfDoesntExist(EventFolder, \"MutePlayerRequest\", \"RemoteFunction\")\nCreateIfDoesntExist(EventFolder, \"UnMutePlayerRequest\", \"RemoteFunction\")\n\nEventFolder = useEvents\n\nlocal function CreatePlayerSpeakerObject(playerObj)\n\t--// If a developer already created a speaker object with the\n\t--// name of a player and then a player joins and tries to\n\t--// take that name, we first need to remove the old speaker object\n\tlocal speaker = ChatService:GetSpeaker(playerObj.Name)\n\tif (speaker) then\n\t\tChatService:RemoveSpeaker(playerObj.Name)\n\tend\n\n\tspeaker = ChatService:InternalAddSpeakerWithPlayerObject(playerObj.Name, playerObj, false)\n\n\tfor _, channel in pairs(ChatService:GetAutoJoinChannelList()) do\n\t\tspeaker:JoinChannel(channel.Name)\n\tend\n\n\tspeaker:InternalAssignEventFolder(EventFolder)\n\n\tspeaker.ChannelJoined:connect(function(channel, welcomeMessage)\n\t\tlocal log = nil\n\t\tlocal channelNameColor = nil\n\n\t\tlocal channelObject = ChatService:GetChannel(channel)\n\t\tif (channelObject) then\n\t\t\tlog = channelObject:GetHistoryLogForSpeaker(speaker)\n\t\t\tchannelNameColor = channelObject.ChannelNameColor\n\t\tend\n\t\tEventFolder.OnChannelJoined:FireClient(playerObj, channel, welcomeMessage, log, channelNameColor)\n\tend)\n\n\tspeaker.Muted:connect(function(channel, reason, length)\n\t\tEventFolder.OnMuted:FireClient(playerObj, channel, reason, length)\n\tend)\n\n\tspeaker.Unmuted:connect(function(channel)\n\t\tEventFolder.OnUnmuted:FireClient(playerObj, channel)\n\tend)\n\n\tChatService:InternalFireSpeakerAdded(speaker.Name)\nend\n\nEventFolder.SayMessageRequest.OnServerEvent:connect(function(playerObj, message, channel)\n\tif type(message) ~= \"string\" then\n\t\treturn\n\tend\n\tif type(channel) ~= \"string\" then\n\t\treturn\n\tend\n\n\tlocal speaker = ChatService:GetSpeaker(playerObj.Name)\n\tif (speaker) then\n\t\treturn speaker:SayMessage(message, channel)\n\tend\n\n\treturn nil\nend)\n\nEventFolder.MutePlayerRequest.OnServerInvoke = function(playerObj, muteSpeakerName)\n\tif type(muteSpeakerName) ~= \"string\" then\n\t\treturn\n\tend\n\n\tlocal speaker = ChatService:GetSpeaker(playerObj.Name)\n\tif speaker then\n\t\tlocal muteSpeaker = ChatService:GetSpeaker(muteSpeakerName)\n\t\tif muteSpeaker then\n\t\t\tspeaker:AddMutedSpeaker(muteSpeaker.Name)\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\nEventFolder.UnMutePlayerRequest.OnServerInvoke = function(playerObj, unmuteSpeakerName)\n\tif type(unmuteSpeakerName) ~= \"string\" then\n\t\treturn\n\tend\n\n\tlocal speaker = ChatService:GetSpeaker(playerObj.Name)\n\tif speaker then\n\t\tlocal unmuteSpeaker = ChatService:GetSpeaker(unmuteSpeakerName)\n\t\tif unmuteSpeaker then\n\t\t\tspeaker:RemoveMutedSpeaker(unmuteSpeaker.Name)\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\n-- Map storing Player -> Blocked user Ids.\nlocal BlockedUserIdsMap = {}\n\nPlayersService.PlayerAdded:connect(function(newPlayer)\n\tfor player, blockedUsers in pairs(BlockedUserIdsMap) do\n\t\tlocal speaker = ChatService:GetSpeaker(player.Name)\n\t\tif speaker then\n\t\t\tfor i = 1, #blockedUsers do\n\t\t\t\tlocal blockedUserId = blockedUsers[i]\n\t\t\t\tif blockedUserId == newPlayer.UserId then\n\t\t\t\t\tspeaker:AddMutedSpeaker(newPlayer.Name)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend)\n\nPlayersService.PlayerRemoving:connect(function(removingPlayer)\n\tBlockedUserIdsMap[removingPlayer] = nil\nend)\n\nEventFolder.SetBlockedUserIdsRequest.OnServerEvent:connect(function(player, blockedUserIdsList)\n\tif type(blockedUserIdsList) ~= \"table\" then\n\t\treturn\n\tend\n\n\tBlockedUserIdsMap[player] = blockedUserIdsList\n\tlocal speaker = ChatService:GetSpeaker(player.Name)\n\tif speaker then\n\t\tfor i = 1, #blockedUserIdsList do\n\t\t\tif type(blockedUserIdsList[i]) == \"number\" then\n\t\t\t\tlocal blockedPlayer = PlayersService:GetPlayerByUserId(blockedUserIdsList[i])\n\t\t\t\tif blockedPlayer then\n\t\t\t\t\tspeaker:AddMutedSpeaker(blockedPlayer.Name)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend)\n\nEventFolder.GetInitDataRequest.OnServerInvoke = (function(playerObj)\n\tlocal speaker = ChatService:GetSpeaker(playerObj.Name)\n\tif not (speaker and speaker:GetPlayer()) then\n\t\tCreatePlayerSpeakerObject(playerObj)\n\t\tspeaker = ChatService:GetSpeaker(playerObj.Name)\n\tend\n\n\tlocal data = {}\n\tdata.Channels = {}\n\tdata.SpeakerExtraData = {}\n\n\tfor _, channelName in pairs(speaker:GetChannelList()) do\n\t\tlocal channelObj = ChatService:GetChannel(channelName)\n\t\tif (channelObj) then\n\t\t\tlocal channelData =\n\t\t\t{\n\t\t\t\tchannelName,\n\t\t\t\tchannelObj:GetWelcomeMessageForSpeaker(speaker),\n\t\t\t\tchannelObj:GetHistoryLogForSpeaker(speaker),\n\t\t\t\tchannelObj.ChannelNameColor,\n\t\t\t}\n\n\t\t\ttable.insert(data.Channels, channelData)\n\t\tend\n\tend\n\n\tfor _, oSpeakerName in pairs(ChatService:GetSpeakerList()) do\n\t\tlocal oSpeaker = ChatService:GetSpeaker(oSpeakerName)\n\t\tdata.SpeakerExtraData[oSpeakerName] = oSpeaker.ExtraData\n\tend\n\n\treturn data\nend)\n\nlocal function DoJoinCommand(speakerName, channelName, fromChannelName)\n\tlocal speaker = ChatService:GetSpeaker(speakerName)\n\tlocal channel = ChatService:GetChannel(channelName)\n\n\tif (speaker) then\n\t\tif (channel) then\n\t\t\tif (channel.Joinable) then\n\t\t\t\tif (not speaker:IsInChannel(channel.Name)) then\n\t\t\t\t\tspeaker:JoinChannel(channel.Name)\n\t\t\t\telse\n\t\t\t\t\tspeaker:SetMainChannel(channel.Name)\n\t\t\t\t\tspeaker:SendSystemMessage(\n\t\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\t\"GameChat_SwitchChannel_NowInChannel\",\n\t\t\t\t\t\t\t\tstring.format(\"You are now chatting in channel: '%s'\", channel.Name)\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\"{RBX_NAME}\",channel.Name),\n\t\t\t\t\t\tchannel.Name\n\t\t\t\t\t)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tspeaker:SendSystemMessage(\n\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\"GameChat_ChatServiceRunner_YouCannotJoinChannel\",\n\t\t\t\t\t\t\t(\"You cannot join channel '\" .. channelName .. \"'.\")\n\t\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\",channelName),\n\t\t\t\t\tfromChannelName\n\t\t\t\t)\n\t\t\tend\n\t\telse\n\t\t\tspeaker:SendSystemMessage(\n\t\t\t\tstring.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatServiceRunner_ChannelDoesNotExist\",\n\t\t\t\t\t\t(\"Channel '\" .. channelName .. \"' does not exist.\")\n\t\t\t\t\t),\n\t\t\t\t\"{RBX_NAME}\",channelName),\n\t\t\t\tfromChannelName\n\t\t\t)\n\t\tend\n\tend\nend\n\nlocal function DoLeaveCommand(speakerName, channelName, fromChannelName)\n\tlocal speaker = ChatService:GetSpeaker(speakerName)\n\tlocal channel = ChatService:GetChannel(channelName)\n\n\tif (speaker) then\n\t\tif (speaker:IsInChannel(channelName)) then\n\t\t\tif (channel.Leavable) then\n\t\t\t\tspeaker:LeaveChannel(channel.Name)\n\t\t\t\tspeaker:SendSystemMessage(\n\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\"GameChat_ChatService_YouHaveLeftChannel\",\n\t\t\t\t\t\t\tstring.format(\"You have left channel '%s'\", channelName)\n\t\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\",channel.Name),\n\t\t\t\t\t\"System\"\n\t\t\t\t)\n\t\t\telse\n\t\t\t\tspeaker:SendSystemMessage(\n\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\"GameChat_ChatServiceRunner_YouCannotLeaveChannel\",\n\t\t\t\t\t\t\t(\"You cannot leave channel '\" .. channelName .. \"'.\")\n\t\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\",channelName),\n\t\t\t\t\tfromChannelName\n\t\t\t\t)\n\t\t\tend\n\t\telse\n\t\t\tspeaker:SendSystemMessage(\n\t\t\t\tstring.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatServiceRunner_YouAreNotInChannel\",\n\t\t\t\t\t\t(\"You are not in channel '\" .. channelName .. \"'.\")\n\t\t\t\t\t),\n\t\t\t\t\"{RBX_NAME}\",channelName),\n\t\t\t\tfromChannelName\n\t\t\t)\n\t\tend\n\tend\nend\n\nChatService:RegisterProcessCommandsFunction(\"default_commands\", function(fromSpeaker, message, channel)\n\tif (string.sub(message, 1, 6):lower() == \"/join \") then\n\t\tDoJoinCommand(fromSpeaker, string.sub(message, 7), channel)\n\t\treturn true\n\telseif (string.sub(message, 1, 3):lower() == \"/j \") then\n\t\tDoJoinCommand(fromSpeaker, string.sub(message, 4), channel)\n\t\treturn true\n\n\telseif (string.sub(message, 1, 7):lower() == \"/leave \") then\n\t\tDoLeaveCommand(fromSpeaker, string.sub(message, 8), channel)\n\t\treturn true\n\telseif (string.sub(message, 1, 3):lower() == \"/l \") then\n\t\tDoLeaveCommand(fromSpeaker, string.sub(message, 4), channel)\n\t\treturn true\n\n\telseif (string.sub(message, 1, 3) == \"/e \" or string.sub(message, 1, 7) == \"/emote \") then\n\t\t-- Just don't show these in the chatlog. The animation script listens on these.\n\t\treturn true\n\n\tend\n\n\treturn false\nend)\n\nif ChatSettings.GeneralChannelName and ChatSettings.GeneralChannelName ~= \"\" then\n\tlocal allChannel = ChatService:AddChannel(ChatSettings.GeneralChannelName)\n\n\tallChannel.Leavable = false\n\tallChannel.AutoJoin = true\n\n\tallChannel:RegisterGetWelcomeMessageFunction(function(speaker)\n\t\tif RunService:IsStudio() then\n\t\t\treturn nil\n\t\tend\n\t\tlocal player = speaker:GetPlayer()\n\t\tif player then\n\t\t\tlocal success, canChat = pcall(function()\n\t\t\t\treturn Chat:CanUserChatAsync(player.UserId)\n\t\t\tend)\n\t\t\tif success and not canChat then\n\t\t\t\treturn \"\"\n\t\t\tend\n\t\tend\n\tend)\nend\n\nlocal systemChannel = ChatService:AddChannel(\"System\")\nsystemChannel.Leavable = false\nsystemChannel.AutoJoin = true\nsystemChannel.WelcomeMessage = ChatLocalization:Get(\n\t\"GameChat_ChatServiceRunner_SystemChannelWelcomeMessage\", \"This channel is for system and game notifications.\"\n)\n\nsystemChannel.SpeakerJoined:connect(function(speakerName)\n\tsystemChannel:MuteSpeaker(speakerName)\nend)\n\n\nlocal function TryRunModule(module)\n\tif module:IsA(\"ModuleScript\") then\n\t\tlocal ret = require(module)\n\t\tif (type(ret) == \"function\") then\n\t\t\tret(ChatService)\n\t\tend\n\tend\nend\n\nlocal modules = Chat:WaitForChild(\"ChatModules\")\nmodules.ChildAdded:connect(function(child)\n\tlocal success, returnval = pcall(TryRunModule, child)\n\tif not success and returnval then\n\t\tprint(\"Error running module \" ..child.Name.. \": \" ..returnval)\n\tend\nend)\n\nfor _, module in pairs(modules:GetChildren()) do\n\tlocal success, returnval = pcall(TryRunModule, module)\n\tif not success and returnval then\n\t\tprint(\"Error running module \" ..module.Name.. \": \" ..returnval)\n\tend\nend\n\nPlayersService.PlayerRemoving:connect(function(playerObj)\n\tif (ChatService:GetSpeaker(playerObj.Name)) then\n\t\tChatService:RemoveSpeaker(playerObj.Name)\n\tend\nend)\n"
  },
  {
    "path": "src/Chat/ClientChatModules/ChatConstants.lua",
    "content": "--\t// FileName: ChatConstants.lua\n--\t// Written by: TheGamer101\n--\t// Description: Module for creating chat constants shared between server and client.\n\nlocal module = {}\n\n---[[ Message Types ]]\nmodule.MessageTypeDefault = \"Message\"\nmodule.MessageTypeSystem = \"System\"\nmodule.MessageTypeMeCommand = \"MeCommand\"\nmodule.MessageTypeWelcome = \"Welcome\"\nmodule.MessageTypeSetCore = \"SetCore\"\nmodule.MessageTypeWhisper = \"Whisper\"\n\n--[[ Version ]]\nmodule.MajorVersion = 0\nmodule.MinorVersion = 8\nmodule.BuildVersion = \"2018.05.16\"\n---[[ Command/Filter Priorities ]]\nmodule.VeryLowPriority = -5\nmodule.LowPriority = 0\nmodule.StandardPriority = 10\nmodule.HighPriority = 20\nmodule.VeryHighPriority = 25\n\nmodule.WhisperChannelPrefix = \"To \"\n\nreturn module\n"
  },
  {
    "path": "src/Chat/ClientChatModules/ChatLocalization.lua",
    "content": "local LocalizationService = game:GetService(\"LocalizationService\")\nlocal ChatService = game:GetService(\"Chat\")\n\nlocal ChatLocalization = {\n\t_hasFetchedLocalization = false,\n}\n\nfunction ChatLocalization:_getTranslator()\n\tif not self._translator and not self._hasFetchedLocalization then\n\t\t-- Don't keep retrying if this fails.\n\t\tself._hasFetchedLocalization = true\n\t\t\n\t\tlocal localizationTable = ChatService:WaitForChild(\"ChatLocalization\", 4)\n\t\tif localizationTable then\n\t\t\tself._translator = localizationTable:GetTranslator(LocalizationService.RobloxLocaleId)\n\t\t\tLocalizationService:GetPropertyChangedSignal(\"RobloxLocaleId\"):Connect(function()\n\t\t\t\t-- If RobloxLocaleId changes invalidate the cached Translator.\n\t\t\t\tself._hasFetchedLocalization = false\n\t\t\t\tself._translator = nil\n\t\t\tend)\n\t\telse\n\t\t\t-- warn(\"Missing ChatLocalization. Chat interface will not be localized.\")\n\t\tend\n\tend\n\treturn self._translator\nend\n\nfunction ChatLocalization:Get(key, default)\n\tlocal rtv = default\n\tpcall(function()\n\t\tlocal translator = self:_getTranslator()\n\t\tif translator then\n\t\t\trtv = translator:FormatByKey(key)\n\t\telse\n\t\t\t-- warn(\"Missing Translator. Used default for\", key)\n\t\tend\n\tend)\n\treturn rtv\nend\n\nreturn ChatLocalization"
  },
  {
    "path": "src/Chat/ClientChatModules/ChatSettings.lua",
    "content": "--\t// FileName: ChatSettings.lua\n--\t// Written by: Xsitsu\n--\t// Description: Settings module for configuring different aspects of the chat window.\n\nlocal PlayersService = game:GetService(\"Players\")\n\nlocal clientChatModules = script.Parent\nlocal ChatConstants = require(clientChatModules:WaitForChild(\"ChatConstants\"))\n\nlocal module = {}\n\n---[[ Chat Behaviour Settings ]]\nmodule.WindowDraggable = false\nmodule.WindowResizable = false\nmodule.ShowChannelsBar = false\nmodule.GamepadNavigationEnabled = false\nmodule.ShowUserOwnFilteredMessage = true\t--Show a user the filtered version of their message rather than the original.\n-- Make the chat work when the top bar is off\nmodule.ChatOnWithTopBarOff = false\nmodule.ScreenGuiDisplayOrder = 0 -- The DisplayOrder value for the ScreenGui containing the chat.\n\nmodule.ShowFriendJoinNotification = true -- Show a notification in the chat when a players friend joins the game.\n\n--- Replace with true/false to force the chat type. Otherwise this will default to the setting on the website.\nmodule.BubbleChatEnabled = true\nmodule.ClassicChatEnabled = true\n\n---[[ Chat Text Size Settings ]]\nmodule.ChatWindowTextSize = 19\nmodule.ChatChannelsTabTextSize = 19\nmodule.ChatBarTextSize = 19\nmodule.ChatWindowTextSizePhone = 14\nmodule.ChatChannelsTabTextSizePhone = 19\nmodule.ChatBarTextSizePhone = 14\n\n---[[ Font Settings ]]\nmodule.DefaultFont = Enum.Font.SourceSansBold\nmodule.ChatBarFont = Enum.Font.SourceSansBold\n\n----[[ Color Settings ]]\nmodule.BackGroundColor = Color3.new(0.05, 0.05, 0.05)\nmodule.DefaultChatColor = Color3.new(1, 1, 1)\nmodule.DefaultMessageColor = Color3.new(1, 1, 1)\nmodule.DefaultNameColor = Color3.new(1, 1, 1)\nmodule.ChatBarBackGroundColor = Color3.new(0, 0, 0)\nmodule.ChatBarBoxColor = Color3.new(1, 1, 1)\nmodule.ChatBarTextColor = Color3.new(0, 0, 0)\nmodule.ChannelsTabUnselectedColor = Color3.new(0, 0, 0)\nmodule.ChannelsTabSelectedColor = Color3.new(30/255, 30/255, 30/255)\nmodule.DefaultChannelNameColor = Color3.fromRGB(35, 76, 142)\nmodule.WhisperChannelNameColor = Color3.fromRGB(102, 14, 102)\nmodule.ErrorMessageTextColor = Color3.fromRGB(245, 50, 50)\n\n---[[ Window Settings ]]\nmodule.MinimumWindowSize = UDim2.new(0.3, 0, 0.25, 0)\nmodule.MaximumWindowSize = UDim2.new(1, 0, 1, 0) -- if you change this to be greater than full screen size, weird things start to happen with size/position bounds checking.\nmodule.DefaultWindowPosition = UDim2.new(0, 0, 0, 0)\nlocal extraOffset = (7 * 2) + (5 * 2) -- Extra chatbar vertical offset\nmodule.DefaultWindowSizePhone = UDim2.new(0.5, 0, 0.5, extraOffset)\nmodule.DefaultWindowSizeTablet = UDim2.new(0.4, 0, 0.3, extraOffset)\nmodule.DefaultWindowSizeDesktop = UDim2.new(0.3, 0, 0.25, extraOffset)\n\n---[[ Fade Out and In Settings ]]\nmodule.ChatWindowBackgroundFadeOutTime = 0.5 --Chat background will fade out after this many seconds.\nmodule.ChatWindowTextFadeOutTime = 30\t\t\t\t--Chat text will fade out after this many seconds.\nmodule.ChatDefaultFadeDuration = 0.8\nmodule.ChatShouldFadeInFromNewInformation = false\nmodule.ChatAnimationFPS = 20.0\n\n---[[ Channel Settings ]]\nmodule.GeneralChannelName = \"All\" -- You can set to nil to turn off echoing to a general channel.\nmodule.EchoMessagesInGeneralChannel = true -- Should messages to channels other than general be echoed into the general channel.\n-- \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSetting this to false should be used with ShowChannelsBar\nmodule.ChannelsBarFullTabSize = 4 -- number of tabs in bar before it starts to scroll\nmodule.MaxChannelNameLength = 12\n--// Although this feature is pretty much ready, it needs some UI design still.\nmodule.RightClickToLeaveChannelEnabled = false\nmodule.MessageHistoryLengthPerChannel = 50\n-- Show the help text for joining and leaving channels. This is not useful unless custom channels have been added.\n-- So it is turned off by default.\nmodule.ShowJoinAndLeaveHelpText = false\n\n---[[ Message Settings ]]\nmodule.MaximumMessageLength = 200\nmodule.DisallowedWhiteSpace = {\"\\n\", \"\\r\", \"\\t\", \"\\v\", \"\\f\"}\nmodule.ClickOnPlayerNameToWhisper = true\nmodule.ClickOnChannelNameToSetMainChannel = true\nmodule.BubbleChatMessageTypes = {ChatConstants.MessageTypeDefault, ChatConstants.MessageTypeWhisper}\n\n---[[ Misc Settings ]]\nmodule.WhisperCommandAutoCompletePlayerNames = true\n\nlocal ChangedEvent = Instance.new(\"BindableEvent\")\n\nlocal proxyTable = setmetatable({},\n{\n\t__index = function(tbl, index)\n\t\treturn module[index]\n\tend,\n\t__newindex = function(tbl, index, value)\n\t\tmodule[index] = value\n\t\tChangedEvent:Fire(index, value)\n\tend,\n})\n\nrawset(proxyTable, \"SettingsChanged\", ChangedEvent.Event)\n\nreturn proxyTable\n"
  },
  {
    "path": "src/Chat/ClientChatModules/CommandModules/ClearMessages.lua",
    "content": "--\t// FileName: ClearMessages.lua\n--\t// Written by: TheGamer101\n--\t// Description: Command to clear the message log of the current channel.\n\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\n\nfunction ProcessMessage(message, ChatWindow, ChatSettings)\n\tif string.sub(message, 1, 4):lower() == \"/cls\" or string.sub(message, 1, 6):lower() == \"/clear\" then\n\t\tlocal currentChannel = ChatWindow:GetCurrentChannel()\n\t\tif (currentChannel) then\n\t\t\tcurrentChannel:ClearMessageLog()\n\t\tend\n\t\treturn true\n\tend\n\treturn false\nend\n\nreturn {\n\t[util.KEY_COMMAND_PROCESSOR_TYPE] = util.COMPLETED_MESSAGE_PROCESSOR,\n\t[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage\n}\n"
  },
  {
    "path": "src/Chat/ClientChatModules/CommandModules/Commands.lua",
    "content": "-- Custom Vesteria chat commands\n-- by berezaa\n\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\n \nlocal customState = {}\ncustomState.__index = customState\n\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal network \t\t\t= modules.load(\"network\")\n\tlocal configuration \t= modules.load(\"configuration\")\n \nfunction matches(newText, pattern)\n\tnewText = string.lower(newText)\n\tpattern = string.lower(pattern)\n\tif string.sub(newText, 1, pattern:len()) == pattern then\n\t\treturn true\n\tend\nend\n\nfunction isCommand(newText)\n\tif matches(newText, \"/invite \") or  matches(newText, \"/i \") then\n\t\treturn \"invite\"\n\telseif matches(newText, \"/duel \") or  matches(newText, \"/d \") then\n\t\treturn \"duel\"\n\telseif matches(newText, \"/trade \") or  matches(newText, \"/t \") then\n\t\treturn \"trade\"\n\telseif matches(newText, \"/e \") then\n\t\treturn \"emote\", true\n\telseif matches(newText, \"/expel \") then\n\t\treturn \"expel\"\n\tend\nend\n\nlocal commandText = {\n\tinvite = \"Invite player to party:\";\n\tduel = \"Challenge player to duel:\";\n\ttrade = \"Request trade with player:\";\n\temote = \"Perform emote:\";\n\texpel = \"Expel player from Guild Hall:\",\n}\n\nfunction customState:TrimWhiteSpace(text)\n\tlocal newText = string.gsub(text, \"%s+\", \"\")\n\tlocal wasWhitespaceTrimmed = text[#text] == \" \"\n\treturn newText, wasWhitespaceTrimmed\nend\n\nfunction customState:AutoComplete(enteredText)\n\tenteredText = enteredText:lower()\n\tlocal trimmedText = enteredText\n--\tlocal trimmedText = self:TrimWhisperCommand(enteredText)\n\tif trimmedText then\n\t\tlocal possiblePlayerName, whitespaceTrimmed = self:TrimWhiteSpace(trimmedText)\n\t\t\n\t\tlocal possibleMatches = {}\n\t\tlocal players = game.Players:GetPlayers()\n\t\tfor i = 1, #players do\n\t\t\tif players[i] ~= game.Players.LocalPlayer then\n\t\t\t\tlocal lowerPlayerName = players[i].Name:lower()\n\t\t\t\tif string.sub(lowerPlayerName, 1, string.len(possiblePlayerName)) == possiblePlayerName then\n\t\t\t\t\tpossibleMatches[players[i]] = players[i].Name:lower()\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\tlocal matchCount = 0\n\t\tlocal lastMatch = nil\n\t\tlocal lastMatchName = nil\n\t\tfor player, playerName in pairs(possibleMatches) do\n\t\t\tmatchCount = matchCount + 1\n\t\t\tlastMatch = player\n\t\t\tlastMatchName = playerName\n\t\t\tif playerName == possiblePlayerName and whitespaceTrimmed then\n\t\t\t\treturn player\n\t\t\tend\n\t\tend\n\t\tif matchCount == 1 then\n\t\t\treturn lastMatch\n\t\tend\n\tend\n\treturn nil\nend\n\nfunction customState:enterFocus(command)\n\t\n\tcommand = command or isCommand(self:GetMessage())\n\tif command then\n\t\t\n\t\tself.currentCommand = command\n\t\n\t\tself.MessageModeButton.Size = UDim2.new(0, 1000, 1, 0)\n\t\tself.MessageModeButton.Text = commandText[command] or command .. \":\"\n\t\tself.MessageModeButton.TextColor3 = Color3.fromRGB(0, 12, 255)\n\t\n\t\tlocal xSize = self.MessageModeButton.TextBounds.X\n\t\tself.MessageModeButton.Size = UDim2.new(0, xSize, 1, 0)\n\t\tself.TextBox.Size = UDim2.new(1, -xSize, 1, 0)\n\t\tself.TextBox.Position = UDim2.new(0, xSize, 0, 0)\n\t\tself.OriginalPartyText = self.TextBox.Text\n\t\tself.TextBox.Text = \" \"\t\t\n\t\n\tend\nend\n\nfunction customState:TextUpdated()\n\tlocal newText = self.TextBox.Text\n\tif not self.currentCommand then\n\t\tlocal command = isCommand(newText)\n\t\tif command then\n\t\t\tself:enterFocus(command)\t\n\t\tend\n\telse\n\t\tif newText == \"\" then\n\t\t\tself.MessageModeButton.Text = \"\"\n\t\t\tself.MessageModeButton.Size = UDim2.new(0, 0, 0, 0)\n\t\t\tself.TextBox.Size = UDim2.new(1, 0, 1, 0)\n\t\t\tself.TextBox.Position = UDim2.new(0, 0, 0, 0)\n\t\t\tself.TextBox.Text = \"\"\n\t\t\t---Implement this when setting cursor positon is a thing.\n\t\t\t---self.TextBox.Text = self.OriginalPartyText\n\t\t\tself.currentCommand = nil\n\t\t\t---Temporary until setting cursor position...\n\t\t\tself.ChatBar:ResetCustomState()\n\t\t\tself.ChatBar:CaptureFocus()\n\t\telseif self.doAutoComplete then\n\t\t\t-- try to autocomplete\n\t\t\tlocal targetPlayer = self:AutoComplete(newText)\n\t\t\tif targetPlayer then\n\t\t\t\tself.TextBox.Text = \" \"..targetPlayer.Name\n\t\t\tend\t\n\t\t\tself.ChatBar:CaptureFocus()\n\t\t\tself.doAutoComplete = false\n\t\tend\t\t\n\tend\n\nend\n \nfunction customState:GetMessage()\n\tlocal currentCommand = self.currentCommand\n\tif currentCommand then\n\t\treturn \"/\"..currentCommand..self.TextBox.Text\n\tend\n\treturn self.TextBox.Text\nend\n\nlocal starterGui = game:GetService(\"StarterGui\")\n \nlocal MSG_COLOR = Color3.new(0.7,0.7,0.7)\nlocal ERR_COLOR = Color3.new(1,0.6,0.6)\n\n--welcome msg\nif game.gameId ==712031239 then\n\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {\n\t\tText = \"Welcome to Free to Play Vesteria! This game is still in early development and may be reset. Say '/help' for a list of commands.\"; \n\t\tColor = Color3.fromRGB(0, 255, 149)\n\t})\nelse\n\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"Welcome to Vesteria! Say '/help' for a list of commands.\"; Color = Color3.fromRGB(0, 255, 149)})\nend\n\n\n\n\n\nfunction customState:ProcessCompletedMessage()\n\t\n\tlocal message = self:GetMessage()\n\t\n\tlocal command = self.currentCommand\n\tif command then\n\t\t\n\t\tif command == \"emote\" then\n\t\t\tlocal targetEmote = string.gsub(self:GetMessage(), \"^[^%s]+ \", \"\")\n\t\t\t\n\t\t\tlocal success, reason = false, \"invalid emote\"\n\t\t\tsuccess, reason = network:invoke(\"playerRequest_performEmote\", targetEmote)\n\t\t\tif not success then\n\t\t\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = reason; Color = ERR_COLOR})\n\t\t\tend\n\t\t\t\n\t\telse\n\t\t\t-- commands that involve players\n\t\t\tlocal targetPlayerName = string.gsub(self:GetMessage(), \"^[^%s]+ \", \"\")\n\t\t\tlocal targetPlayer = game.Players:FindFirstChild(targetPlayerName)\n\t\t\t\n\t\t\t-- idk in case spaces in usernames ever become a thing\n\t\t\tif targetPlayer == nil then\n\t\t\t\ttargetPlayer = game.Players:FindFirstChild(string.gsub(targetPlayerName, \" \", \"\"))\n\t\t\tend\n\t\t\t\n\t\t\tif targetPlayer then\n\t\t\t\tlocal success, reason = false, \"invalid command\"\n\t\t\t\tif command == \"invite\" then\n\t\t\t\t\tsuccess, reason = network:invokeServer(\"playerRequest_invitePlayerToMyParty\", targetPlayer)\n\t\t\t\t\tif success then\n\t\t\t\t\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"Sent a party invite to \" .. targetPlayerName; Color = MSG_COLOR})\n\t\t\t\t\telse\n\t\t\t\t\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = reason; Color = ERR_COLOR})\n\t\t\t\t\tend\n\t\t\t\telseif command == \"duel\" then\n\t\t\t\t\tsuccess, reason = network:invokeServer(\"playerRequest_requestChallenge\", targetPlayer)\n\t\t\t\t\tif success then\n\t\t\t\t\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"Sent a duel challenge to \" .. targetPlayerName; Color = MSG_COLOR})\n\t\t\t\t\telse\n\t\t\t\t\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = reason; Color = ERR_COLOR})\n\t\t\t\t\tend\n\t\t\t\telseif command == \"expel\" then\n\t\t\t\t\tsuccess, reason = network:invokeServer(\"playerRequest_expelPlayer\", targetPlayer)\n\t\t\t\t\tif success then\n\t\t\t\t\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"Expelling \"..targetPlayerName..\"...\", Color = MSG_COLOR})\n\t\t\t\t\tend\n\t\t\t\telseif command == \"trade\" then\n\t\t\t\t\t\n\t\t\t\t\tsuccess, reason = false, \"Trading is temporarily disabled.\"\n\t\t\t\n\t\t\t\t\tif configuration.getConfigurationValue(\"isTradingEnabled\") then\n\t\t\t\t\t\tsuccess, reason = network:invokeServer(\"playerRequest_requestTrade\", targetPlayer)\n\t\t\t\t\tend\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\tif success then\n\t\t\t\t\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"Sent a trade request to \" .. targetPlayerName; Color = MSG_COLOR})\n\t\t\t\t\tend\t\t\t\t\t\n\t\t\t\tend\n\t\t\t\tif not success then\n\t\t\t\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = reason; Color = ERR_COLOR})\n\t\t\t\tend\n\t\t\t\t\n\t\t\telse\n\t\t\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"Invalid player\"; Color = ERR_COLOR})\t\t\n\t\t\tend\n\t\tend\n\t\t\n\t\t\n\t\t\n\t\t\n\t\treturn true\t\n\tend\n\t\n\t-- help command\t\t\n\tif (message:lower() == \"/?\" or message:lower() == \"/help\") then\n\t\t\n\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"Vesteria chat commands:\"; Color = MSG_COLOR})\n\n\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"  /me <text> :: roleplaying command for doing actions.\"; Color = MSG_COLOR})\t\n\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"  /whisper <player> (/w) :: whisper a private message to a player.\"; Color = MSG_COLOR})\t\n\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"  /mute <player> :: stop seeing chats from a player.\"; Color = MSG_COLOR})\n\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"  /unmute <player> :: unmute a muted player.\"; Color = MSG_COLOR})\n\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"  /party <text> (/p) :: send a message to party members.\"; Color = MSG_COLOR})\n\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"  /invite <player> (/i) :: invite a player to your party.\"; Color = MSG_COLOR})\n\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"  /duel <player> (/d) :: challenge a player to a duel.\"; Color = MSG_COLOR})\n\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"  /trade <player> (/t) :: request a trade with a player.\"; Color = MSG_COLOR})\n\t\tstarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = \"  /e <emote> (/t) :: perform emote.\"; Color = MSG_COLOR})\n\n\t\treturn true\n\tend\t\t\t\n\t\t\t\n\treturn false\nend\n \nfunction customState:Destroy()\n\tprint(\"destroy custom state\")\n\tself.Destroyed = true\nend\n \nfunction customState.new(ChatWindow, ChatBar, ChatSettings, overrideAutoComplete)\n\t\n\tlocal obj = {}\n\tsetmetatable(obj, customState)\n \n\tobj.Destroyed = false\n\tobj.ChatWindow = ChatWindow\n\tobj.ChatBar = ChatBar\n\tobj.ChatSettings = ChatSettings\n\tobj.TextBox = ChatBar:GetTextBox()\n\tobj.MessageModeButton = ChatBar:GetMessageModeTextButton()\n\tobj.MessageModeLabel = ChatBar:GetMessageModeTextLabel()\t\n\t\n\tif overrideAutoComplete then\n\t\tobj.doAutoComplete = false\n\t\t--print(\"overrided autocomplete\")\n\telse\n\t\tobj.doAutoComplete = true\n\tend\n\t\n\t\n\tobj.MessageModeConnection = obj.MessageModeButton.MouseButton1Click:connect(function()\n\t\tlocal chatBarText = obj.TextBox.Text\n\t\tif string.sub(chatBarText, 1, 1) == \" \" then\n\t\t\tchatBarText = string.sub(chatBarText, 2)\n\t\tend\n\t\tobj.ChatBar:ResetCustomState()\n\t\tobj.ChatBar:SetTextBoxText(chatBarText)\n\t\tobj.ChatBar:CaptureFocus()\n\tend)\t\n\t\n\tobj:enterFocus()\t\n \n\treturn obj\nend\n\nlocal function ProcessMessage(message, ChatWindow, ChatBar, ChatSettings)\n\t-- overrideAutoComplete is nil or true\n\tlocal command, overrideAutoComplete = isCommand(message)\n\t\n\tif command or message:lower() == \"/?\" or message:lower() == \"/help\" then\n\t\treturn customState.new(ChatWindow, ChatBar, ChatSettings, overrideAutoComplete)\n\tend\nend\n \nreturn {\n\t[util.KEY_COMMAND_PROCESSOR_TYPE] = util.IN_PROGRESS_MESSAGE_PROCESSOR,\n\t[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage\n}"
  },
  {
    "path": "src/Chat/ClientChatModules/CommandModules/DeveloperConsole.lua",
    "content": "--\t// FileName: DeveloperConsole.lua\n--\t// Written by: TheGamer101\n--\t// Description: Command to open or close the developer console.\n\nlocal StarterGui = game:GetService(\"StarterGui\")\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\n\nfunction ProcessMessage(message, ChatWindow, ChatSettings)\n\tif string.sub(message, 1, 8):lower() == \"/console\" or string.sub(message, 1, 11):lower() == \"/newconsole\" then\n\t\tlocal success, developerConsoleVisible = pcall(function() return StarterGui:GetCore(\"DevConsoleVisible\") end)\n\t\tif success then\n\t\t\tlocal success, err = pcall(function() StarterGui:SetCore(\"DevConsoleVisible\", not developerConsoleVisible) end)\n\t\t\tif not success and err then\n\t\t\t\tprint(\"Error making developer console visible: \" ..err)\n\t\t\tend\n\t\tend\n\t\treturn true\n\telseif string.sub(message, 1, 11):lower() == \"/oldconsole\" then\n\t\tlocal success, developerConsoleVisible = pcall(function() return StarterGui:GetCore(\"DeveloperConsoleVisible\") end)\n\t\tif success then\n\t\t\tlocal success, err = pcall(function() StarterGui:SetCore(\"DeveloperConsoleVisible\", not developerConsoleVisible) end)\n\t\t\tif not success and err then\n\t\t\t\tprint(\"Error making developer console visible: \" ..err)\n\t\t\tend\n\t\tend\n\t\treturn true\n\tend\n\treturn false\nend\n\nreturn {\n\t[util.KEY_COMMAND_PROCESSOR_TYPE] = util.COMPLETED_MESSAGE_PROCESSOR,\n\t[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage\n}\n\n"
  },
  {
    "path": "src/Chat/ClientChatModules/CommandModules/GetVersion.lua",
    "content": "--\t// FileName: GetVersion.lua\n--\t// Written by: spotco\n--\t// Description: Command to print the chat version.\n\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\nlocal ChatConstants = require(script.Parent.Parent:WaitForChild(\"ChatConstants\"))\n\nlocal function ProcessMessage(message, ChatWindow, _)\n\tif string.sub(message, 1,  8):lower() == \"/version\" or string.sub(message, 1, 9):lower() == \"/version \" then\n\t\tutil:SendSystemMessageToSelf(\n\t\t\tstring.format(\"This game is running chat version [%d.%d.%s].\",\n\t\t\t\tChatConstants.MajorVersion,\n\t\t\t\tChatConstants.MinorVersion,\n\t\t\t\tChatConstants.BuildVersion),\n\t\t\tChatWindow:GetCurrentChannel(),\n\t\t\t{})\n\t\treturn true\n\tend\n\treturn false\nend\n\nreturn {\n\t[util.KEY_COMMAND_PROCESSOR_TYPE] = util.COMPLETED_MESSAGE_PROCESSOR,\n\t[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage\n}"
  },
  {
    "path": "src/Chat/ClientChatModules/CommandModules/Guild.lua",
    "content": "--\t// FileName: Guild.lua\n--\t// Written by: Partixel/TheGamer101\n--\t// Description: Guild chat bar manipulation.\n\nlocal PlayersService = game:GetService(\"Players\")\n\nlocal GUILD_COMMANDS = {\"/guild \", \"/g \"}\n\nfunction IsGuildCommand(message)\n\tfor i = 1, #GUILD_COMMANDS do\n\t\tlocal guildCommand = GUILD_COMMANDS[i]\n\t\tif string.sub(message, 1, guildCommand:len()):lower() == guildCommand then\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\nlocal guildStateMethods = {}\nguildStateMethods.__index = guildStateMethods\n\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\n\nlocal GuildCustomState = {}\n\nfunction guildStateMethods:EnterGuildChat()\n\tself.GuildChatEntered = true\n\tself.MessageModeButton.Size = UDim2.new(0, 1000, 1, 0)\n\tself.MessageModeButton.Text = \"[Guild]\"\n\tself.MessageModeButton.TextColor3 = self:GetGuildChatColor()\n\n\tlocal xSize = self.MessageModeButton.TextBounds.X\n\tself.MessageModeButton.Size = UDim2.new(0, xSize, 1, 0)\n\tself.TextBox.Size = UDim2.new(1, -xSize, 1, 0)\n\tself.TextBox.Position = UDim2.new(0, xSize, 0, 0)\n\tself.OriginalGuildText = self.TextBox.Text\n\tself.TextBox.Text = \" \"\nend\n\nfunction guildStateMethods:TextUpdated()\n\tlocal newText = self.TextBox.Text\n\tif not self.GuildChatEntered then\n\t\tif IsGuildCommand(newText) then\n\t\t\tself:EnterGuildChat()\n\t\tend\n\telse\n\t\tif newText == \"\" then\n\t\t\tself.MessageModeButton.Text = \"\"\n\t\t\tself.MessageModeButton.Size = UDim2.new(0, 0, 0, 0)\n\t\t\tself.TextBox.Size = UDim2.new(1, 0, 1, 0)\n\t\t\tself.TextBox.Position = UDim2.new(0, 0, 0, 0)\n\t\t\tself.TextBox.Text = \"\"\n\t\t\t---Implement this when setting cursor positon is a thing.\n\t\t\t---self.TextBox.Text = self.OriginalGuildText\n\t\t\tself.GuildChatEntered = false\n\t\t\t---Temporary until setting cursor position...\n\t\t\tself.ChatBar:ResetCustomState()\n\t\t\tself.ChatBar:CaptureFocus()\n\t\tend\n\tend\nend\n\nfunction guildStateMethods:GetMessage()\n\tif self.GuildChatEntered then\n\t\treturn \"/g \" ..self.TextBox.Text\n\tend\n\treturn self.TextBox.Text\nend\n\nfunction guildStateMethods:ProcessCompletedMessage()\n\treturn false\nend\n\nfunction guildStateMethods:Destroy()\n\tself.MessageModeConnection:disconnect()\n\tself.Destroyed = true\nend\n\nfunction guildStateMethods:GetGuildChatColor()\n\n\treturn Color3.fromRGB(145, 71, 255)\nend\n\nfunction GuildCustomState.new(ChatWindow, ChatBar, ChatSettings)\n\tlocal obj = setmetatable({}, guildStateMethods)\n\tobj.Destroyed = false\n\tobj.ChatWindow = ChatWindow\n\tobj.ChatBar = ChatBar\n\tobj.ChatSettings = ChatSettings\n\tobj.TextBox = ChatBar:GetTextBox()\n\tobj.MessageModeButton = ChatBar:GetMessageModeTextButton()\n\tobj.OriginalGuildText = \"\"\n\tobj.GuildChatEntered = false\n\n\tobj.MessageModeConnection = obj.MessageModeButton.MouseButton1Click:connect(function()\n\t\tlocal chatBarText = obj.TextBox.Text\n\t\tif string.sub(chatBarText, 1, 1) == \" \" then\n\t\t\tchatBarText = string.sub(chatBarText, 2)\n\t\tend\n\t\tobj.ChatBar:ResetCustomState()\n\t\tobj.ChatBar:SetTextBoxText(chatBarText)\n\t\tobj.ChatBar:CaptureFocus()\n\tend)\n\n\tobj:EnterGuildChat()\n\n\treturn obj\nend\n\nfunction ProcessMessage(message, ChatWindow, ChatBar, ChatSettings)\n\tif ChatBar.TargetChannel == \"Guild\" then\n\t\treturn\n\tend\n\n\tif IsGuildCommand(message) then\n\t\treturn GuildCustomState.new(ChatWindow, ChatBar, ChatSettings)\n\tend\n\treturn nil\nend\n\nreturn {\n\t[util.KEY_COMMAND_PROCESSOR_TYPE] = util.IN_PROGRESS_MESSAGE_PROCESSOR,\n\t[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage\n}\n"
  },
  {
    "path": "src/Chat/ClientChatModules/CommandModules/Party.lua",
    "content": "--\t// FileName: Party.lua\n--\t// Written by: Partixel/TheGamer101\n--\t// Description: Party chat bar manipulation.\n\nlocal PlayersService = game:GetService(\"Players\")\n\nlocal PARTY_COMMANDS = {\"/party \", \"/p \"}\n\nfunction IsPartyCommand(message)\n\tfor i = 1, #PARTY_COMMANDS do\n\t\tlocal partyCommand = PARTY_COMMANDS[i]\n\t\tif string.sub(message, 1, partyCommand:len()):lower() == partyCommand then\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\nlocal partyStateMethods = {}\npartyStateMethods.__index = partyStateMethods\n\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\n\nlocal PartyCustomState = {}\n\nfunction partyStateMethods:EnterPartyChat()\n\tself.PartyChatEntered = true\n\tself.MessageModeButton.Size = UDim2.new(0, 1000, 1, 0)\n\tself.MessageModeButton.Text = \"[Party]\"\n\tself.MessageModeButton.TextColor3 = self:GetPartyChatColor()\n\n\tlocal xSize = self.MessageModeButton.TextBounds.X\n\tself.MessageModeButton.Size = UDim2.new(0, xSize, 1, 0)\n\tself.TextBox.Size = UDim2.new(1, -xSize, 1, 0)\n\tself.TextBox.Position = UDim2.new(0, xSize, 0, 0)\n\tself.OriginalPartyText = self.TextBox.Text\n\tself.TextBox.Text = \" \"\nend\n\nfunction partyStateMethods:TextUpdated()\n\tlocal newText = self.TextBox.Text\n\tif not self.PartyChatEntered then\n\t\tif IsPartyCommand(newText) then\n\t\t\tself:EnterPartyChat()\n\t\tend\n\telse\n\t\tif newText == \"\" then\n\t\t\tself.MessageModeButton.Text = \"\"\n\t\t\tself.MessageModeButton.Size = UDim2.new(0, 0, 0, 0)\n\t\t\tself.TextBox.Size = UDim2.new(1, 0, 1, 0)\n\t\t\tself.TextBox.Position = UDim2.new(0, 0, 0, 0)\n\t\t\tself.TextBox.Text = \"\"\n\t\t\t---Implement this when setting cursor positon is a thing.\n\t\t\t---self.TextBox.Text = self.OriginalPartyText\n\t\t\tself.PartyChatEntered = false\n\t\t\t---Temporary until setting cursor position...\n\t\t\tself.ChatBar:ResetCustomState()\n\t\t\tself.ChatBar:CaptureFocus()\n\t\tend\n\tend\nend\n\nfunction partyStateMethods:GetMessage()\n\tif self.PartyChatEntered then\n\t\treturn \"/p \" ..self.TextBox.Text\n\tend\n\treturn self.TextBox.Text\nend\n\nfunction partyStateMethods:ProcessCompletedMessage()\n\treturn false\nend\n\nfunction partyStateMethods:Destroy()\n\tself.MessageModeConnection:disconnect()\n\tself.Destroyed = true\nend\n\nfunction partyStateMethods:GetPartyChatColor()\n\n\treturn Color3.fromRGB(0, 240, 244)\nend\n\nfunction PartyCustomState.new(ChatWindow, ChatBar, ChatSettings)\n\tlocal obj = setmetatable({}, partyStateMethods)\n\tobj.Destroyed = false\n\tobj.ChatWindow = ChatWindow\n\tobj.ChatBar = ChatBar\n\tobj.ChatSettings = ChatSettings\n\tobj.TextBox = ChatBar:GetTextBox()\n\tobj.MessageModeButton = ChatBar:GetMessageModeTextButton()\n\tobj.OriginalPartyText = \"\"\n\tobj.PartyChatEntered = false\n\n\tobj.MessageModeConnection = obj.MessageModeButton.MouseButton1Click:connect(function()\n\t\tlocal chatBarText = obj.TextBox.Text\n\t\tif string.sub(chatBarText, 1, 1) == \" \" then\n\t\t\tchatBarText = string.sub(chatBarText, 2)\n\t\tend\n\t\tobj.ChatBar:ResetCustomState()\n\t\tobj.ChatBar:SetTextBoxText(chatBarText)\n\t\tobj.ChatBar:CaptureFocus()\n\tend)\n\n\tobj:EnterPartyChat()\n\n\treturn obj\nend\n\nfunction ProcessMessage(message, ChatWindow, ChatBar, ChatSettings)\n\tif ChatBar.TargetChannel == \"Party\" then\n\t\treturn\n\tend\n\n\tif IsPartyCommand(message) then\n\t\treturn PartyCustomState.new(ChatWindow, ChatBar, ChatSettings)\n\tend\n\treturn nil\nend\n\nreturn {\n\t[util.KEY_COMMAND_PROCESSOR_TYPE] = util.IN_PROGRESS_MESSAGE_PROCESSOR,\n\t[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage\n}\n"
  },
  {
    "path": "src/Chat/ClientChatModules/CommandModules/SwallowGuestChat.lua",
    "content": "--\t// FileName: SwallowGuestChat.lua\n--\t// Written by: TheGamer101\n--\t// Description: Stop Guests from chatting and give them a message telling them to sign up.\n-- \t// Guests are generally not allowed to chat, so please do not remove this.\n\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\nlocal RunService = game:GetService(\"RunService\")\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\nfunction ProcessMessage(message, ChatWindow, ChatSettings)\n\tlocal LocalPlayer = game:GetService(\"Players\").LocalPlayer\n\tif LocalPlayer and LocalPlayer.UserId < 0 and not RunService:IsStudio() then\n\n\t\tlocal channelObj = ChatWindow:GetCurrentChannel()\n\t\tif channelObj then\n\t\t\tutil:SendSystemMessageToSelf(\n\t\t\t\tChatLocalization:Get(\"GameChat_SwallowGuestChat_Message\",\"Create a free account to get access to chat permissions!\"), \n\t\t\t\tchannelObj, \n\t\t\t\t{}\n\t\t\t)\n\t\tend\n\n\t\treturn true\n\tend\n\treturn false\nend\n\nreturn {\n\t[util.KEY_COMMAND_PROCESSOR_TYPE] = util.COMPLETED_MESSAGE_PROCESSOR,\n\t[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage\n}\n"
  },
  {
    "path": "src/Chat/ClientChatModules/CommandModules/SwitchChannel.lua",
    "content": "--\t// FileName: ClearMessages.lua\n--\t// Written by: TheGamer101\n--\t// Description: Command to switch channel.\n\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = { Get = function(key,default) return default end } end\n\nfunction ProcessMessage(message, ChatWindow, ChatSettings)\n\tif string.sub(message, 1, 3):lower() ~= \"/c \" then\n\t\treturn false\n\tend\n\n\tlocal channelName = string.sub(message, 4)\n\n\tlocal targetChannel = ChatWindow:GetChannel(channelName)\n\tif targetChannel then\n\t\tChatWindow:SwitchCurrentChannel(channelName)\n\t\tif not ChatSettings.ShowChannelsBar then\n\t\t\tlocal currentChannel = ChatWindow:GetCurrentChannel()\n\t\t\tif currentChannel then\n\t\t\t\tutil:SendSystemMessageToSelf(\n\t\t\t\t\tstring.gsub(ChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_SwitchChannel_NowInChannel\",\n\t\t\t\t\t\tstring.format(\"You are now chatting in channel: '%s'\", channelName)\n\t\t\t\t\t),\"{RBX_NAME}\",channelName),\n\t\t\t\t\ttargetChannel, {}\n\t\t\t\t)\n\t\t\tend\n\t\tend\n\telse\n\t\tlocal currentChannel = ChatWindow:GetCurrentChannel()\n\t\tif currentChannel then\n\t\t\tutil:SendSystemMessageToSelf(\n\t\t\t\tstring.gsub(ChatLocalization:Get(\n\t\t\t\t\t\"GameChat_SwitchChannel_NotInChannel\",\n\t\t\t\t\tstring.format(\"You are not in channel: '%s'\", channelName)\n\t\t\t\t),\"{RBX_NAME}\",channelName), \n\t\t\t\tcurrentChannel, {ChatColor = Color3.fromRGB(245, 50, 50)}\n\t\t\t)\n\t\tend\n\tend\n\n\treturn true\nend\n\nreturn {\n\t[util.KEY_COMMAND_PROCESSOR_TYPE] = util.COMPLETED_MESSAGE_PROCESSOR,\n\t[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage\n}\n"
  },
  {
    "path": "src/Chat/ClientChatModules/CommandModules/Team.lua",
    "content": "--\t// FileName: Team.lua\n--\t// Written by: Partixel/TheGamer101\n--\t// Description: Team chat bar manipulation.\n\nlocal PlayersService = game:GetService(\"Players\")\n\nlocal TEAM_COMMANDS = {\"/team \", \"/t \"}\n\nfunction IsTeamCommand(message)\n\tfor i = 1, #TEAM_COMMANDS do\n\t\tlocal teamCommand = TEAM_COMMANDS[i]\n\t\tif string.sub(message, 1, teamCommand:len()):lower() == teamCommand then\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\nlocal teamStateMethods = {}\nteamStateMethods.__index = teamStateMethods\n\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\n\nlocal TeamCustomState = {}\n\nfunction teamStateMethods:EnterTeamChat()\n\tself.TeamChatEntered = true\n\tself.MessageModeButton.Size = UDim2.new(0, 1000, 1, 0)\n\tself.MessageModeButton.Text = \"[Team]\"\n\tself.MessageModeButton.TextColor3 = self:GetTeamChatColor()\n\n\tlocal xSize = self.MessageModeButton.TextBounds.X\n\tself.MessageModeButton.Size = UDim2.new(0, xSize, 1, 0)\n\tself.TextBox.Size = UDim2.new(1, -xSize, 1, 0)\n\tself.TextBox.Position = UDim2.new(0, xSize, 0, 0)\n\tself.OriginalTeamText = self.TextBox.Text\n\tself.TextBox.Text = \" \"\nend\n\nfunction teamStateMethods:TextUpdated()\n\tlocal newText = self.TextBox.Text\n\tif not self.TeamChatEntered then\n\t\tif IsTeamCommand(newText) then\n\t\t\tself:EnterTeamChat()\n\t\tend\n\telse\n\t\tif newText == \"\" then\n\t\t\tself.MessageModeButton.Text = \"\"\n\t\t\tself.MessageModeButton.Size = UDim2.new(0, 0, 0, 0)\n\t\t\tself.TextBox.Size = UDim2.new(1, 0, 1, 0)\n\t\t\tself.TextBox.Position = UDim2.new(0, 0, 0, 0)\n\t\t\tself.TextBox.Text = \"\"\n\t\t\t---Implement this when setting cursor positon is a thing.\n\t\t\t---self.TextBox.Text = self.OriginalTeamText\n\t\t\tself.TeamChatEntered = false\n\t\t\t---Temporary until setting cursor position...\n\t\t\tself.ChatBar:ResetCustomState()\n\t\t\tself.ChatBar:CaptureFocus()\n\t\tend\n\tend\nend\n\nfunction teamStateMethods:GetMessage()\n\tif self.TeamChatEntered then\n\t\treturn \"/t \" ..self.TextBox.Text\n\tend\n\treturn self.TextBox.Text\nend\n\nfunction teamStateMethods:ProcessCompletedMessage()\n\treturn false\nend\n\nfunction teamStateMethods:Destroy()\n\tself.MessageModeConnection:disconnect()\n\tself.Destroyed = true\nend\n\nfunction teamStateMethods:GetTeamChatColor()\n\tlocal LocalPlayer = PlayersService.LocalPlayer\n\tif LocalPlayer.Team then\n\t\treturn LocalPlayer.Team.TeamColor.Color\n\tend\n\tif self.ChatSettings.DefaultChannelNameColor then\n\t\treturn self.ChatSettings.DefaultChannelNameColor\n\tend\n\treturn Color3.fromRGB(35, 76, 142)\nend\n\nfunction TeamCustomState.new(ChatWindow, ChatBar, ChatSettings)\n\tlocal obj = setmetatable({}, teamStateMethods)\n\tobj.Destroyed = false\n\tobj.ChatWindow = ChatWindow\n\tobj.ChatBar = ChatBar\n\tobj.ChatSettings = ChatSettings\n\tobj.TextBox = ChatBar:GetTextBox()\n\tobj.MessageModeButton = ChatBar:GetMessageModeTextButton()\n\tobj.OriginalTeamText = \"\"\n\tobj.TeamChatEntered = false\n\n\tobj.MessageModeConnection = obj.MessageModeButton.MouseButton1Click:connect(function()\n\t\tlocal chatBarText = obj.TextBox.Text\n\t\tif string.sub(chatBarText, 1, 1) == \" \" then\n\t\t\tchatBarText = string.sub(chatBarText, 2)\n\t\tend\n\t\tobj.ChatBar:ResetCustomState()\n\t\tobj.ChatBar:SetTextBoxText(chatBarText)\n\t\tobj.ChatBar:CaptureFocus()\n\tend)\n\n\tobj:EnterTeamChat()\n\n\treturn obj\nend\n\nfunction ProcessMessage(message, ChatWindow, ChatBar, ChatSettings)\n\tif ChatBar.TargetChannel == \"Team\" then\n\t\treturn\n\tend\n\n\tif IsTeamCommand(message) then\n\t\treturn TeamCustomState.new(ChatWindow, ChatBar, ChatSettings)\n\tend\n\treturn nil\nend\n\nreturn {\n\t[util.KEY_COMMAND_PROCESSOR_TYPE] = util.IN_PROGRESS_MESSAGE_PROCESSOR,\n\t[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage\n}\n"
  },
  {
    "path": "src/Chat/ClientChatModules/CommandModules/Toggle.lua",
    "content": "--\t// FileName: Toggle.lua\n--\t// Written by: Nicholas_Foreman\n--\t// Description: Allows for toggling chat tags/chat color.\n\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\nlocal ChatConstants = require(script.Parent.Parent:WaitForChild(\"ChatConstants\"))\n\nlocal event = game:GetService(\"ReplicatedStorage\"):WaitForChild(\"DefaultChatSystemChatEvents\"):WaitForChild(\"Toggle\");\nlocal function ProcessMessage(message, ChatWindow, _)\n\tif string.sub(message, 1,  7):lower() == \"/toggle\" then\n\t\tif message:lower() == \"/toggle\" then\n\t\t\tutil:SendSystemMessageToSelf(\"Usage : /toggle <tags/color> : toggles chat tags or chat color.\", ChatWindow:GetCurrentChannel(), {})\n\t\t\treturn true\n\t\telseif message:lower() == \"/toggle tags\" then\n\t\t\tevent:FireServer(\"Tags\")\n\t\t\tutil:SendSystemMessageToSelf(\"Successfully toggled chat tags.\", ChatWindow:GetCurrentChannel(), {})\n\t\t\treturn true\n\t\telseif message:lower() == \"/toggle color\" then\n\t\t\tevent:FireServer(\"Color\")\n\t\t\tutil:SendSystemMessageToSelf(\"Successfully toggled chat color.\", ChatWindow:GetCurrentChannel(), {})\n\t\t\treturn true\n\t\telse\n\t\t\tutil:SendSystemMessageToSelf(\"Usage : /toggle <tags/color> : toggles chat tags or chat color.\", ChatWindow:GetCurrentChannel(), {})\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\nreturn {\n\t[util.KEY_COMMAND_PROCESSOR_TYPE] = util.COMPLETED_MESSAGE_PROCESSOR,\n\t[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage\n}"
  },
  {
    "path": "src/Chat/ClientChatModules/CommandModules/Util.lua",
    "content": "--\t// FileName: Util.lua\n--\t// Written by: TheGamer101\n--\t// Description: Module for shared code between CommandModules.\n\n--[[\nCreating a command module:\n1) Create a new module inside the CommandModules folder.\n2) Create a function that takes a message, the ChatWindow object and the ChatSettings and returns\na bool command processed.\n3) Return this function from the module.\n--]]\n\nlocal clientChatModules = script.Parent.Parent\nlocal ChatConstants = require(clientChatModules:WaitForChild(\"ChatConstants\"))\n\nlocal COMMAND_MODULES_VERSION = 1\n\nlocal KEY_COMMAND_PROCESSOR_TYPE = \"ProcessorType\"\nlocal KEY_PROCESSOR_FUNCTION = \"ProcessorFunction\"\n\n---Command types.\n---Process a command as it is being typed. This allows for manipulation of the chat bar.\nlocal IN_PROGRESS_MESSAGE_PROCESSOR = 0\n---Simply process a completed message.\nlocal COMPLETED_MESSAGE_PROCESSOR = 1\n\nlocal module = {}\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:SendSystemMessageToSelf(message, channelObj, extraData)\n\tlocal messageData =\n\t{\n\t\tID = -1,\n\t\tFromSpeaker = nil,\n\t\tSpeakerUserId = 0,\n\t\tOriginalChannel = channelObj.Name,\n\t\tIsFiltered = true,\n\t\tMessageLength = string.len(message),\n\t\tMessage = message,\n\t\tMessageType = ChatConstants.MessageTypeSystem,\n\t\tTime = os.time(),\n\t\tExtraData = extraData,\n\t}\n\n\tchannelObj:AddMessageToChannel(messageData)\nend\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.COMMAND_MODULES_VERSION = COMMAND_MODULES_VERSION\n\n\tobj.KEY_COMMAND_PROCESSOR_TYPE = KEY_COMMAND_PROCESSOR_TYPE\n\tobj.KEY_PROCESSOR_FUNCTION = KEY_PROCESSOR_FUNCTION\n\n\tobj.IN_PROGRESS_MESSAGE_PROCESSOR = IN_PROGRESS_MESSAGE_PROCESSOR\n\tobj.COMPLETED_MESSAGE_PROCESSOR = COMPLETED_MESSAGE_PROCESSOR\n\n\treturn obj\nend\n\nreturn module.new()\n"
  },
  {
    "path": "src/Chat/ClientChatModules/CommandModules/Whisper.lua",
    "content": "--\t// FileName: Whisper.lua\n--\t// Written by: TheGamer101\n--\t// Description: Whisper chat bar manipulation.\n\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\nlocal ChatSettings = require(script.Parent.Parent:WaitForChild(\"ChatSettings\"))\n\nlocal PlayersService = game:GetService(\"Players\")\n\nlocal LocalPlayer = PlayersService.LocalPlayer\nwhile LocalPlayer == nil do\n\tPlayersService.ChildAdded:wait()\n\tLocalPlayer = PlayersService.LocalPlayer\nend\n\nlocal whisperStateMethods = {}\nwhisperStateMethods.__index = whisperStateMethods\n\nlocal WhisperCustomState = {}\n\nfunction whisperStateMethods:TrimWhisperCommand(text)\n\tif string.sub(text, 1, 3):lower() == \"/w \" then\n\t\treturn string.sub(text, 4)\n\telseif string.sub(text, 1, 9):lower() == \"/whisper \" then\n\t\treturn string.sub(text, 10)\n \tend\n\treturn nil\nend\n\nfunction whisperStateMethods:TrimWhiteSpace(text)\n\tlocal newText = string.gsub(text, \"%s+\", \"\")\n\tlocal wasWhitespaceTrimmed = text[#text] == \" \"\n\treturn newText, wasWhitespaceTrimmed\nend\n\nfunction whisperStateMethods:ShouldAutoCompleteNames()\n\tif ChatSettings.WhisperCommandAutoCompletePlayerNames ~= nil then\n\t\treturn ChatSettings.WhisperCommandAutoCompletePlayerNames\n\tend\n\treturn true\nend\n\nfunction whisperStateMethods:GetWhisperingPlayer(enteredText)\n\tenteredText = enteredText:lower()\n\tlocal trimmedText = self:TrimWhisperCommand(enteredText)\n\tif trimmedText then\n\t\tlocal possiblePlayerName, whitespaceTrimmed = self:TrimWhiteSpace(trimmedText)\n\t\tlocal possibleMatches = {}\n\t\tlocal players = PlayersService:GetPlayers()\n\t\tfor i = 1, #players do\n\t\t\tif players[i] ~= LocalPlayer then\n\t\t\t\tlocal lowerPlayerName = players[i].Name:lower()\n\t\t\t\tif string.sub(lowerPlayerName, 1, string.len(possiblePlayerName)) == possiblePlayerName then\n\t\t\t\t\tpossibleMatches[players[i]] = players[i].Name:lower()\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\tlocal matchCount = 0\n\t\tlocal lastMatch = nil\n\t\tlocal lastMatchName = nil\n\t\tfor player, playerName in pairs(possibleMatches) do\n\t\t\tmatchCount = matchCount + 1\n\t\t\tlastMatch = player\n\t\t\tlastMatchName = playerName\n\t\t\tif playerName == possiblePlayerName and whitespaceTrimmed then\n\t\t\t\treturn player\n\t\t\tend\n\t\tend\n\t\tif matchCount == 1 then\n\t\t\tif self:ShouldAutoCompleteNames() then\n\t\t\t\treturn lastMatch\n\t\t\telseif lastMatchName == possiblePlayerName then\n\t\t\t\treturn lastMatch\n\t\t\tend\n\t\tend\n\tend\n\treturn nil\nend\n\nfunction whisperStateMethods:GetWhisperChanneNameColor()\n\tif self.ChatSettings.WhisperChannelNameColor then\n\t\treturn self.ChatSettings.WhisperChannelNameColor\n\tend\n\treturn Color3.fromRGB(102, 14, 102)\nend\n\nfunction whisperStateMethods:TextUpdated()\n\tlocal newText = self.TextBox.Text\n\tif not self.PlayerNameEntered then\n\t\tlocal player = self:GetWhisperingPlayer(newText)\n\t\tif player then\n\t\t\tself.PlayerNameEntered = true\n\t\t\tself.PlayerName = player.Name\n\n\t\t\tself.MessageModeButton.Size = UDim2.new(0, 1000, 1, 0)\n\t\t\tself.MessageModeButton.Text = string.format(\"[To %s]\", player.Name)\n\t\t\tself.MessageModeButton.TextColor3 = self:GetWhisperChanneNameColor()\n\n\t\t\tlocal xSize = self.MessageModeButton.TextBounds.X\n\t\t\tself.MessageModeButton.Size = UDim2.new(0, xSize, 1, 0)\n\t\t\tself.TextBox.Size = UDim2.new(1, -xSize, 1, 0)\n\t\t\tself.TextBox.Position = UDim2.new(0, xSize, 0, 0)\n\t\t\tself.TextBox.Text = \" \"\n\t\tend\n\telse\n\t\tif newText == \"\" then\n\t\t\tself.MessageModeButton.Text = \"\"\n\t\t\tself.MessageModeButton.Size = UDim2.new(0, 0, 0, 0)\n\t\t\tself.TextBox.Size = UDim2.new(1, 0, 1, 0)\n\t\t\tself.TextBox.Position = UDim2.new(0, 0, 0, 0)\n\t\t\tself.TextBox.Text = \"\"\n\t\t\t---Implement this when setting cursor positon is a thing.\n\t\t\t---self.TextBox.Text = self.OriginalText .. \" \" .. self.PlayerName\n\t\t\tself.PlayerNameEntered = false\n\t\t\t---Temporary until setting cursor position...\n\t\t\tself.ChatBar:ResetCustomState()\n\t\t\tself.ChatBar:CaptureFocus()\n\t\tend\n\tend\nend\n\nfunction whisperStateMethods:GetMessage()\n\tif self.PlayerNameEntered then\n\t\treturn \"/w \" ..self.PlayerName.. \" \" ..self.TextBox.Text\n\tend\n\treturn self.TextBox.Text\nend\n\nfunction whisperStateMethods:ProcessCompletedMessage()\n\treturn false\nend\n\nfunction whisperStateMethods:Destroy()\n\tself.MessageModeConnection:disconnect()\n\tself.Destroyed = true\nend\n\nfunction WhisperCustomState.new(ChatWindow, ChatBar, ChatSettings)\n\tlocal obj = setmetatable({}, whisperStateMethods)\n\tobj.Destroyed = false\n\tobj.ChatWindow = ChatWindow\n\tobj.ChatBar = ChatBar\n\tobj.ChatSettings = ChatSettings\n\tobj.TextBox = ChatBar:GetTextBox()\n\tobj.MessageModeButton = ChatBar:GetMessageModeTextButton()\n\tobj.OriginalWhisperText = \"\"\n\tobj.PlayerNameEntered = false\n\n\tobj.MessageModeConnection = obj.MessageModeButton.MouseButton1Click:connect(function()\n\t\tlocal chatBarText = obj.TextBox.Text\n\t\tif string.sub(chatBarText, 1, 1) == \" \" then\n\t\t\tchatBarText = string.sub(chatBarText, 2)\n\t\tend\n\t\tobj.ChatBar:ResetCustomState()\n\t\tobj.ChatBar:SetTextBoxText(chatBarText)\n\t\tobj.ChatBar:CaptureFocus()\n\tend)\n\n\tobj:TextUpdated()\n\n\treturn obj\nend\n\nfunction ProcessMessage(message, ChatWindow, ChatBar, ChatSettings)\n\tif string.sub(message, 1, 3):lower() == \"/w \" or\tstring.sub(message, 1, 9):lower() == \"/whisper \" then\n\t\treturn WhisperCustomState.new(ChatWindow, ChatBar, ChatSettings)\n\tend\n\treturn nil\nend\n\nreturn {\n\t[util.KEY_COMMAND_PROCESSOR_TYPE] = util.IN_PROGRESS_MESSAGE_PROCESSOR,\n\t[util.KEY_PROCESSOR_FUNCTION] = ProcessMessage\n}\n"
  },
  {
    "path": "src/Chat/ClientChatModules/CommandModules/init.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/Chat/ClientChatModules/MessageCreatorModules/DefaultChatMessage.lua",
    "content": "--\t// FileName: DefaultChatMessage.lua\n--\t// Written by: TheGamer101\n--\t// Description: Create a message label for a standard chat message.\n\nlocal clientChatModules = script.Parent.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal ChatConstants = require(clientChatModules:WaitForChild(\"ChatConstants\"))\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\n\nfunction CreateMessageLabel(messageData, channelName)\n\tif channelName ~= messageData.OriginalChannel then\n\t\tif ChatSettings.ShowChannelsBar then\n\t\t\treturn\n\t\tend\n\tend\n\t\n\tlocal fromSpeaker = messageData.FromSpeaker\n\tlocal message = messageData.Message\n\n\tlocal extraData = messageData.ExtraData or {}\n\tlocal useFont = extraData.Font or ChatSettings.DefaultFont\n\tlocal useTextSize = extraData.TextSize or ChatSettings.ChatWindowTextSize\n\tlocal useNameColor = extraData.NameColor or ChatSettings.DefaultNameColor\n\tlocal useChatColor = extraData.ChatColor or ChatSettings.DefaultChatColor\n\tlocal useChannelColor = extraData.ChannelColor or useChatColor\n\tlocal tags = extraData.Tags or {}\n\n\tlocal formatUseName = string.format(\"%s: \", fromSpeaker)\n\tlocal speakerNameSize = util:GetStringTextBounds(formatUseName, useFont, useTextSize)\n\tlocal numNeededSpaces = util:GetNumberOfSpaces(formatUseName, useFont, useTextSize)\n\n\tlocal BaseFrame, BaseMessage = util:CreateBaseMessage(\"\", useFont, useTextSize, useChatColor)\n\tlocal NameButton = util:AddNameButtonToBaseMessage(BaseMessage, useNameColor, formatUseName, fromSpeaker)\n\n\tlocal ChannelButton = nil\n\n\tlocal guiObjectSpacing = UDim2.new(0, 0, 0, 0)\n\n\tif channelName ~= messageData.OriginalChannel then\n\t\tlocal formatChannelName = string.format(\"{%s} \", messageData.OriginalChannel)\n\t\tChannelButton = util:AddChannelButtonToBaseMessage(BaseMessage, useChannelColor, formatChannelName, messageData.OriginalChannel)\n\t\tguiObjectSpacing = UDim2.new(0, ChannelButton.Size.X.Offset, 0, 0)\n\t\tnumNeededSpaces = numNeededSpaces + util:GetNumberOfSpaces(formatChannelName, useFont, useTextSize)\n\tend\n\n\tlocal tagLabels = {}\n\t\n\t\n\tlocal player = game.Players:FindFirstChild(fromSpeaker)\n\tif player and player:FindFirstChild(\"level\") then\n\t\tlocal tagColor = Color3.fromRGB(255, 219, 12)\n\t\tlocal tagText = \"Lvl.\"..player.level.Value\n\t\tlocal formatTagText = string.format(\"[%s] \", tagText)\n\t\tlocal label = util:AddTagLabelToBaseMessage(BaseMessage, tagColor, formatTagText)\n\t\tlabel.Position = guiObjectSpacing\n\n\t\tnumNeededSpaces = numNeededSpaces + util:GetNumberOfSpaces(formatTagText, useFont, useTextSize)\n\t\tguiObjectSpacing = guiObjectSpacing + UDim2.new(0, label.Size.X.Offset, 0, 0)\n\n\t\ttable.insert(tagLabels, label)\t\t\n\tend\n\t\n\tfor i, tag in pairs(tags) do\n\t\tlocal tagColor = tag.TagColor or Color3.fromRGB(255, 0, 255)\n\t\tlocal tagText = tag.TagText or \"???\"\n\t\tlocal formatTagText = string.format(\"[%s] \", tagText)\n\t\tlocal label = util:AddTagLabelToBaseMessage(BaseMessage, tagColor, formatTagText)\n\t\tlabel.Position = guiObjectSpacing\n\n\t\tnumNeededSpaces = numNeededSpaces + util:GetNumberOfSpaces(formatTagText, useFont, useTextSize)\n\t\tguiObjectSpacing = guiObjectSpacing + UDim2.new(0, label.Size.X.Offset, 0, 0)\n\n\t\ttable.insert(tagLabels, label)\n\tend\n\n\tNameButton.Position = guiObjectSpacing\n\n\tlocal function UpdateTextFunction(messageObject)\n\t\tif messageData.IsFiltered then\n\t\t\tBaseMessage.Text = string.rep(\" \", numNeededSpaces) .. messageObject.Message\n\t\telse\n\t\t\tBaseMessage.Text = string.rep(\" \", numNeededSpaces) .. string.rep(\"_\", messageObject.MessageLength)\n\t\tend\n\tend\n\n\tUpdateTextFunction(messageData)\n\n\tlocal function GetHeightFunction(xSize)\n\t\treturn util:GetMessageHeight(BaseMessage, BaseFrame, xSize)\n\tend\n\n\tlocal FadeParmaters = {}\n\tFadeParmaters[NameButton] = {\n\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t}\n\n\tFadeParmaters[BaseMessage] = {\n\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t}\n\n\tfor i, tagLabel in pairs(tagLabels) do\n\t\tlocal index = string.format(\"Tag%d\", i)\n\t\tFadeParmaters[tagLabel] = {\n\t\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t\t}\n\tend\n\n\tif ChannelButton then\n\t\tFadeParmaters[ChannelButton] = {\n\t\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t\t}\n\tend\n\n\tlocal FadeInFunction, FadeOutFunction, UpdateAnimFunction = util:CreateFadeFunctions(FadeParmaters)\n\n\treturn {\n\t\t[util.KEY_BASE_FRAME] = BaseFrame,\n\t\t[util.KEY_BASE_MESSAGE] = BaseMessage,\n\t\t[util.KEY_UPDATE_TEXT_FUNC] = UpdateTextFunction,\n\t\t[util.KEY_GET_HEIGHT] = GetHeightFunction,\n\t\t[util.KEY_FADE_IN] = FadeInFunction,\n\t\t[util.KEY_FADE_OUT] = FadeOutFunction,\n\t\t[util.KEY_UPDATE_ANIMATION] = UpdateAnimFunction\n\t}\nend\n\nreturn {\n\t[util.KEY_MESSAGE_TYPE] = ChatConstants.MessageTypeDefault,\n\t[util.KEY_CREATOR_FUNCTION] = CreateMessageLabel\n}\n"
  },
  {
    "path": "src/Chat/ClientChatModules/MessageCreatorModules/MeCommandMessage.lua",
    "content": "--\t// FileName: MeCommandMessage.lua\n--\t// Written by: TheGamer101\n--\t// Description: Create a message label for a me command message.\n\nlocal clientChatModules = script.Parent.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal ChatConstants = require(clientChatModules:WaitForChild(\"ChatConstants\"))\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\n\nfunction CreateMeCommandMessageLabel(messageData, channelName)\n\tif channelName ~= messageData.OriginalChannel then\n\t\tif ChatSettings.ShowChannelsBar then\n\t\t\treturn\n\t\tend\n\tend\n\t\n\tlocal fromSpeaker = messageData.FromSpeaker\n\tlocal message = messageData.Message\n\n\tlocal extraData = messageData.ExtraData or {}\n\tlocal useFont = extraData.Font or Enum.Font.SourceSansItalic\n\tlocal useTextSize = extraData.TextSize or ChatSettings.ChatWindowTextSize\n\tlocal useNameColor = extraData.NameColor or ChatSettings.DefaultNameColor\n\tlocal useChatColor = extraData.ChatColor or ChatSettings.DefaultChatColor\n\tlocal useChannelColor = extraData.ChannelColor or useChatColor\n\tlocal tags = extraData.Tags or {}\n\n\tlocal formatUseName = string.format(\"%s \", fromSpeaker)\n\tlocal speakerNameSize = util:GetStringTextBounds(formatUseName, useFont, useTextSize)\n\tlocal numNeededSpaces = util:GetNumberOfSpaces(formatUseName, useFont, useTextSize)\n\n\tlocal guiObjectSpacing = UDim2.new(0, 0, 0, 0)\n\n\tlocal BaseFrame, BaseMessage = util:CreateBaseMessage(\"\", useFont, useTextSize, useChatColor)\n\tlocal NameButton = util:AddNameButtonToBaseMessage(BaseMessage, useNameColor, formatUseName, fromSpeaker)\n\tlocal ChannelButton = nil\n\n\tif channelName ~= messageData.OriginalChannel then\n\t\tlocal formatChannelName = string.format(\"{%s} \", messageData.OriginalChannel)\n\t\tChannelButton = util:AddChannelButtonToBaseMessage(BaseMessage, useChannelColor, formatChannelName, messageData.OriginalChannel)\n\t\tnumNeededSpaces = numNeededSpaces + util:GetNumberOfSpaces(formatChannelName, useFont, useTextSize)\n\t\tguiObjectSpacing = UDim2.new(0, ChannelButton.Size.X.Offset, 0, 0)\n\tend\n\n\tlocal tagLabels = {}\n\tfor i, tag in pairs(tags) do\n\t\tlocal tagColor = tag.TagColor or Color3.fromRGB(255, 0, 255)\n\t\tlocal tagText = tag.TagText or \"???\"\n\t\t--I personally suggest remove the space, but it depends on you :?\n\t\tlocal formatTagText = string.format(\"[%s] \", tagText)\n\t\tlocal label = util:AddTagLabelToBaseMessage(BaseMessage, tagColor, formatTagText)\n\t\tlabel.Position = guiObjectSpacing\n\n\t\tnumNeededSpaces = numNeededSpaces + util:GetNumberOfSpaces(formatTagText, useFont, useTextSize)\n\t\tguiObjectSpacing = guiObjectSpacing + UDim2.new(0, label.Size.X.Offset, 0, 0)\n\n\t\ttable.insert(tagLabels, label)\n\tend\n\t\n\tNameButton.Position = guiObjectSpacing\n\n\tlocal function UpdateTextFunction(messageObject)\n\t\tif messageData.IsFiltered then\n\t\t\tBaseMessage.Text = string.rep(\" \", numNeededSpaces) .. string.sub(messageObject.Message, 5)\n\t\telse\n\t\t\tlocal messageLength = string.len(messageObject.FromSpeaker) + messageObject.MessageLength - 4\n\t\t\tBaseMessage.Text = string.rep(\" \", numNeededSpaces) .. string.rep(\"_\", messageLength)\n\t\tend\n\tend\n\n\tUpdateTextFunction(messageData)\n\n\tlocal function GetHeightFunction(xSize)\n\t\treturn util:GetMessageHeight(BaseMessage, BaseFrame, xSize)\n\tend\n\n\tlocal FadeParmaters = {}\n\tFadeParmaters[NameButton] = {\n\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t}\n\n\tFadeParmaters[BaseMessage] = {\n\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t}\n\n\tfor i, tagLabel in pairs(tagLabels) do\n\t\tlocal index = string.format(\"Tag%d\", i)\n\t\tFadeParmaters[tagLabel] = {\n\t\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t\t}\n\tend\n\n\tif ChannelButton then\n\t\tFadeParmaters[ChannelButton] = {\n\t\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t\t}\n\tend\n\n\tlocal FadeInFunction, FadeOutFunction, UpdateAnimFunction = util:CreateFadeFunctions(FadeParmaters)\n\n\treturn {\n\t\t[util.KEY_BASE_FRAME] = BaseFrame,\n\t\t[util.KEY_BASE_MESSAGE] = BaseMessage,\n\t\t[util.KEY_UPDATE_TEXT_FUNC] = UpdateTextFunction,\n\t\t[util.KEY_GET_HEIGHT] = GetHeightFunction,\n\t\t[util.KEY_FADE_IN] = FadeInFunction,\n\t\t[util.KEY_FADE_OUT] = FadeOutFunction,\n\t\t[util.KEY_UPDATE_ANIMATION] = UpdateAnimFunction\n\t}\nend\n\nreturn {\n\t[util.KEY_MESSAGE_TYPE] = ChatConstants.MessageTypeMeCommand,\n\t[util.KEY_CREATOR_FUNCTION] = CreateMeCommandMessageLabel\n}\n"
  },
  {
    "path": "src/Chat/ClientChatModules/MessageCreatorModules/SetCoreMessage.lua",
    "content": "--\t// FileName: SetCoreMessage.lua\n--\t// Written by: TheGamer101\n--\t// Description: Create a message label for a message created with SetCore(ChatMakeSystemMessage).\n\nlocal clientChatModules = script.Parent.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal ChatConstants = require(clientChatModules:WaitForChild(\"ChatConstants\"))\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\n\nfunction CreateSetCoreMessageLabel(messageData, channelName)\n\tif channelName ~= messageData.OriginalChannel then\n\t\tif ChatSettings.ShowChannelsBar then\n\t\t\treturn\n\t\tend\n\tend\n\n\tlocal message = messageData.Message\n\tlocal extraData = messageData.ExtraData or {}\n\tlocal useFont = extraData.Font or ChatSettings.DefaultFont\n\tlocal useTextSize = extraData.TextSize or ChatSettings.ChatWindowTextSize\n\tlocal useColor = extraData.Color or ChatSettings.DefaultMessageColor\n\n\tlocal BaseFrame, BaseMessage = util:CreateBaseMessage(message, useFont, useTextSize, useColor)\n\n\tlocal function GetHeightFunction(xSize)\n\t\treturn util:GetMessageHeight(BaseMessage, BaseFrame, xSize)\n\tend\n\n\tlocal FadeParmaters = {}\n\tFadeParmaters[BaseMessage] = {\n\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t}\n\n\tlocal FadeInFunction, FadeOutFunction, UpdateAnimFunction = util:CreateFadeFunctions(FadeParmaters)\n\n\treturn {\n\t\t[util.KEY_BASE_FRAME] = BaseFrame,\n\t\t[util.KEY_BASE_MESSAGE] = BaseMessage,\n\t\t[util.KEY_UPDATE_TEXT_FUNC] = nil,\n\t\t[util.KEY_GET_HEIGHT] = GetHeightFunction,\n\t\t[util.KEY_FADE_IN] = FadeInFunction,\n\t\t[util.KEY_FADE_OUT] = FadeOutFunction,\n\t\t[util.KEY_UPDATE_ANIMATION] = UpdateAnimFunction\n\t}\nend\n\nreturn {\n\t[util.KEY_MESSAGE_TYPE] = ChatConstants.MessageTypeSetCore,\n\t[util.KEY_CREATOR_FUNCTION] = CreateSetCoreMessageLabel\n}\n"
  },
  {
    "path": "src/Chat/ClientChatModules/MessageCreatorModules/SystemMessage.lua",
    "content": "--\t// FileName: SystemMessage.lua\n--\t// Written by: TheGamer101\n--\t// Description: Create a message label for a system message.\n\nlocal clientChatModules = script.Parent.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal ChatConstants = require(clientChatModules:WaitForChild(\"ChatConstants\"))\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\n\nfunction CreateSystemMessageLabel(messageData, channelName)\n\tif channelName ~= messageData.OriginalChannel then\n\t\tif ChatSettings.ShowChannelsBar then\n\t\t\treturn\n\t\tend\n\tend\n\n\tlocal message = messageData.Message\n\tlocal extraData = messageData.ExtraData or {}\n\tlocal useFont = extraData.Font or ChatSettings.DefaultFont\n\tlocal useTextSize = extraData.TextSize or ChatSettings.ChatWindowTextSize\n\tlocal useChatColor = extraData.ChatColor or ChatSettings.DefaultMessageColor\n\tlocal useChannelColor = Color3.fromRGB(255, 0, 0)--extraData.ChannelColor or useChatColor\n\n\tlocal BaseFrame, BaseMessage = util:CreateBaseMessage(message, useFont, useTextSize, useChatColor)\n\tlocal ChannelButton = nil\n\n\tif channelName ~= messageData.OriginalChannel then\n\t\tlocal formatChannelName = string.format(\"[%s] \", string.upper(messageData.OriginalChannel))\n\t\tChannelButton = util:AddChannelButtonToBaseMessage(BaseMessage, useChannelColor, formatChannelName, messageData.OriginalChannel)\n\t\tlocal numNeededSpaces = util:GetNumberOfSpaces(formatChannelName, useFont, useTextSize)\n\t\tBaseMessage.Text = string.rep(\" \", numNeededSpaces) .. message\n\tend\n\n\tlocal function GetHeightFunction(xSize)\n\t\treturn util:GetMessageHeight(BaseMessage, BaseFrame, xSize)\n\tend\n\n\tlocal FadeParmaters = {}\n\tFadeParmaters[BaseMessage] = {\n\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t}\n\n\tif ChannelButton then\n\t\tFadeParmaters[ChannelButton] = {\n\t\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t\t}\n\tend\n\n\tlocal FadeInFunction, FadeOutFunction, UpdateAnimFunction = util:CreateFadeFunctions(FadeParmaters)\n\n\treturn {\n\t\t[util.KEY_BASE_FRAME] = BaseFrame,\n\t\t[util.KEY_BASE_MESSAGE] = BaseMessage,\n\t\t[util.KEY_UPDATE_TEXT_FUNC] = nil,\n\t\t[util.KEY_GET_HEIGHT] = GetHeightFunction,\n\t\t[util.KEY_FADE_IN] = FadeInFunction,\n\t\t[util.KEY_FADE_OUT] = FadeOutFunction,\n\t\t[util.KEY_UPDATE_ANIMATION] = UpdateAnimFunction\n\t}\nend\n\nreturn {\n\t[util.KEY_MESSAGE_TYPE] = ChatConstants.MessageTypeSystem,\n\t[util.KEY_CREATOR_FUNCTION] = CreateSystemMessageLabel\n}\n"
  },
  {
    "path": "src/Chat/ClientChatModules/MessageCreatorModules/UnknownMessage.lua",
    "content": "--\t// FileName: UnknownMessage.lua\n--\t// Written by: TheGamer101\n--\t// Description: Default handler for message types with no other creator registered.\n--\t// Just print that there was a message with no creator for now.\n\nlocal MESSAGE_TYPE = \"UnknownMessage\"\n\nlocal clientChatModules = script.Parent.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\n\nfunction CreateUnknownMessageLabel(messageData)\n\tprint(\"No message creator for message: \" ..messageData.Message)\nend\n\nreturn {\n\t[util.KEY_MESSAGE_TYPE] = MESSAGE_TYPE,\n\t[util.KEY_CREATOR_FUNCTION] = CreateUnknownMessageLabel\n}\n"
  },
  {
    "path": "src/Chat/ClientChatModules/MessageCreatorModules/Util.lua",
    "content": "--\t// FileName: Util.lua\n--\t// Written by: Xsitsu, TheGamer101\n--\t// Description: Module for shared code between MessageCreatorModules.\n\n--[[\nCreating a message creator module:\n1) Create a new module inside the MessageCreatorModules folder.\n2) Create a function that takes a messageData object and returns:\n{\n\tKEY_BASE_FRAME = BaseFrame,\n\tKEY_BASE_MESSAGE = BaseMessage,\n\tKEY_UPDATE_TEXT_FUNC = function(newMessageObject) ---Function to update the text of the message.\n\tKEY_GET_HEIGHT = function() ---Function to get the height of the message in absolute pixels,\n\tKEY_FADE_IN = function(duration, CurveUtil) ---Function to tell the message to start fading in.\n\tKEY_FADE_OUT = function(duration, CurveUtil) ---Function to tell the message to start fading out.\n\tKEY_UPDATE_ANIMATION = function(dtScale, CurveUtil) ---Update animation function.\n}\n3) return the following format from the module:\n{\n\tKEY_MESSAGE_TYPE = \"Message type this module creates messages for.\"\n\tKEY_CREATOR_FUNCTION = YourFunctionHere\n}\n--]]\n\nlocal DEFAULT_MESSAGE_CREATOR = \"UnknownMessage\"\nlocal MESSAGE_CREATOR_MODULES_VERSION = 1\n---Creator Module Object Keys\nlocal KEY_MESSAGE_TYPE = \"MessageType\"\nlocal KEY_CREATOR_FUNCTION = \"MessageCreatorFunc\"\n---Creator function return object keys\nlocal KEY_BASE_FRAME = \"BaseFrame\"\nlocal KEY_BASE_MESSAGE = \"BaseMessage\"\nlocal KEY_UPDATE_TEXT_FUNC = \"UpdateTextFunction\"\nlocal KEY_GET_HEIGHT = \"GetHeightFunction\"\nlocal KEY_FADE_IN = \"FadeInFunction\"\nlocal KEY_FADE_OUT = \"FadeOutFunction\"\nlocal KEY_UPDATE_ANIMATION = \"UpdateAnimFunction\"\n\nlocal TextService = game:GetService(\"TextService\")\n\nlocal Players = game:GetService(\"Players\")\nlocal LocalPlayer = Players.LocalPlayer\nwhile not LocalPlayer do\n\tPlayers.ChildAdded:wait()\n\tLocalPlayer = Players.LocalPlayer\nend\n\nlocal clientChatModules = script.Parent.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal ChatConstants = require(clientChatModules:WaitForChild(\"ChatConstants\"))\n\nlocal okShouldClipInGameChat, valueShouldClipInGameChat = pcall(function() return UserSettings():IsUserFeatureEnabled(\"UserShouldClipInGameChat\") end)\nlocal shouldClipInGameChat = okShouldClipInGameChat and valueShouldClipInGameChat\n\nlocal module = {}\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:GetStringTextBounds(text, font, textSize, sizeBounds)\n\tsizeBounds = sizeBounds or Vector2.new(10000, 10000)\n\treturn TextService:GetTextSize(text, textSize, font, sizeBounds)\nend\n--// Above was taken directly from Util.GetStringTextBounds() in the old chat corescripts.\n\nfunction methods:GetMessageHeight(BaseMessage, BaseFrame, xSize)\n\txSize = xSize or BaseFrame.AbsoluteSize.X\n\tlocal textBoundsSize = self:GetStringTextBounds(BaseMessage.Text, BaseMessage.Font, BaseMessage.TextSize, Vector2.new(xSize, 1000))\n\treturn textBoundsSize.Y\nend\n\nfunction methods:GetNumberOfSpaces(str, font, textSize)\n\tlocal strSize = self:GetStringTextBounds(str, font, textSize)\n\tlocal singleSpaceSize = self:GetStringTextBounds(\" \", font, textSize)\n\treturn math.ceil(strSize.X / singleSpaceSize.X)\nend\n\nfunction methods:CreateBaseMessage(message, font, textSize, chatColor)\n\tlocal BaseFrame = self:GetFromObjectPool(\"Frame\")\n\tBaseFrame.Selectable = false\n\tBaseFrame.Size = UDim2.new(1, 0, 0, 18)\n\tBaseFrame.Visible = true\n\tBaseFrame.BackgroundTransparency = 1\n\n\tlocal messageBorder = 8\n\n\tlocal BaseMessage = self:GetFromObjectPool(\"TextLabel\")\n\tBaseMessage.Selectable = false\n\tBaseMessage.Size = UDim2.new(1, -(messageBorder + 6), 1, 0)\n\tBaseMessage.Position = UDim2.new(0, messageBorder, 0, 0)\n\tBaseMessage.BackgroundTransparency = 1\n\tBaseMessage.Font = font\n\tBaseMessage.TextSize = textSize\n\tBaseMessage.TextXAlignment = Enum.TextXAlignment.Left\n\tBaseMessage.TextYAlignment = Enum.TextYAlignment.Top\n\tBaseMessage.TextTransparency = 0\n\tBaseMessage.TextColor3 = chatColor\n\tBaseMessage.TextWrapped = true\n\tif shouldClipInGameChat then\n\t\tBaseMessage.ClipsDescendants = true\n\tend\n\tBaseMessage.Text = message\n\tBaseMessage.Visible = true\n\tBaseMessage.Parent = BaseFrame\n\t-- Vesteria Styling\n\tBaseMessage.TextStrokeTransparency = 0\n\tBaseMessage.TextStrokeColor3 = Color3.new(0.2, 0.2, 0.2)\n\n\treturn BaseFrame, BaseMessage\nend\n\nfunction methods:AddNameButtonToBaseMessage(BaseMessage, nameColor, formatName, playerName)\n\tlocal speakerNameSize = self:GetStringTextBounds(formatName, BaseMessage.Font, BaseMessage.TextSize)\n\tlocal NameButton = self:GetFromObjectPool(\"TextButton\")\n\tNameButton.Selectable = false\n\tNameButton.Size = UDim2.new(0, speakerNameSize.X, 0, speakerNameSize.Y)\n\tNameButton.Position = UDim2.new(0, 0, 0, 0)\n\tNameButton.BackgroundTransparency = 1\n\tNameButton.Font = BaseMessage.Font\n\tNameButton.TextSize = BaseMessage.TextSize\n\tNameButton.TextXAlignment = BaseMessage.TextXAlignment\n\tNameButton.TextYAlignment = BaseMessage.TextYAlignment\n\tNameButton.TextTransparency = BaseMessage.TextTransparency\n\tNameButton.TextStrokeTransparency = BaseMessage.TextStrokeTransparency\n\tNameButton.TextStrokeColor3 = BaseMessage.TextStrokeColor3\n\tNameButton.TextColor3 = nameColor\n\tNameButton.Text = formatName\n\tNameButton.Visible = true\n\tNameButton.Parent = BaseMessage\n\n\tlocal clickedConn = NameButton.MouseButton1Click:connect(function()\n\t\tself:NameButtonClicked(NameButton, playerName)\n\tend)\n\n\tlocal changedConn = nil\n\tchangedConn = NameButton.Changed:connect(function(prop)\n\t\tif prop == \"Parent\" then\n\t\t\tclickedConn:Disconnect()\n\t\t\tchangedConn:Disconnect()\n\t\tend\n\tend)\n\n\treturn NameButton\nend\n\nfunction methods:AddChannelButtonToBaseMessage(BaseMessage, channelColor, formatChannelName, channelName)\n\tlocal channelNameSize = self:GetStringTextBounds(formatChannelName, BaseMessage.Font, BaseMessage.TextSize)\n\tlocal ChannelButton = self:GetFromObjectPool(\"TextButton\")\n\tChannelButton.Selectable = false\n\tChannelButton.Size = UDim2.new(0, channelNameSize.X, 0, channelNameSize.Y)\n\tChannelButton.Position = UDim2.new(0, 0, 0, 0)\n\tChannelButton.BackgroundTransparency = 1\n\tChannelButton.Font = BaseMessage.Font\n\tChannelButton.TextSize = BaseMessage.TextSize\n\tChannelButton.TextXAlignment = BaseMessage.TextXAlignment\n\tChannelButton.TextYAlignment = BaseMessage.TextYAlignment\n\tChannelButton.TextTransparency = BaseMessage.TextTransparency\n\tChannelButton.TextStrokeTransparency = BaseMessage.TextStrokeTransparency\n\tChannelButton.TextStrokeColor3 = BaseMessage.TextStrokeColor3\n\tChannelButton.TextColor3 = channelColor\n\tChannelButton.Text = formatChannelName\n\tChannelButton.Visible = true\n\tChannelButton.Parent = BaseMessage\n\n\tlocal clickedConn = ChannelButton.MouseButton1Click:connect(function()\n\t\tself:ChannelButtonClicked(ChannelButton, channelName)\n\tend)\n\n\tlocal changedConn = nil\n \tchangedConn = ChannelButton.Changed:connect(function(prop)\n\t\tif prop == \"Parent\" then\n\t\t\tclickedConn:Disconnect()\n\t\t\tchangedConn:Disconnect()\n\t\tend\n\tend)\n\n\treturn ChannelButton\nend\n\nfunction methods:AddTagLabelToBaseMessage(BaseMessage, tagColor, formatTagText)\n\tlocal tagNameSize = self:GetStringTextBounds(formatTagText, BaseMessage.Font, BaseMessage.TextSize)\n\tlocal TagLabel = self:GetFromObjectPool(\"TextLabel\")\n\tTagLabel.Selectable = false\n\tTagLabel.Size = UDim2.new(0, tagNameSize.X, 0, tagNameSize.Y)\n\tTagLabel.Position = UDim2.new(0, 0, 0, 0)\n\tTagLabel.BackgroundTransparency = 1\n\tTagLabel.Font = BaseMessage.Font\n\tTagLabel.TextSize = BaseMessage.TextSize\n\tTagLabel.TextXAlignment = BaseMessage.TextXAlignment\n\tTagLabel.TextYAlignment = BaseMessage.TextYAlignment\n\tTagLabel.TextTransparency = BaseMessage.TextTransparency\n\tTagLabel.TextStrokeTransparency = BaseMessage.TextStrokeTransparency\n\tTagLabel.TextStrokeColor3 = BaseMessage.TextStrokeColor3\n\tTagLabel.TextColor3 = tagColor\n\tTagLabel.Text = formatTagText\n\tTagLabel.Visible = true\n\tTagLabel.Parent = BaseMessage\t\n\n\treturn TagLabel\nend\n\nfunction GetWhisperChannelPrefix()\n\tif ChatConstants.WhisperChannelPrefix then\n\t\treturn ChatConstants.WhisperChannelPrefix\n\tend\n\treturn \"To \"\nend\n\nfunction methods:NameButtonClicked(nameButton, playerName)\n\tif not self.ChatWindow then\n\t\treturn\n\tend\n\n\tif ChatSettings.ClickOnPlayerNameToWhisper then\n\t\tlocal player = Players:FindFirstChild(playerName)\n\t\tif player and player ~= LocalPlayer then\n\t\t\tlocal whisperChannel = GetWhisperChannelPrefix() ..playerName\n\t\t\tif self.ChatWindow:GetChannel(whisperChannel) then\n\t\t\t\tself.ChatBar:ResetCustomState()\n\t\t\t\tlocal targetChannelName = self.ChatWindow:GetTargetMessageChannel()\n\t\t\t\tif targetChannelName ~= whisperChannel then\n\t\t\t\t\tself.ChatWindow:SwitchCurrentChannel(whisperChannel)\n\t\t\t\tend\n\n\t\t\t\tlocal whisperMessage = \"/w \" ..playerName\n\t\t\t\tself.ChatBar:SetText(whisperMessage)\n\n\t\t\t\tself.ChatBar:CaptureFocus()\n\t\t\telseif not self.ChatBar:IsInCustomState() then\n\t\t\t\tlocal whisperMessage = \"/w \" ..playerName\n\t\t\t\tself.ChatBar:SetText(whisperMessage)\n\n\t\t\t\tself.ChatBar:CaptureFocus()\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction methods:ChannelButtonClicked(channelButton, channelName)\n\tif not self.ChatWindow then\n\t\treturn\n\tend\n\n\tif ChatSettings.ClickOnChannelNameToSetMainChannel then\n\t\tif self.ChatWindow:GetChannel(channelName) then\n\t\t\tself.ChatBar:ResetCustomState()\n\t\t\tlocal targetChannelName = self.ChatWindow:GetTargetMessageChannel()\n\t\t\tif targetChannelName ~= channelName then\n\t\t\t\tself.ChatWindow:SwitchCurrentChannel(channelName)\n\t\t\tend\n\t\t\tself.ChatBar:ResetText()\n\t\t\tself.ChatBar:CaptureFocus()\n\t\tend\n\tend\nend\n\nfunction methods:RegisterChatWindow(chatWindow)\n\tself.ChatWindow = chatWindow\n\tself.ChatBar = chatWindow:GetChatBar()\nend\n\nfunction methods:GetFromObjectPool(className)\n\tif self.ObjectPool == nil then\n\t\treturn Instance.new(className)\n\tend\n\treturn self.ObjectPool:GetInstance(className)\nend\n\nfunction methods:RegisterObjectPool(objectPool)\n\tself.ObjectPool = objectPool\nend\n\n-- CreateFadeFunctions usage:\n-- fadeObjects is a map of text labels and button to start and end values for a given property.\n-- e.g {\n--   NameButton = {\n--     TextTransparency = {\n--       FadedIn = 0.5,\n--       FadedOut = 1,\n--     }\n--   },\n--  ImageOne = {\n--\t  ImageTransparency = {\n--       FadedIn = 0,\n--       FadedOut = 0.5,\n--     }\n--   }\n-- }\nfunction methods:CreateFadeFunctions(fadeObjects)\n\tlocal AnimParams = {}\n\tfor object, properties in pairs(fadeObjects) do\n\t\tAnimParams[object] = {}\n\t\tfor property, values in pairs(properties) do\n\t\t\tAnimParams[object][property] = {\n\t\t\t\tTarget = values.FadedIn,\n\t\t\t\tCurrent = object[property],\n\t\t\t\tNormalizedExptValue = 1,\n\t\t\t}\n\t\tend\n\tend\n\n\tlocal function FadeInFunction(duration, CurveUtil)\n\t\tfor object, properties in pairs(AnimParams) do\n\t\t\tfor property, values in pairs(properties) do\n\t\t\t\tvalues.Target = fadeObjects[object][property].FadedIn\n\t\t\t\tvalues.NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function FadeOutFunction(duration, CurveUtil)\n\t\tfor object, properties in pairs(AnimParams) do\n\t\t\tfor property, values in pairs(properties) do\n\t\t\t\tvalues.Target = fadeObjects[object][property].FadedOut\n\t\t\t\tvalues.NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function AnimGuiObjects()\n\t\tfor object, properties in pairs(AnimParams) do\n\t\t\tfor property, values in pairs(properties) do\n\t\t\t\tobject[property] = values.Current\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function UpdateAnimFunction(dtScale, CurveUtil)\n\t\tfor object, properties in pairs(AnimParams) do\n\t\t\tfor property, values in pairs(properties) do\n\t\t\t\tvalues.Current = CurveUtil:Expt(\n\t\t\t\t\tvalues.Current,\n\t\t\t\t\tvalues.Target,\n\t\t\t\t\tvalues.NormalizedExptValue,\n\t\t\t\t\tdtScale\n\t\t\t\t)\n\t\t\tend\n\t\tend\n\n\t\tAnimGuiObjects()\n\tend\n\n\treturn FadeInFunction, FadeOutFunction, UpdateAnimFunction\nend\n\nfunction methods:NewBindableEvent(name)\n\tlocal bindable = Instance.new(\"BindableEvent\")\n\tbindable.Name = name\n\treturn bindable\nend\n\n--- DEPRECATED METHODS:\nfunction methods:RegisterGuiRoot()\n\t-- This is left here for compatibility with ChatScript versions lower than 0.5\nend\n--- End of Deprecated methods.\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.ObjectPool = nil\n\tobj.ChatWindow = nil\n\n\tobj.DEFAULT_MESSAGE_CREATOR = DEFAULT_MESSAGE_CREATOR\n\tobj.MESSAGE_CREATOR_MODULES_VERSION = MESSAGE_CREATOR_MODULES_VERSION\n\n\tobj.KEY_MESSAGE_TYPE = KEY_MESSAGE_TYPE\n\tobj.KEY_CREATOR_FUNCTION = KEY_CREATOR_FUNCTION\n\n\tobj.KEY_BASE_FRAME = KEY_BASE_FRAME\n\tobj.KEY_BASE_MESSAGE = KEY_BASE_MESSAGE\n\tobj.KEY_UPDATE_TEXT_FUNC = KEY_UPDATE_TEXT_FUNC\n\tobj.KEY_GET_HEIGHT = KEY_GET_HEIGHT\n\tobj.KEY_FADE_IN = KEY_FADE_IN\n\tobj.KEY_FADE_OUT = KEY_FADE_OUT\n\tobj.KEY_UPDATE_ANIMATION = KEY_UPDATE_ANIMATION\n\n\treturn obj\nend\n\nreturn module.new()\n"
  },
  {
    "path": "src/Chat/ClientChatModules/MessageCreatorModules/WelcomeMessage.lua",
    "content": "--\t// FileName: WelcomeMessage.lua\n--\t// Written by: TheGamer101\n--\t// Description: Create a message label for a welcome message.\n\nlocal clientChatModules = script.Parent.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal ChatConstants = require(clientChatModules:WaitForChild(\"ChatConstants\"))\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\n\nfunction CreateWelcomeMessageLabel(messageData, channelName)\n\tif channelName ~= messageData.OriginalChannel then\n\t\tif ChatSettings.ShowChannelsBar then\n\t\t\treturn\n\t\tend\n\tend\n\n\tlocal message = messageData.Message\n\tlocal extraData = messageData.ExtraData or {}\n\tlocal useFont = extraData.Font or ChatSettings.DefaultFont\n\tlocal useTextSize = extraData.FontSize or ChatSettings.ChatWindowTextSize\n\tlocal useChatColor = extraData.ChatColor or ChatSettings.DefaultMessageColor\n\tlocal useChannelColor = extraData.ChannelColor or useChatColor\n\n\tlocal BaseFrame, BaseMessage = util:CreateBaseMessage(message, useFont, useTextSize, useChatColor)\n\tlocal ChannelButton = nil\n\n\tif channelName ~= messageData.OriginalChannel then\n\t\tlocal formatChannelName = string.format(\"{%s} \", messageData.OriginalChannel)\n\t\tChannelButton = util:AddChannelButtonToBaseMessage(BaseMessage, useChannelColor, formatChannelName, messageData.OriginalChannel)\n\t\tlocal numNeededSpaces = util:GetNumberOfSpaces(formatChannelName, useFont, useTextSize)\n\t\tBaseMessage.Text = string.rep(\" \", numNeededSpaces) .. message\n\tend\n\n\tlocal function GetHeightFunction(xSize)\n\t\treturn util:GetMessageHeight(BaseMessage, BaseFrame, xSize)\n\tend\n\n\tlocal FadeParmaters = {}\n\tFadeParmaters[BaseMessage] = {\n\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t}\n\n\tif ChannelButton then\n\t\tFadeParmaters[ChannelButton] = {\n\t\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t\t}\n\tend\n\n\tlocal FadeInFunction, FadeOutFunction, UpdateAnimFunction = util:CreateFadeFunctions(FadeParmaters)\n\n\treturn {\n\t\t[util.KEY_BASE_FRAME] = BaseFrame,\n\t\t[util.KEY_BASE_MESSAGE] = BaseMessage,\n\t\t[util.KEY_UPDATE_TEXT_FUNC] = nil,\n\t\t[util.KEY_GET_HEIGHT] = GetHeightFunction,\n\t\t[util.KEY_FADE_IN] = FadeInFunction,\n\t\t[util.KEY_FADE_OUT] = FadeOutFunction,\n\t\t[util.KEY_UPDATE_ANIMATION] = UpdateAnimFunction\n\t}\nend\n\nreturn {\n\t[util.KEY_MESSAGE_TYPE] = ChatConstants.MessageTypeWelcome,\n\t[util.KEY_CREATOR_FUNCTION] = CreateWelcomeMessageLabel\n}\n"
  },
  {
    "path": "src/Chat/ClientChatModules/MessageCreatorModules/WhisperMessage.lua",
    "content": "--\t// FileName: WhisperMessage.lua\n--\t// Written by: TheGamer101\n--\t// Description: Create a message label for a whisper chat message.\n\nlocal PlayersService = game:GetService(\"Players\")\nlocal LocalPlayer = PlayersService.LocalPlayer\nwhile not LocalPlayer do\n\tPlayersService.ChildAdded:wait()\n\tLocalPlayer = PlayersService.LocalPlayer\nend\n\nlocal clientChatModules = script.Parent.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal ChatConstants = require(clientChatModules:WaitForChild(\"ChatConstants\"))\nlocal util = require(script.Parent:WaitForChild(\"Util\"))\n\nfunction CreateMessageLabel(messageData, channelName)\n\tif channelName ~= messageData.OriginalChannel then\n\t\tif ChatSettings.ShowChannelsBar then\n\t\t\treturn\n\t\tend\n\tend\n\n\tlocal fromSpeaker = messageData.FromSpeaker\n\tlocal message = messageData.Message\n\n\tlocal extraData = messageData.ExtraData or {}\n\tlocal useFont = extraData.Font or ChatSettings.DefaultFont\n\tlocal useTextSize = extraData.TextSize or ChatSettings.ChatWindowTextSize\n\tlocal useNameColor = extraData.NameColor or ChatSettings.DefaultNameColor\n\tlocal useChatColor = extraData.ChatColor or ChatSettings.DefaultChatColor\n\tlocal useChannelColor = extraData.ChannelColor or useChatColor\n\tlocal tags = extraData.Tags or {}\n\n\tlocal formatUseName = string.format(\"[%s]: \", fromSpeaker)\n\tlocal speakerNameSize = util:GetStringTextBounds(formatUseName, useFont, useTextSize)\n\tlocal numNeededSpaces = util:GetNumberOfSpaces(formatUseName, useFont, useTextSize)\n\n\tlocal BaseFrame, BaseMessage = util:CreateBaseMessage(\"\", useFont, useTextSize, useChatColor)\n\tlocal NameButton = util:AddNameButtonToBaseMessage(BaseMessage, useNameColor, formatUseName, fromSpeaker)\n\tlocal ChannelButton = nil\n\n\tlocal guiObjectSpacing = UDim2.new(0, 0, 0, 0)\n\n\tif channelName ~= messageData.OriginalChannel then\n\t\tlocal whisperString = messageData.OriginalChannel\n\t\tif messageData.FromSpeaker ~= LocalPlayer.Name then\n\t\t\twhisperString = string.format(\"From %s\", messageData.FromSpeaker)\n\t\tend\n\n\t\tlocal formatChannelName = string.format(\"{%s} \", whisperString)\n\t\tChannelButton = util:AddChannelButtonToBaseMessage(BaseMessage, useChannelColor, formatChannelName, messageData.OriginalChannel)\n\t\tguiObjectSpacing = UDim2.new(0, ChannelButton.Size.X.Offset, 0, 0)\n\t\tnumNeededSpaces = numNeededSpaces + util:GetNumberOfSpaces(formatChannelName, useFont, useTextSize)\n\tend\n\n\tlocal tagLabels = {}\n\tfor i, tag in pairs(tags) do\n\t\tlocal tagColor = tag.TagColor or Color3.fromRGB(255, 0, 255)\n\t\tlocal tagText = tag.TagText or \"???\"\n\t\t--I personally suggest remove the space, but it depends on you :?\n\t\tlocal formatTagText = string.format(\"[%s] \", tagText)\n\t\tlocal label = util:AddTagLabelToBaseMessage(BaseMessage, tagColor, formatTagText)\n\t\tlabel.Position = guiObjectSpacing\n\n\t\tnumNeededSpaces = numNeededSpaces + util:GetNumberOfSpaces(formatTagText, useFont, useTextSize)\n\t\tguiObjectSpacing = guiObjectSpacing + UDim2.new(0, label.Size.X.Offset, 0, 0)\n\n\t\ttable.insert(tagLabels, label)\n\tend\n\n\tNameButton.Position = guiObjectSpacing\n\n\tlocal function UpdateTextFunction(messageObject)\n\t\tif messageData.IsFiltered then\n\t\t\tBaseMessage.Text = string.rep(\" \", numNeededSpaces) .. messageObject.Message\n\t\telse\n\t\t\tBaseMessage.Text = string.rep(\" \", numNeededSpaces) .. string.rep(\"_\", messageObject.MessageLength)\n\t\tend\n\tend\n\n\tUpdateTextFunction(messageData)\n\n\tlocal function GetHeightFunction(xSize)\n\t\treturn util:GetMessageHeight(BaseMessage, BaseFrame, xSize)\n\tend\n\n\tlocal FadeParmaters = {}\n\tFadeParmaters[NameButton] = {\n\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t}\n\n\tFadeParmaters[BaseMessage] = {\n\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t}\n\t\n\tfor i, tagLabel in pairs(tagLabels) do\n\t\tlocal index = string.format(\"Tag%d\", i)\n\t\tFadeParmaters[tagLabel] = {\n\t\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t\t}\n\tend\n\n\tif ChannelButton then\n\t\tFadeParmaters[ChannelButton] = {\n\t\t\tTextTransparency = {FadedIn = 0, FadedOut = 1},\n\t\t\tTextStrokeTransparency = {FadedIn = BaseMessage.TextStrokeTransparency, FadedOut = 1}\n\t\t}\n\tend\n\n\tlocal FadeInFunction, FadeOutFunction, UpdateAnimFunction = util:CreateFadeFunctions(FadeParmaters)\n\n\treturn {\n\t\t[util.KEY_BASE_FRAME] = BaseFrame,\n\t\t[util.KEY_BASE_MESSAGE] = BaseMessage,\n\t\t[util.KEY_UPDATE_TEXT_FUNC] = UpdateTextFunction,\n\t\t[util.KEY_GET_HEIGHT] = GetHeightFunction,\n\t\t[util.KEY_FADE_IN] = FadeInFunction,\n\t\t[util.KEY_FADE_OUT] = FadeOutFunction,\n\t\t[util.KEY_UPDATE_ANIMATION] = UpdateAnimFunction\n\t}\nend\n\nreturn {\n\t[util.KEY_MESSAGE_TYPE] = ChatConstants.MessageTypeWhisper,\n\t[util.KEY_CREATOR_FUNCTION] = CreateMessageLabel\n}\n"
  },
  {
    "path": "src/Chat/ClientChatModules/MessageCreatorModules/init.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/Chat/ClientChatModules/init.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/admin.lua",
    "content": "local abilities = {} do\n\t--[[\n\tcoroutine.resume(coroutine.create(function()\n\t\tfor i, v in pairs(game.ReplicatedStorage.abilityLookup:GetChildren()) do\n\t\t\ttable.insert(abilities, {id = require(v).id})\n\t\tend\n\tend))\n\t]]\n\tspawn(function()\n\t\tlocal abilityLookup = require(game.ReplicatedStorage.abilityLookup)\n\t\tfor id, ability in pairs(abilityLookup) do\n\t\t\t-- prevent duplicates for string ids\n\t\t\tif typeof(id) == \"number\" then\n\t\t\t\ttable.insert(abilities, {id = id})\n\t\t\tend\n\t\tend\n\tend)\nend\n\nreturn {\n\tname \t\t\t\t\t= \"Admin\";\n\tpointsGainPerLevel \t\t= 5;\n\tstartingPoints \t\t\t= 0;\n\tlockPointsOnClassChange = false;\n\tminLevel \t\t\t\t= 1;\n\tmaxLevel\t\t\t\t= 9999999999;\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 5;\n\tbookColor\t\t\t\t= Color3.fromRGB(130, 78, 154);\n\t\n\tabilities = abilities;\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/adventurer.lua",
    "content": "return {\n\tname \t\t\t\t\t= \"Adventurer\";\n\tdescription\t\t\t\t= \"A fledgling explorer can pick up many neat tricks on their adventures.\";\n\tpointsGainPerLevel \t\t= 1;\n\tstartingPoints \t\t\t= 0;\n\tlockPointsOnClassChange = true;\n\tminLevel \t\t\t\t= 1;\n\tmaxLevel\t\t\t\t= 10;\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 1;\n\tbookColor\t\t\t\t= Color3.fromRGB(130, 98, 54);\n\t\n\tthumbnail = \"rbxassetid://3559739117\";\n\t\n\tabilities = {\n\t\t{\n\t\t\tid \t\t\t\t= 3;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\t\n\t\t\n\t\t{\n\t\t\tid \t\t\t\t= 34;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\t\t\n\t\t\t\n\t\t{\n\t\t\tid \t\t\t\t= 1;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t\n\t\t{\n\t\t\tid \t\t\t\t= 2;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t\n\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/assassin.lua",
    "content": "return {\n\tname \t\t\t\t\t= \"Assassin\";\n\tdescription\t\t\t\t= \"Move in silence, strike in darkness. Such is the way.\";\n\tpointsGainPerLevel \t\t= 1;\n\tstartingPoints \t\t\t= 1;\n\tlockPointsOnClassChange = true;\n\tminLevel \t\t\t\t= 30;\n\tmaxLevel\t\t\t\t= 50;\t\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 3;\n\tbookColor\t\t\t\t= Color3.fromRGB(20, 112, 84);\n\t\n\tbookBackgroundImage\t\t= \"rbxassetid://4149312565\";\n\tthumbnail = \"rbxassetid://3559733836\";\n\t\n\tabilities = {\n\t\t{\n\t\t\tid \t\t\t\t= 43;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 14;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 53;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\t\t\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/berserker.lua",
    "content": "return {\n\tname \t\t\t\t\t= \"Berserker\";\n\tdescription\t\t\t\t= \"The fury of the twin blades knows no equal in battle.\";\n\tpointsGainPerLevel \t\t= 1;\n\tstartingPoints \t\t\t= 1;\n\tlockPointsOnClassChange = true;\n\tminLevel \t\t\t\t= 30;\n\tmaxLevel\t\t\t\t= 50;\t\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 3;\n\tbookColor\t\t\t\t= Color3.fromRGB(117, 37, 100);\n\t\n\tbookBackgroundImage\t\t= \"rbxassetid://4149116180\";\n\tthumbnail = \"rbxassetid://3559733966\";\n\t\n\tabilities = {\n\t\t{\n\t\t\tid \t\t\t\t= 47;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 55;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid = 60;\n\t\t};\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/cleric.lua",
    "content": "return {\n\tname \t\t\t\t\t= \"Cleric\";\n\tdescription\t\t\t\t= \"By the light of Vesra, your friends will be healed... and your enemies extinguished\";\n\tpointsGainPerLevel \t\t= 1;\n\tstartingPoints \t\t\t= 1;\n\tlockPointsOnClassChange = true;\n\tminLevel \t\t\t\t= 30;\n\tmaxLevel\t\t\t\t= 50;\t\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 3;\n\tbookColor\t\t\t\t= Color3.fromRGB(8, 172, 167);\n\t\n\tbookBackgroundImage\t\t= \"rbxassetid://4149214333\";\n\tthumbnail = \"rbxassetid://3559734054\";\n\t\n\tabilities = {\n\t\t{\n\t\t\tid \t\t\t\t= 38;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 50;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 45;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/hunter.lua",
    "content": "return {\n\tname \t\t\t\t\t= \"Hunter\";\n\tdescription\t\t\t\t= \"Wild and free, the hunter strikes quickly and with deadly precision.\";\n\tpointsGainPerLevel \t\t= 1;\n\tstartingPoints \t\t\t= 1;\n\tlockPointsOnClassChange = true;\n\tminLevel \t\t\t\t= 10;\n\tmaxLevel\t\t\t\t= 30;\t\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 2;\n\tbookColor\t\t\t\t= Color3.fromRGB(63, 136, 57);\n\t\n\tbookBackgroundImage\t\t= \"rbxassetid://4149155579\";\n\tthumbnail = \"rbxassetid://3559733836\";\n\t\n\tabilities = {\n\t\t{\n\t\t\tid \t\t\t\t= 7;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\t\t\n\t\t{\n\t\t\tid \t\t\t\t= 6;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 15;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\t\t\n\t\t{\n\t\t\tid \t\t\t\t= 13;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\t\t\t\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/init.lua",
    "content": "--[[\n\tabilityData {}\n\t\t--> identifying information <--\n\t\tint abilityId\n\t\t\n\t\t--> generic information <--\n\t\tstring name\n\t\tstring image\n\t\tstring description\n\t\t\n\t\t--> execution information <--\n\t\tfunction \texecute\n\t\tnumber \t\tmaxRank\n\t\tnumber \t\tcooldown\n\t\tnumber \t\tcost\n\t\ttable \t\tprerequisite\n\t\n\tprerequisiteData {}\n\t\tnumber abilityId\n\t\tnumber points\n--]]\n\nlocal lookupTable = {} do\n\tfor i, abilityBookModule in pairs(script:GetChildren()) do\n\t\tlocal abilityBookData = require(abilityBookModule)\n\t\t\n\t\t-- internal stuff\n\t\tabilityBookData.module = abilityBookModule\n\t\t\n\t\t-- hook ups\n\t\tlookupTable[string.lower(abilityBookData.name)] = abilityBookData\n\t\tlookupTable[string.upper(abilityBookData.name):sub(1, 1) .. abilityBookData.name:sub(2)] = abilityBookData\n\tend\nend\n\nreturn lookupTable"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/knight.lua",
    "content": "return {\n\tname \t\t\t\t\t= \"Knight\";\n\tdescription\t\t\t\t= \"Let them try and strike you. Your shield shall protect you, and they shall perish.\";\n\tpointsGainPerLevel \t\t= 1;\n\tstartingPoints \t\t\t= 1;\n\tlockPointsOnClassChange = true;\n\tminLevel \t\t\t\t= 30;\n\tmaxLevel\t\t\t\t= 50;\t\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 3;\n\tbookColor\t\t\t\t= Color3.fromRGB(130, 25, 26);\n\t\n\tbookBackgroundImage\t\t= \"rbxassetid://4149116051\";\n\tthumbnail = \"rbxassetid://3559733966\";\n\t\n\tabilities = {\n\t\t{\n\t\t\tid \t\t\t\t= 46;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid = 58,\n\t\t},\n\t\t{\n\t\t\tid = 61,\n\t\t},\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/mage.lua",
    "content": "return {\n\tname \t\t\t\t\t= \"Mage\";\n\tdescription\t\t\t\t= \"An old tome of basic mana techniques for the apprentice mage.\";\t\n\tpointsGainPerLevel \t\t= 1;\n\tstartingPoints \t\t\t= 1;\n\tlockPointsOnClassChange = true;\n\tminLevel \t\t\t\t= 10;\n\tmaxLevel\t\t\t\t= 30;\t\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 2;\n\tbookColor\t\t\t\t= Color3.fromRGB(11, 110, 130);\n\t\n\tbookBackgroundImage\t\t= \"rbxassetid://4149155849\";\n\tthumbnail = \"rbxassetid://3559734054\";\n\t\n\tabilities = {\n\t\t{\n\t\t\tid \t\t\t\t= 4;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 9;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 12;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/paladin.lua",
    "content": "return {\n\tname \t\t\t\t\t= \"Paladin\";\n\tdescription\t\t\t\t= \"Gaze upon the light and disintegrate evil in it's warm embrace.\";\t\t\n\tpointsGainPerLevel \t\t= 1;\n\tstartingPoints \t\t\t= 1;\n\tlockPointsOnClassChange = true;\n\tminLevel \t\t\t\t= 30;\n\tmaxLevel\t\t\t\t= 50;\t\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 3;\n\tbookColor\t\t\t\t= Color3.fromRGB(153, 74, 6);\n\t\n\tbookBackgroundImage\t\t= \"rbxassetid://4149136281\";\n\tthumbnail = \"rbxassetid://3559733966\";\n\t\n\tabilities = {\n\t\t{\n\t\t\tid = 48,\n\t\t\tprerequisiteId = nil\n\t\t},\n\t\t{\n\t\t\tid \t\t\t\t= 56;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 54;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\t\t\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/ranger.lua",
    "content": "return {\n\tname \t\t\t\t\t= \"Ranger\";\n\tdescription\t\t\t\t= \"Fear the opponent that can tear you to pieces before you've even spotted them.\";\t\n\tpointsGainPerLevel \t\t= 1;\n\tstartingPoints \t\t\t= 1;\n\tlockPointsOnClassChange = true;\n\tminLevel \t\t\t\t= 30;\n\tmaxLevel\t\t\t\t= 50;\t\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 3;\n\tbookColor\t\t\t\t= Color3.fromRGB(134, 136, 34);\n\t\n\tbookBackgroundImage\t\t= \"rbxassetid://4149312424\";\n\tthumbnail = \"rbxassetid://3559733836\";\n\t\n\tabilities = {\n\t\t{\n\t\t\tid \t\t\t\t= 31;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 44;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 36;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\t\t\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/shadow.lua",
    "content": "return {\n\tname \t\t\t\t\t= \"Shadow Master\";\n\tpointsGainPerLevel \t\t= 5;\n\tstartingPoints \t\t\t= 0;\n\tlockPointsOnClassChange = false;\n\tminLevel \t\t\t\t= 1;\n\tmaxLevel\t\t\t\t= 9999999999;\t\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 2;\n\tbookColor\t\t\t\t= Color3.fromRGB(63, 136, 57);\n\t\n\tbookBackgroundImage\t\t= \"rbxgameasset://emblem_hunter\";\n\t\n\tabilities = {\n\t\t{\n\t\t\tid \t\t\t\t= 23;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 24;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/sorcerer.lua",
    "content": "return {\n\tname \t\t\t\t\t= \"Sorcerer\";\n\tdescription\t\t\t\t= \"The gold-lined elemantlist tome of the sorcerer contains many types of powerful spells.\";\t\t\n\tpointsGainPerLevel \t\t= 1;\n\tstartingPoints \t\t\t= 1;\n\tlockPointsOnClassChange = true;\n\tminLevel \t\t\t\t= 30;\n\tmaxLevel\t\t\t\t= 50;\t\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 3;\n\tbookColor\t\t\t\t= Color3.fromRGB(24, 93, 166);\n\t\n\tbookBackgroundImage\t\t= \"rbxassetid://4149214600\";\n\tthumbnail = \"rbxassetid://3559734054\";\n\t\n\tabilities = {\n\t\t{\n\t\t\tid \t\t\t\t= 49;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 52;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 37;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/trickster.lua",
    "content": "return {\n\tname \t\t\t\t\t= \"Trickster\";\n\tdescription\t\t\t\t= \"Some fear the unknown and revile what they cannot understand. Others embrace it.\";\t\t\n\tpointsGainPerLevel \t\t= 1;\n\tstartingPoints \t\t\t= 1;\n\tlockPointsOnClassChange = true;\n\tminLevel \t\t\t\t= 30;\n\tmaxLevel\t\t\t\t= 50;\t\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 3;\n\tbookColor\t\t\t\t= Color3.fromRGB(14, 141, 5);\n\t\n\tbookBackgroundImage\t\t= \"rbxassetid://4149257161\";\n\tthumbnail = \"rbxassetid://3559733836\";\n\t\n\tabilities = {\n\t\t{\n\t\t\tid \t\t\t\t= 42;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 41;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 51;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\t\t\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/warlock.lua",
    "content": "return {\n\tname \t\t\t\t\t= \"Warlock\";\n\tdescription\t\t\t\t= \"An ancient tome of powerful cursed magic. But at what cost?\";\t\t\n\tpointsGainPerLevel \t\t= 1;\n\tstartingPoints \t\t\t= 1;\n\tlockPointsOnClassChange = true;\n\tminLevel \t\t\t\t= 30;\n\tmaxLevel\t\t\t\t= 50;\t\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 3;\n\tbookColor\t\t\t\t= Color3.fromRGB(76, 9, 153);\n\t\n\tbookBackgroundImage\t\t= \"rbxassetid://4149214475\";\n\tthumbnail = \"rbxassetid://3559734054\";\n\t\n\tabilities = {\n\t\t{\n\t\t\tid \t\t\t\t= 39;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 40;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid = 57,\n\t\t\tprerequisiteId = nil\n\t\t},\n\t\t{\n\t\t\tid = 59,\n\t\t},\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityBookLookup/warrior.lua",
    "content": "return {\n\tname \t\t\t\t\t= \"Warrior\";\n\tdescription\t\t\t\t= \"Formidable abilities taught to new Warrior recruits.\";\t\n\tpointsGainPerLevel \t\t= 1;\n\tstartingPoints \t\t\t= 1;\n\tlockPointsOnClassChange = true;\n\tminLevel \t\t\t\t= 10;\n\tmaxLevel\t\t\t\t= 30;\t\n\t\n\t-- visual attributes\n\tlayoutOrder\t\t\t\t= 2;\n\tbookColor\t\t\t\t= Color3.fromRGB(130, 59, 60);\n\t\n\tbookBackgroundImage\t\t= \"rbxassetid://4149155716\";\n\tthumbnail = \"rbxassetid://3559733966\";\n\t\n\tabilities = {\n\t\t{\n\t\t\tid \t\t\t\t= 30;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\t\t\n\t\t{\n\t\t\tid \t\t\t\t= 5;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 8;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 26;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\n\t\t{\n\t\t\tid \t\t\t\t= 17;\n\t\t\tprerequisiteId \t= nil;\n\t\t};\t\t\n\t\t\t\t\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/abilityLookup/groundSlam.lua",
    "content": "--Modules\nlocal Modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = Modules.load(\"network\")\nlocal Effects = Modules.load(\"effects\")\nlocal Tween = Modules.load(\"tween\")\nlocal Damage = Modules.load(\"damage\")\nlocal Detection = Modules.load(\"detection\")\nlocal PlaceSetup = Modules.load(\"placeSetup\")\n\n--Ability Assets\nlocal replicatedStorage = game.ReplicatedStorage\nlocal abilityAnims = replicatedStorage.assets.abilityAnimations\nlocal abilitySounds = replicatedStorage.assets.abilities[script.Name].sounds\nlocal abilityEffects = replicatedStorage.assets.abilities[script.Name].effects\n\n--Ability Base Data\nlocal abilityData = {\n\t--> Identifying Information <--\n\tid = 2;\n\n\t--> Generic Information <--\n\tname = \"Ground Slam\";\n\timage = \"rbxassetid://3736598447\";\n\tdescription = \"AOE Damage Ability\";\n\n\t--> Misc Information <--\n\tanimationName = {\"cast\", \"prayer\"};\n\twindupTime = 0.5;\n\tspeedMulti = 1.5;\n\n\t--> Execution Data <--\n\texecutionData = {\n\t\tlevel = 1;\n\t\tmaxLevel = 5;\n\t};\n\n\t--> Ability Stats <--\n\tstatistics = {\n\t\tdamage = 25;\n\t\tradius = 15;\n\n\t\tmanaCost = 10;\n\t\tcooldown = 15;\n\n\t\tincreasingStat = \"radius\";\n\t\tincreaseExponent = 0.2;\n\t};\n\n\tprerequisites = {\n\t\tplayerLevel = 1;\n\n\t\tclassRestricted = false;\n\t\tdeveloperOnly = false;\n\n\t\tabilities = {};\n\t};\n}\n\n--Client Execute Function\nfunction abilityData:execute(abilityExecutionData, isAbilitySource)\n\tlocal character = abilityExecutionData.casterCharacter\n\tlocal renderCharacterContainer = network:invoke(\"getRenderCharacterContainerByEntityManifest\", character.PrimaryPart)\n\n\tif not renderCharacterContainer or not renderCharacterContainer.PrimaryPart then return false end\n\n\tlocal currentlyEquipped = network:invoke(\"getCurrentlyEquippedForRenderCharacter\", renderCharacterContainer.entity)\n\tlocal currentWeaponManifest = currentlyEquipped[\"1\"] and currentlyEquipped[\"1\"].manifest\n\n\tif not currentWeaponManifest then return end\n\n\tlocal animTrack = renderCharacterContainer.entity.AnimationController:LoadAnimation(abilityAnims.warrior_forwardDownslash)\n\tlocal trail\n\n\tlocal sound = abilitySounds.cast:Clone()\n\tsound.Parent = currentWeaponManifest\n\tsound:Play()\n\tgame.Debris:AddItem(sound, 5)\n\n\tlocal attach0 = currentWeaponManifest:FindFirstChild(\"bottomAttachment\")\n\tlocal attach1 = currentWeaponManifest:FindFirstChild(\"topAttachment\")\n\n\tif attach0 and attach1 then\n\t\ttrail = abilityEffects.Trail:Clone()\n\t\ttrail.Name = \"groundSlamTrail\"\n\t\ttrail.Attachment0 = attach0\n\t\ttrail.Attachment1 = attach1\n\t\ttrail.Parent = currentWeaponManifest\n\t\ttrail.Enabled = true\n\tend\n\n\tanimTrack:Play(0.1, 1, self.speedMulti or 1)\n\twait(animTrack.Length * 0.06 / animTrack.Speed)\n\n\tif isAbilitySource then\n\t\tlocal movementVelocity = network:invoke(\"getMovementVelocity\")\n\t\tlocal movementDirection\n\n\t\tif movementVelocity.magnitude > 0 then\n\t\t\tmovementDirection = movementVelocity.unit\n\t\telse\n\t\t\tmovementDirection = character.PrimaryPart.CFrame.lookVector * 0.05\n\t\tend\n\n\t\tnetwork:invoke(\"setCharacterArrested\", true)\n\n\t\tlocal bodyGyro = character.PrimaryPart.hitboxGyro\n\t\tlocal bodyVelocity = character.PrimaryPart.hitboxVelocity\n\n\t\tbodyGyro.CFrame = CFrame.new(Vector3.new(), movementDirection)\n\t\tmovementDirection = movementDirection + Vector3.new(0, 1, 0)\n\n\t\tnetwork:invoke(\"setMovementVelocity\", movementDirection * 23 * animTrack.Speed)\n\tend\n\n\twait(animTrack.Length * 0.36 / animTrack.Speed)\n\n\tlocal timeLeft = 30\n\tlocal startTime = tick()\n\tlocal hitPart, hitPosition, hitNormal\n\n\tanimTrack:AdjustSpeed(0)\n\n\tif isAbilitySource then\n\t\tnetwork:invoke(\"setMovementVelocity\", Vector3.new())\n\t\tnetwork:invoke(\"setCharacterArrested\", false)\n\tend\n\n\trepeat\n\t\tlocal ray = Ray.new(currentWeaponManifest.Position + (renderCharacterContainer.PrimaryPart.CFrame.lookVector * 3) + Vector3.new(0, 2.5, 0), Vector3.new(0, -10, 0))\n\t\thitPart, hitPosition, hitNormal = workspace:FindPartOnRayWithIgnoreList(ray, {renderCharacterContainer, currentWeaponManifest})\n\t\twait()\n\tuntil hitPart or (tick() - startTime) >= timeLeft\n\n\tif trail then\n\t\ttrail:Destroy()\n\tend\n\n\tif isAbilitySource then\n\t\tnetwork:invoke(\"setCharacterArrested\", true)\n\tend\n\n\tanimTrack:AdjustSpeed(self.speedMulti)\n\n\tspawn(function()\n\t\tif not hitPart then return false end\n\n\t\tlocal shockwave1 = abilityEffects.shockwaveEntity:Clone()\n\t\tlocal shockwave2 = abilityEffects.shockwaveEntity:Clone()\n\n\t\tshockwave1.Parent = entitiesFolder\n\t\tshockwave2.Parent = entitiesFolder\n\n\t\tlocal radius = abilityExecutionData.abilityData[\"statistics\"].radius\n\t\tlocal dustPart = abilityEffects.dustPart:Clone()\n\t\tdustPart.Parent = workspace.CurrentCamera\n\t\tdustPart.CFrame = CFrame.new(hitPosition)\n\t\tdustPart.Dust.Speed = NumberRange.new(30 * (radius/10), 50 * (radius/10))\n\t\tdustPart.Dust:Emit(100)\n\t\tgame.Debris:AddItem(dustPart,6)\n\n\t\tlocal sound = abilitySounds.impact:Clone()\n\t\tsound.Parent = dustPart\n\t\tsound:Play()\n\n\t\tif isAbilitySource then\n\t\t\tfor i, v in pairs(Damage.getDamagableTargets(game.Players.LocalPlayer)) do\n\t\t\t\tlocal targetPosition = Detection.projection_Box(v.CFrame, v.Size, hitPosition)\n\t\t\t\tif ((targetPosition - hitPosition) * Vector3.new(1,0,1)).magnitude <= radius * 0.7 and ((targetPosition - hitPosition) * Vector3.new(0,1,0)).magnitude <= (radius/2) * 0.7 then\n\t\t\t\t\tnetwork:fire(\"requestEntityDamageDealt\", v, hitPosition, \"ability\", self.id, \"shockwave\", abilityExecutionData.abilityGuid)\n\t\t\t\t\t--self:doKnockback(abilityExecutionData, v, hitPosition, getKnockbackAmount(abilityExecutionData))\n\t\t\t\telseif ((targetPosition - hitPosition) * Vector3.new(1,0,1)).magnitude <= radius and ((targetPosition - hitPosition) * Vector3.new(0,1,0)).magnitude <= (radius/2) then\n\t\t\t\t\tnetwork:fire(\"requestEntityDamageDealt\", v, hitPosition, \"ability\", self.id, \"shockwave-outer\", abilityExecutionData.abilityGuid)\n\t\t\t\t\t--self:doKnockback(abilityExecutionData, v, hitPosition, getKnockbackAmount(abilityExecutionData))\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tlocal cf = CFrame.new(hitPosition, hitPosition + hitNormal) * CFrame.Angles(math.pi / 2, 0, 0)\n\t\tlocal multi = Vector3.new(radius * 1.7, -1.1, radius * 1.7)\n\t\tlocal base = Vector3.new(0.25, 1.5, 0.25)\n\t\tlocal duration = 1\n\n\t\tshockwave1.Size = base\n\t\tshockwave2.Size = base\n\t\tshockwave1.CFrame = cf\n\t\tshockwave2.CFrame = cf\n\t\tshockwave1.Transparency = 0\n\t\tshockwave2.Transparency = 0\n\n\t\tTween(shockwave1, {\"Size\", \"Transparency\"}, {base + multi, 1}, duration, Enum.EasingStyle.Quad)\n\t\tTween(shockwave2, {\"Size\", \"Transparency\"}, {base + multi, 1}, duration, Enum.EasingStyle.Quint)\n\n\t\tgame.Debris:AddItem(shockwave1, 1)\n\t\tgame.Debris:AddItem(shockwave2, 1)\n\tend)\n\n\tanimTrack:AdjustSpeed(self.speedMulti * 1.65)\n\twait(animTrack.Length * 0.4 / animTrack.Speed)\n\n\tif isAbilitySource then\n\t\tnetwork:invoke(\"setCharacterArrested\", false)\n\tend\n\n\treturn true, self.statistics.cooldown\nend\n\n--Server Execute Function\nfunction abilityData:execute_server(castPlayer, abilityExecutionData, isAbilitySource)\n\nend\n\nreturn abilityData"
  },
  {
    "path": "src/ReplicatedStorage/abilityLookup/init.lua",
    "content": "local lookupTable = {}\nlocal registerIds = {}\n\nfor i, abilityDataModule in pairs(script:GetChildren()) do\n\tlocal abilityData = require(abilityDataModule)\n\n\tlookupTable[abilityData.id] = abilityData\n\tlookupTable[abilityDataModule.Name] = abilityData\n\t\n\tif not registerIds[abilityData.id] then\n\t\tregisterIds[abilityData.id] = true\n\telse\n\t\twarn(\"@@@ ABILITY ID OVERLAP --\", abilityData.id, abilityData.name, abilityDataModule.Name)\n\tend\nend\n\nfor i = 1, #script:GetChildren() do\n\tif not registerIds[i] then\n\t\twarn(\"@@@ ABILITY ID NOT TAKEN\", i)\n\tend\nend\n\n\t\nfunction lookupTable:GetAbilityIds()\n\treturn registerIds\nend\n\nreturn lookupTable"
  },
  {
    "path": "src/ReplicatedStorage/abilityLookup/regeneration.lua",
    "content": "--Modules\nlocal Modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = Modules.load(\"network\")\nlocal Effects = Modules.load(\"effects\")\nlocal Tween = Modules.load(\"tween\")\nlocal PlaceSetup = Modules.load(\"placeSetup\")\n\n--Ability Assets\nlocal replicatedStorage = game.ReplicatedStorage\nlocal abilityAnims = replicatedStorage.assets.abilityAnimations\nlocal abilitySounds = replicatedStorage.assets.abilities[script.Name].sounds\nlocal abilityEffects = replicatedStorage.assets.abilities[script.Name].effects\n\n--Ability Base Data\nlocal abilityData = {\n\t--> Identifying Information <--\n\tid = 1;\n\n\t--> Generic Information <--\n\tname = \"Regeneration\";\n\timage = \"rbxassetid://2528901754\";\n\tdescription = \"Regenerates all players health who are within a certain radius of cast\";\n\n\t--> Misc Information <--\n\tanimationName = {\"cast\", \"prayer\"};\n\twindupTime = 1;\n\n\t--> Execution Data <--\n\texecutionData = {\n\t\tlevel = 1;\n\t\tmaxLevel = 15;\n\t};\n\n\t--> Ability Stats <--\n\tstatistics = {\n\t\thealing = 5;\n\t\tradius = 10;\n\n\t\tmanaCost = 5;\n\t\tcooldown = 10;\n\n\t\tincreasingStat = \"healing\";\n\t\tincreaseExponent = 0.2;\n\t};\n\n\tprerequisites = {\n\t\tplayerLevel = 1;\n\n\t\tclassRestricted = false;\n\t\tdeveloperOnly = false;\n\n\t\tabilities = {};\n\t};\n}\n\n--Client Execute Function\nfunction abilityData:execute(abilityExecutionData, isAbilitySource)\n\tlocal character = abilityExecutionData.casterCharacter\n\tlocal renderCharacterContainer = network:invoke(\"getRenderCharacterContainerByEntityManifest\", character.PrimaryPart)\n\tlocal abilityGuid = abilityExecutionData.abilityGuid\n\n\tif not renderCharacterContainer or not abilityExecutionData or not abilityGuid then return false end\n\tif not renderCharacterContainer.PrimaryPart then return false end\n\n\tlocal root = renderCharacterContainer.PrimaryPart\n\tlocal playerData = network:invoke(\"getLocalPlayerDataCache\")\n\tlocal showWeapons = Effects.hideWeapons(renderCharacterContainer.entity)\n\n\t--If casting player is source then freeze player during ability\n\tif isAbilitySource then\n\t\tnetwork:invoke(\"setCharacterArrested\", true)\n\n\t\tdelay(self.windupTime, function()\n\t\t\tnetwork:invoke(\"setCharacterArrested\", false)\n\t\tend)\n\tend\n\n\t--Animation Here\n\n\tlocal animTrack = renderCharacterContainer.entity.AnimationController:LoadAnimation(abilityAnims.prayer)\n\tanimTrack:Play()\n\n\t--Cast Sound Here\n\tlocal castSound = abilitySounds.cast:Clone()\n\tcastSound.Parent = root\n\tcastSound:Play()\n\tgame.Debris:AddItem(castSound, castSound.TimeLength)\n\n\twait(self.windupTime)\n\n\tanimTrack:Stop(0.5)\n\tshowWeapons()\n\n\t--Ability Sound Here\n\tlocal abilitySound = abilitySounds.prayer:Clone()\n\tabilitySound.Parent = root\n\tabilitySound:Play()\n\tgame.Debris:AddItem(abilitySound, abilitySound.TimeLength)\n\n\t--Define Ability Statistics\n\tlocal radius = abilityExecutionData.abilityData[\"statistics\"][\"radius\"]\n\tlocal diameter = radius * 2\n\n\tlocal ring = abilityEffects.ring:Clone()\n\tring.CFrame = root.CFrame * CFrame.new(0, -2, 0)\n\tring.Parent = PlaceSetup.awaitPlaceFolder(\"entities\")\n\n\tTween(ring, {\"Size\"}, Vector3.new(diameter, 2, diameter), 0.25)\n\tTween(ring, {\"Transparency\"}, 1, 1)\n\tgame.Debris:AddItem(ring, 1)\n\n\tif isAbilitySource then\n\t\tnetwork:fireServer(\"requestAbilityStateUpdate\", \"end\", abilityExecutionData)\n\tend\n\n\treturn true, self.statistics.cooldown\nend\n\n--Server Execute Function\nfunction abilityData:execute_server(castPlayer, abilityExecutionData, isAbilitySource)\n\nend\n\nreturn abilityData"
  },
  {
    "path": "src/ReplicatedStorage/accessoryLookup.lua",
    "content": "local module = {}\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/beam-stuf.lua",
    "content": "local attach0 = Instance.new(\"Attachment\", game.Workspace.Terrain);\nlocal attach1 = Instance.new(\"Attachment\", game.Workspace.Terrain);\n\nlocal beam = Instance.new(\"Beam\", game.Workspace.Terrain);\nbeam.Attachment0 = attach0;\nbeam.Attachment1 = attach1;\n\n-- credits to: EgoMoose\nlocal function beamProjectile(g, v0, x0, t1)\n\t\n\t-- calculate the bezier points\n\tlocal c \t= 0.5*0.5*0.5\n\tlocal p3 \t= 0.5*g*t1*t1 + v0*t1 + x0\n\tlocal p2 \t= p3 - (g*t1*t1 + v0*t1)/3\n\tlocal p1 \t= (c*g*t1*t1 + 0.5*v0*t1 + x0 - c*(x0+p3))/(3*c) - p2\n\t\n\t-- the curve sizes\n\tlocal curve0 = (p1 - x0).magnitude\n\tlocal curve1 = (p2 - p3).magnitude\n\t\n\t-- build the world CFrames for the attachments\n\tlocal b \t= (x0 - p3).unit\n\tlocal r1 \t= (p1 - x0).unit\n\tlocal u1 \t= r1:Cross(b).unit\n\tlocal r2 \t= (p2 - p3).unit\n\tlocal u2 \t= r2:Cross(b).unit\n\tb \t\t\t= u1:Cross(r1).unit\n\t\n\tlocal cf1 = CFrame.new(\n\t\tx0.x, x0.y, x0.z,\n\t\tr1.x, u1.x, b.x,\n\t\tr1.y, u1.y, b.y,\n\t\tr1.z, u1.z, b.z\n\t)\n\t\n\tlocal cf2 = CFrame.new(\n\t\tp3.x, p3.y, p3.z,\n\t\tr2.x, u2.x, b.x,\n\t\tr2.y, u2.y, b.y,\n\t\tr2.z, u2.z, b.z\n\t)\n\t\n\treturn curve0, -curve1, cf1, cf2\nend\n\nlocal x0 \t= Vector3.new(0, 50, 0)\nlocal v0 \t= Vector3.new(10, 25, 0)\nlocal g  \t= Vector3.new(0, -10, 0)\nlocal t \t= 1\n\nlocal p = Instance.new(\"Part\", workspace)\np.Anchored = true\np.CFrame = CFrame.new(x0, x0+v0)\np.Size = Vector3.new(1, 1, 1)\np.FrontSurface = Enum.SurfaceType.Hinge\n\nlocal curve0, curve1, cf1, cf2 = beamProjectile(g, v0, x0, t)\n\nbeam.CurveSize0 = curve0\nbeam.CurveSize1 = curve1\n\n-- convert world space CFrames to be relative to the attachment parent\nattach0.CFrame = attach0.Parent.CFrame:inverse() * cf1\nattach1.CFrame = attach1.Parent.CFrame:inverse() * cf2"
  },
  {
    "path": "src/ReplicatedStorage/blessingLookup.lua",
    "content": "--[[\n\tblessingData {}\n\t\t--> identifying information <--\n\t\tint blessingId\n\t\t\n\t\t--> generic information <--\n\t\tstring name\n\t\tstring image\n\t\tstring description\n\t\t\n\t\t--> execution information <--\n\t\t\n--]]\n\nlocal lookupTable = {} do\n\tfor i, blessingDataModule in pairs(script:GetChildren()) do\n\t\tlocal blessingData = require(blessingDataModule)\n\t\t\n\t\t-- internal stuff\n\t\tblessingData.module = blessingDataModule\n\t\t\n\t\t-- hook ups\n\t\tlookupTable[blessingData.id] \t\t\t= blessingData\n\t\tlookupTable[blessingDataModule.Name] \t= blessingData\n\tend\nend\n\nreturn lookupTable"
  },
  {
    "path": "src/ReplicatedStorage/defaultCharacterAppearance.lua",
    "content": "local characterAppearanceData = {}\n\tcharacterAppearanceData.equipment \t= {}\n\tcharacterAppearanceData.accessories = {}\n\t\tcharacterAppearanceData.accessories.face \t\t= 1\n\t\tcharacterAppearanceData.accessories.hair \t\t= 3\n\t\tcharacterAppearanceData.accessories.undershirt \t= 1\n\t\tcharacterAppearanceData.accessories.underwear \t= 1\n\t\tcharacterAppearanceData.accessories.hairColorId = 1\n\t\tcharacterAppearanceData.accessories.skinColorId = 1\n\t\t\nreturn characterAppearanceData"
  },
  {
    "path": "src/ReplicatedStorage/defaultMonsterState.lua",
    "content": "local replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal pathfinding \t= modules.load(\"pathfinding\")\n\t\tlocal utilities\t\t= modules.load(\"utilities\")\n\t\tlocal detection\t\t= modules.load(\"detection\")\n\t\tlocal network\t\t= modules.load(\"network\")\n\nlocal MONSTER_END_POSITION_ALPHA = 1\n\nlocal rand = Random.new()\n\nreturn {\n\tprocessDamageRequest = function(sourceId, baseDamage)\n\t\treturn baseDamage, \"physical\", \"direct\"\n\tend;\n\n\tgetClosestEntities = function(monster)\n\t\tlocal pool = utilities.getEntities()\n\n\t\tfor i = #pool, 1, -1 do\n\t\t\tlocal v = pool[i]\n\n\t\t\tif v.Name == monster.monsterName or v == monster.manifest or v.health.Value <= 0 then\n\t\t\t\ttable.remove(pool, i)\n\t\t\tend\n\t\tend\n\n\t\treturn pool\n\tend;\n\n\tdefault = \"idling\",\n\tstates \t= {\n\t\t[\"setup\"] = {\n\t\t\tanimationEquivalent = \"idling\";\n\t\t\ttransitionLevel = 0;\n\t\t\tstep \t\t\t= function(monster)\n\t\t\t\tif monster.moveGoal then\n\t\t\t\t\tmonster.__directRoamGoal = monster.moveGoal\n\t\t\t\t\tmonster.__directRoamOrigin = monster.manifest.Position\n\t\t\t\t\tmonster.__blockConfidence = 0\n\t\t\t\t\tmonster.__LAST_ROAM_TIME = tick()\n\t\t\t\t\treturn \"direct-roam\"\n\t\t\t\tend\n\t\t\tend;\n\t\t};\n\t\t[\"sleeping\"] = {\n\t\t\tanimationEquivalent = \"idling\";\n\t\t\ttimeBetweenUpdates \t= 5;\n\t\t\ttransitionLevel \t= 1;\n\t\t\tstep \t\t\t\t= function(monster, canSwitchState)\n\t\t\t\tif monster.closestEntity then\n\t\t\t\t\treturn \"idling\"\n\t\t\t\tend\n\t\t\tend;\n\t\t}; [\"idling\"] = {\n\t\t\tlockTimeForLowerTransition \t= 3;\n\t\t\ttransitionLevel \t\t\t= 2;\n\t\t\tstep \t\t\t\t\t\t= function(monster, canSwitchState)\n\n\t\t\t\tmonster.manifest.BodyVelocity.Velocity = Vector3.new()\n\n\t\t\t\tif monster.moveGoal then\n\t\t\t\t\tmonster.__directRoamGoal = monster.moveGoal\n\t\t\t\t\tmonster.__directRoamOrigin = monster.manifest.Position\n\t\t\t\t\tmonster.__blockConfidence = 0\n\t\t\t\t\tmonster.__LAST_ROAM_TIME = tick()\n\t\t\t\t\treturn \"direct-roam\"\n\t\t\t\tend\n\n\t\t\t\tif monster.targetEntity then\n\t\t\t\t\treturn \"following\"\n\t\t\t\tend\n\n\t\t\t\tif monster.closestEntity and monster.closestEntity.health.value >= 0 then\n\t\t\t\t\tlocal closestEntityDistance = utilities.magnitude(monster.closestEntity.Position - monster.manifest.Position)\n\t\t\t\t\tlocal aggressionLockRange = monster.aggressionLockRange or monster.aggressionRange\n\n\t\t\t\t\tif closestEntityDistance <= aggressionLockRange and monster:isTargetEntityInLineOfSight(aggressionLockRange, true) then\n\t\t\t\t\t\tmonster.__providedDirectRoamTheta = nil\n\t\t\t\t\t\tmonster:setTargetEntity(monster.closestEntity)\n\n\t\t\t\t\t\treturn \"following\"\n\t\t\t\t\telse\n\t\t\t\t\t\tif canSwitchState or monster.__providedDirectRoamTheta then\n\t\t\t\t\t\t\tif (monster.__LAST_ROAM_TIME and tick() - monster.__LAST_ROAM_TIME < 5) and (monster.__providedDirectRoamTheta == nil) then\n\t\t\t\t\t\t\t\treturn \"idling\"\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tlocal XZ = Vector3.new(1, 0, 1)\n\n--\t\t\t\t\t\t\tlocal targetPosition = monster:getRoamPositionInSpawnRegion()\n\n\t\t\t\t\t\t\tlocal theta = rand:NextInteger(1,360)\n\n\t\t\t\t\t\t\tif monster.__providedDirectRoamTheta then\n\t\t\t\t\t\t\t\ttheta = monster.__providedDirectRoamTheta\n\t\t\t\t\t\t\t\tmonster.__providedDirectRoamTheta = nil\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tlocal rad = math.rad(theta)\n\n\t\t\t\t\t\t\tlocal monsterPos = monster.manifest.Position * XZ\n\n\t\t\t\t\t\t\tlocal targetPosition = monsterPos + Vector3.new(math.cos(rad), 0, math.sin(rad)) * rand:NextInteger(10, monster.maxRoamDistance or 50)\n\n\t\t\t\t\t\t\tlocal direction = targetPosition - monsterPos\n\n\t\t\t\t\t\t\tlocal startPos = monster.manifest.Position + Vector3.new(0, monster.manifest.Size.Y / 2, 0)\n\t\t\t\t\t\t\tlocal finalDirection = direction - Vector3.new(0, monster.manifest.Size.Y / 2, 0)\n\n\t\t\t\t\t\t\tlocal rey = Ray.new(startPos, finalDirection)\n\n\t\t\t\t\t\t\tlocal hitPart, hitPosition = workspace:FindPartOnRayWithIgnoreList(rey,{monster.manifest,workspace.placeFolders:FindFirstChild(\"entityRenderCollection\"),workspace.placeFolders:FindFirstChild(\"foilage\")})\n\n\t\t\t\t\t\t\tlocal monsterSize = (monster.manifest.Size.X + monster.manifest.Size.Z) / 2\n\n\t\t\t\t\t\t\tlocal distance = (hitPosition - monster.manifest.Position).magnitude\n\n\t\t\t\t\t\t\t-- cap the distance\n\t\t\t\t\t\t\t--[[\n\t\t\t\t\t\t\tif distance > (monster.maxRoamDistance or 50) then\n\t\t\t\t\t\t\t\thitPosition = startPos + finalDirection * 50\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t]]\n\n\t\t\t\t\t\t\tlocal canRoam = true\n\n\t\t\t\t\t\t\tlocal directRoamGoal = (hitPosition - direction * monsterSize/2) * XZ\n\n\t\t\t\t\t\t\tif distance >= monsterSize and canRoam then\n\t\t\t\t\t\t\t\tmonster.__directRoamGoal = directRoamGoal\n\t\t\t\t\t\t\t\tmonster.__directRoamOrigin = monsterPos\n\t\t\t\t\t\t\t\tmonster.__directRoamTheta = theta\n\t\t\t\t\t\t\t\tmonster.__blockConfidence = 0\n\t\t\t\t\t\t\t\tmonster.__LAST_ROAM_TIME = tick()\n\t\t\t\t\t\t\t\treturn \"direct-roam\"\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tmonster.__LAST_ROAM_TIME = tick() - 4.5\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\treturn \"sleeping\"\n\t\t\t\tend\n\t\t\tend;\n\t\t}; [\"wait-roaming\"] = {\n\t\t\tanimationEquivalent = \"idling\";\n\t\t\ttransitionLevel \t= 3;\n\t\t\tstep \t\t\t\t= function(monster, canSwitchState)\n\t\t\t\tif not monster.__IS_WAITING_FOR_PATH_FINDING then\n\t\t\t\t\tif monster.isProcessingPath then\n\t\t\t\t\t\treturn \"roaming\"\n\t\t\t\t\telse\n\t\t\t\t\t\tmonster:setTargetEntity(nil, nil)\n\t\t\t\t\t\treturn \"idling\"\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\t--ber edit: end wait-roaming state and cancel pathfind if its been longer than 5 seconds\n\t\t\t\t--i think getting stuck in the wait-roaming step is what leads monsters getting frozen\n\t\t\t\tif monster.__PATHFIND_QUEUE_TIME and tick() - monster.__PATHFIND_QUEUE_TIME > 5 then\n\t\t\t\t\tmonster:resetPathfinding()\n\n\t\t\t\t\treturn \"idling\"\n\t\t\t\tend\n\t\t\tend\n\n\t\t}; [\"direct-roam\"] = {\n\t\t\tanimationEquivalent = \"walking\";\n\t\t\ttransitionLevel \t= 3;\n\t\t\tstep \t\t\t\t= function(monster, canSwitchState)\n\n\t\t\t\tif monster.moveGoal then\n\t\t\t\t\tmonster.moveGoal = nil\n\t\t\t\t\t-- ignore block confidence and other checks for normal roaming\n\t\t\t\t\tmonster.__strictMovement = true\n\t\t\t\tend\n\n\t\t\t\tif monster.targetEntity then\n\t\t\t\t\treturn \"following\"\n\t\t\t\tend\n\n\t\t\t\tif monster.closestEntity and canSwitchState then\n\t\t\t\t\tlocal closestEntityDistance = utilities.magnitude(monster.closestEntity.Position - monster.manifest.Position)\n\t\t\t\t\tlocal aggressionLockRange = monster.aggressionLockRange or monster.aggressionRange\n\n\n\t\t\t\t\tif closestEntityDistance <= aggressionLockRange and monster:isTargetEntityInLineOfSight(aggressionLockRange, true) then\n\n\t\t\t\t\t\tmonster:setTargetEntity(monster.closestEntity, monster.closestEntity)\n\t\t\t\t\t\treturn \"following\"\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tlocal start, goal = monster.__directRoamOrigin, monster.__directRoamGoal\n\t\t\t\tif start and goal then\n\t\t\t\t\tlocal XZ = Vector3.new(1, 0, 1)\n\n\t\t\t\t\tlocal goalDistance = utilities.magnitude((goal - start) * XZ)\n\t\t\t\t\tlocal distance = utilities.magnitude((monster.manifest.Position - start) * XZ)\n\n\t\t\t\t\tlocal moveDirection = (goal * XZ - start * XZ).unit\n\n\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity = moveDirection * monster.baseSpeed\n\t\t\t\t\tmonster.manifest.BodyGyro.CFrame = CFrame.new(start * XZ, goal * XZ)\n\n\t\t\t\t\t-- arrived\n\t\t\t\t\tif distance >= goalDistance then\n\t\t\t\t\t\tmonster.__strictMovement = false\n\t\t\t\t\t\t-- give a chance to keep going in a similar direction\n\t\t\t\t\t\tif monster.__directRoamTheta and rand:NextNumber() > 0.5 then\n\t\t\t\t\t\t\tmonster.__providedDirectRoamTheta = monster.__directRoamTheta + rand:NextInteger(-35,35)\n\t\t\t\t\t\t\treturn \"idling\"\n\t\t\t\t\t\tend\n\t\t\t\t\t\tmonster.__providedDirectRoamTheta = nil\n\t\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity \t= Vector3.new()\n\t\t\t\t\t\treturn \"idling\"\n\t\t\t\t\tend\n\n\t\t\t\t\t-- blocked\n\t\t\t\t\tlocal expectedVelocity = monster.manifest.BodyVelocity.Velocity\n\t\t\t\t\tlocal actualVelocity = monster.manifest.Velocity\n\n\t\t\t\t\tlocal ray1 = Ray.new(monster.manifest.Position, Vector3.new(0,-50,0))\n\t\t\t\t\tlocal ray2 = Ray.new(monster.manifest.Position + (moveDirection * monster.baseSpeed / 4), Vector3.new(0,-50,0))\n\n\t\t\t\t\tlocal _, hitpos1 = workspace:FindPartOnRayWithIgnoreList(ray1,{monster.manifest,workspace.placeFolders:FindFirstChild(\"entityRenderCollection\"),workspace.placeFolders:FindFirstChild(\"foilage\")})\n\t\t\t\t\tlocal _, hitpos2 = workspace:FindPartOnRayWithIgnoreList(ray2,{monster.manifest,workspace.placeFolders:FindFirstChild(\"entityRenderCollection\"),workspace.placeFolders:FindFirstChild(\"foilage\")})\n\n\t\t\t\t\t-- prevent running into things or falling off cliffs\n\t\t\t\t\tlocal yDisplacement = hitpos2.Y - hitpos1.Y\n\t\t\t\t\tif not monster.__strictMovement then\n\t\t\t\t\t\tif -yDisplacement >= math.max(monster.manifest.Size.Y, 6)then\n\t\t\t\t\t\t\t-- cliff!\n\t\t\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity \t= Vector3.new()\n\t\t\t\t\t\t\treturn \"idling\"\n\t\t\t\t\t\telseif tick() - monster.__LAST_ROAM_TIME >= 1 and actualVelocity.magnitude <= expectedVelocity.magnitude / 10 then\n\t\t\t\t\t\t\t-- blocked\n\t\t\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity \t= Vector3.new()\n\t\t\t\t\t\t\treturn \"idling\"\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\treturn\n\t\t\t\tend\n\t\t\t\treturn \"idling\"\n\t\t\tend\n\t\t}; [\"roaming\"] = {\n\t\t\tanimationEquivalent = \"walking\";\n\t\t\ttransitionLevel = 3;\n\t\t\tstep \t\t\t= function(monster, canSwitchState)\n\t\t\t\tif monster.closestEntity then\n\t\t\t\t\tif not monster.path then\n\t\t\t\t\t\treturn \"idling\"\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal closestEntityDistance = utilities.magnitude(monster.closestEntity.Position - monster.manifest.Position)\n\n\t\t\t\t\tlocal nodeAt = monster.path[monster.currentNode]\n\t\t\t\t\tlocal nodeTo = monster.path[monster.currentNode + 1]\n\t\t\t\t\tif nodeAt and nodeTo then\n\t\t\t\t\t\tif pathfinding.isPastNextPathfindingNodeNode(nodeAt.Position, monster.manifest.Position, nodeTo.Position) then\n\t\t\t\t\t\t\t-- arrived at node\n\t\t\t\t\t\t\tmonster.currentNode \t\t\t= monster.currentNode + 1\n\t\t\t\t\t\t\tmonster.__PATH_FIND_NODE_START \t= tick()\n\n\t\t\t\t\t\t\tlocal aggressionLockRange = monster.aggressionLockRange or monster.aggressionRange\n\n\n\n\t\t\t\t\t\t\tif closestEntityDistance <= aggressionLockRange and monster:isTargetEntityInLineOfSight(aggressionLockRange, true) then\n\t\t\t\t\t\t\t\tmonster:resetPathfinding()\n\t\t\t\t\t\t\t\tmonster:setTargetEntity(monster.closestEntity, monster.closestEntity)\n\n\t\t\t\t\t\t\t\treturn \"following\"\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tif tick() - monster.__PATH_FIND_NODE_START < 2 then\n\t\t\t\t\t\t\t\t-- move!\n\t\t\t\t\t\t\t\tlocal adjustNodeToPosition = Vector3.new(nodeTo.Position.X, monster.manifest.Position.Y, nodeTo.Position.Z)\n\n\t\t\t\t\t\t\t\tmonster.manifest.BodyGyro.CFrame \t\t= CFrame.new(monster.manifest.Position, adjustNodeToPosition)\n\t\t\t\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity \t= (adjustNodeToPosition - monster.manifest.Position).unit * monster.baseSpeed\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t-- monster taking too long to move to node\n\t\t\t\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity = Vector3.new()\n\t\t\t\t\t\t\t\tmonster:resetPathfinding()\n\n\t\t\t\t\t\t\t\treturn \"idling\"\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\telseif nodeAt and not nodeTo then\n\t\t\t\t\t\t-- reached the end\n\n\t\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity = Vector3.new()\n\t\t\t\t\t\tmonster:resetPathfinding()\n\n\t\t\t\t\t\treturn \"idling\"\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend;\n\t\t}; [\"following\"] = {\n\t\t\tanimationEquivalent = \"walking\";\n\t\t\ttransitionLevel = 4;\n\t\t\tstep \t\t\t= function(monster, canSwitchState)\n\t\t\t\tif not monster.targetEntity then\n\t\t\t\t\treturn \"idling\"\n\t\t\t\tend\n\n\t\t\t\tif monster.targetEntity.health.Value <= 0 then\n\t\t\t\t\tmonster:setTargetEntity(nil, nil)\n\t\t\t\t\treturn \"idling\"\n\t\t\t\tend\n\n\t\t\t\tlocal manifestProjectorPosition = monster.manifest.Position\n\t\t\t\tlocal targetEntity \t\t\t\t= monster.targetEntity\n\t\t\t\tlocal hrpPosition \t\t\t\t= targetEntity.Position\n\n\t\t\t\t-- normalize y value to prevent difficulties when it comes\n\t\t\t\t-- to the player and the monster being on different elevations\n\n\t\t\t\tlocal correctHRPPosition\t\t\t= hrpPosition\n\n\t\t\t\tif monster.manifest.BodyVelocity.MaxForce.Y <= 0.1 then\n\t\t\t\t\tcorrectHRPPosition \t\t\t= Vector3.new(hrpPosition.X, manifestProjectorPosition.Y, hrpPosition.Z)\n\t\t\t\tend\n\n\t\t\t\tif monster.targetingYOffsetMulti then\n\t\t\t\t\tcorrectHRPPosition = correctHRPPosition + Vector3.new(0, monster.manifest.Size.Y * monster.targetingYOffsetMulti, 0)\n\t\t\t\tend\n\n\t\t\t\t-- account for velocity\n\t\t\t\tcorrectHRPPosition = correctHRPPosition + targetEntity.Velocity * 0.1\n\n\n\t\t\t\tlocal targetEntityDistance \t\t\t= utilities.magnitude(manifestProjectorPosition - correctHRPPosition)\n\t\t\t\tlocal moveDirection \t\t\t\t= (correctHRPPosition - manifestProjectorPosition).unit\n\t\t\t\tlocal positionJustInsideAttackRange = correctHRPPosition - moveDirection * (monster.attackRange)\n\n\t\t\t\t--monster.DEBUG_TARGET_PART.CFrame = CFrame.new(positionJustInsideAttackRange)\n\t\t\t\tlocal movingSpeed = monster.followSpeed or monster.baseSpeed\n\t\t\t\t-- have monster move in direction of player\n\t\t\t\t-- but respect step to make this movement\n\t\t\t\t-- framerate independent\n\t\t\t\t-- CFrame.new(manifestProjectorPosition, positionJustInsideAttackRange) * CFrame.new(0, 0, -monster.baseSpeed * (step + (tick() - currTime)))\n\t\t\t\tif monster:isTargetEntityInLineOfSight(monster.targetEntitySetSource and 999, not monster.targetEntitySetSource) then\n\t\t\t\t\t-- player is still in line of sight of monster\n\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity \t= moveDirection * movingSpeed\n\t\t\t\t\tmonster.manifest.BodyGyro.CFrame \t\t= CFrame.new(manifestProjectorPosition, Vector3.new(correctHRPPosition.X, manifestProjectorPosition.Y, correctHRPPosition.Z))   --CFrame.new(manifestProjectorPosition, correctHRPPosition)\n\t\t\t\t\tmonster.__LAST_POSITION_SEEN \t\t\t= positionJustInsideAttackRange\n\t\t\t\t\tmonster.__LAST_MOVE_DIRECTION \t\t\t= moveDirection * movingSpeed\n\t\t\t\telse\n\t\t\t\t\tif not monster.__LAST_POSITION_SEEN then\n\t\t\t\t\t\t-- player is not in line of sight, but is still within aggro range -- pathfind!\n\n\t\t\t\t\t\t-- ber edit: lose focus if the player is out of range\n\t\t\t\t\t\tmonster:setTargetEntity(nil, nil)\n\t\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity \t= Vector3.new()\n\t\t\t\t\t\treturn \"idling\"\n\n\n\t\t\t\t\telseif monster.__LAST_POSITION_SEEN then\n\t\t\t\t\t\tlocal lowerLastPositionSeen = Vector3.new(monster.__LAST_POSITION_SEEN.X, manifestProjectorPosition.Y, monster.__LAST_POSITION_SEEN.Z)\n\n\t\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity = (lowerLastPositionSeen - manifestProjectorPosition).unit * movingSpeed\n\n\t\t\t\t\t\tlocal sightRange = monster.sightRange\n\n\n\t\t\t\t\t\tif targetEntityDistance <= sightRange then\n\t\t\t\t\t\t\t-- check if the monster is close 'enough' to the last position seen\n\t\t\t\t\t\t\t-- based on the position alpha\n\t\t\t\t\t\t\tif utilities.magnitude(manifestProjectorPosition - monster.__LAST_POSITION_SEEN) > MONSTER_END_POSITION_ALPHA then\n\t\t\t\t\t\t\t\tmonster.__LAST_POSITION_SEEN = nil\n\n\n\t\t\t\t\t\t\t\tmonster:setTargetEntity(nil)\n\t\t\t\t\t\t\t\treturn \"idling\"\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tif monster.targetEntitySetSource == nil then\n\t\t\t\t\t\t\t\tmonster:setTargetEntity(nil)\n\t\t\t\t\t\t\t\treturn \"idling\"\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\tmonster:setTargetEntity(nil)\n\t\t\t\t\t\treturn \"idling\"\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tlocal manifestProjection \t\t= detection.projection_Box(monster.manifest.CFrame, monster.manifest.Size, hrpPosition)\n\t\t\t\tlocal targetEntityProjection \t= detection.projection_Box(monster.targetEntity.CFrame, monster.targetEntity.Size, monster.manifest.Position)\n\t\t\t\tlocal targetEntityDistance \t\t= utilities.magnitude(manifestProjection - targetEntityProjection)\n\n\t\t\t\tif targetEntityDistance <= monster.attackRange then\n\t\t\t\t\treturn \"attack-ready\"\n\t\t\t\tend\n\t\t\tend;\n\t\t}; [\"attack-ready\"] = {\n\t\t\tanimationEquivalent = \"idling\";\n\t\t\ttransitionLevel \t= 5;\n\t\t\tstep \t\t\t\t= function(monster, canSwitchState)\n\t\t\t\tif monster.targetEntity == nil then\n\t\t\t\t\treturn \"idling\"\n\t\t\t\tend\n\n\t\t\t\tif monster.targetEntity.health.Value <= 0 then\n\t\t\t\t\tmonster:setTargetEntity(nil, nil)\n\t\t\t\t\treturn \"idling\"\n\t\t\t\tend\n\n\t\t\t\tlocal manifestProjectorPosition = monster.manifest.Position\n\t\t\t\tlocal targetEntity \t\t\t\t= monster.targetEntity\n\t\t\t\tlocal hrpPosition \t\t\t\t= targetEntity.Position\n\n\t\t\t\t-- normalize y value to prevent difficulties when it comes\n\t\t\t\t-- to the player and the monster being on different elevations\n\n\t\t\t\tlocal correctHRPPosition = hrpPosition\n\n\t\t\t\tif monster.manifest.BodyVelocity.MaxForce.Y <= 0.1 then\n\t\t\t\t\tcorrectHRPPosition \t\t\t= Vector3.new(hrpPosition.X, manifestProjectorPosition.Y, hrpPosition.Z)\n\t\t\t\tend\n\n\t\t\t\tif monster.targetingYOffsetMulti then\n\t\t\t\t\tcorrectHRPPosition = correctHRPPosition + Vector3.new(0, monster.manifest.Size.Y * monster.targetingYOffsetMulti, 0)\n\t\t\t\tend\n\n\t\t\t\tlocal manifestProjection \t\t= detection.projection_Box(monster.manifest.CFrame, monster.manifest.Size, hrpPosition)\n\t\t\t\tlocal targetEntityProjection \t= detection.projection_Box(monster.targetEntity.CFrame, monster.targetEntity.Size, monster.manifest.Position)\n\t\t\t\tlocal targetEntityDistance \t\t= utilities.magnitude(manifestProjection - targetEntityProjection)\n\n\t\t\t\tif targetEntityDistance <= monster.attackRange then\n\t\t\t\t\t-- aggro'd and within attackRange\n\t\t\t\t\tlocal manifestProjectorPosition \t\t= monster.manifest.Position\n--\t\t\t\t\tmonster.manifest.BodyGyro.CFrame \t\t= CFrame.new(manifestProjectorPosition, correctHRPPosition)\n\n\t\t\t\t\tif tick() - monster.__LAST_ATTACK_TIME >= monster.attackSpeed then\n\t\t\t\t\t\tmonster.__LAST_ATTACK_TIME = tick()\n\n\t\t\t\t\t\treturn \"attacking\"\n\t\t\t\t\telse\n\t\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity = Vector3.new()\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\treturn \"following\"\n\t\t\t\tend\n\t\t\tend;\n\t\t}; [\"attacking\"] = {\n\t\t\ttransitionLevel \t\t= 6;\n\t\t\tanimationPriority \t\t= Enum.AnimationPriority.Action;\n\t\t\tdoNotLoopAnimation \t\t= true;\n\t\t\tdoNotStopAnimation \t\t= true;\n\t\t\texecute \t\t\t\t= function(player, targetAnimation, monsterData, clientMonsterContainer)\n\t\t\t\tif not game:GetService(\"RunService\"):IsClient() then\n\t\t\t\t\twarn(\"attacking::execute can only be called on client\")\n\n\t\t\t\t\treturn\n\t\t\t\telseif not monsterData.damageHitboxCollection then\n\t\t\t\t\t-- no damageHitboxCollection\n\t\t\t\tend\n\n\t\t\t\t-- prevent the player from taking damage twice from same animation\n\t\t\t\tlocal damageDebounce \t= false\n\t\t\t\tlocal characterHitbox \t= player.Character.PrimaryPart\n\t\t\t\tlocal serverHitbox \t\t= clientMonsterContainer.clientHitboxToServerHitboxReference.Value\n\n\t\t\t\tif clientMonsterContainer:FindFirstChild(\"entity\") and clientMonsterContainer.entity.PrimaryPart:FindFirstChild(\"attacking\") then\n\t\t\t\t\tlocal chance = 1\n\t\t\t\t\tif clientMonsterContainer:FindFirstChild(\"entity\") and clientMonsterContainer.entity.PrimaryPart:FindFirstChild(\"attacking2\") then\n\t\t\t\t\t\tchance = math.random(2)\n\t\t\t\t\t\tif chance == 2 then\n\t\t\t\t\t\t\tclientMonsterContainer.entity.PrimaryPart.attacking2:Play()\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\tif chance == 1 then\n\t\t\t\t\t\tclientMonsterContainer.entity.PrimaryPart.attacking:Play()\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\t-- slight delay\n\t\t\t\tlocal delayTime = targetAnimation.Length * (monsterData.animationDamageStart or 0.3)\n\n\t\t\t\twait(delayTime)\n\n\t\t\t\tlocal endTime = targetAnimation.Length * (monsterData.animationDamageEnd or 0.7)\n\n\t\t\t\tlocal duration = endTime - delayTime\n\n\t\t\t\tlocal step = duration / 10\n\n\t\t\t\tfor i = 0, duration, step do\n\t\t\t\t\tif targetAnimation.IsPlaying and not damageDebounce and clientMonsterContainer:FindFirstChild(\"entity\") then\n\t\t\t\t\t\tfor i, hitboxData in pairs(monsterData.damageHitboxCollection) do\n\t\t\t\t\t\t\tif clientMonsterContainer.entity:FindFirstChild(hitboxData.partName) and not damageDebounce then\n\t\t\t\t\t\t\t\tif hitboxData.castType == \"sphere\" then\n\t\t\t\t\t\t\t\t\tlocal spherecastOrigin = (clientMonsterContainer.entity[hitboxData.partName].CFrame * hitboxData.originOffset).p\n\t\t\t\t\t\t\t\t\tlocal boxProjection_HRP = detection.projection_Box(characterHitbox.CFrame, characterHitbox.Size, spherecastOrigin)\n\t\t\t\t\t\t\t\t\tif detection.spherecast_singleTarget(spherecastOrigin, hitboxData.radius, boxProjection_HRP) then\n\t\t\t\t\t\t\t\t\t\tdamageDebounce = true\n\n\t\t\t\t\t\t\t\t\t\tnetwork:fireServer(\"playerRequest_damageEntity\", serverHitbox, boxProjection_HRP, \"monster\")\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\telseif hitboxData.castType == \"box\" then\n\t\t\t\t\t\t\t\t\tlocal boxcastOriginCF \t= clientMonsterContainer.entity[hitboxData.partName].CFrame * hitboxData.originOffset\n\t\t\t\t\t\t\t\t\tlocal boxProjection_HRP = detection.projection_Box(characterHitbox.CFrame, characterHitbox.Size, boxcastOriginCF.p)\n\t\t\t\t\t\t\t\t\tif detection.boxcast_singleTarget(boxcastOriginCF, clientMonsterContainer.entity[hitboxData.partName].Size * (hitboxData.hitboxSizeMultiplier or Vector3.new(1, 1, 1)), boxProjection_HRP) then\n\t\t\t\t\t\t\t\t\t\tdamageDebounce = true\n\n\t\t\t\t\t\t\t\t\t\tnetwork:fireServer(\"playerRequest_damageEntity\", serverHitbox, boxProjection_HRP, \"monster\")\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\twait(step)\n\t\t\t\t\telse\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend;\n\n\t\t\tstep \t\t\t\t\t= function(monster, canSwitchState)\n\t\t\t\tif monster.targetEntity == nil then\n\t\t\t\t\treturn \"idling\"\n\t\t\t\tend\n\n\t\t\t\tlocal manifestProjectorPosition = monster.manifest.Position\n\t\t\t\tlocal targetEntity \t\t\t\t= monster.targetEntity\n\t\t\t\tlocal hrpPosition \t\t\t\t= targetEntity.Position\n\n\t\t\t\t-- normalize y value to prevent difficulties when it comes\n\t\t\t\t-- to the player and the monster being on different elevations\n\n\t\t\t\tlocal correctHRPPosition = hrpPosition\n\n\t\t\t\tif monster.manifest.BodyVelocity.MaxForce.Y <= 0.1 then\n\t\t\t\t\tcorrectHRPPosition = Vector3.new(hrpPosition.X, manifestProjectorPosition.Y, hrpPosition.Z)\n\t\t\t\tend\n\n\t\t\t\tif monster.targetingYOffsetMulti then\n\t\t\t\t\tcorrectHRPPosition = correctHRPPosition + Vector3.new(0, monster.manifest.Size.Y * monster.targetingYOffsetMulti, 0)\n\t\t\t\tend\n\n\t\t\t\tcorrectHRPPosition = correctHRPPosition + targetEntity.Velocity * 0.1\n\n\t\t\t\tlocal targetEntityDistance \t\t= utilities.magnitude(manifestProjectorPosition - correctHRPPosition)\n\t\t\t\tlocal moveDirection \t\t\t\t= (correctHRPPosition - manifestProjectorPosition).unit\n\t\t\t\tlocal positionJustInsideAttackRange = correctHRPPosition - moveDirection * (monster.attackRange)\n\n\t\t\t\t-- todo: this is awfully hacky, change this!\n\t\t\t\t-- monster.manifest.stance.Value = tostring(math.random())\n\n\n\t\t\t\t\tmonster.manifest.BodyGyro.CFrame \t\t= CFrame.new(manifestProjectorPosition, correctHRPPosition)\n\n\t\t\t\tif utilities.magnitude(targetEntity.Velocity) > 3 then\n\t\t\t\t\tlocal moveDirection \t\t\t\t\t= (correctHRPPosition - manifestProjectorPosition).unit\n\t\t\t\t\tlocal positionJustInsideAttackRange \t= correctHRPPosition - moveDirection * (monster.attackRange)\n\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity \t= moveDirection * (monster.baseSpeed * 0.7)\n\t\t\t\telse\n\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity \t= Vector3.new()\n\t\t\t\tend\n\n\t\t\t\tlocal targetEntityPlayer = game.Players:GetPlayerFromCharacter(monster.targetEntity.Parent)\n\n\t\t\t\tif not targetEntityPlayer then\n\n\t\t\t\t\tdelay(0.25, function()\n\t\t\t\t\t\tnetwork:invoke(\"monsterDamageRequest_server\",\n\t\t\t\t\t\t\tnil,\n\t\t\t\t\t\t\tmonster.targetEntity,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tdamage \t\t\t= monster.damage;\n\t\t\t\t\t\t\t\tsourceType \t\t= \"monster\";\n\t\t\t\t\t\t\t\tsourceId \t\t= nil;\n\t\t\t\t\t\t\t\tdamageCategory \t= \"direct\";\n\t\t\t\t\t\t\t\tdamageType \t\t= \"physical\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t)\n\t\t\t\t\tend)\n\t\t\t\tend\n\n\t\t\t\treturn \"micro-sleeping\"\n\t\t\tend;\n\t\t}; [\"special-attacking\"] = {\n\t\t\tanimationEquivalent = \"special-attack\";\n\t\t\ttransitionLevel = 7;\n\t\t\tstep \t\t\t= function(monster, canSwitchState)\n\t\t\t\tmonster.specialsUsed = monster.specialsUsed + 1\n\n\t\t\t\tif monster.__STATE_OVERRIDES[\"special-attacking\"] then\n\t\t\t\t\tmonster.__STATE_OVERRIDES[\"special-attacking\"](monster)\n\t\t\t\tend\n\n\t\t\t\treturn \"special-recovering\";\n\t\t\tend;\n\t\t}; [\"micro-sleeping\"] = {\n\t\t\tanimationEquivalent \t\t= \"idling\";\n\t\t\ttransitionLevel \t\t\t= 8;\n\t\t\tlockTimeForLowerTransition \t= 0.2;\n\t\t\tstep \t\t\t\t\t\t= function(monster, canSwitchState)\n\t\t\t\treturn \"attack-ready\"\n\t\t\tend;\n\t\t};[\"special-recovering\"] = {\n\t\t\tanimationEquivalent \t\t= \"idling\";\n\t\t\ttransitionLevel \t\t\t= 9;\n\t\t\tlockTimeForLowerTransition \t= 0.75;\n\t\t\tstep \t\t\t= function(monster, canSwitchState)\n\t\t\t\treturn \"attack-ready\";\n\t\t\tend;\n\t\t}; [\"dead\"] = {\n\t\t\tanimationEquivalent = \"death\";\n\t\t\ttransitionLevel \t= math.huge;\n\t\t\tstopTransitions \t= false;\n\t\t\tstep \t\t\t\t= function(monster, canSwitchState)\n\t\t\t\treturn nil\n\t\t\tend;\n\t\t\texecute = function()\n\t\t\t\treturn nil\n\t\t\tend\n\t\t}; [\"attacked-by-player\"] = {\n\t\t\ttransitionLevel = 1;\n\t\t\tstep \t\t\t= function(monster)\n\t\t\t\tprint(\"HELLO! :D\", monster.closestEntity, monster.entityMonsterWasAttackedBy)\n\t\t\t\tif monster.closestEntity and (monster.targetEntityLockType or 0) <= 1 and monster.entityMonsterWasAttackedBy then\n\n\t\t\t\t\tlocal closestEntityDistance = utilities.magnitude(monster.entityMonsterWasAttackedBy.Position - monster.manifest.Position)\n\t\t\t\t\tif monster:isTargetEntityInLineOfSight(nil, false, monster.entityMonsterWasAttackedBy) then\n\t\t\t\t\t\tmonster:setTargetEntity(monster.entityMonsterWasAttackedBy)\n\n\t\t\t\t\t\treturn \"following\"\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\treturn \"idling\"\n\t\t\tend\n\t\t}\n\t};\n}\n"
  },
  {
    "path": "src/ReplicatedStorage/defaultPetState.lua",
    "content": "-- piggyback off monsters hehe!\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal pathfinding \t= modules.load(\"pathfinding\")\n\t\tlocal utilities\t\t= modules.load(\"utilities\")\n\t\tlocal projectile\t= modules.load(\"projectile\")\n\t\tlocal detection\t\t= modules.load(\"detection\")\n\t\tlocal network\t\t= modules.load(\"network\")\n\t\tlocal placeSetup\t= modules.load(\"placeSetup\")\n\t\t\tlocal placeFolders = placeSetup.getPlaceFoldersFolder()\n\t\t\t\nlocal itemLookup do\n\tspawn(function()\n\t\t-- unfortunately there's a circular reference \n\t\t-- here if we don't do this.. yucky\n\t\titemLookup = require(replicatedStorage.itemData)\n\tend)\nend\n\nlocal playerHeight \t\t\t= 2\nlocal petAcquisitionRange \t= 6\nlocal petScanRange \t\t\t= 20\nlocal petIgnoreList \t\t= {placeFolders}\n\nlocal function getClosestItemWithinRange(monster, doForceRefresh)\n\tif not monster.__LAST_NEARBY_ITEM_CHECK then\n\t\tmonster.__LAST_NEARBY_ITEM_CHECK = 0\n\tend\n\t\n\tif not monster.owner then\n\t\treturn nil\n\tend\n\t\n\tif not itemLookup then\n\t\treturn nil\n\tend\n\t\n\tif doForceRefresh or tick() - monster.__LAST_NEARBY_ITEM_CHECK >= 2 then\n\t\tmonster.__LAST_NEARBY_ITEM_CHECK = tick()\n\t\t\n\t\tlocal closestItem, closestItemDistane = nil, petScanRange\n\t\tfor i, itemPart in pairs(placeFolders.items:GetChildren()) do\n\t\t\tlocal distanceAway = (itemPart.Position - monster.manifest.Position).magnitude\n\t\t\tif distanceAway <= closestItemDistane and utilities.playerCanPickUpItem(monster.owner, itemPart, true) and (not itemPart:FindFirstChild(\"playerDropSource\") or itemPart.playerDropSource.Value ~= monster.owner.userId) then\n\t\t\t\tlocal ray = Ray.new(monster.manifest.Position, itemPart.Position - monster.manifest.Position)\n\t\t\t\tlocal hitPart = projectile.raycastForProjectile(ray, petIgnoreList)\n\t\t\t\t\n\t\t\t\tif not hitPart then\n\t\t\t\t\tif itemLookup[itemPart.Name] then\n\t\t\t\t\t\tif network:invoke(\"doesPlayerHaveInventorySpaceForTrade\", monster.owner, {}, {{id = itemLookup[itemPart.Name].id}}) then\n\t\t\t\t\t\t\t-- good, nothing between\n\t\t\t\t\t\t\tclosestItem \t\t= itemPart\n\t\t\t\t\t\t\tclosestItemDistane \t= distanceAway\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\t\n\t\tmonster.__CLOSEST_ITEM = closestItem\n\tend\n\t\n\treturn monster.__CLOSEST_ITEM\nend\n\nreturn {\n\tdefault = \"setup\",\n\tstates \t= {\n\t\t[\"setup\"] = {\n\t\t\tanimationEquivalent = \"idle\";\n\t\t\ttransitionLevel \t= 0;\n\t\t\tstep \t\t\t\t= function(monster)\n\t\t\t\tif monster.owner then\n\t\t\t\t\tif monster.owner.Character and monster.owner.Character.PrimaryPart then\n\t\t\t\t\t\tlocal targetCFrame = monster.owner.Character.PrimaryPart.CFrame * CFrame.new(0, 0, 3) * CFrame.new(2, 0, 0)\n\t\t\t\t\t\tmonster.manifest.CFrame = targetCFrame\n\t\t\t\t\t\t\n\t\t\t\t\t\treturn \"idle\"\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend;\n\t\t}; [\"idle\"] = {\n\t\t\tanimationEquivalent = \"idle\";\n\t\t\ttransitionLevel \t= 1;\n\t\t\tstep \t\t\t\t= function(monster, canSwitchState)\n\t\t\t\tif monster.owner then\n\t\t\t\t\tif monster.owner.Character and monster.owner.Character.PrimaryPart then\n\t\t\t\t\t\tlocal closestItem = getClosestItemWithinRange(monster)\n\t\t\t\t\t\t\n\t\t\t\t\t\tif not closestItem then\n--\t\t\t\t\t\t\tlocal targetCFrame = monster.owner.Character.PrimaryPart.CFrame * CFrame.new(0, 0, 3) * CFrame.new(2, 0, 0)\n--\t\t\t\t\t\t\tlocal correct_manifestPosition = Vector3.new(monster.manifest.Position.X, targetCFrame.Y, monster.manifest.Position.Z)\n--\t\t\t\t\t\t\tif (correct_manifestPosition - targetCFrame.p).magnitude >= 3 then\n--\t\t\t\t\t\t\t\treturn \"follow\"\n--\t\t\t\t\t\t\telse\n--\t\t\t\t\t\t\t\tmonster.manifest.BodyGyro.CFrame = CFrame.new(monster.manifest.Position, monster.manifest.Position + targetCFrame.lookVector)\n--\t\t\t\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity = Vector3.new()\n--\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tlocal distanceFromOwner = ((monster.owner.Character.PrimaryPart.Position - monster.manifest.Position) * Vector3.new(1, 0, 1)).magnitude\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif distanceFromOwner > 7 then\n\t\t\t\t\t\t\t\treturn \"follow\"\n\t\t\t\t\t\t\telse\n--\t\t\t\t\t\t\t\tmonster.manifest.BodyGyro.CFrame \t\t= CFrame.new(monster.manifest.Position, monster.manifest.Position + monster.owner.Character.PrimaryPart.CFrame.lookVector)\n\t\t\t\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity \t= Vector3.new()\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t-- close item!\n\t\t\t\t\t\t\tmonster.timeSwitchToFetching = tick()\n\t\t\t\t\t\n\t\t\t\t\t\t\treturn \"fetching-item\"\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend;\n\t\t}; [\"follow\"] = {\n\t\t\tanimationEquivalent = \"movement\";\n\t\t\ttransitionLevel \t= 2;\n\t\t\tstep \t\t\t\t= function(monster, canSwitchState)\n\t\t\t\tlocal closestItem = getClosestItemWithinRange(monster)\n\t\t\t\t\t\t\n\t\t\t\tif not closestItem then\n--\t\t\t\t\tlocal targetCFrame = monster.owner.Character.PrimaryPart.CFrame * CFrame.new(0, 0, 3) * CFrame.new(2, 0, 0)\n--\t\t\t\t\tlocal correct_manifestPosition = Vector3.new(monster.manifest.Position.X, targetCFrame.Y, monster.manifest.Position.Z)\n\t\t\t\t\tlocal targetMovementDirection = ((monster.owner.Character.PrimaryPart.Position - monster.manifest.Position) * Vector3.new(1, 0, 1))\n\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity = targetMovementDirection.unit * 20\n\t\t\t\t\tmonster.manifest.BodyGyro.CFrame = CFrame.new(monster.manifest.Position, monster.manifest.Position + targetMovementDirection)\n\t\t\t\t\t\n\t\t\t\t\tif targetMovementDirection.magnitude < 7 then\n\t\t\t\t\t\treturn \"idle\"\n\t\t\t\t\telseif targetMovementDirection.magnitude >= 40 then\n\t\t\t\t\t\treturn \"setup\"\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tmonster.timeSwitchToFetching = tick()\n\t\t\t\t\t\n\t\t\t\t\treturn \"fetching-item\"\n\t\t\t\tend\n\t\t\tend;\n\t\t}; [\"fetching-item\"] = {\n\t\t\tanimationEquivalent = \"movement\";\n\t\t\ttransitionLevel \t= 3;\n\t\t\tstep \t\t\t\t= function(monster, canSwitchState)\n\t\t\t\tlocal closestItem = getClosestItemWithinRange(monster)\n\t\t\t\t\n\t\t\t\tif closestItem then\n\t\t\t\t\tlocal targetCFrame = closestItem.CFrame\n\t\t\t\t\tlocal correct_manifestPosition = Vector3.new(monster.manifest.Position.X, targetCFrame.Y, monster.manifest.Position.Z)\n\t\t\t\t\t\n\t\t\t\t\tmonster.manifest.BodyGyro.CFrame = CFrame.new(correct_manifestPosition, targetCFrame.p)\n\t\t\t\t\t\n\t\t\t\t\tif monster.timeSwitchToFetching and tick() - monster.timeSwitchToFetching <= 6 then\n\t\t\t\t\t\tif (correct_manifestPosition - targetCFrame.p).magnitude < 3 then\n\t\t\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity = Vector3.new()\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tlocal success, errmsg = network:invoke(\"pickUpItemForPlayer_server\", monster.owner, closestItem, true)\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tif success then\n\t\t\t\t\t\t\t\tgetClosestItemWithinRange(monster, true)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tmonster.manifest.BodyVelocity.Velocity = (targetCFrame.p - correct_manifestPosition).unit * 20\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\treturn \"setup\"\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\treturn \"follow\"\n\t\t\t\tend\n\t\t\tend;\n\t\t};\n\t};\n}\n\t"
  },
  {
    "path": "src/ReplicatedStorage/dialogue/genericShopkeeper.lua",
    "content": "local replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal modules = require(replicatedStorage.modules)\nlocal network = modules.load(\"network\")\n\nreturn {\n\tid = \"startTalkingToShopkeeper\";\n\tdialogue = {{text = \"Hey, you! I'm a test NPC for Dialogue. How are you?\"}};\n\toptions = {\n\t\t[\"Gross, don't talk to me.\"] = {\n\t\t\tdialogue = {{text = \"That's not very nice.. :(\"}};\n\t\t\tdialogue2 = {{text = \"Get out of here whippersnapper!\"}};\n\t\t};\n\t\t\t\n\t\t[\"That's cool!\"] = {\n\t\t\tdialogue = {{text = \"I know.\"}};\n\t\t};\n\t\t\n\t\t[\"Give me a sick quest.\"] = {\n\t\t\tonSelected = function()\n\n\t\t\tend;\n\t\t\t\n\t\t\tdialogue = {{text = \"Don't worry, I got you.\"}};\n\t\t}\n\t};\n}"
  },
  {
    "path": "src/ReplicatedStorage/dialogue/init.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/ReplicatedStorage/dialogue/testNPCDialog.lua",
    "content": "local replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal modules = require(replicatedStorage.modules)\nlocal network = modules.load(\"network\")\n\n-- quest 1\nlocal dialogue = {\n\tonSelected = function()\n\t\n\tend;\n\n\tid \t= \"startTalkingToShopkeeper\";\n\tdialogue = {{text = \"Hey, you! I'm a test NPC for Dialogue. How are you?\"}};\n\toptions = {\n\t\t[\"Gross, don't talk to me.\"] = {\n\t\t\tdialogue = {{text = \"That's not very nice.. :(\"}};\n\t\t\tdialogue2 = {{text = \"Get out of here whippersnapper!\"}};\n\t\t};\n\t\t\t\n\t\t[\"That's cool!\"] = {\n\t\t\tdialogue = {{text = \"I know.\"}};\n\t\t};\n\t\t\n\t\t[\"Give me a sick quest.\"] = {\n\t\t\tonSelected = function()\n\n\t\t\tend;\n\t\t\t\n\t\t\tdialogue = {{text = \"Don't worry, I got you.\"}};\n\t\t}\n\t};\n}\n\nlocal dialogue2 = {\n\tdialogue = {{text = \"Greetings Adventurer!\"}};\n\toptions = {\n\t\t{\n\t\t\tresponse = \"Today is such a beautiful day!\";\n\t\t\tdialogue = {{text = \"No it isn't, my wife is sick.\"}};\n\t\t};\n\t\t{\n\t\t\tquestId = 2;\n\t\t}\n\t};\n}\n\nreturn dialogue2"
  },
  {
    "path": "src/ReplicatedStorage/dialogueLookup.lua",
    "content": "local module = {}\n\nreturn module\n"
  },
  {
    "path": "src/ReplicatedStorage/itemAttributes.lua",
    "content": "local function stat(itemBaseData, inventorySlotData, multi)\n\tmulti = multi or 1\n\tlocal level = itemBaseData.minLevel or 1\n\tlocal itemType = itemBaseData.equipmentSlot\n\tlocal typeScaleMulti = (itemType == 1 and 0.75) or (itemType == 8 and 1) or 1.5\n\treturn math.ceil((multi * level) / (5 * typeScaleMulti))\nend\n\nlocal attributes = {\n\t\n\t-- hats only\n\tworn = {\n\t\tprefix = \"Worn\";\n\t\tcolor = Color3.fromRGB(148, 148, 148);\n\t\tvalueMulti = 0.75;\n\t\tmodifier = function(itemBaseData, inventorySlotData)\n\t\t\tlocal penalty = -stat(itemBaseData, inventorySlotData, 0.5)\n\t\t\t-- ??\n\t\tend;\t\t\t\n\t};\n\t\n\t-- armor only\n\ttattered = {\n\t\tprefix = \"Tattered\";\n\t\tcolor = Color3.fromRGB(148, 148, 148);\n\t\tvalueMulti = 0.75;\n\t\tmodifier = function(itemBaseData, inventorySlotData)\n\t\t\treturn {defense = -(stat(itemBaseData, inventorySlotData)+1)}\n\t\tend;\t\t\t\n\t};\n\t\n\t-- weapons only\n\tdull = {\n\t\tprefix = \"Dull\";\n\t\tcolor = Color3.fromRGB(148, 148, 148);\n\t\tvalueMulti = 0.75;\n\t\tmodifier = function(itemBaseData, inventorySlotData)\n\t\t\treturn {baseDamage = -(stat(itemBaseData, inventorySlotData, 0.75)+1)}\n\t\tend;\t\t\n\t};\n\t\n\tkeen = {\n\t\tprefix = \"Keen\";\n\t\tcolor = Color3.fromRGB(135, 186, 213);\n\t\tvalueMulti = 1.25;\n\t\tmodifier = function(itemBaseData, inventorySlotData)\n\t\t\treturn {int = stat(itemBaseData, inventorySlotData)}\n\t\tend;\t\t\n\t};\n\t\n\tfierce = {\n\t\tprefix = \"Fierce\";\n\t\tcolor = Color3.fromRGB(190, 143, 109);\n\t\tvalueMulti = 1.25;\n\t\tmodifier = function(itemBaseData, inventorySlotData)\n\t\t\treturn {str = stat(itemBaseData, inventorySlotData)}\n\t\tend;\t\t\t\t\n\t};\n\t\n\tswift = {\n\t\tprefix = \"Swift\";\n\t\tcolor = Color3.fromRGB(190, 130, 179);\n\t\tvalueMulti = 1.25;\n\t\tmodifier = function(itemBaseData, inventorySlotData)\n\t\t\treturn {dex = stat(itemBaseData, inventorySlotData)}\n\t\tend;\t\t\n\t};\n\t\n\tvibrant = {\n\t\tprefix = \"Vibrant\";\n\t\tcolor = Color3.fromRGB(194, 127, 128);\n\t\tvalueMulti = 1.25;\n\t\tmodifier = function(itemBaseData, inventorySlotData)\n\t\t\treturn {vit = stat(itemBaseData, inventorySlotData)}\n\t\tend;\t\t\n\t};\n\t\n\tpristine = {\n\t\tprefix = \"Pristine\";\n\t\tcolor = Color3.fromRGB(150, 45, 202);\n\t\tvalueMulti = 1.75;\n\t\tmodifier = function(itemBaseData, inventorySlotData)\n\t\t\tlocal itemType = itemBaseData.equipmentSlot\n\t\t\tif itemType == 1 then\n\t\t\t\treturn {\n\t\t\t\t\tbaseDamage = stat(itemBaseData, inventorySlotData, 0.75 * 0.7);\n\t\t\t\t\twisdom = stat(itemBaseData, inventorySlotData, 0.4)/100;\n\t\t\t\t}\n\t\t\telseif itemType == 8 then\n\t\t\t\treturn {\n\t\t\t\t\tdefense = stat(itemBaseData, inventorySlotData, 0.7);\n\t\t\t\t\twisdom = stat(itemBaseData, inventorySlotData, 0.4)/100;\n\t\t\t\t}\n\t\t\tend\n\t\tend;\t\t\n\t\t\n\t};\n\t\n\tlegendary = {\n\t\tprefix = \"Legendary\";\n\t\tcolor = Color3.fromRGB(225, 176, 28);\n\t\tvalueMulti = 3;\n\t\tmodifier = function(itemBaseData, inventorySlotData)\n\t\t\tlocal itemType = itemBaseData.equipmentSlot\n\t\t\tif itemType == 1 then\n\t\t\t\treturn {\n\t\t\t\t\tbaseDamage = stat(itemBaseData, inventorySlotData, 0.75 * 1.2);\n\t\t\t\t\twisdom = stat(itemBaseData, inventorySlotData, 0.6)/100;\n\t\t\t\t}\n\t\t\telseif itemType == 8 then\n\t\t\t\treturn {\n\t\t\t\t\tdefense = stat(itemBaseData, inventorySlotData, 1.2);\n\t\t\t\t\twisdom = stat(itemBaseData, inventorySlotData, 0.6)/100;\n\t\t\t\t}\n\t\t\tend\n\t\tend;\t\t\t\n\t};\n\t\t\n}\n\nreturn attributes\n"
  },
  {
    "path": "src/ReplicatedStorage/itemData/100% armor defense scroll.lua",
    "content": "\n\nreturn {\n\t--> identifying information <--\n\tid \t\t= 26;\n\t\n\t--> generic information <--\n\tname \t\t= \"Basic Armor DEF Scroll (100%)\";\n\trarity \t\t= \"Rare\";\n\timage \t\t= \"rbxassetid://2528903584\";\n\tdescription = \"A magical scroll that has the ability to increase an armor's defense.\";\n\t\n\tenchantments = {\n\t\t[1] = {modifierData = {defense = 1}; selectionWeight = 1; tier = 1};\n\t};\t\n\t\n\ttier = 2;\n\t\n\t--> stats information <--\n\tsuccessRate = 1;\n\t\n\tvalidation = function(itemBaseData, inventorySlotData)\n\t\tif itemBaseData.category == \"equipment\" and itemBaseData.equipmentSlot == 8 then\t\t\t\n\t\t\treturn true\n\t\tend\t\t\n\t\treturn false\t\t\n\tend;\n\t\n\tapplyScroll = function(player, itemInventorySlotData, successfullyScrolled)\n\t\tlocal itemLookup = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"itemData\"))\n\t\tlocal itemBaseData = itemLookup[itemInventorySlotData.id]\t\t\n\t\tif itemBaseData.category == \"equipment\" and itemBaseData.equipmentSlot == 8 then\t\t\t\n\t\t\treturn true\n\t\tend\t\t\n\t\treturn false\n\tend;\n\t\n\t--> shop information <--\n\tbuyValue = 12000;\n\tsellValue = 400;\n\t\n\t--> handling information <--\n\tcanStack \t\t\t= false;\n\tcanBeBound \t\t\t= false;\n\tcanAwaken \t\t\t= false;\n\tenchantsEquipment \t= true;\n\t\n\t--> sorting information <--\n\tisImportant \t= true;\n\tcategory \t\t= \"consumable\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/100% headgear defense scroll.lua",
    "content": "\n\nreturn {\n\t--> identifying information <--\n\tid \t\t= 61;\n\t\n\t--> generic information <--\n\tname \t\t= \"Basic Headgear Scroll (100%)\";\n\trarity \t\t= \"Rare\";\n\timage \t\t= \"rbxassetid://2528903584\";\n\tdescription = \"A magical scroll that has the ability to increase a headgear's stats.\";\n\t\n\tenchantments = {\n\t\t[1] = {modifierData = {}; statUpgrade = 1; selectionWeight = 1; tier = 1};\n\t};\t\n\ttier = 2;\t\n\t\n\t--> stats information <--\n\tsuccessRate = 1;\n\t\n\tvalidation = function(itemBaseData, inventorySlotData)\n\t\tif itemBaseData.category == \"equipment\" and itemBaseData.equipmentSlot == 2 then\t\t\t\n\t\t\treturn true\n\t\tend\t\t\n\t\treturn false\t\t\n\tend;\t\n\t\n\tapplyScroll = function(player, itemInventorySlotData, successfullyScrolled)\n\t\tlocal itemLookup = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"itemData\"))\n\t\tlocal itemBaseData = itemLookup[itemInventorySlotData.id]\t\t\n\t\tif itemBaseData.category == \"equipment\" and itemBaseData.equipmentSlot == 2 then\t\t\t\n\t\t\treturn true\n\t\tend\t\t\n\t\treturn false\n\tend;\t\n\t\n\t\n\t--> shop information <--\n\tbuyValue = 10000;\n\tsellValue = 700;\n\t\n\t--> handling information <--\n\tcanStack \t\t\t= false;\n\tcanBeBound \t\t\t= false;\n\tcanAwaken \t\t\t= false;\n\tenchantsEquipment \t= true;\n\t\n\t--> sorting information <--\n\tisImportant \t= true;\n\tcategory \t\t= \"consumable\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/100% weapon attack scroll.lua",
    "content": "return {\n\t--> identifying information <--\n\tid \t\t= 14;\n\t\n\t--> generic information <--\n\tname \t\t= \"Basic Weapon ATK Scroll (100%)\";\n\trarity \t\t= \"Rare\";\n\timage \t\t= \"rbxassetid://2528903584\";\n\tdescription = \"A magical scroll that has the ability to increase a weapon's attack power.\";\n\t\n\tenchantments = {\n\t\t[1] = {modifierData = {baseDamage = 1}; selectionWeight = 1; tier = 1};\n\t};\n\t\n\ttier = 2;\n\t\n\tvalidation = function(itemBaseData, inventorySlotData)\n\t\tif itemBaseData.category == \"equipment\" and itemBaseData.equipmentSlot == 1 then\t\t\t\t\n\t\t\treturn true\n\t\tend\t\t\n\t\treturn false\t\t\n\tend;\t\n\t\n\t--> stats information <--\n\tsuccessRate = 1;\n\tapplyScroll = function(player, itemInventorySlotData, successfullyScrolled, playerInput, _scrData)\n\t\tlocal itemLookup \t= require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"itemData\"))\n\t\tlocal itemBaseData \t= itemLookup[itemInventorySlotData.id]\n\t\tif itemBaseData.category == \"equipment\" and itemBaseData.equipmentSlot == 1 then\t\t\t\n\t\t\treturn true\n\t\tend\n\t\treturn false\n\tend;\n\t\n\t--> shop information <--\n\tbuyValue = 15000;\n\tsellValue = 1000;\n\t\n\t--> handling information <--\n\tcanStack \t\t\t= false;\n\tcanBeBound \t\t\t= false;\n\tcanAwaken \t\t\t= false;\n\tenchantsEquipment \t= true;\n\t\n\t--> sorting information <--\n\tisImportant \t= true;\n\tcategory \t\t= \"consumable\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/apple.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nitem = {\n\t--> identifying information <--\n\tid \t\t= 226;\n\n\t--> generic information <--\n\tname \t\t= \"Red Apple\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://4574376530\";\n\tdescription = \"A crisp apple fresh from the tree.\";\n\t\n\titemType = \"food\";\n\n\tuseSound \t= \"eat_food\";\n\t\n\t--> stats information <--\n\tactivationEffect = function(player)\n\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.health.Value > 0 and player.Character.PrimaryPart.health.Value < player.Character.PrimaryPart.maxHealth.Value then\n\t\t\tlocal success = network:invoke(\"applyPotionStatusEffectToEntityManifest_server\", player.Character.PrimaryPart, 30, nil, \"item\", 226)\n\t\t\t\n\t\t\treturn success, success and \"You feel refreshed.\" or \"ERRORRRR\"\n\t\tend\n\t\t\n\t\treturn false, \"Character is invalid.\"\n\tend;\n\t\n\tconsumeTime = 1;\n\tstackSize = 44;\n\t\n\t--> shop information <--\n\tbuyValue = 200;\n\tsellValue = 50;\t\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= true;\n\tcanAwaken \t= false;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"consumable\";\n}\n\nreturn item"
  },
  {
    "path": "src/ReplicatedStorage/itemData/arrow.lua",
    "content": "\nreturn {\n\t--> identifying information <--\n\tid = 87;\n\n\t--> generic information <--\n\tname = \"Arrow\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://2744225103\";\n\tdescription = \"For shooting things.\";\n\n\t--> handling information <--\n\tcanStack = true;\n\tcanBeBound = false;\n\tcanAwaken = false;\n\n\tequipmentType = \"arrow\";\n\tequipmentPosition = 12;\n\n\t--> shop information <--\n\tbuyValue = 10;\n\tsellValue = 3;\n\tstackSize = 999;\n\n\t--> crafting information <--\n\trecipe = {\n\t\t-- oak wood\n\t\t{id = 2, stacks = 1},\n\t\t-- stone\n\t\t{id = 600, stacks = 1},\n\t\t-- feather\n\t\t{id = 271, stacks = 1},\n\t};\n\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/chicken egg.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nitem = {\n\t--> identifying information <--\n\tid \t\t= 270;\n\n\t--> generic information <--\n\tname \t\t= \"Chicken Egg\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://4635646183\";\n\tdescription = \"Makes for a great breakfast.\";\n\t\n\t--> shop information <--\t\n\tsellValue = 15;\n\tbuyValue = 150;\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= false;\n\tcanAwaken \t= false;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"miscellaneous\";\n}\n\nreturn item"
  },
  {
    "path": "src/ReplicatedStorage/itemData/chicken feather.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nitem = {\n\t--> identifying information <--\n\tid \t\t= 271;\n\n\t--> generic information <--\n\tname \t\t= \"Chicken Feather\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://4635643200\";\n\tdescription = \"Plucked fresh from the source.\";\n\t\n\t--> shop information <--\t\n\tsellValue = 15;\n\tbuyValue = 150;\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= false;\n\tcanAwaken \t= false;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"miscellaneous\";\n}\n\nreturn item"
  },
  {
    "path": "src/ReplicatedStorage/itemData/chicken leg.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nitem = {\n\t--> identifying information <--\n\tid \t\t= 277;\n\n\t--> generic information <--\n\tname \t\t= \"Chicken Leg\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://5048073993\";\n\tdescription = \"A rare treat.\";\n\t\n\titemType = \"food\";\n\t\n\tuseSound \t= \"eat_food\";\n\t\n\t--> stats information <--\n\tactivationEffect = function(player)\n\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.health.Value > 0 and player.Character.PrimaryPart.health.Value < player.Character.PrimaryPart.maxHealth.Value then\n\t\t\tlocal success = network:invoke(\"applyPotionStatusEffectToEntityManifest_server\", player.Character.PrimaryPart, 30, nil, \"item\", 277)\n\t\t\t\n\t\t\treturn success, success and \"You feel refreshed.\" or \"ERRORRRR\"\n\t\tend\n\t\t\n\t\treturn false, \"Character is invalid.\"\n\tend;\n\t\n\tconsumeTime = 3;\n\tstackSize = 16;\t\n\t\n\t--> shop information <--\t\n\tbuyValue = 1000;\n\tsellValue = 250;\t\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= false;\n\tcanAwaken \t= false;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"consumable\";\n}\n\nreturn item"
  },
  {
    "path": "src/ReplicatedStorage/itemData/coin piece.lua",
    "content": "local replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network = modules.load(\"network\")\n\nreturn {\n\t--> identifying information <--\n\tid \t\t= 1;\n\t\n\t--> generic information <--\n\tname \t\t= \"Mushcoin\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://2535600080\";\n\tdescription = \"The main currency of Vesteria\";\n\t\n\tuseSound \t= \"coins\";\t\n\t--> stats information <--\n\tactivationEffect = function(player, physItem)\n\t\tlocal value = physItem:FindFirstChild(\"itemValue\") and physItem.itemValue.Value or 1\n\t\tlocal source = physItem:FindFirstChild(\"itemSource\") and physItem.itemSource.Value\n\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\tif playerData then\n\t\t\t\n\t\t\tlocal totalStats = network:invoke(\"getPlayerData\", player).nonSerializeData.statistics_final\n\t\t\tlocal greed = totalStats and totalStats.greed or 1\t\t\t\n\t\t\t\n\t\t\tlocal amount = math.floor(value * greed)\n\t\t\t\n\t\t\tplayerData.nonSerializeData.incrementPlayerData(\"gold\", amount, source)\n\t\t\t\n\t\t\treturn true, \"Successfully gained \"..value..\" coins!\", amount\n\t\tend\n\t\t\n\t\treturn false, \"Can't consume this right now\", 0\n\tend;\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= false;\n\tcanAwaken \t= false;\n\tautoConsume = true;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"consumable\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/copper axe.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 403;\n\n\t--> generic information <--\n\tname = \"Copper Axe\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://5344159593\";\n\tdescription = \"A smelted bronze axe with a smoothed frame.\";\n\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"axe\";\n\tgripCFrame = CFrame.Angles(math.pi / 2, 0, 0) * CFrame.new(0, -0.2, 2);\n\tminLevel = 5;\n\n\t--> stats information <--\n\tbaseDamage = 6;\n\tmodifierData = {{woodcutting = 7}};\n\n\t--> crafting information <--\n\trecipe = {\n\t\t-- oak wood\n\t\t{id = 2, stacks = 20},\n\t\t-- copper\n\t\t{id = 601, stacks = 20}\n\t};\n\n\t--> shop information <--\n\tbuyValue = 2000;\n\tsellValue = 700;\n\n\t--> handling information <--\n\tcanStack = false;\n\tcanBeBound = false;\n\tcanAwaken = true;\n\n\t--> sorting information <--\n\tisImportant = false;\n\tcategory = \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/copper ore.lua",
    "content": "\nreturn {\n\t--> identifying information <--\n\tid = 601;\n\n\t--> generic information <--\n\tname = \"Copper Ore\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://5429389124\";\n\tdescription = \"An ore of shiny copper that is used in many recipes.\";\n\n\t-- todo: wood item type icon\n--\titemType = \"wood\";\n\n\t--> handling information <--\n\tcanStack = true;\n\n\t--> shop information <--\n\tsellValue = 250;\n\tbuyValue = 2500;\n\n\t--> sorting information <--\n\tcategory = \"miscellaneous\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/copper pickaxe.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 503;\n\n\t--> generic information <--\n\tname = \"Copper Pickaxe\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://5344159326\";\n\tdescription = \"A smelted bronze pickaxe with a smoothed frame.\";\n\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"pickaxe\";\n\tgripCFrame = CFrame.Angles(math.pi / 2, 0, 0) * CFrame.new(0, -0.2, 2);\n\tminLevel = 5;\n\n\t--> stats information <--\n\tbaseDamage = 6;\n\tmodifierData = {{mining = 7}};\n\n\t--> crafting information <--\n\trecipe = {\n\t\t-- oak wood\n\t\t{id = 2, stacks = 20},\n\t\t-- copper\n\t\t{id = 601, stacks = 20}\n\t};\n\n\t--> shop information <--\n\tbuyValue = 2000;\n\tsellValue = 700;\n\n\t--> handling information <--\n\tcanStack = false;\n\tcanBeBound = false;\n\tcanAwaken = true;\n\n\t--> sorting information <--\n\tisImportant = false;\n\tcategory = \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/copper sword.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 32;\n\n\t--> generic information <--\n\tname = \"Copper Sword\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://2528903917\";\n\tdescription = \"A large sword forged from copper. It's not the strongest, but at least it's not falling apart.\";\n\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"sword\";\n\n\tgripCFrame = CFrame.Angles(-math.pi / 2, 0, 0) * CFrame.new(0.1, 0, 2) * CFrame.Angles(0, 0, math.pi / 2);\n\n\tminLevel = 7;\n\n\t--> stats information <--\n\tbaseDamage = 9;\n\tattackSpeed = 3;\n    bonusStats = {};\n\n\t--> crafting information <--\n\trecipe = {\n\t\t-- copper\n\t\t{id = 601, stacks = 30}\n\t};\n\n\t--> shop information <--\n\tbuyValue = 2000;\n\tsellValue = 1000;\n\n\t--> sorting information <--\n\tcategory = \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/fish.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nreturn {\n\t--> identifying information <--\n\tid \t\t= 30;\n\n\t--> generic information <--\n\tname \t\t= \"Fresh Fish\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://2528901664\";\n\tdescription = \"A tasty freshly-caught fish that restores 100 HP & MP.\";\n\t\n\titemType = \"fish\";\n\t\n\tuseSound \t= \"eat_food\";\n\t\n\t--> stats information <--\n\tactivationEffect = function(player)\n\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.health.Value > 0 then\n\t\t\tif (player.Character.PrimaryPart.mana.Value < player.Character.PrimaryPart.maxMana.Value or player.Character.PrimaryPart.health.Value < player.Character.PrimaryPart.maxHealth.Value) then\n\t\t\t\tnetwork:invoke(\"applyPotionStatusEffectToEntityManifest_server\", player.Character.PrimaryPart, 100, 100, \"item\", 6)\n\t\t\tend\n\t\t\t\n\t\t\treturn true, \"You feel refreshed.\"\n\t\tend\n\t\t\n\t\treturn false, \"Character is invalid.\"\n\tend;\n\t\n\t--> shop information <--\n\tbuyValue = 300;\n\tsellValue = 80;\t\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= true;\n\tcanAwaken \t= false;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"consumable\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/frying pan.lua",
    "content": "return {\n\tid = 295,\n\n\tname = \"Frying Pan\",\n\trarity = \"Common\",\n\timage = \"rbxassetid://4906325480\",\n\tdescription = \"You have to break a few eggs to make an omelette.\",\n\n\tisEquippable = true,\n\tequipmentSlot = 1,\n\tequipmentType = \"sword\",\n\n\tminLevel = 6;\n\n\t--> stats information <--\n\tbaseDamage = 7;\n\tattackSpeed = 3;\n\tbonusStats = {};\n\n\t--> shop information <--\n\tbuyValue = 1300;\n\tsellValue = 400;\n\n\n\tcanAwaken = true,\n\tcategory = \"equipment\",\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/health potion.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nreturn {\n\t--> identifying information <--\n\tid \t\t= 6;\n\t\n\t--> generic information <--\n\tname \t\t= \"Red Potion\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://2528902180\";\n\tdescription = \"A vibrant potion\";\n\t\n\tcustomTag = { \n\t\t{text = \"Restores\"; font = Enum.Font.SourceSans; textColor3 = Color3.fromRGB(160,160,160); textSize = 19; textTransparency = 0};\n\t\t{text = \"25 HP\"; font = Enum.Font.SourceSansBold; textColor3 = Color3.fromRGB(226,34,40); textSize = 19; textTransparency = 0};\n\t\t{text = \"after\"; font = Enum.Font.SourceSans; textColor3 = Color3.fromRGB(160,160,160); textSize = 19; textTransparency = 0};\n\t\t{text = \"1s\"; font = Enum.Font.SourceSansBold; textColor3 = Color3.fromRGB(160,160,160); textSize = 19; textTransparency = 0};\n\t};\n\t\n\titemType = \"potion\";\n\t\n\tuseSound \t= \"potion\";\n\t\n\t--> stats information <--\n\tactivationEffect = function(player)\n\t\tprint(\n\t\t\tplayer.Character,\n\t\t\tplayer.Character.PrimaryPart,\n\t\t\tplayer.Character.PrimaryPart.health.Value, \n\t\t\tplayer.Character.PrimaryPart.maxHealth.Value\n\t\t)\n\n\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.health.Value > 0 and player.Character.PrimaryPart.health.Value < player.Character.PrimaryPart.maxHealth.Value then\n\t\t\tlocal success = network:invoke(\"applyPotionStatusEffectToEntityManifest_server\", player.Character.PrimaryPart, 25, nil, \"item\", 6)\n\t\t\t\n\t\t\treturn success, success and \"You feel refreshed.\" or \"ERRORRRR\"\n\t\tend\n\t\t\n\t\treturn false, \"Character is invalid.\"\n\tend;\n\t\n\tstackSize = 32;\n\t\n\t--> shop information <--\n\tbuyValue = 50;\n\tsellValue = 20;\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= true;\n\tcanAwaken \t= false;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"consumable\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/hog headdress.lua",
    "content": "return {\n\tid = 291,\n\n\tname = \"Hog Headdress\",\n\trarity = \"Rare\",\n\timage = \"rbxassetid://4899219006\",\n\tdescription = \"Some hunters like to wear the remains of their enemies for good luck.\",\n\n\titemType = \"hat\";\n\n\tisEquippable = true,\n\tequipmentSlot = 2,\n\tequipmentType = \"hat\",\n\n\tperks = nil,\n\tmodifierData = {{maxHealth = 10; luck = 1;}},\n\n\tminimumClass = nil,\n\tminLevel = 15,\n\n\tcanAwaken = true,\n\tcategory = \"equipment\",\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/hog meat.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\n\nitem = {\n\t--> identifying information <--\n\tid \t\t= 144;\n\n\t--> generic information <--\n\tname \t\t= \"Hog Meat\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://3164395419\";\n\tdescription = \"Mmm, fresh hog. Restores 70 HP and boosts Attack for 3 minutes.\";\n\t\n\titemType = \"food\";\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= true;\n\tcanAwaken \t= false;\n\n--> shop information <--\t\n\tsellValue = 200; \n\tbuyValue  = 2000; \n\t\n\t--> stats information <--\n\tactivationEffect = function(player)\n\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.health.Value > 0 then\n\t\t\tlocal success = network:invoke(\"applyPotionStatusEffectToEntityManifest_server\", player.Character.PrimaryPart, 70, nil, \"item\", 6)\n\t\t\t\n\t\t\tif not success then return false, \"failed to give health\" end\n\t\t\t\n\t\t\tlocal wasApplied, reason = network:invoke(\"applyStatusEffectToEntityManifest\", player.Character.PrimaryPart, \"empower\", {\n\t\t\t\tduration = 3 * 60;\n\t\t\t\t\n\t\t\t\tmodifierData = {\n\t\t\t\t\tequipmentDamage = 5;\n\t\t\t\t};\n\t\t\t}, player.Character.PrimaryPart, \"item\", item.id)\n\t\t\t\n\t\t\treturn wasApplied, reason\n\t\tend\n\t\t\n\t\treturn false, \"Character is invalid.\"\n\tend;\n\t\n\tstackSize = 32;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"consumable\";\n}\n\nreturn item"
  },
  {
    "path": "src/ReplicatedStorage/itemData/hog tusk dagger.lua",
    "content": "return {\n\tid = 289,\n\t\n\tname = \"Hog Tusk Dagger\",\n\trarity = \"Rare\",\n\timage = \"rbxassetid://4899155150\",\n\tdescription = \"Not in a hog's face, but still used for stabbing.\",\n\t\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"dagger\";\n\tgripCFrame = CFrame.Angles(math.pi / 2, 0, 0) * CFrame.new(-0.15, 0, 0.83) * CFrame.Angles(0, math.pi, -math.pi/2);\n\tminLevel = 6;\n\t\n\t--> stats information <--\n\tbaseDamage = 4;\n\tattackSpeed = 5;\n\tmodifierData = {{criticalStrikeChance = 0.07}};\n\tcustomTag = {text = \"Inflicts Bleeding\"; font = Enum.Font.SourceSans; textSize = 19; textColor3 = Color3.fromRGB(212, 95, 91); textTransparency = 0} ;\n\t--> shop information <--\n\tbuyValue = 800;\n\tsellValue = 160;\n\t\t\n\t--> handling information <--\n\tcanStack = false;\n\tcanBeBound = false;\n\tcanAwaken = false;\n\t\n\t--> sorting information <--\n\tisImportant = false;\n\tcategory = \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/init.lua",
    "content": "--[[\n\titemData {}\n\t\t--> identifying information <--\n\t\tint id\n\t\t\n\t\t--> generic information <--\n\t\tstring name\n\t\tstring rarity\n\t\tstring image\n\t\tstring description\n\t\t\n\t\t--> tool information <--\n\t\tCFrame grip\n\t\t\n\t\t--> stats information <--\n\t\tnumber baseDamage\n\t\tbonusStats {}\n\t\t\tint str = 0\n\t\t\tint sta = 0\n\t\t\tint dex = 0\n\t\t\tint int = 0\n\t\t\t\n\t\t\tnumber cdrPercentage \t= 0\n\t\t\tnumber cdrFlat \t\t\t= 0\n\t\t\t\n\t\t\tnumber dmgPercent \t= 0\n\t\t\tnumber dmgFlat \t\t= 0\n\t\t\t\n\t\t\tnumber hpPercent \t= 0;\n\t\t\tnumber hpFlat \t\t= 0;\n\t\t\t\n\t\t\tnumber expPercent \t= 0;\n\t\t\tnumber expFlat \t\t= 0;\n\t\t\t\n\t\t\tnumber defenseFlat = nil;\n\t\t\n\t\t--> handling information <--\n\t\tbool canStack \t= true\n\t\tbool canBeBound = false\n\t\tbool canAwaken \t= false\n\t\t\n\t\t--> sorting information <--\n\t\tbool isImportant \t= false\n\t\tstring category \t= \"miscellaneous\" [\"equipment\", \"consumable\", \"miscellaneous\"]\n--]]\n\nlocal collectionService = game:GetService(\"CollectionService\")\n\n-- doing a direct require here to avoid a circular require problem\nlocal levels = require(game.ReplicatedStorage:WaitForChild(\"modules\"):WaitForChild(\"levels\"))\n\nlocal highest = 0\nlocal lookupTable = {} do\n\tfor i, itemDataModule in pairs(script:GetChildren()) do\n\t\t\n\t\t-- automatically fix up bad items\n\t\tif itemDataModule:FindFirstChild(\"container\") then\n\t\t\tfor i, part in pairs(itemDataModule.container:GetChildren()) do\n\t\t\t\tif part:IsA(\"BasePart\") then\n\t\t\t\t\tif part.Name == \"Head\" then\n\t\t\t\t\t\tpart.Transparency = 1\n\t\t\t\t\telse\n\t\t\t\t\t\tpart.Material = \"Glass\"\n\t\t\t\t\t\tpart.Transparency = 0.5\n\t\t\t\t\t\tpart.Reflectance = 0.2\n\t\t\t\t\t\tpart.Color = Color3.new(1,1,1)\t\t\t\t\t\t\n\t\t\t\t\tend\n\n\t\t\t\t\tif collectionService:HasTag(part, \"interact\") then\n\t\t\t\t\t\tcollectionService:RemoveTag(part, \"interact\")\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\t\t\n\t\tlocal itemData = require(itemDataModule)\n\t\t\n\t\tif itemData.category == \"equipment\" then\n\t\t\t\n\t\t\t-- determine the amount of an upgrades an item can have based on its rarity and time\n\t\t\tlocal maxUpgrades = 0\n\t\t\tif itemData.equipmentSlot == 1 or itemData.equipmentSlot == 8 then\n\t\t\t\tmaxUpgrades = 7\n\t\t\telseif itemData.equipmentSlot == 2 then\n\t\t\t\tmaxUpgrades = 3\n\t\t\tend\n\t\t\t\n\t\t\tif itemData.equipmentType == \"greatsword\" then\n--\t\t\t\titemData.minimumClass = \"paladin\"\n\t\t\telseif itemData.equipmentType == \"shield\" then\n--\t\t\t\titemData.minimumClass = \"knight\"\n\t\t\tend\n\t\t\t--[[\n\t\t\tlocal equipInfo = levels.getEquipmentInfo(itemData)\t\t\n\t\t\tif equipInfo then\t\n\t\t\t\tif equipInfo.cost then\n\t\t\t\t\titemData.buyValue = itemData.cost or math.ceil(equipInfo.cost * (itemData.valueMulti or 1))\n\t\t\t\t\titemData.sellValue = itemData.cost and itemData.cost * 0.2 or math.ceil(equipInfo.cost * 0.2)\n\t\t\t\tend\n\t\t\t\tif equipInfo.damage then\n\t\t\t\t\titemData.baseDamage = math.ceil(equipInfo.damage * (itemData.damageMulti or 1))\n\t\t\t\tend\n\t\t\t\tif equipInfo.defense then\n\t\t\t\t\titemData.modifierData = itemData.modifierData or {{}}\n\t\t\t\t\titemData.modifierData[1] = itemData.modifierData[1] or {}\n\t\t\t\t\titemData.modifierData[1].defense = (itemData.modifierData[1].defense or 0) + math.ceil(equipInfo.defense * (itemData.defenseMulti or 1))\n\t\t\t\tend\n\t\t\t\tif equipInfo.modifierData then\n\t\t\t\t\titemData.modifierData = itemData.modifierData or {}\n\t\t\t\t\ttable.insert(itemData.modifierData, equipInfo.modifierData)\n\t\t\t\tend\n\t\t\t\tif equipInfo.statUpgrade then\n\t\t\t\t\titemData.statUpgrade = itemData.statUpgrade or equipInfo.statUpgrade\n\t\t\t\tend\n\t\t\tend\n\t\t\t\n\t\t\tif itemData.perks then\n\t\t\t\t-- items with perks get blue tier by default\n\t\t\t\titemData.tier = itemData.tier or 2\n\t\t\tend\n\t\t\t]]\n\t\t\titemData.maxUpgrades = itemData.maxUpgrades or maxUpgrades\n\t\tend\n\t\t\n\t\t-- item categorization for icons\n\t\tlocal itemType = \"misc\"\n\t\tif itemData.category == \"equipment\" then\n\t\t\tif itemData.equipmentSlot == 1 then\n\t\t\t\titemType = itemData.equipmentType or \"sword\"\n\t\t\tend\n\t\telseif itemData.category == \"consumable\" then\n\t\t\tif itemData.applyScroll then\n\t\t\t\titemType = \"scroll\"\n\t\t\tend\n\t\tend\n\t\titemData.itemType = itemData.itemType or itemType\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\n--\t\tif itemData.category == \"equipment\" and itemData.canStack then\n--\t\t\terror(itemDataModule.Name .. \" should not be allowed to stack\")\n--\t\tend\n\t\t\n\t\tif itemDataModule:FindFirstChild(\"container\") and not itemDataModule.container.PrimaryPart then\n\t\t\titemDataModule.container.PrimaryPart = itemDataModule.container:FindFirstChildWhichIsA(\"BasePart\")\n\t\tend\n\t\t\n\t\t-- internal stuff\n\t\titemData.module = itemDataModule\n\t\t\n\t\t-- hook ups, check for conflicts..\n\t\tif lookupTable[itemData.id] then\n\t\t\twarn(\"CONFLICT OF ITEM IDS @\", itemData.id, itemData.name, lookupTable[itemData.id].name)\n\t\tend\n\t\t\n\t\tlookupTable[itemData.id] \t\t\t= itemData\n\t\tlookupTable[itemDataModule.Name] \t= itemData\n\t\t\n\t\tif itemData.id > highest then\n\t\t\thighest = itemData.id\n\t\tend\n\t\t\n\tend\nend\n\t\t\nprint(\"HIGHEST ID >>>\", highest)\n\nreturn lookupTable"
  },
  {
    "path": "src/ReplicatedStorage/itemData/iron ore.lua",
    "content": "return {\n\tid = 288,\n\t\n\tname = \"Iron Ore\",\n\trarity = \"Common\",\n\timage = \"rbxassetid://4849612961\",\n\tdescription = \"Rock rich in iron. Somehow the exposed metal hasn't rusted.\",\n\t\n\tcanStack = true,\n\t\n\tsellValue = 30,\n\tbuyValue = 150,\n\t\n\tisImportant = false,\n\tcategory = \"miscellaneous\",\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/item lore.lua",
    "content": "-- prevents requiring the modules outside of runtime\n-- a problem for future me to deal with\n\n--local disallowedWhiteSpace = {\"\\n\", \"\\r\", \"\\t\", \"\\v\", \"\\f\"}\n\n--local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\n--local network = modules.load(\"network\")\n\nreturn {\n\t--> identifying information <--\n\tid \t\t= 104;\n\t\n\t--> generic information <--\n\tname \t\t= \"Ink & Quill\";\n\tnameColor\t= Color3.fromRGB(237, 98, 255);\n\trarity \t\t= \"Legendary\";\n\timage \t\t= \"rbxassetid://2856319694\";\n\tdescription = \"Add a line of custom lore description to your equipment.\";\n\t\n\t--> stats information <--\n\tsuccessRate = 1;\n\tupgradeCost = 0;\n\tplayerInputFunction = function()\n\t\tlocal playerInput = {}\n\t\tplayerInput.desiredName = network:invoke(\"textInputPrompt\", {prompt = \"Write your item's lore...\"})\n\t\treturn playerInput\n\tend;\n\tapplyScroll = function(player, itemInventorySlotData, successfullyScrolled, playerInput)\n\t\tlocal itemLookup = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"itemData\"))\n\t\tlocal itemBaseData = itemLookup[itemInventorySlotData.id]\n\t\t\n\t\tif not playerInput then \n\t\t\treturn false, \"no player input\"\n\t\tend\n\t\t\n\t\tif itemBaseData.category == \"equipment\" then\n\t\t\tif successfullyScrolled then\n\t\t\t\tlocal desiredName = playerInput.desiredName\n\n\t\t\t\t\n\t\t\t\tif not desiredName then\n\t\t\t\t\treturn false, \"desired item story not provided\"\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\tif #desiredName > 40 then\n\t\t\t\t\treturn false, \"Item story cannot be longer than 40 characters\"\n\t\t\t\tend\n\n\t\t\t\t-- eliminate white space\t\t\t\t\n\t\t\t\tfor i,whitespace in pairs(disallowedWhiteSpace) do\n\t\t\t\t\tif string.find(desiredName, whitespace) then\n\t\t\t\t\t\treturn false, \"Item story cannot contain whitespace characters.\"\n\t\t\t\t\tend\n\t\t\t\tend\t\t\t\t\n\t\t\t\t\n\t\t\t\tif #desiredName < 3 then\n\t\t\t\t\treturn false, \"Item story must be at least 3 characters long.\"\n\t\t\t\tend\t\t\t\t\n\t\t\t\t\n\t\t\t\tlocal filteredText\n\t\t\t\t\n\t\t\t\tlocal filterSuccess, filterError = pcall(function()\n\t\t\t\t\tfilteredText = game.Chat:FilterStringForBroadcast(desiredName, player)\n\t\t\t\tend)\n\t\t\t\t\n\t\t\t\tif not filterSuccess then\n\t\t\t\t\treturn false, \"filter error: \"..filterError\n\t\t\t\tend\t\t\n\t\t\t\t\n\t\t\t\tif not filteredText or string.find(filteredText, \"#\") then\n\t\t\t\t\treturn false, \"Item story rejected by Roblox filter.\"\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\t-- if we've gotten this far, we must be good\n\t\t\t\titemInventorySlotData.customStory = filteredText\n\t\t\t\treturn true, \"Item story applied.\"\n\t\t\t\t\n\t\t\t\t\t\n\t\t\tend\n\t\tend\n\t\t\n\t\treturn false, \"Only equipment can be given a story.\"\n\tend;\n\t\n\t--> shop information <--\n\tbuyValue = 1000000;\n\tsellValue = 1000;\n\t\n\t--> handling information <--\n\tcanStack \t\t\t= false;\n\tcanBeBound \t\t\t= false;\n\tcanAwaken \t\t\t= false;\n\tenchantsEquipment \t= true;\n\t\n\t--> sorting information <--\n\tisImportant \t= true;\n\tcategory \t\t= \"consumable\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/item renamer.lua",
    "content": "-- prevents requiring the modules outside of runtime\n-- a problem for future me to deal with\n\n\n--local disallowedWhiteSpace = {\"\\n\", \"\\r\", \"\\t\", \"\\v\", \"\\f\"}\n\n--local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\n--local network = modules.load(\"network\")\n\nreturn {\n\t--> identifying information <--\n\tid \t\t= 103;\n\t\n\t--> generic information <--\n\tname \t\t= \"Name Tag\";\n\tnameColor\t= Color3.fromRGB(237, 98, 255);\n\trarity \t\t= \"Legendary\";\n\timage \t\t= \"rbxassetid://2858147882\";\n\tdescription = \"Give a piece of equipment a special name.\";\n\t\n\t--> stats information <--\n\tsuccessRate = 1;\n\tupgradeCost = 0;\n\tplayerInputFunction = function()\n\t\tlocal playerInput = {}\n\t\tplayerInput.desiredName = network:invoke(\"textInputPrompt\", {prompt = \"Name your item...\"})\n\t\treturn playerInput\n\tend;\n\tapplyScroll = function(player, itemInventorySlotData, successfullyScrolled, playerInput)\n\t\tlocal itemLookup = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"itemData\"))\n\t\tlocal itemBaseData = itemLookup[itemInventorySlotData.id]\n\t\t\n\t\tif not playerInput then \n\t\t\treturn false, \"no player input\"\n\t\tend\n\t\t\n\t\tif itemBaseData.category == \"equipment\" then\n\t\t\tif successfullyScrolled then\n\t\t\t\tlocal desiredName = playerInput.desiredName\n\n\t\t\t\tif not desiredName then\n\t\t\t\t\treturn false, \"desired item name not provided\"\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\tif #desiredName > 30 then\n\t\t\t\t\treturn false, \"Item name cannot be longer than 30 characters.\"\n\t\t\t\tend\n\n\t\t\t\t-- eliminate white space\t\t\t\t\n\t\t\t\tfor i,whitespace in pairs(disallowedWhiteSpace) do\n\t\t\t\t\tif string.find(desiredName, whitespace) then\n\t\t\t\t\t\treturn false, \"Item name cannot contain whitespace characters.\"\n\t\t\t\t\tend\n\t\t\t\tend\t\t\t\t\n\t\t\t\t\n\t\t\t\tif #desiredName < 3 then\n\t\t\t\t\treturn false, \"Item name must be at least 3 characters long.\"\n\t\t\t\tend\t\t\t\t\n\t\t\t\t\n\t\t\t\tlocal filteredText\n\t\t\t\t\n\t\t\t\tlocal filterSuccess, filterError = pcall(function()\n\t\t\t\t\tfilteredText = game.Chat:FilterStringForBroadcast(desiredName, player)\n\t\t\t\tend)\n\t\t\t\t\n\t\t\t\tif not filterSuccess then\n\t\t\t\t\treturn false, \"filter error: \"..filterError\n\t\t\t\tend\t\t\n\t\t\t\t\n\t\t\t\tif not filteredText or string.find(filteredText, \"#\") then\n\t\t\t\t\treturn false, \"Item name rejected by Roblox filter.\"\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\t-- if we've gotten this far, we must be good\n\t\t\t\titemInventorySlotData.customName = filteredText\n\t\t\t\treturn true, \"Item renamed\"\n\t\t\t\t\n\t\t\t\t\t\n\t\t\tend\n\t\tend\n\t\t\n\t\treturn false, \"Only equipment can be re-named.\"\n\tend;\n\t\n\t--> shop information <--\n\tbuyValue = 2000000;\n\tsellValue = 1000;\n\t\n\t--> handling information <--\n\tcanStack \t\t\t= false;\n\tcanBeBound \t\t\t= false;\n\tcanAwaken \t\t\t= false;\n\tenchantsEquipment \t= true;\n\t\n\t--> sorting information <--\n\tisImportant \t= true;\n\tcategory \t\t= \"consumable\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/leather tunic.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 205;\n\t\n\t--> generic information <--\n\tname = \"Leather Tunic\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://4054263398\";\n\tdescription = \"A small tunic made of rough leather. Any protection is better than nothing.\";\n\t\n\titemType = \"armor\";\n\t\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 8;\n\tequipmentType = \"armor\";\n\t\n\tminLevel = 1;\n\t\n\t--> stats information <--\n\tmodifierData = {{defense = 1;}};\n\n\t--> shop information <--\n\tbuyValue = 50;\n\tsellValue = 40;\t\n\t\t\t\n\t--> handling information <--\n\tcanStack = false;\n\tcanBeBound = false;\n\tcanAwaken = true;\n\t\n\t\n\t--> sorting information <--\n\tisImportant = false;\n\tcategory = \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/mana potion.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nreturn {\n\t--> identifying information <--\n\tid \t\t= 22;\n\t\n\t--> generic information <--\n\tname \t\t= \"Blue Potion\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://2528903811\";\n\tdescription = \"A magical potion\";\n\t\n\tcustomTag = { \n\t\t{text = \"Restores\"; font = Enum.Font.SourceSans; textColor3 = Color3.fromRGB(160,160,160); textSize = 19; textTransparency = 0};\n\t\t{text = \"25 MP\"; font = Enum.Font.SourceSansBold; textColor3 = Color3.fromRGB(0, 152, 255); textSize = 19; textTransparency = 0};\n\t\t{text = \"after\"; font = Enum.Font.SourceSans; textColor3 = Color3.fromRGB(160,160,160); textSize = 19; textTransparency = 0};\n\t\t{text = \"1s\"; font = Enum.Font.SourceSansBold; textColor3 = Color3.fromRGB(160,160,160); textSize = 19; textTransparency = 0};\n\t};\t\n\t\n\titemType = \"potion\";\n\t\n\tuseSound \t= \"potion\";\n\t\n\t--> stats information <--\n\tactivationEffect = function(player)\n\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.mana.Value < player.Character.PrimaryPart.maxMana.Value then\n\t\t\tlocal success, reason = network:invoke(\"applyPotionStatusEffectToEntityManifest_server\", player.Character.PrimaryPart, nil, 50, \"item\", 22)\t\t\t\n\t\t\t\n\t\t\treturn success, success and \"You feel recharged.\" or reason\n\t\tend\n\t\t\n\t\treturn false, \"Character is invalid.\"\n\tend;\n\t\n\tstackSize = 32;\n\t\n\t--> shop information <--\n\tbuyValue = 70;\n\tsellValue = 30;\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= true;\n\tcanAwaken \t= false;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"consumable\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/megaphone.lua",
    "content": "-- prevents requiring the modules outside of runtime\n-- a problem for future me to deal with\n\n--local disallowedWhiteSpace = {\"\\n\", \"\\r\", \"\\t\", \"\\v\", \"\\f\"}\n\n--local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\n--local network = modules.load(\"network\")\n\nitem = {\n\t--> identifying information <--\n\tid \t\t= 135;\n\t\n\t--> generic information <--\n\tname \t\t= \"Megaphone\";\n\tnameColor\t= Color3.fromRGB(237, 98, 255);\n\trarity \t\t= \"Legendary\";\n\timage \t\t= \"rbxassetid://3109212291\";\n\tdescription = \"Send a message to all online Vesteria players.\";\n--\tsoulbound = true;\n\ttier = 2;\n\t\n\tuseSound \t= \"potion\";\n\t\n\tconsumeTime = 0;\n\t\n\tplayerInputFunction = function()\n\t\tlocal playerInput = {}\n\t\tplayerInput.desiredName = network:invoke(\"textInputPrompt\", {prompt = \"Enter shout message...\"})\n\t\treturn playerInput\n\tend;\t\n\n\tactivationEffect = function(player, playerInput)\n\t\tif game.ReplicatedStorage:FindFirstChild(\"doNotSaveData\") then\n\t\t\treturn false, \"No shouting in testing realms\"\n\t\tend\n\n\t\tif not playerInput then\n\t\t\treturn false, \"No player input recieved\"\n\t\tend\n\n\t\tlocal desiredName = playerInput.desiredName\n\n\t\tif not desiredName then\n\t\t\treturn false, \"Desired shout not provided\"\n\t\tend\n\t\t\n\t\tif #desiredName > 100 then\n\t\t\treturn false, \"Shout cannot be longer than 100 characters.\"\n\t\tend\n\n\t\t-- eliminate white space\t\t\t\t\n\t\tfor i,whitespace in pairs(disallowedWhiteSpace) do\n\t\t\tif string.find(desiredName, whitespace) then\n\t\t\t\treturn false, \"Shout cannot contain whitespace characters.\"\n\t\t\tend\n\t\tend\t\t\t\t\n\t\t\n\t\tif #desiredName < 3 then\n\t\t\treturn false, \"Shout must be at least 3 characters long.\"\n\t\tend\t\t\t\t\n\t\t\n\t\tlocal filteredText\n\t\t\n\t\tlocal filterSuccess, filterError = pcall(function()\n\t\t\tfilteredText = game.Chat:FilterStringForBroadcast(desiredName, player)\n\t\tend)\n\t\t\n\t\tif not filterSuccess then\n\t\t\treturn false, \"filter error: \"..filterError\n\t\tend\t\t\n\t\t\n\t\tif not filteredText or string.find(filteredText, \"#\") then\n\t\t\treturn false, \"Shout rejected by Roblox filter: \\\"\"..filteredText..\"\\\"\"\n\t\tend\n\t\t\n\t\t-- if we've gotten this far, we must be good\n\t\tlocal messageSuccess, errMsg = pcall(function()\n\t\t\tgame:GetService(\"MessagingService\"):PublishAsync(\"megaphone\", \"[\"..player.Name..\"'s shout] \" .. filteredText)\n\t\t\twarn(\"MESSAGE SENT!!!\")\n\t\tend)\n\t\t\n\t\tif not messageSuccess then\n\t\t\treturn false, \"MessagingService error: \"..errMsg\n\t\tend\n\n\t\treturn true, \"Shout sent!\"\n\t\t\t\t\n\tend;\n\t\n\n\t\n\t--> shop information <--\n\tbuyValue = 2500;\n\tsellValue = 400;\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= true;\n\tcanAwaken \t= false;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"consumable\";\n}\n\nreturn item"
  },
  {
    "path": "src/ReplicatedStorage/itemData/mushroom beard.lua",
    "content": "\nreturn {\n\t--> identifying information <--\n\tid \t\t= 10;\n\n\t--> generic information <--\n\tname \t\t= \"Elder Beard\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://2528901939\";\n\tdescription = \"A beard stolen from an Elder Shroom\";\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= false;\n\tcanAwaken \t= false;\n\t\n\t--> shop information <--\t\n\t\tsellValue = 20;\t\n\t\tbuyValue = 100;\t\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"miscellaneous\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/mushroom hat.lua",
    "content": "return {\n\t--> identifying information <--\n\tid \t\t= 43;\n\t\n\t--> generic information <--\n\tname \t\t= \"Mushroom Hat\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://2539157198\";\n\tdescription = \"A unique mushroom-shaped hat found in the Mushroom Forest.\";\n\t\n\titemType = \"hat\";\n\t\n\t--> equipment information <--\n\tisEquippable \t= true;\n\tequipmentSlot \t= 2;\n\tequipmentType \t= \"hat\";\n\tequipmentHairType = 2;\n\t\n\tminLevel\t\t= 5;\n\t\n\t--> shop information <--\t\n\tbuyValue = 5000;\n\tsellValue = 1000;\t\t\t\n\t\n\t--> stats information <--\n\tmodifierData = {{jump = 10;}};\n\n\tstatUpgrade = {\n\t\tjump = 1;\n\t};\n\n\t--> handling information <--\n\tcanStack \t= false;\n\tcanBeBound \t= false;\n\tcanAwaken \t= true;\n\tclipsHair \t= true;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/mushroom mini.lua",
    "content": "\nreturn {\n\t--> identifying information <--\n\tid \t\t= 11;\n\n\t--> generic information <--\n\tname \t\t= \"Red Mushroom\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://2528902146\";\n\tdescription = \"A little red mushroom dropped by a felled Shroom\";\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= false;\n\tcanAwaken \t= false;\n\t\n\t--> shop information <--\t\n\t\tsellValue = 15;\t\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"miscellaneous\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/mushroom soup.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nreturn {\n\t--> identifying information <--\n\tid \t\t= 131;\n\n\t--> generic information <--\n\tname \t\t= \"Mushroom Soup\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://3164341819\";\n\tdescription = \"A hearty bowl of delicious mushroom soup. Fully recovers all HP and MP.\";\n\t\n\titemType = \"food\";\n\t\n\tuseSound \t= \"potion\";\n\ttier=3;\n\t\n\t--> stats information <--\n\tactivationEffect = function(player)\n\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.health.Value > 0  then\n\t\t\tlocal success = network:invoke(\"applyPotionStatusEffectToEntityManifest_server\", player.Character.PrimaryPart,\n\t\t\t\t player.Character.PrimaryPart.maxHealth.Value - player.Character.PrimaryPart.health.Value, \n\t\t\t\t player.Character.PrimaryPart.maxMana.Value - player.Character.PrimaryPart.mana.Value, \n\t\t\t\t\"item\", 6)\n\t\t\t\n\t\t\treturn success, \"You feel refreshed.\"\n\t\tend\n\t\t\n\t\treturn false, \"Character is invalid.\"\n\tend;\n\t\n\t--> shop information <--\n\tbuyValue = 50000;\n\tsellValue = 5000;\t\n\t\n\tstackSize = 8;\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= true;\n\tcanAwaken \t= false;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"consumable\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/mushroom spore.lua",
    "content": "\nreturn {\n\t--> identifying information <--\n\tid \t\t= 9;\n\n\t--> generic information <--\n\tname \t\t= \"Mushroom Spore\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://2538834712\";\n\tdescription = \"A clump of spores dropped by a defeated Baby Shroom\";\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= false;\n\tcanAwaken \t= false;\n\n--> shop information <--\t\n\tsellValue = 10;\n\tbuyValue = 100;\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"miscellaneous\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/oak axe.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 401;\n\n\t--> generic information <--\n\tname = \"Oak Axe\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://5344158782\";\n\tdescription = \"A shoddy wooden axe that still gets the job done.\";\n\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"axe\";\n\tgripCFrame = CFrame.Angles(math.pi / 2, 0, 0) * CFrame.new(0, -0.2, 2);\n\tminLevel = 1;\n\n\t--> stats information <--\n\tbaseDamage = 2;\n\n\tmodifierData = {{woodcutting = 3}};\n\n\t--> crafting information <--\n\trecipe = {\n\t\t-- oak wood\n\t\t{id = 2, stacks = 10}\n\t};\n\n\t--> shop information <--\n\tbuyValue = 200;\n\tsellValue = 150;\n\n\t--> handling information <--\n\tcanStack = false;\n\tcanBeBound = false;\n\tcanAwaken = true;\n\n\t--> sorting information <--\n\tisImportant = false;\n\tcategory = \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/oak pickaxe.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 501;\n\n\t--> generic information <--\n\tname = \"Oak Pickaxe\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://5344159105\";\n\tdescription = \"A shoddy wooden pickaxe that still gets the job done.\";\n\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"pickaxe\";\n\tgripCFrame = CFrame.Angles(math.pi / 2, 0, 0) * CFrame.new(0, -0.2, 2);\n\tminLevel = 1;\n\n\t--> stats information <--\n\tbaseDamage = 2;\n\n\tmodifierData = {{mining = 3}};\n\n\t--> crafting information <--\n\trecipe = {\n\t\t-- oak wood\n\t\t{id = 2, stacks = 10}\n\t};\n\n\t--> shop information <--\n\tbuyValue = 200;\n\tsellValue = 150;\n\n\t--> handling information <--\n\tcanStack = false;\n\tcanBeBound = false;\n\tcanAwaken = true;\n\n\t--> sorting information <--\n\tisImportant = false;\n\tcategory = \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/oak polearm.lua",
    "content": "return {\n\t--> identifying information <--\n\tid \t\t= 13;\n\n\t--> generic information <--\n\tname = \"Oak Polearm\";\n\trarity = \"Common\";\n\t--\n\timage = \"rbxassetid://2528901964\";\n\tdescription = \"A sturdy polearm made from great oak. Great at felling large groups of monsters.\";\n\t\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"greatsword\";\n\tgripCFrame = CFrame.Angles(math.pi / 1, 0, math.pi) * CFrame.new(-.45, 0, 1.6) * CFrame.Angles(0, 0, math.pi / 2);\n\tminLevel = 6;\n\t\n\t--> stats information <--\n\tbaseDamage = 11;\n\tattackSpeed = 2;\n\tbonusStats = {};\n\t\n\t--> shop information <--\n\tbuyValue = 1500;\n\tsellValue = 450;\t\t\n\t\n\t--> handling information <--\n\tcanStack = false;\n\tcanBeBound = false;\n\tcanAwaken = true;\n\t\n\t--> sorting information <--\n\tisImportant = false;\n\tcategory = \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/oak wood.lua",
    "content": "\nreturn {\n\t--> identifying information <--\n\tid = 2;\n\n\t--> generic information <--\n\tname = \"Oak Wood\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://5319820628\";\n\tdescription = \"The oak is well-known for its sturdy wood that is used in many recipes.\";\n\n\t-- todo: wood item type icon\n--\titemType = \"wood\";\n\n\t--> handling information <--\n\tcanStack = true;\n\n\t--> shop information <--\n\tsellValue = 100;\n\tbuyValue = 1000;\n\n\t--> sorting information <--\n\tcategory = \"miscellaneous\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/pear.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nitem = {\n\t--> identifying information <--\n\tid \t\t= 253;\n\n\t--> generic information <--\n\tname \t\t= \"Pear\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://2661683979\";\n\tdescription = \"A ripe pear bustling with flavor.\";\n\t\n\titemType = \"food\";\n\n\tuseSound \t= \"eat_food\";\n\t\n\t--> stats information <--\n\tactivationEffect = function(player)\n\t\tif player.Character then\n\t\t\tlocal success = network:invoke(\"applyPotionStatusEffectToEntityManifest_server\", player.Character.PrimaryPart, 50, nil, \"item\", 226)\n\t\t\tlocal wasApplied, reason = network:invoke(\"applyStatusEffectToEntityManifest\", player.Character.PrimaryPart, \"empower\", {\n\t\t\t\tduration = 60;\n\t\t\t\t\n\t\t\t\tmodifierData = {\n\t\t\t\t\tstamina = 1;\n\t\t\t\t};\n\t\t\t}, player.Character.PrimaryPart, \"item\", item.id)\t\t\t\n\t\t\t\n\t\t\treturn wasApplied and \"You feel refreshed.\" or \"ERRORRRR\"\n\t\tend\n\t\t\n\t\treturn false, \"Character is invalid.\"\n\tend;\n\t\n\tconsumeTime = 1;\n\tstackSize = 44;\n\t\n\t--> shop information <--\n\tbuyValue = 500;\n\tsellValue = 100;\t\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= true;\n\tcanAwaken \t= false;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"consumable\";\n}\n\nreturn item"
  },
  {
    "path": "src/ReplicatedStorage/itemData/rune mushtown.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nreturn {\n\t--> identifying information <--\n\tid \t\t= 90;\n\t\n\t--> generic information <--\n\tname \t\t= \"Mushtown Rune\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://2747737875\";\n\tdescription = \"A magical gemstone that can be used to return to Mushtown.\";\n\t\n\tuseSound \t= \"fireIgnite\";\n\t\n\tconsumeTime = 0;\n\t\n\t--> stats information <--\n\taskForConfirmationBeforeConsume = true;\t\n\tactivationEffect \t\t\t\t= function(player)\n\t\tif player:FindFirstChild(\"teleportRune\") == nil  and player:FindFirstChild(\"teleporting\") == nil and player:FindFirstChild(\"DataSaveFail\") == nil then\n\t\t\t\n\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\ttag.Name = \"teleportRune\"\n\t\t\ttag.Parent = player\t\t\t\n\t\t\t\n\t\t\tif game.GameId == 712031239 then\n\t\t\t\t-- demo\n\t\t\t\tdelay(0.2, function() network:invoke(\"teleportPlayer_rune\", player, 4041449372) end)\n\t\t\telse\n\t\t\t\tdelay(0.2, function() network:invoke(\"teleportPlayer_rune\", player, 2064647391) end)\n\t\t\tend\n\t\t\treturn true, \"teleport queued\"\n\t\tend\n\t\t\n\t\treturn false, \"Character is invalid.\"\n\tend;\n\t\n\t--> shop information <--\n\tbuyValue = 4000;\n\tsellValue = 300;\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= true;\n\tcanAwaken \t= false;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"consumable\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/rune nilgarf.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nreturn {\n\t--> identifying information <--\n\tid \t\t= 149;\n\t\n\t--> generic information <--\n\tname \t\t= \"Nilgarf Rune\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://3185379989\";\n\tdescription = \"A magical gemstone that can be used to return to Nilgarf.\";\n\t\n\tuseSound \t= \"fireIgnite\";\n\t\n\tconsumeTime = 0;\n\t\n\t--> stats information <--\n\taskForConfirmationBeforeConsume = true;\t\n\tactivationEffect \t\t\t\t= function(player)\n\t\tif player:FindFirstChild(\"teleportRune\") == nil  and player:FindFirstChild(\"teleporting\") == nil and player:FindFirstChild(\"DataSaveFail\") == nil then\n\t\t\t\n\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\ttag.Name = \"teleportRune\"\n\t\t\ttag.Parent = player\t\t\t\n\t\t\t\n\t\t\tif game.GameId == 712031239 then\n\t\t\t\t-- demo\n\t\t\t\tdelay(0.2, function() network:invoke(\"teleportPlayer_rune\", player, 4042577479) end)\n\t\t\telse\n\t\t\t\tdelay(0.2, function() network:invoke(\"teleportPlayer_rune\", player, 2119298605) end)\n\t\t\tend\n\t\t\treturn true, \"teleport queued\"\n\t\tend\n\t\t\n\t\treturn false, \"Character is invalid.\"\n\tend;\t\n\t\n\t--> shop information <--\n\tbuyValue = 4000;\n\tsellValue = 300;\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= true;\n\tcanAwaken \t= false;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"consumable\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/rusty dagger.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 27;\n\t\n\t--> generic information <--\n\tname = \"Rusty Dagger\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://2528902431\";\n\tdescription = \"Give your enemies little tetanus kisses.\";\n\t\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"dagger\";\n\tgripCFrame = CFrame.Angles(math.pi / 2, 0, 0) * CFrame.new(-0.15, 0, 0.83) * CFrame.Angles(0, math.pi, -math.pi/2);\n\tminLevel = 6;\n\t\n\t--> stats information <--\n\tbaseDamage = 4;\n\tattackSpeed = 5;\n\tmodifierData = {{criticalStrikeChance = 0.08}};\n\t--> shop information <--\n\tbuyValue = 1500;\n\tsellValue = 600;\n\t\t\n\t--> handling information <--\n\tcanStack = false;\n\tcanBeBound = false;\n\tcanAwaken = false;\n\t\n\t--> sorting information <--\n\tisImportant = false;\n\tcategory = \"equipment\";\n}\n"
  },
  {
    "path": "src/ReplicatedStorage/itemData/shoulder pads 2.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 25;\n\n\t--> generic information <--\n\tname = \"Oak Shoulder Pads\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://2528902858\";\n\tdescription = \"Wooden protection that's quite effective against soft targets.\";\n\n\titemType = \"armor\";\n\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 8;\n\tequipmentType = \"armor\";\n\n\tminLevel = 3;\n\n\t--> stats information <--\n\tmodifierData = {{defense = 2;}};\n\n\t--> shop information <--\n\tbuyValue = 400;\n\tsellValue = 200;\n\n\t--> handling information <--\n\tcanStack = false;\n\tcanBeBound = false;\n\tcanAwaken = true;\n\n\t--> sorting information <--\n\tisImportant = false;\n\tcategory = \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/shoulder pads 3.lua",
    "content": "return {\n\t--> identifying information <--\n\tid \t\t= 280;\n\n\t--> generic information <--\n\tname \t\t= \"Pots and Pans\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://4778835694\";\n\tdescription = \"It might not be metal plate, but you're getting there.\";\n\n\titemType = \"armor\";\n\n\t--> equipment information <--\n\tisEquippable \t= true;\n\tequipmentSlot \t= 8;\n\tequipmentType \t= \"armor\";\n\n\tminLevel = 5;\n\t--> stats information <--\n\tmodifierData = {{defense = 3;}};\n\n\t--> shop information <--\n\tbuyValue = 2000;\n\tsellValue = 400;\n\n\n\t--> handling information <--\n\tcanStack \t= false;\n\tcanBeBound \t= false;\n\tcanAwaken \t= true;\n\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/stick.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 5;\n\t\n\t--> generic information <--\n\tname = \"Stick\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://2528902878\";\n\tdescription = \"A particularly crisp stick.\";\n\t\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"sword\";\n\tgripCFrame = CFrame.Angles(math.pi / 2, 0, 0) * CFrame.new(0, -0.2, 2);\n\tminLevel = 1;\n\t\n\t--> stats information <--\n\tbaseDamage = 1;\n\tattackSpeed = 3;\n\tbonusStats = {};\n\t\n\tbuyValue = 100;\n\tsellValue = 20;\n\t\n\t--> handling information <--\n\tcanStack \t= false;\n\tcanBeBound \t= false;\n\tcanAwaken \t= true;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/stone axe.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 402;\n\n\t--> generic information <--\n\tname = \"Stone Axe\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://5344159446\";\n\tdescription = \"A sharp stone axe built on a sturdier oak frame.\";\n\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"axe\";\n\tgripCFrame = CFrame.Angles(math.pi / 2, 0, 0) * CFrame.new(0, -0.2, 2);\n\tminLevel = 3;\n\n\t--> stats information <--\n\tbaseDamage = 4;\n\tmodifierData = {{woodcutting = 5}};\n\n\t--> crafting information <--\n\trecipe = {\n\t\t-- oak wood\n\t\t{id = 2, stacks = 20},\n\t\t-- stone\n\t\t{id = 600, stacks = 20}\n\t};\n\n\t--> shop information <--\n\tbuyValue = 500;\n\tsellValue = 300;\n\n\t--> handling information <--\n\tcanStack = false;\n\tcanBeBound = false;\n\tcanAwaken = true;\n\n\t--> sorting information <--\n\tisImportant = false;\n\tcategory = \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/stone pickaxe.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 502;\n\n\t--> generic information <--\n\tname = \"Stone Pickaxe\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://5344159226\";\n\tdescription = \"A sharp stone pickaxe built on a sturdier oak frame.\";\n\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"pickaxe\";\n\tgripCFrame = CFrame.Angles(math.pi / 2, 0, 0) * CFrame.new(0, -0.2, 2);\n\tminLevel = 3;\n\n\t--> stats information <--\n\tbaseDamage = 4;\n\tmodifierData = {{mining = 5}};\n\n\t--> crafting information <--\n\trecipe = {\n\t\t-- oak wood\n\t\t{id = 2, stacks = 20},\n\t\t-- stone\n\t\t{id = 600, stacks = 20}\n\t};\n\n\t--> shop information <--\n\tbuyValue = 500;\n\tsellValue = 300;\n\n\t--> handling information <--\n\tcanStack = false;\n\tcanBeBound = false;\n\tcanAwaken = true;\n\n\t--> sorting information <--\n\tisImportant = false;\n\tcategory = \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/stone.lua",
    "content": "\nreturn {\n\t--> identifying information <--\n\tid = 600;\n\n\t--> generic information <--\n\tname = \"Stone\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://5431762747\";\n\tdescription = \"A chunk of rock that is used in many recipes.\";\n\n\t-- todo: wood item type icon\n--\titemType = \"wood\";\n\n\t--> handling information <--\n\tcanStack = true;\n\n\t--> shop information <--\n\tsellValue = 100;\n\tbuyValue = 1000;\n\n\t--> sorting information <--\n\tcategory = \"miscellaneous\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/wooden bow.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 83;\n\t\n\t--> generic information <--\n\tname = \"Oak Bow\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://2744225022\";\n\tdescription = \"For those who prefer to fight from afar.\";\n\t\n\tmodifierData = {};\n\t\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"bow\";\n\tprojectileSpeed = 170;\n\t\n\tminLevel = 4;\n\t\n\t--> stats information <--\n\tbaseDamage = 4;\n\tattackSpeed = 2;\n\tbonusStats = {};\n\t\n\t--> shop information <--\n\tbuyValue = 900;\n\tsellValue = 160;\n\t\n\t--> handling information <--\n\tcanStack \t= false;\n\tcanBeBound \t= false;\n\tcanAwaken \t= false;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"equipment\";\n}\n"
  },
  {
    "path": "src/ReplicatedStorage/itemData/wooden club.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 7;\n\t\n\t--> generic information <--\n\tname = \"Oak Club\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://2528902806\";\n\tdescription = \"A heavy wooden club\";\n\t\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"sword\";\n\tgripCFrame = CFrame.Angles(math.pi / 2, 0, 0) * CFrame.new(0, -0.2, 2);\n\tminLevel = 2;\n\t\n\t--> stats information <--\n\tbaseDamage = 3;\n\tattackSpeed = 3;\n\tbonusStats = {};\n\t\n\t--> shop information <--\n\tbuyValue = 200;\n\tsellValue = 150;\t\t\n\t\n\t--> handling information <--\n\tcanStack = false;\n\tcanBeBound = false;\n\tcanAwaken = true;\n\t\n\t--> sorting information <--\n\tisImportant = false;\n\tcategory = \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/wooden dagger.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 15;\n\t\n\t--> generic information <--\n\tname = \"Oak Dagger\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://2528902839\";\n\tdescription = \"A small wooden blade that can be swung very quickly.\";\n\t\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"dagger\";\n\tgripCFrame = CFrame.Angles(math.pi / 2, 0, 0) * CFrame.new(-0.15, 0, 0.83) * CFrame.Angles(0, math.pi, -math.pi/2);\n\tminLevel = 4;\n\t\n\t--> stats information <--\n\tbaseDamage = 3;\n\tattackSpeed = 5;\n\tmodifierData = {{criticalStrikeChance = 0.05}};\n\t--> shop information <--\n\tbuyValue = 700;\n\tsellValue = 160;\n\t\t\n\t--> handling information <--\n\tcanStack = false;\n\tcanBeBound = false;\n\tcanAwaken = false;\n\t\n\t--> sorting information <--\n\tisImportant = false;\n\tcategory = \"equipment\";\n}\n"
  },
  {
    "path": "src/ReplicatedStorage/itemData/wooden fishing pole.lua",
    "content": "return {\n\t--> identifying information <--\n\tid \t\t= 37;\n\t\n\t--> generic information <--\n\tname \t\t= \"Old Fishing Rod\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://2544830441\";\n\tdescription = \"An ancient rod once used to catch fish.\";\n\t\n\t--> equipment information <--\n\tisEquippable \t= true;\n\tequipmentSlot \t= 1;\n\tequipmentType \t= \"fishingrod\";\n\tgripCFrame \t\t= CFrame.Angles(math.pi / 2, 0, 0) * CFrame.new(-0.25, 0, 2.25) * CFrame.Angles(0, 0, math.pi / 2);\n\tgripType \t\t= 2;\n\tminLevel\t\t= 3;\n\t\n\t--> stats information <--\n\tbaseDamage \t\t= 26;\n\tbonusStats \t\t= {};\n\n\t--> shop information <--\t\n\tbuyValue = 400;\n\tsellValue = 150;\t\t\n\t\n\t--> handling information <--\n\tcanStack \t= false;\n\tcanBeBound \t= false;\n\tcanAwaken \t= true;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/wooden sword.lua",
    "content": "return {\n\t--> identifying information <--\n\tid = 3;\n\t\n\t--> generic information <--\n\tname = \"Oak Sword\";\n\trarity = \"Common\";\n\timage = \"rbxassetid://2528902907\";\n\tdescription = \"A wooden apprentice sword.\";\n\t\n\t--> equipment information <--\n\tisEquippable = true;\n\tequipmentSlot = 1;\n\tequipmentType = \"sword\";\n\tgripCFrame = CFrame.Angles(math.pi / 2, 0, 0) * CFrame.new(0.35, 0, 2) * CFrame.Angles(0, 0, math.pi / 2);\n\tminLevel = 4;\n\t\n\t--> stats information <--\n\tbaseDamage = 5;\n\tattackSpeed = 3;\n\tbonusStats = {};\n\t\n\t--> shop information <--\n\tbuyValue = 800;\n\tsellValue = 160;\t\n\t\n\t--> handling information <--\n\tcanStack = false;\n\tcanBeBound = false;\n\tcanAwaken = true;\n\t\n\t--> sorting information <--\n\tisImportant = false;\n\tcategory = \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/worn boots.lua",
    "content": "return {\n\t--> identifying information <--\n\tid \t\t= 184;\n\t\n\t--> generic information <--\n\tname \t\t= \"Worn Boots\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://3377146671\";\n\tdescription = \"A pair of hand-me-down leather boots to help you run longer.\";\n\t\n\t--> equipment information <--\n\tisEquippable \t= true;\n\tequipmentSlot \t= 9;\n\tequipmentType \t= \"armour\";\n\t\n\tminLevel\t\t= 2;\n\tvalueMulti = 2.2;\n\t\n\t--> shop information <--\t\n\tbuyValue = 8000;\n\tsellValue = 1500;\t\t\n\t\n\t--> stats information <--\n\tmodifierData = {{stamina = 1; }};\n\t\n\t--> handling information <--\n\tcanStack \t= false;\n\tcanBeBound \t= false;\n\tcanAwaken \t= true;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"equipment\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/itemData/yellow puffer fish.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nreturn {\n\t--> identifying information <--\n\tid \t\t= 39;\n\n\t--> generic information <--\n\tname \t\t= \"Yellow Pufferfish\";\n\trarity \t\t= \"Common\";\n\timage \t\t= \"rbxassetid://2539240983\";\n\tdescription = \"You probably don't want to eat this fish.\";\n\t\n\titemType = \"fish\";\n\t\n\tuseSound \t= \"eat_food\";\n\t\n\t--> stats information <--\n\tactivationEffect = function(player)\n\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.health.Value > 0 then\n\t\t\t\n\t\t\tplayer.Character.PrimaryPart.health.Value = player.Character.PrimaryPart.health.Value - 500\n\n\t\t\tif player.Character.PrimaryPart.health.Value <= 0 then\n\t\t\t\tlocal text = \"☠ \" .. player.Name .. \" ate a Yellow Pufferfish ☠\"\n\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\t\tText = text,\n\t\t\t\t\tFont = Enum.Font.SourceSansBold,\n\t\t\t\t\tColor = Color3.fromRGB(255, 130, 100),\n\t\t\t\t})\t\t\t\t\t\n\t\t\tend\n\t\t\t\n\t\t\treturn true, \"You feel refreshed.\"\n\t\tend\n\t\t\n\t\treturn false, \"Character is invalid.\"\n\tend;\n\t\n\t--> shop information <--\n\tbuyValue = 1000;\n\tsellValue = 500;\t\n\t\n\t--> handling information <--\n\tcanStack \t= true;\n\tcanBeBound \t= true;\n\tcanAwaken \t= false;\n\t\n\t--> sorting information <--\n\tisImportant \t= false;\n\tcategory \t\t= \"consumable\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/loreBooks.lua",
    "content": "\n\n\n\nlocal library = {\n\t[\"Mississippi's Journal\"] = {\n\t\t{text = \"Day 1\\n\\nWell, we made it into the Whispering Dunes after negotiating with the bandits. Who knows how long it's been since someone from the three factions has reached this place? Even the Mages seem to have forgotten it. Put bluntly, the region is massive. Who knows if I'll ever be able to explore it all?\"},\n\t\t{text = \"Day 5\\n\\nI can't say I've been sleeping particularly well. The locals call this place the Whispering Dunes because, apparently, spirits in the night will whisper their final regrets. I dismissed it as superstitious nonsense, but the nightmares I've been having... and that place. I can't get it out of my head.\"},\n\t\t{text = \"Day 22\\n\\nIt took some time, but I finally found it. Small groups of bandits go there at night. I wonder if their dreams are disturbed, too? It appears to have been a palace, but it's now buried in sand. According to local legend, it was once ruled by a powerful mage called Tal-rey. The nightmares won't stop.\"},\n\t\t{text = \"it must be me\\n\\nhaven't slept in days\\nthe dreams keep coming, more and more terrible, more and more wonderful\\ni must be the only one, i'm special\\ni am his return\\ni'm him\\ni'm tal-rey\\ni'm him i'm him i'm him i'm him i'm him i'm him\"},\n\t},\n\t\t\n\t\t\n\t\t[\"Evil Journal\"] = {\n\t\t\t{text = \"HAAHAAHAAA! THAT FOOL MOBEUS! WHAT A FUNNY GUY! I WAS JUST LOUNGING AT THE BEACH, SOAKING IN THE BEAUTIFUL VESTERIAN SUN RAYS WHEN I HEARD THE BOY MUMBLING TO HIMSELF! HAHAHAHA! A WHALE HUNTER HE SAID HE WANTED TO BE!\"}, \n\t\t\t\t\n\t\t\t{text = \"I TOLD THE BOY YES OF COURSE I CAN HELP YOU BECOME A WHALE HUNTER! I KNOW THE INITIATION PROCESS! JUST A SIMPLE POTION TO BECOME BIG AND STRONG, THAT'S ALL IT TAKES! HERE YOU GO MOBEUS... POOF!\"},{text = \"WELCOME TO THE FACTION OF WHALE HUNTERS, YOU SLIPPERY SEA MAMMAL YOU! HAVE FUN IN THE BAY SWIMMING AROUND AND DOING YOUR FUN WHALE THINGS!\"; \n\t\t\t\t\n\t\t\t\topenFunc = function(util)\n\t\t\t\t\tlocal playerQuests = util.network:invoke(\"getCacheValueByNameTag\", \"quests\")\n\t\t\t\t\t\t\tfor i, quest in pairs(playerQuests.active) do\n\t\t\t\t\t\t\t\tif quest.id == 8 then\n\t\t\t\t\t\t\t\t\tif quest.currentObjective == 2 and quest.objectives[2].started and quest.objectives[2].steps[2].completion.amount == 0 then\n\t\t\t\t\t\t\t\t\t\tutil.network:invokeServer(\"playerRequest_readEvilJournal\") -- needs to yield\n\t\t\t\t\t\t\t\t\t\tutil.network:fire(\"MobeusDialogueEnable\")\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\tend};\n\t\t\t\n\t\t};\n\t\t\n\t\t[\"Drowned Note - Crossroads\"] = {\n\t\t\t{text = \"Aha! MELVIN, YOU ARE A GENIUS! Who needs a bank when you can hide all your belongings in a confusing underwater tunnel system? Any thief will surely drown! Now I just- wait a second. How do I get out of here? Which way is it?!?\"}\n\t\t};\n\t\t\t\n\t\t[\"Yeti cave note\"] = {\n\t\t\t{text = \"I'm alive! The yeti wasn’t able to reach me inside this hole and eventually gave up trying. Now I just have to wait for him to let his guard down... maybe I’ll take a nap to pass the time...\"}\n\t\t};\n}\n\t\n\t\n\t\n\n\n\n\n\nlocal books = {} \n\nfunction books.getBook(name)\n\tif library[name] then\n\t\treturn library[name]\n\tend\n\treturn { {text = \"book does not exist\"} }\nend\n\nreturn books"
  },
  {
    "path": "src/ReplicatedStorage/modules/ability_utilities.lua",
    "content": "local module = {}\n\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal RunService = game:GetService(\"RunService\")\n\nlocal Modules = require(ReplicatedStorage.modules)\nlocal Network = Modules.load(\"network\")\nlocal Utilities = Modules.load(\"utilities\")\n\nlocal abilityLookup = require(ReplicatedStorage.abilityLookup)\n\nfunction module.canPlayerEquipAbility(player, playerData, abilityId)\n\tlocal abilityData = abilityLookup[abilityId]\n\n\tif not playerData then return false, \"invalid_data\" end\n\tif not abilityData then return false, \"invalid_ability\" end\n\n\tif playerData.abilities[abilityId] == nil then return false, \"ability_locked\" end\n\tif playerData.level < abilityData.prerequisites.playerLevel then return false, \"low_level\" end\n\n\tif abilityData.prerequisites.classRestriction == true then\n\t\tif not playerData.class == abilityData.prerequisites.playerClass then return false, \"wrong_class\" end\n\tend\n\n\t--Check if player is in area which they can equip an ability\n\t--Probably use region3 to achieve this ^\n\n\treturn true\nend\n\nfunction module.canPlayerCast(player, playerData, abilityId)\n\tif not Utilities.isEntityManifestValid(player.Character.PrimaryPart) then return false, \"invalid_character\" end\n\n\tlocal abilityData = abilityLookup[abilityId]\n\tlocal canEquip, errorCode = module.canPlayerEquipAbility(player, playerData, abilityId)\n\n\tif player.Character.PrimaryPart.mana.Value < abilityData.statistics.manaCost then return false, \"lacking_mana\" end\n\tif not canEquip then return false, errorCode end\n\n\tlocal lastCasted\n\tif RunService:IsServer() then\n\t\tlastCasted = Network:invoke(\"returnAbilityCooldown\", player, abilityId)\n\telse\n\t\tlastCasted = Network:invokeServer(\"requestAbilityCooldown\", abilityId)\n\tend\n\n\tif lastCasted ~= nil and (tick() - lastCasted) < abilityData.statistics.cooldown then return false, \"on_cooldown\" end\n\t--Check if ability is equipped???\n\n\treturn true\nend\n\nfunction module.returnNearbyPlayers(sourceCFrame, maximumDistance)\n\tif type(sourceCFrame) == type(CFrame) and tonumber(maximumDistance) then\n\t\tlocal nearbyPlayers = {}\n\n\t\tfor i, player in pairs(game.Players:GetPlayers()) do\n\t\t\tlocal char = player.Character\n\t\t\tif char and char.PrimaryPart then\n\t\t\t\tif (sourceCFrame.p - char.PrimaryPart.CFrame.p).magnitude < maximumDistance then\n\t\t\t\t\ttable.insert(nearbyPlayers, player)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif #nearbyPlayers >= 1 then return nearbyPlayers end\n\tend\nend\n\nfunction module.getCastingPlayer(abilityExecutionData)\n\treturn game:GetService(\"Players\"):GetPlayerByUserId(abilityExecutionData[\"cast-player-userId\"])\nend\n\nfunction module.getAbilityStatisticsForRank(abilityBaseData, rank)\n\nend\n\nfunction module.calculateStats(playerData, abilityId)\n\tlocal abilityData = abilityLookup[abilityId]\n\tif not abilityData or not playerData then return nil end\n\tif not playerData.abilities[abilityId] then return nil end\n\n\tlocal increasingStat = abilityData.statistics.increasingStat\n\tlocal baseStat = abilityData.statistics[increasingStat]\n\tlocal exponent = abilityData.statistics.increaseExponent\n\tlocal playerAbilityLevel = playerData.abilities[abilityId].level\n\n\tif not increasingStat or not exponent then return nil end\n\tlocal finalStatData = baseStat * (1 + (playerAbilityLevel * exponent))\n\n\treturn increasingStat, finalStatData\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/camera_shaker/CameraShakeInstance.lua",
    "content": "-- Camera Shake Instance\n-- Crazyman32\n-- February 26, 2018\n\n--[[\n\t\n\tcameraShakeInstance = CameraShakeInstance.new(magnitude, roughness, fadeInTime, fadeOutTime)\n\t\n--]]\n\n\n\nlocal CameraShakeInstance = {}\nCameraShakeInstance.__index = CameraShakeInstance\n\nlocal V3 = Vector3.new\nlocal NOISE = math.noise\n\n\nCameraShakeInstance.CameraShakeState = {\n\tFadingIn = 0;\n\tFadingOut = 1;\n\tSustained = 2;\n\tInactive = 3;\n}\n\n\nfunction CameraShakeInstance.new(magnitude, roughness, fadeInTime, fadeOutTime)\n\t\n\tif (fadeInTime == nil) then fadeInTime = 0 end\n\tif (fadeOutTime == nil) then fadeOutTime = 0 end\n\t\n\tassert(type(magnitude) == \"number\", \"Magnitude must be a number\")\n\tassert(type(roughness) == \"number\", \"Roughness must be a number\")\n\tassert(type(fadeInTime) == \"number\", \"FadeInTime must be a number\")\n\tassert(type(fadeOutTime) == \"number\", \"FadeOutTime must be a number\")\n\t\n\tlocal self = setmetatable({\n\t\tMagnitude = magnitude;\n\t\tRoughness = roughness;\n\t\tPositionInfluence = V3();\n\t\tRotationInfluence = V3();\n\t\tDeleteOnInactive = true;\n\t\troughMod = 1;\n\t\tmagnMod = 1;\n\t\tfadeOutDuration = fadeOutTime;\n\t\tfadeInDuration = fadeInTime;\n\t\tsustain = (fadeInTime > 0);\n\t\tcurrentFadeTime = (fadeInTime > 0 and 0 or 1);\n\t\ttick = Random.new():NextNumber(-100, 100);\n\t\t_camShakeInstance = true;\n\t}, CameraShakeInstance)\n\t\n\treturn self\n\t\nend\n\n\nfunction CameraShakeInstance:UpdateShake(dt)\n\t\n\tlocal _tick = self.tick\n\tlocal currentFadeTime = self.currentFadeTime\n\t\n\tlocal offset = V3(\n\t\tNOISE(_tick, 0) * 0.5,\n\t\tNOISE(0, _tick) * 0.5,\n\t\tNOISE(_tick, _tick) * 0.5\n\t)\n\t\n\tif (self.fadeInDuration > 0 and self.sustain) then\n\t\tif (currentFadeTime < 1) then\n\t\t\tcurrentFadeTime = currentFadeTime + (dt / self.fadeInDuration)\n\t\telseif (self.fadeOutDuration > 0) then\n\t\t\tself.sustain = false\n\t\tend\n\tend\n\t\n\tif (not self.sustain) then\n\t\tcurrentFadeTime = currentFadeTime - (dt / self.fadeOutDuration)\n\tend\n\t\n\tif (self.sustain) then\n\t\tself.tick = _tick + (dt * self.Roughness * self.roughMod)\n\telse\n\t\tself.tick = _tick + (dt * self.Roughness * self.roughMod * currentFadeTime)\n\tend\n\t\n\tself.currentFadeTime = currentFadeTime\n\t\n\treturn offset * self.Magnitude * self.magnMod * currentFadeTime\n\t\nend\n\n\nfunction CameraShakeInstance:StartFadeOut(fadeOutTime)\n\tif (fadeOutTime == 0) then\n\t\tself.currentFadeTime = 0\n\tend\n\tself.fadeOutDuration = fadeOutTime\n\tself.fadeInDuration = 0\n\tself.sustain = false\nend\n\n\nfunction CameraShakeInstance:StartFadeIn(fadeInTime)\n\tif (fadeInTime == 0) then\n\t\tself.currentFadeTime = 1\n\tend\n\tself.fadeInDuration = fadeInTime or self.fadeInDuration\n\tself.fadeOutDuration = 0\n\tself.sustain = true\nend\n\n\nfunction CameraShakeInstance:GetScaleRoughness()\n\treturn self.roughMod\nend\n\n\nfunction CameraShakeInstance:SetScaleRoughness(v)\n\tself.roughMod = v\nend\n\n\nfunction CameraShakeInstance:GetScaleMagnitude()\n\treturn self.magnMod\nend\n\n\nfunction CameraShakeInstance:SetScaleMagnitude(v)\n\tself.magnMod = v\nend\n\n\nfunction CameraShakeInstance:GetNormalizedFadeTime()\n\treturn self.currentFadeTime\nend\n\n\nfunction CameraShakeInstance:IsShaking()\n\treturn (self.currentFadeTime > 0 or self.sustain)\nend\n\n\nfunction CameraShakeInstance:IsFadingOut()\n\treturn ((not self.sustain) and self.currentFadeTime > 0)\nend\n\n\nfunction CameraShakeInstance:IsFadingIn()\n\treturn (self.currentFadeTime < 1 and self.sustain and self.fadeInDuration > 0)\nend\n\n\nfunction CameraShakeInstance:GetState()\n\tif (self:IsFadingIn()) then\n\t\treturn CameraShakeInstance.CameraShakeState.FadingIn\n\telseif (self:IsFadingOut()) then\n\t\treturn CameraShakeInstance.CameraShakeState.FadingOut\n\telseif (self:IsShaking()) then\n\t\treturn CameraShakeInstance.CameraShakeState.Sustained\n\telse\n\t\treturn CameraShakeInstance.CameraShakeState.Inactive\n\tend\nend\n\n\nreturn CameraShakeInstance"
  },
  {
    "path": "src/ReplicatedStorage/modules/camera_shaker/CameraShakePresets.lua",
    "content": "-- Camera Shake Presets\n-- Crazyman32\n-- February 26, 2018\n\n--[[\n\n\tCameraShakePresets.Bump\n\tCameraShakePresets.Explosion\n\tCameraShakePresets.Earthquake\n\tCameraShakePresets.BadTrip\n\tCameraShakePresets.HandheldCamera\n\tCameraShakePresets.Vibration\n\tCameraShakePresets.RoughDriving\n\n--]]\n\n\n\nlocal CameraShakeInstance = require(script.Parent.CameraShakeInstance)\n\nlocal CameraShakePresets = {\n\n\n\t-- A high-magnitude, short, yet smooth shake.\n\t-- Should happen once.\n\tBump = function()\n\t\tlocal c = CameraShakeInstance.new(2.5, 4, 0.1, 0.75)\n\t\tc.PositionInfluence = Vector3.new(0.15, 0.15, 0.15)\n\t\tc.RotationInfluence = Vector3.new(1, 1, 1)\n\t\treturn c\n\tend;\n\n\n\t-- An intense and rough shake.\n\t-- Should happen once.\n\tExplosion = function()\n\t\tlocal c = CameraShakeInstance.new(5, 10, 0, 1.5)\n\t\tc.PositionInfluence = Vector3.new(0.25, 0.25, 0.25)\n\t\tc.RotationInfluence = Vector3.new(4, 1, 1)\n\t\treturn c\n\tend;\n\n\n\t-- A continuous, rough shake\n\t-- Sustained.\n\tEarthquake = function()\n\t\tlocal c = CameraShakeInstance.new(0.6, 3.5, 2, 10)\n\t\tc.PositionInfluence = Vector3.new(0.25, 0.25, 0.25)\n\t\tc.RotationInfluence = Vector3.new(1, 1, 4)\n\t\treturn c\n\tend;\n\n\n\t-- A bizarre shake with a very high magnitude and low roughness.\n\t-- Sustained.\n\tBadTrip = function()\n\t\tlocal c = CameraShakeInstance.new(10, 0.15, 5, 10)\n\t\tc.PositionInfluence = Vector3.new(0, 0, 0.15)\n\t\tc.RotationInfluence = Vector3.new(2, 1, 4)\n\t\treturn c\n\tend;\n\n\n\t-- A subtle, slow shake.\n\t-- Sustained.\n\tHandheldCamera = function()\n\t\tlocal c = CameraShakeInstance.new(1, 0.25, 5, 10)\n\t\tc.PositionInfluence = Vector3.new(0, 0, 0)\n\t\tc.RotationInfluence = Vector3.new(1, 0.5, 0.5)\n\t\treturn c\n\tend;\n\n\n\t-- A very rough, yet low magnitude shake.\n\t-- Sustained.\n\tVibration = function()\n\t\tlocal c = CameraShakeInstance.new(0.4, 20, 2, 2)\n\t\tc.PositionInfluence = Vector3.new(0, 0.15, 0)\n\t\tc.RotationInfluence = Vector3.new(1.25, 0, 4)\n\t\treturn c\n\tend;\n\n\n\t-- A slightly rough, medium magnitude shake.\n\t-- Sustained.\n\tRoughDriving = function()\n\t\tlocal c = CameraShakeInstance.new(1, 2, 1, 1)\n\t\tc.PositionInfluence = Vector3.new(0, 0, 0)\n\t\tc.RotationInfluence = Vector3.new(1, 1, 1)\n\t\treturn c\n\tend;\n\n\n}\n\n\nreturn setmetatable({}, {\n\t__index = function(t, i)\n\t\tlocal f = CameraShakePresets[i]\n\t\tif (type(f) == \"function\") then\n\t\t\treturn f()\n\t\tend\n\t\twarn(\"No preset found with index \\\"\" .. i .. \"\\\"\")\n\tend;\n})"
  },
  {
    "path": "src/ReplicatedStorage/modules/camera_shaker/init.lua",
    "content": "-- Camera Shaker\n-- Crazyman32\n-- February 26, 2018\n\n--[[\n\t\n\tCameraShaker.CameraShakeInstance\n\t\n\tcameraShaker = CameraShaker.new(renderPriority, callbackFunction)\n\t\n\tCameraShaker:Start()\n\tCameraShaker:Stop()\n\tCameraShaker:Shake(shakeInstance)\n\tCameraShaker:ShakeSustain(shakeInstance)\n\tCameraShaker:ShakeOnce(magnitude, roughness [, fadeInTime, fadeOutTime, posInfluence, rotInfluence])\n\tCameraShaker:StartShake(magnitude, roughness [, fadeInTime, posInfluence, rotInfluence])\n\t\n\t\n\t\n\tEXAMPLE:\n\t\n\t\tlocal camShake = CameraShaker.new(Enum.RenderPriority.Camera.Value, function(shakeCFrame)\n\t\t\tcamera.CFrame = playerCFrame * shakeCFrame\n\t\tend)\n\t\t\n\t\tcamShake:Start()\n\t\t\n\t\t-- Explosion shake:\n\t\tcamShake:Shake(CameraShaker.Presets.Explosion)\n\t\t\n\t\twait(1)\n\t\t\n\t\t-- Custom shake:\n\t\tcamShake:ShakeOnce(3, 1, 0.2, 1.5)\n\t\n\t\n\t\n\tNOTE:\n\t\n\t\tThis was based entirely on the EZ Camera Shake asset for Unity3D. I was given written\n\t\tpermission by the developer, Road Turtle Games, to port this to Roblox.\n\t\t\n\t\tOriginal asset link: https://assetstore.unity.com/packages/tools/camera/ez-camera-shake-33148\n\t\n\t\n--]]\n\n\n\nlocal CameraShaker = {}\nCameraShaker.__index = CameraShaker\n\nlocal profileBegin = debug.profilebegin\nlocal profileEnd = debug.profileend\nlocal profileTag = \"CameraShakerUpdate\"\n\nlocal V3 = Vector3.new\nlocal CF = CFrame.new\nlocal ANG = CFrame.Angles\nlocal RAD = math.rad\nlocal v3Zero = V3()\n\nlocal CameraShakeInstance = require(script.CameraShakeInstance)\nlocal CameraShakeState = CameraShakeInstance.CameraShakeState\n\nlocal defaultPosInfluence = V3(0.15, 0.15, 0.15)\nlocal defaultRotInfluence = V3(1, 1, 1)\n\n\nCameraShaker.CameraShakeInstance = CameraShakeInstance\nCameraShaker.Presets = require(script.CameraShakePresets)\n\n\nfunction CameraShaker.new(renderPriority, callback)\n\t\n\tassert(type(renderPriority) == \"number\", \"RenderPriority must be a number (e.g.: Enum.RenderPriority.Camera.Value)\")\n\tassert(type(callback) == \"function\", \"Callback must be a function\")\n\t\n\tlocal self = setmetatable({\n\t\t_running = false;\n\t\t_renderName = \"CameraShaker\";\n\t\t_renderPriority = renderPriority;\n\t\t_posAddShake = v3Zero;\n\t\t_rotAddShake = v3Zero;\n\t\t_camShakeInstances = {};\n\t\t_removeInstances = {};\n\t\t_callback = callback;\n\t}, CameraShaker)\n\t\n\treturn self\n\t\nend\n\n\nfunction CameraShaker:Start()\n\tif (self._running) then return end\n\tself._running = true\n\tlocal callback = self._callback\n\tgame:GetService(\"RunService\"):BindToRenderStep(self._renderName, self._renderPriority, function(dt)\n\t\tprofileBegin(profileTag)\n\t\tlocal cf = self:Update(dt)\n\t\tprofileEnd()\n\t\tcallback(cf)\n\tend)\nend\n\n\nfunction CameraShaker:Stop()\n\tif (not self._running) then return end\n\tgame:GetService(\"RunService\"):UnbindFromRenderStep(self._renderName)\n\tself._running = false\nend\n\n\nfunction CameraShaker:Update(dt)\n\t\n\tlocal posAddShake = v3Zero\n\tlocal rotAddShake = v3Zero\n\t\n\tlocal instances = self._camShakeInstances\n\t\n\t-- Update all instances:\n\tfor i = 1,#instances do\n\t\t\n\t\tlocal c = instances[i]\n\t\tlocal state = c:GetState()\n\t\t\n\t\tif (state == CameraShakeState.Inactive and c.DeleteOnInactive) then\n\t\t\tself._removeInstances[#self._removeInstances + 1] = i\n\t\telseif (state ~= CameraShakeState.Inactive) then\n\t\t\tposAddShake = posAddShake + (c:UpdateShake(dt) * c.PositionInfluence)\n\t\t\trotAddShake = rotAddShake + (c:UpdateShake(dt) * c.RotationInfluence)\n\t\tend\n\t\t\n\tend\n\t\n\t-- Remove dead instances:\n\tfor i = #self._removeInstances,1,-1 do\n\t\tlocal instIndex = self._removeInstances[i]\n\t\ttable.remove(instances, instIndex)\n\t\tself._removeInstances[i] = nil\n\tend\n\t\n\treturn CF(posAddShake) *\n\t\t\tANG(0, RAD(rotAddShake.Y), 0) *\n\t\t\tANG(RAD(rotAddShake.X), 0, RAD(rotAddShake.Z))\n\t\nend\n\n\nfunction CameraShaker:Shake(shakeInstance)\n\tassert(type(shakeInstance) == \"table\" and shakeInstance._camShakeInstance, \"ShakeInstance must be of type CameraShakeInstance\")\n\tself._camShakeInstances[#self._camShakeInstances + 1] = shakeInstance\n\treturn shakeInstance\nend\n\n\nfunction CameraShaker:ShakeSustain(shakeInstance)\n\tassert(type(shakeInstance) == \"table\" and shakeInstance._camShakeInstance, \"ShakeInstance must be of type CameraShakeInstance\")\n\tself._camShakeInstances[#self._camShakeInstances + 1] = shakeInstance\n\tshakeInstance:StartFadeIn(shakeInstance.fadeInDuration)\n\treturn shakeInstance\nend\n\n\nfunction CameraShaker:ShakeOnce(magnitude, roughness, fadeInTime, fadeOutTime, posInfluence, rotInfluence)\n\tlocal shakeInstance = CameraShakeInstance.new(magnitude, roughness, fadeInTime, fadeOutTime)\n\tshakeInstance.PositionInfluence = (typeof(posInfluence) == \"Vector3\" and posInfluence or defaultPosInfluence)\n\tshakeInstance.RotationInfluence = (typeof(rotInfluence) == \"Vector3\" and rotInfluence or defaultRotInfluence)\n\tself._camShakeInstances[#self._camShakeInstances + 1] = shakeInstance\n\treturn shakeInstance\nend\n\n\nfunction CameraShaker:StartShake(magnitude, roughness, fadeInTime, posInfluence, rotInfluence)\n\tlocal shakeInstance = CameraShakeInstance.new(magnitude, roughness, fadeInTime)\n\tshakeInstance.PositionInfluence = (typeof(posInfluence) == \"Vector3\" and posInfluence or defaultPosInfluence)\n\tshakeInstance.RotationInfluence = (typeof(rotInfluence) == \"Vector3\" and rotInfluence or defaultRotInfluence)\n\tshakeInstance:StartFadeIn(fadeInTime)\n\tself._camShakeInstances[#self._camShakeInstances + 1] = shakeInstance\n\treturn shakeInstance\nend\n\n\nreturn CameraShaker"
  },
  {
    "path": "src/ReplicatedStorage/modules/client_quest_util.lua",
    "content": "\n-- to be used when writing client quest npc functionality\n\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal collectionService = game:GetService(\"CollectionService\")\nlocal runService\t\t= game:GetService(\"RunService\")\n\nlocal questLookup \t= require(replicatedStorage:WaitForChild(\"questLookupNew\"))\n\n\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal network \t\t= modules.load(\"network\")\n\tlocal tween \t\t= modules.load(\"tween\")\n\tlocal utilities \t= modules.load(\"utilities\")\n\tlocal mapping\t\t= modules.load(\"mapping\")\n\tlocal itemLookup = require(game.ReplicatedStorage:WaitForChild(\"itemData\"))\nfor i, questDataModule in pairs(script:GetChildren()) do\n\t\tlocal questData = require(questDataModule)\n\n\t\t-- internal stuff\n\t\t--questData.module = questDataModule\n\n\t\t-- hook ups\n\t\t--lookupTable[questData.id] \t\t\t= questData\n\t\t--lookupTable[questDataModule.Name] \t= questData\n\tend\n\nlocal module = {}\n\nlocal function questState(questId)\n\tlocal quests = network:invoke(\"getCacheValueByNameTag\", \"quests\")\n\n\tfor i, playerQuestData in pairs(quests.active) do\n\t\tif playerQuestData.id == questId then\n\n\t\t\tlocal objectiveSteps \t= 0\n\t\t\tlocal objectiveStepsDone \t    = 0\n\n\t\t\tfor ii, playerStepData in pairs(playerQuestData.objectives[playerQuestData.currentObjective].steps) do\n\t\t\t\tobjectiveSteps = objectiveSteps + 1\n\t\t\t\t\tif playerStepData.requirement.amount <= playerStepData.completion.amount then\n\t\t\t\t\t\tobjectiveStepsDone = objectiveStepsDone + 1\n\t\t\t\t\tend\n\t\t\tend\n\t\t\tif objectiveStepsDone > 0 and objectiveStepsDone == objectiveSteps and playerQuestData.objectives[playerQuestData.currentObjective].started then\n\t\t\t\t\treturn mapping.questState.objectiveDone\n\t\t\telse\n\t\t\t\tif playerQuestData.objectives[playerQuestData.currentObjective].started then\n\t\t\t\t\t\treturn mapping.questState.active\n\t\t\t\t\telse\n\t\t\t\t\t\treturn mapping.questState.unassigned\n\t\t\t\t\tend\n\n\t\t\t\t\treturn mapping.questState.active\n\t\t\tend\n\t\tend\n\tend\n\n\tfor i, completePlayerQuestData in pairs(quests.completed) do\n\t\tif completePlayerQuestData.id == questId then\n\t\t\treturn mapping.questState.completed\n\t\tend\n\tend\n\n\n\n\treturn mapping.questState.unassigned\nend\n\n\nfunction module.getPlayerQuestStateByQuestId(questId)\n\treturn questState(questId)\nend\n\n\nlocal function questObjective(questId)\n\tlocal quests = network:invoke(\"getCacheValueByNameTag\", \"quests\")\n\n\n\tfor i, playerQuestData in pairs(quests.active) do\n\n\t\tif playerQuestData.id == questId then\n\t\t\tif playerQuestData.objectives[playerQuestData.currentObjective] then --.started\n\t\t\t\treturn playerQuestData.currentObjective\n\t\t\tend\n\t\tend\n\tend\n\n\treturn -1\nend\n\n\n\nfunction module.getQuestObjective(questId)\n\treturn questObjective(questId)\nend\n\nlocal function questObjectiveAndStarted(questId)\n\tlocal quests = network:invoke(\"getCacheValueByNameTag\", \"quests\")\n\tlocal notStartedCurrentObjective\n\n\tfor i, playerQuestData in pairs(quests.active) do\n\n\t\tif playerQuestData.id == questId then\n\t\t\tif playerQuestData.objectives[playerQuestData.currentObjective].started then --.started\n\t\t\t\treturn playerQuestData.currentObjective\n\t\t\telse\n\t\t\t\treturn playerQuestData.currentObjective *-1\n\t\t\tend\n\t\tend\n\tend\n\n\n\treturn -1\nend\n\n\n\n\nfunction module.getQuestObjectiveAndStarted(questId)\n\treturn questObjectiveAndStarted(questId)\nend\n\nlocal function levelReq(questId, currentObjective)\n\n\tlocal questData = questLookup[questId]\n\tif currentObjective == -1 then\n\t\tcurrentObjective = 1\n\tend\n\treturn questData.objectives[currentObjective].requireLevel or 1\nend\n\n\nfunction module.getQuestLevelReq(questId, currentObjective)\n\treturn levelReq(questId, currentObjective)\nend\n\n\nlocal function classReq(questId)\n\tlocal questData = questLookup[questId]\n\n\treturn questData.requireClass\nend\n\nfunction module.getQuestClassReq(questId)\n\treturn classReq(questId)\nend\n\nlocal function reqQuests(questId)\n\tlocal questData = questLookup[questId]\n\n\treturn { unpack(questData.requireQuests) }\nend\n\nfunction module.getQuestRequiredQuests(questId)\n\treturn reqQuests(questId)\nend\n\nlocal function repeatData(questId)\n\tlocal questData = questLookup[questId]\n\n\treturn questData.repeatableData.value, questData.repeatableData.timeInterval\nend\n\nfunction module.getQuestRepeatData(questId)\n\treturn repeatData(questId)\nend\n\n\nfunction module.canPlayerInventoryObtainItem(itemId)\n\tlocal inventory = network:invoke(\"getCacheValueByNameTag\", \"inventory\")\n\tlocal category = itemLookup[itemId].category\n\n\n\t-- if inventory is full but player already has 1 of the item\n\tfor i, itemData in pairs(inventory) do\n\t\tif itemData.id == itemId then\n\t\t\treturn true\n\t\tend\n\tend\n\n\tlocal spacesTaken = 0\n\tfor i, itemData in pairs(inventory) do\n\t\tif itemData.category == category then\n\t\t\tspacesTaken = spacesTaken + 1\n\t\tend\n\tend\n\n\t-- inv full\n\tif spacesTaken >= 20 then -- remember to change to 20 when that update comes\n\t\treturn false\n\tend\n\n\t-- inv not full\n\treturn true\n\n\nend\n\n\n-- will go through all the checks and return true/false if player can start quest. used for npcquestmarker when the reason for not being able to start the quest doesn't matter\nfunction module.masterCanStartQuest(questId)\n\tlocal currentObjective = questObjective(questId)\n\n\n\n\tlocal playerLevel = network:invoke(\"getCacheValueByNameTag\", \"level\")\n\tlocal completedQuests = network:invoke(\"getCacheValueByNameTag\", \"quests\").completed\n\tlocal playerClass = network:invoke(\"getCacheValueByNameTag\", \"class\")\n\n\t--local playerLevel, completedQuests, playerClass = network:invokeServer(\"playerRequest_questStartStats\")\n\n\n\n\tlocal requireLevel = levelReq(questId, currentObjective)\n\tlocal requireClass = classReq(questId)\n\tlocal requireQuests = reqQuests(questId)\n\n\tlocal isRepeatable, repeatInterval = repeatData(questId)\n\n\tif currentObjective == -1 then\n\t\tcurrentObjective = 1\n\tend\n\n\t-- check level\n\tif playerLevel < requireLevel then\n\t\treturn false\n\tend\n\n\t-- check class\n\tif requireClass and requireClass ~= playerClass then\n\t\treturn false\n\tend\n\n\t-- check quest reqs\n\tif #requireQuests > 0 then\n\t\tfor i, reqQuest in pairs(requireQuests) do\n\t\t\tlocal hasQuest = false\n\t\t\tfor ii, completeQuest in pairs(completedQuests) do\n\t\t\t\tif completeQuest.id == reqQuest then\n\t\t\t\t\thasQuest = true\n\t\t\t\tend\n\t\t\tend\n\t\t\tif not hasQuest then\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\tend\n\n\t-- is repeatable\n\n\tif isRepeatable then\n\t\tlocal activeQuests = network:invoke(\"getCacheValueByNameTag\", \"quests\").active\n\t\tlocal canStartAfterTim\n\t\tfor i, activeData in pairs(activeQuests) do\n\t\t\tif activeData.id == questId and activeData.canStartAfterTime then\n\t\t\t\tif activeData.canStartAfterTime > os.time() then\n\t\t\t\t\treturn false\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\t-- special check for desert quest\n\tif questId == 22 then\n\t\tlocal started = false\n\t\tlocal activeQuests = network:invoke(\"getCacheValueByNameTag\", \"quests\").active\n\t\tfor i, quest in pairs(activeQuests) do\n\t\t\tif quest.id == 22 then\n\t\t\t\tstarted = false\n\t\t\tend\n\t\tend\n\n\t\tlocal hasNotebook = false\n\t\tif not started then\n\n\t\t\tlocal inv = network:invoke(\"getCacheValueByNameTag\", \"inventory\")\n\t\t\tfor i, item in pairs(inv) do\n\n\t\t\t\tif item.id == 187 then\n\t\t\t\t\thasNotebook = true\n\t\t\t\tend\n\t\t\tend\n\t\t\tif not hasNotebook then return false end\n\t\tend\n\n\tend\n\n\treturn true\nend\n\n\n\n\nreturn module\n"
  },
  {
    "path": "src/ReplicatedStorage/modules/client_utilities.lua",
    "content": "local module = {}\n\nlocal modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\n\tlocal network \t\t= modules.load(\"network\")\n\tlocal placeSetup \t= modules.load(\"placeSetup\")\n\t\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal IGNORE_LIST \t\t= {placeSetup.getPlaceFoldersFolder()}\n\nfunction module.raycastFromCurrentScreenPoint(customIgnoreList, range)\n\trange = range or 2048\n\tlocal mouseLocation = userInputService:GetMouseLocation()\n\t\n\tlocal cameraRay = workspace.CurrentCamera:ScreenPointToRay(mouseLocation.X, mouseLocation.Y - 36)\n\tlocal ray \t\t= Ray.new(cameraRay.Origin, cameraRay.Direction.unit * range)\n\t\n\tlocal hitPart, hitPosition, hitNormal, hitMaterial = workspace:FindPartOnRayWithIgnoreList(ray, customIgnoreList or IGNORE_LIST)\n\t\n\twhile hitPart and (not hitPart.CanCollide or hitPart.Transparency >= 0.95) do\n\t\ttable.insert(customIgnoreList or IGNORE_LIST, hitPart)\n\t\t\n\t\thitPart, hitPosition, hitNormal, hitMaterial = workspace:FindPartOnRayWithIgnoreList(ray, customIgnoreList or IGNORE_LIST)\n\tend\n\t\n\treturn hitPart, hitPosition, hitNormal, hitMaterial, ray\nend\n\nlocal function main()\n\t\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/configuration.lua",
    "content": "local module = {}\n\t\nlocal insertService \t= game:GetService(\"InsertService\")\nlocal httpService \t\t= game:GetService(\"HttpService\")\nlocal runService \t\t= game:GetService(\"RunService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network = modules.load(\"network\")\n-- above means that modules and network cannot require configuration!\n-- keep this in mind.\n\nlocal rawGameConfigurationAssetId = 2553891018\nlocal rawGameConfigurationLatestAssetVersionId\n\nlocal rawGameConfiguration\nlocal clientGameConfiguration\nlocal serverGameConfiguration\nlocal playerGameConfigurationCache \t= {}\n\nlocal isRunningOnServer = runService:IsServer()\nlocal isRunningOnClient = runService:IsClient()\n\nlocal function getGameConfiguration(player)\n\tif isRunningOnClient then warn(\"Unable to call `getGameConfiguration` on client\") return nil end\n\t\n\tlocal specificGameConfiguration = {}\n\t\n\tfor configurationName, configurationData in pairs(rawGameConfiguration) do\n\t\tif configurationData.overrides then\n\t\t\tlocal placeOverride = nil\n\t\t\tlocal playerOverride = nil\n\t\t\t\n\t\t\tfor overrideString, overrideValue in pairs(configurationData.overrides) do\n\t\t\t\tlocal overrideType, overrideId = string.match(overrideString, \"([puid]+):(%d+)\")\n\t\t\t\t\n\t\t\t\tif overrideType == \"pid\" and overrideId and game.PlaceId == tonumber(overrideId) then\n\t\t\t\t\tplaceOverride = overrideValue\n\t\t\t\telseif overrideType == \"uid\" and overrideId and player and player.userId == tonumber(overrideId) then\n\t\t\t\t\tplayerOverride = overrideValue\n\t\t\t\tend\n\t\t\tend\n\t\t\t\n\t\t\tif playerOverride ~= nil then\n\t\t\t\tspecificGameConfiguration[configurationName] = playerOverride\n\t\t\telseif placeOverride ~= nil then\n\t\t\t\tspecificGameConfiguration[configurationName] = placeOverride\n\t\t\telse\n\t\t\t\tspecificGameConfiguration[configurationName] = configurationData.value\n\t\t\tend\n\t\telse\n\t\t\tspecificGameConfiguration[configurationName] = configurationData.value\n\t\tend\n\tend\n\t\n\treturn specificGameConfiguration\nend\n\nlocal function updateGameConfiguration()\n\tif isRunningOnClient then warn(\"Unable to call `updateGameConfiguration` on client\") return nil end\n\t\n\tlocal newRawGameConfiguration\n\tlocal success, returnValue = pcall(function()\n\t\treturn game:GetService(\"InsertService\"):GetLatestAssetVersionAsync(rawGameConfigurationAssetId)\n\tend)\n\t\n\tif success and returnValue then\n\t\tif not rawGameConfigurationLatestAssetVersionId or rawGameConfigurationLatestAssetVersionId ~= returnValue then\n\t\t\tlocal success2, returnValue2 = pcall(function()\n\t\t\t\treturn game:GetService(\"InsertService\"):LoadAssetVersion(returnValue)\n\t\t\tend)\n\t\t\t\n\t\t\tif success2 and returnValue2 and returnValue2:GetChildren()[1] then\n\t\t\t\tnewRawGameConfiguration \t\t\t\t\t= require(returnValue2:GetChildren()[1])\n\t\t\t\trawGameConfigurationLatestAssetVersionId \t= returnValue\n\t\t\telse\n\t\t\t\tif not rawGameConfiguration then\n\t\t\t\t\twarn(\"ERR2: gameConfiguration failed to load, defaulting to embedded gameConfiguration\")\n\t\t\t\t\tnewRawGameConfiguration = require(game.ServerStorage.gameConfiguration)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\telse\n\t\tif not rawGameConfiguration then\n\t\t\twarn(\"ERR1: gameConfiguration failed to load, defaulting to embedded gameConfiguration\")\n\t\t\tnewRawGameConfiguration = require(game.ServerStorage.gameConfiguration)\n\t\tend\n\tend\n\t\n\t-- unload gameConfiguration\n\tif newRawGameConfiguration then\n\t\trawGameConfiguration = newRawGameConfiguration\n\t\t\n\t\tlocal newPlayerGameConfigurationCache = {}\n\t\tfor i, player in pairs(game.Players:GetPlayers()) do\n\t\t\tnewPlayerGameConfigurationCache[player] = getGameConfiguration(player)\n\t\t\t\n\t\t\t-- strip away all server exclusive configurations\n\t\t\tfor configurationName, configurationValue in pairs(newPlayerGameConfigurationCache[player]) do\n\t\t\t\tif configurationName:sub(1, 7) == \"server_\" then\n\t\t\t\t\tnewPlayerGameConfigurationCache[player][configurationName] = nil\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\t\n\t\tserverGameConfiguration \t\t= getGameConfiguration(nil)\n\t\tplayerGameConfigurationCache \t= newPlayerGameConfigurationCache\n\t\t\n\t\tfor i, player in pairs(game.Players:GetPlayers()) do\n\t\t\tif playerGameConfigurationCache[player] then\n\t\t\t\tnetwork:fireClient(\"signal_playerGameConfigurationUpdated\", player, playerGameConfigurationCache[player], serverGameConfiguration)\n\t\t\tend\n\t\tend\n\t\t\n\t\tnetwork:fire(\"gameConfigurationUpdated\", serverGameConfiguration)\n\tend\nend\n\nlocal function playerRequest_getPlayerGameConfiguration(player)\n\treturn playerGameConfigurationCache[player], serverGameConfiguration\nend\n\nfunction module.getConfigurationValue(configurationValueName, invokingPlayer)\n\tif isRunningOnServer then\n\t\t\n\t\t-- check for config to exist, and if it doesnt wait!\n\t\tif not serverGameConfiguration then\n\t\t\twhile not serverGameConfiguration do\n\t\t\t\twait(0.1)\n\t\t\tend\n\t\tend\n\t\t\n\t\tif invokingPlayer then\n\t\t\tlocal resp = playerGameConfigurationCache[invokingPlayer][configurationValueName]\n\t\t\t\n\t\t\t-- make sure serverGameConfiguration isn't nil, might be a\n\t\t\t-- generic configuration!\n\t\t\tif resp == nil then\n\t\t\t\treturn serverGameConfiguration[configurationValueName]\n\t\t\tend\n\t\t\t\n\t\t\treturn resp\n\t\telse\n\t\t\treturn serverGameConfiguration[configurationValueName]\n\t\tend\n\telseif isRunningOnClient then\n\t\t\n\t\t-- check for config to exist, and if it doesnt wait!\n\t\tif not clientGameConfiguration and not serverGameConfiguration then\n\t\t\twhile not clientGameConfiguration and not serverGameConfiguration do\n\t\t\t\twait(0.1)\n\t\t\tend\n\t\tend\n\t\t\n\t\tlocal resp = clientGameConfiguration[configurationValueName]\n\t\t\n\t\t-- make sure serverGameConfiguration isn't nil, might be a\n\t\t-- generic configuration!\n\t\tif resp == nil then\n\t\t\treturn serverGameConfiguration[configurationValueName]\n\t\tend\n\t\t\n\t\treturn resp\n\tend\nend\n\nlocal function onPlayerAdded(player)\n\tplayerGameConfigurationCache[player] = getGameConfiguration(player)\nend\n\nlocal function onPlayerRemoving(player)\n\tplayerGameConfigurationCache[player] = nil\nend\n\nlocal function main()\n\tif isRunningOnServer then\n\t\t-- used internally to let clients update their configuration\n\t\tnetwork:create(\"playerRequest_getPlayerGameConfiguration\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_getPlayerGameConfiguration)\n\t\tnetwork:create(\"signal_playerGameConfigurationUpdated\", \"RemoteEvent\")\n\t\tnetwork:create(\"gameConfigurationUpdated\", \"BindableEvent\")\n\t\t\n\t\tupdateGameConfiguration()\n\t\t\n\t\tfor i, player in pairs(game.Players:GetPlayers()) do\n\t\t\tonPlayerAdded(player)\n\t\tend\n\t\t\n\t\tgame.Players.PlayerAdded:connect(onPlayerAdded)\n\t\tgame.Players.PlayerRemoving:connect(onPlayerRemoving)\n\t\t\t\t\n\t\twhile wait(module.getConfigurationValue(\"gameConfigurationRefreshTimeInSeconds\", nil)) do\n\t\t\tupdateGameConfiguration()\n\t\tend\n\telseif isRunningOnClient then\n\t\tnetwork:create(\"gameConfigurationUpdated\", \"BindableEvent\")\n\t\tnetwork:connect(\"signal_playerGameConfigurationUpdated\", \"OnClientEvent\", function(newClientGameConfiguration, newServerGameConfiguration)\n\t\t\tclientGameConfiguration = newClientGameConfiguration\n\t\t\tserverGameConfiguration = newServerGameConfiguration\n\t\t\t\n\t\t\tnetwork:fire(\"gameConfigurationUpdated\", clientGameConfiguration, serverGameConfiguration)\n\t\tend)\n\t\t\n\t\tclientGameConfiguration, serverGameConfiguration = network:invokeServer(\"playerRequest_getPlayerGameConfiguration\")\n\tend\nend\n\nspawn(main)\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/contains.lua",
    "content": "return function(t, lookfor)\n\tfor k,v in pairs(t) do\n\t\tif v == lookfor then\n\t\t\treturn true, k\n\t\tend\n\tend\n\treturn false\nend"
  },
  {
    "path": "src/ReplicatedStorage/modules/damage.lua",
    "content": "local module = {}\n\n-- hurts but we need to stop circular dependencies\nlocal network \t\t= require(script.Parent:WaitForChild(\"network\"))\nlocal utilities \t= require(script.Parent:WaitForChild(\"utilities\"))\nlocal placeSetup \t= require(script.Parent:WaitForChild(\"placeSetup\"))\n\tlocal entityManifestCollectionFolder \t= placeSetup.getPlaceFolder(\"entityManifestCollection\")\n\nlocal runService = game:GetService(\"RunService\")\n\nlocal rand = Random.new(os.time())\n\nlocal function getNonSerializeData(player)\n\tlocal nonSerializeData do\n\t\tif runService:IsServer() and player then\n\t\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\t\t\n\t\t\tif playerData then\n\t\t\t\tnonSerializeData = playerData.nonSerializeData\n\t\t\tend\n\t\telseif runService:IsClient() then\n\t\t\tnonSerializeData = network:invoke(\"getCacheValueByNameTag\", \"nonSerializeData\")\n\t\tend\n\tend\n\treturn nonSerializeData\nend\n\n-- TODO(dnurkkala, 8/28/2019): maybe this function is worthless\n-- but I'm leaving it here in case we decide to do something\n-- with it. not like it does any harm just sitting here uncalled\nfunction module.getNonHostileTargets(playerDoingDamage)\n\tlocal nonHostileTargets = {}\n\t\n\tfor _, entityManifest in pairs(entityManifestCollectionFolder:GetChildren()) do\n\t\tif\n\t\t\tentityManifest:IsA(\"BasePart\") and\n\t\t\tentityManifest:FindFirstChild(\"pet\") and\n\t\t\tentityManifest:FindFirstChild(\"state\") and\n\t\t\tentityManifest.state.Value ~= \"dead\" and\n\t\t\tentityManifest:FindFirstChild(\"isTargetImmune\") == nil\n\t\tthen\n\t\t\ttable.insert(nonHostileTargets, entityManifest)\n\t\tend\n\tend\n\t\n\tlocal nonSerializeData = getNonSerializeData(playerDoingDamage)\n\t\n\tif nonSerializeData.isGlobalPVPEnabled or #nonSerializeData.whitelistPVPEnabled > 0 then\n\t\tlocal players = nonSerializeData.isGlobalPVPEnabled and game.Players:GetPlayers() or nonSerializeData.whitelistPVPEnabled\n\t\tfor _, player in pairs(players) do\n\t\t\tif\n\t\t\t\tplayer ~= playerDoingDamage and\n\t\t\t\tplayer:FindFirstChild(\"isInPVP\") and\n\t\t\t\tplayer.Character and\n\t\t\t\tplayer.Character.PrimaryPart\n\t\t\tthen\n\t\t\t\ttable.insert(nonHostileTargets, player.Character.PrimaryPart)\n\t\t\tend\n\t\tend\n\tend\n\n\t-- you're always non-hostile to yourself\n\tif playerDoingDamage.Character then\n\t\ttable.insert(nonHostileTargets, playerDoingDamage.Character.PrimaryPart)\n\tend\n\t\n\treturn nonHostileTargets\nend\n\nfunction module.getFriendlies(playerDoingDamage)\n\tlocal friendlies = {}\n\t\n\t-- our party is friendly!\n\tlocal partyData\n\tif runService:IsClient() then\n\t\tpartyData = network:invokeServer(\"playerRequest_getMyPartyData\")\n\t\n\telseif runService:IsServer() then\n\t\tpartyData = network:invoke(\"getPartyDataByPlayer\", playerDoingDamage)\n\tend\n\t\n\tif partyData then\n\t\tfor _, partyMemberData in pairs(partyData.members) do\n\t\t\tif\n\t\t\t\tpartyMemberData.player and\n\t\t\t\tpartyMemberData.player ~= playerDoingDamage and\n\t\t\t\tpartyMemberData.player.Character\n\t\t\tthen\n\t\t\t\ttable.insert(friendlies, partyMemberData.player.Character.PrimaryPart)\n\t\t\tend\n\t\tend\n\tend\n\t\n\t-- you're always friendly to yourself\n\tif playerDoingDamage.Character then\n\t\ttable.insert(friendlies, playerDoingDamage.Character.PrimaryPart)\n\tend\n\t\n\treturn friendlies\nend\n\n\n\n-- at some point, do some calculations\nfunction module.getDamagableTargets(playerDoingDamage)\n\tlocal damagableTargets = {}\n\t\n\tfor i, entityManifest in pairs(entityManifestCollectionFolder:GetChildren()) do\n\t\tif entityManifest:IsA(\"BasePart\") then\n\t\t\tif not entityManifest:FindFirstChild(\"pet\") and entityManifest:FindFirstChild(\"entityType\") and entityManifest.entityType.Value == \"monster\" and entityManifest:FindFirstChild(\"state\") and entityManifest.state.Value ~= \"dead\" and not entityManifest:FindFirstChild(\"isTargetImmune\") then\n\t\t\t\ttable.insert(damagableTargets, entityManifest)\n\t\t\tend\n\t\tend\n\tend\n\t\t\n\tlocal nonSerializeData do\n\t\tif runService:IsServer() and playerDoingDamage then\n\t\t\tlocal playerData = network:invoke(\"getPlayerData\", playerDoingDamage)\n\t\t\t\n\t\t\tif playerData then\n\t\t\t\tnonSerializeData = playerData.nonSerializeData\n\t\t\tend\n\t\telseif runService:IsClient() then\n\t\t-- dnurkkala removed this check and it looks like it won't cause issues but if\n\t\t-- shit goes down then y'all can blame me I'm ready to take responsibility\n\n\t\t-- elseif runService:IsClient() and playerDoingDamage == game.Players.LocalPlayer then\n\t\t\tnonSerializeData = network:invoke(\"getCacheValueByNameTag\", \"nonSerializeData\")\n\t\tend\n\tend\n\t\n\tif not nonSerializeData then return damagableTargets end\n\t\n\tif nonSerializeData.isGlobalPVPEnabled or #nonSerializeData.whitelistPVPEnabled > 0 then\n\t\tlocal playersToScan = nonSerializeData.isGlobalPVPEnabled and game.Players:GetPlayers() or nonSerializeData.whitelistPVPEnabled\n\t\tfor i, playerToScan in pairs(playersToScan) do\n\t\t\tif playerToScan ~= playerDoingDamage and playerToScan:FindFirstChild(\"isInPVP\") and playerToScan.isInPVP.Value and playerToScan.Character and playerToScan.Character.PrimaryPart then \n\t\t\t\ttable.insert(damagableTargets, playerToScan.Character.PrimaryPart)\n\t\t\tend\n\t\tend\n\tend\n\t\n\treturn damagableTargets\nend\n\nfunction module.canPlayerDamageTarget(playerDoingDamage, target)\n\tif not target then return false, nil end\n\t\n\tlocal damagableTargets = module.getDamagableTargets(playerDoingDamage)\n\t\n\tlocal trueTarget = target do\n\t\tif runService:IsClient() then\n\t\t\tif target:IsDescendantOf(workspace.placeFolders.entityRenderCollection) then\n\t\t\t\tlocal currentLocation = target\n\t\t\t\tlocal clientHitboxToServerHitboxReference\n\t\t\t\t\n\t\t\t\twhile not currentLocation:FindFirstChild(\"clientHitboxToServerHitboxReference\") and currentLocation ~= workspace.placeFolders.entityRenderCollection do\n\t\t\t\t\tcurrentLocation = currentLocation.Parent\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\tif currentLocation:FindFirstChild(\"clientHitboxToServerHitboxReference\") then\n\t\t\t\t\ttrueTarget = currentLocation.clientHitboxToServerHitboxReference.Value\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\t\n\tfor i, damagableTarget in pairs(damagableTargets) do\n\t\tif trueTarget == damagableTarget then\n\t\t\treturn true, trueTarget\n\t\tend\n\tend\n\t\n\treturn false, trueTarget\nend\n\nfunction module.getNeutrals(playerDoingDamage)\n\tlocal neutrals = {}\n\tfor i, player in pairs(game.Players:GetPlayers()) do\n\t\tif player.Character and player.Character.PrimaryPart then\n\t\t\tlocal target = player.Character.PrimaryPart\n\t\t\tif not module.canPlayerDamageTarget(playerDoingDamage, target) then\n\t\t\t\ttable.insert(neutrals, player.Character.PrimaryPart)\n\t\t\tend\t\n\t\tend\n\tend\n\treturn neutrals\nend;\n\nreturn module\n\n"
  },
  {
    "path": "src/ReplicatedStorage/modules/debug.lua",
    "content": "local module = {}\n\nlocal debugPlaceIds \t= {[2061558182] = true}\nlocal IS_DEBUG_PLACE \t= debugPlaceIds[game.PlaceId]\n\nfunction module.print(...)\n\tif IS_DEBUG_PLACE then\n\t\tprint(...)\n\tend\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/detection.lua",
    "content": "local module = {}\n\nfunction module.boxcast_singleTarget(boundingBoxCFrame, boundingBoxSize, targetPosition)\n\tlocal cf = boundingBoxCFrame:pointToObjectSpace(targetPosition) / boundingBoxSize\n\t\n\treturn\n\t\tcf.X <= 0.5 and cf.X >= -0.5\n\t\tand cf.Y <= 0.5 and cf.Y >= -0.5\n\t\tand cf.Z <= 0.5 and cf.Z >= -0.5\nend\n\nfunction module.boxcast_closest(targets, hitboxCFrame, hitboxSize, point)\n\tlocal closest = nil\n\tlocal closestDistanceSq = math.huge\n\t\n\tfor _, target in pairs(targets) do\n\t\tlocal targetPosition = module.projection_Box(target.CFrame, target.Size, hitboxCFrame.Position)\n\t\tif module.boxcast_singleTarget(hitboxCFrame, hitboxSize, targetPosition) then\n\t\t\tlocal delta = targetPosition - point\n\t\t\tlocal distanceSq =\n\t\t\t\tdelta.X * delta.X +\n\t\t\t\tdelta.Y * delta.Y +\n\t\t\t\tdelta.Z * delta.Z\n\t\t\tif distanceSq < closestDistanceSq then\n\t\t\t\tclosest = target\n\t\t\t\tclosestDistanceSq = distanceSq\n\t\t\tend\n\t\tend\n\tend\n\t\n\treturn closest\nend\n\nfunction module.boxcast_all(targets, hitboxCFrame, hitboxSize)\n\tlocal hits = {}\n\t\n\tfor _, target in pairs(targets) do\n\t\tlocal targetPosition = module.projection_Box(target.CFrame, target.Size, hitboxCFrame.Position)\n\t\tif module.boxcast_singleTarget(hitboxCFrame, hitboxSize, targetPosition) then\n\t\t\ttable.insert(hits, target)\n\t\tend\n\tend\n\t\n\treturn hits\nend\n\nlocal function sphereCastShow(boundingSpherePosition, boundingSphereRadius)\n\tlocal p = Instance.new(\"Part\")\n\tp.Shape = Enum.PartType.Ball\n\tp.Anchored = true\n\tp.CanCollide = false\n\tp.Size = Vector3.new(2, 2, 2) * boundingSphereRadius\n\tp.Material = Enum.Material.Neon\n\tp.BrickColor = BrickColor.new(\"Hot pink\")\n\tp.CFrame = CFrame.new(boundingSpherePosition)\n\tp.Parent = workspace\n\t\n\tgame:GetService(\"Debris\"):AddItem(p, 1.5)\nend\n\nfunction module.spherecast_singleTarget(boundingSpherePosition, boundingSphereRadius, targetPosition)\n\treturn (boundingSpherePosition - targetPosition).magnitude <= boundingSphereRadius\nend\n\nfunction module.projection_Box(boxCFrame, boxSize, targetPosition)\n\tlocal offset = boxCFrame:pointToObjectSpace(targetPosition)\n\n\treturn (boxCFrame * CFrame.new(\n\t\tmath.clamp(offset.X, -boxSize.X / 2, boxSize.X / 2),\n\t\tmath.clamp(offset.Y, -boxSize.Y / 2, boxSize.Y / 2),\n\t\tmath.clamp(offset.Z, -boxSize.Z / 2, boxSize.Z / 2)\n\t)).p\nend\n\nfunction module.projection_Sphere(spherePosition, sphereRadius, targetPosition)\n\treturn spherePosition + (targetPosition - spherePosition).unit * sphereRadius\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/economy.lua",
    "content": "local runService = game:GetService(\"RunService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network = modules.load(\"network\")\n\t\tlocal itemLookup = require(game.ReplicatedStorage:WaitForChild(\"itemData\"))\t\n\n\nlocal module = {}\n\n\nfunction module.getSellValue(itemBaseData, inventorySlotData)\n\tlocal baseSellValue = itemBaseData.sellValue or 0\n\t\n\tif inventorySlotData and inventorySlotData.enchantments then\n\t\tfor i,enchantment in pairs(inventorySlotData.enchantments) do\n\t\t\tlocal enchantmentBaseData = itemLookup[enchantment.id]\n\t\t\tbaseSellValue = baseSellValue + enchantmentBaseData.sellValue\n\t\tend\n\tend\n\t\n\treturn baseSellValue\nend\n\nreturn module\n"
  },
  {
    "path": "src/ReplicatedStorage/modules/effects.lua",
    "content": "-- written by Davidii\n-- utility functions to help out with making cool-looking effects\nlocal runService = game:GetService(\"RunService\")\n\nlocal module = {}\n\nfunction module.onHeartbeatFor(duration, callback)\n\tlocal connection\n\tlocal timer = 0\n\t\n\tlocal function onHeartbeat(dt)\n\t\ttimer = timer + dt\n\t\t\n\t\t-- if we hit the timer ending disconnect\n\t\tif (duration > 0) and (timer >= duration) then\n\t\t\tconnection:Disconnect()\n\t\tend\n\t\t\n\t\t-- ternary operation\n\t\tlocal progress = (duration > 0) and math.min(timer / duration, 1) or 0\n\t\t\n\t\t-- call the callback, if it returns true then we end early\n\t\tif callback(dt, timer, progress) then\n\t\t\tconnection:Disconnect()\n\t\tend\n\tend\n\t\n\tconnection = runService.Heartbeat:Connect(onHeartbeat)\n\tonHeartbeat(0)\n\t\n\treturn connection\nend\n\nfunction module.hideWeapons(entity)\n\tlocal network = require(game.ReplicatedStorage.modules).load(\"network\")\n\t\n\tlocal weapons = network:invoke(\"getCurrentlyEquippedForRenderCharacter\", entity)\n\tif not weapons then return end\n\t\n\tlocal restores = {}\n\t\n\tfor _, weapon in pairs(weapons) do\n\t\tlocal manifest = weapon.manifest\n\t\tif manifest:IsA(\"BasePart\") then\n\t\t\ttable.insert(restores, {\n\t\t\t\tpart = manifest,\n\t\t\t\ttransparency = manifest.Transparency\n\t\t\t})\n\t\t\tmanifest.Transparency = 1\n\t\tend\n\t\tfor _, object in pairs(manifest:GetDescendants()) do\n\t\t\tif object:IsA(\"BasePart\") then\n\t\t\t\ttable.insert(restores, {\n\t\t\t\t\tpart = object,\n\t\t\t\t\ttransparency = object.Transparency\n\t\t\t\t})\n\t\t\t\tobject.Transparency = 1\n\t\t\tend\n\t\tend\n\tend\n\t\n\tlocal function callback()\n\t\tfor _, restore in pairs(restores) do\n\t\t\trestore.part.Transparency = restore.Transparency\n\t\tend\n\tend\n\t\n\treturn callback\nend\n\nreturn module\n"
  },
  {
    "path": "src/ReplicatedStorage/modules/enchantment.lua",
    "content": "local module = {}\n\nlocal runService = game:GetService(\"RunService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network = modules.load(\"network\")\n\t\tlocal itemLookup = require(game.ReplicatedStorage:WaitForChild(\"itemData\"))\t\n\nmodule.tierColors = {\n\t[-1] = Color3.new(0.7,0.7,0.7);\n\t[1] = Color3.new(1,1,1);\n\t[2] = Color3.fromRGB(112, 241, 255);\n\t[3] = Color3.fromRGB(165, 55, 255);\n\t[4] = Color3.fromRGB(235, 42, 87);\n\t[5] = Color3.fromRGB(255, 238, 0);\n\t[6] = Color3.fromRGB(0, 255, 0);\n}\n\nfunction module.enchantmentCanBeAppliedToItem(enchantmentSlotData, equipmentSlotData)\n\t-- returns bool canEnchant, int enchantmentIndexToRemove\n\tlocal enchantmentBaseData = itemLookup[enchantmentSlotData.id]\n\tlocal equipmentBaseData = itemLookup[equipmentSlotData.id]\n\t\n\tif equipmentSlotData.notUpgradable or equipmentBaseData.notUpgradable then\n\t\treturn false\n\tend\n\t\n\tif enchantmentBaseData.validation then\n\t\tif not enchantmentBaseData.validation(equipmentBaseData, equipmentSlotData) then\n\t\t\t-- blocked by scroll validation function\n\t\t\treturn false\n\t\tend\n\tend\n\t\n\tlocal upgradeCost = enchantmentBaseData.upgradeCost or 1\n\tlocal maxUpgrades = (equipmentBaseData.maxUpgrades or 0) + (equipmentSlotData.bonusUpgrades or 0)\n\t\n\tlocal enchantments = equipmentSlotData.enchantments or {}\n\t\n\tlocal existingUpgrades = equipmentSlotData.upgrades or 0\n\tif existingUpgrades + upgradeCost <= maxUpgrades then\n\t\treturn true\n\t--[[\n\telse\n\t\t\n\t\t-- allow higher-tier scrolls to override weaker upgrades\n\t\tlocal lowestTier = 999\n\t\tlocal index\n\t\tfor i,enchantment in pairs(enchantments) do\n\t\t\tlocal existingEnchantmentBaseData = itemLookup[enchantment.id]\n\t\t\tlocal tier = existingEnchantmentBaseData.enchantments[enchantment.state].tier or 1\n\t\t\tif tier < lowestTier then\n\t\t\t\tlowestTier = tier\n\t\t\t\tindex = i\n\t\t\tend\t\t\t\n\t\tend\n\t\tif (enchantmentBaseData.tier - 1) > lowestTier then\n\t\t\treturn true, index\n\t\tend\n\t]]\n\tend\nend\n\n\n\n-- everything below this is pretty much defunct\n\n\n\n\n\n\n\n\tlocal costMulti = {2/3, 3/2, 3, 5, 8, 10, 15}\n\n\tmodule.enchantmentPrice = function(itemInventorySlotData)\n\t\tif not itemInventorySlotData then\n\t\t\treturn false\n\t\tend\n\t\t\n\t\tlocal previousEnchants = itemInventorySlotData.enchantments or 0\n\t\tlocal itemLookup = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"itemData\"))\n\t\tlocal itemBaseData = itemLookup[itemInventorySlotData.id]\n\t\t\n\t\tif itemInventorySlotData.upgrades and itemInventorySlotData.upgrades >= 7 then\n\t\t\treturn false\n\t\tend\t\t\n\t\t\t\n\t\tif not itemBaseData.buyValue then\n\t\t\treturn false\n\t\tend\t\n\t\t\t\t\n\t\tif itemBaseData.category == \"equipment\" then\n\t\t\treturn math.ceil(itemBaseData.buyValue * costMulti [(itemInventorySlotData.upgrades or 0) + 1])\n\t\tend\n\tend\n\t\n\n\tmodule.applyEnchantment = function(itemInventorySlotData)\n\t\tlocal itemLookup = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"itemData\"))\n\t\tlocal itemBaseData = itemLookup[itemInventorySlotData.id]\n\t\t\n\t\tif itemInventorySlotData.upgrades and itemInventorySlotData.upgrades >= 7 then\n\t\t\treturn false\n\t\tend\n\t\t\n\t\tif itemBaseData.category == \"equipment\" then\n\t\t\n\t\t\tif not itemInventorySlotData.modifierData then\n\t\t\t\titemInventorySlotData.modifierData = {}\n\t\t\tend\n\t\t\t\n\t\t\tif not itemBaseData.buyValue then\n\t\t\t\treturn false\n\t\t\tend\t\t\t\t\n\t\t\t\n\t\t\tlocal modifierData = itemBaseData.modifierData and itemBaseData.modifierData[1] or {}\n\t\t\t\n\t\t\tlocal upgrades = {}\n\t\t\t\n\n\t\t\tlocal doBlessItem = itemInventorySlotData.enchantments and (itemInventorySlotData.enchantments + 1) == 7\n\n\t\t\tif not itemInventorySlotData.modifierData then\n\t\t\t\titemInventorySlotData.modifierData = {}\n\t\t\tend\n\t\t\t\n\t\t\n\t\t\t\n\t\t\tif itemBaseData.equipmentSlot == 1 then\n\t\t\t\n\t\t\t\tlocal modifierData = itemBaseData.modifierData and itemBaseData.modifierData[1] or {}\n\t\t\t\tlocal damage = (itemBaseData.baseDamage and itemBaseData.baseDamage > 0 and itemBaseData.baseDamage or 1)\n\t\t\t\t\t\t\t\t\t+ (modifierData.rangedDamage or 0) * 0.65 \n\t\t\t\t\t\t\t\t\t+ (modifierData.magicalDamage or 0) * 0.65 \n\t\t\t\t\t\t\t\t\t+ (modifierData.physicalDamage or 0) * 0.65 \t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t\tlocal multi = 0.06\n\t\t\t\t\n\t\t\t\tif doBlessItem and not itemBaseData.blessedUpgrade then\n\t\t\t\t\titemInventorySlotData.blessed = true\n\t\t\t\t\tmulti = 0.09\n\t\t\t\tend\n\t\t\t\n\t\t\t\tlocal damageBuff = math.clamp(math.floor((damage or 1) * multi), 1, math.huge)\n\n\t\t\t\t\t\t\n\t\t\t\tupgrades[\"baseDamage\"] = damageBuff;\n\t\t\t\t\t\n\t\t\telseif itemBaseData.equipmentSlot == 2 or itemBaseData.equipmentSlot == 8 or itemBaseData.equipmentSlot == 9 then\n\t\t\t\t\n\t\t\t\tlocal modifierData = itemBaseData.modifierData and itemBaseData.modifierData[1] or {}\n\t\t\t\t\n\t\t\t\tlocal baseDefense = (modifierData.defense and modifierData.defense > 0 and modifierData.defense or 1)\n\t\t\t\t\t\t\t\t\t+ (modifierData.rangedDefense or 0) * 0.65 \n\t\t\t\t\t\t\t\t\t+ (modifierData.magicalDefense or 0) * 0.65 \n\t\t\t\t\t\t\t\t\t+ (modifierData.physicalDefense or 0) * 0.65\n\t\t\t\t\t\t\t\t\t \t\t\n\t\t\t\t\t\t\t\t\t+ (modifierData.rangedDamage or 0) * 0.55\n\t\t\t\t\t\t\t\t\t+ (modifierData.magicalDamage or 0) * 0.55\n\t\t\t\t\t\t\t\t\t+ (modifierData.physicalDamage or 0) * 0.55\n\t\t\t\t\t\t\t\t\t+ (modifierData.equipmentDamage or 0) * 0.75\n\t\t\t\t\n\t\t\t\tlocal multi = 0.06\n\t\t\t\t\n\t\t\t\tif doBlessItem and not itemBaseData.blessedUpgrade then\n\t\t\t\t\titemInventorySlotData.blessed = true\n\t\t\t\t\tmulti = 0.09\n\t\t\t\tend\t\t\t\t\n\t\t\t\t\n\t\t\t\tlocal defensebuff = math.clamp(math.floor((baseDefense or 1) * multi), 1, math.huge)\n\t\t\t\t\n\t\t\t\t\n\t\t\t\tupgrades[\"defense\"] = defensebuff;\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\telse\n\t\t\t\treturn false\n\t\t\tend\n\t\t\t\n\t\t\tif doBlessItem and itemBaseData.blessedUpgrade then\n\t\t\t\tfor upgradeName, upgradeValue in pairs(itemBaseData.blessedUpgrade) do\n\t\t\t\t\tupgrades[upgradeName] = (upgrades[upgradeName] or 0) + upgradeValue\n\t\t\t\tend\n\t\t\t\titemInventorySlotData.blessed = true\n\t\t\tend\n\t\t\t\n\n\t\t\t\n\n\t\t\t\t\t\t\n\t\t\n\t\t\t--[[\n\t\t\tlocal baseDefense = (modifierData.defense and modifierData.defense > 0 and modifierData.defense or 1)\n\t\t\t\t\t\t\t\t+ (modifierData.rangedDefense or 0) * 0.65 \n\t\t\t\t\t\t\t\t+ (modifierData.magicalDefense or 0) * 0.65 \n\t\t\t\t\t\t\t\t+ (modifierData.physicalDefense or 0) * 0.65\n\t\t\t\t\t\t\t\t \t\t\n\t\t\t\t\t\t\t\t+ (modifierData.rangedDamage or 0) * 0.55\n\t\t\t\t\t\t\t\t+ (modifierData.magicalDamage or 0) * 0.55\n\t\t\t\t\t\t\t\t+ (modifierData.physicalDamage or 0) * 0.55\n\t\t\t\t\t\t\t\t+ (modifierData.equipmentDamage or 0) * 0.75\n\t\t\t]]\t\t\t\t\t\n\t\t\t--[[\n\t\t\ttable.insert(itemInventorySlotData.modifierData, {\n\t\t\t\tdefense = math.clamp(math.floor((baseDefense or 1) * 0.05), 1, math.huge)\n\t\t\t})\n\t\t\t]]\n\t\t\t\n\t\t\ttable.insert(itemInventorySlotData.modifierData, upgrades)\n\t\t\t\n\t\t\t\t\t\titemInventorySlotData.upgrades = (itemInventorySlotData.upgrades or 0) + 1\n\t\t\t\t\t\titemInventorySlotData.successfulUpgrades = (itemInventorySlotData.successfulUpgrades or 0) + 1\t\n\t\t\t\t\t\titemInventorySlotData.enchantments = (itemInventorySlotData.enchantments or 0) + 1\t\n\t\t\t\n\t\t\treturn itemInventorySlotData\n\t\tend\n\t\t\n\t\treturn nil\n\tend;\n\nreturn module\n"
  },
  {
    "path": "src/ReplicatedStorage/modules/entityUtilities.lua",
    "content": "local module = {}\n\nlocal lookupTable = {}\n\nfunction module.getEntityGUIDByEntityManifest(entityManifest)\n\tif entityManifest:IsA(\"Model\") then\n\t\tif not entityManifest.PrimaryPart then\n\t\t\treturn\n\t\tend\n\t\t\n\t\tentityManifest = entityManifest.PrimaryPart\n\tend\n\t\n\tlocal player\n\tif entityManifest.Parent and entityManifest.Parent:IsA(\"Model\") then\n\t\tplayer = game.Players:GetPlayerFromCharacter(entityManifest.Parent)\n\tend\n\t\n\t-- this is important because 1 player will always have the same GUID, but the character\n\t-- might change!\n\tif player then\n\t\treturn player.entityGUID.Value\n\telseif entityManifest:FindFirstChild(\"entityGUID\") then\n\t\treturn entityManifest.entityGUID.Value\n\tend\n\t\t\n\treturn nil\nend\n\nfunction module.getEntityManifestByEntityGUID(entityGUID)\n\treturn lookupTable[entityGUID]\nend\n\nlocal function onEntityManifestAdded(entityManifest)\n\tif entityManifest:IsA(\"Model\") then\n\t\tif not entityManifest.PrimaryPart then\n\t\t\treturn\n\t\tend\n\t\t\n\t\tentityManifest = entityManifest.PrimaryPart\n\tend\n\t\n\tlocal entityGUID = module.getEntityGUIDByEntityManifest(entityManifest)\n\t\n\tif entityGUID then\n\t\tlookupTable[entityGUID] = entityManifest\n\tend\nend\n\nlocal function onEntityManifestRemoved(entityManifest)\n\tif entityManifest:IsA(\"Model\") then\n\t\tif not entityManifest.PrimaryPart then\n\t\t\treturn\n\t\tend\n\t\t\n\t\tentityManifest = entityManifest.PrimaryPart\n\tend\n\t\n\t-- clear up references\n\tfor entityGUID, _entityManifest in pairs(lookupTable) do\n\t\tif _entityManifest == entityManifest then\n\t\t\tlookupTable[entityGUID] = nil\n\t\tend\n\tend\nend\n\n-- hookup events to stalk the entities folder\ncoroutine.wrap(function()\n\tworkspace:WaitForChild(\"placeFolders\"):WaitForChild(\"entityManifestCollection\")\n\t\n\tfor i, entityManifest in pairs(workspace.placeFolders.entityManifestCollection:GetChildren()) do\n\t\tonEntityManifestAdded(entityManifest)\n\tend\n\t\n\tworkspace.placeFolders.entityManifestCollection.ChildAdded:connect(onEntityManifestAdded)\n\tworkspace.placeFolders.entityManifestCollection.ChildRemoved:connect(onEntityManifestRemoved)\nend)()\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/events.lua",
    "content": "local module = {}\n\nlocal registrationsByGuid = {}\nlocal registrationGuidsByEventName = {}\n\nlocal httpService = game:GetService(\"HttpService\")\nlocal runService = game:GetService(\"RunService\")\n\nlocal modules = require(game.ReplicatedStorage.modules)\nlocal network = modules.load(\"network\")\n\nfunction module:registerForEvent(eventName, callback)\n\tlocal guid = httpService:GenerateGUID()\n\t\n\tlocal registration = {\n\t\tcallback = callback,\n\t\tguid = guid,\n\t\teventName = eventName,\n\t}\n\t\n\tregistrationsByGuid[guid] = registration\n\t\n\tif not registrationGuidsByEventName[eventName] then\n\t\tregistrationGuidsByEventName[eventName] = {}\n\tend\n\t\n\ttable.insert(registrationGuidsByEventName[eventName], guid)\n\t\n\treturn guid\nend\n\nfunction module:deregisterEventByGuid(guid)\n\tif not registrationsByGuid[guid] then return end\n\t\n\tlocal eventName = registrationsByGuid[guid].eventName\n\tregistrationsByGuid[guid] = nil\n\t\n\tlocal registrationGuids = registrationGuidsByEventName[eventName]\n\tif not registrationGuids then return end\n\t\n\tfor index = #registrationGuids, 1, -1 do\n\t\tlocal registrationGuid = registrationGuids[index]\n\t\tif registrationGuid == guid then\n\t\t\ttable.remove(registrationGuids, index)\n\t\tend\n\tend\nend\nmodule.deregisterFromEvent = module.deregisterEventByGuid\n\nfunction module:__invokeCallbacks(eventName, ...)\n\tlocal registrationGuids = registrationGuidsByEventName[eventName]\n\tif not registrationGuids then return end\n\t\n\tfor _, registrationGuid in pairs(registrationGuids) do\n\t\tlocal registration = registrationsByGuid[registrationGuid]\n\t\tif registration then\n\t\t\tregistration.callback(...)\n\t\tend\n\tend\nend\n\nfunction module:fireEventAll(eventName, ...)\n\tself:__invokeCallbacks(eventName, ...)\n\t\n\tlocal network = require(script.Parent).load(\"network\")\n\t\n\tif runService:IsServer() then\n\t\tnetwork:fireAllClients(\"fireEvent\", eventName, ...)\n\telse\n\t\tnetwork:fireServer(\"fireEvent\", eventName, ...)\n\tend\nend\n\nfunction module:fireEventLocal(eventName, ...)\n\tself:__invokeCallbacks(eventName, ...)\nend\n\nfunction module:fireEventPlayer(eventName, player, ...)\n\tif not runService:IsServer() then\n\t\terror(\"events:fireEventPlayer can only be called from the server.\")\n\tend\n\t\n\tself:__invokeCallbacks(eventName, player, ...)\n\t\n\tlocal network = require(script.Parent).load(\"network\")\n\tnetwork:fireClient(\"fireEvent\", player, eventName, ...)\nend\n\nfunction module:fireEventPlayers(eventName, players, ...)\n\tif not runService:IsServer() then\n\t\terror(\"events:fireEventPlayers can only be called from the server.\")\n\tend\n\t\n\tself:__invokeCallbacks(eventName, players, ...)\n\t\n\tlocal network = require(script.Parent).load(\"network\")\n\tfor _, player in pairs(players) do\n\t\tnetwork:fireClient(\"fireEvent\", player, eventName, ...)\n\tend\nend\n\nfunction module:fireEventExcluding(eventName, playerExcluded, ...)\n\tif not runService:IsServer() then\n\t\terror(\"events:fireEventExcluding can only be called from the server.\")\n\tend\n\t\n\tself:__invokeCallbacks(eventName, playerExcluded, ...)\n\t\n\tlocal network = require(script.Parent).load(\"network\")\n\tlocal players = game:GetService(\"Players\"):GetPlayers()\n\tfor _, player in pairs(players) do\n\t\tif player ~= playerExcluded then\n\t\t\tnetwork:fireClient(\"fireEvent\", player, eventName, ...)\n\t\tend\n\tend\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/init.lua",
    "content": "local module = {}\n\nfunction module.load(moduleName, doClone)\n\n\tif script:FindFirstChild(moduleName) then\n\t\treturn\n\t\t\tdoClone and require(script[moduleName]:Clone())\n\t\t\tor require(script[moduleName])\n\telse\n\t\t-- if we're on the client, wait for the module in case\n\t\t-- it's still replicating, if it doesn't show up in a\n\t\t-- reasonable amount of time, assume it was scripter\n\t\t-- error and notify them as such\n\t\tif game:GetService(\"RunService\"):IsClient() then\n\t\t\tlocal scr = script:WaitForChild(moduleName, 15)\n\t\t\t\n\t\t\tif not scr then\n\t\t\t\terror(\"Requesting module that probably doesn't exist: \"..moduleName)\n\t\t\tend\n\t\t\t\n\t\t\tif doClone then\n\t\t\t\treturn require(scr:Clone())\n\t\t\telse\n\t\t\t\treturn require(scr)\n\t\t\tend\n\t\tend\n\tend\n\t\n\treturn nil\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/init.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": false\n}"
  },
  {
    "path": "src/ReplicatedStorage/modules/levels.lua",
    "content": " \nlocal runService = game:GetService(\"RunService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network = modules.load(\"network\")\n\nlocal module = {}\n\n-- returns the total exp needed to reach this level\nfunction module.getEXPForLevel(level)\n\treturn 7 + 5*(level - 2) + 4*(level - 2)^2\nend\n\n\nfunction module.getTotalAP(playerData)\n\treturn playerData.level - 1\nend\n\n\n-- returns the exp granted from a quest at a specific level\nfunction module.getQuestGoldFromLevel(questLevel)\n\treturn 100 + math.floor(questLevel ^ 1.18 * 300)\nend\n\n-- returns the exp needed to gain the level above the given\nfunction module.getEXPToNextLevel(currentLevel)\n\treturn module.getEXPForLevel(currentLevel + 1)\nend\n\n-- returns the exp granted from a quest at a specific level\nfunction module.getQuestEXPFromLevel(questLevel)\n\treturn 10 + (module.getEXPToNextLevel(questLevel) * (1/1.5) * questLevel^(-1/6))\nend\n\nfunction module.getMonsterEXPFromLevel(questLevel)\n\tlocal killsToLevel = 7 * (1 + questLevel^1.21 - questLevel^1.1)\n\treturn module.getEXPToNextLevel(questLevel) * killsToLevel^-1\nend\n\n-- let andrew decide whatever\nfunction module.getBaseStatInfoForMonster(monsterLevel)\n\tmonsterLevel = 0--monsterLevel or 1\n\t\n\treturn {\n\t\tstr = monsterLevel * 1;\n\t\tint = monsterLevel * 1;\n\t\tdex = monsterLevel * 1;\n\t\tvit = monsterLevel * 1;\n\t}\nend\n\n\n\n\n-- returns main stat and cost of equipment based on the itemBaseData\nfunction module.getEquipmentInfo(itemBaseData)\n\tif itemBaseData then\n\t\tlocal level = itemBaseData.level or itemBaseData.minLevel\n\t\tif level then\n\t\t\tlocal stat = getStatForLevel(level)\t\t\n--\t\t\tlocal stat = 0.009*level^2 + 3*level + 12\n\n\t\t\t-- me: can we get good cost algorithm\n\t\t\t-- mom: we have good cost algorithm at home\n\t\t\t-- cost algorithm at home:\n\t\t\tlocal function sig(x)\n\t\t\t\tlocal e = 2.7182818284590452353602874713527\n\t\t\t\treturn 15 / (1 + e^(-1 * (0.25 * x - 9)))\n\t\t\tend\n\t\t\tlocal cost = 1.35 * module.getQuestGoldFromLevel(level) * level^(1/3) * math.max(sig(level), 1)\t\n\t\t\tlocal rarity = itemBaseData.rarity or \"Common\"\n\t\t\tif rarity == \"Legendary\" then\n\t\t\t\tcost = cost * 2\n\t\t\telseif rarity == \"Rare\" then\n\t\t\t\tcost = cost * 1.5\n\t\t\tend\n\t\t\tlocal modifierData \n\t\t\tif itemBaseData.equipmentSlot then\n\t\t\t\tif itemBaseData.equipmentSlot == 1 then\n\t\t\t\t\t-- weapons\n\t\t\t\t\tlocal damage = stat\t\t\t\t\t\n\t\t\t\t\tcost = cost * 0.7\n\t\t\t\t\tif rarity == \"Legendary\" then\n\t\t\t\t\t\tdamage = damage + 10\n\t\t\t\t\telseif rarity == \"Rare\" then\n\t\t\t\t\t\tdamage = damage + 5\n\t\t\t\t\tend\n\t\t\t\t\treturn {damage = math.ceil(damage); cost = math.floor(cost); }\n\t\t\t\t\n\t\t\t\telseif itemBaseData.equipmentSlot == 11 then\n\t\t\t\t\treturn {cost = math.floor(cost * 0.6)}\n\t\t\t\t\n\t\t\t\telse\n\t\t\t\t\tlocal statUpgrade\n\t\t\t\t\t\n\t\t\t\t\t-- armor\n\t\t\t\t\tlocal defense\n\t\t\t\t\tif itemBaseData.equipmentSlot == 8 then\n\t\t\t\t\t\t-- body\n\t\t\t\t\t\tdefense = stat \n\t\t\t\t\t\tif rarity == \"Legendary\" then\n\t\t\t\t\t\t\tdefense = defense + 10\n\t\t\t\t\t\telseif rarity == \"Rare\" then\n\t\t\t\t\t\t\tdefense = defense + 5\n\t\t\t\t\t\tend\n\t\t\t\t\t\tcost = cost * 1\n\t\t\t\t\t\t\n\t\t\t\t\t\tdefense = defense * (itemBaseData.defenseModifier or 1)\n\t\t\t\t\t\t\n\t\t\t\t\telseif itemBaseData.equipmentSlot == 9 then\n\t\t\t\t\t\t-- lower\n\t\t\t\t\t\tdefense = 0\n\t\t\t\t\t\tcost = cost * 0.35\n\t\t\t\t\telseif itemBaseData.equipmentSlot == 2 then\n\t\t\t\t\t\t-- hat\n\t\t\t\t\t\tdefense = 0\n\t\t\t\t\t\tcost = cost * 0.5\n\t\t\t\t\t\tlocal value = stat/5\n\t\t\t\t\t\tlocal distribution = itemBaseData.statDistribution\n\t\t\t\t\t\t\n\t\t\t\t\t\tif itemBaseData.minimumClass == \"hunter\" then\n\t\t\t\t\t\t\tdistribution = distribution or {\n\t\t\t\t\t\t\t\tstr = 0;\n\t\t\t\t\t\t\t\tdex = 1;\n\t\t\t\t\t\t\t\tint = 0;\n\t\t\t\t\t\t\t\tvit = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tstatUpgrade = {\n\t\t\t\t\t\t\t\tstr = 0;\n\t\t\t\t\t\t\t\tdex = 1;\n\t\t\t\t\t\t\t\tint = 0;\n\t\t\t\t\t\t\t\tvit = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\telseif itemBaseData.minimumClass == \"warrior\" then\n\t\t\t\t\t\t\tdistribution = distribution or {\n\t\t\t\t\t\t\t\tstr = 1;\n\t\t\t\t\t\t\t\tdex = 0;\n\t\t\t\t\t\t\t\tint = 0;\n\t\t\t\t\t\t\t\tvit = 0;\n\t\t\t\t\t\t\t}\t\t\n\t\t\t\t\t\t\tstatUpgrade = {\t\t\n\t\t\t\t\t\t\t\tstr = 1;\n\t\t\t\t\t\t\t\tdex = 0;\n\t\t\t\t\t\t\t\tint = 0;\n\t\t\t\t\t\t\t\tvit = 0;\n\t\t\t\t\t\t\t}\t\t\n\t\t\t\n\t\t\t\t\t\telseif itemBaseData.minimumClass == \"mage\" then\n\t\t\t\t\t\t\tdistribution = distribution or {\n\t\t\t\t\t\t\t\tstr = 0;\n\t\t\t\t\t\t\t\tdex = 0;\n\t\t\t\t\t\t\t\tint = 1;\n\t\t\t\t\t\t\t\tvit = 0;\n\t\t\t\t\t\t\t}\t\n\t\t\t\t\t\t\tstatUpgrade = {\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tstr = 0;\n\t\t\t\t\t\t\t\tdex = 0;\n\t\t\t\t\t\t\t\tint = 1;\n\t\t\t\t\t\t\t\tvit = 0;\n\t\t\t\t\t\t\t}\t\n\t\t\t\t\t\tend\t\n\t\t\t\t\t\tif distribution then\n\t\t\t\t\t\t\tmodifierData = {}\n\t\t\t\t\t\t\tfor stat, coefficient in pairs(distribution) do\n\t\t\t\t\t\t\t\tmodifierData[stat] = math.floor(value * coefficient)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n--\t\t\t\t\t\tmodifierData = {int = math.floor(value)}\t\t\t\t\t\n\t\t\t\t\tend\n\t\t\t\t\tif defense then\n\t\t\t\t\t\treturn {defense = math.ceil(defense); cost = math.floor(cost); modifierData = modifierData; statUpgrade = statUpgrade}\n\t\t\t\t\tend\n\t\t\t\t\treturn false\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\treturn false\nend\n\n\n\nfunction module.getPlayerTickHealing(player)\n\tif player and player.Character and player:FindFirstChild(\"level\") and player.Character.PrimaryPart then\n\t\tlocal level = player.level.Value\n\t\tlocal vit \t= player.vit.Value\n\t\t\n\t\treturn (0.24 * level) + (0.1 * vit)\n\tend\n\t\n\treturn 0\nend\n-- warning: this is just base crit chance. Doesn't include bonus crit chance from equipment\nfunction module.calculateCritChance(dex, playerLevel)\n\treturn 0 --math.clamp(0.4 * (dex / (3 * playerLevel)), 0, 1)\nend\n\nfunction module.getPlayerCritChance(player)\n\tif runService:IsServer() then\n\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\t\n\t\tif playerData then\n\t\t\treturn module.calculateCritChance(\n\t\t\t\tplayerData.nonSerializeData.statistics_final.dex,\n\t\t\t\tplayerData.level\n\t\t\t) + (playerData.nonSerializeData.statistics_final.criticalStrikeChance or 0)\n\t\tend\n\telse\n\t\twarn(\"attempt to call getPlayerCritChance on client\")\n\tend\n\t\n\treturn 0\nend\n\nfunction module.getAttackSpeed(dex)\n\treturn dex * 0.5 / 2\nend\n\nfunction module.getMonsterGoldForLevel(level)\n\treturn 10 + (3 * (level - 1))\nend\n\nfunction module.getStatPointsForLevel(level)\n\treturn 3 * (level-1)\nend\n\n-- returns the level associated with the exp\nfunction module.getLevelForEXP(exp)\n\treturn math.floor((5 * exp / 10) ^ (1 / 3))\nend\n\n\n-- returns how much exp you are past the exp required to earn your current level\nfunction module.getEXPPastCurrentLevel(currentEXP)\n\treturn currentEXP\n\t--return currentEXP - module.getEXPForLevel(math.floor(module.getLevelForEXP(currentEXP)))\nend\n\n-- returns fraction of how close you are until next level\nfunction module.getFractionForNextLevel(currentEXP)\n\treturn module.getEXPPastCurrentLevel(currentEXP) / module.getEXPToNextLevel(math.floor(module.getLevelForEXP(currentEXP)))\nend\n\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/levels_old.lua",
    "content": "\nlocal runService = game:GetService(\"RunService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network = modules.load(\"network\")\n\nlocal module = {}\n\n-- returns the total exp needed to reach this level\n\nfunction module.getBaseEXPForLevel(level)\n\treturn ((0.08/level)*level^6)/1.15\n--\treturn 0.0444+(1/level)*level^5\n--\treturn (10.5 * (level) ^ (3.24))\nend\n\n\n\nfunction module.getEXPForLevel(level)\n\treturn 70 + 100*(level-1)^1.3 + module.getBaseEXPForLevel(level) - module.getBaseEXPForLevel(level - 1)\nend\n\nlocal function getStatForLevel(level)\n\treturn 0.008*level^2 + 13*level^(1/2.1) + level\t\t\nend\n\nfunction module.getMonsterHealthForLevel(level)\n\treturn math.floor(70 + (60 * (level - 1)^1.07))\nend\n\nfunction module.getMonsterDefenseForLevel(level)\n\treturn getStatForLevel(level^1.02) \nend\n\nfunction module.getMonsterDamageForLevel(level)\n\treturn getStatForLevel(level^1.02) - 7\nend\n\n-- bounty data\nmodule.bountyPageInfo = {\n\t[\"1\"] = {\n\t\t{kills = 10;};\n\t\t{kills = 30;};\n\t\t{kills = 80;};\n\t};\n\t[\"2\"] = {\n\t\t{kills = 20;};\n\t\t{kills = 70;};\n\t\t{kills = 150;};\n\t};\t\n\t[\"3\"] = {\n\t\t{kills = 30;};\n\t\t{kills = 100;};\n\t\t{kills = 250;};\n\t};\t\n\t[\"99\"] = {\n\t\t{kills = 1;};\n\t\t{kills = 3;};\n\t\t{kills = 10;};\n\t};\t\t\n}\n\n\nfunction module.getBountyGoldReward(bountyInfo, monster)\n\treturn bountyInfo.kills * (module.goldMulti or 1) * module.getQuestGoldFromLevel(monster.level or 1)/25 \t\nend\n\n-- returns the exp granted from a quest at a specific level\nfunction module.getQuestGoldFromLevel(questLevel)\n\treturn 100 + math.floor(questLevel ^ 1.18 * 300)\nend\n\n-- returns the exp needed to gain the level above the given\nfunction module.getEXPToNextLevel(currentLevel)\n\treturn module.getEXPForLevel(currentLevel + 1)\nend\n\n-- returns the exp granted from a quest at a specific level\nfunction module.getQuestEXPFromLevel(questLevel)\n\treturn 10 + (module.getEXPToNextLevel(questLevel) * (1/1.5) * questLevel^(-1/6))\nend\n\n-- let andrew decide whatever\nfunction module.getBaseStatInfoForMonster(monsterLevel)\n\tmonsterLevel = 0--monsterLevel or 1\n\t\n\treturn {\n\t\tstr = monsterLevel * 1;\n\t\tint = monsterLevel * 1;\n\t\tdex = monsterLevel * 1;\n\t\tvit = monsterLevel * 1;\n\t}\nend\n\n\n\n-- returns main stat and cost of equipment based on the itemBaseData\nfunction module.getEquipmentInfo(itemBaseData)\n\tif itemBaseData then\n\t\tlocal level = itemBaseData.level or itemBaseData.minLevel\n\t\tif level then\n\t\t\tlocal stat = getStatForLevel(level)\t\t\n--\t\t\tlocal stat = 0.009*level^2 + 3*level + 12\n\t\t\tlocal cost = 1.35 * module.getQuestGoldFromLevel(level) * level^(1/3)\n\t\t\tlocal modifierData \n\t\t\tif itemBaseData.equipmentSlot then\n\t\t\t\tif itemBaseData.equipmentSlot == 1 then\n\t\t\t\t\t-- weapons\n\t\t\t\t\tlocal damage = stat\t\t\t\t\t\n\t\t\t\t\tcost = cost * 0.7\n\t\t\t\t\t\n\n\t\t\t\t\treturn {damage = math.ceil(damage); cost = math.floor(cost); }\n\t\t\t\t\n\t\t\t\telseif itemBaseData.equipmentSlot == 11 then\n\t\t\t\t\treturn {cost = math.floor(cost * 0.6)}\n\t\t\t\t\n\t\t\t\telse\n\t\t\t\t\tlocal upgradeStat\n\t\t\t\t\t\n\t\t\t\t\t-- armor\n\t\t\t\t\tlocal defense\n\t\t\t\t\tif itemBaseData.equipmentSlot == 8 then\n\t\t\t\t\t\t-- body\n\t\t\t\t\t\tdefense = stat\n\t\t\t\t\t\tcost = cost * 1\n\t\t\t\t\t\t\n\t\t\t\t\t\tif itemBaseData.noDefense == true then\n\t\t\t\t\t\t\tdefense = 0\n\t\t\t\t\t\tend\n\t\t\t\t\t\t\n\t\t\t\t\telseif itemBaseData.equipmentSlot == 9 then\n\t\t\t\t\t\t-- lower\n\t\t\t\t\t\tdefense = 0\n\t\t\t\t\t\tcost = cost * 0.35\n\t\t\t\t\telseif itemBaseData.equipmentSlot == 2 then\n\t\t\t\t\t\t-- hat\n\t\t\t\t\t\tdefense = 0\n\t\t\t\t\t\tcost = cost * 0.5\n\t\t\t\t\t\tlocal value = stat/5\n\t\t\t\t\t\tlocal distribution = itemBaseData.statDistribution\n\t\t\t\t\t\t\n\t\t\t\t\t\tif itemBaseData.minimumClass == \"hunter\" then\n\t\t\t\t\t\t\tdistribution = distribution or {\n\t\t\t\t\t\t\t\tstr = 0;\n\t\t\t\t\t\t\t\tdex = 1;\n\t\t\t\t\t\t\t\tint = 0;\n\t\t\t\t\t\t\t\tvit = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tupgradeStat = {\n\t\t\t\t\t\t\t\tstr = 0;\n\t\t\t\t\t\t\t\tdex = 1;\n\t\t\t\t\t\t\t\tint = 0;\n\t\t\t\t\t\t\t\tvit = 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\telseif itemBaseData.minimumClass == \"warrior\" then\n\t\t\t\t\t\t\tdistribution = distribution or {\n\t\t\t\t\t\t\t\tstr = 1;\n\t\t\t\t\t\t\t\tdex = 0;\n\t\t\t\t\t\t\t\tint = 0;\n\t\t\t\t\t\t\t\tvit = 0;\n\t\t\t\t\t\t\t}\t\t\n\t\t\t\t\t\t\tupgradeStat = {\t\t\n\t\t\t\t\t\t\t\tstr = 1;\n\t\t\t\t\t\t\t\tdex = 0;\n\t\t\t\t\t\t\t\tint = 0;\n\t\t\t\t\t\t\t\tvit = 0;\n\t\t\t\t\t\t\t}\t\t\n\t\t\t\n\t\t\t\t\t\telseif itemBaseData.minimumClass == \"mage\" then\n\t\t\t\t\t\t\tdistribution = distribution or {\n\t\t\t\t\t\t\t\tstr = 0;\n\t\t\t\t\t\t\t\tdex = 0;\n\t\t\t\t\t\t\t\tint = 1;\n\t\t\t\t\t\t\t\tvit = 0;\n\t\t\t\t\t\t\t}\t\n\t\t\t\t\t\t\tupgradeStat = {\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tstr = 0;\n\t\t\t\t\t\t\t\tdex = 0;\n\t\t\t\t\t\t\t\tint = 1;\n\t\t\t\t\t\t\t\tvit = 0;\n\t\t\t\t\t\t\t}\t\n\t\t\t\t\t\tend\t\n\t\t\t\t\t\tif distribution then\n\t\t\t\t\t\t\tmodifierData = {}\n\t\t\t\t\t\t\tfor stat, coefficient in pairs(distribution) do\n\t\t\t\t\t\t\t\tmodifierData[stat] = math.floor(value * coefficient)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n--\t\t\t\t\t\tmodifierData = {int = math.floor(value)}\t\t\t\t\t\n\t\t\t\t\tend\n\t\t\t\t\tif defense then\n\t\t\t\t\t\treturn {defense = math.ceil(defense); cost = math.floor(cost); modifierData = modifierData; upgradeStat = upgradeStat}\n\t\t\t\t\tend\n\t\t\t\t\treturn false\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\treturn false\nend\n\n\n\nfunction module.getPlayerTickHealing(player)\n\tif player and player.Character and player:FindFirstChild(\"level\") and player.Character.PrimaryPart then\n\t\tlocal level = player.level.Value\n\t\tlocal vit \t= player.vit.Value\n\t\t\n\t\treturn (0.24 * level) + (0.1 * vit)\n\tend\n\t\n\treturn 0\nend\n-- warning: this is just base crit chance. Doesn't include bonus crit chance from equipment\nfunction module.calculateCritChance(dex, playerLevel)\n\treturn 0 --math.clamp(0.4 * (dex / (3 * playerLevel)), 0, 1)\nend\n\nfunction module.getPlayerCritChance(player)\n\tif runService:IsServer() then\n\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\t\n\t\tif playerData then\n\t\t\treturn module.calculateCritChance(\n\t\t\t\tplayerData.nonSerializeData.statistics_final.dex,\n\t\t\t\tplayerData.level\n\t\t\t) + (playerData.nonSerializeData.statistics_final.criticalStrikeChance or 0)\n\t\tend\n\telse\n\t\twarn(\"attempt to call getPlayerCritChance on client\")\n\tend\n\t\n\treturn 0\nend\n\nfunction module.getAttackSpeed(dex)\n\treturn dex * 0.5 / 2\nend\n\nfunction module.getMonsterGoldForLevel(level)\n\treturn 10 + (3 * (level - 1))\nend\n\nfunction module.getStatPointsForLevel(level)\n\treturn 3 * (level-1)\nend\n\n-- returns the level associated with the exp\nfunction module.getLevelForEXP(exp)\n\treturn math.floor((5 * exp / 10) ^ (1 / 3))\nend\n\n\n-- returns how much exp you are past the exp required to earn your current level\nfunction module.getEXPPastCurrentLevel(currentEXP)\n\treturn currentEXP\n\t--return currentEXP - module.getEXPForLevel(math.floor(module.getLevelForEXP(currentEXP)))\nend\n\n-- returns fraction of how close you are until next level\nfunction module.getFractionForNextLevel(currentEXP)\n\treturn module.getEXPPastCurrentLevel(currentEXP) / module.getEXPToNextLevel(math.floor(module.getLevelForEXP(currentEXP)))\nend\n\n-- calculates the exp gained for a monster kill \nfunction module.getEXPGainedFromMonsterKill(baseMonsterEXP, monsterLevel, playerCurrentLevel)\n\t-- general formula for this seems to be (baseMultiFactor * levelDifferenceFactor + minimumEXP) * totalMultiFactor\n\t-- we should always at least grant the player 1 EXP\n\t-- the formula below is just pulled from Pokemon, we'll customize it later\n--\treturn ((baseMonsterEXP * monsterLevel ^ 0.9) / 8) * ((2 * monsterLevel + 10) ^ 2.5 / (monsterLevel + playerCurrentLevel + 10) ^ 2.5) + 1\n\t\n\tlocal levelDifference = playerCurrentLevel - monsterLevel\n\tlocal levelMulti = math.clamp(1 - levelDifference / 7, 0.25, 1.5)\n\t\n\treturn baseMonsterEXP * levelMulti \nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/levels_older.lua",
    "content": "local module = {}\nlocal runService = game:GetService(\"RunService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network = modules.load(\"network\")\n\n-- returns the total exp needed to reach this level\nfunction module.getEXPForLevel(level)\n\treturn 70 + (10 * level ^ (3.35)) - (10 * (level - 1) ^ (3.35))\nend\n\nfunction module.getMonsterHealthForLevel(level)\n\treturn 100 + (70 * (level - 1))\nend\n\nfunction module.getMonsterDamageForLevel(level)\n\treturn 8 + (12 * (level - 1))\nend\n\nfunction module.getPlayerTickHealing(player)\n\tif player and player.Character and player:FindFirstChild(\"level\") and player.Character.PrimaryPart then\n\t\tlocal level = player.level.Value\n\t\tlocal vit \t= player.vit.Value\n\t\t\n\t\treturn (0.24 * level) + (0.1 * vit)\n\tend\n\t\n\treturn 0\nend\n-- warning: this is just base crit chance. Doesn't include bonus crit chance from equipment\nfunction module.calculateCritChance(dex, playerLevel)\n\treturn math.clamp(0.4 * (dex / (3 * playerLevel)), 0, 1)\nend\n\nfunction module.getPlayerCritChance(player)\n\tif runService:IsServer() then\n\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\t\n\t\tif playerData then\n\t\t\treturn module.calculateCritChance(\n\t\t\t\tplayerData.nonSerializeData.statistics_final.dex,\n\t\t\t\tplayerData.level\n\t\t\t) + (playerData.nonSerializeData.statistics_final.criticalStrikeChance or 0)\n\t\tend\n\telse\n\t\twarn(\"attempt to call getPlayerCritChance on client\")\n\tend\n\t\n\treturn 0\nend\n\nfunction module.getAttackSpeed(dex)\n\treturn dex * 0.5 / 2\nend\n\nfunction module.getMonsterGoldForLevel(level)\n\treturn 10 + (3 * (level - 1))\nend\n\nfunction module.getStatPointsForLevel(level)\n\treturn 3 * level\nend\n\n-- returns the level associated with the exp\nfunction module.getLevelForEXP(exp)\n\treturn math.floor((5 * exp / 10) ^ (1 / 3))\nend\n\n-- returns the exp needed to gain the level above the given\nfunction module.getEXPToNextLevel(currentLevel)\n\treturn module.getEXPForLevel(currentLevel + 1)\nend\n\n-- returns the exp granted from a quest at a specific level\nfunction module.getQuestEXPFromLevel(questLevel)\n\treturn 30 + ((7 * questLevel ^ (3.35)) - (7 * (questLevel - 1) ^ (3.35)))\nend\n\n-- returns the exp granted from a quest at a specific level\nfunction module.getQuestGoldFromLevel(questLevel)\n\treturn questLevel * 50\nend\n\n-- returns how much exp you are past the exp required to earn your current level\nfunction module.getEXPPastCurrentLevel(currentEXP)\n\treturn currentEXP\n\t--return currentEXP - module.getEXPForLevel(math.floor(module.getLevelForEXP(currentEXP)))\nend\n\n-- returns fraction of how close you are until next level\nfunction module.getFractionForNextLevel(currentEXP)\n\treturn module.getEXPPastCurrentLevel(currentEXP) / module.getEXPToNextLevel(math.floor(module.getLevelForEXP(currentEXP)))\nend\n\n-- calculates the exp gained for a monster kill \nfunction module.getEXPGainedFromMonsterKill(baseMonsterEXP, monsterLevel, playerCurrentLevel)\n\t-- general formula for this seems to be (baseMultiFactor * levelDifferenceFactor + minimumEXP) * totalMultiFactor\n\t-- we should always at least grant the player 1 EXP\n\t-- the formula below is just pulled from Pokemon, we'll customize it later\n--\treturn ((baseMonsterEXP * monsterLevel ^ 0.9) / 8) * ((2 * monsterLevel + 10) ^ 2.5 / (monsterLevel + playerCurrentLevel + 10) ^ 2.5) + 1\n\t\n\tlocal levelDifference = playerCurrentLevel - monsterLevel\n\tlocal levelMulti = math.clamp(1 - levelDifference / 10, 0.1, 1.5)\n\t\n\treturn baseMonsterEXP * levelMulti \nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/localization.lua",
    "content": "local module = {}\nlocal lookup = {}\n\tlookup.color = {\n\t\tb = BrickColor.Blue().Color;\n\t}\n\n\tlookup.font = {\n\t\tsourceSansBold = Enum.Font.SourceSansBold;\n\t}\n\nlocal localizationService = game:GetService(\"LocalizationService\")\n\n-- this module is client-only, shouldnt even be in replicated storage, but I cant move it right now\nif game:GetService(\"RunService\"):IsServer() then\n\treturn module\nend\n\n-- this is a temp. translator with no yield, does not include web translations\nlocal translator = localizationService:GetTranslatorForPlayer(game.Players.LocalPlayer)\n\n-- fetch the web translations\nspawn(function()\n\tlocal success, err\n\tlocal n = 0\n\trepeat\n\t\tif err then\n\t\t\twarn(\"Failed to access cloud translations:\", err)\n\t\t\twait(n * 3)\n\t\tend\n\t\tsuccess, err = pcall(function()\n\t\t\ttranslator = localizationService:GetTranslatorForPlayerAsync(game.Players.LocalPlayer)\n\t\tend)\n\t\tn = n + 1\n\tuntil success\nend)\n\nfunction module.translate(str, context)\n\tcontext = context or game\n\treturn translator:Translate(context, str) or str\nend\n\nfunction module.convertToVesteriaDialogueTable(str)\n\tlocal dialogueTable = {}\n\tlocal currentSubDialogue = {}\n\n\tif str:sub(1, 1) ~= \"[\" then\n\t\tstr = \"[]\" .. str\n\tend\n\n\n\tif str:sub(#str, #str) ~= \"]\" then\n\t\tstr = str .. \"[]\"\n\tend\n\n\tfor stylingData, text in string.gmatch(str, \"([%w%;%:%=]-)%](.-)%[\") do\n\t\tlocal block = {}\n\t\t\tblock.text = text\n\n\t\tfor styleType, style in string.gmatch(stylingData, \"(%w+)%=(%w+)\") do\n\t\t\tif (styleType == \"font\" or styleType == \"f\") then\n\t\t\t\tblock.font = lookup.font[style] or Enum.Font.SourceSans\n\t\t\telseif (styleType == \"color\" or styleType == \"c\") then\n\t\t\t\tblock.textColor3 = lookup.color[style] or Color3.fromRGB(255, 255, 255)\n\t\t\tend\n\t\tend\n\n\t\ttable.insert(dialogueTable, block)\n\tend\n\n\treturn dialogueTable\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/mapping.lua",
    "content": "local module = {}\n\tmodule.equipmentPosition = {}\n\t\tmodule.equipmentPosition.weapon = 1\n\t\tmodule.equipmentPosition.head = 2\n\n\t\t-- unused --\n\t\tmodule.equipmentPosition.body = 3\n\t\tmodule.equipmentPosition[\"left arm\"] = 4\n\t\tmodule.equipmentPosition[\"right arm\"] = 5\n\t\tmodule.equipmentPosition[\"left leg\"] = 6\n\t\tmodule.equipmentPosition[\"right leg\"] = 7\n\t\t-- /unused --\n\n\t\tmodule.equipmentPosition[\"upper\"] = 8\n\t\tmodule.equipmentPosition[\"lower\"] = 9\n\n\t\tmodule.equipmentPosition[\"pet\"] = 10\n\n\t\tmodule.equipmentPosition[\"offhand\"] = 11\n\n\t\tmodule.equipmentPosition[\"arrow\"] = 12\n\n\tmodule.dataType = {}\n\t\tmodule.dataType.item = 1\n\t\tmodule.dataType.ability = 2\n\t\tmodule.dataType.abilitySlot = 3\n\n\tmodule.gripType = {}\n\t\tmodule.gripType.right = 1\n\t\tmodule.gripType.left = 2\n\t\tmodule.gripType.both = 2\n\n\tmodule.equipmentHairType = {}\n\t\tmodule.equipmentHairType.all = 1\n\t\tmodule.equipmentHairType.partial = 2\n\t\tmodule.equipmentHairType.none = 3\n\n\tmodule.accountBanState = {}\n\t\tmodule.accountBanState.warned = 1\n\t\tmodule.accountBanState.day1 = 2\n\t\tmodule.accountBanState.day3 = 3\n\t\tmodule.accountBanState.day7 = 4\n\t\tmodule.accountBanState.permanent = 5\n\n\tmodule.questState = {}\n\t\tmodule.questState.accepted = 1\n\t\tmodule.questState.active = 2\n\t\tmodule.questState.completed = 3\n\t\tmodule.questState.cooldown = 4\n\t\tmodule.questState.insufficient = 5\n\t\tmodule.questState.denied = 6\n\t\tmodule.questState.handing = 7\n\t\tmodule.questState.unassigned = 8\n\t\tmodule.questState.objectiveDone = 9\n\n\n\t--hat, skin, undershirt, underwear, eyebrow\n\tmodule.accessoryType = {}\n\t\tmodule.accessoryType.hair = 1\n\t\tmodule.accessoryType.skin = 2\n\t\tmodule.accessoryType.eyebrow = 3\n\t\tmodule.accessoryType.undershirt = 4\n\t\tmodule.accessoryType.underwear = 5\n\nfunction module.getMappingByValue(mapSection, value)\n\tfor i, v in pairs(module[mapSection]) do\n\t\tif v == value then\n\t\t\treturn i\n\t\tend\n\tend\n\n\treturn nil\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/network.lua",
    "content": "local RunService \t= game:GetService(\"RunService\")\nlocal module \t\t= {}\n\nlocal RemoteEvent \t\t= Instance.new(\"RemoteEvent\")\nlocal RemoteFunction \t= Instance.new(\"RemoteFunction\")\nlocal BindableEvent \t= Instance.new(\"BindableEvent\")\nlocal BindableFunction \t= Instance.new(\"BindableFunction\")\n\nlocal log = {}\nmodule.log = log\n\nlocal methods = {\n\t-- camelCase method pointers\n\tfireServer \t\t= RemoteEvent.FireServer;\n\tfireClient \t\t= RemoteEvent.FireClient;\n\tfireAllClients \t= RemoteEvent.FireAllClients;\n--\tinvokeServer \t= RemoteFunction.InvokeServer;\n\tinvokeClient \t= RemoteFunction.InvokeClient;\n\tfire \t\t\t= BindableEvent.Fire;\n\tinvoke \t\t\t= BindableFunction.Invoke;\n\t\n\t-- fire for table of players\n\tfireClients = function(obj, players, ...)\n\t\tfor i, player in pairs(players) do\n\t\t\tobj:FireClient(player, ...)\n\t\tend\n\tend;\n\t\n\t-- fire for all players but one\n\tfireAllClientsExcludingPlayer = function(obj, excludedPlayer, ...)\n\t\tfor i, player in pairs(game.Players:GetPlayers()) do\n\t\t\tif player ~= excludedPlayer then\n\t\t\t\tobj:FireClient(player, ...)\n\t\t\tend\n\t\tend\n\tend;\n\t\n\t-- fire based on custom checker function\n\tfireAllClientsCustomCheck = function(obj, checker, ...)\n\t\tfor i, player in pairs(game.Players:GetPlayers()) do\n\t\t\tif checker(player) == true then\n\t\t\t\tobj:FireClient(player, ...)\n\t\t\tend\n\t\tend\n\tend;\n\t\n\t-- invoke table of players and gather their responses\n\tinvokeClients = function(obj, players, ...)\n\t\tlocal output = {}\n\t\tfor i, player in pairs(players) do\n\t\t\toutput[player] = obj:InvokeClient(player, ...)\n\t\tend\n\t\t\n\t\treturn output\n\tend;\n\t\n\t-- invoke all clients and gather their responses\n\tinvokeAllClients = function(obj, ...)\n\t\tlocal output = {}\n\t\tfor i, player in pairs(game.Players:GetPlayers()) do\n\t\t\toutput[player] = obj:InvokeClient(player, ...)\n\t\tend\n\t\t\n\t\treturn output\n\tend;\n\t\n\t-- invoke all clients excluding one player and gather their responses\n\tinvokeAllClientsExcludingPlayer = function(obj, excludedPlayer, ...)\n\t\tlocal output = {}\n\t\tfor i, player in pairs(game.Players:GetPlayers()) do\n\t\t\tif player ~= excludedPlayer then\n\t\t\t\toutput[player] = obj:InvokeClient(player, ...)\n\t\t\tend\n\t\tend\n\t\t\n\t\treturn output\n\tend;\n\t\n\t-- invoke based on custom checker and gather their responses\n\tinvokeAllClientsCustomCheck = function(obj, checker, ...)\n\t\tlocal output = {}\n\t\tfor i, player in pairs(game.Players:GetPlayers()) do\n\t\t\tif checker(player) == true then\n\t\t\t\toutput[player] = obj:InvokeClient(player, ...)\n\t\t\tend\n\t\tend\n\t\t\n\t\treturn output\n\tend;\n}\n\n\n\n-- creates new remote/bindable events/functions and allows immediate connection\nfunction module:create(objName, objClass, connection, func)\n\t\n\tlocal parent = script\n\t\n\tif RunService:IsServer() then\n--\t\tif objClass == \"RemoteEvent\" and connection == \"OnServerEvent\" then\n--\t\t\tobjClass = \"BindableEvent\"\n--\t\t\tconnection = \"Event\"\n--\t\t\tparent = game.ServerStorage.serverNetwork\n\t\tif objClass == \"RemoteFunction\" and connection == \"OnServerInvoke\" then\n\t\t\tobjClass = \"BindableFunction\"\n\t\t\tconnection = \"OnInvoke\"\n\t\t\tparent = game.ServerStorage.serverNetwork\t\n\t\telseif objClass == \"BindableEvent\" then\n\t\t\tparent = game.ServerStorage.serverNetwork\t\n\t\telseif objClass == \"BindableFunction\" then\n\t\t\tparent = game.ServerStorage.serverNetwork\t\n\t\tend\n\tend\t\n\t\n\tif parent:FindFirstChild(objName) == nil then\n\n\t\tlocal obj = Instance.new(objClass)\n\t\tobj.Name = objName\t\t\n\t\t\t\t\t\t\n\t\tif connection and func then\n\t\t\tif objClass == \"BindableEvent\" or objClass == \"RemoteEvent\" then\n\t\t\t\tlocal event = obj[connection]:connect(func)\n\t\t\t\tobj.Parent = parent\n\t\t\t\t\n\t\t\t\treturn obj, event\n\t\t\telseif objClass == \"BindableFunction\" or objClass == \"RemoteFunction\" then\n\t\t\t\tobj[connection] = func\n\t\t\t\tobj.Parent = parent\n\t\t\t\t\n\t\t\t\treturn obj\n\t\t\tend\n\t\tend\n\t\t\n\t\tobj.Parent = parent\n\telse\n\t\tlocal obj = parent[objName]\n\t\tif connection ~= nil and func ~= nil then\n\t\t\tif objClass == \"BindableEvent\" or objClass == \"RemoteEvent\" then\n\t\t\t\tlocal event = obj[connection]:connect(func)\n\t\t\t\tobj.Parent = parent\n\t\t\t\t\n\t\t\t\treturn obj, event\n\t\t\telseif objClass == \"BindableFunction\" or objClass == \"RemoteFunction\" then\n\t\t\t\tobj[connection] = func\n\t\t\t\tobj.Parent = parent\n\t\t\t\t\n\t\t\t\treturn obj\n\t\t\tend\n\t\tend\n\t\t\n\t\treturn obj\n\tend\nend\n\n-- connection to bindable/remote event/functions\nfunction module:connect(objName, connection, func)\n\t\n\tlocal obj\n\t\n\tif RunService:IsServer() then\n--\t\tif connection == \"OnServerEvent\" then\n--\t\t\tconnection = \"Event\"\n--\t\t\tobj = game.ServerStorage.serverNetwork:WaitForChild(objName)\n\t\tif connection == \"OnServerInvoke\" then\n\t\t\tconnection = \"OnInvoke\"\n\t\t\tobj = game.ServerStorage.serverNetwork:WaitForChild(objName)\t\n\t\telseif connection == \"Event\" or connection == \"OnInvoke\" then\n\t\t\tobj = game.ServerStorage.serverNetwork:WaitForChild(objName)\t\n\t\telse\n\t\t\tobj = script:WaitForChild(objName)\n\t\tend\n\telse\n\t\tobj = script:WaitForChild(objName)\n\tend\t\t\n\t\n\tif obj.ClassName == \"BindableEvent\" or obj.ClassName == \"RemoteEvent\" then\n\t\tlocal event = obj[connection]:connect(func)\n\t\treturn obj, event\n\telseif obj.ClassName == \"BindableFunction\" or obj.ClassName == \"RemoteFunction\" then\n\t\tobj[connection] = func\n\t\treturn obj\n\tend\nend\n\nlocal function report(objName, method)\n\tlog[method] = log[method] or {}\n\tlog[method][objName] = (log[method][objName] or 0) + 1\nend\n\nfunction module:invokeServer(objName, ...)\n\treport(objName, \"invokeServer\")\n\tlocal playerRequest = game.ReplicatedStorage:WaitForChild(\"playerRequest\")\n\tif playerRequest then\n\t\treturn playerRequest:InvokeServer(objName, ...)\n\telse\n\t\terror(\"playerRequest not found\")\n\tend\nend\n--[[\nfunction module:fireServer(objName, ...)\n\tlocal signal = game.ReplicatedStorage:WaitForChild(\"signal\")\n\tif signal then\n\t\treturn signal:FireServer(objName, ...)\n\telse\n\t\terror(\"signal not found\")\n\tend\t \nend\n]]\nlocal function main()\n\t-- setup primary module metatable that hooks the methods up\n\tsetmetatable(module, {\n\t\t__index = function(self, method)\n\t\t\treturn function(self, objName, ...)\n\t\t\t\tlocal parent = script\n\t\t\t\tif RunService:IsServer() and (method == \"invoke\" or method == \"fire\") then\n\t\t\t\t\tparent = game.ServerStorage.serverNetwork\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\tparent:WaitForChild(objName, 60)\n\t\t\t\treport(objName, method)\n--\t\t\t\tif method == \"fireClient\" or method == \"fireAllClients\" or method == \"fireServer\" then\n--\t\t\t\t\twarn(\"$net\",objName,method,...)\n--\t\t\t\tend\n\t\t\t\treturn methods[method](parent[objName], ...)\n\t\t\tend\n\t\tend;\n\t})\nend\n\nmain()\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/pathfinding.lua",
    "content": "local module = {}\n\nfunction module.isPathValid(monster)\n\treturn true\nend\n\nfunction module.isBetweenPathfindingNodes(previousPosition, currentPosition, nextPosition)\n\tlocal projection = (previousPosition - nextPosition):Dot((currentPosition - nextPosition))\n\tlocal magnitude = (previousPosition - nextPosition).magnitude\n\treturn projection > 0 and magnitude ^ 2 > projection\nend\n\nfunction module.isPastNextPathfindingNodeNode(previousPosition, currentPosition, nextPosition)\n\tlocal adjustPreviousPosition = previousPosition - Vector3.new(0, previousPosition.Y, 0)\n\tlocal adjustCurrentPosition = currentPosition - Vector3.new(0, currentPosition.Y, 0)\n\tlocal adjustNextPosition = nextPosition - Vector3.new(0, nextPosition.Y, 0)\n\t\n\treturn (adjustPreviousPosition - adjustNextPosition):Dot((adjustCurrentPosition - adjustNextPosition)) <= 2\nend\n\nfunction module.dropPosition(pos, IGNORE_LIST)\n\tlocal ray = Ray.new(\n\t\tpos + Vector3.new(0, 1, 0),\n\t\tVector3.new(0, -999, 0)\n\t)\n\t\n\tlocal hitPart, hitPosition = workspace:FindPartOnRayWithIgnoreList(ray, IGNORE_LIST)\n\t\n\treturn hitPosition\nend\n\nfunction module.didReachPosition(previousPosition, currentPosition, nextPosition)\n\treturn (previousPosition - currentPosition):Dot(nextPosition - currentPosition) >= 0\nend\n\nfunction module.adjustPathForProjectors(path, IGNORE_LIST)\n\tlocal waypoints = path:GetWaypoints()\n\tlocal fixWaypoints = {}\n\tlocal doesNeedToJump = false\n\t\n\tfor i, pathWaypoint in pairs(waypoints) do\n\t\tif pathWaypoint.Action == Enum.PathWaypointAction.Jump then\n\t\t\tdoesNeedToJump = true\n\t\tend\n\t\t\n\t\t--table.insert(fixWaypoints, PathWaypoint.new(pathWaypoint.Position, pathWaypoint.Action))\n\tend\n\t\n\treturn waypoints\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/physics.lua",
    "content": "local module = {}\nlocal physicsService = game:GetService(\"PhysicsService\")\n\nlocal valid = {}\n\n-- returns a valid group id (int) or nil\nfunction module:getCollisionGroup(name)\n\tlocal ok, groupId = pcall(physicsService.GetCollisionGroupId, physicsService, name)\n\t\n\tif not ok then\n\t\t-- Create may fail if we have hit the maximum of 32 different groups\n\t\tok, groupId = pcall(physicsService.CreateCollisionGroup, physicsService, name)\n\tend\n\t\n\tif ok and not valid[name] then\n\t\tvalid[name] = true\n\tend\n\t\n\treturn ok and groupId or nil\nend\n\nfunction module:setWholeCollisionGroup(obj, name)\n--\tif valid[name] then\n\t\tif obj:IsA(\"BasePart\") then\n\t\t\tphysicsService:SetPartCollisionGroup(obj, name)\n\t\tend\n\t\t\n\t\tfor i, v in pairs(obj:GetChildren()) do\n\t\t\tself:setWholeCollisionGroup(v, name)\n\t\tend\n--\telse\n--\t\terror(\"invalid collision group\")\n--\tend\nend\n\nfunction module:removeWholeCollisionGroup(obj, name)\n--\tif valid[name] then\n\t\tif obj:IsA(\"BasePart\") then\n\t\t\tphysicsService:RemoveCollisionGroup(obj, name)\n\t\tend\n\t\t\n\t\tfor i, v in pairs(obj:GetChildren()) do\n\t\t\tself:removeWholeCollisionGroup(v, name)\n\t\tend\n--\telse\n--\t\terror(\"invalid collision group\")\n--\tend\nend\n\nlocal function main()\n\t\n\tif game:GetService(\"RunService\"):IsClient() then\n\t\treturn \n\tend\n\t\n\tmodule:getCollisionGroup(\"passthrough\")\n\tmodule:getCollisionGroup(\"items\")\n\tmodule:getCollisionGroup(\"characters\")\n\tmodule:getCollisionGroup(\"pvpCharacters\")\n\tmodule:getCollisionGroup(\"monsters\")\n\tmodule:getCollisionGroup(\"monstersLocal\")\n\tmodule:getCollisionGroup(\"npcs\")\n\tmodule:getCollisionGroup(\"antiJumpHitbox\")\n\tmodule:getCollisionGroup(\"fishingSpots\")\n\t\n\tphysicsService:CollisionGroupSetCollidable(\"items\", \"items\", false)\n\tphysicsService:CollisionGroupSetCollidable(\"items\", \"characters\", false)\n\tphysicsService:CollisionGroupSetCollidable(\"items\", \"monsters\", false)\n\tphysicsService:CollisionGroupSetCollidable(\"items\", \"npcs\", false)\n\tphysicsService:CollisionGroupSetCollidable(\"items\", \"fishingSpots\", false)\n\tphysicsService:CollisionGroupSetCollidable(\"antiJumpHitbox\", \"antiJumpHitbox\", true)\n\t\n\tphysicsService:CollisionGroupSetCollidable(\"characters\", \"Default\", true)\n\tphysicsService:CollisionGroupSetCollidable(\"characters\", \"monsters\", false)\n\tphysicsService:CollisionGroupSetCollidable(\"characters\", \"characters\", true)\n\tphysicsService:CollisionGroupSetCollidable(\"characters\", \"fishingSpots\", false)\n\t\n\tphysicsService:CollisionGroupSetCollidable(\"pvpCharacters\", \"Default\", true)\n\tphysicsService:CollisionGroupSetCollidable(\"pvpCharacters\", \"monsters\", false)\n\tphysicsService:CollisionGroupSetCollidable(\"pvpCharacters\", \"pvpCharacters\", true)\n\t\n\tphysicsService:CollisionGroupSetCollidable(\"passthrough\", \"items\", false)\n\tphysicsService:CollisionGroupSetCollidable(\"passthrough\", \"npcs\", false)\n\tphysicsService:CollisionGroupSetCollidable(\"passthrough\", \"monsters\", false)\n\tphysicsService:CollisionGroupSetCollidable(\"passthrough\", \"monstersLocal\", false)\n\tphysicsService:CollisionGroupSetCollidable(\"passthrough\", \"characters\", false)\nend\n\nmain()\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/placeSetup.lua",
    "content": "local module = {}\n\n-- wait for the server to create placeFolders\nlocal placeFoldersFolder = workspace:WaitForChild(\"placeFolders\", 60)\nlocal lookupCache = {}\n\nlocal runService = game:GetService(\"RunService\")\n\n-- waits for placeFolder by name placeFolder to be created\nfunction module.awaitPlaceFolder(placeFolderName)\n\tif not lookupCache[placeFolderName] then\n\t\tlocal placeFolder = placeFoldersFolder:WaitForChild(placeFolderName)\n\t\tif placeFolder then\n\t\t\tlookupCache[placeFolderName] = placeFolder\n\t\tend\n\tend\n\n\treturn lookupCache[placeFolderName]\nend\n\n-- returns placeFolder placeFolderName and creates it if it does not exist\nfunction module.getPlaceFolder(placeFolderName, doNotCreate)\n\tif not lookupCache[placeFolderName] then\n\n\t\tif runService:IsClient() then\n\t\t\tmodule.awaitPlaceFolder(placeFolderName)\n\t\telse\n\t\t\tlocal placeFolder = placeFoldersFolder:FindFirstChild(placeFolderName)\n\t\t\tif not placeFolder and not doNotCreate then\n\t\t\t\tplaceFolder = Instance.new(\"Folder\", placeFoldersFolder)\n\t\t\t\tplaceFolder.Name = placeFolderName\n\t\t\tend\n\n\t\t\tlookupCache[placeFolderName] = placeFolder\n\t\tend\n\tend\n\n\treturn lookupCache[placeFolderName]\nend\n\n\nfunction module.getPlaceFoldersFolder()\n\treturn placeFoldersFolder\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/projectile.lua",
    "content": "local module \t= {}\nlocal projectileQueue = {}\n\nlocal projectileUpdateConnection\nlocal runService = game:GetService(\"RunService\")\n\nlocal player = game.Players.LocalPlayer\n\nlocal modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\n\nlocal configuration = modules.load(\"configuration\")\nlocal placeSetup = modules.load(\"placeSetup\")\nlocal detection = modules.load(\"detection\")\n\nlocal entitiesPlaceFolder \t\t\t\t= placeSetup.getPlaceFolder(\"entities\")\nlocal entityManifestCollectionFolder \t= placeSetup.getPlaceFolder(\"entityManifestCollection\")\n\nlocal LAST_UPDATE \t= tick()\n\n\n\nlocal GRAVITY \t\t= Vector3.new(0, -60, 0)\n\tmodule.GRAVITY = GRAVITY\n\nfunction module.calculateBeamProjectile(x0, v0, t1, gMulti)\n\tgMulti = gMulti or 1\n\n\t-- calculate the bezier points\n\tlocal c = 0.5*0.5*0.5;\n\tlocal p3 = 0.5*GRAVITY*gMulti*t1*t1 + v0*t1 + x0;\n\tlocal p2 = p3 - (GRAVITY*gMulti*t1*t1 + v0*t1)/3;\n\tlocal p1 = (c*GRAVITY*gMulti*t1*t1 + 0.5*v0*t1 + x0 - c*(x0+p3))/(3*c) - p2;\n\n\t-- the curve sizes\n\tlocal curve0 = (p1 - x0).magnitude;\n\tlocal curve1 = (p2 - p3).magnitude;\n\n\t-- build the world CFrames for the attachments\n\tlocal b = (x0 - p3).unit;\n\tlocal r1 = (p1 - x0).unit;\n\tlocal u1 = r1:Cross(b).unit;\n\tlocal r2 = (p2 - p3).unit;\n\tlocal u2 = r2:Cross(b).unit;\n\tb = u1:Cross(r1).unit;\n\n\tlocal cf1 = CFrame.new(\n\t\tx0.x, x0.y, x0.z,\n\t\tr1.x, u1.x, b.x,\n\t\tr1.y, u1.y, b.y,\n\t\tr1.z, u1.z, b.z\n\t)\n\n\tlocal cf2 = CFrame.new(\n\t\tp3.x, p3.y, p3.z,\n\t\tr2.x, u2.x, b.x,\n\t\tr2.y, u2.y, b.y,\n\t\tr2.z, u2.z, b.z\n\t)\n\n\treturn curve0, -curve1, cf1, cf2;\nend\n\nfunction module.showProjectilePath(x0, v0, t, gMulti)\n\tgMulti = gMulti or 1\n\n\tlocal attach0 = Instance.new(\"Attachment\", workspace.Terrain)\n\tlocal attach1 = Instance.new(\"Attachment\", workspace.Terrain)\n\n\tlocal beam \t\t\t= Instance.new(\"Beam\", workspace.Terrain)\n\tbeam.Attachment0 \t= attach0\n\tbeam.Attachment1 \t= attach1\n\n\tlocal curve0, curve1, cf1, cf2 = module.calculateBeamProjectile(x0, v0, t, gMulti)\n\n\tbeam.CurveSize0 = curve0\n\tbeam.CurveSize1 = curve1\n\tbeam.Segments \t= 50\n\n\t-- convert world space CFrames to be relative to the attachment parent\n\tattach0.CFrame = attach0.Parent.CFrame:inverse() * cf1\n\tattach1.CFrame = attach1.Parent.CFrame:inverse() * cf2\n\n\treturn beam, attach0, attach1\nend\n\nfunction module.updateProjectilePath(beam, attach0, attach1, x0, v0, t, gravMulti)\n\tgravMulti = gravMulti or 1\n\n\tlocal curve0, curve1, cf1, cf2 = module.calculateBeamProjectile(x0, v0, t, gravMulti)\n\n\tbeam.CurveSize0 = curve0\n\tbeam.CurveSize1 = curve1\n\tbeam.Segments \t= 50\n\n\t-- convert world space CFrames to be relative to the attachment parent\n\tattach0.CFrame = attach0.Parent.CFrame:inverse() * cf1\n\tattach1.CFrame = attach1.Parent.CFrame:inverse() * cf2\nend\n\nlocal function raycast(ray, ignoreList)\n\tlocal hitPart, hitPosition, hitNormal, hitMaterial = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)\n\n\twhile hitPart and not hitPart.CanCollide do\n\t\tignoreList[#ignoreList + 1] \t\t\t\t\t= hitPart\n\t\thitPart, hitPosition, hitNormal, hitMaterial \t= workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)\n\tend\n\n\treturn hitPart, hitPosition, hitNormal, hitMaterial\nend\n\n-- oh wow ;o\nmodule.raycastForProjectile = raycast\nmodule.raycast \t\t\t\t= raycast\n\nlocal function getManifests()\n\tlocal m = {}\n\tfor _, v in pairs(entityManifestCollectionFolder:GetChildren()) do\n\t\tif v:IsA(\"BasePart\") then\n\t\t\ttable.insert(m, v)\n\t\telseif v:IsA(\"Model\") and v.PrimaryPart then\n\t\t\ttable.insert(m, v.PrimaryPart)\n\t\tend\n\tend\n\n\treturn m\nend\n\nlocal function int__updateProjectiles(step)\n\tlocal currTime \t= LAST_UPDATE + step\n\tlocal targets \t= getManifests()\n\n\tfor i, projectileData in pairs(projectileQueue) do\n\t\tlocal t \t\t\t= currTime - projectileData.startTime\n\t\tlocal nextPosition \t= projectileData.origin + projectileData.velocity * t + 0.5 * (GRAVITY * projectileData.projectileGravityMultipler) * t * t\n\n\t\tlocal dir = nextPosition - projectileData.lastPosition\n\n\t\tlocal hitPart, hitPosition, hitNormal, hitMaterial = raycast(\n\t\t\tRay.new(\n\t\t\t\tprojectileData.lastPosition,\n\t\t\t\tdir + dir.unit * 0.15\n\t\t\t), projectileData.ignoreList or {entitiesPlaceFolder, placeSetup.getPlaceFolder(\"items\"), placeSetup.getPlaceFolder(\"foilage\"), game.Players.LocalPlayer.Character}\n\t\t)\n\n\t\tlocal alterationCF\n\t\tif projectileData.stepFunction then\n\t\t\talterationCF = projectileData.stepFunction(t)\n\t\tend\n\n\t\tif projectileData.trackerPart then\n\t\t\tif projectileData.pointToNextPosition then\n\t\t\t\tif alterationCF then\n\t\t\t\t\tprojectileData.trackerPart.CFrame = CFrame.new(hitPosition, hitPosition + dir) * alterationCF\n\t\t\t\telse\n\t\t\t\t\tprojectileData.trackerPart.CFrame = CFrame.new(hitPosition, hitPosition + dir)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tif alterationCF then\n\t\t\t\t\tprojectileData.trackerPart.CFrame = CFrame.new(hitPosition) * alterationCF\n\t\t\t\telse\n\t\t\t\t\tprojectileData.trackerPart.CFrame = CFrame.new(hitPosition)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif configuration.getConfigurationValue(\"doUseTrackerPartAsHitbox\", player) then\n\t\t\t\tif not hitPart then\n\t\t\t\t\tfor i, manifest in pairs(targets) do\n\t\t\t\t\t\tif not projectileData.reverseIgnoreList or not projectileData.reverseIgnoreList[manifest] then\n\t\t\t\t\t\t\tlocal adjustPos = detection.projection_Box(manifest.CFrame, manifest.Size, projectileData.trackerPart.CFrame.p)\n\n\t\t\t\t\t\t\tif detection.boxcast_singleTarget(projectileData.trackerPart.CFrame, projectileData.trackerPart.Size, adjustPos) then\n\t\t\t\t\t\t\t\thitPart = manifest\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif hitPart or t > (projectileData.projectileLifetime or 3) then\n\t\t\ttable.remove(projectileQueue, i)\n\n\t\t\tlocal clamp_t = math.clamp(t, 0, projectileData.projectileLifetime or 3)\n\n\t\t\tif t <= (projectileData.projectileLifetime or 3) and (not projectileData.collisionFunction or not projectileData.collisionFunction(hitPart, hitPosition, hitNormal, hitMaterial, clamp_t)) then\n\t\t\t\t-- keep it removed\n\t\t\telseif t <= (projectileData.projectileLifetime or 3) and projectileData.collisionFunction then\n\t\t\t\t-- returned true, so it means we should ignore the part we hit\n\t\t\t\tprojectileData.lastPosition = nextPosition\n\n\t\t\t\ttable.insert(projectileQueue, projectileData)\n\t\t\t\ttable.insert(projectileData.ignoreList, hitPart)\n\t\t\telse\n\t\t\t\tprojectileData.collisionFunction(hitPart, hitPosition, hitNormal, hitMaterial, clamp_t)\n\t\t\tend\n\t\telse\n\t\t\tprojectileData.lastPosition = nextPosition\n\t\tend\n\tend\n\n\tif #projectileQueue == 0 and projectileUpdateConnection then\n\t\tprojectileUpdateConnection:disconnect()\n\t\tprojectileUpdateConnection = nil\n\tend\n\n\tLAST_UPDATE = tick()\nend\n\nfunction module.createProjectile(origin, direction, speed, trackerPart, collisionFunction, stepFunction, ignoreList, pointToNextPosition, projectileGravityMultipler, projectileLifetime)\n\tlocal projectileData = {\n\t\torigin \t\t\t\t\t\t= origin;\n\t\tdirection \t\t\t\t\t= direction;\n\t\tspeed \t\t\t\t\t\t= speed;\n\t\tvelocity \t\t\t\t\t= direction * speed;\n\t\tcollisionFunction \t\t\t= collisionFunction;\n\t\ttrackerPart \t\t\t\t= trackerPart;\n\t\tstepFunction \t\t\t\t= stepFunction;\n\t\tlastPosition \t\t\t\t= origin;\n\t\tstartTime \t\t\t\t\t= tick();\n\t\tignoreList \t\t\t\t\t= ignoreList;\n\t\tpointToNextPosition \t\t= pointToNextPosition or false;\n\t\tprojectileGravityMultipler \t= projectileGravityMultipler or 1;\n\t\tprojectileLifetime \t\t\t= projectileLifetime or 3;\n\t}\n\n\tif projectileData.ignoreList then\n\t\tprojectileData.reverseIgnoreList = {}\n\n\t\tfor i,v in pairs(projectileData.ignoreList) do\n\t\t\tprojectileData.reverseIgnoreList[v] = true\n\t\tend\n\tend\n\n\ttable.insert(projectileQueue, projectileData)\n\n\tif not projectileUpdateConnection then\n\t\tLAST_UPDATE \t\t\t\t= tick()\n\t\tprojectileUpdateConnection \t= runService.Heartbeat:connect(int__updateProjectiles)\n\tend\nend\n\nfunction module.createProjectileByProjectileData(projectileData)\n\tassert(projectileData.origin, \"projectileData lacking origin\")\n\tassert(projectileData.origin, \"projectileData lacking direction\")\n\tassert(projectileData.origin, \"projectileData lacking speed\")\n\n\t-- start at origin\n\tprojectileData.lastPosition \t\t\t\t= projectileData.origin\n\tprojectileData.startTime \t\t\t\t\t= tick()\n\tprojectileData.ignoreList \t\t\t\t\t= projectileData.ignoreList;\n\tprojectileData.pointToNextPosition \t\t\t= projectileData.pointToNextPosition or false;\n\tprojectileData.projectileGravityMultipler \t= projectileData.projectileGravityMultipler or 1;\n\tprojectileData.projectileLifetime \t\t\t= projectileData.projectileLifetime or 3;\n\tprojectileData.collisionFunction \t\t\t= projectileData.collisionFunction or nil;\n\tprojectileData.trackerPart \t\t\t\t\t= projectileData.trackerPart or nil;\n\tprojectileData.stepFunction \t\t\t\t= projectileData.stepFunction or nil;\nend\n\nfunction module.makeIgnoreList(additions)\n\tlocal ignoreList = {\n\t\tplaceSetup.getPlaceFolder(\"entities\"),\n\t\tplaceSetup.getPlaceFolder(\"spawnRegionCollections\"),\n\t\tplaceSetup.getPlaceFolder(\"items\"),\n\t\tplaceSetup.getPlaceFolder(\"foilage\"),\n\t}\n\n\tfor _, addition in pairs(additions or {}) do\n\t\ttable.insert(ignoreList, addition)\n\tend\n\n\treturn ignoreList\nend\n\nlocal sqrt = math.sqrt\nfunction module.getUnitVelocityToImpact_predictive(projecileStartPosition, projectileSpeed, targetPosition, targetVelocity, projectileGravityMultipler, projectileLifetime)\n\tprojectileLifetime = projectileLifetime or 3\n\n\tprojectileGravityMultipler = projectileGravityMultipler or 1\n\n\tlocal p1x = projecileStartPosition.X\n\tlocal p1z = projecileStartPosition.Z\n\n\tlocal p2x = targetPosition.X\n\tlocal p2z = targetPosition.Z\n\n\tlocal v2x = targetVelocity.X\n\tlocal v2z = targetVelocity.Z\n\n\t-- 0 = at^2 + bt + c\n\tlocal a = (v2x * v2x + v2z * v2z - projectileSpeed * projectileSpeed)\n\tlocal b = (2*p2x*v2x - 2*p1x*v2x+ 2*p2z*v2z  - 2*p1z*v2z)\n\tlocal c = (p2x*p2x - 2*p2x*p1x + p1x*p1x + p2z*p2z - 2*p2z*p1z + p1z*p1z)\n\n\tif b * b - 4 * a * c < 0 then\n\t\t-- solution is a complex number, not real\n\t\treturn nil, nil\n\tend\n\n\tlocal t1 = (-b + math.sqrt(b * b - 4 * a * c)) / (2 * a)\n\tlocal t2 = (-b - math.sqrt(b * b - 4 * a * c)) / (2 * a)\n\n\tif t1 < 0 and t2 < 0 then\n\t\t-- no valid solution in the future, both solutions in the past\n\t\treturn nil, nil\n\tend\n\n\t-- use smallest (postitive) time value\n\tlocal t\n\tif t1 > 0 and t2 < 0 then\n\t\tt = t1\n\telseif t2 > 0 and t1 < 0 then\n\t\tt = t2\n\telseif t1 > 0 and t2 > 0 then\n\t\tt = t1 < t2 and t1 or t2\n\tend\n\n\tif t > projectileLifetime then\n\t\treturn nil, nil\n\tend\n\n\t-- find where we think the target will be after time `t`\n\tlocal adjusted_targetPosition = targetPosition + t * targetVelocity\n\n\tlocal unitDirection = (adjusted_targetPosition - projecileStartPosition - 0.5 * (module.GRAVITY * projectileGravityMultipler) * t^2) / (projectileSpeed * t)\n\n\treturn unitDirection, adjusted_targetPosition\nend\n\n\n-- added scalable predicivity to find a nice balance on a per mob basis\n--[[function module.getUnitVelocityToImpact_slightpredictive(projecileStartPosition, projectileSpeed, targetPosition, targetVelocity, projectileGravityMultipler, predictivityMultiplier)\n\n\tprojectileGravityMultipler = projectileGravityMultipler or 1\n\n\tlocal p1x = projecileStartPosition.X\n\tlocal p1z = projecileStartPosition.Z\n\n\tlocal p2x = targetPosition.X\n\tlocal p2z = targetPosition.Z\n\n\tlocal v2x = targetVelocity.X\n\tlocal v2z = targetVelocity.Z\n\n\t-- 0 = at^2 + bt + c\n\tlocal a = (v2x * v2x + v2z * v2z - projectileSpeed * projectileSpeed)\n\tlocal b = (2*p2x*v2x - 2*p1x*v2x+ 2*p2z*v2z  - 2*p1z*v2z)\n\tlocal c = (p2x*p2x - 2*p2x*p1x + p1x*p1x + p2z*p2z - 2*p2z*p1z + p1z*p1z)\n\n\tif b * b - 4 * a * c < 0 then\n\t\t-- solution is a complex number, not real\n\t\treturn nil, nil\n\tend\n\n\tlocal t1 = (-b + math.sqrt(b * b - 4 * a * c)) / (2 * a)\n\tlocal t2 = (-b - math.sqrt(b * b - 4 * a * c)) / (2 * a)\n\n\tif t1 < 0 and t2 < 0 then\n\t\t-- no valid solution in the future, both solutions in the past\n\t\treturn nil, nil\n\tend\n\n\t-- find the largest time value and use it (incase one was negative, and one isn't\n\tlocal t = t1 > t2 and t1 or t2\n\n\t-- find where we think the target will be after time `t`\n\tlocal adjusted_targetPosition = targetPosition + t * Vector3.new(targetVelocity.X * predictivityMultiplier, targetVelocity.Y * predictivityMultiplier, targetVelocity.Z * predictivityMultiplier)\n\n\tlocal unitDirection = (adjusted_targetPosition - projecileStartPosition - 0.5 * (module.GRAVITY * projectileGravityMultipler) * t^2) / (projectileSpeed * t)\n\n\treturn unitDirection, adjusted_targetPosition\nend]]--\n\n\nfunction module.getTargetPositionByAbilityExecutionData(abilityExecutionData)\n\treturn\n\t\tabilityExecutionData[\"target-casting-position\"]\n\t\tor abilityExecutionData[\"target-position\"]\n\t\tor abilityExecutionData[\"mouse-world-position\"]\nend\n\nfunction module.getUnitVelocityToImpact_predictiveByAbilityExecutionData(projecileStartPosition, projectileSpeed, abilityExecutionData, projectileGravityMultipler)\n\treturn module.getUnitVelocityToImpact_predictive(\n\t\tprojecileStartPosition,\n\t\tprojectileSpeed,\n\t\tmodule.getTargetPositionByAbilityExecutionData(abilityExecutionData),\n\t\t(abilityExecutionData[\"target-casting-position\"] and Vector3.new()) or abilityExecutionData[\"target-velocity\"] or Vector3.new(),\n\t\tprojectileGravityMultipler\n\t)\nend\n\nlocal clientCharacter\nlocal function onCharacterAdded(character)\n\tlocal IGNORE_LIST = {character; entitiesPlaceFolder}\nend\n\nlocal function main()\n\tif runService:IsClient() then\n\t\twhile not game.Players.LocalPlayer do wait() end\n\t\tplayer = game.Players.LocalPlayer\n\n\t\tif player.Character then\n\t\t\tonCharacterAdded(player.Character)\n\t\tend\n\n\t\tplayer.CharacterAdded:connect(onCharacterAdded)\n\tend\nend\n\nspawn(main)\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/projectile_predictive.lua",
    "content": "local module \t= {}\nlocal projectileQueue = {}\n\nlocal projectileUpdateConnection\nlocal runService = game:GetService(\"RunService\")\n\nlocal player = game.Players.LocalPlayer\n\nlocal modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\n\tlocal network \t\t= modules.load(\"network\")\n\tlocal placeSetup \t= modules.load(\"placeSetup\")\n\tlocal utilities\t\t= modules.load(\"utilities\")\n\nlocal entitiesPlaceFolder = placeSetup.getPlaceFolder(\"entities\")\n\nlocal myClientCharacterContainer\n\n-- how long each segment of 'raycasting' is, this number is needed\n-- to ensure raycast is approximate to the real function. smaller = better\nlocal segmentLength = 3\n\nlocal GRAVITY \t\t= Vector3.new(0, 0.5 * 60, 0)\nlocal LAST_UPDATE \t= tick()\nlocal IGNORE_LIST \t= {}\nlocal function raycastBetween_accomodate(startTime, origin, speed, velocity, i)\n\tlocal previous_i \t\t\t= i - 1 > 0 and i - 1 or 0\n\tlocal t \t\t\t\t\t= (LAST_UPDATE + segmentLength / speed * i) - startTime\n\tlocal t_compare \t\t\t= (LAST_UPDATE + segmentLength / speed * (previous_i)) - startTime\n\tlocal newPosition \t\t\t= origin + velocity * t - GRAVITY * t * t\n\tlocal newPosition_compare \t= origin + velocity * t_compare - GRAVITY * t_compare * t_compare\n\t\n\t--workspace.tracker.visualizer.CFrame = CFrame.new(newPosition)\n\t\n\treturn workspace:FindPartOnRayWithIgnoreList(\n\t\tRay.new(\n\t\t\tnewPosition_compare,\n\t\t\tnewPosition - newPosition_compare\n\t\t), IGNORE_LIST\n\t)\nend\n\nlocal function raycastDownIgnoreCancollideFalse(ray, ignoreList)\n\tlocal hitPart, hitPosition = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)\n\t\n\twhile hitPart and not hitPart.CanCollide do\n\t\tignoreList[#ignoreList + 1] = hitPart\n\t\thitPart, hitPosition \t\t= workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)\n\tend\n\t\n\treturn hitPart, hitPosition\nend\n\nlocal function int__updateProjectiles(step)\n\tfor i, projectileData in pairs(projectileQueue) do\n\t\tlocal distanceMoved = projectileData.velocity * step - GRAVITY * step * step\n\t\tif utilities.magnitude(distanceMoved) >= segmentLength then\n\n\t\t\tfor i = 1, math.floor(utilities.magnitude(distanceMoved) / segmentLength) do\n\t\t\t\tlocal hitPart, hitPosition = raycastBetween_accomodate(projectileData.startTime, projectileData.origin, projectileData.speed, projectileData.velocity, i)\n\t\t\t\t\n\t\t\t\tif projectileData.trackerPart then\n\t\t\t\t\tprojectileData.trackerPart.CFrame = CFrame.new(hitPosition)\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\tif hitPart then\n\t\t\t\t\tprojectileData.collisionFunction(hitPart)\n\t\t\t\t\tprojectileData.markForRemove = true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\t\n\t\tif not projectileData.markForRemove then\n\t\t\tlocal i = (utilities.magnitude(distanceMoved) / segmentLength) % 1\n\t\t\t\n\t\t\tif i > 0 then\n\t\t\t\tlocal hitPart, hitPosition = raycastBetween_accomodate(projectileData.startTime, projectileData.origin, projectileData.speed, projectileData.velocity, i)\n\t\t\t\t\n\t\t\t\tif projectileData.trackerPart then\n\t\t\t\t\tprojectileData.trackerPart.CFrame = CFrame.new(hitPosition)\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\tif hitPart then\n\t\t\t\t\tprojectileData.collisionFunction(hitPart)\n\t\t\t\t\tprojectileData.markForRemove = true\n\t\t\t\telseif tick() - projectileData.startTime > 3 then\n\t\t\t\t\tprojectileData.collisionFunction(nil)\n\t\t\t\t\tprojectileData.markForRemove = true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\t\n\t\tif projectileData.markForRemove then\n\t\t\ttable.remove(projectileQueue, i)\n\t\tend\n\tend\n\t\n\tif #projectileQueue == 0 then\n\n\t\tprojectileUpdateConnection:disconnect()\n\t\tprojectileUpdateConnection = nil\n\tend\n\t\n\tLAST_UPDATE = tick()\nend\n\nfunction module.createProjectile(origin, direction, speed, trackerPart, collisionFunction)\n\ttable.insert(projectileQueue, {\n\t\torigin \t\t\t\t= origin;\n\t\tdirection \t\t\t= direction;\n\t\tspeed \t\t\t\t= speed;\n\t\tvelocity \t\t\t= direction * speed;\n\t\tcollisionFunction \t= collisionFunction;\n\t\ttrackerPart \t\t= trackerPart;\n\t\tstartTime \t\t\t= tick();\n\t})\n\t\n\tif not projectileUpdateConnection then\n\t\tprojectileUpdateConnection = runService.Heartbeat:connect(int__updateProjectiles)\n\tend\nend\n\nlocal function onCharacterAdded(character)\n\tmyClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\t\n\tIGNORE_LIST = {character; myClientCharacterContainer; entitiesPlaceFolder}\nend\n\nlocal function main()\n\twhile not game.Players.LocalPlayer do wait() end\n\tplayer = game.Players.LocalPlayer\n\t\n\tif player.Character then\n\t\tonCharacterAdded(player.Character)\n\tend\n\t\n\tplayer.CharacterAdded:connect(onCharacterAdded)\nend\n\nspawn(main)\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/tableUtil.lua",
    "content": "-- Table Util\n-- Stephen Leitnick\n-- September 13, 2017\n\n--[[\n\n\tTableUtil.Copy(Table tbl)\n\tTableUtil.CopyShallow(Table tbl)\n\tTableUtil.Sync(Table tbl, Table templateTbl)\n\tTableUtil.Print(Table tbl, String label, Boolean deepPrint)\n\tTableUtil.FastRemove(Table tbl, Number index)\n\tTableUtil.FastRemoveFirstValue(Table tbl, Variant value)\n\tTableUtil.Map(Table tbl, Function callback)\n\tTableUtil.Filter(Table tbl, Function callback)\n\tTableUtil.Reduce(Table tbl, Function callback [, Number initialValue])\n\tTableUtil.Assign(Table target, ...Table sources)\n\tTableUtil.IndexOf(Table tbl, Variant item)\n\tTableUtil.Reverse(Table tbl)\n\tTableUtil.Shuffle(Table tbl)\n\tTableUtil.IsEmpty(Table tbl)\n\tTableUtil.EncodeJSON(Table tbl)\n\tTableUtil.DecodeJSON(String json)\n\n\tEXAMPLES:\n\n\t\tCopy:\n\n\t\t\tPerforms a deep copy of the given table. In other words,\n\t\t\tall nested tables will also get copied.\n\n\t\t\tlocal tbl = {\"a\", \"b\", \"c\"}\n\t\t\tlocal tblCopy = TableUtil.Copy(tbl)\n\n\n\t\tCopyShallow:\n\n\t\t\tPerforms a shallow copy of the given table. In other words,\n\t\t\tall nested tables will not be copied, but only moved by\n\t\t\treference. Thus, a nested table in both the original and\n\t\t\tthe copy will be the same.\n\n\t\t\tlocal tbl = {\"a\", \"b\", \"c\"}\n\t\t\tlocal tblCopy = TableUtil.CopyShallow(tbl)\n\n\n\t\tSync:\n\n\t\t\tSynchronizes a table to a template table. If the table does not have an\n\t\t\titem that exists within the template, it gets added. If the table has\n\t\t\tsomething that the template does not have, it gets removed.\n\n\t\t\tlocal tbl1 = {kills = 0; deaths = 0; points = 0}\n\t\t\tlocal tbl2 = {points = 0}\n\t\t\tTableUtil.Sync(tbl2, tbl1)  -- In words: \"Synchronize table2 to table1\"\n\t\t\tprint(tbl2.deaths)\n\n\n\t\tPrint:\n\n\t\t\tPrints out the table to the output in an easy-to-read format. Good for\n\t\t\tdebugging tables. If deep printing, avoid cyclical references.\n\n\t\t\tlocal tbl = {a = 32; b = 64; c = 128; d = {x = 0; y = 1; z = 2}}\n\t\t\tTableUtil.Print(tbl, \"My Table\", true)\n\n\n\t\tFastRemove:\n\n\t\t\tRemoves an item from an array at a given index. Only use this if you do\n\t\t\tNOT care about the order of your array. This works by simply popping the\n\t\t\tlast item in the array and overwriting the given index with the last\n\t\t\titem. This is O(1), compared to table.remove's O(n) speed.\n\n\t\t\tlocal tbl = {\"hello\", \"there\", \"this\", \"is\", \"a\", \"test\"}\n\t\t\tTableUtil.FastRemove(tbl, 2)   -- Remove \"there\" in the array\n\t\t\tprint(table.concat(tbl, \" \"))  -- > hello test is a\n\n\n\t\tFastRemoveFirstValue:\n\n\t\t\tCalls FastRemove on the first index that holds the given value.\n\n\t\t\tlocal tbl = {\"abc\", \"hello\", \"hi\", \"goodbye\", \"hello\", \"hey\"}\n\t\t\tlocal removed, atIndex = TableUtil.FastRemoveFirstValue(tbl, \"hello\")\n\t\t\tif (removed) then\n\t\t\t\tprint(\"Removed at index \" .. atIndex)\n\t\t\t\tprint(table.concat(tbl, \" \"))  -- > abc hi goodbye hello hey\n\t\t\telse\n\t\t\t\tprint(\"Did not find value\")\n\t\t\tend\n\n\n\t\tMap:\n\n\t\t\tThis allows you to construct a new table by calling the given function\n\t\t\ton each item in the table.\n\n\t\t\tlocal peopleData = {\n\t\t\t\t{firstName = \"Bob\"; lastName = \"Smith\"};\n\t\t\t\t{firstName = \"John\"; lastName = \"Doe\"};\n\t\t\t\t{firstName = \"Jane\"; lastName = \"Doe\"};\n\t\t\t}\n\n\t\t\tlocal people = TableUtil.Map(peopleData, function(item)\n\t\t\t\treturn {Name = item.firstName .. \" \" .. item.lastName}\n\t\t\tend)\n\n\t\t\t-- 'people' is now an array that looks like: { {Name = \"Bob Smith\"}; ... }\n\n\n\t\tFilter:\n\n\t\t\tThis allows you to create a table based on the given table and a filter\n\t\t\tfunction. If the function returns 'true', the item remains in the new\n\t\t\ttable; if the function returns 'false', the item is discluded from the\n\t\t\tnew table.\n\n\t\t\tlocal people = {\n\t\t\t\t{Name = \"Bob Smith\"; Age = 42};\n\t\t\t\t{Name = \"John Doe\"; Age = 34};\n\t\t\t\t{Name = \"Jane Doe\"; Age = 37};\n\t\t\t}\n\n\t\t\tlocal peopleUnderForty = TableUtil.Filter(people, function(item)\n\t\t\t\treturn item.Age < 40\n\t\t\tend)\n\n\n\t\tReduce:\n\n\t\t\tThis allows you to reduce an array to a single value. Useful for quickly\n\t\t\tsumming up an array.\n\n\t\t\tlocal tbl = {40, 32, 9, 5, 44}\n\t\t\tlocal tblSum = TableUtil.Reduce(tbl, function(accumulator, value)\n\t\t\t\treturn accumulator + value\n\t\t\tend)\n\t\t\tprint(tblSum)  -- > 130\n\n\n\t\tAssign:\n\n\t\t\tThis allows you to assign values from multiple tables into one. The\n\t\t\tAssign function is very similar to JavaScript's Object.Assign() and\n\t\t\tis useful for things such as composition-designed systems.\n\n\t\t\tlocal function Driver()\n\t\t\t\treturn {\n\t\t\t\t\tDrive = function(self) self.Speed = 10 end;\n\t\t\t\t}\n\t\t\tend\n\n\t\t\tlocal function Teleporter()\n\t\t\t\treturn {\n\t\t\t\t\tTeleport = function(self, pos) self.Position = pos end;\n\t\t\t\t}\n\t\t\tend\n\n\t\t\tlocal function CreateCar()\n\t\t\t\tlocal state = {\n\t\t\t\t\tSpeed = 0;\n\t\t\t\t\tPosition = Vector3.new();\n\t\t\t\t}\n\t\t\t\t-- Assign the Driver and Teleporter components to the car:\n\t\t\t\treturn TableUtil.Assign({}, Driver(), Teleporter())\n\t\t\tend\n\n\t\t\tlocal car = CreateCar()\n\t\t\tcar:Drive()\n\t\t\tcar:Teleport(Vector3.new(0, 10, 0))\n\n\n\t\tIndexOf:\n\n\t\t\tReturns the index of the given item in the table. If not found, this\n\t\t\twill return nil.\n\n\t\t\tThis is the same as table.find, which Roblox added after this method\n\t\t\twas written. To keep backwards compatibility, this method will continue\n\t\t\tto exist, but will point directly to table.find.\n\n\t\t\tlocal tbl = {\"Hello\", 32, true, \"abc\"}\n\t\t\tlocal abcIndex = TableUtil.IndexOf(tbl, \"abc\")     -- > 4\n\t\t\tlocal helloIndex = TableUtil.IndexOf(tbl, \"Hello\") -- > 1\n\t\t\tlocal numberIndex = TableUtil.IndexOf(tbl, 64)     -- > nil\n\n\n\t\tReverse:\n\n\t\t\tCreates a reversed version of the array. Note: This is a shallow\n\t\t\tcopy, so existing references will remain within the new table.\n\n\t\t\tlocal tbl = {2, 4, 6, 8}\n\t\t\tlocal rblReversed = TableUtil.Reverse(tbl)  -- > {8, 6, 4, 2}\n\n\n\t\tShuffle:\n\n\t\t\tShuffles (i.e. randomizes) an array. This uses the Fisher-Yates algorithm.\n\n\t\t\tlocal tbl = {1, 2, 3, 4, 5, 6, 7, 8, 9}\n\t\t\tTableUtil.Shuffle(tbl)\n\t\t\tprint(table.concat(tbl, \", \"))  -- e.g. > 3, 6, 9, 2, 8, 4, 1, 7, 5\n\n--]]\n\n\n\nlocal TableUtil = {}\n\nlocal http = game:GetService(\"HttpService\")\n\nlocal IndexOf = table.find\n\n\nlocal function CopyTable(t)\n\tassert(type(t) == \"table\", \"First argument must be a table\")\n\tlocal tCopy = table.create(#t)\n\tfor k,v in pairs(t) do\n\t\tif (type(v) == \"table\") then\n\t\t\ttCopy[k] = CopyTable(v)\n\t\telse\n\t\t\ttCopy[k] = v\n\t\tend\n\tend\n\treturn tCopy\nend\n\n\nlocal function CopyTableShallow(t)\n\tlocal tCopy = table.create(#t)\n\tfor k,v in pairs(t) do tCopy[k] = v end\n\treturn tCopy\nend\n\n\nlocal function Sync(tbl, templateTbl)\n\n\tassert(type(tbl) == \"table\", \"First argument must be a table\")\n\tassert(type(templateTbl) == \"table\", \"Second argument must be a table\")\n\n\t-- If 'tbl' has something 'templateTbl' doesn't, then remove it from 'tbl'\n\t-- If 'tbl' has something of a different type than 'templateTbl', copy from 'templateTbl'\n\t-- If 'templateTbl' has something 'tbl' doesn't, then add it to 'tbl'\n\tfor k,v in pairs(tbl) do\n\n\t\tlocal vTemplate = templateTbl[k]\n\n\t\t-- Remove keys not within template:\n\t\tif (vTemplate == nil) then\n\t\t\ttbl[k] = nil\n\n\t\t-- Synchronize data types:\n\t\telseif (type(v) ~= type(vTemplate)) then\n\t\t\tif (type(vTemplate) == \"table\") then\n\t\t\t\ttbl[k] = CopyTable(vTemplate)\n\t\t\telse\n\t\t\t\ttbl[k] = vTemplate\n\t\t\tend\n\n\t\t-- Synchronize sub-tables:\n\t\telseif (type(v) == \"table\") then\n\t\t\tSync(v, vTemplate)\n\t\tend\n\n\tend\n\n\t-- Add any missing keys:\n\tfor k,vTemplate in pairs(templateTbl) do\n\n\t\tlocal v = tbl[k]\n\n\t\tif (v == nil) then\n\t\t\tif (type(vTemplate) == \"table\") then\n\t\t\t\ttbl[k] = CopyTable(vTemplate)\n\t\t\telse\n\t\t\t\ttbl[k] = vTemplate\n\t\t\tend\n\t\tend\n\n\tend\n\nend\n\n\nlocal function FastRemove(t, i)\n\tlocal n = #t\n\tt[i] = t[n]\n\tt[n] = nil\nend\n\n\nlocal function Map(t, f)\n\tassert(type(t) == \"table\", \"First argument must be a table\")\n\tassert(type(f) == \"function\", \"Second argument must be an array\")\n\tlocal newT = table.create(#t)\n\tfor k,v in pairs(t) do\n\t\tnewT[k] = f(v, k, t)\n\tend\n\treturn newT\nend\n\n\nlocal function Filter(t, f)\n\tassert(type(t) == \"table\", \"First argument must be a table\")\n\tassert(type(f) == \"function\", \"Second argument must be an array\")\n\tlocal newT = table.create(#t)\n\tif (#t > 0) then\n\t\tlocal n = 0\n\t\tfor i = 1,#t do\n\t\t\tlocal v = t[i]\n\t\t\tif (f(v, i, t)) then\n\t\t\t\tn = (n + 1)\n\t\t\t\tnewT[n] = v\n\t\t\tend\n\t\tend\n\telse\n\t\tfor k,v in pairs(t) do\n\t\t\tif (f(v, k, t)) then\n\t\t\t\tnewT[k] = v\n\t\t\tend\n\t\tend\n\tend\n\treturn newT\nend\n\n\nlocal function Reduce(t, f, init)\n\tassert(type(t) == \"table\", \"First argument must be a table\")\n\tassert(type(f) == \"function\", \"Second argument must be an array\")\n\tassert(init == nil or type(init) == \"number\", \"Third argument must be a number or nil\")\n\tlocal result = (init or 0)\n\tfor k,v in pairs(t) do\n\t\tresult = f(result, v, k, t)\n\tend\n\treturn result\nend\n\n\n-- tableUtil.Assign(Table target, ...Table sources)\nlocal function Assign(target, ...)\n\tfor _,src in ipairs({...}) do\n\t\tfor k,v in pairs(src) do\n\t\t\ttarget[k] = v\n\t\tend\n\tend\n\treturn target\nend\n\n\nlocal function Print(tbl, label, deepPrint)\n\n\tassert(type(tbl) == \"table\", \"First argument must be a table\")\n\tassert(label == nil or type(label) == \"string\", \"Second argument must be a string or nil\")\n\n\tlabel = (label or \"TABLE\")\n\n\tlocal strTbl = {}\n\tlocal indent = \" - \"\n\n\t-- Insert(string, indentLevel)\n\tlocal function Insert(s, l)\n\t\tstrTbl[#strTbl + 1] = (indent:rep(l) .. s .. \"\\n\")\n\tend\n\n\tlocal function AlphaKeySort(a, b)\n\t\treturn (tostring(a.k) < tostring(b.k))\n\tend\n\n\tlocal function PrintTable(t, lvl, lbl)\n\t\tInsert(lbl .. \":\", lvl - 1)\n\t\tlocal nonTbls = {}\n\t\tlocal tbls = {}\n\t\tlocal keySpaces = 0\n\t\tfor k,v in pairs(t) do\n\t\t\tif (type(v) == \"table\") then\n\t\t\t\ttable.insert(tbls, {k = k, v = v})\n\t\t\telse\n\t\t\t\ttable.insert(nonTbls, {k = k, v = \"[\" .. typeof(v) .. \"] \" .. tostring(v)})\n\t\t\tend\n\t\t\tlocal spaces = #tostring(k) + 1\n\t\t\tif (spaces > keySpaces) then\n\t\t\t\tkeySpaces = spaces\n\t\t\tend\n\t\tend\n\t\ttable.sort(nonTbls, AlphaKeySort)\n\t\ttable.sort(tbls, AlphaKeySort)\n\t\tfor _,v in ipairs(nonTbls) do\n\t\t\tInsert(tostring(v.k) .. \":\" .. (\" \"):rep(keySpaces - #tostring(v.k)) .. v.v, lvl)\n\t\tend\n\t\tif (deepPrint) then\n\t\t\tfor _,v in ipairs(tbls) do\n\t\t\t\tPrintTable(v.v, lvl + 1, tostring(v.k) .. (\" \"):rep(keySpaces - #tostring(v.k)) .. \" [Table]\")\n\t\t\tend\n\t\telse\n\t\t\tfor _,v in ipairs(tbls) do\n\t\t\t\tInsert(tostring(v.k) .. \":\" .. (\" \"):rep(keySpaces - #tostring(v.k)) .. \"[Table]\", lvl)\n\t\t\tend\n\t\tend\n\tend\n\n\tPrintTable(tbl, 1, label)\n\n\tprint(table.concat(strTbl, \"\"))\n\nend\n\n\nlocal function Reverse(tbl)\n\tlocal n = #tbl\n\tlocal tblRev = table.create(n)\n\tfor i = 1,n do\n\t\ttblRev[i] = tbl[n - i + 1]\n\tend\n\treturn tblRev\nend\n\n\nlocal function Shuffle(tbl)\n\tassert(type(tbl) == \"table\", \"First argument must be a table\")\n\tlocal rng = Random.new()\n\tfor i = #tbl, 2, -1 do\n\t\tlocal j = rng:NextInteger(1, i)\n\t\ttbl[i], tbl[j] = tbl[j], tbl[i]\n\tend\nend\n\n\nlocal function IsEmpty(tbl)\n\treturn (next(tbl) == nil)\nend\n\n\nlocal function EncodeJSON(tbl)\n\treturn http:JSONEncode(tbl)\nend\n\n\nlocal function DecodeJSON(str)\n\treturn http:JSONDecode(str)\nend\n\n\nlocal function FastRemoveFirstValue(t, v)\n\tlocal index = IndexOf(t, v)\n\tif (index) then\n\t\tFastRemove(t, index)\n\t\treturn true, index\n\tend\n\treturn false, nil\nend\n\n\nTableUtil.Copy = CopyTable\nTableUtil.CopyShallow = CopyTableShallow\nTableUtil.Sync = Sync\nTableUtil.FastRemove = FastRemove\nTableUtil.FastRemoveFirstValue = FastRemoveFirstValue\nTableUtil.Print = Print\nTableUtil.Map = Map\nTableUtil.Filter = Filter\nTableUtil.Reduce = Reduce\nTableUtil.Assign = Assign\nTableUtil.IndexOf = IndexOf\nTableUtil.Reverse = Reverse\nTableUtil.Shuffle = Shuffle\nTableUtil.IsEmpty = IsEmpty\nTableUtil.EncodeJSON = EncodeJSON\nTableUtil.DecodeJSON = DecodeJSON\n\n\nreturn TableUtil\n"
  },
  {
    "path": "src/ReplicatedStorage/modules/tempData.lua",
    "content": "local module = {\n\tdata = {}\n}\n\nfunction module:set(player, key, val)\n\tif not self.data[player] then\n\t\tself.data[player] = {}\n\tend\n\tself.data[player][key] = val\nend\n\nfunction module:get(player, key)\n\tif self.data[player] then\n\t\treturn self.data[player][key]\n\tend\nend\n\nfunction module:delete(player, key)\n\tif not self.data[player] then return end\n\t\n\tself.data[player][key] = nil\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/modules/terrainUtil.lua",
    "content": "-- kyle emmerich\n\nlocal terrainUtil = {}\n\nlocal VOXEL_SIZE = Vector3.new(4, 4, 4)\nlocal TERRAIN = workspace.Terrain\n\nfunction terrainUtil.getTerrainAt(pos)\n    local cellPos = TERRAIN:WorldToCell(pos)\n    local regionCorner = TERRAIN:CellCornerToWorld(cellPos)\n    local region = Region3.new(regionCorner, regionCorner + VOXEL_SIZE)\n    local material, occupancy = TERRAIN:ReadVoxels(region, 4)\n    return material[1][1][1], occupancy[1][1][1]\nend\n\nlocal WATER = Enum.Material.Water\nlocal AIR = Enum.Material.Air\n\n\nfunction terrainUtil.isPointUnderwater(point)\n    local cellPos = TERRAIN:WorldToCell(point)\n    local regionCorner = TERRAIN:CellCornerToWorld(cellPos.X, cellPos.Y, cellPos.Z)\n    local region = Region3.new(regionCorner, regionCorner + Vector3.new(4, 8, 4))\n    local material, occupancy = TERRAIN:ReadVoxels(region, 4)\n    \n    local material0, occupancy0 = material[1][1][1], occupancy[1][1][1] -- cell containing point\n    local material1, occupancy1 = material[1][2][1], occupancy[1][2][1] -- cell above\n    \n    if material0 == WATER then\n        -- point is in water cell and cell above is not air => we're either completely in water or at the water-solid boundary\n        -- so we can safely assume underwater\n        if material1 ~= AIR then\n            -- Can overestimate water level to be at top of cell\n            return true, cellPos.Y * VOXEL_SIZE.Y, false\n        end\n    \n        -- cell above is air => have to estimate the plane based on occupancy\n        local waterHeightCell = occupancy0\n        -- cell clamping from mesher\n        local waterLevelCell = cellPos.Y + 0.5 + math.max(0, waterHeightCell - 0.5)\n        return point.Y / 4 < waterLevelCell, waterLevelCell * VOXEL_SIZE.Y, true\n    elseif material1 == WATER then\n        -- point is in solid cell and cell above is water => we're at the water-solid boundary\n        -- so we can safely assume underwater\n        if material0 ~= AIR then\n            -- Can overestimate water level to be at top of cell\n            -- Adding plus 2 because cell0 is already + 1 above posY\n            return true, (cellPos.Y + 1) * VOXEL_SIZE.Y, false\n        end\n    \n        -- point is in air => have to estimate the plane based on occupancy\n        local waterHeightCell = occupancy1\n        -- cell clamping from mesher\n        local waterLevelCell = (cellPos.Y + 1) - math.min(waterHeightCell, 0.5)\n        return point.Y / 4 > waterLevelCell, waterLevelCell * VOXEL_SIZE.Y, true\n    end\n    return false, 0, false\nend\n\n    \nreturn terrainUtil"
  },
  {
    "path": "src/ReplicatedStorage/modules/thread.lua",
    "content": "-- Thread\n-- Stephen Leitnick\n-- January 5, 2020\n\n--[[\n\n\tThread.SpawnNow(func, ...)\n\tThread.Spawn(func, ...)\n\tThread.Delay(waitTime, func, ...)\n\n\tSpawnNow(Function func, Arguments...)\n\n\t\t>\tUses a BindableEvent to spawn a new thread\n\t\t\timmediately. More performance-intensive than\n\t\t\tusing Thread.Spawn, but will guarantee a\n\t\t\tthread is started immediately.\n\n\t\t>\tUse this only if the thread must be executed\n\t\t\tright away, otherwise use Thread.Spawn for\n\t\t\tthe sake of performance.\n\n\tSpawn(Function func, Arguments...)\n\n\t\t>\tUses RunService's Heartbeat to spawn a new\n\t\t\tthread on the next heartbeat and then\n\t\t\tcall the given function.\n\n\t\t>\tBetter performance than Thread.SpawnNow, but\n\t\t\twill have a short delay of 1 frame before\n\t\t\tcalling the function.\n\n\tDelay(Number waitTime, Function func, Arguments...)\n\n\t\t>\tThe same as Thread.Spawn, but waits to call\n\t\t\tthe function until the in-game time as elapsed\n\t\t\tby 'waitTime' amount.\n\n\t\t>\tReturns the connection to the Heartbeat event,\n\t\t\tso the delay can be cancelled by disconnecting\n\t\t\tthe returned connection.\n\n\tDelayRepeat(Number intervalTime, Function func, Arguments...)\n\n\t\t>\tThe same as Thread.Delay, except it repeats\n\t\t\tindefinitely.\n\n\t\t>\tReturns the Heartbeat connection, thus the\n\t\t\trepeated delay can be stopped by disconnecting\n\t\t\tthe returned connection.\n\n\t\t>\tProperly bound to the time interval, thus will\n\t\t\tnot experience drift.\n\n\n\tExamples:\n\n\t\tThread.Spawn(function()\n\t\t\tprint(\"Hello from Spawn\")\n\t\tend)\n\n\t\tThread.Delay(1, function()\n\t\t\tprint(\"Hello from Delay\")\n\t\tend)\n\n\t\tThread.SpawnNow(function()\n\t\t\tprint(\"Hello from SpawnNow\")\n\t\tend)\n\n\t\tlocal delayConnection = Thread.Delay(5, function()\n\t\t\tprint(\"Hello?\")\n\t\tend)\n\t\tdelayConnection:Disconnect()\n\n\t\tlocal repeatConnection = Thread.DelayRepeat(1, function()\n\t\t\tprint(\"Hello again\", tick())\n\t\tend)\n\t\twait(5)\n\t\trepeatConnection:Disconnect()\n\n\n\tWhy:\n\n\t\tThe built-in 'spawn' and 'delay' functions have the\n\t\tpotential to be throttled unknowingly. This can cause\n\t\tall sorts of problems. Developers need to be certain\n\t\twhen their code is going to run. This small library\n\t\thelps give the same functionality as 'spawn' and 'delay'\n\t\tbut with the expected behavior.\n\n\tWhy not coroutines:\n\n\t\tCoroutines are powerful, but can be extremely difficult\n\t\tto debug due to the ways that coroutines obscure the\n\t\tstack trace.\n\n\tCredit:\n\n\t\tevaera & buildthomas: https://devforum.roblox.com/t/coroutines-v-s-spawn-which-one-should-i-use/368966\n\t\tQuenty: FastSpawn (AKA SpawnNow) method using BindableEvent\n\n--]]\n\n\n\nlocal Thread = {}\n\nlocal heartbeat = game:GetService(\"RunService\").Heartbeat\n\n\nfunction Thread.SpawnNow(func, ...)\n\t--[[\n\t\tThis method was originally written by Quenty and is slightly\n\t\tmodified for this module. The original source can be found in\n\t\tthe link below, as well as the MIT license:\n\t\t\thttps://github.com/Quenty/NevermoreEngine/blob/version2/Modules/Shared/Utility/fastSpawn.lua\n\t\t\thttps://github.com/Quenty/NevermoreEngine/blob/version2/LICENSE.md\n\t--]]\n\tlocal args = table.pack(...)\n\tlocal bindable = Instance.new(\"BindableEvent\")\n\tbindable.Event:Connect(function() func(table.unpack(args, 1, args.n)) end)\n\tbindable:Fire()\n\tbindable:Destroy()\nend\n\n\nfunction Thread.Spawn(func, ...)\n\tlocal args = table.pack(...)\n\tlocal hb\n\thb = heartbeat:Connect(function()\n\t\thb:Disconnect()\n\t\tfunc(table.unpack(args, 1, args.n))\n\tend)\nend\n\n\nfunction Thread.Delay(waitTime, func, ...)\n\tlocal args = table.pack(...)\n\tlocal executeTime = (tick() + waitTime)\n\tlocal hb\n\thb = heartbeat:Connect(function()\n\t\tif (tick() >= executeTime) then\n\t\t\thb:Disconnect()\n\t\t\tfunc(table.unpack(args, 1, args.n))\n\t\tend\n\tend)\n\treturn hb\nend\n\n\nfunction Thread.DelayRepeat(intervalTime, func, ...)\n\tlocal args = table.pack(...)\n\tlocal nextExecuteTime = (tick() + intervalTime)\n\tlocal hb\n\thb = heartbeat:Connect(function()\n\t\tif (tick() >= nextExecuteTime) then\n\t\t\tnextExecuteTime = (tick() + intervalTime)\n\t\t\tfunc(table.unpack(args, 1, args.n))\n\t\tend\n\tend)\n\treturn hb\nend\n\n\nreturn Thread\n"
  },
  {
    "path": "src/ReplicatedStorage/modules/tween.lua",
    "content": "return function (Object, Properties, Value, Time, Style, Direction)\n\tStyle = Style or Enum.EasingStyle.Quad\n\tDirection = Direction or Enum.EasingDirection.Out\n\t\n\tTime = Time or 0.5\n\n\tlocal propertyGoals = {}\n\t\n\tlocal Table = (type(Value) == \"table\" and true) or false\n\t\n\tfor i,Property in pairs(Properties) do\n\t\tpropertyGoals[Property] = Table and Value[i] or Value\n\tend\n\tlocal tweenInfo = TweenInfo.new(\n\t\tTime,\n\t\tStyle,\n\t\tDirection\n\t)\n\tlocal tween = game:GetService(\"TweenService\"):Create(Object,tweenInfo,propertyGoals)\n\ttween:Play()\n\t\n\treturn tween\nend\n\n"
  },
  {
    "path": "src/ReplicatedStorage/modules/utilities.lua",
    "content": "\nlocal module = {}\n\nlocal modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal sounds = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"sounds\"))\nlocal network = modules.load(\"network\")\nlocal configuration = modules.load(\"configuration\")\n\n-- x = x0 + v0 * t + 0.5 * g * t ^ 2\n-- todo\nfunction module.simulateProjectileMotion()\n\nend\n\nmodule.romanNumerals = {\"I\",\"II\",\"III\",\"IV\",\"V\",\"VI\",\"VII\",\"VIII\",\"IX\",\"X\",\"XI\",\"XII\",\"XIII\",\"XIV\",\"XV\",\"XVI\",\"XVII\",\"XVIII\",\"XIX\",\"XX\"}\n\nfunction module.copyTable(tableToCopy)\n\tlocal tableCopy = {}\n\tfor i, v in pairs(tableToCopy) do\n\t\tif type(v) == \"table\" then\n\t\t\ttableCopy[i] = module.copyTable(v)\n\t\telse\n\t\t\ttableCopy[i] = v\n\t\tend\n\tend\n\n\treturn tableCopy\nend\n\n\nlocal mapNameCache = {}\n\nfunction module.getPlaceName(destination)\n\tif mapNameCache[tostring(destination)] then\n\t\treturn mapNameCache[tostring(destination)]\n\telse\n\t\tlocal placeInfo = game.MarketplaceService:GetProductInfo(destination,Enum.InfoType.Asset)\n\t\tlocal placeName = placeInfo.Name\n\t\tif placeName then\n\t\t\tplaceName = string.gsub(placeName, \" %(Demo%)\", \"\")\n\t\t\tmapNameCache[tostring(destination)] = placeName\n\t\t\treturn placeName\n\t\tend\n\tend\n\treturn \"???\"\nend\n\nlocal function addComas(str)\n\treturn #str % 3 == 0 and str:reverse():gsub(\"(%d%d%d)\", \"%1,\"):reverse():sub(2) or str:reverse():gsub(\"(%d%d%d)\", \"%1,\"):reverse()\nend\n\nfunction module.formatNumber(number)\n\tnumber = math.floor(number)\n\treturn addComas(tostring(number))\nend\n\n\nfunction module.isEntityManifestValid(entityManifest, deadIncluded)\n\treturn\n\t\t\tentityManifest:FindFirstChild(\"entityType\")\n\t\tand entityManifest:FindFirstChild(\"entityId\")\n\t\tand entityManifest:FindFirstChild(\"state\") and (deadIncluded or entityManifest.state.Value ~= \"dead\")\n\t\tand entityManifest:FindFirstChild(\"health\")\n\t\tand entityManifest:FindFirstChild(\"maxHealth\")\nend\n\nfunction module.getEntities(_entityType, deadIncluded)\n\tlocal entities = {}\n\n\tfor i, entityObject in pairs(workspace.placeFolders.entityManifestCollection:GetChildren()) do\n\t\tlocal entityManifest do\n\t\t\tif entityObject:IsA(\"Model\") and entityObject.PrimaryPart then\n\t\t\t\tentityManifest = entityObject.PrimaryPart\n\t\t\telseif entityObject:IsA(\"BasePart\") then\n\t\t\t\tentityManifest = entityObject\n\t\t\tend\n\t\tend\n\n\t\tif entityManifest then\n\t\t\tif module.isEntityManifestValid(entityManifest, deadIncluded) then\n\t\t\t\tif not _entityType or entityManifest.entityType.Value == _entityType then\n\t\t\t\t\ttable.insert(entities, entityManifest)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\treturn entities\nend\n\nfunction module.timeToString(seconds)\n\tlocal days = math.floor(seconds / 86400)\n\tseconds = seconds - days * 86400\n\tlocal hours = math.floor(seconds / 3600)\n\tseconds = seconds - hours * 3600\n\tlocal minutes = math.floor(seconds / 60)\n\tseconds = seconds - minutes * 60\n\n\tlocal timestring = \"\"\n\tif days > 0 then\n\t\ttimestring = timestring .. days .. \"d \"\n\tend\n\tif hours > 0 then\n\t\ttimestring = timestring .. hours .. \"h \"\n\tend\n\tif minutes > 0 then\n\t\ttimestring = timestring .. minutes .. \"m \"\n\tend\n\tif seconds > 0 then\n\t\ttimestring = timestring .. seconds .. \"s\"\n\tend\n\treturn timestring\nend\n\n-- LOCATION: Part, Vector3 or CFrame\n-- location and duration are optional\n\nfunction module.playSound(soundName, location, duration, additionalInfo)\n\t-- soundName can be an instance (deprecated)\n\n\tlocal sound\n\tif typeof(soundName) == \"string\" then\n\t\tlocal soundData = sounds[soundName]\n\t\tassert(soundData, \"Sound \" .. soundName .. \" missing from assets.\")\n\t\tsound = Instance.new(\"Sound\")\n\t\tfor property, value in pairs(soundData) do\n\t\t\tsound[property] = value\n\t\tend\n\telse\n\t\tsound = soundName\n\tend\n\n\tif sound then\n\t\tif location then\n\t\t\tif typeof(location) == \"Instance\" and location:IsA(\"BasePart\") then\n\t\t\t\t-- existing part\n\n\t\t\t\tsound.Volume = additionalInfo and additionalInfo.volume or sound.Volume\n\t\t\t\tsound.EmitterSize = additionalInfo and additionalInfo.emitterSize or sound.EmitterSize\n\t\t\t\tsound.MaxDistance = additionalInfo and additionalInfo.maxDistance or sound.MaxDistance\n\n\t\t\t\tsound.Parent = location\n\t\t\t\tsound:Play()\n\n\t\t\t\tif not sound.Looped then\n\t\t\t\t\tgame.Debris:AddItem(sound, duration or sound.TimeLength + 1)\n\t\t\t\telseif duration then\n\t\t\t\t\tgame.Debris:AddItem(sound, duration)\n\t\t\t\tend\n\n\t\t\t\treturn sound\n\t\t\telse\n\t\t\t\t-- create a new part\n\t\t\t\tlocal targetCF\n\t\t\t\tif typeof(location) == \"CFrame\" then\n\t\t\t\t\ttargetCF = location\n\t\t\t\telseif typeof(location) == \"Vector3\" then\n\t\t\t\t\ttargetCF = CFrame.new(location)\n\t\t\t\tend\n\t\t\t\tif targetCF then\n\t\t\t\t\tlocal soundPart = Instance.new(\"Part\")\n\t\t\t\t\tsoundPart.Name = \"soundPart\"\n\t\t\t\t\tsoundPart.Size = Vector3.new(1,1,1)\n\t\t\t\t\tsoundPart.Anchored = true\n\t\t\t\t\tsoundPart.CanCollide = false\n\t\t\t\t\tsoundPart.Transparency = 1\n\t\t\t\t\tsoundPart.CFrame = targetCF\n\t\t\t\t\tsoundPart.Parent = workspace.CurrentCamera\n\n\t\t\t\t\tsound = sound:Clone()\n\n\t\t\t\t\tsound.Volume = additionalInfo and additionalInfo.volume or sound.Volume\n\t\t\t\t\tsound.EmitterSize = additionalInfo and additionalInfo.emitterSize or sound.EmitterSize\n\t\t\t\t\tsound.MaxDistance = additionalInfo and additionalInfo.maxDistance or sound.MaxDistance\n\t\t\t\t\tsound.Parent = soundPart\n\n\t\t\t\t\tsound:Play()\n\t\t\t\t\tif not sound.Looped then\n\t\t\t\t\t\tgame.Debris:AddItem(soundPart, duration or sound.TimeLength + 1)\n\t\t\t\t\telseif duration then\n\t\t\t\t\t\tgame.Debris:AddItem(soundPart, duration)\n\t\t\t\t\tend\n\t\t\t\t\treturn soundPart\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\t-- no location provided, just play the root sound\n\t\t\tif sound:IsA(\"Sound\") and sound.Looped then\n\t\t\t\tsound:Play()\n\t\t\telse\n\t\t\t\tsound = sound:Clone()\n\t\t\t\tsound.Parent = workspace.CurrentCamera\n\t\t\t\tsound:Play()\n\t\t\t\tgame.Debris:AddItem(sound, 1 + sound.TimeLength)\n\t\t\tend\n\n\t\t\treturn sound\n\t\tend\n\tend\nend\n\n\n-- welds two parts together\nfunction module.weld(part0, part1)\n\tlocal motor6d \t= Instance.new(\"Motor6D\")\n\tmotor6d.Part0 \t= part0\n\tmotor6d.Part1\t= part1\n\tmotor6d.C0    \t= CFrame.new()\n\tmotor6d.C1 \t\t= part1.CFrame:toObjectSpace(part0.CFrame)\n\tmotor6d.Name \t= part1.Name\n\tmotor6d.Parent\t= part0\nend\n\nlocal httpService = game:GetService(\"HttpService\")\n\nfunction module.safeJSONEncode(t)\n\tlocal t2 = module.copyTable(t)\n\tfor i, v in pairs(t2) do\n\t\tif typeof(v) == \"Vector3\" then\n\t\t\tt2[i] = {x = v.X; y = v.Y; z = v.Z}\n\t\telseif typeof(v) == \"Vector2\" then\n\t\t\tt2[i] = {x = v.X; y = v.Y}\n\t\tend\n\tend\n\n\treturn pcall(function() return httpService:JSONEncode(t2) end)\nend\n\nfunction module.safeJSONDecode(t)\n\tlocal success, response = pcall(function() return httpService:JSONDecode(t) end)\n\n\tif success then\n\t\tlocal t2 = module.copyTable(response)\n\t\tfor i, v in pairs(t2) do\n\t\t\tif typeof(v) == \"table\" and v.x and v.y and v.z then\n\t\t\t\tt2[i] = Vector3.new(v.x, v.y, v.z)\n\t\t\telseif typeof(v) == \"table\" and v.x and v.y and not v.z then\n\t\t\t\tt2[i] = Vector2.new(v.x, v.y)\n\t\t\tend\n\t\tend\n\n\t\treturn true, t2\n\tend\n\n\treturn false, nil\nend\n\n-- i put this here just so the client and the server can access the same function\nlocal itemLookup\nfunction module.playerCanPickUpItem(player, item, isPet)\n\tif not itemLookup then\n\t\titemLookup = require(game.ReplicatedStorage.itemData)\n\tend\n\n\tif item:FindFirstChild(\"pickupBlacklist\") and item.pickupBlacklist:FindFirstChild(tostring(player.userId)) then\n\t\treturn false\n\tend\n\n\tif isPet and item:FindFirstChild(\"petsIgnore\") then\n\t\treturn false\n\tend\n\n\tif item:FindFirstChild(\"created\") then\n\t\tlocal timeSinceCreated = os.time() - item.created.Value\n\n\t\tif timeSinceCreated >= configuration.getConfigurationValue(\"timeForAnyonePickupItem\") then\n\t\t\treturn true\n\t\telseif itemLookup[item.Name] then\n\t\t\tif itemLookup[item.Name].everyoneAvailabilityTime and timeSinceCreated >= itemLookup[item.Name].everyoneAvailabilityTime then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\n\n\tif item:FindFirstChild(\"owners\") then\n\t\tfor _, owner in pairs(item.owners:GetChildren()) do\n\t\t\tif owner.Value == player or tonumber(owner.Name) == player.userId then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\n\t\treturn false\n\tend\n\n\treturn true\nend\n\nfunction module.magnitude(vec)\n\tlocal x = vec.x;\n\tlocal y = vec.y;\n\tlocal z = vec.z;\n\n\tlocal m = (x*x + y*y + z*z)\n\n\treturn m ^ .5;\nend\n\nfunction module.wipeReferences(tableToLookInto)\n\tfor i, v in pairs(tableToLookInto) do\n\t\tif typeof(v) == \"Instance\" then\n\t\t\ttableToLookInto[i] = nil\n\t\telseif type(v) == \"table\" then\n\t\t\tmodule.wipeReferences(v)\n\t\tend\n\n\t\t-- do this after iterating through v to make sure we get deepest references\n\t\tif typeof(i) == \"Instance\" then\n\t\t\ttableToLookInto[i] = nil\n\t\tend\n\tend\nend\n\nfunction module.isSafeToProcess(part, doWaitForChild, ...)\n\tfor _, v in pairs({...}) do\n\t\tif doWaitForChild then\n\t\t\tpart:WaitForChild(v)\n\t\telse\n\t\t\tif not part:FindFirstChild(v) then\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\tend\n\n\treturn true\nend\n\nfunction module.isInTable(t, valueToLookFor)\n\tfor _, v in pairs(t) do\n\t\tif v == valueToLookFor then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal itemWeightSelectionGenerator = Random.new()\nfunction module.selectFromWeightTable(weightTable)\n\tlocal sum = 0\n\tfor _, item in pairs(weightTable) do\n\t\tsum = sum + item.selectionWeight\n\tend\n\n\twhile true do\n\t\tlocal rIndex \t= itemWeightSelectionGenerator:NextInteger(1, #weightTable)\n\t\tlocal item \t\t= weightTable[rIndex]\n\n\t\tif item then\n\t\t\tif sum - item.selectionWeight > 0 then\n\t\t\t\tsum = sum - item.selectionWeight\n\t\t\telse\n\t\t\t\treturn item, rIndex\n\t\t\tend\n\t\telse\n\n\t\t\treturn weightTable[1] or {}, weightTable[1] and 1 or nil\n\t\tend\n\tend\nend\n\nlocal function findObjectHelper(model, objectName, className, listOfFoundObjects)\n\tif not model then return end\n\tlocal findStart, findEnd = string.find(model.Name, objectName)\n\tif findStart == 1 and findEnd == #(model.Name) then  -- must match entire name\n\t\tif not className or model.className == className or (pcall(model.IsA, model, className) and model:IsA(className)) then\n\t\t\ttable.insert(listOfFoundObjects, model)\n\t\tend\n\tend\n\tif pcall(model.GetChildren, model) then\n\t\tlocal modelChildren = model:GetChildren()\n\t\tfor i = 1, #modelChildren do\n\t\t\tfindObjectHelper(modelChildren[i], objectName, className, listOfFoundObjects)\n\t\tend\n\tend\nend\n\nlocal function resizeModelInternal(model, resizeFactor)\n\tlocal modelCFrame = model:GetModelCFrame()\n\tlocal modelSize = model:GetModelSize()\n\tlocal baseParts = {}\n\tlocal basePartCFrames = {}\n\tlocal joints = {}\n\tlocal jointParents = {}\n\tlocal meshes = {}\n\n\tfindObjectHelper(model, \".*\", \"BasePart\", baseParts)\n\tfindObjectHelper(model, \".*\", \"JointInstance\", joints)\n\n\t-- meshes don't inherit from anything accessible?\n\tfindObjectHelper(model, \".*\", \"FileMesh\", meshes)                    -- base class for SpecialMesh and FileMesh\n\tfindObjectHelper(model, \".*\", \"CylinderMesh\", meshes)\n\tfindObjectHelper(model, \".*\", \"BlockMesh\", meshes)\n\n\t-- store the CFrames, so our other changes don't rearrange stuff\n\tfor _, basePart in pairs(baseParts) do\n\t\tbasePartCFrames[basePart] = basePart.CFrame\n\tend\n\n\t-- scale joints\n\tfor _, joint in pairs(joints) do\n\t\tjoint.C0 = joint.C0 + (joint.C0.p) * (resizeFactor - 1)\n\t\tjoint.C1 = joint.C1 + (joint.C1.p) * (resizeFactor - 1)\n\t\tjointParents[joint] = joint.Parent\n\tend\n\n\t-- scale parts and reposition them within the model\n\tfor _, basePart in pairs(baseParts) do\n\t\t-- if pcall(function() basePart.FormFactor = \"Custom\" end) then basePart.FormFactor = \"Custom\" end\n\t\tbasePart.Size = basePart.Size * resizeFactor\n\t\tlocal oldCFrame = basePartCFrames[basePart]\n\t\tlocal oldPositionInModel = modelCFrame:pointToObjectSpace(oldCFrame.p)\n\t\tlocal distanceFromCorner = oldPositionInModel + modelSize/2\n\t\tdistanceFromCorner = distanceFromCorner * resizeFactor\n\n\t\tlocal newPositionInSpace = modelCFrame:pointToWorldSpace(distanceFromCorner - modelSize/2)\n\t\tbasePart.CFrame = oldCFrame - oldCFrame.p + newPositionInSpace\n\tend\n\n\t-- scale meshes\n\tfor _,mesh in pairs(meshes) do\n--\t\tmesh.Scale = mesh.Scale * resizeFactor\n\tend\n\n\t-- pop the joints back, because they prolly got borked\n\tfor _, joint in pairs(joints) do\n\t\tjoint.Parent = jointParents[joint]\n\tend\n\n\treturn model\nend\n\nfunction module.getEntityGUIDByEntityManifest(entityManifest)\n\tlocal player\n\tif entityManifest.Parent and entityManifest.Parent:IsA(\"Model\") then\n\t\tplayer = game.Players:GetPlayerFromCharacter(entityManifest.Parent)\n\tend\n\t-- this is important because 1 player will always have the same GUID, but the character\n\t-- might change!\n\tif player then\n\t\treturn player.entityGUID.Value\n\telseif entityManifest:FindFirstChild(\"entityGUID\") then\n\t\treturn entityManifest.entityGUID.Value\n\tend\n\n\treturn nil\nend\n\n-- returns\n-- * BasePart entityManifest\n-- * Boolean isEntityInWorld [even if first argument is nil, treat it like it isn't... ie if player is respawning]\nfunction module.getEntityManifestByEntityGUID(entityGUID)\n\tlocal entityManifests = module.getEntities(nil, true)\n\n\tfor i, entityManifest in pairs(entityManifests) do\n\t\tlocal _entityGUID = module.getEntityGUIDByEntityManifest(entityManifest)\n\t\tif _entityGUID == entityGUID then\n\t\t\treturn entityManifest, true\n\t\tend\n\tend\n\n\tfor i, player in pairs(game.Players:GetPlayers()) do\n\t\tif player:FindFirstChild(\"entityGUID\") and player.entityGUID.Value == entityGUID then\n\t\t\treturn nil, true\n\t\tend\n\tend\n\n\treturn nil, false\nend\n\nfunction module.connectEventHelper(event, func)\n\tlocal connection\n\tconnection = event:connect(function(...)\n\t\tlocal doDisconnect = func(...)\n\n\t\tif doDisconnect then\n\t\t\tconnection:disconnect()\n\t\tend\n\tend)\n\n\treturn connection\nend\n\nmodule.placeIdMapping = {\n\t[\"2376885433\"] = 2015602902;\n\t[\"2064647391\"] = 2015602902;--4041449372;\n\t[\"2035250551\"] = 4041616995;\n\t[\"2060360203\"] = 4041642879;\n\t[\"2060556572\"] = 4784798551;\n\t[\"2093766642\"] = 4784800626;\n\t[\"2471035818\"] = 4042327457;\n\t[\"2546689567\"] = 4042356215;\n\t[\"3852057184\"] = 4041618739;\n\t[\"2376890690\"] = 4042533453;\n\t[\"2470481225\"] = 4042553675;\n\t[\"2260598172\"] = 4042431927;\n\t[\"3112029149\"] = 4042493740;\n\t[\"3460262616\"] = 4042399045;\n\t[\"2496503573\"] = 4042381342;\n\t[\"2119298605\"] = 4042577479;\n\t[\"2878620739\"] = 4042595899;\n\t[\"2677014001\"] = 4786263828;\n\n\t[\"3232913902\"] = 4787417227;\n\t[\"2544075708\"] = 4787415375;\n}\n\nfunction module.placeIdForGame(placeId)\n\n\tif game.GameId == 712031239 then\n\t\treturn module.placeIdMapping[tostring(placeId)] or placeId\n\telse\n\t\treturn placeId\n\tend\nend\n\nfunction module.originPlaceId(placeId)\n\tif game.GameId == 712031239 then\n\t\tfor originPlaceIdString, mappedId in pairs(module.placeIdMapping) do\n\t\t\tif mappedId == placeId then\n\t\t\t\treturn tonumber(originPlaceIdString)\n\t\t\tend\n\t\tend\n\tend\n\treturn placeId\nend\n\nmodule.scale = resizeModelInternal\n\nfunction module.doesPlayerHaveEquipmentPerk(player, perkName)\n\tlocal char = player.Character\n\tif not char then return false end\n\tlocal entityManifest = char.PrimaryPart\n\tif not entityManifest then return false end\n\tlocal renderCharacterContainer = network:invoke(\"getRenderCharacterContainerByEntityManifest\", entityManifest)\n\tif not renderCharacterContainer then return false end\n\tlocal entityRender = renderCharacterContainer:FindFirstChild(\"entity\")\n\tif not entityRender then return false end\n\tlocal equipment = network:invoke(\"getCurrentlyEquippedForRenderCharacter\", entityRender)\n\tif not equipment then return false end\n\n\tfor position, info in pairs(equipment) do\n\t\tif info.baseData.perks then\n\t\t\tfor perk, active in pairs(info.baseData.perks) do\n\t\t\t\tif perk == perkName then\n\t\t\t\t\treturn active\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nfunction module.calculateNumArrowsFromDex(dex)\n\tlocal numArrows = 1\n\tlocal dexAccumulator = 0\n\tlocal currentDexStep = 20\n\n\trepeat\n\t\tdexAccumulator = dexAccumulator + currentDexStep\n\t\tcurrentDexStep = currentDexStep + 10\n\t\tif dexAccumulator <= dex then\n\t\t\tnumArrows = numArrows + 1\n\t\tend\n\tuntil dexAccumulator >= dex\n\n\treturn numArrows\nend\n\nfunction module.calculatePierceFromStr(str)\n\tlocal pierce = 0\n\tlocal accumulator = 0\n\tlocal currentStep = 20\n\tlocal stepIncrease = 10\n\n\trepeat\n\t\taccumulator = accumulator + currentStep\n\t\tcurrentStep = currentStep + stepIncrease\n\t\tif accumulator <= str then\n\t\t\tpierce = pierce + 1\n\t\tend\n\tuntil accumulator >= str\n\n\treturn pierce\nend\n\nfunction module.doesEntityHaveStatusEffect(entity, statusEffectName)\n\tlocal statusEffectsV2 = entity:FindFirstChild(\"statusEffectsV2\")\n\tif not statusEffectsV2 then return false end\n\n\tlocal success, statusEffects = module.safeJSONDecode(statusEffectsV2.Value)\n\tif not success then return false end\n\n\tfor _, statusEffect in pairs(statusEffects) do\n\t\tif statusEffect.statusEffectType == statusEffectName then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nfunction module.healEntity(sourceEntity, targetEntity, amount)\n\tlocal health = targetEntity:FindFirstChild(\"health\")\n\tif not health then return end\n\tlocal maxHealth = targetEntity:FindFirstChild(\"maxHealth\")\n\tif not maxHealth then return end\n\n\tlocal healthBefore = health.Value\n\thealth.Value = math.min(health.Value + amount, maxHealth.Value)\n\tlocal trueHealing = health.Value - healthBefore\n\n\tif trueHealing > 1 then\n\t\tnetwork:fireAllClients(\"signal_damage\", targetEntity, {\n\t\t\tdamage = -trueHealing,\n\t\t})\n\tend\nend\n\nlocal function rankCheck(player)\n\twait(0.1)\n\n\tlocal playerRank = 0 do\n\t\tlocal success, returnValue = pcall(function()\n\t\t\treturn player:GetRankInGroup(4238824)\n\t\tend)\n\n\t\tif success and returnValue > playerRank then\n\t\t\tplayerRank = returnValue\n\t\tend\n\tend\n\n\tif game:GetService(\"RunService\"):IsStudio() or player.Name == \"berezaa\" or player.Name == \"Polymorphic\" or player.Name == \"sk3let0n\" or playerRank >= 250 then\n\t\tlocal devTag \t= Instance.new(\"BoolValue\")\n\t\tdevTag.Name \t= \"developer\"\n\t\tdevTag.Parent \t= player\n\tend\n\n\tif playerRank > 1 then\n\t\tlocal devTag \t= Instance.new(\"BoolValue\")\n\t\tdevTag.Name \t= \"QA\"\n\t\tdevTag.Parent \t= player\n--\telseif game.PlaceId == 2061558182 and not (runService:IsRunMode() or runService:IsStudio()) then\n--\t\tplayer:Kick(\"Not allowed to be here.\")\n\tend\nend\n\ngame.Players.PlayerAdded:connect(rankCheck)\nfor _, player in pairs(game.Players:GetPlayers()) do\n\trankCheck(player)\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/monsterLookup/Baby Shroom.lua",
    "content": "local monsterData = {\n\tlevel = 1;\n\t\n\t--> combat stats <--\n\tattackRange = 2.5;\n\tbaseSpeed = 12;\n\tattackSpeed = 4;\n\tmaxHealth = 6;\n\tdamage = 2;\n\tdefense = 0;\n\t\n\t--> death reward stuff <--\n\tEXP = 3;\n\tgold = 5;\n\n\t--> monster damage <--\n\tdamageHitboxCollection = {\n\t\t{partName = \"Cap\"; castType = \"box\"; originOffset = CFrame.new(0, 0.5, 0)};\n\t};\n\t\n\t--> loot drop stuff <--\n\tlootDrops \t= {\n\t\t{id = 1; spawnChance = 3/4};\n\t\t{itemName = \"mushroom spore\"; spawnChance = 1};\n\t\t{itemName = \"mushroom spore\"; spawnChance = 1/7};\n\t\t{itemName = \"health potion\"; spawnChance = 1/12};\n\t\t{itemName = \"wooden club\"; spawnChance = 1/40};\n\t};\n\t\n\t--> ease of access <--\n\tmodule \t= script;\n\tid \t\t= \"monster\";\n}\n\n\nreturn monsterData"
  },
  {
    "path": "src/ReplicatedStorage/monsterLookup/Chicken.lua",
    "content": "return {\n\t--> combat stats <--\n\tattackRange = 2;\n\tbaseSpeed \t= 6;\n\tbaseHealth \t= 10;\n\n--\thealthMulti = 10000;\n\n\tdoNotLure = true;\n\n\tattackSpeed = 2;\n\tbaseDamage \t= 10;\n\t\n\t--> death reward stuff <--\n\tbaseEXP \t= 10;\n\tlevel \t\t= 2;\n\tbaseMoney \t= 10;\n\n\t--> monster damage <--\n\tdamageHitboxCollection = {};\n\t\n\t--> loot drop stuff <--\n\tlootDrops \t= {\n\t\t{id = 270; spawnChance = 0.5};\n\t\t{id = 271; spawnChance = 1};\n\t\t{id = 277; spawnChance = 0.7};\n\t};\n\t\n\t--> ease of access <--\n\tmodule \t= script;\n\tid \t\t= \"chicken\";\n}"
  },
  {
    "path": "src/ReplicatedStorage/monsterLookup/Dummy.lua",
    "content": "local monsterData = {\n\t--> combat stats <--\n\tattackRange = 0;\n\tbaseSpeed \t= 0;\n\tattackSpeed = 0;\n\n\tbaseHealth  = 1e9;\n\tbaseDamage \t= 0;\n\tmaxHealth = 1e9;\n\tdamage = 0;\n\n\t--> gameplay flags <--\n\tresilient = true;\n\n\t--> death reward stuff <--\n\tbaseEXP \t= 1;\n\tlevel \t\t= 1;\n\tbaseMoney \t= 0;\n\n\t--> spawn region stuff <--\n\tmonsterSpawnRegions = {\n\t\t[script.Name] = 1;\n\t\t[script.Name..\"2\"] = 1;\n\t\t[script.Name..\"3\"] = 1;\n\t};\n\n\tanimationDamageEnd \t= 1;\n\tdontScale \t\t\t= true;\n\n\t--> monster damage <--\n\tdamageHitboxCollection = {\n\n\t};\n\n\t--> loot drop stuff <--\n\tlootDrops \t= {\n\n\t};\n\n\t--> ease of access <--\n\tmodule \t= script;\n\tid \t\t= \"monster\";\n}\n\nreturn monsterData"
  },
  {
    "path": "src/ReplicatedStorage/monsterLookup/Elder Shroom.lua",
    "content": "local monsterData = {\n\tlevel = 5;\n\t\n\t--> combat stats <--\n\tattackRange = 6;\n\tbaseSpeed = 10;\n\tattackSpeed = 3.2;\n\tmaxHealth = 30;\n\tdamage = 8;\n\tdefense = 1;\n\t\n\t--> death reward stuff <--\n\tEXP = 20;\n\tgold = 25;\t\n\t\n\t--> monster damage <--\n\tdamageHitboxCollection = {\n\t\t{partName = \"RightFoot\"; castType = \"sphere\"; radius = 3; originOffset = CFrame.new(0, 0.5, 1.5)};\n\t};\n\t-- only for effect rn \n\tdamageHitboxCollection2 = {\n\t\t{partName = \"RightFoot\"; castType = \"sphere\"; radius = 6; originOffset = CFrame.new(0, -2.5, 0)};\n\t};\n\t\n\t--> loot drop stuff <--\n\tlootDrops \t= {\n\t\t{id = 1; spawnChance = 3/4};\n\t\t{itemName = \"mushroom beard\"; spawnChance = 1};\n\t\t{itemName = \"mushroom mini\"; spawnChance = 1/7};\n\t\t{itemName = \"mushroom spore\"; spawnChance = 1/7};\n\t\t{itemName = \"health potion\"; spawnChance = 1/8};\n\t\t{itemName = \"mana potion\"; spawnChance = 1/12};\n\t\t{itemName = \"100% weapon attack scroll\"; spawnChance = 1/40};\n\t\t{itemName = \"rune mushtown\"; spawnChance = 1/120};\n\t\t{itemName = \"shoulder pads 2\"; spawnChance = 1/150};\n\t\t{itemName = \"worn boots\"; spawnChance = 1/200};\n\t\t\n\t};\n\t\n\t--> ease of access <--\n\tmodule = script;\n\t\n\tanimationDamageStart = .3;\n\tanimationDamageEnd = 1;\n}\n\nreturn monsterData"
  },
  {
    "path": "src/ReplicatedStorage/monsterLookup/Shroom.lua",
    "content": "local monsterData = {\n\tlevel = 3;\n\t\n\t--> combat stats <--\n\tattackRange = 3.4;\n\tbaseSpeed = 17;\n\tattackSpeed = 1.5;\n\tmaxHealth = 14;\n\tdamage = 4;\n\tdefense = 0;\n\t\n\t--> death reward stuff <--\n\tEXP = 7;\n\tgold = 10;\n\t\n\tvariations = {\n\t\tpoisoned =\t{\n\t\t\tspecialName = \"Poisoned Shroom\";\n\t\t\tdye = {r=160;g=255;b=160};\n\t\t\tlevel = 6;\n\t\t\tbonusXPMulti = 2.5;\n\t\t\tbonusLootMulti = 4;\n\t\t\thealthMulti = 2;\n\t\t\tscale = 1.25;\n\t\t\tspecialAttackHealth = 1;\n\t\t\tspecialAttackCap = 10;\n\t\t\t\n\t\t};\n\t};\n\t\n\t--> monster damage <--\n\tdamageHitboxCollection = {\n\t\t{partName = \"LeftLeg\"; castType = \"sphere\"; radius = 3; originOffset = CFrame.new(0, -0.3, 0.4)};\n\t};\n\t\n\t--> loot drop stuff <--\n\tlootDrops \t= {\n\t\t{id = 1; spawnChance = 3/4};\n\t\t{itemName = \"mushroom mini\"; spawnChance = 1};\n\t\t{itemName = \"mushroom mini\"; spawnChance = 1/7};\n\t\t{itemName = \"health potion\"; spawnChance = 1/10};\n\t\t{itemName = \"mana potion\"; spawnChance = 1/15};\n\t\t{itemName = \"wooden sword\"; spawnChance = 1/80;};\n\t\t{itemName = \"leather tunic\"; spawnChance = 1/100;};\n\t};\n\t\n\t--> ease of access <--\n\tmodule \t\t\t= script;\n\tmonsterEvents \t= {};\n\n}\n\nreturn monsterData"
  },
  {
    "path": "src/ReplicatedStorage/monsterLookup/init.lua",
    "content": "--[[\n\tmonsterData {}\n\n--]]\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal models = replicatedStorage:WaitForChild(\"assets\"):WaitForChild(\"monsters\")\n\nlocal lookupTable = {} do\n\tfor _, monsterDataModule in pairs(script:GetChildren()) do\n\t\tlocal monsterData = require(monsterDataModule)\n\t\tlocal entry = models:FindFirstChild(monsterDataModule.Name)\n\n\t\tif entry then\n\t\t\t-- internal stuff\n\t\t\tmonsterData.module = monsterDataModule\n\t\t\tmonsterData.entity = entry:WaitForChild(\"entity\")\n\n\t\t\tlocal defaultStatesData = require(replicatedStorage.defaultMonsterState)\n\t\t\tlocal statesData\n\t\t\tlocal statesModule = monsterDataModule:FindFirstChild(\"states\")\n\t\t\t\n\t\t\tif statesModule then\n\t\t\t\tstatesData = require(statesModule)\n\t\t\tend\n\n\t\t\tif statesData then\n\t\t\t\tsetmetatable(statesData, {__index = defaultStatesData})\n\t\t\t\tsetmetatable(statesData.states, {__index = defaultStatesData.states})\n\t\t\telse\n\t\t\t\tstatesData = defaultStatesData\n\t\t\tend\n\n\t\t\tmonsterData.statesData = statesData\n\t\t\t-- hook ups\n\t\t\tlookupTable[monsterDataModule.Name] = monsterData\n\t\tend\n\tend\nend\n\nreturn lookupTable"
  },
  {
    "path": "src/ReplicatedStorage/perkLookup/colosseum equipment.lua",
    "content": "local modules = require(game.ReplicatedStorage.modules)\n\tlocal network = modules.load(\"network\")\n\tlocal tempData = modules.load(\"tempData\")\n\tlocal events = modules.load(\"events\")\n\tlocal utilities = modules.load(\"utilities\")\n\nlocal rand = Random.new()\n\nlocal perkData = {\n\tsharedValues = {\n\t\tlayoutOrder = 0;\n\t\tsubtitle = \"Equipment Perk\";\n\t\tcolor = Color3.fromRGB(234, 102, 84);\n\t},\n\tperks = {\n\n\t\t[\"inoculation\"] = {\n\t\t\ttitle = \"Inoculation\",\n\t\t\tdescription = \"10% chance to heal 200 HP when damaged.\",\n\t\t\t\n\t\t\tonDamageTaken = function(sourceManifest, sourceType, sourceId, targetManifest, damageData)\n\t\t\t\tif damageData.damage > 1 and rand:NextNumber() <= 0.1 then\n\t\t\t\t\t-- wait required to seperate damage from healing\n\t\t\t\t\t-- healing should probably get its own signal like damage has now\n\t\t\t\t\twait(0.1)\n\t\t\t\t\tpcall(function()\n\t\t\t\t\t\tutilities.healEntity(targetManifest, targetManifest, 200)\n\t\t\t\t\t\tlocal heal = script.heal:Clone()\n\t\t\t\t\t\theal.Parent = targetManifest\n\t\t\t\t\t\theal:Emit(30)\n\t\t\t\t\t\tgame.Debris:AddItem(heal,3)\n\t\t\t\t\t\tutilities.playSound(\"item_heal\", targetManifest)\n\t\t\t\t\tend)\n\t\t\t\tend\n\t\t\tend;\n\t\t},\n\t\t[\"resonance\"] = {\n\t\t\ttitle = \"Resonance Charms\",\n\t\t\tdescription = \"Restore 35% of damage taken as MP.\",\n\t\t\t\n\t\t\tonDamageTaken = function(sourceManifest, sourceType, sourceId, targetManifest, damageData)\n\t\t\t\tif damageData.damage > 1 then\n\t\t\t\t\t-- wait required to seperate damage from healing\n\t\t\t\t\t-- healing should probably get its own signal like damage has now\n\t\t\t\t\twait(0.1)\n\t\t\t\t\tpcall(function()\n\t\t\t\t\t\tlocal restored = math.floor(damageData.damage * 0.35)\n\n\t\t\t\t\t\tlocal actuallyRestored \n\t\t\t\t\t\tif targetManifest.mana.Value + restored > targetManifest.maxMana.Value then\n\t\t\t\t\t\t\tactuallyRestored = targetManifest.maxMana.Value - targetManifest.mana.Value\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tactuallyRestored = restored\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\ttargetManifest.mana.Value = math.min(targetManifest.mana.Value + restored, targetManifest.maxMana.Value)\n\t\t\t\t\t\tif actuallyRestored >= 10 then\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tlocal heal = script.manaEmitter:Clone()\n\t\t\t\t\t\t\theal.Parent = targetManifest\n\t\t\t\t\t\t\theal:Emit(math.floor(math.clamp( 3 * actuallyRestored^(1/2), 1, 50)))\n\t\t\t\t\t\t\tgame.Debris:AddItem(heal,3)\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tutilities.playSound(\"item_mana\", targetManifest, nil, {volume = 0.3; maxDistance = 100; emitterSize = 7}) \n\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\t\t\t\tend\n\t\t\tend;\n\t\t},\t\t\n\t\t[\"reckless\"] = {\n\t\t\ttitle = \"Reckless\",\n\t\t\tdescription = \"Deal and recieve 120% damage.\",\n\t\t\t\n\t\t\tonDamageTaken = function(sourceManifest, sourceType, sourceId, targetManifest, ref_damageData)\n\t\t\t\tref_damageData.damage = ref_damageData.damage * 1.2\n\t\t\tend;\n\t\t\tonDamageGiven = function(sourceManifest, sourceType, sourceId, targetManifest, ref_damageData)\n\t\t\t\tref_damageData.damage = ref_damageData.damage * 1.2\n\t\t\tend\n\t\t},\t\t\n\t}\n}\nreturn perkData"
  },
  {
    "path": "src/ReplicatedStorage/perkLookup/dunes equipment.lua",
    "content": "local modules = require(game.ReplicatedStorage.modules)\nlocal network = modules.load(\"network\")\nlocal targeting = modules.load(\"damage\")\nlocal utilities = modules.load(\"utilities\")\n\nlocal itemLookup = require(game.ReplicatedStorage.itemData)\n\nlocal function isDay(clockTime)\n\treturn (clockTime > 6) and (clockTime < 18)\nend\n\nlocal function isNight(clockTime)\n\treturn not isDay(clockTime)\nend\n\nlocal perkData = {\n\tsharedValues = {\n\t\tlayoutOrder = 0,\n\t\tsubtitle = \"Equipment Perk\",\n\t\tcolor = BrickColor.new(\"Cool yellow\").Color,\n\t},\n\t\n\tperks = {\n\t\t[\"overdraw\"] = {\n\t\t\ttitle = \"Overdraw\",\n\t\t\tdescription = \"Fires larger arrows further.\",\n\t\t\t\n\t\t\t-- implementation is with arrow basic attacks\n\t\t},\n\t\t\n\t\t[\"you look great\"] = {\n\t\t\ttitle = \"You Look Great\",\n\t\t\tdescription = \"Not even the Sun can stand in your way.\",\n\t\t\t\n\t\t\t-- implementation is somewhere\n\t\t},\t\t\n\t\t\n\t\t[\"solar wind\"] = {\n\t\t\ttitle = \"Solar Wind\",\n\t\t\tdescription = \"Increases the range of Blink.\",\n\t\t\t\n\t\t\t-- implementation is in the Blink ability\n\t\t},\n\t\t\n\t\t[\"one good hit\"] = {\n\t\t\ttitle = \"One Good Hit\",\n\t\t\tdescription = \"Execute instantly kills low health non-resilient foes.\",\n\t\t\t\n\t\t\t-- implementation is in Execute ability\n\t\t},\n\t\t\n\t\t[\"aftershock\"] = {\n\t\t\ttitle = \"Aftershock\",\n\t\t\tdescription = \"Adds shockwaves to Ground Slam.\",\n\t\t\t\n\t\t\t-- implementation is in Ground Slam ability\n\t\t},\n\t\t\n\t\t[\"dunes wisdom\"] = {\n\t\t\ttitle = \"Dunes Wisdom\",\n\t\t\tdescription = \"Regenerate extra mana during the day.\",\n\t\t\t\n\t\t\tonTimeOfDayUpdated = function(player, clockTime, dt)\n\t\t\t\tif not isDay(clockTime) then return end\n\t\t\t\t\n\t\t\t\tlocal char = player.Character\n\t\t\t\tif not char then return end\n\t\t\t\tlocal entity = char.PrimaryPart\n\t\t\t\tif not entity then return end\n\t\t\t\tlocal mana = entity:FindFirstChild(\"mana\")\n\t\t\t\tlocal maxMana = entity:FindFirstChild(\"maxMana\")\n\t\t\t\tif not (mana and maxMana) then return end\n\t\t\t\t\n\t\t\t\tlocal regenerationPerSecond = 6\n\t\t\t\tmana.Value = math.min(maxMana.Value, mana.Value + regenerationPerSecond * dt)\n\t\t\tend,\n\t\t},\n\t\t\n\t\t[\"apogee\"] = {\n\t\t\ttitle = \"Apogee\",\n\t\t\tdescription = \"During the day, deal more damage and regenerate health.\",\n\t\t\t\n\t\t\tonDamageGiven = function(sourceManifest, sourceType, sourceId, targetManifest, damageData)\n\t\t\t\tif isDay(game.Lighting.ClockTime) then\n\t\t\t\t\tdamageData.damage = damageData.damage * 1.2\n\t\t\t\tend\n\t\t\tend,\n\t\t\t\n\t\t\tonTimeOfDayUpdated = function(player, clockTime, dt)\n\t\t\t\tif not isDay(clockTime) then return end\n\t\t\t\t\n\t\t\t\tlocal char = player.Character\n\t\t\t\tif not char then return end\n\t\t\t\tlocal entity = char.PrimaryPart\n\t\t\t\tif not entity then return end\n\t\t\t\tlocal health = entity:FindFirstChild(\"health\")\n\t\t\t\tlocal maxHealth = entity:FindFirstChild(\"maxHealth\")\n\t\t\t\tlocal state = entity:FindFirstChild(\"state\")\n\t\t\t\tif not (health and maxHealth and state) then return end\n\t\t\t\tif state.Value == \"dead\" then return end\n\t\t\t\t\n\t\t\t\tlocal regenerationPerSecond = 20\n\t\t\t\thealth.Value = math.min(maxHealth.Value, health.Value + regenerationPerSecond * dt)\n\t\t\tend,\n\t\t},\n\t\t\n\t\t[\"midnight\"] = {\n\t\t\ttitle = \"Midnight\",\n\t\t\tdescription = \"During the night, deal more damage and move faster.\",\n\t\t\t\n\t\t\tonDamageGiven = function(sourceManifest, sourceType, sourceId, targetManifest, damageData)\n\t\t\t\tif isNight(game.Lighting.ClockTime) then\n\t\t\t\t\tdamageData.damage = damageData.damage * 1.2\n\t\t\t\tend\n\t\t\tend,\n\t\t\t\n\t\t\tonTimeOfDayUpdated = function(player, clockTime, dt)\n\t\t\t\tif not isNight(clockTime) then return end\n\t\t\t\t\n\t\t\t\tlocal char = player.Character\n\t\t\t\tif not char then return end\n\t\t\t\tlocal entity = char.PrimaryPart\n\t\t\t\tif not entity then return end\n\t\t\t\t\n\t\t\t\tnetwork:invoke(\n\t\t\t\t\t\"applyStatusEffectToEntityManifest\",\n\t\t\t\t\tentity,\n\t\t\t\t\t\"empower\",\n\t\t\t\t\t{\n\t\t\t\t\t\thideInStatusBar = true,\n\t\t\t\t\t\tduration = 5,\n\t\t\t\t\t\tmodifierData = {walkspeed = 4},\n\t\t\t\t\t},\n\t\t\t\t\tentity,\n\t\t\t\t\t\"perk\"\n\t\t\t\t)\n\t\t\tend,\n\t\t},\n\t\t\n\t\t[\"twilight\"] = {\n\t\t\ttitle = \"Twilight\",\n\t\t\tdescription = \"Empower Magic Missile to launch Mana Stars.\",\n\t\t\t\n\t\t\t-- implementation is in Magic Missile\n\t\t},\n\t\t\n\t\t[\"acidic arrows\"] = {\n\t\t\ttitle = \"Acidic Arrows\",\n\t\t\tdescription = \"Critical strikes on enemies create a splash of acid.\",\n\t\t\t\n\t\t\tonCritGiven = function(sourceManifest, sourceType, sourceId, targetManifest, damageData)\n\t\t\t\tlocal player = game:GetService(\"Players\"):GetPlayerFromCharacter(sourceManifest.Parent)\n\t\t\t\tif not player then return end\n\t\t\t\t\n\t\t\t\tlocal isArrow = (damageData.sourceType == \"equipment\") and (damageData.category == \"projectile\")\n\t\t\t\tif not isArrow then return end\n\t\t\t\t\n\t\t\t\tlocal radius = 12\n\t\t\t\t\n\t\t\t\tnetwork:fireAllClients(\"effects_requestEffect\", \"acidSplash\", {\n\t\t\t\t\tposition = damageData.position,\n\t\t\t\t\tradius = radius,\n\t\t\t\t\tduration = 0.25,\n\t\t\t\t})\n\t\t\t\t\n\t\t\t\tlocal function damageEntity(entity)\n\t\t\t\t\tlocal entityType = entity:FindFirstChild(\"entityType\")\n\t\t\t\t\tif not entityType then return end\n\t\t\t\t\tentityType = entityType.Value\n\t\t\t\t\t\n\t\t\t\t\tlocal damageData = {\n\t\t\t\t\t\tdamage = damageData.damage / 2,\n\t\t\t\t\t\tsourceType = \"perk\",\n\t\t\t\t\t\tsourceId = 0,\n\t\t\t\t\t\tdamageType = \"magical\",\n\t\t\t\t\t\tsourcePlayerId = player.UserId,\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tif entityType == \"monster\" then\n\t\t\t\t\t\tnetwork:invoke(\"monsterDamageRequest_server\", player, entity, damageData)\n\t\t\t\t\telse\n\t\t\t\t\t\tnetwork:invoke(\"playerDamageRequest_server\", player, entity, damageData)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\tlocal radiusSq = radius * radius\n\t\t\t\tfor _, target in pairs(targeting.getDamagableTargets(game.Players.LocalPlayer)) do\n\t\t\t\t\tlocal delta = target.Position - damageData.position\n\t\t\t\t\tlocal distanceSq = delta.X * delta.X + delta.Y * delta.Y + delta.Z * delta.Z\n\t\t\t\t\tif distanceSq < radiusSq then\n\t\t\t\t\t\tdamageEntity(target)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend,\n\t\t},\n\t\t\n\t\t[\"dunes courage\"] = {\n\t\t\ttitle = \"Dunes Courage\",\n\t\t\tdescription = \"Deal more damage to non-human Dunes enemies.\",\n\t\t\t\n\t\t\tonDamageGiven = function(sourceManifest, sourceType, sourceId, targetManifest, damageData)\n\t\t\t\tlocal n = targetManifest.Name\n\t\t\t\tif\n\t\t\t\t\tn == \"Stingtail\" or\n\t\t\t\t\tn == \"Deathsting\" or\n\t\t\t\t\tn == \"Sandwurm\" or\n\t\t\t\t\tn == \"Scarab\"\n\t\t\t\tthen\n\t\t\t\t\tdamageData.damage = damageData.damage * 1.2\n\t\t\t\tend\n\t\t\tend,\n\t\t},\n\t\t\n\t\t[\"bloodcraze\"] = {\n\t\t\ttitle = \"Bloodcraze\",\n\t\t\tdescription = \"Critical hits grant a stacking attack speed bonus.\",\n\t\t\t\n\t\t\tonCritGiven = function(sourceManifest, sourceType, sourceId, targetManifest, damageData)\n\t\t\t\tlocal guid = utilities.getEntityGUIDByEntityManifest(sourceManifest)\n\t\t\t\tif not guid then return false end\n\t\t\t\t\n\t\t\t\tlocal statuses = network:invoke(\"getStatusEffectsOnEntityManifestByEntityGUID\", guid)\n\t\t\t\t\n\t\t\t\tlocal stacks = 0\n\t\t\t\t\n\t\t\t\tfor _, status in pairs(statuses) do\n\t\t\t\t\tif status.statusEffectType == \"bloodcrazed\" then\n\t\t\t\t\t\tstacks = status.statusEffectModifier.stacks\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\tstacks = math.min(stacks + 1, 6)\n\t\t\t\t\n\t\t\t\tnetwork:invoke(\n\t\t\t\t\t\"applyStatusEffectToEntityManifest\",\n\t\t\t\t\tsourceManifest,\n\t\t\t\t\t\"empower\",\n\t\t\t\t\t{\n\t\t\t\t\t\tstacks = stacks,\n\t\t\t\t\t\tduration = 8,\n\t\t\t\t\t\tmodifierData = {attackSpeed = 0.05 * stacks},\n\t\t\t\t\t},\n\t\t\t\t\tsourceManifest,\n\t\t\t\t\t\"item\",\n\t\t\t\t\t229\n\t\t\t\t)\n\t\t\tend\n\t\t},\n\t\t\n\t\t[\"weakening venom\"] = {\n\t\t\ttitle = \"Weakening Venom\",\n\t\t\tdescription = \"Melee attacks mark a target to take more damage.\",\n\t\t\t\n\t\t\tonDamageGiven = function(sourceManifest, sourceType, sourceId, targetManifest, damageData)\n\t\t\t\tif damageData.sourceType == \"equipment\" then\n\t\t\t\t\tnetwork:invoke(\n\t\t\t\t\t\t\"applyStatusEffectToEntityManifest\",\n\t\t\t\t\t\ttargetManifest,\n\t\t\t\t\t\t\"weakened by venom\",\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tduration = 5,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tsourceManifest,\n\t\t\t\t\t\t\"item\",\n\t\t\t\t\t\t230\n\t\t\t\t\t)\n\t\t\t\telseif damageData.sourceType == \"ability\" then\n\t\t\t\t\tlocal hasStatus, status = network:invoke(\"doesEntityHaveStatusEffect\", targetManifest, \"weakened by venom\")\n\t\t\t\t\t\n\t\t\t\t\tif hasStatus then\n\t\t\t\t\t\tdamageData.damage = damageData.damage * 1.5\n\t\t\t\t\t\tnetwork:invoke(\"revokeStatusEffectByStatusEffectGUID\", status.statusEffectGUID)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend,\n\t\t},\n\t\t\n\t\t[\"just in case\"] = {\n\t\t\ttitle = \"Just in Case\",\n\t\t\tdescription = \"Increase damage when above 80% health. Does not work in off-hand.\",\n\t\t\t\n\t\t\tonDamageGiven = function(sourceManifest, sourceType, sourceId, targetManifest, damageData)\n\t\t\t\tlocal bowId = 249\n\t\t\t\t\n\t\t\t\tlocal player = game.Players:GetPlayerFromCharacter(sourceManifest.Parent)\n\t\t\t\tif not player then return end\n\t\t\t\t\n\t\t\t\tlocal equipmentInfo = network:invoke(\"getPlayerEquipmentDataByEquipmentPosition\", player, 1)\n\t\t\t\tif equipmentInfo.id ~= bowId then return end\n\t\t\t\t\n\t\t\t\tlocal health = sourceManifest:FindFirstChild(\"health\")\n\t\t\t\tlocal maxHealth = sourceManifest:FindFirstChild(\"maxHealth\")\n\t\t\t\tif not (health and maxHealth) then return end\n\t\t\t\t\n\t\t\t\tlocal ratio = health.Value / maxHealth.Value\n\t\t\t\tif ratio < 0.8 then return end\n\t\t\t\t\n\t\t\t\tdamageData.damage = damageData.damage * 1.1\n\t\t\tend\n\t\t},\n\t\t\n\t\t[\"not like this\"] = {\n\t\t\ttitle = \"Not Like This\",\n\t\t\tdescription = \"Dramatically increase damage when below 20% health. Works in off-hand.\",\n\t\t\t\n\t\t\tonDamageGiven = function(sourceManifest, sourceType, sourceId, targetManifest, damageData)\n\t\t\t\tlocal health = sourceManifest:FindFirstChild(\"health\")\n\t\t\t\tlocal maxHealth = sourceManifest:FindFirstChild(\"maxHealth\")\n\t\t\t\tif not (health and maxHealth) then return end\n\t\t\t\t\n\t\t\t\tlocal ratio = health.Value / maxHealth.Value\n\t\t\t\tif ratio > 0.2 then return end\n\t\t\t\t\n\t\t\t\tdamageData.damage = damageData.damage * 1.4\n\t\t\tend\n\t\t},\n\t\t\n\t\t[\"living blade\"] = {\n\t\t\ttitle = \"Living Blade\",\n\t\t\tdescription = \"Basic attacks deal magic damage and scale accordingly.\",\n\t\t\t\n\t\t\tonDamageGiven = function(sourceManifest, sourceType, sourceId, targetManifest, damageData)\n\t\t\t\tif sourceType == \"equipment\" then\n\t\t\t\t\tdamageData.damageType = \"magical\"\n\t\t\t\tend\n\t\t\tend,\n\t\t}\n\t},\n}\nreturn perkData"
  },
  {
    "path": "src/ReplicatedStorage/perkLookup/init.lua",
    "content": "--[[\n\t\n\tperkFolder\n\t\ttable perks\n\t\ttable sharedValues\n\t\n\tperkData\n\t\tstring title\n\t\tstring description\n\t\tfunction apply[ref statistics_final]\n\t\ttable modifierData\n\t\t\n\t\t-- \"ref damageData\" can be modified to modify the damage dealt\n\t\tfunction onAbilityHit[sourceEntity, sourceType, sourceId, targetManifest, ref damageData]\n\t\tfunction onBasicAttackHit[sourceEntity, sourceType, sourceId, targetManifest, ref damageData]\n\t\t\n\t\tfunction onDamageDealt[sourceEntity, sourceType, sourceId, targetManifest, ref damageData]\n\t\tfunction onDamageReceived[sourceEntity, sourceType, sourceId, targetManifest, ref damageData]\n--]]\n\nlocal perkLookup = {}\n\nfor i,folder in pairs(script:GetChildren()) do\n\tlocal perkFolderData = require(folder)\n\tfor perkName, perk in pairs(perkFolderData.perks) do\n\t\tif perkFolderData.sharedValues then\n\t\t\tfor key, data in pairs(perkFolderData.sharedValues) do\n\t\t\t\tperk[key] = data\n\t\t\tend\n\t\tend\n\t\tperk.folder = folder.Name\n\t\tperkLookup[perkName] = perk\n\tend\nend\n\nreturn perkLookup\n"
  },
  {
    "path": "src/ReplicatedStorage/perkLookup/misc.lua",
    "content": "local perkData = {\n\tperks = {\n\t\t--[[\n\t\t[\"warrior\"] = {\n\t\t\tlayoutOrder = 1;\n\t\t\ttitle = \"Steadfast\";\n\t\t\tsubtitle = \"Warrior Perk\";\n\t\t\tcolor = Color3.fromRGB(130, 59, 60);\n\t\t\tdescription = \"Take 20% less damage.\";\n\t\t\t-- auto-assign based on class\n\t\t\tclass = \"warrior\";\t\t\t\n\t\t\tapply \t\t= function(statistics_final)\n\t\t\t\tstatistics_final.damageTakenMulti = statistics_final.damageTakenMulti * 0.8\n\t\t\tend;\n\t\t};\t\n\t\t]]\n\t\t[\"holymagic\"] = {\n\t\t\tlayoutOrder = 1;\n\t\t\ttitle = \"Holy Magic\";\n\t\t\tsubtitle = \"Cleric Perk\";\n\t\t\tcolor = Color3.fromRGB(235, 198, 48);\n\t\t\tdescription = \"Mage Ability upgrade.\";\n\t\t\t-- auto-assign based on class\n\t\t\tclass = \"cleric\";\t\t\t\n\t\t};\t\t\t\n\t\t[\"colosseum\"] = {\n\t\t\tlayoutOrder = 2;\n\t\t\ttitle = \"Colosseum Blessing\";\n\t\t\tsubtitle = \"Location Perk\";\n\t\t\tcolor = Color3.fromRGB(226, 166, 61);\n\t\t\tdescription = \"Take 50% less damage.\";\n\t\t\t-- auto-assign based on class\n\t\t\tcondition   = function(statistics_final)\n\t\t\t\treturn game.PlaceId == 2496503573 or game.PlaceId == 4042381342\n\t\t\tend;\t\t\t\t\t\n\t\t\tapply \t\t= function(statistics_final)\n\t\t\t\tstatistics_final.damageTakenMulti = statistics_final.damageTakenMulti * 0.5\n\t\t\tend;\n\t\t};\n\t\t\n\t\t[\"battydagger\"] = {\n\t\t\tlayoutOrder = 1,\n\t\t\ttitle = \"Nocturnal Flight\",\n\t\t\tcolor = Color3.fromRGB(77, 34, 89),\n\t\t\tdescription = \"Shunpo has no cooldown.\",\n\t\t}\n\t}\n}\nreturn perkData"
  },
  {
    "path": "src/ReplicatedStorage/perkLookup/mokotuaa equipment.lua",
    "content": "local modules = require(game.ReplicatedStorage.modules)\n\tlocal network = modules.load(\"network\")\n\tlocal tempData = modules.load(\"tempData\")\n\tlocal events = modules.load(\"events\")\n\nlocal function keyDamage(slot)\n\treturn \"perk_rhythmDamage_\"..slot\nend\n\nlocal function keyStun(slot)\n\treturn \"perk_rhythmStun_\"..slot\nend\n\nlocal function keyDefense(slot)\n\treturn \"perk_rhythmDefense_\"..slot\nend\n\nlocal function keyMana(slot)\n\treturn \"perk_rhythmMana_\"..slot\nend\n\nlocal function keySplinter(slot)\n\treturn \"perk_rhythmSplinter_\"..slot\nend\n\nlocal function keyAttackSpeed(slot)\n\treturn \"perk_rhythmAttackSpeed_\"..slot\nend\n\nlocal perkData = {\n\tsharedValues = {\n\t\tlayoutOrder = 0;\n\t\tsubtitle = \"Equipment Perk\";\n\t\tcolor = Color3.fromRGB(235, 131, 82);\n\t},\n\tperks = {\n\t\t[\"rhythmDamage\"] = {\n\t\t\ttitle = \"Rhythmic Ferocity\",\n\t\t\tdescription = \"Every 4th basic attack deals extra damage.\",\n\t\t\t\n\t\t\tonEquipped = function(player, itemData, slot)\n\t\t\t\tif slot ~= \"1\" then return end\n\t\t\t\t\n\t\t\t\tlocal data = {\n\t\t\t\t\thits = 0,\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tdata.guid = events:registerForEvent(\"playerWillDealDamage\", function(playerDamaging, damageData)\n\t\t\t\t\tif playerDamaging ~= player then return end\n\t\t\t\t\tif damageData.sourceType ~= \"equipment\" then return end\n\t\t\t\t\t\n\t\t\t\t\tdata.hits = data.hits + 1\n\t\t\t\t\tif data.hits == 4 then\n\t\t\t\t\t\tdata.hits = 0\n\t\t\t\t\t\tdamageData.damage = damageData.damage * 2\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\t\t\n\t\t\t\ttempData:set(player, keyDamage(slot), data)\n\t\t\tend,\n\t\t\t\n\t\t\tonUnequipped = function(player, itemData, slot)\n\t\t\t\tif slot ~= \"1\" then return end\n\t\t\t\t\n\t\t\t\tlocal data = tempData:get(player, keyDamage(slot))\n\t\t\t\tevents:deregisterFromEvent(data.guid)\n\t\t\t\ttempData:delete(player, keyDamage(slot))\n\t\t\tend\n\t\t},\n\t\t\n\t\t[\"rhythmStun\"] = {\n\t\t\ttitle = \"Rhythmic Concussion\",\n\t\t\tdescription = \"Every 4th basic attack hit stuns the victim.\",\n\t\t\t\n\t\t\tonEquipped = function(player, itemData, slot)\n\t\t\t\tlocal data = {\n\t\t\t\t\thits = 0,\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tdata.guid = events:registerForEvent(\"playerWillDealDamage\", function(playerDamaging, damageData)\n\t\t\t\t\tif playerDamaging ~= player then return end\n\t\t\t\t\tif damageData.sourceType ~= \"equipment\" then return end\n\t\t\t\t\t\n\t\t\t\t\tlocal char = player.Character\n\t\t\t\t\tif not char then return end\n\t\t\t\t\tlocal manifest = char.PrimaryPart\n\t\t\t\t\tif not manifest then return end\n\t\t\t\t\t\n\t\t\t\t\tdata.hits = data.hits + 1\n\t\t\t\t\tif data.hits == 4 then\n\t\t\t\t\t\tdata.hits = 0\n\t\t\t\t\t\t\n\t\t\t\t\t\tnetwork:invoke(\n\t\t\t\t\t\t\t\"applyStatusEffectToEntityManifest\",\n\t\t\t\t\t\t\tdamageData.target,\n\t\t\t\t\t\t\t\"stunned\",\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tduration = 1,\n\t\t\t\t\t\t\t\tmodifierData = {\n\t\t\t\t\t\t\t\t\twalkspeed_totalMultiplicative = -1,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tmanifest,\n\t\t\t\t\t\t\t\"equipment\",\n\t\t\t\t\t\t\t213\n\t\t\t\t\t\t)\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\t\t\n\t\t\t\ttempData:set(player, keyStun(slot), data)\n\t\t\tend,\n\t\t\t\n\t\t\tonUnequipped = function(player, itemData, slot)\n\t\t\t\tlocal data = tempData:get(player, keyStun(slot))\n\t\t\t\tevents:deregisterFromEvent(data.guid)\n\t\t\t\ttempData:delete(player, keyStun(slot))\n\t\t\tend\n\t\t},\n\t\t\n\t\t[\"rhythmDefense\"] = {\n\t\t\ttitle = \"Rhythmic Tenacity\",\n\t\t\tdescription = \"Every 4th hit suffered deals half damage.\",\n\t\t\t\n\t\t\tonEquipped = function(player, itemData, slot)\n\t\t\t\tlocal data = {\n\t\t\t\t\thits = 0,\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tdata.guid = events:registerForEvent(\"playerWillTakeDamage\", function(playerDamaged, damageData)\n\t\t\t\t\tif playerDamaged ~= player then return end\n\t\t\t\t\t\n\t\t\t\t\tdata.hits = data.hits + 1\n\t\t\t\t\tif data.hits == 4 then\n\t\t\t\t\t\tdata.hits = 0\n\t\t\t\t\t\tdamageData.damage = damageData.damage / 2\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\t\t\n\t\t\t\ttempData:set(player, keyDefense(slot), data)\n\t\t\tend,\n\t\t\t\n\t\t\tonUnequipped = function(player, itemData, slot)\n\t\t\t\tlocal data = tempData:get(player, keyDefense(slot))\n\t\t\t\tevents:deregisterFromEvent(data.guid)\n\t\t\t\ttempData:delete(player, keyDefense(slot))\n\t\t\tend\n\t\t},\n\t\t\n\t\t[\"rhythmMana\"] = {\n\t\t\ttitle = \"Rhythmic Invocation\",\n\t\t\tdescription = \"Every 4th ability used costs no mana.\",\n\t\t\t\n\t\t\tonEquipped = function(player, itemData, slot)\n\t\t\t\tlocal data = {\n\t\t\t\t\tuses = 0,\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tdata.guid = events:registerForEvent(\"playerWillUseAbility\", function(playerUsing, abilityData)\n\t\t\t\t\tif playerUsing ~= player then return end\n\t\t\t\t\t\n\t\t\t\t\tdata.uses = data.uses + 1\n\t\t\t\t\tif data.uses == 4 then\n\t\t\t\t\t\tdata.uses = 0\n\t\t\t\t\t\tabilityData.manaCost = 0\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\t\t\n\t\t\t\ttempData:set(player, keyMana(slot), data)\n\t\t\tend,\n\t\t\t\n\t\t\tonUnequipped = function(player, itemData, slot)\n\t\t\t\tlocal data = tempData:get(player, keyMana(slot))\n\t\t\t\tevents:deregisterFromEvent(data.guid)\n\t\t\t\ttempData:delete(player, keyMana(slot))\n\t\t\tend\n\t\t},\n\t\t\n\t\t[\"rhythmSplinter\"] = {\n\t\t\ttitle = \"Rhythmic Splintering\",\n\t\t\tdescription = \"Only every 4th shot uses an arrow.\",\n\t\t\t\n\t\t\tonEquipped = function(player, itemData, slot)\n\t\t\t\tif slot ~= \"1\" then return end\n\t\t\t\t\n\t\t\t\tlocal data = {\n\t\t\t\t\tuses = 0,\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tdata.guid = events:registerForEvent(\"playerWillUseArrow\", function(playerUsing, arrowData)\n\t\t\t\t\tif playerUsing ~= player then return end\n\t\t\t\t\t\n\t\t\t\t\tdata.uses = data.uses + 1\n\t\t\t\t\tif data.uses == 4 then\n\t\t\t\t\t\tdata.uses = 0\n\t\t\t\t\t\t\n\t\t\t\t\t\tarrowData.needsArrow = true\n\t\t\t\t\telse\n\t\t\t\t\t\tarrowData.needsArrow = false\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\t\t\n\t\t\t\ttempData:set(player, keySplinter(slot), data)\n\t\t\tend,\n\t\t\t\n\t\t\tonUnequipped = function(player, itemData, slot)\n\t\t\t\tif slot ~= \"1\" then return end\n\t\t\t\t\n\t\t\t\tlocal data = tempData:get(player, keySplinter(slot))\n\t\t\t\tevents:deregisterFromEvent(data.guid)\n\t\t\t\ttempData:delete(player, keySplinter(slot))\n\t\t\tend\n\t\t},\n\t\t\n\t\t[\"rhythmAttackSpeed\"] = {\n\t\t\ttitle = \"Rhythmic Acceleration\",\n\t\t\tdescription = \"Every 4th hit temporarily boosts attack speed.\",\n\t\t\t\n\t\t\tonEquipped = function(player, itemData, slot)\n\t\t\t\tif slot ~= \"1\" then return end\n\t\t\t\t\n\t\t\t\tlocal data = {\n\t\t\t\t\thits = 0,\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tdata.guid = events:registerForEvent(\"playerWillDealDamage\", function(playerDamaging, damageData)\n\t\t\t\t\tif playerDamaging ~= player then return end\n\t\t\t\t\tif damageData.sourceType ~= \"equipment\" then return end\n\t\t\t\t\t\n\t\t\t\t\tlocal char = player.Character\n\t\t\t\t\tif not char then return end\n\t\t\t\t\tlocal manifest = char.PrimaryPart\n\t\t\t\t\tif not manifest then return end\n\t\t\t\t\t\n\t\t\t\t\tdata.hits = data.hits + 1\n\t\t\t\t\tif data.hits == 4 then\n\t\t\t\t\t\tdata.hits = 0\n\t\t\t\t\t\t\n\t\t\t\t\t\tnetwork:invoke(\n\t\t\t\t\t\t\t\"applyStatusEffectToEntityManifest\",\n\t\t\t\t\t\t\tmanifest,\n\t\t\t\t\t\t\t\"empower\",\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tduration = 2,\n\t\t\t\t\t\t\t\tmodifierData = {\n\t\t\t\t\t\t\t\t\tattackSpeed = 0.25,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tmanifest,\n\t\t\t\t\t\t\t\"item\",\n\t\t\t\t\t\t\t210\n\t\t\t\t\t\t)\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\t\t\n\t\t\t\ttempData:set(player, keyAttackSpeed(slot), data)\n\t\t\tend,\n\t\t\t\n\t\t\tonUnequipped = function(player, itemData, slot)\n\t\t\t\tif slot ~= \"1\" then return end\n\t\t\t\t\n\t\t\t\tlocal data = tempData:get(player, keyAttackSpeed(slot))\n\t\t\t\tevents:deregisterFromEvent(data.guid)\n\t\t\t\ttempData:delete(player, keyAttackSpeed(slot))\n\t\t\tend\n\t\t},\n\t}\n}\nreturn perkData"
  },
  {
    "path": "src/ReplicatedStorage/perkLookup/mushroom equipment.lua",
    "content": "local perkData = {\n\tsharedValues = {\n\t\tlayoutOrder = 0;\n\t\tsubtitle = \"Equipment Perk\";\n\t\tcolor = Color3.fromRGB(171, 47, 86);\n\t};\n\tperks = {\n\t\t[\"airborne\"] = {\n\t\t\ttitle = \"Airborne\";\n\t\t\tdescription = \"1.5x Basic Attack damage while jumping.\";\n\t\t\t\n\t\t\tonDamageGiven = function(sourceManifest, sourceType, sourceId, targetManifest, ref_damageData)\n\t\t\t\tif sourceManifest:FindFirstChild(\"state\") and sourceManifest.state.Value == \"jumping\" then\n\t\t\t\t\tif ref_damageData.sourceType == \"equipment\" then\n\t\t\t\t\t\tref_damageData.damage = ref_damageData.damage * 1.5\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend;\n\t\t};\n\t\t\n\t\t[\"poisonblink\"] = {\n\t\t\ttitle = \"Spore Cloud\";\n\t\t\tdescription = \"Blink releases a cloud of poisonous spores.\";\n\t\t};\n\t\t\n\t\t[\"bounceback\"] = {\n\t\t\ttitle = \"Bounceback\",\n\t\t\tdescription = \"15x Ability Knockback\",\n\t\t},\n\t}\n}\nreturn perkData"
  },
  {
    "path": "src/ReplicatedStorage/perkLookup/spider equipment.lua",
    "content": "local modules = require(game.ReplicatedStorage.modules)\n\tlocal network = modules.load(\"network\")\n\tlocal utilities = modules.load(\"utilities\")\n\nlocal SPIDER_BOW_ID = 92\nlocal SPIDER_DAGGER_ID = 52\n\nlocal perkData = {\n\tsharedValues = {\n\t\tlayoutOrder = 0;\n\t\tsubtitle = \"Equipment Perk\";\n\t\tcolor = Color3.fromRGB(147, 40, 234);\n\t};\n\tperks = {\n\t\t[\"causticwounds\"] = {\n\t\t\ttitle = \"Caustic Wounds\";\n\t\t\tdescription = \"Basic attacks inflict poison damage.\";\n\t\t\t\n\t\t\tonDamageGiven = function(sourceManifest, sourceType, sourceId, targetManifest, ref_damageData)\n\t\t\t\t-- only perform this bonus if we're using the dagger in main hand\n\t\t\t\tlocal player = game.Players:GetPlayerFromCharacter(sourceManifest.Parent)\n\t\t\t\tif not player then return end\n\t\t\t\tlocal data = network:invoke(\"getPlayerEquipmentDataByEquipmentPosition\", player, 1)\n\t\t\t\tif data.id ~= SPIDER_DAGGER_ID then return end\n\t\t\t\t\n\t\t\t\tif targetManifest:FindFirstChild(\"poisoned\") == nil and ref_damageData.sourceType == \"equipment\" then\n\t\t\t\t\tif targetManifest and targetManifest:FindFirstChild(\"health\") and targetManifest:FindFirstChild(\"maxHealth\") then\n\t\t\t\t\t\tif targetManifest.health.Value > 0 then\n\n\t\t\t\t\t\t\tlocal wasApplied, reason = require(game.ReplicatedStorage.modules.network):invoke(\n\t\t\t\t\t\t\t\t\"applyStatusEffectToEntityManifest\",\n\t\t\t\t\t\t\t\ttargetManifest,\n\t\t\t\t\t\t\t\t\"poison\",\n\t\t\t\t\t\t\t\t{duration = 3; healthLost = 1 * ref_damageData.damage},\n\t\t\t\t\t\t\t\tsourceManifest,\n\t\t\t\t\t\t\t\t\"perk\",\n\t\t\t\t\t\t\t\t52\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tif wasApplied then\n\t\t\t\t\t\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\t\t\t\t\t\ttag.Name = \"poisoned\"\n\t\t\t\t\t\t\t\ttag.Parent = targetManifest\n\t\t\t\t\t\t\t\tgame.Debris:AddItem(tag, 3)\t\n\t\t\t\t\t\t\t\tutilities.playSound(\"bubbles\", targetManifest)\t\t\n\t\t\t\t\t\t\t\tlocal poison = script.poison:Clone()\n\t\t\t\t\t\t\t\tpoison.Parent = targetManifest\n\t\t\t\t\t\t\t\tpoison:Emit(50)\n\t\t\t\t\t\t\t\tpoison.Enabled = true\n\t\t\t\t\t\t\t\tspawn(function()\n\t\t\t\t\t\t\t\t\twait(3)\n\t\t\t\t\t\t\t\t\tif poison then\n\t\t\t\t\t\t\t\t\t\tpoison.Enabled = false\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\t\tgame.Debris:AddItem(poison,5)\t\t\t\t\t\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\treturn wasApplied\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend;\n\t\t};\t\n\t\t[\"webbedshots\"] = {\n\t\t\ttitle = \"Webbed Shots\";\n\t\t\tdescription = \"Ranged attacks slow enemies.\";\n\t\t\t\n\t\t\tonDamageGiven = function(sourceManifest, sourceType, sourceId, targetManifest, ref_damageData)\n\t\t\t\t-- only perform this perk when we're in the main hand\n\t\t\t\tlocal player = game.Players:GetPlayerFromCharacter(sourceManifest.Parent)\n\t\t\t\tif not player then return end\n\t\t\t\tlocal data = network:invoke(\"getPlayerEquipmentDataByEquipmentPosition\", player, 1)\n\t\t\t\tif data.id ~= SPIDER_BOW_ID then return end\n\t\t\t\t\n\t\t\t\tif targetManifest and targetManifest:FindFirstChild(\"health\") and targetManifest:FindFirstChild(\"maxHealth\") then\n\t\t\t\t\tif targetManifest.health.Value > 0 then\n\t\t\t\t\t\tlocal wasApplied, reason = require(game.ReplicatedStorage.modules.network):invoke(\n\t\t\t\t\t\t\t\"applyStatusEffectToEntityManifest\",\n\t\t\t\t\t\t\ttargetManifest,\n\t\t\t\t\t\t\t\"empower\",\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tduration \t\t= 3;\n\t\t\t\t\t\t\t\tmodifierData \t= {\n\t\t\t\t\t\t\t\t\twalkspeed_totalMultiplicative = -0.35;\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\ttargetManifest,\n\t\t\t\t\t\t\t\"perk\",\n\t\t\t\t\t\t\t92\n\t\t\t\t\t\t)\n\t\t\t\t\t\tif wasApplied then\n\t\t\t\t\t\t\tlocal slow = script.slow:Clone()\n\t\t\t\t\t\t\tslow.Parent = targetManifest\n\t\t\t\t\t\t\tslow:Emit(30)\n\t\t\t\t\t\t\tgame.Debris:AddItem(slow, 3)\n\t\t\t\t\t\tend\n\t\t\t\t\t\t\n\t\t\t\t\t\treturn true\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend;\n\t\t};\t\n\t\t[\"venombomb\"] = {\n\t\t\ttitle = \"Venom Bomb\";\n\t\t\tdescription = \"Magic bombs leave behind damaging venom.\";\n\t\t\t\n\t\t\tapply = function(ref_statistics_final)\t\t\t\n\t\t\t\tref_statistics_final.VENOM_BOMB = true\n\t\t\tend;\n\t\t};\t\n\t\t[\"manathief\"] = {\n\t\t\ttitle = \"Mana Thief\";\n\t\t\tdescription = \"Basic attacks steal Mana.\";\n\t\t\t\n\t\t\tonDamageGiven = function(sourceManifest, sourceType, sourceId, targetManifest, ref_damageData)\n\t\t\t\tif ref_damageData.sourceType == \"equipment\" then\n\t\t\t\t\tif targetManifest and sourceManifest:FindFirstChild(\"mana\") and sourceManifest:FindFirstChild(\"maxMana\") then\n\t\t\t\t\t\tif targetManifest:FindFirstChild(\"mana\") and targetManifest:FindFirstChild(\"maxMana\") then\n\t\t\t\t\t\t\tsourceManifest.mana.Value = math.clamp(sourceManifest.mana.Value + 5, 0, sourceManifest.maxMana.Value)\n\t\t\t\t\t\t\ttargetManifest.mana.Value = math.clamp(targetManifest.mana.Value - 5, 0, targetManifest.maxMana.Value)\n\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tsourceManifest.mana.Value = math.clamp(sourceManifest.mana.Value + 2, 0, sourceManifest.maxMana.Value)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend;\n\t\t};\t\t\t\t\t\t\n\t\t\t\t\n\t}\n}\nreturn perkData"
  },
  {
    "path": "src/ReplicatedStorage/professionLookup/fishing.lua",
    "content": "\nlocal modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\n\tlocal projectile \t\t= modules.load(\"projectile\")\n\tlocal placeSetup \t\t= modules.load(\"placeSetup\")\n\tlocal client_utilities \t= modules.load(\"client_utilities\")\n\tlocal network \t\t\t= modules.load(\"network\")\n\nlocal abilityData = {\n\t--> identifying information <--\n\tid \t= 1;\n\t\n\t--> generic information <--\n\tname \t\t\t= \"Fishing\";\n\timage \t\t\t= \"rbxassetid://2585107511\";\n\tcolor \t\t\t= Color3.fromRGB(9, 79, 113);\n\tdescription \t= \"Catch some fish\";\n\t\n\t\n\t\n\t\n\n}\n\nreturn abilityData\n"
  },
  {
    "path": "src/ReplicatedStorage/professionLookup/init.lua",
    "content": "\t\n\nlocal lookupTable = {} do\n\tfor i, professionDataModule in pairs(script:GetChildren()) do\n\t\tlocal professionData = require(professionDataModule)\n\t\t\n\t\t-- internal stuff\n\t\tprofessionData.module = professionDataModule\n\t\t\n\t\t-- hook ups\n\t\tlookupTable[professionData.id] \t\t= professionData\n\t\tlookupTable[professionDataModule.Name] = professionData\n\tend\nend\n\nreturn lookupTable"
  },
  {
    "path": "src/ReplicatedStorage/questLookupNew/init.lua",
    "content": "local lookupTable = {}\n\nfor i, questModule in pairs(script:GetChildren()) do\n\tlocal questData = require(questModule)\n\tif questData then\n\t\tif lookupTable[questData.id] then\n\t\t\twarn(\"QuestId: \" .. questData.id .. \" already taken.\")\n\t\telse\n\t\t\tlookupTable[questData.id] = questData\n\t\tend\n\tend\nend\n\nreturn lookupTable\n"
  },
  {
    "path": "src/ReplicatedStorage/questLookupNew/testQuest.lua",
    "content": "local ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal Modules = require(ReplicatedStorage.modules)\nlocal Network = Modules.load(\"network\")\n\nlocal questData = {\n\tid = 1;\n\tname = \"Test Quest\";\n\tquestType = \"findAndReturn\";\n\t\n\tsteps = {\n\t\t[1] = {\n\t\t\tstepData = {\n\t\t\t\tstepType = \"findAndReturn\";\n\t\t\t\tobjective = \"Apple\"; -- Item name.\n\t\t\t\ttimeLimit = 0; -- Time in seconds, 0 = Infinite\n\t\t\t};\n\t\t};\n\t\t\n\t\t[2] = {\n\t\t\tstepData = {\n\t\t\t\tstepType = \"killMonster\";\n\t\t\t\tobjective = \"Baby Shroom\"; -- Monster name.\n\t\t\t\ttimeLimit = 0; -- Time in seconds, 0 = Infinite\n\t\t\t};\n\t\t};\n\t};\n\t\n\tstartData = {\n\t\ttriggerType = \"npc\";\n\t\ttrigger = \"Test NPC\";\n\t};\n\t\n\tfinishData = {\n\t\ttriggerType = \"npcReturn\";\n\t\ttrigger = \"Test NPC\";\n\t};\n}\n\nfunction questData:beginQuest(player)\n\t\nend\n\nfunction questData:beginQuestServer(player)\n\t\nend\n\nfunction questData:endQuest(player)\n\t\nend\n\nfunction questData:endQuestServer(player)\n\t\nend\n\nreturn questData\n"
  },
  {
    "path": "src/ReplicatedStorage/sounds.lua",
    "content": "-- All Vesteria sound effects\n-- made with tools/serializer.lua\n\nreturn {\n\t[\"potion\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://2095021574\",\n\t},\n\t[\"pickup\"] = {\n\t\tPlaybackSpeed = 1.7,\n\t\tVolume = 0.1,\n\t\tSoundId = \"rbxassetid://2094665072\",\n\t},\n\t[\"error\"] = {\n\t\tVolume = 0.2,\n\t\tSoundId = \"rbxassetid://3209996586\",\n\t},\n\t[\"coins\"] = {\n\t\tSoundId = \"rbxassetid://607665037\",\n\t},\n\t[\"scrollSuccess\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 1,\n\t\tEmitterSize = 5,\n\t\tSoundId = \"rbxassetid://2396595172\",\n\t},\n\t[\"bowFireStance\"] = {\n\t\tMaxDistance = 270,\n\t\tVolume = 4,\n\t\tEmitterSize = 2,\n\t\tSoundId = \"rbxassetid://4731966529\",\n\t},\n\t[\"rareItemDrop\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://2396499850\",\n\t},\n\t[\"rareItemPickup\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://2396558969\",\n\t},\n\t[\"giantEnemySpawned\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://4834146449\",\n\t},\n\t[\"giantEnemyBoom\"] = {\n\t\tMaxDistance = 400,\n\t\tVolume = 2,\n\t\tEmitterSize = 20,\n\t\tSoundId = \"rbxassetid://2401259303\",\n\t},\n\t[\"door\"] = {\n\t\tSoundId = \"rbxassetid://2414883467\",\n\t},\n\t[\"water_out\"] = {\n\t\tPlaybackSpeed = 1.1,\n\t\tMaxDistance = 65,\n\t\tVolume = 1,\n\t\tEmitterSize = 1,\n\t\tSoundId = \"rbxassetid://2480953100\",\n\t},\n\t[\"water_in\"] = {\n\t\tPlaybackSpeed = 1.1,\n\t\tMaxDistance = 65,\n\t\tVolume = 1,\n\t\tEmitterSize = 1,\n\t\tSoundId = \"rbxassetid://2480952670\",\n\t},\n\t[\"scrollFail\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 1,\n\t\tEmitterSize = 5,\n\t\tSoundId = \"rbxassetid://2396595731\",\n\t},\n\t[\"footstep_dirt\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491169979\",\n\t},\n\t[\"footstep_dirt2\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491170122\",\n\t},\n\t[\"footstep_dirt3\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491170245\",\n\t},\n\t[\"footstep_grass\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491170395\",\n\t},\n\t[\"footstep_sand2\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491170805\",\n\t},\n\t[\"footstep_grass2\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491170501\",\n\t},\n\t[\"footstep_sand\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491171031\",\n\t},\n\t[\"footstep_grass3\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491170625\",\n\t},\n\t[\"footstep_sand3\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491170886\",\n\t},\n\t[\"footstep_snow\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491172015\",\n\t},\n\t[\"footstep_snow2\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491172469\",\n\t},\n\t[\"footstep_snow3\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491172586\",\n\t},\n\t[\"footstep_stone\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491172765\",\n\t},\n\t[\"footstep_stone2\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491172866\",\n\t},\n\t[\"footstep_stone3\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.1,\n\t\tEmitterSize = 4,\n\t\tSoundId = \"rbxassetid://2491172965\",\n\t},\n\t[\"chest_unlock\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://2564067410\",\n\t},\n\t[\"fireIgnite\"] = {\n\t\tMaxDistance = 200,\n\t\tVolume = 1,\n\t\tEmitterSize = 5,\n\t\tSoundId = \"rbxassetid://2564068060\",\n\t},\n\t[\"fireLoop\"] = {\n\t\tMaxDistance = 150,\n\t\tLooped = true,\n\t\tSoundId = \"rbxassetid://2564068359\",\n\t},\n\t[\"chest_reward\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://2564067500\",\n\t},\n\t[\"fishing_BaitSplash\"] = {\n\t\tMaxDistance = 400,\n\t\tVolume = 0.6,\n\t\tSoundId = \"rbxassetid://2577401402\",\n\t},\n\t[\"fishing_FishBite\"] = {\n\t\tMaxDistance = 250,\n\t\tVolume = 0.2,\n\t\tSoundId = \"rbxassetid://2577401252\",\n\t},\n\t[\"fishingPoleCast_Short\"] = {\n\t\tMaxDistance = 250,\n\t\tVolume = 0.2,\n\t\tSoundId = \"rbxassetid://2577400844\",\n\t},\n\t[\"bowArrowImpact\"] = {\n\t\tMaxDistance = 250,\n\t\tVolume = 1,\n\t\tEmitterSize = 2,\n\t\tSoundId = \"rbxassetid://2765142756\",\n\t},\n\t[\"eat_food\"] = {\n\t\tSoundId = \"rbxassetid://2452054634\",\n\t},\n\t[\"spiderQueenScreech\"] = {\n\t\tVolume = 1.2,\n\t\tEmitterSize = 30,\n\t\tSoundId = \"rbxassetid://2686159346\",\n\t},\n\t[\"spiderQueenRotating\"] = {\n\t\tSoundId = \"rbxassetid://2686159185\",\n\t},\n\t[\"spiderQueenAbilityUse\"] = {\n\t\tSoundId = \"rbxassetid://2686159040\",\n\t},\n\t[\"spiderQueenSpawn\"] = {\n\t\tVolume = 1.5,\n\t\tEmitterSize = 30,\n\t\tSoundId = \"rbxassetid://2686159459\",\n\t},\n\t[\"bowFire\"] = {\n\t\tMaxDistance = 270,\n\t\tVolume = 0.2,\n\t\tEmitterSize = 2,\n\t\tSoundId = \"rbxassetid://2765142550\",\n\t},\n\t[\"bowDraw\"] = {\n\t\tMaxDistance = 120,\n\t\tVolume = 0.2,\n\t\tEmitterSize = 2,\n\t\tSoundId = \"rbxassetid://2765142358\",\n\t},\n\t[\"fishing_FishSplashOutOfWater\"] = {\n\t\tMaxDistance = 400,\n\t\tSoundId = \"rbxassetid://2577401081\",\n\t},\n\t[\"npc_male_old_mhm\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820828299\",\n\t},\n\t[\"npc_male_old_uh\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820828833\",\n\t},\n\t[\"npc_male_old_ouh\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820828598\",\n\t},\n\t[\"npc_female_hmm4\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820823423\",\n\t},\n\t[\"npc_female_huh\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820823573\",\n\t},\n\t[\"npc_female_laugh\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820823688\",\n\t},\n\t[\"npc_female_uhuh\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820823809\",\n\t},\n\t[\"npc_female_ah\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820823938\",\n\t},\n\t[\"npc_female_aha\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.4,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820824109\",\n\t},\n\t[\"npc_female_aha2\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.6,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820824290\",\n\t},\n\t[\"npc_male_grunt\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820824651\",\n\t},\n\t[\"npc_male_ahaa\"] = {\n\t\tMaxDistance = 100,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820824774\",\n\t},\n\t[\"npc_male_hm\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.6,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820824979\",\n\t},\n\t[\"npc_male_hmph\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820825124\",\n\t},\n\t[\"npc_male_aah\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820825274\",\n\t},\n\t[\"npc_tiny_laugh\"] = {\n\t\tPlaybackSpeed = 1.7,\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820825450\",\n\t},\n\t[\"npc_male_ah\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820825661\",\n\t},\n\t[\"npc_male_aha\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820825947\",\n\t},\n\t[\"npc_male_aha2\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820826277\",\n\t},\n\t[\"npc_male_eh\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820826734\",\n\t},\n\t[\"npc_male_grunt2\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820826986\",\n\t},\n\t[\"npc_male_huh\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820827159\",\n\t},\n\t[\"npc_male_huh2\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820827354\",\n\t},\n\t[\"npc_male_laugh2\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820828025\",\n\t},\n\t[\"npc_female_hmm\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.6,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820822988\",\n\t},\n\t[\"npc_female_hmm2\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820823153\",\n\t},\n\t[\"npc_female_hmm3\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820823291\",\n\t},\n\t[\"stranger_greeting\"] = {\n\t\tMaxDistance = 200,\n\t\tVolume = 1,\n\t\tEmitterSize = 5,\n\t\tSoundId = \"rbxassetid://2863573429\",\n\t},\n\t[\"npc_male_hobo\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2885930060\",\n\t},\n\t[\"ladder\"] = {\n\t\tVolume = 0.6,\n\t\tSoundId = \"rbxassetid://2886528596\",\n\t},\n\t[\"npc_male_burp\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2886886266\",\n\t},\n\t[\"itemEnchanted\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://3049829444\",\n\t},\n\t[\"kill\"] = {\n\t\tMaxDistance = 400,\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://5273351117\",\n\t},\n\t[\"blink\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 0.6,\n\t\tEmitterSize = 5,\n\t\tSoundId = \"rbxassetid://2621111307\",\n\t},\n\t[\"theYetiSpawn\"] = {\n\t\tMaxDistance = 1000002,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 500,\n\t\tSoundId = \"rbxassetid://3179288791\",\n\t},\n\t[\"ethyr4\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://3188891018\",\n\t},\n\t[\"ethyr3\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://3188890637\",\n\t},\n\t[\"ethyr2\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://3188890819\",\n\t},\n\t[\"ethyr1\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://3188890464\",\n\t},\n\t[\"bounce\"] = {\n\t\tMaxDistance = 150,\n\t\tSoundId = \"rbxassetid://2103090015\",\n\t},\n\t[\"npc_male_owh\"] = {\n\t\tPlaybackSpeed = 1.5,\n\t\tMaxDistance = 100,\n\t\tVolume = 1,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://3233161636\",\n\t},\n\t[\"legendaryItemDrop\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://3293384571\",\n\t},\n\t[\"legendaryItemPickup\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://3293384131\",\n\t},\n\t[\"questComplete\"] = {\n\t\tVolume = 0.1,\n\t\tSoundId = \"rbxassetid://3293432741\",\n\t},\n\t[\"idolDrop\"] = {\n\t\tVolume = 1.2,\n\t\tSoundId = \"rbxassetid://3373168511\",\n\t},\n\t[\"idolPickup\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://3373168139\",\n\t},\n\t[\"pvp\"] = {\n\t\tVolume = 0.3,\n\t\tEmitterSize = 40,\n\t\tSoundId = \"rbxassetid://3454331891\",\n\t},\n\t[\"npc_male_laugh\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820825450\",\n\t},\n\t[\"npc_female_burp\"] = {\n\t\tPlaybackSpeed = 1.2,\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2886886266\",\n\t},\n\t[\"npc_tiny_hmph\"] = {\n\t\tPlaybackSpeed = 1.2,\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820825124\",\n\t},\n\t[\"npc_tiny_grunt\"] = {\n\t\tPlaybackSpeed = 1.5,\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820824651\",\n\t},\n\t[\"npc_tiny_eh\"] = {\n\t\tPlaybackSpeed = 1.3,\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820826734\",\n\t},\n\t[\"npc_female_laugh_tiny\"] = {\n\t\tPlaybackSpeed = 1.5,\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820823688\",\n\t},\n\t[\"npc_female_huh_tiny\"] = {\n\t\tPlaybackSpeed = 1.5,\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820823573\",\n\t},\n\t[\"npc_male_hm_tiny\"] = {\n\t\tPlaybackSpeed = 1.7,\n\t\tMaxDistance = 100,\n\t\tVolume = 0.6,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820824979\",\n\t},\n\t[\"npc_female_hmm_tiny\"] = {\n\t\tPlaybackSpeed = 1.5,\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820822988\",\n\t},\n\t[\"npc_male_old_uh_tiny\"] = {\n\t\tPlaybackSpeed = 1.5,\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820828833\",\n\t},\n\t[\"npc_male_grunt_kid\"] = {\n\t\tPlaybackSpeed = 1.3,\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820824651\",\n\t},\n\t[\"npc_female_laugh_kid\"] = {\n\t\tPlaybackSpeed = 1.1,\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820823688\",\n\t},\n\t[\"magicAttack\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.6,\n\t\tEmitterSize = 5,\n\t\tSoundId = \"rbxassetid://3663291385\",\n\t},\n\t[\"item_mana\"] = {\n\t\tSoundId = \"rbxassetid://3663291828\",\n\t},\n\t[\"item_heal\"] = {\n\t\tVolume = 0.3,\n\t\tSoundId = \"rbxassetid://3663293574\",\n\t},\n\t[\"item_buff\"] = {\n\t\tSoundId = \"rbxassetid://3663292263\",\n\t},\n\t[\"stat_point\"] = {\n\t\tVolume = 0.6,\n\t\tSoundId = \"rbxassetid://3663293127\",\n\t},\n\t[\"stat_perk\"] = {\n\t\tSoundId = \"rbxassetid://3663292694\",\n\t},\n\t[\"lightning\"] = {\n\t\tMaxDistance = 120,\n\t\tVolume = 0.2,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2620389369\",\n\t},\n\t[\"splash\"] = {\n\t\tPlaybackSpeed = 2,\n\t\tMaxDistance = 65,\n\t\tVolume = 1.7,\n\t\tEmitterSize = 1,\n\t\tSoundId = \"rbxassetid://2480952670\",\n\t},\n\t[\"npc_female_huh_gross\"] = {\n\t\tPlaybackSpeed = 0.8,\n\t\tMaxDistance = 100,\n\t\tVolume = 0.8,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://2820823573\",\n\t},\n\t[\"questTurnedIn\"] = {\n\t\tPlaybackSpeed = 0.8,\n\t\tVolume = 0.6,\n\t\tSoundId = \"rbxassetid://3663292694\",\n\t},\n\t[\"npc_asnelar_hiss\"] = {\n\t\tMaxDistance = 100,\n\t\tVolume = 0.4,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://3236705213\",\n\t},\n\t[\"npc_asnelar_laugh\"] = {\n\t\tMaxDistance = 120,\n\t\tEmitterSize = 3,\n\t\tSoundId = \"rbxassetid://3236705652\",\n\t},\n\t[\"npc_moglo_grunt\"] = {\n\t\tPlaybackSpeed = 1.1,\n\t\tMaxDistance = 100,\n\t\tVolume = 1.5,\n\t\tEmitterSize = 5,\n\t\tSoundId = \"rbxassetid://2551318823\",\n\t},\n\t[\"bubbles\"] = {\n\t\tMaxDistance = 300,\n\t\tEmitterSize = 6,\n\t\tSoundId = \"rbxassetid://2669521458\",\n\t},\n\t[\"DEATH\"] = {\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://2820640568\",\n\t},\n\t[\"bush3\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://4831368756\",\n\t},\n\t[\"shunpoCast\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 1,\n\t\tEmitterSize = 5,\n\t\tSoundId = \"rbxassetid://4621088159\",\n\t},\n\t[\"shunpoImpact\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 1,\n\t\tEmitterSize = 5,\n\t\tSoundId = \"rbxassetid://4621088322\",\n\t},\n\t[\"orbLightning1\"] = {\n\t\tSoundId = \"rbxassetid://821439273\",\n\t},\n\t[\"orbLightning2\"] = {\n\t\tSoundId = \"rbxassetid://4818807812\",\n\t},\n\t[\"bush2\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://4831368446\",\n\t},\n\t[\"bush1\"] = {\n\t\tMaxDistance = 150,\n\t\tVolume = 1,\n\t\tSoundId = \"rbxassetid://4831368165\",\n\t},\n\t[\"swoosh_soft\"] = {\n\t\tPlaybackSpeed = 1.5,\n\t\tVolume = 0.2,\n\t\tSoundId = \"rbxassetid://997701840\",\n\t},\n\t[\"itemDrop\"] = {\n\t\tMaxDistance = 250,\n\t\tVolume = 1,\n\t\tEmitterSize = 5,\n\t\tSoundId = \"rbxassetid://5273348701\",\n\t},\n\t[\"restore\"] = {\n\t\tPlaybackSpeed = 1.2,\n\t\tSoundId = \"rbxassetid://3763842006\",\n\t},\n\t[\"swoosh\"] = {\n\t\tPlaybackSpeed = 2,\n\t\tVolume = 0.3,\n\t\tSoundId = \"rbxassetid://997701840\",\n\t},\n\t[\"boom\"] = {\n\t\tMaxDistance = 400,\n\t\tVolume = 0.6,\n\t\tSoundId = \"rbxassetid://3066286483\",\n\t},\n}"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/ablaze.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\nlocal utilities = modules.load(\"utilities\")\n\nlocal debris = game:GetService(\"Debris\")\n\nlocal statusEffectData = {\n\t--> identifying information <--\n\tid = 10;\n\n\t--> generic information <--\n\tname \t\t\t\t= \"Ablaze\";\n\tactiveEffectName \t= \"Ablaze\";\n\tstyleText \t\t\t= \"On fire (and not in a good way).\";\n\tdescription \t\t= \"\";\n\timage \t\t\t\t= \"rbxassetid://2528902271\";\n\n\tnotSavedToPlayerData = true,\n}\n\nlocal MAX_DAMAGE_PER_SECOND = 256\n\nfunction statusEffectData.onStarted_server(activeStatusEffectData, entityManifest)\n\tlocal emitterAttachment = Instance.new(\"Attachment\")\n\temitterAttachment.Position = Vector3.new(0, 0, 0)\n\temitterAttachment.Parent = entityManifest\n\n\tlocal emitter = script.emitter:Clone()\n\temitter.Parent = emitterAttachment\n\n\tactiveStatusEffectData.__emitterAttachment = emitterAttachment\nend\n\nfunction statusEffectData.onEnded_server(activeStatusEffectData, entityManifest)\n\tlocal emitterAttachment = activeStatusEffectData.__emitterAttachment\n\tif not emitterAttachment then return end\n\tlocal emitter = emitterAttachment:FindFirstChild(\"emitter\")\n\tif not emitter then return end\n\n\temitter.Enabled = false\n\tdebris:AddItem(emitterAttachment, emitter.Lifetime.Max)\nend\n\nfunction statusEffectData.execute(activeStatusEffectData, entityManifest, ticksPerSecond)\n\tlocal maxHealth = entityManifest:FindFirstChild(\"maxHealth\")\n\tif not maxHealth then return end\n\tlocal damage = maxHealth.Value * activeStatusEffectData.statusEffectModifier.percent\n\tlocal duration = activeStatusEffectData.statusEffectModifier.duration\n\tlocal dps = math.min(damage / duration, MAX_DAMAGE_PER_SECOND)\n\tlocal damage = dps / ticksPerSecond\n\tif damage <= 0 then return end\n\n\tlocal entityType = entityManifest:FindFirstChild(\"entityType\")\n\tif not entityType then return end\n\tentityType = entityType.Value\n\n\tlocal sourceGuid = activeStatusEffectData.sourceEntityGUID\n\tif not sourceGuid then return end\n\tlocal source = utilities.getEntityManifestByEntityGUID(sourceGuid)\n\tif not source then return end\n\tlocal sourceType = source:FindFirstChild(\"entityType\")\n\tif not sourceType then return end\n\tsourceType = sourceType.Value\n\tif sourceType ~= \"character\" then\n\t\treturn\n\tend\n\tlocal char = source.Parent\n\tlocal player = game.Players:GetPlayerFromCharacter(char)\n\tif not player then return end\n\n\tlocal damageData = {\n\t\tdamage = damage,\n\t\tsourceType = \"status\",\n\t\tsourceId = statusEffectData.id,\n\t\tdamageType = \"magical\",\n\t\tsourcePlayerId = player.UserId,\n\t\tsourceEntityGUID = activeStatusEffectData.sourceEntityGUID,\n\t}\n\n\tif entityType == \"monster\" then\n\t\tnetwork:invoke(\"monsterDamageRequest_server\", player, entityManifest, damageData)\n\telse\n\t\tnetwork:invoke(\"playerDamageRequest_server\", player, entityManifest, damageData)\n\tend\n\n\t-- remove this if my boy is dead\n\tlocal state = entityManifest:FindFirstChild(\"state\")\n\tif not state then return end\n\tstate = state.Value\n\tif state == \"dead\" then\n\t\tstatusEffectData.onEnded_server(activeStatusEffectData, entityManifest)\n\tend\nend\n\nreturn statusEffectData"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/empower.lua",
    "content": "local statusEffectData = {\n\t--> identifying information <--\n\tid = 4;\n\n\t--> generic information <--\n\tname \t\t\t\t= \"Empowered\";\n\tactiveEffectName \t= \"Empowered\";\n\tstyleText \t\t\t= \"This unit is empowered.\";\n\tdescription \t\t= \"\";\n\timage \t\t\t\t= \"rbxassetid://2528902271\";\n}\n\n--\t\t\t\t\t\t\t(renderCharacterContainer, \ttargetPosition, isAbilitySource, hitNormal, nil, \tguid)\nfunction statusEffectData:execute()\n\nend\n\nreturn statusEffectData"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/explode.lua",
    "content": "local statusEffectData = {\n\t--> identifying information <--\n\tid = 5;\n\n\t--> generic information <--\n\tname \t\t\t\t= \"Explode\";\n\tactiveEffectName \t= \"Exploding\";\n\tstyleText \t\t\t= \"This unit is exploding.\";\n\tdescription \t\t= \"\";\n\timage \t\t\t\t= \"rbxassetid://2528902271\";\n\n\t--\n\tstatusEffectApplicationData = {\n\t\tduration = 8;\n\t}\n}\n\n-- explode status effect works like this:\n-- duration refers to the wind-up time BEFORE THE EXPLOSION!\n-- when the effect is ADDED, the user will see the windup particle effects\n-- when the effect is REMOVED, the explosion effect will actually occur and deal whatever damage\n\nfunction statusEffectData.__clientApplyStatusEffectOnCharacter(renderCharacterContainer)\n\tif not renderCharacterContainer or not renderCharacterContainer:FindFirstChild(\"entity\") then return false end\n\n\nend\n\nfunction statusEffectData.__clientApplyTransitionEffectOnCharacter(renderCharacterContainer)\n\t-- do nothing here\nend\n\nfunction statusEffectData.__clientRemoveStatusEffectOnCharacter(renderCharacterContainer)\n\tif not renderCharacterContainer or not renderCharacterContainer:FindFirstChild(\"entity\") then return false end\nend\n\nfunction statusEffectData._serverExecutionFunction(activeStatusEffectData, entityManifest)\n\nend\n\nfunction statusEffectData._serverCleanupFunction(activeStatusEffectData, entityManifest)\n\nend\n\nfunction statusEffectData.onStarted_server(activeStatusEffectData, entityManifest)\n\tif entityManifest then\n\n\tend\nend\n\nfunction statusEffectData.onEnded_server(activeStatusEffectData, entityManifest)\n\tif entityManifest then\n\n\tend\nend\n\nreturn statusEffectData"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/heat exhausted.lua",
    "content": "local statusEffectData = {\n\t--> identifying information <--\n\tid = 18;\n\n\t--> generic information <--\n\tname \t\t\t\t= \"Heat Exhausted\";\n\tactiveEffectName \t= \"Heat Exhausted\";\n\tstyleText \t\t\t= \"Suffering from heat exhaustion.\";\n\tdescription \t\t= \"\";\n\timage \t\t\t\t= \"rbxassetid://334869557\";\n\n\thideInStatusBar = true,\n\tnotSavedToPlayerData = true,\n}\n\nfunction statusEffectData.execute(activeStatusEffectData, entityManifest, ticksPerSecond)\n\t-- purely handled by the client\nend\n\nreturn statusEffectData"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/init.lua",
    "content": "--[[\n\tabilityData {}\n\t\t--> identifying information <--\n\t\tint abilityId\n\n\t\t--> generic information <--\n\t\tstring name\n\t\tstring image\n\t\tstring description\n\n\t\t--> execution information <--\n\t\tfunction \texecute\n\t\tnumber \t\tmaxRank\n\t\tnumber \t\tcooldown\n\t\tnumber \t\tcost\n\t\ttable \t\tprerequisite\n\n\tprerequisiteData {}\n\t\tnumber abilityId\n\t\tnumber points\n--]]\n\nlocal lookupTable = {} do\n\tfor _, statusEffectDataModule in pairs(script:GetChildren()) do\n\t\tlocal statusEffectData = require(statusEffectDataModule)\n\n\t\t-- internal stuff\n\t\tstatusEffectData.module = statusEffectDataModule\n\n\t\t-- hook ups\n\t\tlookupTable[statusEffectData.id] \t\t\t= statusEffectData\n\t\tlookupTable[statusEffectDataModule.Name] \t= statusEffectData\n\tend\nend\n\nreturn lookupTable"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/mystically bound.lua",
    "content": "local statusEffectData = {\n\t--> identifying information <--\n\tid \t= 6;\n\n\t--> generic information <--\n\tname \t\t\t\t= \"Mystically Bound\";\n\tactiveEffectName \t= \"Mystically Bound\";\n\tstyleText \t\t\t= \"Mystically bound in place.\";\n\tdescription \t\t= \"\";\n\timage \t\t\t\t= \"rbxassetid://2528902271\";\n}\n\n--\t\t\t\t\t\t\t(renderCharacterContainer, \ttargetPosition, isAbilitySource, hitNormal, nil, \tguid)\nfunction statusEffectData.execute(activeStatusEffectData, entityManifest, activeStatusEffectTickTimePerSecond)\n\nend\n\nfunction statusEffectData.onStarted_server(activeStatusEffectData, entityManifest)\n\tlocal binding = Instance.new(\"BodyPosition\")\n\tbinding.Name = \"MysticallyBoundBindingForce\"\n\tbinding.Position = entityManifest.Position + Vector3.new(0, 8, 0)\n\tbinding.MaxForce = Vector3.new(1e9, 1e9, 1e9)\n\tbinding.Parent = entityManifest\n\n\tactiveStatusEffectData.__binding = binding\nend\n\nfunction statusEffectData.onEnded_server(activeStatusEffectData, entityManifest)\n\tif not activeStatusEffectData.__binding then\n\t\twarn(\"CRITICAL ERROR WITH MYSTICALLY BOUND\\nCouldn't find binding force!\")\n\t\treturn\n\tend\n\n\tactiveStatusEffectData.__binding:Destroy()\nend\n\nreturn statusEffectData"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/poison.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\nlocal utilities = modules.load(\"utilities\")\n\nlocal statusEffectData = {\n\t--> identifying information <--\n\tid \t= 3;\n\n\t--> generic information <--\n\tname \t\t\t\t= \"Poisoned\";\n\tactiveEffectName \t= \"Poisoned\";\n\tstyleText \t\t\t= \"Poisoned and taking damage over time.\";\n\tdescription \t\t= \"\";\n\timage \t\t\t\t= \"rbxassetid://2528902271\";\n}\n\n--function statusEffectData.execute_old(playerStatusEffectData, targetEntity, ticksPerSecond)\n--\tlocal healthLost \t= playerStatusEffectData.statusEffectModifier.healthLost or 10\n--\tlocal duration \t\t= playerStatusEffectData.statusEffectModifier.duration or 5\n--\n--\tif targetEntity:FindFirstChild(\"health\") and targetEntity:FindFirstChild(\"maxHealth\") then\n--\t\ttargetEntity.health.Value = math.clamp(targetEntity.health.Value - healthLost / duration / ticksPerSecond, 0, targetEntity.maxHealth.Value)\n--\tend\n--end\n\nfunction statusEffectData.execute(activeStatusEffectData, entityManifest, ticksPerSecond)\n\tlocal totalDamage = activeStatusEffectData.statusEffectModifier.healthLost\n\tlocal duration = activeStatusEffectData.statusEffectModifier.duration\n\tlocal dps = totalDamage / duration\n\tlocal damage = dps / ticksPerSecond\n\n\tlocal entityType = entityManifest:FindFirstChild(\"entityType\")\n\tif not entityType then return end\n\tentityType = entityType.Value\n\n\tlocal sourceGuid = activeStatusEffectData.sourceEntityGUID\n\tif not sourceGuid then return end\n\tlocal source = utilities.getEntityManifestByEntityGUID(sourceGuid)\n\tif not source then return end\n\tlocal sourceType = source:FindFirstChild(\"entityType\")\n\tif not sourceType then return end\n\tsourceType = sourceType.Value\n\tif sourceType ~= \"character\" then\n\t\treturn\n\tend\n\tlocal char = source.Parent\n\tlocal player = game.Players:GetPlayerFromCharacter(char)\n\tif not player then return end\n\n\tlocal damageData = {\n\t\tdamage = damage,\n\t\tsourceType = \"status\",\n\t\tsourceId = statusEffectData.id,\n\t\tdamageType = \"magical\",\n\t\tsourcePlayerId = player.UserId,\n\t}\n\n\tif entityType == \"monster\" then\n\t\tnetwork:invoke(\"monsterDamageRequest_server\", player, entityManifest, damageData)\n\telse\n\t\tnetwork:invoke(\"playerDamageRequest_server\", player, entityManifest, damageData)\n\tend\nend\n\nfunction statusEffectData.__clientApplyStatusEffectOnCharacter(renderCharacterContainer)\n\tif not renderCharacterContainer or not renderCharacterContainer:FindFirstChild(\"entity\") then return false end\n\n\nend\n\nfunction statusEffectData.__clientRemoveStatusEffectOnCharacter(renderCharacterContainer)\n\nend\n\nreturn statusEffectData"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/potion.lua",
    "content": "local statusEffectData = {\n\t--> identifying information <--\n\tid \t= 1;\n\n\t--> generic information <--\n\tname \t\t\t\t= \"Potion\";\n\tactiveEffectName \t= \"Potioning\";\n\tstyleText \t\t\t= \"Activated a potion.\";\n\tdescription \t\t= \"\";\n\timage \t\t\t\t= \"rbxassetid://2528902271\";\n}\n\n--\t\t\t\t\t\t\t(renderCharacterContainer, \ttargetPosition, isAbilitySource, hitNormal, nil, \tguid)\nfunction statusEffectData.execute(activeStatusEffectData, entityManifest, activeStatusEffectTickTimePerSecond)\n\tprint('WE ARE RUNNING THE POT!')\n\tlocal healthHealed \t= activeStatusEffectData.statusEffectModifier.healthToHeal or 0\n\tlocal manaRestored \t= activeStatusEffectData.statusEffectModifier.manaToRestore or 0\n\tlocal duration \t\t= activeStatusEffectData.statusEffectModifier.duration or 5\n\n\tif entityManifest:FindFirstChild(\"health\") and entityManifest:FindFirstChild(\"maxHealth\") and healthHealed > 0 then\n\t\tentityManifest.health.Value = math.clamp(\n\t\t\tentityManifest.health.Value + healthHealed / duration / activeStatusEffectTickTimePerSecond,\n\t\t\t0,\n\t\t\tentityManifest.maxHealth.Value\n\t\t)\n\tend\n\n\tif entityManifest:FindFirstChild(\"mana\") and entityManifest:FindFirstChild(\"maxMana\") and manaRestored > 0 then\n\t\tentityManifest.mana.Value = math.clamp(\n\t\t\tentityManifest.mana.Value + manaRestored / duration / activeStatusEffectTickTimePerSecond,\n\t\t\t0,\n\t\t\tentityManifest.maxMana.Value\n\t\t)\n\tend\nend\n\nfunction statusEffectData.onStatusEffectApplied_server(player)\n\nend\n\nfunction statusEffectData.onStatusEffectRemoved_server(player)\n\nend\n\nfunction statusEffectData.onStatusEffectApplied_client(player)\n\nend\n\nfunction statusEffectData.onStatusEffectRemoved_client(player)\n\nend\n\nreturn statusEffectData"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/ranger stance.lua",
    "content": "local abilityAnimations = game:GetService(\"ReplicatedStorage\").assets:WaitForChild(\"abilityAnimations\")\n\nlocal modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\nlocal events = modules.load(\"events\")\n\nlocal debris = game:GetService(\"Debris\")\nlocal itemLookup = require(game.ReplicatedStorage.itemData)\n\nlocal statusEffectData = {\n\t--> identifying information <--\n\tid = 7;\n\n\t--> generic information <--\n\tname \t\t\t\t= \"Ranger's Stance\";\n\tactiveEffectName \t= \"Ranger's Stance\";\n\tstyleText \t\t\t= \"In ranger's stance.\";\n\tdescription \t\t= \"\";\n\timage \t\t\t\t= \"rbxassetid://2528902271\";\n\n\tnotSavedToPlayerData = true,\n}\n\nlocal function findTrackByAnimation(animator, animation)\n\tfor _, track in pairs(animator:GetPlayingAnimationTracks()) do\n\t\tif track.Animation == animation then\n\t\t\treturn track\n\t\tend\n\tend\n\treturn nil\nend\n\nfunction statusEffectData.__clientApplyTransitionEffectOnCharacter(renderCharacterContainer)\n\tlocal entity = renderCharacterContainer:FindFirstChild(\"entity\")\n\tif not entity then return end\n\n\tlocal animator = entity:FindFirstChild(\"AnimationController\")\n\tif not animator then return end\n\n\tlocal startTrack = animator:LoadAnimation(abilityAnimations.rangerStanceStarting)\n\tstartTrack.Looped = true\n\tstartTrack:Play()\n\n\tdelay(startTrack.Length - 0.05, function()\n\t\tif not startTrack.IsPlaying then return end\n\n\t\tlocal idleTrack = animator:LoadAnimation(abilityAnimations.rangerStanceIdling)\n\t\tidleTrack:Play(0)\n\n\t\tstartTrack:Stop(0)\n\tend)\nend\n\nfunction statusEffectData.__clientRemoveStatusEffectOnCharacter(renderCharacterContainer)\n\tlocal entity = renderCharacterContainer:FindFirstChild(\"entity\")\n\tif not entity then return end\n\n\tlocal animator = entity:FindFirstChild(\"AnimationController\")\n\tif not animator then return end\n\n\tlocal leaveTrack = animator:LoadAnimation(abilityAnimations.rangerStanceExiting)\n\tleaveTrack:Play(0)\n\n\tlocal startTrack = findTrackByAnimation(animator, abilityAnimations.rangerStanceStarting)\n\tif startTrack then\n\t\tstartTrack:Stop(0)\n\tend\n\n\tlocal idleTrack = findTrackByAnimation(animator, abilityAnimations.rangerStanceIdling)\n\tif idleTrack then\n\t\tidleTrack:Stop(0)\n\tend\nend\n\nfunction statusEffectData.onStarted_server(activeStatusEffectData, entityManifest)\n\tlocal emitterAttachment = Instance.new(\"Attachment\")\n\temitterAttachment.Position = Vector3.new(0, -2, 0)\n\temitterAttachment.Parent = entityManifest\n\n\tlocal emitter = script.emitter:Clone()\n\temitter.Parent = emitterAttachment\n\n\tactiveStatusEffectData.__emitterAttachment = emitterAttachment\n\n\tactiveStatusEffectData.__eventGuid = events:registerForEvent(\"playerEquipmentChanged\", function(player)\n\t\tlocal char = player.Character\n\t\tif not char then return end\n\t\tlocal manifest = char.PrimaryPart\n\t\tif not manifest then return end\n\n\t\tif manifest == entityManifest then\n\t\t\tnetwork:invoke(\"revokeStatusEffectByStatusEffectGUID\", activeStatusEffectData.statusEffectGUID)\n\t\tend\n\tend)\nend\n\nfunction statusEffectData.onEnded_server(activeStatusEffectData, entityManifest)\n\tlocal emitterAttachment = activeStatusEffectData.__emitterAttachment\n\tif not emitterAttachment then return end\n\tlocal emitter = emitterAttachment:FindFirstChild(\"emitter\")\n\tif not emitter then return end\n\n\temitter.Enabled = false\n\tdebris:AddItem(emitterAttachment, emitter.Lifetime.Max)\n\n\tevents:deregisterEventByGuid(activeStatusEffectData.__eventGuid)\nend\n\nfunction statusEffectData.execute(activeStatusEffectData, entityManifest, ticksPerSecond)\n\tlocal manaCost = activeStatusEffectData.statusEffectModifier.manaCost / ticksPerSecond\n\n\tlocal mana = entityManifest:FindFirstChild(\"mana\")\n\tif not mana then return end\n\n\tif mana.Value >= manaCost then\n\t\tmana.Value = mana.Value - manaCost\n\telse\n\t\tnetwork:invoke(\"revokeStatusEffectByStatusEffectGUID\", activeStatusEffectData.statusEffectGUID)\n\tend\n\n\t-- revoke instantly if we're not holding a bow, ok?\n\tlocal player = game.Players:GetPlayerFromCharacter(entityManifest.Parent)\n\tif player then\n\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\tlocal equipment = playerData.equipment\n\n\t\tfor _, equipmentInfo in pairs(equipment) do\n\t\t\tif equipmentInfo.position == 1 then\n\t\t\t\tlocal itemData = itemLookup[equipmentInfo.id]\n\t\t\t\tif itemData then\n\t\t\t\t\tif itemData.equipmentType ~= \"bow\" then\n\t\t\t\t\t\tnetwork:invoke(\"revokeStatusEffectByStatusEffectGUID\", activeStatusEffectData.statusEffectGUID)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nreturn statusEffectData"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/refreshed.lua",
    "content": "local statusEffectData = {\n\t--> identifying information <--\n\tid = 19;\n\n\t--> generic information <--\n\tname \t\t\t\t= \"Refreshed\";\n\tactiveEffectName \t= \"Refreshed\";\n\tstyleText \t\t\t= \"Hydrated and refreshed.\";\n\tdescription \t\t= \"\";\n\timage \t\t\t\t= \"rbxassetid://3298789929\";\n\n\tnotSavedToPlayerData = true,\n}\n\nfunction statusEffectData.execute(activeStatusEffectData, entityManifest, ticksPerSecond)\n\nend\n\nreturn statusEffectData"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/regenerate.lua",
    "content": "local statusEffectData = {\n\t--> identifying information <--\n\tid \t= 1;\n\n\t--> generic information <--\n\tname \t\t\t\t= \"Regeneration\";\n\tactiveEffectName \t= \"Regenerating\";\n\tstyleText \t\t\t= \"Regenerating health.\";\n\tdescription \t\t= \"\";\n\timage \t\t\t\t= \"rbxassetid://2528902271\";\n}\n\nfunction statusEffectData.execute(activeStatusEffectData, entityManifest, activeStatusEffectTickTimePerSecond)\n\tlocal healthHealed = activeStatusEffectData.statusEffectModifier.healthToHeal or 10\n\tlocal duration = activeStatusEffectData.statusEffectModifier.duration or 5\n\n\tlocal health = entityManifest:FindFirstChild(\"health\")\n\tlocal maxHealth = entityManifest:FindFirstChild(\"maxHealth\")\n\tif health and maxHealth then\n\t\thealth.Value = math.clamp(health.Value + healthHealed / duration / activeStatusEffectTickTimePerSecond, 0, maxHealth.Value)\n\tend\nend\n\nreturn statusEffectData"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/stealth.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\nlocal events = modules.load(\"events\")\n\nlocal statusEffectData = {\n\t--> identifying information <--\n\tid = 5;\n\n\t--> generic information <--\n\tname \t\t\t\t= \"Stealth\";\n\tactiveEffectName \t= \"Stealthed\";\n\tstyleText \t\t\t= \"This unit is stealthed.\";\n\tdescription \t\t= \"\";\n\timage \t\t\t\t= \"rbxassetid://2528902271\";\n\n\t--\n\tstatusEffectApplicationData = {\n\t\tduration = 8;\n\t}\n}\n\nlocal effectTagName = \"stealthStatusEffectVisualEffectTag\"\n\nfunction statusEffectData.__clientApplyStatusEffectOnCharacter(renderCharacterContainer)\n\tif not renderCharacterContainer or not renderCharacterContainer:FindFirstChild(\"entity\") then return false end\n\n\t-- hide stealthed units\n\tlocal transparencyToSetTo = 1 do\n\t\tif game.Players.LocalPlayer.Character and game.Players.LocalPlayer.Character.PrimaryPart then\n\t\t\tlocal isClient = renderCharacterContainer.clientHitboxToServerHitboxReference.Value == game.Players.LocalPlayer.Character.PrimaryPart\n\n\t\t\tif isClient then\n\t\t\t\t-- what user sees\n\t\t\t\ttransparencyToSetTo = 0.8\n\t\t\telse\n\t\t\t\ttransparencyToSetTo = 1\n\t\t\tend\n\t\tend\n\tend\n\n--\tlocal tag = renderCharacterContainer.entity.PrimaryPart:FindFirstChild(effectTagName)\n--\tif not tag then return end\n\n\tfor i, obj in pairs(renderCharacterContainer.entity:GetChildren()) do\n\t\tif obj:IsA(\"BasePart\") and obj ~= renderCharacterContainer.entity.PrimaryPart then\n\t\t\tobj.Transparency \t= transparencyToSetTo\n\t\t\tobj.Material \t\t= Enum.Material.Glass\n\t\tend\n\tend\nend\n\nfunction statusEffectData.__clientApplyTransitionEffectOnCharacter(renderCharacterContainer)\n\tif not renderCharacterContainer or not renderCharacterContainer:FindFirstChild(\"entity\") then return false end\n\n\t-- hide stealthed units\n\tlocal transparencyToSetTo = 1 do\n\t\tif game.Players.LocalPlayer.Character and game.Players.LocalPlayer.Character.PrimaryPart then\n\t\t\tlocal isClient = renderCharacterContainer.clientHitboxToServerHitboxReference.Value == game.Players.LocalPlayer.Character.PrimaryPart\n\n\t\t\tif isClient then\n\t\t\t\t-- what user sees\n\t\t\t\ttransparencyToSetTo = 0.8\n\t\t\telse\n\t\t\t\ttransparencyToSetTo = 1\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal tag = Instance.new(\"BoolValue\")\n\ttag.Name = effectTagName\n\ttag.Parent = renderCharacterContainer.entity.PrimaryPart\n\n\tlocal iters = 10\n\tlocal models = {}\n\n\t\tspawn(function()\n\t\tfor i = 1, iters do\n\t\t\tif not tag.Parent then return end\n\n\t\t\tlocal transparency = transparencyToSetTo * i / iters\n\n\t\t\tfor _, obj in pairs(renderCharacterContainer.entity:GetDescendants()) do\n\t\t\t\tif obj:IsA(\"BasePart\") and obj ~= renderCharacterContainer.entity.PrimaryPart then\n\t\t\t\t\tobj.Transparency \t= transparency\n\t\t\t\t\tobj.Material \t\t= Enum.Material.Glass\n\n\t\t\t\telseif obj:IsA(\"RopeConstraint\") then\n\t\t\t\t\tobj.Visible = false\n\t\t\t\tend\n\t\t\tend\n\n\t\t\twait(1 / 30)\n\t\tend\n\n\t\tstatusEffectData.__clientApplyStatusEffectOnCharacter(renderCharacterContainer)\n\tend)\nend\n\nfunction statusEffectData.__clientRemoveStatusEffectOnCharacter(renderCharacterContainer)\n\tlocal tag = renderCharacterContainer.entity.PrimaryPart:FindFirstChild(effectTagName)\n\tif tag then\n\t\ttag:Destroy()\n\tend\n\n\tfor _, obj in pairs(renderCharacterContainer.entity:GetDescendants()) do\n\t\tif obj:IsA(\"BasePart\") and obj ~= renderCharacterContainer.entity.PrimaryPart then\n\t\t\tobj.Transparency = 0\n\t\t\tobj.Material = Enum.Material.SmoothPlastic\n\t\telseif obj:IsA(\"RopeConstraint\") then\n\t\t\tobj.Visible = true\n\t\tend\n\tend\nend\n\nfunction statusEffectData.onStarted_server(activeStatusEffectData, entityManifest)\n\tlocal stealthTag = Instance.new(\"BoolValue\", entityManifest)\n\tstealthTag.Name = \"isStealthed\"\n\tstealthTag.Value = true\n\n\tif entityManifest and entityManifest:FindFirstChild(\"entityType\") and entityManifest.entityType.Value == \"character\" then\n\t\tlocal stealthedPlayer = game.Players:GetPlayerFromCharacter(entityManifest.Parent)\n\n\t\tlocal function onStealthBroken(player)\n\t\t\tif player ~= stealthedPlayer then return end\n\n\t\t\tlocal char = player.Character\n\t\t\tif not char then return end\n\t\t\tlocal manifest = char.PrimaryPart\n\t\t\tif not manifest then return end\n\n\t\t\tif manifest == entityManifest then\n\t\t\t\tnetwork:invoke(\"revokeStatusEffectByStatusEffectGUID\", activeStatusEffectData.statusEffectGUID)\n\t\t\tend\n\t\tend\n\n\t\tactiveStatusEffectData.__eventGuids = {\n\t\t\tevents:registerForEvent(\"playerUsedAbility\", function(player, abilityId)\n\t\t\t\tlocal abilitiesById = require(game.ReplicatedStorage.abilityLookup)\n\t\t\t\tlocal abilityData = abilitiesById[abilityId]\n\t\t\t\tif not abilityData.doesNotBreakStealth then\n\t\t\t\t\tonStealthBroken(player)\n\t\t\t\tend\n\t\t\tend),\n\t\t\tevents:registerForEvent(\"playerWillUseBasicAttack\", onStealthBroken),\n\t\t\tevents:registerForEvent(\"playerWillTakeDamage\", onStealthBroken)\n\t\t}\n\n\t\tactiveStatusEffectData.__damageEventGuid = events:registerForEvent(\"playerWillDealDamage\", function(player, damageData)\n\t\t\tif player ~= stealthedPlayer then return end\n\n\t\t\tif damageData.sourceType == \"equipment\" then\n\t\t\t\tdamageData.damage = damageData.damage * activeStatusEffectData.statusEffectModifier.damageMultiplier\n\t\t\t\tevents:deregisterEventByGuid(activeStatusEffectData.__damageEventGuid)\n\t\t\tend\n\t\tend)\n\tend\nend\n\nfunction statusEffectData.onEnded_server(activeStatusEffectData, entityManifest)\n\tif entityManifest:FindFirstChild(\"isStealthed\") then\n\t\tentityManifest.isStealthed:Destroy()\n\tend\n\n\tif entityManifest and entityManifest:FindFirstChild(\"entityType\") and entityManifest.entityType.Value == \"character\" then\n\t\tfor _, guid in pairs(activeStatusEffectData.__eventGuids) do\n\t\t\tevents:deregisterEventByGuid(guid)\n\t\tend\n\n\t\tdelay(1, function()\n\t\t\tevents:deregisterEventByGuid(activeStatusEffectData.__damageEventGuid)\n\t\tend)\n\n\t\tlocal player = game.Players:GetPlayerFromCharacter(entityManifest.Parent)\n\t\tif not player then return end\n\n\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart:FindFirstChild(\"isStealthed\") then\n\t\t\tplayer.Character.PrimaryPart.isStealthed:Destroy()\n\t\tend\n\tend\nend\n\nreturn statusEffectData"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/stunned.lua",
    "content": "local statusEffectData = {\n\t--> identifying information <--\n\tid = 13;\n\n\t--> generic information <--\n\tname \t\t\t\t= \"Stunned\";\n\tactiveEffectName \t= \"Stunned\";\n\tstyleText \t\t\t= \"Stunned.\";\n\tdescription \t\t= \"\";\n\timage \t\t\t\t= \"rbxassetid://2528902271\";\n}\n\nfunction statusEffectData:execute()\n\nend\n\nreturn statusEffectData"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/taunted.lua",
    "content": "local modules = require(game.ReplicatedStorage.modules)\nlocal network = modules.load(\"network\")\n\nlocal statusEffectData = {\n\t--> identifying information <--\n\tid = 16;\n\n\t--> generic information <--\n\tname \t\t\t\t= \"Taunted\";\n\tactiveEffectName \t= \"Taunted\";\n\tstyleText \t\t\t= \"Taunted and less resilient.\";\n\tdescription \t\t= \"\";\n\timage \t\t\t\t= \"rbxassetid://2528902271\";\n}\n\nfunction statusEffectData.execute(activeStatusEffectData, entityManifest, ticksPerSecond)\n\tlocal target = activeStatusEffectData.statusEffectModifier.target\n\tnetwork:invoke(\"setMonsterTargetEntity\", entityManifest, target, \"status\", 3)\nend\n\nreturn statusEffectData"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectLookup/weakened by venom.lua",
    "content": "local debris = game:GetService(\"Debris\")\n\nlocal statusEffectData = {\n\t--> identifying information <--\n\tid = 22;\n\n\t--> generic information <--\n\tname \t\t\t\t= \"Weakened by Venom\";\n\tactiveEffectName \t= \"Weakened by Venom\";\n\tstyleText \t\t\t= \"Weakened by the venom of a Stingtail Staff.\";\n}\n\nfunction statusEffectData.onStarted_server(activeStatusEffectData, entityManifest)\n\tlocal emitter = script.emitter:Clone()\n\temitter.Parent = entityManifest\n\n\tactiveStatusEffectData.__emitter = emitter\nend\n\nfunction statusEffectData.onEnded_server(activeStatusEffectData, entityManifest)\n\tlocal emitter = activeStatusEffectData.__emitter\n\tif not emitter then return end\n\n\temitter.Enabled = false\n\tdebris:AddItem(emitter, emitter.Lifetime.Max)\nend\n\nreturn statusEffectData"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectsV3/ablaze.lua",
    "content": "--[[\n\tdata {}\n\t\tint damage\n\t\tstring sourceEntityGUID\n]]\n\nlocal module = {\n\tdescription = \"Lose health over time! Jump into water to put out.\";\n\tdoesStack \t= true;\n}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network = modules.load(\"network\")\n\t\tlocal entityUtilities = modules.load(\"entityUtilities\")\n\t\tlocal events = modules.load(\"events\")\n\nlocal activeEffects = {}\n\nfunction module.__onStatusEffectBegan(statusEffect)\n\tlocal targetEntity = entityUtilities.getEntityManifestByEntityGUID(statusEffect.targetEntityGUID)\n\t\n\tif not targetEntity then return end\n\t\n\tlocal ablazeEmitter \t= script.emitter:Clone()\n\tablazeEmitter.Name \t\t= \"ablazeEmitter\"\n\tablazeEmitter.Parent \t= targetEntity\n\t\n\tactiveEffects[statusEffect.guid] = statusEffect\n\t\n\tcoroutine.wrap(function()\n\t\twhile activeEffects[statusEffect] and statusEffect.isActive do\n\t\t\tlocal targetEntity = entityUtilities.getEntityManifestByEntityGUID(statusEffect.targetEntityGUID)\n\t\t\t\n\t\t\tif targetEntity then\n\t\t\t\tlocal hitPart, hitPosition, hitNormal, hitMaterial = workspace:FindPartOnRayWithWhitelist(\n\t\t\t\t\tRay.new(\n\t\t\t\t\t\ttargetEntity.Position,\n\t\t\t\t\t\tVector3.new(0, -targetEntity.Size.Y / 2 - 3, 0)\n\t\t\t\t\t),\n\t\t\t\t\t\n\t\t\t\t\t{workspace.Terrain}\n\t\t\t\t)\n\t\t\t\t\n\t\t\t\tif hitMaterial == Enum.Material.Water then\n\t\t\t\t\tstatusEffect:stop()\n\t\t\t\t\t\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\t-- how often we checkin?\n\t\t\t\twait(1.5)\n\t\t\telse\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\tend)()\nend\n\nfunction module.__onStatusEffectEnded(statusEffect)\n\tactiveEffects[statusEffect.guid] = nil\n\t\n\tlocal targetEntity = entityUtilities.getEntityManifestByEntityGUID(statusEffect.targetEntityGUID)\n\t\n\tif not targetEntity then return end\n\t\n\tif targetEntity:FindFirstChild(\"ablazeEmitter\") then\n\t\ttargetEntity.ablazeEmitter:Destroy()\n\tend\nend\n\n-- note: the statusEffect here is NON-FUNCTIONAL\n-- it lost all its functionality, it was JSONified. ONLY READ FROM THE OBJECT.\nfunction module.__onClientStatusEffectBegan(statusEffect)\n\t\nend\n\n-- note: the statusEffect here is NON-FUNCTIONAL\n-- it lost all its functionality, it was JSONified. ONLY READ FROM THE OBJECT.\nfunction module.__onClientStatusEffectEnded(statusEffect)\n\t\nend\n\nfunction module.__onStatusEffectTick(statusEffect, t)\n\tlocal rate = statusEffect.data.damage / statusEffect.data.duration\n\t\n\tlocal targetEntity = entityUtilities.getEntityManifestByEntityGUID(statusEffect.targetEntityGUID)\n\tlocal sourceEntity = entityUtilities.getEntityManifestByEntityGUID(statusEffect.sourceEntityGUID)\n\t\n\tif targetEntity then\n\t\t-- turn into a table and triggerEvent so that the amount can be modified\n\t\t-- if applicable\n\t\tlocal burnData = {\n\t\t\tdamage = rate * t * statusEffect.stacks;\n\t\t\t\n\t\t\tsourceType = \"status\";\n\t\t\tsourceId = statusEffect.id;\n\t\t\tdamageType = \"physical\";\n\t\t\tsourceEntityGUID = statusEffect.sourceEntityGUID;\n\t\t}\n\t\t\n\t\t-- apply damage\n\t\tlocal entityType = targetEntity:FindFirstChild(\"entityType\")\n\t\tif not entityType then return end\n\t\tentityType = entityType.Value\n\t\n\t\tif entityType == \"monster\" then\n\t\t\tnetwork:invoke(\"monsterDamageRequest_server\", nil, targetEntity, burnData)\n\t\telse\n\t\t\tnetwork:invoke(\"playerDamageRequest_server\", nil, targetEntity, burnData)\n\t\tend\t\n\tend\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectsV3/bleed.lua",
    "content": "local module = {\n\tdescription = \"Heal over time.\"\n}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal events = modules.load(\"events\")\n\nfunction module.__onStatusEffectBegan(statusEffect, t)\n\t\nend\n\nfunction module.__onStatusEffectEnded(statusEffect, t)\n\t\nend\n\nfunction module.__onStatusEffectTick(statusEffect, t)\n\tlocal rate = statusEffect.data.amount / statusEffect.data.duration\n\t\n\tlocal character = statusEffect.entity\n\t\n\tif character then\n\t\t-- turn into a table and triggerEvent so that the amount can be modified\n\t\tlocal healingData = {amount = rate * t; isImmuneToReduction = false}\n\t\tevents.triggerEvent(\"beforeEntityHealed\", character, healingData)\n\t\t\n\t\t-- heal the target\n\t\tcharacter.Humanoid.Health = character.Humanoid.Health + healingData.amount\n\t\tprint('heal in tick', t, healingData.amount)\n\t\t\n\t\tevents.triggerEvent(\"afterEntityHealed\", character, healingData)\n\tend\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectsV3/init.lua",
    "content": "--[[\n\tstatusEffect {}\n\t\tstring icon;\n\t\t\n]]"
  },
  {
    "path": "src/ReplicatedStorage/statusEffectsV3/regenerate.lua",
    "content": "local module = {\n\tdescription = \"Heal over time.\"\n}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal modules = require(replicatedStorage.modules)\nlocal events = modules.load(\"events\")\n\nfunction module.__onStatusEffectBegan(statusEffect, t)\n\nend\n\nfunction module.__onStatusEffectEnded(statusEffect, t)\n\nend\n\nfunction module.__onStatusEffectTick(statusEffect, t)\n\tlocal rate = statusEffect.data.amount / statusEffect.data.duration\n\n\tlocal character = statusEffect.entity\n\n\tif character then\n\t\t-- turn into a table and triggerEvent so that the amount can be modified\n\t\tlocal healingData = {amount = rate * t; isImmuneToReduction = false}\n\t\tevents.fireEventLocal(\"beforeEntityHealed\", character, healingData)\n\n\t\t-- heal the target\n\t\tcharacter.Humanoid.Health = character.Humanoid.Health + healingData.amount\n\t\tprint('heal in tick', t, healingData.amount)\n\n\t\tevents.fireEventLocal(\"afterEntityHealed\", character, healingData)\n\tend\nend\n\nreturn module"
  },
  {
    "path": "src/ReplicatedStorage/temporaryEquipment/init.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/ReplicatedStorage/temporaryEquipment/lantern/application.lua",
    "content": "return function(renderCharacter)\n\tif not renderCharacter:FindFirstChild(\"LowerTorso\") then return false end\n\tif renderCharacter:FindFirstChild(\"LANTERN_TEMP_EQUIP\") then return false end\n\t\n\tlocal lanternModel \t= script.Parent:Clone()\n\t\tlanternModel.application:Destroy()\n\tlanternModel.Name \t= \"LANTERN_TEMP_EQUIP\"\n\t\n\tfor i, obj in pairs(lanternModel:GetChildren()) do\n\t\tif obj:IsA(\"BasePart\") then\n\t\t\tobj.CanCollide \t= false\n\t\t\tobj.Anchored \t= false\n\t\tend\n\tend\n\t\n\tlocal attachmentMotor \t= Instance.new(\"Motor6D\")\n\tattachmentMotor.Part0 \t= renderCharacter.LowerTorso\n\tattachmentMotor.Part1 \t= lanternModel.Main\n\tattachmentMotor.C1 \t\t= CFrame.new(renderCharacter.LowerTorso.Size.X / 2 - 0.1, lanternModel.Main.Size.Y / 2, -0.15) * CFrame.Angles(0, 0, 0) * CFrame.Angles(0, 0, math.pi / 6) + Vector3.new(0, 0.5, 0)\n\tattachmentMotor.Parent \t= lanternModel.Main\n\n--\tlanternModel.Main.BallSocketConstraint.Attachment1 \t= renderCharacter.LowerTorso.RightHipRigAttachment\n--\tlanternModel.Main.RopeConstraint.Attachment1 \t\t= renderCharacter.LowerTorso.RightHipRigAttachment\n\t\n\tlanternModel.Parent = renderCharacter\n\t\n\twarn(\"applying temporary equipment :angrycry:\")\nend"
  },
  {
    "path": "src/ReplicatedStorage/temporaryEquipment/lantern/init.meta.json",
    "content": "{\n  \"className\": \"Model\",\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/ServerScriptService/ChatServiceRunner/ChatChannel.lua",
    "content": "--\t// FileName: ChatChannel.lua\n--\t// Written by: Xsitsu\n--\t// Description: A representation of one channel that speakers can chat in.\n\nlocal forceNewFilterAPI = false\nlocal IN_GAME_CHAT_USE_NEW_FILTER_API\ndo\n\tlocal textServiceExists = (game:GetService(\"TextService\") ~= nil)\n\tlocal success, enabled = pcall(function() return UserSettings():IsUserFeatureEnabled(\"UserInGameChatUseNewFilterAPIV2\") end)\n\tlocal flagEnabled = (success and enabled)\n\tIN_GAME_CHAT_USE_NEW_FILTER_API = (forceNewFilterAPI or flagEnabled) and textServiceExists\nend\n\nlocal module = {}\n\nlocal modulesFolder = script.Parent\nlocal Chat = game:GetService(\"Chat\")\nlocal RunService = game:GetService(\"RunService\")\nlocal replicatedModules = Chat:WaitForChild(\"ClientChatModules\")\n\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal ChatConstants = require(replicatedModules:WaitForChild(\"ChatConstants\"))\nlocal Util = require(modulesFolder:WaitForChild(\"Util\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = { Get = function(key,default) return default end } end\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\n\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:SendSystemMessage(message, extraData)\n\tlocal messageObj = self:InternalCreateMessageObject(message, nil, true, extraData)\n\n\tlocal success, err = pcall(function() self.eMessagePosted:Fire(messageObj) end)\n\tif not success and err then\n\t\tprint(\"Error posting message: \" ..err)\n\tend\n\n\tself:InternalAddMessageToHistoryLog(messageObj)\n\n\tfor i, speaker in pairs(self.Speakers) do\n\t\tspeaker:InternalSendSystemMessage(messageObj, self.Name)\n\tend\n\n\treturn messageObj\nend\n\nfunction methods:SendSystemMessageToSpeaker(message, speakerName, extraData)\n\tlocal speaker = self.Speakers[speakerName]\n\tif (speaker) then\n\t\tlocal messageObj = self:InternalCreateMessageObject(message, nil, true, extraData)\n\t\tspeaker:InternalSendSystemMessage(messageObj, self.Name)\n\telse\n\t\twarn(string.format(\"Speaker '%s' is not in channel '%s' and cannot be sent a system message\", speakerName, self.Name))\n\tend\nend\n\nfunction methods:SendMessageObjToFilters(message, messageObj, fromSpeaker)\n\tlocal oldMessage = messageObj.Message\n\tmessageObj.Message = message\n\tself:InternalDoMessageFilter(fromSpeaker.Name, messageObj, self.Name)\n\tself.ChatService:InternalDoMessageFilter(fromSpeaker.Name, messageObj, self.Name)\n\tlocal newMessage = messageObj.Message\n\tmessageObj.Message = oldMessage\n\treturn newMessage\nend\n\nfunction methods:CanCommunicateByUserId(userId1, userId2)\n\tif RunService:IsStudio() then\n\t\treturn true\n\tend\n\t-- UserId is set as 0 for non player speakers.\n\tif userId1 == 0 or userId2 == 0 then\n\t\treturn true\n\tend\n\tlocal success, canCommunicate = pcall(function()\n\t\treturn Chat:CanUsersChatAsync(userId1, userId2)\n\tend)\n\treturn success and canCommunicate\nend\n\nfunction methods:CanCommunicate(speakerObj1, speakerObj2)\n\tlocal player1 = speakerObj1:GetPlayer()\n\tlocal player2 = speakerObj2:GetPlayer()\n\tif player1 and player2 then\n\t\treturn self:CanCommunicateByUserId(player1.UserId, player2.UserId)\n\tend\n\treturn true\nend\n\nfunction methods:SendMessageToSpeaker(message, speakerName, fromSpeakerName, extraData)\n\tlocal speakerTo = self.Speakers[speakerName]\n\tlocal speakerFrom = self.ChatService:GetSpeaker(fromSpeakerName)\n\tif speakerTo and speakerFrom then\n\t\tlocal isMuted = speakerTo:IsSpeakerMuted(fromSpeakerName)\n\t\tif isMuted then\n\t\t\treturn\n\t\tend\n\n\t\tif not self:CanCommunicate(speakerTo, speakerFrom) then\n\t\t\treturn\n\t\tend\n\n\t\t-- We need to claim the message is filtered even if it not in this case for compatibility with legacy client side code.\n\t\tlocal isFiltered = speakerName == fromSpeakerName\n\t\tlocal messageObj = self:InternalCreateMessageObject(message, fromSpeakerName, isFiltered, extraData)\n\t\tmessage = self:SendMessageObjToFilters(message, messageObj, fromSpeakerName)\n\t\tspeakerTo:InternalSendMessage(messageObj, self.Name)\n\n\t\t--// START FFLAG\n\t\tif (not IN_GAME_CHAT_USE_NEW_FILTER_API) then --// USES FFLAG\n\t\t--// OLD BEHAVIOR\n\t\tlocal filteredMessage = self.ChatService:InternalApplyRobloxFilter(messageObj.FromSpeaker, message, speakerName)\n\t\tif filteredMessage then\n\t\t\tmessageObj.Message = filteredMessage\n\t\t\tmessageObj.IsFiltered = true\n\t\t\tspeakerTo:InternalSendFilteredMessage(messageObj, self.Name)\n\t\tend\n\t\t--// OLD BEHAVIOR\n\t\telse\n\t\t--// NEW BEHAVIOR\n\t\t\tlocal textContext = self.Private and Enum.TextFilterContext.PrivateChat or Enum.TextFilterContext.PublicChat\n\t\t\tlocal filterSuccess, isFilterResult, filteredMessage = self.ChatService:InternalApplyRobloxFilterNewAPI(\n\t\t\t\tmessageObj.FromSpeaker,\n\t\t\t\tmessage,\n\t\t\t\ttextContext\n\t\t\t)\n\t\t\tif (filterSuccess) then\n\t\t\t\tmessageObj.FilterResult = filteredMessage\n\t\t\t\tmessageObj.IsFilterResult = isFilterResult\n\t\t\t\tmessageObj.IsFiltered = true\n\t\t\t\tspeakerTo:InternalSendFilteredMessageWithFilterResult(messageObj, self.Name)\n\t\t\tend\n\t\t--// NEW BEHAVIOR\n\t\tend\n\t\t--// END FFLAG\n\telse\n\t\twarn(string.format(\"Speaker '%s' is not in channel '%s' and cannot be sent a message\", speakerName, self.Name))\n\tend\nend\n\nfunction methods:KickSpeaker(speakerName, reason)\n\tlocal speaker = self.ChatService:GetSpeaker(speakerName)\n\tif (not speaker) then\n\t\terror(\"Speaker \\\"\" .. speakerName .. \"\\\" does not exist!\")\n\tend\n\n\tlocal messageToSpeaker = \"\"\n\tlocal messageToChannel = \"\"\n\n\tif (reason) then\n\t\tmessageToSpeaker = string.format(\"You were kicked from '%s' for the following reason(s): %s\", self.Name, reason)\n\t\tmessageToChannel = string.format(\"%s was kicked for the following reason(s): %s\", speakerName, reason)\n\telse\n\t\tmessageToSpeaker = string.format(\"You were kicked from '%s'\", self.Name)\n\t\tmessageToChannel = string.format(\"%s was kicked\", speakerName)\n\tend\n\n\tself:SendSystemMessageToSpeaker(messageToSpeaker, speakerName)\n\tspeaker:LeaveChannel(self.Name)\n\tself:SendSystemMessage(messageToChannel)\nend\n\nfunction methods:MuteSpeaker(speakerName, reason, length)\n\tlocal speaker = self.ChatService:GetSpeaker(speakerName)\n\tif (not speaker) then\n\t\terror(\"Speaker \\\"\" .. speakerName .. \"\\\" does not exist!\")\n\tend\n\n\tself.Mutes[speakerName:lower()] = (length == 0 or length == nil) and 0 or (os.time() + length)\n\n\tif (reason) then\n\t\tself:SendSystemMessage(string.format(\"%s was muted for the following reason(s): %s\", speakerName, reason))\n\tend\n\n\tlocal success, err = pcall(function() self.eSpeakerMuted:Fire(speakerName, reason, length) end)\n\tif not success and err then\n\t\tprint(\"Error mutting speaker: \" ..err)\n\tend\n\n\tlocal spkr = self.ChatService:GetSpeaker(speakerName)\n\tif (spkr) then\n\t\tlocal success, err = pcall(function() spkr.eMuted:Fire(self.Name, reason, length) end)\n\t\tif not success and err then\n\t\t\tprint(\"Error mutting speaker: \" ..err)\n\t\tend\n\tend\n\nend\n\nfunction methods:UnmuteSpeaker(speakerName)\n\tlocal speaker = self.ChatService:GetSpeaker(speakerName)\n\tif (not speaker) then\n\t\terror(\"Speaker \\\"\" .. speakerName .. \"\\\" does not exist!\")\n\tend\n\n\tself.Mutes[speakerName:lower()] = nil\n\n\tlocal success, err = pcall(function() self.eSpeakerUnmuted:Fire(speakerName) end)\n\tif not success and err then\n\t\tprint(\"Error unmuting speaker: \" ..err)\n\tend\n\n\tlocal spkr = self.ChatService:GetSpeaker(speakerName)\n\tif (spkr) then\n\t\tlocal success, err = pcall(function() spkr.eUnmuted:Fire(self.Name) end)\n\t\tif not success and err then\n\t\t\tprint(\"Error unmuting speaker: \" ..err)\n\t\tend\n\tend\nend\n\nfunction methods:IsSpeakerMuted(speakerName)\n\treturn (self.Mutes[speakerName:lower()] ~= nil)\nend\n\nfunction methods:GetSpeakerList()\n\tlocal list = {}\n\tfor i, speaker in pairs(self.Speakers) do\n\t\ttable.insert(list, speaker.Name)\n\tend\n\treturn list\nend\n\nfunction methods:RegisterFilterMessageFunction(funcId, func, priority)\n\tif self.FilterMessageFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"FilterMessageFunction '%s' already exists\", funcId))\n\tend\n\tself.FilterMessageFunctions:AddFunction(funcId, func, priority)\nend\n\nfunction methods:FilterMessageFunctionExists(funcId)\n\treturn self.FilterMessageFunctions:HasFunction(funcId)\nend\n\nfunction methods:UnregisterFilterMessageFunction(funcId)\n\tif not self.FilterMessageFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"FilterMessageFunction '%s' does not exists\", funcId))\n\tend\n\tself.FilterMessageFunctions:RemoveFunction(funcId)\nend\n\nfunction methods:RegisterProcessCommandsFunction(funcId, func, priority)\n\tif self.ProcessCommandsFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"ProcessCommandsFunction '%s' already exists\", funcId))\n\tend\n\tself.ProcessCommandsFunctions:AddFunction(funcId, func, priority)\nend\n\nfunction methods:ProcessCommandsFunctionExists(funcId)\n\treturn self.ProcessCommandsFunctions:HasFunction(funcId)\nend\n\nfunction methods:UnregisterProcessCommandsFunction(funcId)\n\tif not self.ProcessCommandsFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"ProcessCommandsFunction '%s' does not exist\", funcId))\n\tend\n\tself.ProcessCommandsFunctions:RemoveFunction(funcId)\nend\n\nlocal function ShallowCopy(table)\n\tlocal copy = {}\n\tfor i, v in pairs(table) do\n\t\tcopy[i] = v\n\tend\n\treturn copy\nend\n\nfunction methods:GetHistoryLog()\n\treturn ShallowCopy(self.ChatHistory)\nend\n\nfunction methods:GetHistoryLogForSpeaker(speaker)\n\tlocal userId = -1\n\tlocal player = speaker:GetPlayer()\n\tif player then\n\t\tuserId = player.UserId\n\tend\n\tlocal chatlog = {}\n\t--// START FFLAG\n\tif (not IN_GAME_CHAT_USE_NEW_FILTER_API) then --// USES FFLAG\n\t--// OLD BEHAVIOR\n\tfor i = 1, #self.ChatHistory do\n\t\tlocal logUserId = self.ChatHistory[i].SpeakerUserId\n\t\tif self:CanCommunicateByUserId(userId, logUserId) then\n\t\t\ttable.insert(chatlog, ShallowCopy(self.ChatHistory[i]))\n\t\tend\n\tend\n\t--// OLD BEHAVIOR\n\telse\n\t--// NEW BEHAVIOR\n\t\tfor i = 1, #self.ChatHistory do\n\t\t\tlocal logUserId = self.ChatHistory[i].SpeakerUserId\n\t\t\tif self:CanCommunicateByUserId(userId, logUserId) then\n\t\t\t\tlocal messageObj = ShallowCopy(self.ChatHistory[i])\n\n\t\t\t\t--// Since we're using the new filter API, we need to convert the stored filter result\n\t\t\t\t--// into an actual string message to send to players for their chat history.\n\t\t\t\t--// System messages aren't filtered the same way, so they just have a regular \n\t\t\t\t--// text value in the Message field.\n\t\t\t\tif (messageObj.MessageType == ChatConstants.MessageTypeDefault or messageObj.MessageType == ChatConstants.MessageTypeMeCommand) then\n\t\t\t\t\tlocal filterResult = messageObj.FilterResult\n\t\t\t\t\tif (messageObj.IsFilterResult) then\n\t\t\t\t\t\tif (player) then\n\t\t\t\t\t\t\tmessageObj.Message = filterResult:GetChatForUserAsync(player.UserId)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tmessageObj.Message = filterResult:GetNonChatStringForBroadcastAsync()\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\tmessageObj.Message = filterResult\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\ttable.insert(chatlog, messageObj)\n\t\t\tend\n\t\tend\n\t--// NEW BEHAVIOR\n\tend\n\t--// END FFLAG\n\treturn chatlog\nend\n\n--///////////////// Internal-Use Methods\n--//////////////////////////////////////\nfunction methods:InternalDestroy()\n\tfor i, speaker in pairs(self.Speakers) do\n\t\tspeaker:LeaveChannel(self.Name)\n\tend\n\n\tself.eDestroyed:Fire()\n\n\tself.eDestroyed:Destroy()\n\tself.eMessagePosted:Destroy()\n\tself.eSpeakerJoined:Destroy()\n\tself.eSpeakerLeft:Destroy()\n\tself.eSpeakerMuted:Destroy()\n\tself.eSpeakerUnmuted:Destroy()\nend\n\nfunction methods:InternalDoMessageFilter(speakerName, messageObj, channel)\n\tlocal filtersIterator = self.FilterMessageFunctions:GetIterator()\n\tfor funcId, func, priority in filtersIterator do\n\t\tlocal success, errorMessage = pcall(function()\n\t\t\tfunc(speakerName, messageObj, channel)\n\t\tend)\n\n\t\tif not success then\n\t\t\twarn(string.format(\"DoMessageFilter Function '%s' failed for reason: %s\", funcId, errorMessage))\n\t\tend\n\tend\nend\n\nfunction methods:InternalDoProcessCommands(speakerName, message, channel)\n\tlocal commandsIterator = self.ProcessCommandsFunctions:GetIterator()\n\tfor funcId, func, priority in commandsIterator do\n\t\tlocal success, returnValue = pcall(function()\n\t\t\tlocal ret = func(speakerName, message, channel)\n\t\t\tif type(ret) ~= \"boolean\" then\n\t\t\t\terror(\"Process command functions must return a bool\")\n\t\t\tend\n\t\t\treturn ret\n\t\tend)\n\n\t\tif not success then\n\t\t\twarn(string.format(\"DoProcessCommands Function '%s' failed for reason: %s\", funcId, returnValue))\n\t\telseif returnValue then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nfunction methods:InternalPostMessage(fromSpeaker, message, extraData)\n\tif (self:InternalDoProcessCommands(fromSpeaker.Name, message, self.Name)) then return false end\n\n\tif (self.Mutes[fromSpeaker.Name:lower()] ~= nil) then\n\t\tlocal t = self.Mutes[fromSpeaker.Name:lower()]\n\t\tif (t > 0 and os.time() > t) then\n\t\t\tself:UnmuteSpeaker(fromSpeaker.Name)\n\t\telse\n\t\t\tself:SendSystemMessageToSpeaker(ChatLocalization:Get(\"GameChat_ChatChannel_MutedInChannel\",\"You are muted and cannot talk in this channel\"), fromSpeaker.Name)\n\t\t\treturn false\n\t\tend\n\tend\n\n\tlocal messageObj = self:InternalCreateMessageObject(message, fromSpeaker.Name, false, extraData)\n\n\t-- allow server to process the unfiltered message string\n\tmessageObj.Message = message\n\tlocal processedMessage\n\tpcall(function()\n\t\tprocessedMessage = Chat:InvokeChatCallback(Enum.ChatCallbackType.OnServerReceivingMessage, messageObj)\n\tend)\n\tmessageObj.Message = nil\n\n\tif processedMessage then\n\n\t\t-- developer server code's choice to mute the message\n\t\tif processedMessage.ShouldDeliver == false then\n\t\t\treturn false\n\t\tend\n\t\tmessageObj = processedMessage\n\tend\n\n\tmessage = self:SendMessageObjToFilters(message, messageObj, fromSpeaker)\n\n\tlocal sentToList = {}\n\tfor i, speaker in pairs(self.Speakers) do\n\t\tlocal isMuted = speaker:IsSpeakerMuted(fromSpeaker.Name)\n\t\tif not isMuted and self:CanCommunicate(fromSpeaker, speaker) then\n\t\t\ttable.insert(sentToList, speaker.Name)\n\t\t\tif speaker.Name == fromSpeaker.Name then\n\t\t\t\t-- Send unfiltered message to speaker who sent the message.\n\t\t\t\tlocal cMessageObj = ShallowCopy(messageObj)\n\t\t\t\tcMessageObj.Message = message\n\t\t\t\tcMessageObj.IsFiltered = true\n\t\t\t\t-- We need to claim the message is filtered even if it not in this case for compatibility with legacy client side code.\n\t\t\t\tspeaker:InternalSendMessage(cMessageObj, self.Name)\n\t\t\telse\n\t\t\t\tspeaker:InternalSendMessage(messageObj, self.Name)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal success, err = pcall(function() self.eMessagePosted:Fire(messageObj) end)\n\tif not success and err then\n\t\tprint(\"Error posting message: \" ..err)\n\tend\n\n\t--// START FFLAG\n\tif (not IN_GAME_CHAT_USE_NEW_FILTER_API) then --// USES FFLAG\n\t--// OLD BEHAVIOR\n\tlocal filteredMessages = {}\n\tfor i, speakerName in pairs(sentToList) do\n\t\tlocal filteredMessage = self.ChatService:InternalApplyRobloxFilter(messageObj.FromSpeaker, message, speakerName)\n\t\tif filteredMessage then\n\t\t\tfilteredMessages[speakerName] = filteredMessage\n\t\telse\n\t\t\treturn false\n\t\tend\n\tend\n\n\tfor i, speakerName in pairs(sentToList) do\n\t\tlocal speaker = self.Speakers[speakerName]\n\t\tif (speaker) then\n\t\t\tlocal cMessageObj = ShallowCopy(messageObj)\n\t\t\tcMessageObj.Message = filteredMessages[speakerName]\n\t\t\tcMessageObj.IsFiltered = true\n\t\t\tspeaker:InternalSendFilteredMessage(cMessageObj, self.Name)\n\t\tend\n\tend\n\n\tlocal filteredMessage = self.ChatService:InternalApplyRobloxFilter(messageObj.FromSpeaker, message)\n\tif filteredMessage then\n\t\tmessageObj.Message = filteredMessage\n\telse\n\t\treturn false\n\tend\n\tmessageObj.IsFiltered = true\n\tself:InternalAddMessageToHistoryLog(messageObj)\n\t--// OLD BEHAVIOR\n\telse\n\t--// NEW BEHAVIOR\n\t\tlocal textFilterContext = self.Private and Enum.TextFilterContext.PrivateChat or Enum.TextFilterContext.PublicChat\n\t\tlocal filterSuccess, isFilterResult, filteredMessage = self.ChatService:InternalApplyRobloxFilterNewAPI(\n\t\t\tmessageObj.FromSpeaker,\n\t\t\tmessage,\n\t\t\ttextFilterContext\n\t\t)\n\t\tif (filterSuccess) then\n\t\t\tmessageObj.FilterResult = filteredMessage\n\t\t\tmessageObj.IsFilterResult = isFilterResult\n\t\telse\n\t\t\treturn false\n\t\tend\n\t\tmessageObj.IsFiltered = true\n\t\tself:InternalAddMessageToHistoryLog(messageObj)\n\n\t\tfor _, speakerName in pairs(sentToList) do\n\t\t\tlocal speaker = self.Speakers[speakerName]\n\t\t\tif (speaker) then\n\t\t\t\tspeaker:InternalSendFilteredMessageWithFilterResult(messageObj, self.Name)\n\t\t\tend\n\t\tend\n\t--// NEW BEHAVIOR\n\tend\n\t--// END FFLAG\n\n\t-- One more pass is needed to ensure that no speakers do not recieve the message.\n\t-- Otherwise a user could join while the message is being filtered who had not originally been sent the message.\n\tlocal speakersMissingMessage = {}\n\tfor _, speaker in pairs(self.Speakers) do\n\t\tlocal isMuted = speaker:IsSpeakerMuted(fromSpeaker.Name)\n\t\tif not isMuted and self:CanCommunicate(fromSpeaker, speaker) then\n\t\t\tlocal wasSentMessage = false\n\t\t\tfor _, sentSpeakerName in pairs(sentToList) do\n\t\t\t\tif speaker.Name == sentSpeakerName then\n\t\t\t\t\twasSentMessage = true\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\t\tif not wasSentMessage then\n\t\t\t\ttable.insert(speakersMissingMessage, speaker.Name)\n\t\t\tend\n\t\tend\n\tend\n\n\t--// START FFLAG\n\tif (not IN_GAME_CHAT_USE_NEW_FILTER_API) then --// USES FFLAG\n\t--// OLD BEHAVIOR\n\tfor _, speakerName in pairs(speakersMissingMessage) do\n\t\tlocal speaker = self.Speakers[speakerName]\n\t\tif speaker then\n\t\t\tlocal filteredMessage = self.ChatService:InternalApplyRobloxFilter(messageObj.FromSpeaker, message, speakerName)\n\t\t\tif filteredMessage == nil then\n\t\t\t\treturn false\n\t\t\tend\n\t\t\tlocal cMessageObj = ShallowCopy(messageObj)\n\t\t\tcMessageObj.Message = filteredMessage\n\t\t\tcMessageObj.IsFiltered = true\n\t\t\tspeaker:InternalSendFilteredMessage(cMessageObj, self.Name)\n\t\tend\n\tend\n\t--// OLD BEHAVIOR\n\telse\n\t--// NEW BEHAVIOR\n\t\tfor _, speakerName in pairs(speakersMissingMessage) do\n\t\t\tlocal speaker = self.Speakers[speakerName]\n\t\t\tif speaker then\n\t\t\t\tspeaker:InternalSendFilteredMessageWithFilterResult(messageObj, self.Name)\n\t\t\tend\n\t\tend\n\t--// NEW BEHAVIOR\n\tend\n\t--// END FFLAG\n\n\treturn messageObj\nend\n\nfunction methods:InternalAddSpeaker(speaker)\n\tif (self.Speakers[speaker.Name]) then\n\t\twarn(\"Speaker \\\"\" .. speaker.name .. \"\\\" is already in the channel!\")\n\t\treturn\n\tend\n\n\tself.Speakers[speaker.Name] = speaker\n\tlocal success, err = pcall(function() self.eSpeakerJoined:Fire(speaker.Name) end)\n\tif not success and err then\n\t\tprint(\"Error removing channel: \" ..err)\n\tend\nend\n\nfunction methods:InternalRemoveSpeaker(speaker)\n\tif (not self.Speakers[speaker.Name]) then\n\t\twarn(\"Speaker \\\"\" .. speaker.name .. \"\\\" is not in the channel!\")\n\t\treturn\n\tend\n\n\tself.Speakers[speaker.Name] = nil\n\tlocal success, err = pcall(function() self.eSpeakerLeft:Fire(speaker.Name) end)\n\tif not success and err then\n\t\tprint(\"Error removing speaker: \" ..err)\n\tend\nend\n\nfunction methods:InternalRemoveExcessMessagesFromLog()\n\tlocal remove = table.remove\n\twhile (#self.ChatHistory > self.MaxHistory) do\n\t\tremove(self.ChatHistory, 1)\n\tend\nend\n\nfunction methods:InternalAddMessageToHistoryLog(messageObj)\n\ttable.insert(self.ChatHistory, messageObj)\n\n\tself:InternalRemoveExcessMessagesFromLog()\nend\n\nfunction methods:GetMessageType(message, fromSpeaker)\n\tif fromSpeaker == nil then\n\t\treturn ChatConstants.MessageTypeSystem\n\tend\n\treturn ChatConstants.MessageTypeDefault\nend\n\nfunction methods:InternalCreateMessageObject(message, fromSpeaker, isFiltered, extraData)\n\tlocal messageType = self:GetMessageType(message, fromSpeaker)\n\n\tlocal speakerUserId = -1\n\tlocal speaker = nil\n\n\tif fromSpeaker then\n\t\tspeaker = self.Speakers[fromSpeaker]\n\t\tif speaker then\n\t\t\tlocal player = speaker:GetPlayer()\n\t\t\tif player then\n\t\t\t\tspeakerUserId = player.UserId\n\t\t\telse\n\t\t\t\tspeakerUserId = 0\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal messageObj =\n\t{\n\t\tID = self.ChatService:InternalGetUniqueMessageId(),\n\t\tFromSpeaker = fromSpeaker,\n\t\tSpeakerUserId = speakerUserId,\n\t\tOriginalChannel = self.Name,\n\t\tMessageLength = string.len(message),\n\t\tMessageType = messageType,\n\t\tIsFiltered = isFiltered,\n\t\tMessage = isFiltered and message or nil,\n\t\t--// These two get set by the new API. The comments are just here\n\t\t--// to remind readers that they will exist so it's not super\n\t\t--// confusing if they find them in the code but cannot find them\n\t\t--// here.\n\t\t--FilterResult = nil,\n\t\t--IsFilterResult = false,\n\t\tTime = os.time(),\n\t\tExtraData = {},\n\t}\n\n\tif speaker then\n\t\tfor k, v in pairs(speaker.ExtraData) do\n\t\t\tmessageObj.ExtraData[k] = v\n\t\tend\n\tend\n\n\tif (extraData) then\n\t\tfor k, v in pairs(extraData) do\n\t\t\tmessageObj.ExtraData[k] = v\n\t\tend\n\tend\n\n\treturn messageObj\nend\n\nfunction methods:SetChannelNameColor(color)\n\tself.ChannelNameColor = color\n\tfor i, speaker in pairs(self.Speakers) do\n\t\tspeaker:UpdateChannelNameColor(self.Name, color)\n\tend\nend\n\nfunction methods:GetWelcomeMessageForSpeaker(speaker)\n\tif self.GetWelcomeMessageFunction then\n\t\tlocal welcomeMessage = self.GetWelcomeMessageFunction(speaker)\n\t\tif welcomeMessage then\n\t\t\treturn welcomeMessage\n\t\tend\n\tend\n\treturn self.WelcomeMessage\nend\n\nfunction methods:RegisterGetWelcomeMessageFunction(func)\n\tif type(func) ~= \"function\" then\n\t\terror(\"RegisterGetWelcomeMessageFunction must be called with a function.\")\n\tend\n\tself.GetWelcomeMessageFunction = func\nend\n\nfunction methods:UnRegisterGetWelcomeMessageFunction()\n\tself.GetWelcomeMessageFunction = nil\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new(vChatService, name, welcomeMessage, channelNameColor)\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.ChatService = vChatService\n\n\tobj.Name = name\n\tobj.WelcomeMessage = welcomeMessage or \"\"\n\tobj.GetWelcomeMessageFunction = nil\n\tobj.ChannelNameColor = channelNameColor\n\n\tobj.Joinable = true\n\tobj.Leavable = true\n\tobj.AutoJoin = false\n\tobj.Private = false\n\n\tobj.Speakers = {}\n\tobj.Mutes = {}\n\n\tobj.MaxHistory = 200\n\tobj.HistoryIndex = 0\n\tobj.ChatHistory = {}\n\n\tobj.FilterMessageFunctions = Util:NewSortedFunctionContainer()\n\tobj.ProcessCommandsFunctions = Util:NewSortedFunctionContainer()\n\n\t-- Make sure to destroy added binadable events in the InternalDestroy method.\n\tobj.eDestroyed = Instance.new(\"BindableEvent\")\n\tobj.eMessagePosted = Instance.new(\"BindableEvent\")\n\tobj.eSpeakerJoined = Instance.new(\"BindableEvent\")\n\tobj.eSpeakerLeft = Instance.new(\"BindableEvent\")\n\tobj.eSpeakerMuted = Instance.new(\"BindableEvent\")\n\tobj.eSpeakerUnmuted = Instance.new(\"BindableEvent\")\n\n\tobj.MessagePosted = obj.eMessagePosted.Event\n\tobj.SpeakerJoined = obj.eSpeakerJoined.Event\n\tobj.SpeakerLeft = obj.eSpeakerLeft.Event\n\tobj.SpeakerMuted = obj.eSpeakerMuted.Event\n\tobj.SpeakerUnmuted = obj.eSpeakerUnmuted.Event\n\tobj.Destroyed = obj.eDestroyed.Event\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/ChatServiceRunner/ChatService.lua",
    "content": "--\t// FileName: ChatService.lua\n--\t// Written by: Xsitsu\n--\t// Description: Manages creating and destroying ChatChannels and Speakers.\n\nlocal MAX_FILTER_RETRIES = 3\nlocal FILTER_BACKOFF_INTERVALS = {50/1000, 100/1000, 200/1000}\nlocal MAX_FILTER_DURATION = 60\n\n--- Constants used to decide when to notify that the chat filter is having issues filtering messages.\nlocal FILTER_NOTIFCATION_THRESHOLD = 3 --Number of notifcation failures before an error message is output.\nlocal FILTER_NOTIFCATION_INTERVAL = 60 --Time between error messages.\nlocal FILTER_THRESHOLD_TIME = 60 --If there has not been an issue in this many seconds, the count of issues resets.\n\nlocal module = {}\n\nlocal RunService = game:GetService(\"RunService\")\nlocal Chat = game:GetService(\"Chat\")\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\n\nlocal modulesFolder = script.Parent\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatSettings = require(ReplicatedModules:WaitForChild(\"ChatSettings\"))\n\nlocal errorTextColor = ChatSettings.ErrorMessageTextColor or Color3.fromRGB(245, 50, 50)\nlocal errorExtraData = {ChatColor = errorTextColor}\n\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\n\nlocal ChatChannel = require(modulesFolder:WaitForChild(\"ChatChannel\"))\nlocal Speaker = require(modulesFolder:WaitForChild(\"Speaker\"))\nlocal Util = require(modulesFolder:WaitForChild(\"Util\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:AddChannel(channelName, autoJoin)\n\tif (self.ChatChannels[channelName:lower()]) then\n\t\terror(string.format(\"Channel %q alrady exists.\", channelName))\n\tend\n\n\tlocal function DefaultChannelCommands(fromSpeaker, message)\n\t\tif (message:lower() == \"/leave\") then\n\t\t\tlocal channel = self:GetChannel(channelName)\n\t\t\tlocal speaker = self:GetSpeaker(fromSpeaker)\n\t\t\tif (channel and speaker) then\n\t\t\t\tif (channel.Leavable) then\n\t\t\t\t\tspeaker:LeaveChannel(channelName)\n\t\t\t\t\tspeaker:SendSystemMessage(\n\t\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\t\"GameChat_ChatService_YouHaveLeftChannel\",\n\t\t\t\t\t\t\t\tstring.format(\"You have left channel '%s'\", channelName)\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\"{RBX_NAME}\",channelName),\n\t\t\t\t\t\t\"System\"\n\t\t\t\t\t)\n\t\t\t\telse\n\t\t\t\t\tspeaker:SendSystemMessage(ChatLocalization:Get(\"GameChat_ChatService_CannotLeaveChannel\",\"You cannot leave this channel.\"), channelName)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\treturn true\n\t\tend\n\t\treturn false\n\tend\n\n\n\tlocal channel = ChatChannel.new(self, channelName)\n\tself.ChatChannels[channelName:lower()] = channel\n\n\tchannel:RegisterProcessCommandsFunction(\"default_commands\", DefaultChannelCommands, ChatConstants.HighPriority)\n\n\tlocal success, err = pcall(function() self.eChannelAdded:Fire(channelName) end)\n\tif not success and err then\n\t\tprint(\"Error addding channel: \" ..err)\n\tend\n\n\tif autoJoin ~= nil then\n\t\tchannel.AutoJoin = autoJoin\n\t\tif autoJoin then\n\t\t\tfor _, speaker in pairs(self.Speakers) do\n\t\t\t\tspeaker:JoinChannel(channelName)\n\t\t\tend\n\t\tend\n\tend\n\n\treturn channel\nend\n\nfunction methods:RemoveChannel(channelName)\n\tif (self.ChatChannels[channelName:lower()]) then\n\t\tlocal n = self.ChatChannels[channelName:lower()].Name\n\n\t\tself.ChatChannels[channelName:lower()]:InternalDestroy()\n\t\tself.ChatChannels[channelName:lower()] = nil\n\n\t\tlocal success, err = pcall(function() self.eChannelRemoved:Fire(n) end)\n\t\tif not success and err then\n\t\t\tprint(\"Error removing channel: \" ..err)\n\t\tend\n\telse\n\t\twarn(string.format(\"Channel %q does not exist.\", channelName))\n\tend\nend\n\nfunction methods:GetChannel(channelName)\n\treturn self.ChatChannels[channelName:lower()]\nend\n\n\nfunction methods:AddSpeaker(speakerName)\n\tif (self.Speakers[speakerName:lower()]) then\n\t\terror(\"Speaker \\\"\" .. speakerName .. \"\\\" already exists!\")\n\tend\n\n\tlocal speaker = Speaker.new(self, speakerName)\n\tself.Speakers[speakerName:lower()] = speaker\n\n\tlocal success, err = pcall(function() self.eSpeakerAdded:Fire(speakerName) end)\n\tif not success and err then\n\t\tprint(\"Error adding speaker: \" ..err)\n\tend\n\n\treturn speaker\nend\n\nfunction methods:InternalUnmuteSpeaker(speakerName)\n\tfor channelName, channel in pairs(self.ChatChannels) do\n\t\tif channel:IsSpeakerMuted(speakerName) then\n\t\t\tchannel:UnmuteSpeaker(speakerName)\n\t\tend\n\tend\nend\n\nfunction methods:RemoveSpeaker(speakerName)\n\tif (self.Speakers[speakerName:lower()]) then\n\t\tlocal n = self.Speakers[speakerName:lower()].Name\n\t\tself:InternalUnmuteSpeaker(n)\n\n\t\tself.Speakers[speakerName:lower()]:InternalDestroy()\n\t\tself.Speakers[speakerName:lower()] = nil\n\n\t\tlocal success, err = pcall(function() self.eSpeakerRemoved:Fire(n) end)\n\t\tif not success and err then\n\t\t\tprint(\"Error removing speaker: \" ..err)\n\t\tend\n\n\telse\n\t\twarn(\"Speaker \\\"\" .. speakerName .. \"\\\" does not exist!\")\n\tend\nend\n\nfunction methods:GetSpeaker(speakerName)\n\treturn self.Speakers[speakerName:lower()]\nend\n\nfunction methods:GetChannelList()\n\tlocal list = {}\n\tfor i, channel in pairs(self.ChatChannels) do\n\t\tif (not channel.Private) then\n\t\t\ttable.insert(list, channel.Name)\n\t\tend\n\tend\n\treturn list\nend\n\nfunction methods:GetAutoJoinChannelList()\n\tlocal list = {}\n\tfor i, channel in pairs(self.ChatChannels) do\n\t\tif channel.AutoJoin then\n\t\t\ttable.insert(list, channel)\n\t\tend\n\tend\n\treturn list\nend\n\nfunction methods:GetSpeakerList()\n\tlocal list = {}\n\tfor i, speaker in pairs(self.Speakers) do\n\t\ttable.insert(list, speaker.Name)\n\tend\n\treturn list\nend\n\nfunction methods:SendGlobalSystemMessage(message)\n\tfor i, speaker in pairs(self.Speakers) do\n\t\tspeaker:SendSystemMessage(message, nil)\n\tend\nend\n\nfunction methods:RegisterFilterMessageFunction(funcId, func, priority)\n\tif self.FilterMessageFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"FilterMessageFunction '%s' already exists\", funcId))\n\tend\n\tself.FilterMessageFunctions:AddFunction(funcId, func, priority)\nend\n\nfunction methods:FilterMessageFunctionExists(funcId)\n\treturn self.FilterMessageFunctions:HasFunction(funcId)\nend\n\nfunction methods:UnregisterFilterMessageFunction(funcId)\n\tif not self.FilterMessageFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"FilterMessageFunction '%s' does not exists\", funcId))\n\tend\n\tself.FilterMessageFunctions:RemoveFunction(funcId)\nend\n\nfunction methods:RegisterProcessCommandsFunction(funcId, func, priority)\n\tif self.ProcessCommandsFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"ProcessCommandsFunction '%s' already exists\", funcId))\n\tend\n\tself.ProcessCommandsFunctions:AddFunction(funcId, func, priority)\nend\n\nfunction methods:ProcessCommandsFunctionExists(funcId)\n\treturn self.ProcessCommandsFunctions:HasFunction(funcId)\nend\n\nfunction methods:UnregisterProcessCommandsFunction(funcId)\n\tif not self.ProcessCommandsFunctions:HasFunction(funcId) then\n\t\terror(string.format(\"ProcessCommandsFunction '%s' does not exist\", funcId))\n\tend\n\tself.ProcessCommandsFunctions:RemoveFunction(funcId)\nend\n\nlocal LastFilterNoficationTime = 0\nlocal LastFilterIssueTime = 0\nlocal FilterIssueCount = 0\nfunction methods:InternalNotifyFilterIssue()\n\tif (tick() - LastFilterIssueTime) > FILTER_THRESHOLD_TIME then\n\t\tFilterIssueCount = 0\n\tend\n\tFilterIssueCount = FilterIssueCount + 1\n\tLastFilterIssueTime = tick()\n\tif FilterIssueCount >= FILTER_NOTIFCATION_THRESHOLD then\n\t\tif (tick() - LastFilterNoficationTime) > FILTER_NOTIFCATION_INTERVAL then\n\t\t\tLastFilterNoficationTime = tick()\n\t\t\tlocal systemChannel = self:GetChannel(\"System\")\n\t\t\tif systemChannel then\n\t\t\t\tsystemChannel:SendSystemMessage(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatService_ChatFilterIssues\",\n\t\t\t\t\t\t\"The chat filter is currently experiencing issues and messages may be slow to appear.\"\n\t\t\t\t\t),\n\t\t\t\t\terrorExtraData\n\t\t\t\t)\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal StudioMessageFilteredCache = {}\n\n--///////////////// Internal-Use Methods\n--//////////////////////////////////////\n--DO NOT REMOVE THIS. Chat must be filtered or your game will face\n--moderation.\nfunction methods:InternalApplyRobloxFilter(speakerName, message, toSpeakerName) --// USES FFLAG\n\tif (RunService:IsServer() and not RunService:IsStudio()) then\n\t\tlocal fromSpeaker = self:GetSpeaker(speakerName)\n\t\tlocal toSpeaker = toSpeakerName and self:GetSpeaker(toSpeakerName)\n\n\t\tif fromSpeaker == nil then\n\t\t\treturn nil\n\t\tend\n\n\t\tlocal fromPlayerObj = fromSpeaker:GetPlayer()\n\t\tlocal toPlayerObj = toSpeaker and toSpeaker:GetPlayer()\n\n\t\tif fromPlayerObj == nil then\n\t\t\treturn message\n\t\tend\n\n\t\tlocal filterStartTime = tick()\n\t\tlocal filterRetries = 0\n\t\twhile true do\n\t\t\tlocal success, message = pcall(function()\n\t\t\t\tif toPlayerObj then\n\t\t\t\t\treturn Chat:FilterStringAsync(message, fromPlayerObj, toPlayerObj)\n\t\t\t\telse\n\t\t\t\t\treturn Chat:FilterStringForBroadcast(message, fromPlayerObj)\n\t\t\t\tend\n\t\t\tend)\n\t\t\tif success then\n\t\t\t\treturn message\n\t\t\telse\n\t\t\t\twarn(\"Error filtering message:\", message)\n\t\t\tend\n\t\t\tfilterRetries = filterRetries + 1\n\t\t\tif filterRetries > MAX_FILTER_RETRIES or (tick() - filterStartTime) > MAX_FILTER_DURATION then\n\t\t\t\tself:InternalNotifyFilterIssue()\n\t\t\t\treturn nil\n\t\t\tend\n\t\t\tlocal backoffInterval = FILTER_BACKOFF_INTERVALS[math.min(#FILTER_BACKOFF_INTERVALS, filterRetries)]\n\t\t\t-- backoffWait = backoffInterval +/- (0 -> backoffInterval)\n\t\t\tlocal backoffWait = backoffInterval + ((math.random()*2 - 1) * backoffInterval)\n\t\t\twait(backoffWait)\n\t\tend\n\telse\n\t\t--// Simulate filtering latency.\n\t\t--// There is only latency the first time the message is filtered, all following calls will be instant.\n\t\tif not StudioMessageFilteredCache[message] then\n\t\t\tStudioMessageFilteredCache[message] = true\n\t\t\twait()\n\t\tend\n\t\treturn message\n\tend\n\n\treturn nil\nend\n\n--// Return values: bool filterSuccess, bool resultIsFilterObject, variant result\nfunction methods:InternalApplyRobloxFilterNewAPI(speakerName, message, textFilterContext) --// USES FFLAG\n\tlocal alwaysRunFilter = false\n\tlocal runFilter = RunService:IsServer() and not RunService:IsStudio()\n\tif (alwaysRunFilter or runFilter) then\n\t\tlocal fromSpeaker = self:GetSpeaker(speakerName)\n\t\tif fromSpeaker == nil then\n\t\t\treturn false, nil, nil\n\t\tend\n\n\t\tlocal fromPlayerObj = fromSpeaker:GetPlayer()\n\t\tif fromPlayerObj == nil then\n\t\t\treturn true, false, message\n\t\tend\n\n\t\tlocal success, filterResult = pcall(function()\n\t\t\tlocal ts = game:GetService(\"TextService\")\n\t\t\tlocal result = ts:FilterStringAsync(message, fromPlayerObj.UserId, textFilterContext)\n\t\t\treturn result\n\t\tend)\n\t\tif (success) then\n\t\t\treturn true, true, filterResult\n\t\telse\n\t\t\twarn(\"Error filtering message:\", message, filterResult)\n\t\t\tself:InternalNotifyFilterIssue()\n\t\t\treturn false, nil, nil\n\t\tend\n\tend\n\n\t--// Simulate filtering latency.\n\twait()\n\treturn true, false, message\nend\n\nfunction methods:InternalDoMessageFilter(speakerName, messageObj, channel)\n\tlocal filtersIterator = self.FilterMessageFunctions:GetIterator()\n\n\tfor funcId, func, priority in filtersIterator do\n\t\tlocal success, errorMessage = pcall(function()\n\t\t\tfunc(speakerName, messageObj, channel)\n\t\tend)\n\n\t\tif not success then\n\t\t\twarn(string.format(\"DoMessageFilter Function '%s' failed for reason: %s\", funcId, errorMessage))\n\t\tend\n\tend\nend\n\nfunction methods:InternalDoProcessCommands(speakerName, message, channel)\n\tlocal commandsIterator = self.ProcessCommandsFunctions:GetIterator()\n\n\tfor funcId, func, priority in commandsIterator do\n\t\tlocal success, returnValue = pcall(function()\n\t\t\tlocal ret = func(speakerName, message, channel)\n\t\t\tif type(ret) ~= \"boolean\" then\n\t\t\t\terror(\"Process command functions must return a bool\")\n\t\t\tend\n\t\t\treturn ret\n\t\tend)\n\n\t\tif not success then\n\t\t\twarn(string.format(\"DoProcessCommands Function '%s' failed for reason: %s\", funcId, returnValue))\n\t\telseif returnValue then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nfunction methods:InternalGetUniqueMessageId()\n\tlocal id = self.MessageIdCounter\n\tself.MessageIdCounter = id + 1\n\treturn id\nend\n\nfunction methods:InternalAddSpeakerWithPlayerObject(speakerName, playerObj, fireSpeakerAdded)\n\tif (self.Speakers[speakerName:lower()]) then\n\t\terror(\"Speaker \\\"\" .. speakerName .. \"\\\" already exists!\")\n\tend\n\n\tlocal speaker = Speaker.new(self, speakerName)\n\tspeaker:InternalAssignPlayerObject(playerObj)\n\tself.Speakers[speakerName:lower()] = speaker\n\n\tif fireSpeakerAdded then\n\t\tlocal success, err = pcall(function() self.eSpeakerAdded:Fire(speakerName) end)\n\t\tif not success and err then\n\t\t\tprint(\"Error adding speaker: \" ..err)\n\t\tend\n\tend\n\n\treturn speaker\nend\n\nfunction methods:InternalFireSpeakerAdded(speakerName)\n\tlocal success, err = pcall(function() self.eSpeakerAdded:Fire(speakerName) end)\n\tif not success and err then\n\t\tprint(\"Error firing speaker added: \" ..err)\n\tend\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.MessageIdCounter = 0\n\n\tobj.ChatChannels = {}\n\tobj.Speakers = {}\n\n\tobj.FilterMessageFunctions = Util:NewSortedFunctionContainer()\n\tobj.ProcessCommandsFunctions = Util:NewSortedFunctionContainer()\n\n\tobj.eChannelAdded = Instance.new(\"BindableEvent\")\n\tobj.eChannelRemoved = Instance.new(\"BindableEvent\")\n\tobj.eSpeakerAdded = Instance.new(\"BindableEvent\")\n\tobj.eSpeakerRemoved = Instance.new(\"BindableEvent\")\n\n\tobj.ChannelAdded = obj.eChannelAdded.Event\n\tobj.ChannelRemoved = obj.eChannelRemoved.Event\n\tobj.SpeakerAdded = obj.eSpeakerAdded.Event\n\tobj.SpeakerRemoved = obj.eSpeakerRemoved.Event\n\n\tobj.ChatServiceMajorVersion = 0\n\tobj.ChatServiceMinorVersion = 5\n\n\treturn obj\nend\n\nreturn module.new()\n"
  },
  {
    "path": "src/ServerScriptService/ChatServiceRunner/Speaker.lua",
    "content": "--\t// FileName: Speaker.lua\n--\t// Written by: Xsitsu\n--\t// Description: A representation of one entity that can chat in different ChatChannels.\n\nlocal module = {}\n\nlocal modulesFolder = script.Parent\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal function ShallowCopy(table)\n\tlocal copy = {}\n\tfor i, v in pairs(table) do\n\t\tcopy[i] = v\n\tend\n\treturn copy\nend\n\nlocal methods = {}\n\nlocal lazyEventNames = \n{\n    eDestroyed = true,\n\teSaidMessage = true,\n\teReceivedMessage = true,\n\teReceivedUnfilteredMessage = true,\n\teMessageDoneFiltering = true,\n\teReceivedSystemMessage = true,\n\teChannelJoined = true,\n\teChannelLeft = true,\n\teMuted = true,\n\teUnmuted = true,\n\teExtraDataUpdated = true,\n\teMainChannelSet = true,\n\teChannelNameColorUpdated = true,\n}\nlocal lazySignalNames =\n{\n\tDestroyed = \"eDestroyed\",\n\tSaidMessage = \"eSaidMessage\",\n\tReceivedMessage = \"eReceivedMessage\",\n\tReceivedUnfilteredMessage = \"eReceivedUnfilteredMessage\",\n    RecievedUnfilteredMessage = \"eReceivedUnfilteredMessage\", -- legacy mispelling\n\tMessageDoneFiltering = \"eMessageDoneFiltering\",\n\tReceivedSystemMessage = \"eReceivedSystemMessage\",\n\tChannelJoined = \"eChannelJoined\",\n\tChannelLeft = \"eChannelLeft\",\n\tMuted = \"eMuted\",\n\tUnmuted = \"eUnmuted\",\n\tExtraDataUpdated = \"eExtraDataUpdated\",\n\tMainChannelSet = \"eMainChannelSet\",\n\tChannelNameColorUpdated = \"eChannelNameColorUpdated\"\n}\n\nmethods.__index = function (self, k)\n\tlocal fromMethods = rawget(methods, k)\n\tif fromMethods then return fromMethods end\n\t\n    if lazyEventNames[k] and not rawget(self, k) then\n        rawset(self, k, Instance.new(\"BindableEvent\"))\n    end\n    local lazySignalEventName = lazySignalNames[k]\n    if lazySignalEventName and not rawget(self, k) then\n        if not rawget(self, lazySignalEventName) then\n            rawset(self, lazySignalEventName, Instance.new(\"BindableEvent\"))\n        end\n        rawset(self, k, rawget(self, lazySignalEventName).Event)\n    end\n    return rawget(self, k)\nend\n\nfunction methods:LazyFire(eventName, ...)\n\tlocal createdEvent = rawget(self, eventName)\n\tif createdEvent then\n\t\tcreatedEvent:Fire(...)\n\tend\nend\n\nfunction methods:SayMessage(message, channelName, extraData)\n\tif (self.ChatService:InternalDoProcessCommands(self.Name, message, channelName)) then return end\n\tif (not channelName) then return end\n\n\tlocal channel = self.Channels[channelName:lower()]\n\tif (not channel) then\n\t\terror(\"Speaker is not in channel \\\"\" .. channelName .. \"\\\"\")\n\tend\n\n\tlocal messageObj = channel:InternalPostMessage(self, message, extraData)\n\tif (messageObj) then\n\t\tlocal success, err = pcall(function() self:LazyFire(\"eSaidMessage\", messageObj, channelName) end)\n\t\tif not success and err then\n\t\t\tprint(\"Error saying message: \" ..err)\n\t\tend\n\tend\n\n\treturn messageObj\nend\n\nfunction methods:JoinChannel(channelName)\n\tif (self.Channels[channelName:lower()]) then\n\t\twarn(\"Speaker is already in channel \\\"\" .. channelName .. \"\\\"\")\n\t\treturn\n\tend\n\n\tlocal channel = self.ChatService:GetChannel(channelName)\n\tif (not channel) then\n\t\terror(\"Channel \\\"\" .. channelName .. \"\\\" does not exist!\")\n\tend\n\n\tself.Channels[channelName:lower()] = channel\n\tchannel:InternalAddSpeaker(self)\n\tlocal success, err = pcall(function()\n\t\tself.eChannelJoined:Fire(channel.Name, channel:GetWelcomeMessageForSpeaker(self))\n\tend)\n\tif not success and err then\n\t\tprint(\"Error joining channel: \" ..err)\n\tend\nend\n\nfunction methods:LeaveChannel(channelName)\n\tif (not self.Channels[channelName:lower()]) then\n\t\twarn(\"Speaker is not in channel \\\"\" .. channelName .. \"\\\"\")\n\t\treturn\n\tend\n\n\tlocal channel = self.Channels[channelName:lower()]\n\n\tself.Channels[channelName:lower()] = nil\n\tchannel:InternalRemoveSpeaker(self)\n\tlocal success, err = pcall(function()\n\t\tself:LazyFire(\"eChannelLeft\", channel.Name)\n\t\tself.EventFolder.OnChannelLeft:FireClient(self.PlayerObj, channel.Name)\n\tend)\n\tif not success and err then\n\t\tprint(\"Error leaving channel: \" ..err)\n\tend\nend\n\nfunction methods:IsInChannel(channelName)\n\treturn (self.Channels[channelName:lower()] ~= nil)\nend\n\nfunction methods:GetChannelList()\n\tlocal list = {}\n\tfor i, channel in pairs(self.Channels) do\n\t\ttable.insert(list, channel.Name)\n\tend\n\treturn list\nend\n\nfunction methods:SendMessage(message, channelName, fromSpeaker, extraData)\n\tlocal channel = self.Channels[channelName:lower()]\n\tif (channel) then\n\t\tchannel:SendMessageToSpeaker(message, self.Name, fromSpeaker, extraData)\n\n\telse\n\t\twarn(string.format(\"Speaker '%s' is not in channel '%s' and cannot receive a message in it.\", self.Name, channelName))\n\n\tend\nend\n\nfunction methods:SendSystemMessage(message, channelName, extraData)\n\tlocal channel = self.Channels[channelName:lower()]\n\tif (channel) then\n\t\tchannel:SendSystemMessageToSpeaker(message, self.Name, extraData)\n\n\telse\n\t\twarn(string.format(\"Speaker '%s' is not in channel '%s' and cannot receive a system message in it.\", self.Name, channelName))\n\n\tend\nend\n\nfunction methods:GetPlayer()\n\treturn self.PlayerObj\nend\n\nfunction methods:SetExtraData(key, value)\n\tself.ExtraData[key] = value\n\tself:LazyFire(\"eExtraDataUpdated\", key, value)\nend\n\nfunction methods:GetExtraData(key)\n\treturn self.ExtraData[key]\nend\n\nfunction methods:SetMainChannel(channelName)\n\tlocal success, err = pcall(function()\n\t\tself:LazyFire(\"eMainChannelSet\", channelName)\n\t\tself.EventFolder.OnMainChannelSet:FireClient(self.PlayerObj, channelName)\n\tend)\n\tif not success and err then\n\t\tprint(\"Error setting main channel: \" ..err)\n\tend\nend\n\n--- Used to mute a speaker so that this speaker does not see their messages.\nfunction methods:AddMutedSpeaker(speakerName)\n\tself.MutedSpeakers[speakerName:lower()] = true\nend\n\nfunction methods:RemoveMutedSpeaker(speakerName)\n\tself.MutedSpeakers[speakerName:lower()] = false\nend\n\nfunction methods:IsSpeakerMuted(speakerName)\n\treturn self.MutedSpeakers[speakerName:lower()]\nend\n\n--///////////////// Internal-Use Methods\n--//////////////////////////////////////\nfunction methods:InternalDestroy()\n\tfor i, channel in pairs(self.Channels) do\n\t\tchannel:InternalRemoveSpeaker(self)\n\tend\n\n\tself.eDestroyed:Fire()\n\n\tself.EventFolder = nil\n\tself.eDestroyed:Destroy()\n\tself.eSaidMessage:Destroy()\n\tself.eReceivedMessage:Destroy()\n\tself.eReceivedUnfilteredMessage:Destroy()\n\tself.eMessageDoneFiltering:Destroy()\n\tself.eReceivedSystemMessage:Destroy()\n\tself.eChannelJoined:Destroy()\n\tself.eChannelLeft:Destroy()\n\tself.eMuted:Destroy()\n\tself.eUnmuted:Destroy()\n\tself.eExtraDataUpdated:Destroy()\n\tself.eMainChannelSet:Destroy()\n\tself.eChannelNameColorUpdated:Destroy()\nend\n\nfunction methods:InternalAssignPlayerObject(playerObj)\n\tself.PlayerObj = playerObj\nend\n\nfunction methods:InternalAssignEventFolder(eventFolder)\n\tself.EventFolder = eventFolder\nend\n\nfunction methods:InternalSendMessage(messageObj, channelName)\n\tlocal success, err = pcall(function()\n\t\tself:LazyFire(\"eReceivedUnfilteredMessage\", messageObj, channelName)\n\t\tself.EventFolder.OnNewMessage:FireClient(self.PlayerObj, messageObj, channelName)\n\tend)\n\tif not success and err then\n\t\tprint(\"Error sending internal message: \" ..err)\n\tend\nend\n\nfunction methods:InternalSendFilteredMessage(messageObj, channelName)\n\tlocal success, err = pcall(function()\n\t\tself:LazyFire(\"eReceivedMessage\", messageObj, channelName)\n\t\tself:LazyFire(\"eMessageDoneFiltering\", messageObj, channelName)\n\t\tself.EventFolder.OnMessageDoneFiltering:FireClient(self.PlayerObj, messageObj, channelName)\n\tend)\n\tif not success and err then\n\t\tprint(\"Error sending internal filtered message: \" ..err)\n\tend\nend\n\n--// This method is to be used with the new filter API. This method takes the \n--// TextFilterResult objects and converts them into the appropriate string\n--// messages for each player.\nfunction methods:InternalSendFilteredMessageWithFilterResult(inMessageObj, channelName)\n\tlocal messageObj = ShallowCopy(inMessageObj)\n\n\tlocal oldFilterResult = messageObj.FilterResult\n\tlocal player = self:GetPlayer()\n\n\tlocal msg = \"\"\n\tpcall(function()\n\t\tif (messageObj.IsFilterResult) then\n\t\t\tif (player) then\n\t\t\t\tmsg = oldFilterResult:GetChatForUserAsync(player.UserId)\n\t\t\telse\n\t\t\t\tmsg = oldFilterResult:GetNonChatStringForBroadcastAsync()\n\t\t\tend\n\t\telse\n\t\t\tmsg = oldFilterResult\n\t\tend\n\tend)\n\n\t--// Messages of 0 length are the result of two users not being allowed\n\t--// to chat, or GetChatForUserAsync() failing. In both of these situations,\n\t--// messages with length of 0 should not be sent.\n\tif (#msg > 0) then\n\t\tmessageObj.Message = msg\n\t\tmessageObj.FilterResult = nil\n\t\tself:InternalSendFilteredMessage(messageObj, channelName)\n\tend\nend\n\nfunction methods:InternalSendSystemMessage(messageObj, channelName)\n\tlocal success, err = pcall(function()\n\t\tself:LazyFire(\"eReceivedSystemMessage\", messageObj, channelName)\n\t\tself.EventFolder.OnNewSystemMessage:FireClient(self.PlayerObj, messageObj, channelName)\n\tend)\n\tif not success and err then\n\t\tprint(\"Error sending internal system message: \" ..err)\n\tend\nend\n\nfunction methods:UpdateChannelNameColor(channelName, channelNameColor)\n\tself:LazyFire(\"eChannelNameColorUpdated\", channelName, channelNameColor)\n\tself.EventFolder.ChannelNameColorUpdated:FireClient(self.PlayerObj, channelName, channelNameColor)\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new(vChatService, name)\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.ChatService = vChatService\n\n\tobj.PlayerObj = nil\n\n\tobj.Name = name\n\tobj.ExtraData = {}\n\n\tobj.Channels = {}\n\tobj.MutedSpeakers = {}\n\tobj.EventFolder = nil\n\t\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/ChatServiceRunner/Util.lua",
    "content": "--\t// FileName: Util.lua\n--\t// Written by: TheGamer101\n--\t// Description: Utility code used by the server side chat implementation.\n\nlocal Chat = game:GetService(\"Chat\")\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatConstants = require(ReplicatedModules:WaitForChild(\"ChatConstants\"))\n\nlocal DEFAULT_PRIORITY = ChatConstants.StandardPriority\nif DEFAULT_PRIORITY == nil then\n\tDEFAULT_PRIORITY = 10\nend\n\nlocal Util = {}\nUtil.__index = Util\n\nlocal SortedFunctionContainer = {}; do\n\t-- This sorted function container is used to handle the logic around storing filter functions and\n\t-- command processors by priority.\n\n\tlocal methods = {}\n\tmethods.__index = methods\n\n\tfunction methods:RebuildProcessCommandsPriorities()\n\t\tself.RegisteredPriorites = {}\n\t\tfor priority, functions in pairs(self.FunctionMap) do\n\t\t\tlocal functionsEmpty = true\n\t\t\tfor funcId, funciton in pairs(functions) do\n\t\t\t\tfunctionsEmpty = false\n\t\t\t\tbreak\n\t\t\tend\n\t\t\tif not functionsEmpty then\n\t\t\t\ttable.insert(self.RegisteredPriorites, priority)\n\t\t\tend\n\t\tend\n\t\ttable.sort(self.RegisteredPriorites, function(a, b)\n\t\t\treturn a > b\n\t\tend)\n\tend\n\n\tfunction methods:HasFunction(funcId)\n\t\tif self.RegisteredFunctions[funcId] == nil then\n\t\t\treturn false\n\t\tend\n\t\treturn true\n\tend\n\n\tfunction methods:RemoveFunction(funcId)\n\t\tlocal functionPriority = self.RegisteredFunctions[funcId]\n\t\tself.RegisteredFunctions[funcId] = nil\n\t\tself.FunctionMap[functionPriority][funcId] = nil\n\t\tself:RebuildProcessCommandsPriorities()\n\tend\n\n\tfunction methods:AddFunction(funcId, func, priority)\n\t\tif priority == nil then\n\t\t\tpriority = DEFAULT_PRIORITY\n\t\tend\n\n\t\tif self.RegisteredFunctions[funcId] then\n\t\t\terror(funcId .. \" is already in use!\")\n\t\tend\n\n\t\tself.RegisteredFunctions[funcId] = priority\n\n\t\tif self.FunctionMap[priority] == nil then\n\t\t\tself.FunctionMap[priority] = {}\n\t\tend\n\n\t\tself.FunctionMap[priority][funcId] = func\n\t\tself:RebuildProcessCommandsPriorities()\n\tend\n\n\tfunction methods:GetIterator()\n\t\tlocal priorityIndex = 1\n\t\tlocal funcId = nil\n\t\tlocal func = nil\n\n\t\treturn function()\n\t\t\twhile true do\n\t\t\t\tif priorityIndex > #self.RegisteredPriorites then\n\t\t\t\t\treturn\n\t\t\t\tend\n\t\t\t\tlocal priority = self.RegisteredPriorites[priorityIndex]\n\t\t\t\tfuncId, func = next(self.FunctionMap[priority], funcId)\n\t\t\t\tif funcId == nil then\n\t\t\t\t\tpriorityIndex = priorityIndex + 1\n\t\t\t\telse\n\t\t\t\t\treturn funcId, func, priority\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction SortedFunctionContainer.new()\n\t\tlocal obj = setmetatable({}, methods)\n\n\t\tobj.RegisteredFunctions = {}\n\t\tobj.RegisteredPriorites = {}\n\t\tobj.FunctionMap = {}\n\n\t\treturn obj\n\tend\nend\n\nfunction Util:NewSortedFunctionContainer()\n\treturn SortedFunctionContainer.new()\nend\n\nreturn Util\n"
  },
  {
    "path": "src/ServerScriptService/ChatServiceRunner/init.server.lua",
    "content": "--\t// FileName: ChatServiceRunner.lua\n--\t// Written by: Xsitsu\n--\t// Description: Main script to initialize ChatService and run ChatModules.\n\nlocal EventFolderName = \"DefaultChatSystemChatEvents\"\nlocal EventFolderParent = game:GetService(\"ReplicatedStorage\")\nlocal modulesFolder = script\n\nlocal PlayersService = game:GetService(\"Players\")\nlocal RunService = game:GetService(\"RunService\")\n\nlocal Chat = game:GetService(\"Chat\")\n\nlocal ChatService = require(modulesFolder:WaitForChild(\"ChatService\"))\n\nlocal ReplicatedModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatSettings = require(ReplicatedModules:WaitForChild(\"ChatSettings\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(Chat.ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = { Get = function(key,default) return default end } end\n\nlocal useEvents = {}\n\nlocal EventFolder = EventFolderParent:FindFirstChild(EventFolderName)\nif EventFolder then\n\tEventFolder:Destroy()\n\tEventFolder = nil\nend\nif (not EventFolder) then\n\tEventFolder = Instance.new(\"Folder\")\n\tEventFolder.Name = EventFolderName\n\tEventFolder.Archivable = false\n\tEventFolder.Parent = EventFolderParent\nend\n\n--// No-opt connect Server>Client RemoteEvents to ensure they cannot be called\n--// to fill the remote event queue.\nlocal function emptyFunction()\n\t--intentially empty\nend\n\nlocal function GetObjectWithNameAndType(parentObject, objectName, objectType)\n\tfor _, child in pairs(parentObject:GetChildren()) do\n\t\tif (child:IsA(objectType) and child.Name == objectName) then\n\t\t\treturn child\n\t\tend\n\tend\n\n\treturn nil\nend\n\nlocal function CreateIfDoesntExist(parentObject, objectName, objectType)\n\tlocal obj = GetObjectWithNameAndType(parentObject, objectName, objectType)\n\tif (not obj) then\n\t\tobj = Instance.new(objectType)\n\t\tobj.Name = objectName\n\t\tobj.Parent = parentObject\n\tend\n\tuseEvents[objectName] = obj\n\n\treturn obj\nend\n\n--// All remote events will have a no-opt OnServerEvent connecdted on construction\nlocal function CreateEventIfItDoesntExist(parentObject, objectName)\n\tlocal obj = CreateIfDoesntExist(parentObject, objectName, \"RemoteEvent\")\n\tobj.OnServerEvent:Connect(emptyFunction)\n\treturn obj\nend\n\nCreateEventIfItDoesntExist(EventFolder, \"OnNewMessage\")\nCreateEventIfItDoesntExist(EventFolder, \"OnMessageDoneFiltering\")\nCreateEventIfItDoesntExist(EventFolder, \"OnNewSystemMessage\")\nCreateEventIfItDoesntExist(EventFolder, \"OnChannelJoined\")\nCreateEventIfItDoesntExist(EventFolder, \"OnChannelLeft\")\nCreateEventIfItDoesntExist(EventFolder, \"OnMuted\")\nCreateEventIfItDoesntExist(EventFolder, \"OnUnmuted\")\nCreateEventIfItDoesntExist(EventFolder, \"OnMainChannelSet\")\nCreateEventIfItDoesntExist(EventFolder, \"ChannelNameColorUpdated\")\n\nCreateEventIfItDoesntExist(EventFolder, \"SayMessageRequest\")\nCreateEventIfItDoesntExist(EventFolder, \"SetBlockedUserIdsRequest\")\nCreateIfDoesntExist(EventFolder, \"GetInitDataRequest\", \"RemoteFunction\")\nCreateIfDoesntExist(EventFolder, \"MutePlayerRequest\", \"RemoteFunction\")\nCreateIfDoesntExist(EventFolder, \"UnMutePlayerRequest\", \"RemoteFunction\")\n\nEventFolder = useEvents\n\nlocal function CreatePlayerSpeakerObject(playerObj)\n\t--// If a developer already created a speaker object with the\n\t--// name of a player and then a player joins and tries to\n\t--// take that name, we first need to remove the old speaker object\n\tlocal speaker = ChatService:GetSpeaker(playerObj.Name)\n\tif (speaker) then\n\t\tChatService:RemoveSpeaker(playerObj.Name)\n\tend\n\n\tspeaker = ChatService:InternalAddSpeakerWithPlayerObject(playerObj.Name, playerObj, false)\n\n\tfor _, channel in pairs(ChatService:GetAutoJoinChannelList()) do\n\t\tspeaker:JoinChannel(channel.Name)\n\tend\n\n\tspeaker:InternalAssignEventFolder(EventFolder)\n\n\tspeaker.ChannelJoined:connect(function(channel, welcomeMessage)\n\t\tlocal log = nil\n\t\tlocal channelNameColor = nil\n\n\t\tlocal channelObject = ChatService:GetChannel(channel)\n\t\tif (channelObject) then\n\t\t\tlog = channelObject:GetHistoryLogForSpeaker(speaker)\n\t\t\tchannelNameColor = channelObject.ChannelNameColor\n\t\tend\n\t\tEventFolder.OnChannelJoined:FireClient(playerObj, channel, welcomeMessage, log, channelNameColor)\n\tend)\n\n\tspeaker.Muted:connect(function(channel, reason, length)\n\t\tEventFolder.OnMuted:FireClient(playerObj, channel, reason, length)\n\tend)\n\n\tspeaker.Unmuted:connect(function(channel)\n\t\tEventFolder.OnUnmuted:FireClient(playerObj, channel)\n\tend)\n\n\tChatService:InternalFireSpeakerAdded(speaker.Name)\nend\n\nEventFolder.SayMessageRequest.OnServerEvent:connect(function(playerObj, message, channel)\n\tif type(message) ~= \"string\" then\n\t\treturn\n\tend\n\tif type(channel) ~= \"string\" then\n\t\treturn\n\tend\n\n\tlocal speaker = ChatService:GetSpeaker(playerObj.Name)\n\tif (speaker) then\n\t\treturn speaker:SayMessage(message, channel)\n\tend\n\n\treturn nil\nend)\n\nEventFolder.MutePlayerRequest.OnServerInvoke = function(playerObj, muteSpeakerName)\n\tif type(muteSpeakerName) ~= \"string\" then\n\t\treturn\n\tend\n\n\tlocal speaker = ChatService:GetSpeaker(playerObj.Name)\n\tif speaker then\n\t\tlocal muteSpeaker = ChatService:GetSpeaker(muteSpeakerName)\n\t\tif muteSpeaker then\n\t\t\tspeaker:AddMutedSpeaker(muteSpeaker.Name)\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\nEventFolder.UnMutePlayerRequest.OnServerInvoke = function(playerObj, unmuteSpeakerName)\n\tif type(unmuteSpeakerName) ~= \"string\" then\n\t\treturn\n\tend\n\n\tlocal speaker = ChatService:GetSpeaker(playerObj.Name)\n\tif speaker then\n\t\tlocal unmuteSpeaker = ChatService:GetSpeaker(unmuteSpeakerName)\n\t\tif unmuteSpeaker then\n\t\t\tspeaker:RemoveMutedSpeaker(unmuteSpeaker.Name)\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\n-- Map storing Player -> Blocked user Ids.\nlocal BlockedUserIdsMap = {}\n\nPlayersService.PlayerAdded:connect(function(newPlayer)\n\tfor player, blockedUsers in pairs(BlockedUserIdsMap) do\n\t\tlocal speaker = ChatService:GetSpeaker(player.Name)\n\t\tif speaker then\n\t\t\tfor i = 1, #blockedUsers do\n\t\t\t\tlocal blockedUserId = blockedUsers[i]\n\t\t\t\tif blockedUserId == newPlayer.UserId then\n\t\t\t\t\tspeaker:AddMutedSpeaker(newPlayer.Name)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend)\n\nPlayersService.PlayerRemoving:connect(function(removingPlayer)\n\tBlockedUserIdsMap[removingPlayer] = nil\nend)\n\nEventFolder.SetBlockedUserIdsRequest.OnServerEvent:connect(function(player, blockedUserIdsList)\n\tif type(blockedUserIdsList) ~= \"table\" then\n\t\treturn\n\tend\n\n\tBlockedUserIdsMap[player] = blockedUserIdsList\n\tlocal speaker = ChatService:GetSpeaker(player.Name)\n\tif speaker then\n\t\tfor i = 1, #blockedUserIdsList do\n\t\t\tif type(blockedUserIdsList[i]) == \"number\" then\n\t\t\t\tlocal blockedPlayer = PlayersService:GetPlayerByUserId(blockedUserIdsList[i])\n\t\t\t\tif blockedPlayer then\n\t\t\t\t\tspeaker:AddMutedSpeaker(blockedPlayer.Name)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend)\n\nEventFolder.GetInitDataRequest.OnServerInvoke = (function(playerObj)\n\tlocal speaker = ChatService:GetSpeaker(playerObj.Name)\n\tif not (speaker and speaker:GetPlayer()) then\n\t\tCreatePlayerSpeakerObject(playerObj)\n\t\tspeaker = ChatService:GetSpeaker(playerObj.Name)\n\tend\n\n\tlocal data = {}\n\tdata.Channels = {}\n\tdata.SpeakerExtraData = {}\n\n\tfor _, channelName in pairs(speaker:GetChannelList()) do\n\t\tlocal channelObj = ChatService:GetChannel(channelName)\n\t\tif (channelObj) then\n\t\t\tlocal channelData =\n\t\t\t{\n\t\t\t\tchannelName,\n\t\t\t\tchannelObj:GetWelcomeMessageForSpeaker(speaker),\n\t\t\t\tchannelObj:GetHistoryLogForSpeaker(speaker),\n\t\t\t\tchannelObj.ChannelNameColor,\n\t\t\t}\n\n\t\t\ttable.insert(data.Channels, channelData)\n\t\tend\n\tend\n\n\tfor _, oSpeakerName in pairs(ChatService:GetSpeakerList()) do\n\t\tlocal oSpeaker = ChatService:GetSpeaker(oSpeakerName)\n\t\tdata.SpeakerExtraData[oSpeakerName] = oSpeaker.ExtraData\n\tend\n\n\treturn data\nend)\n\nlocal function DoJoinCommand(speakerName, channelName, fromChannelName)\n\tlocal speaker = ChatService:GetSpeaker(speakerName)\n\tlocal channel = ChatService:GetChannel(channelName)\n\n\tif (speaker) then\n\t\tif (channel) then\n\t\t\tif (channel.Joinable) then\n\t\t\t\tif (not speaker:IsInChannel(channel.Name)) then\n\t\t\t\t\tspeaker:JoinChannel(channel.Name)\n\t\t\t\telse\n\t\t\t\t\tspeaker:SetMainChannel(channel.Name)\n\t\t\t\t\tspeaker:SendSystemMessage(\n\t\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\t\"GameChat_SwitchChannel_NowInChannel\",\n\t\t\t\t\t\t\t\tstring.format(\"You are now chatting in channel: '%s'\", channel.Name)\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\"{RBX_NAME}\",channel.Name),\n\t\t\t\t\t\tchannel.Name\n\t\t\t\t\t)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tspeaker:SendSystemMessage(\n\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\"GameChat_ChatServiceRunner_YouCannotJoinChannel\",\n\t\t\t\t\t\t\t(\"You cannot join channel '\" .. channelName .. \"'.\")\n\t\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\",channelName),\n\t\t\t\t\tfromChannelName\n\t\t\t\t)\n\t\t\tend\n\t\telse\n\t\t\tspeaker:SendSystemMessage(\n\t\t\t\tstring.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatServiceRunner_ChannelDoesNotExist\",\n\t\t\t\t\t\t(\"Channel '\" .. channelName .. \"' does not exist.\")\n\t\t\t\t\t),\n\t\t\t\t\"{RBX_NAME}\",channelName),\n\t\t\t\tfromChannelName\n\t\t\t)\n\t\tend\n\tend\nend\n\nlocal function DoLeaveCommand(speakerName, channelName, fromChannelName)\n\tlocal speaker = ChatService:GetSpeaker(speakerName)\n\tlocal channel = ChatService:GetChannel(channelName)\n\n\tif (speaker) then\n\t\tif (speaker:IsInChannel(channelName)) then\n\t\t\tif (channel.Leavable) then\n\t\t\t\tspeaker:LeaveChannel(channel.Name)\n\t\t\t\tspeaker:SendSystemMessage(\n\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\"GameChat_ChatService_YouHaveLeftChannel\",\n\t\t\t\t\t\t\tstring.format(\"You have left channel '%s'\", channelName)\n\t\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\",channel.Name),\n\t\t\t\t\t\"System\"\n\t\t\t\t)\n\t\t\telse\n\t\t\t\tspeaker:SendSystemMessage(\n\t\t\t\t\tstring.gsub(\n\t\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\t\"GameChat_ChatServiceRunner_YouCannotLeaveChannel\",\n\t\t\t\t\t\t\t(\"You cannot leave channel '\" .. channelName .. \"'.\")\n\t\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\",channelName),\n\t\t\t\t\tfromChannelName\n\t\t\t\t)\n\t\t\tend\n\t\telse\n\t\t\tspeaker:SendSystemMessage(\n\t\t\t\tstring.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatServiceRunner_YouAreNotInChannel\",\n\t\t\t\t\t\t(\"You are not in channel '\" .. channelName .. \"'.\")\n\t\t\t\t\t),\n\t\t\t\t\"{RBX_NAME}\",channelName),\n\t\t\t\tfromChannelName\n\t\t\t)\n\t\tend\n\tend\nend\n\nChatService:RegisterProcessCommandsFunction(\"default_commands\", function(fromSpeaker, message, channel)\n\tif (string.sub(message, 1, 6):lower() == \"/join \") then\n\t\tDoJoinCommand(fromSpeaker, string.sub(message, 7), channel)\n\t\treturn true\n\telseif (string.sub(message, 1, 3):lower() == \"/j \") then\n\t\tDoJoinCommand(fromSpeaker, string.sub(message, 4), channel)\n\t\treturn true\n\n\telseif (string.sub(message, 1, 7):lower() == \"/leave \") then\n\t\tDoLeaveCommand(fromSpeaker, string.sub(message, 8), channel)\n\t\treturn true\n\telseif (string.sub(message, 1, 3):lower() == \"/l \") then\n\t\tDoLeaveCommand(fromSpeaker, string.sub(message, 4), channel)\n\t\treturn true\n\n\telseif (string.sub(message, 1, 3) == \"/e \" or string.sub(message, 1, 7) == \"/emote \") then\n\t\t-- Just don't show these in the chatlog. The animation script listens on these.\n\t\treturn true\n\n\tend\n\n\treturn false\nend)\n\nif ChatSettings.GeneralChannelName and ChatSettings.GeneralChannelName ~= \"\" then\n\tlocal allChannel = ChatService:AddChannel(ChatSettings.GeneralChannelName)\n\n\tallChannel.Leavable = false\n\tallChannel.AutoJoin = true\n\n\tallChannel:RegisterGetWelcomeMessageFunction(function(speaker)\n\t\tif RunService:IsStudio() then\n\t\t\treturn nil\n\t\tend\n\t\tlocal player = speaker:GetPlayer()\n\t\tif player then\n\t\t\tlocal success, canChat = pcall(function()\n\t\t\t\treturn Chat:CanUserChatAsync(player.UserId)\n\t\t\tend)\n\t\t\tif success and not canChat then\n\t\t\t\treturn \"\"\n\t\t\tend\n\t\tend\n\tend)\nend\n\nlocal systemChannel = ChatService:AddChannel(\"System\")\nsystemChannel.Leavable = false\nsystemChannel.AutoJoin = true\nsystemChannel.WelcomeMessage = ChatLocalization:Get(\n\t\"GameChat_ChatServiceRunner_SystemChannelWelcomeMessage\", \"This channel is for system and game notifications.\"\n)\n\nsystemChannel.SpeakerJoined:connect(function(speakerName)\n\tsystemChannel:MuteSpeaker(speakerName)\nend)\n\n\nlocal function TryRunModule(module)\n\tif module:IsA(\"ModuleScript\") then\n\t\tlocal ret = require(module)\n\t\tif (type(ret) == \"function\") then\n\t\t\tret(ChatService)\n\t\tend\n\tend\nend\n\nlocal modules = Chat:WaitForChild(\"ChatModules\")\nmodules.ChildAdded:connect(function(child)\n\tlocal success, returnval = pcall(TryRunModule, child)\n\tif not success and returnval then\n\t\tprint(\"Error running module \" ..child.Name.. \": \" ..returnval)\n\tend\nend)\n\nfor _, module in pairs(modules:GetChildren()) do\n\tlocal success, returnval = pcall(TryRunModule, module)\n\tif not success and returnval then\n\t\tprint(\"Error running module \" ..module.Name.. \": \" ..returnval)\n\tend\nend\n\nPlayersService.PlayerRemoving:connect(function(playerObj)\n\tif (ChatService:GetSpeaker(playerObj.Name)) then\n\t\tChatService:RemoveSpeaker(playerObj.Name)\n\tend\nend)\n"
  },
  {
    "path": "src/ServerScriptService/analytics/GameAnalytics/INSTRUCTIONS.server.lua",
    "content": "--[[\n\t\n\tThanks for using my GameAnalytics module! PM me (ByDefault) with any suggestions, bugs or feedback in\n\tgeneral!\n\t\n\tTo start using the module, follow these steps:\n\t\n\t1. Create an account on gameanalytics.com and create a game\n\t2. Get your game key and secret key\n\t3. Require this module ( require(GameAnalytics) )\n\t4. Call :Init(GameKey, SecretKey) on the module\n\t5. You're ready!\n\t\n\t\n\tTo send events, follow these steps:\n\t\n\t1. Create a table for the event, like so:\n\t\tlocal eventTable = {\n\t\t\t[\"category\"] = \"design\",\n\t\t\t[\"event_id\"] = \"Game:RoundStart:Spleef\",\n\t\t}\n\t2. Call :SendEvent(EventTable) on the module, letting the only parameter be the eventTable you just made\n\t3. That's it! Your event will automatically be sent within 15 seconds!\n\t\n\t\n\tIt may take a few minutes for your event to appear in the gameanalytics realtime page and every\n\tother page gets refreshed with the newest data daily.\n\t\n\tResources:\n\t REST API Docs: http://www.gameanalytics.com/docs/rest-api\n\t Account Management: http://www.gameanalytics.com/docs/account-management\n\t FAQ: http://www.gameanalytics.com/docs/faq\n\t\n--]]\n\nscript:remove()"
  },
  {
    "path": "src/ServerScriptService/analytics/GameAnalytics/bit.lua",
    "content": "local M = {_TYPE='module', _NAME='bit.numberlua', _VERSION='0.3.1.20120131'}\n\nM.bits = 32\n\nlocal floor = math.floor\n\nlocal MOD = 2^32\nlocal MODM = MOD-1\n\nlocal function memoize(f)\n  local mt = {}\n  local t = setmetatable({}, mt)\n  function mt:__index(k)\n    local v = f(k); t[k] = v\n    return v\n  end\n  return t\nend\n\nlocal function make_bitop_uncached(t, m)\n  local function bitop(a, b)\n    local res,p = 0,1\n    while a ~= 0 and b ~= 0 do\n      local am, bm = a%m, b%m\n      res = res + t[am][bm]*p\n      a = (a - am) / m\n      b = (b - bm) / m\n      p = p*m\n    end\n    res = res + (a+b)*p\n    return res\n  end\n  return bitop\nend\n\nlocal function make_bitop(t)\n  local op1 = make_bitop_uncached(t,2^1)\n  local op2 = memoize(function(a)\n    return memoize(function(b)\n      return op1(a, b)\n    end)\n  end)\n  return make_bitop_uncached(op2, 2^(t.n or 1))\nend\n\n-- ok?  probably not if running on a 32-bit int Lua number type platform\nfunction M.tobit(x)\n  return x % 2^32\nend\n\nM.cast = M.tobit\n\nM.bxor = make_bitop {[0]={[0]=0,[1]=1},[1]={[0]=1,[1]=0}, n=4}\nlocal bxor = M.bxor\n\nfunction M.bnot(a)   return MODM - a end\nlocal bnot = M.bnot\n\nfunction M.band(a,b) return ((a+b) - bxor(a,b))/2 end\nlocal band = M.band\n\nfunction M.bor(a,b)  return MODM - band(MODM - a, MODM - b) end\nlocal bor = M.bor\n\nlocal lshift, rshift -- forward declare\n\nfunction M.rshift(a,disp) -- Lua5.2 insipred\n  if disp < 0 then return lshift(a,-disp) end\n  return floor(a % 2^32 / 2^disp)\nend\nrshift = M.rshift\n\nfunction M.lshift(a,disp) -- Lua5.2 inspired\n  if disp < 0 then return rshift(a,-disp) end \n  return (a * 2^disp) % 2^32\nend\nlshift = M.lshift\n\nfunction M.tohex(x, n) -- BitOp style\n  n = n or 8\n  local up\n  if n <= 0 then\n    if n == 0 then return '' end\n    up = true\n    n = - n\n  end\n  x = band(x, 16^n-1)\n  return ('%0'..n..(up and 'X' or 'x')):format(x)\nend\nlocal tohex = M.tohex\n\nfunction M.extract(n, field, width) -- Lua5.2 inspired\n  width = width or 1\n  return band(rshift(n, field), 2^width-1)\nend\nlocal extract = M.extract\n\nfunction M.replace(n, v, field, width) -- Lua5.2 inspired\n  width = width or 1\n  local mask1 = 2^width-1\n  v = band(v, mask1) -- required by spec?\n  local mask = bnot(lshift(mask1, field))\n  return band(n, mask) + lshift(v, field)\nend\nlocal replace = M.replace\n\nfunction M.bswap(x)  -- BitOp style\n  local a = band(x, 0xff); x = rshift(x, 8)\n  local b = band(x, 0xff); x = rshift(x, 8)\n  local c = band(x, 0xff); x = rshift(x, 8)\n  local d = band(x, 0xff)\n  return lshift(lshift(lshift(a, 8) + b, 8) + c, 8) + d\nend\nlocal bswap = M.bswap\n\nfunction M.rrotate(x, disp)  -- Lua5.2 inspired\n  disp = disp % 32\n  local low = band(x, 2^disp-1)\n  return rshift(x, disp) + lshift(low, 32-disp)\nend\nlocal rrotate = M.rrotate\n\nfunction M.lrotate(x, disp)  -- Lua5.2 inspired\n  return rrotate(x, -disp)\nend\nlocal lrotate = M.lrotate\n\nM.rol = M.lrotate  -- LuaOp inspired\nM.ror = M.rrotate  -- LuaOp insipred\n\n\nfunction M.arshift(x, disp) -- Lua5.2 inspired\n  local z = rshift(x, disp)\n  if x >= 0x80000000 then z = z + lshift(2^disp-1, 32-disp) end\n  return z\nend\nlocal arshift = M.arshift\n\nfunction M.btest(x, y) -- Lua5.2 inspired\n  return band(x, y) ~= 0\nend\n\n\n\nM.bit32 = bit32\n\n\n--\n-- Start Lua 5.2 \"bit32\" compat section.\n--\n--[[\nM.bit32 = {} -- Lua 5.2 'bit32' compatibility\n\nM.bit32.bits = M.bits\n\nM.bit32.cast = M.cast\n\nlocal function bit32_bnot(x)\n  return (-1 - x) % MOD\nend\nM.bit32.bnot = bit32_bnot\n\nlocal function bit32_bxor(a, b, c, ...)\n  local z\n  if b then\n    a = a % MOD\n    b = b % MOD\n    z = bxor(a, b)\n    if c then\n      z = bit32_bxor(z, c, ...)\n    end\n    return z\n  elseif a then\n    return a % MOD\n  else\n    return 0\n  end\nend\nM.bit32.bxor = bit32_bxor\n\nlocal function bit32_band(a, b, c, ...)\n  local z\n  if b then\n    a = a % MOD\n    b = b % MOD\n    z = ((a+b) - bxor(a,b)) / 2\n    if c then\n      z = bit32_band(z, c, ...)\n    end\n    return z\n  elseif a then\n    return a % MOD\n  else\n    return MODM\n  end\nend\nM.bit32.band = bit32_band\n\nlocal function bit32_bor(a, b, c, ...)\n  local z\n  if b then\n    a = a % MOD\n    b = b % MOD\n    z = MODM - band(MODM - a, MODM - b)\n    if c then\n      z = bit32_bor(z, c, ...)\n    end\n    return z\n  elseif a then\n    return a % MOD\n  else\n    return 0\n  end\nend\nM.bit32.bor = bit32_bor\n\nfunction M.bit32.btest(...)\n  return bit32_band(...) ~= 0\nend\n\nfunction M.bit32.lrotate(x, disp)\n  return lrotate(x % MOD, disp)\nend\n\nfunction M.bit32.rrotate(x, disp)\n  return rrotate(x % MOD, disp)\nend\n\nfunction M.bit32.lshift(x,disp)\n  if disp > 31 or disp < -31 then return 0 end\n  return lshift(x % MOD, disp)\nend\n\nfunction M.bit32.rshift(x,disp)\n  if disp > 31 or disp < -31 then return 0 end\n  return rshift(x % MOD, disp)\nend\n\nfunction M.bit32.arshift(x,disp)\n  x = x % MOD\n  if disp >= 0 then\n    if disp > 31 then\n      return (x >= 0x80000000) and MODM or 0\n    else\n      local z = rshift(x, disp)\n      if x >= 0x80000000 then z = z + lshift(2^disp-1, 32-disp) end\n      return z\n    end\n  else\n    return lshift(x, -disp)\n  end\nend\n\nfunction M.bit32.extract(x, field, ...)\n  local width = ... or 1\n  if field < 0 or field > 31 or width < 0 or field+width > 32 then error 'out of range' end\n  x = x % MOD\n  return extract(x, field, ...)\nend\n\nfunction M.bit32.replace(x, v, field, ...)\n  local width = ... or 1\n  if field < 0 or field > 31 or width < 0 or field+width > 32 then error 'out of range' end\n  x = x % MOD\n  v = v % MOD\n  return replace(x, v, field, ...)\nend\n\n]]\n--\n-- Start LuaBitOp \"bit\" compat section.\n--\n\nM.bit = {} -- LuaBitOp \"bit\" compatibility\n\nM.bit.bits = M.bits\n\nM.bit.cast = M.cast\n\nfunction M.bit.tobit(x)\n  x = x % MOD\n  if x >= 0x80000000 then x = x - MOD end\n  return x\nend\nlocal bit_tobit = M.bit.tobit\n\nfunction M.bit.tohex(x, ...)\n  return tohex(x % MOD, ...)\nend\n\nfunction M.bit.bnot(x)\n  return bit_tobit(bnot(x % MOD))\nend\n\nlocal function bit_bor(a, b, c, ...)\n  if c then\n    return bit_bor(bit_bor(a, b), c, ...)\n  elseif b then\n    return bit_tobit(bor(a % MOD, b % MOD))\n  else\n    return bit_tobit(a)\n  end\nend\nM.bit.bor = bit_bor\n\nlocal function bit_band(a, b, c, ...)\n  if c then\n    return bit_band(bit_band(a, b), c, ...)\n  elseif b then\n    return bit_tobit(band(a % MOD, b % MOD))\n  else\n    return bit_tobit(a)\n  end\nend\nM.bit.band = bit_band\n\nlocal function bit_bxor(a, b, c, ...)\n  if c then\n    return bit_bxor(bit_bxor(a, b), c, ...)\n  elseif b then\n    return bit_tobit(bxor(a % MOD, b % MOD))\n  else\n    return bit_tobit(a)\n  end\nend\nM.bit.bxor = bit_bxor\n\nfunction M.bit.lshift(x, n)\n  return bit_tobit(lshift(x % MOD, n % 32))\nend\n\nfunction M.bit.rshift(x, n)\n  return bit_tobit(rshift(x % MOD, n % 32))\nend\n\nfunction M.bit.arshift(x, n)\n  return bit_tobit(arshift(x % MOD, n % 32))\nend\n\nfunction M.bit.rol(x, n)\n  return bit_tobit(lrotate(x % MOD, n % 32))\nend\n\nfunction M.bit.ror(x, n)\n  return bit_tobit(rrotate(x % MOD, n % 32))\nend\n\nfunction M.bit.bswap(x)\n  return bit_tobit(bswap(x % MOD))\nend\n\nreturn M"
  },
  {
    "path": "src/ServerScriptService/analytics/GameAnalytics/init.lua",
    "content": "--Variables\nlocal baseURL = \"http://api.gameanalytics.com/v2/\"\n\n\nlocal replicatedStorage \t= game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t\t= modules.load(\"network\")\n\n--Services\nlocal HTTP = game:GetService(\"HttpService\")\n\n--Normalize strings for GameAnalytics processing\nlocal function normify(str)\n\n\tstr = string.gsub(string.gsub(str,\" \",\"_\"),\"[^a-zA-Z0-9 - _ :]\",\"\")\n\n\t\n\t\n\tif #str > 30 then\n\t\tstr = string.sub(str,1,30)\n\tend\n\treturn str\nend\n\nlocal placeName = \"unknown\"\n_G.placeName = placeName\n\nspawn(function()\n\tlocal placeInfo = game.MarketplaceService:GetProductInfo(game.PlaceId,Enum.InfoType.Asset)\n\tif placeInfo then\n\t\tplaceName = normify(placeInfo.Name)\n\t\t_G.placeName = placeName\n\tend\nend)\n\nGA = {\n\tPostFrequency = 20,\n\t\n\tGameKey = nil,\n\tSecretKey = nil,\n\tSessionID = nil,\n\tQueue = {},\n\t\n\tEncodingModules = {},\n\tReady = false,\n}\n\nfunction GA:Init(GameKey, SecretKey)\n\tbaseURL = baseURL..GameKey..\"/\"\n\t\n\tGA.GameKey = GameKey\n\tGA.SecretKey = SecretKey\n\tGA.SessionID = HTTP:GenerateGUID(false):lower()\n\t\n\t--Encoding Modules\n\tGA.EncodingModules.lockbox = require(script.lockbox)\n\tGA.EncodingModules.lockbox.bit = require(script.bit).bit\t\n\tGA.EncodingModules.array = require(GA.EncodingModules.lockbox.util.array)\n\tGA.EncodingModules.stream = require(GA.EncodingModules.lockbox.util.stream)\n\tGA.EncodingModules.base64 = require(GA.EncodingModules.lockbox.util.base64)\n\tGA.EncodingModules.hmac = require(GA.EncodingModules.lockbox.mac.hmac)\n\tGA.EncodingModules.sha256 = require(GA.EncodingModules.lockbox.digest.sha2_256)\n\t\n\t\n\tGA.Base = {\n\t\t[\"device\"] = \"unknown\",\n\t\t[\"v\"] = 2,\n\t\t[\"user_id\"] = \"unknown\",\n\t\t[\"client_ts\"] = os.time(),\n--\t\t[\"sdk_version\"] = \"rest api v2\",\n\t\t[\"sdk_version\"] = \"roblox 1.0.1\",\n\t\t[\"os_version\"] = \"windows 10\",\n\t\t[\"manufacturer\"] = \"unknown\",\n\t\t[\"platform\"] = \"windows\",\t\n\t\t[\"session_id\"] = GA.SessionID,\n\t\t[\"session_num\"] = 1,\n\t}\n\t\n\tlocal Data = HTTP:JSONEncode({\n\t\t[\"platform\"] = \"unknown\",\n\t\t[\"os_version\"] = \"unknown\",\n\t\t[\"sdk_version\"] = \"rest api v2\",\n\t})\n\n\tlocal Headers = {\n\t\tAuthorization = GA:Encode(Data)\n\t}\n\n\n\n\tlocal Response = HTTP:PostAsync(baseURL..\"init\", Data, Enum.HttpContentType.ApplicationJson, false, Headers)\n\tResponse = HTTP:JSONDecode(Response)\n\t\n\tif not Response.enabled then\n\t\twarn(\"GameAnalytics did not initialize properly!\")\t\t\n\t\treturn\n\tend\n\t\n\tGA.Ready = true\n\t\n\tspawn(function()\n\t\twhile true do\n\t\t\twait(GA.PostFrequency)\n\t\t\tGA:Post()\n\t\tend\n\tend)\nend\n\ngame:BindToClose(function()\n\tif game:GetService(\"RunService\"):IsStudio() then return end\n\tGA:Post()\n\twait(1)\n\tGA:Post()\nend)\n\nfunction GA:SendEvent(Data, Player)\n\t\n\t\n\tif not GA.Ready then\n\t\twarn(\"GameAnalytics has not been initialized! Call :Init(GameKey, SecretKey) on the module before sending events!\")\n\tend\n\t\n\t-- normify strings to comply with GameAnalytics\n\t\n\tfor key,value in pairs(Data) do\n\t\tif type(value) == \"string\" and key ~= \"message\" then\n\t\t\tData[key] = normify(value)\n\t\tend\n\tend\n\t-- do this BEFORE the base values are assigned\t\n\t\n\tfor i,v in pairs(GA.Base) do\n\t\tData[i] = Data[i] or v\n\tend\n\t\n\tif Player ~= nil and Player.Parent == game.Players then -- Allow game to specify a player (mod by berezaa)\n\t\tData[\"user_id\"] = tostring(Player.userId)\n\t\tif Player:FindFirstChild(\"AnalyticsSessionId\") then\n\t\t\tData[\"session_id\"] = Player.AnalyticsSessionId.Value\n\t\telse\n\t\t\twarn(\"failed to find analytics session for\",Player.Name)\n\t\tend\n\tend\n\t\n\tlocal playerClass = \"unknown\"\n\t\n\tlocal playerData = network:invoke(\"getPlayerData\", Player)\n\tif playerData then\n\t\tif playerData.class then\n\t\t\tplayerClass = normify(playerData.class)\n\t\tend\n\t\tif playerData.sessionCount then\n\t\t\tData[\"session_num\"] = playerData.sessionCount\n\t\tend\n\tend\n\t\n\tData[\"client_ts\"] = os.time()\n\n\t\t\n\t-- CLASS\n\tData[\"custom_01\"] = playerClass\n\t-- LOCATION\n\tData[\"custom_02\"] = placeName\n\t\n\t\n\ttable.insert(GA.Queue, Data)\n\treturn true\nend\n\nfunction GA:Post()\n\tif not GA.Ready then\n\t\twarn(\"GameAnalytics has not been initialized! Call :Init(GameKey, SecretKey) on the module before sending events!\")\n\t\treturn\t\n\tend\n\t\n\tif #GA.Queue > 0 then\n\t\tlocal Data = HTTP:JSONEncode(GA.Queue)\n\t\tGA.Queue = {}\n\t\t\n\t\t\n\t\tlocal Headers = {\n\t\t\t[\"Authorization\"] = GA:Encode(Data);\n\t\t\t[\"Content-Type\"] = \"application/json\";\n\t\t}\n\t\t\n\t\tlocal HttpRequest = {\n\t\t\tUrl = baseURL..\"events\";\n\t\t\tMethod = \"POST\";\n\t\t\tHeaders = Headers;\n\t\t\tBody = Data;\n\t\t}\n\t\tlocal s,ret = pcall(function() return HTTP:RequestAsync(HttpRequest) end)\n\t\t\n\t\tif not s then\n\t\t\twarn(\"GameAnalytics post failed without result!\")\n\t\telseif (ret.StatusCode and ret.StatusCode ~= 200) then\n\t\t\twarn(\"GameAnalytics error!\", \"HTTP\", ret.StatusCode, HTTP:JSONEncode(ret))\n\t\tend\n\tend\nend\n\nfunction GA:Encode(body)\n\tlocal secretKey = GA.SecretKey\n\t\n\tlocal hmacBuilder = GA.EncodingModules.hmac()\n\t\t.setBlockSize(64)\n\t\t.setDigest(GA.EncodingModules.sha256)\n\t\t.setKey(GA.EncodingModules.array.fromString(secretKey))\n\t\t.init()\n\t\t.update(GA.EncodingModules.stream.fromString(body))\n\t\t.finish()\n\treturn GA.EncodingModules.base64.fromArray(hmacBuilder.asBytes())\nend\n\nreturn GA"
  },
  {
    "path": "src/ServerScriptService/analytics/GameAnalytics/lockbox/digest/init.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/ServerScriptService/analytics/GameAnalytics/lockbox/digest/sha2_256.lua",
    "content": "local lockbox = script.Parent.Parent\nlocal Bit = require(lockbox.util.bit);\nlocal String = string;\nlocal Math = math;\nlocal Queue = require(lockbox.util.queue);\n\nlocal CONSTANTS = {\n   0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n   0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n   0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n   0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n   0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n   0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n   0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n   0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2  };\n\nlocal AND = Bit.band;\nlocal OR  = Bit.bor;\nlocal NOT = Bit.bnot;\nlocal XOR = Bit.bxor;\nlocal LROT = Bit.lrotate;\nlocal RROT = Bit.rrotate;\nlocal LSHIFT = Bit.lshift;\nlocal RSHIFT = Bit.rshift;\n\n--SHA2 is big-endian\nlocal bytes2word = function(b0,b1,b2,b3)\n\tlocal i = b0; i = LSHIFT(i,8);\n\ti = OR(i,b1); i = LSHIFT(i,8);\n\ti = OR(i,b2); i = LSHIFT(i,8);\n\ti = OR(i,b3);\n\treturn i;\nend\n\nlocal word2bytes = function(word)\n\tlocal b0,b1,b2,b3;\n\tb3 = AND(word,0xFF); word = RSHIFT(word,8);\n\tb2 = AND(word,0xFF); word = RSHIFT(word,8);\n\tb1 = AND(word,0xFF); word = RSHIFT(word,8);\n\tb0 = AND(word,0xFF);\n\treturn b0,b1,b2,b3;\nend\n\nlocal bytes2dword = function(b0,b1,b2,b3,b4,b5,b6,b7)\n\tlocal i = bytes2word(b0,b1,b2,b3);\n\tlocal j = bytes2word(b4,b5,b6,b7);\n\treturn (i*0x100000000)+j;\nend\n\nlocal dword2bytes = function(i)\n\tlocal b4,b5,b6,b7 = word2bytes(i);\n\tlocal b0,b1,b2,b3 = word2bytes(Math.floor(i/0x100000000));\n\treturn b0,b1,b2,b3,b4,b5,b6,b7;\nend \n\n\n\n\nlocal SHA2_256 = function()\n\n\tlocal queue = Queue();\n\n\tlocal h0 = 0x6a09e667;\n\tlocal h1 = 0xbb67ae85;\n\tlocal h2 = 0x3c6ef372;\n\tlocal h3 = 0xa54ff53a;\n\tlocal h4 = 0x510e527f;\n\tlocal h5 = 0x9b05688c;\n\tlocal h6 = 0x1f83d9ab;\n\tlocal h7 = 0x5be0cd19;\n\n\tlocal public = {};\n\n\tlocal processBlock = function()\n\t\t\n\t\tlocal a = h0;\n\t\tlocal b = h1;\n\t\tlocal c = h2;\n\t\tlocal d = h3;\n\t\tlocal e = h4;\n\t\tlocal f = h5;\n\t\tlocal g = h6;\n\t\tlocal h = h7;\n\t\t\n\t\tlocal w = {};\n\t\tfor i=0,15 do\n\t\t\tw[i] = bytes2word(queue.pop(),queue.pop(),queue.pop(),queue.pop());\n\t\tend\n\n\t\tgame:GetService(\"RunService\").Heartbeat:Wait()\n\n\t\tfor i=16,63 do\n\t\t\tif i%20 == 0 then\n\t\t\t\tgame:GetService(\"RunService\").Heartbeat:Wait()\n\t\t\tend\n\t\t\tlocal s0 = XOR(RROT(w[i-15],7), XOR(RROT(w[i-15],18), RSHIFT(w[i-15],3)));\n\t\t\tlocal s1 = XOR(RROT(w[i-2],17), XOR(RROT(w[i-2], 19), RSHIFT(w[i-2],10)));\n\t\t\tw[i] = AND(w[i-16] + s0 + w[i-7] + s1, 0xFFFFFFFF);\n\t\tend\n\n\t\tfor i=0,63 do\n\t\t\tif i%12 == 0 then\n\t\t\t\tgame:GetService(\"RunService\").Heartbeat:Wait()\n\t\t\tend\n\t\t\tlocal s1 = XOR(RROT(e,6), XOR(RROT(e,11),RROT(e,25)));\n\t\t\tlocal ch = XOR(AND(e,f), AND(NOT(e),g));\n\t\t\tlocal temp1 = h + s1 + ch + CONSTANTS[i+1] + w[i];\n\t\t\tlocal s0 = XOR(RROT(a,2), XOR(RROT(a,13), RROT(a,22)));\n\t\t\tlocal maj = XOR(AND(a,b), XOR(AND(a,c), AND(b,c)));\n\t\t\tlocal temp2 = s0 + maj;\n\n\t\t\th = g;\n\t\t\tg = f;\n\t\t\tf = e;\n\t\t\te = d + temp1;\n\t\t\td = c;\n\t\t\tc = b;\n\t\t\tb = a;\n\t\t\ta = temp1 + temp2;\n\t\tend\n\n\t\th0 = AND(h0 + a, 0xFFFFFFFF);\n\t\th1 = AND(h1 + b, 0xFFFFFFFF);\n\t\th2 = AND(h2 + c, 0xFFFFFFFF);\n\t\th3 = AND(h3 + d, 0xFFFFFFFF);\n\t\th4 = AND(h4 + e, 0xFFFFFFFF);\n\t\th5 = AND(h5 + f, 0xFFFFFFFF);\n\t\th6 = AND(h6 + g, 0xFFFFFFFF);\n\t\th7 = AND(h7 + h, 0xFFFFFFFF);\n\t\tgame:GetService(\"RunService\").Heartbeat:Wait()\n\tend\n\n\tpublic.init = function()\n\t\tqueue.reset();\n\n\t\th0 = 0x6a09e667;\n\t\th1 = 0xbb67ae85;\n\t\th2 = 0x3c6ef372;\n\t\th3 = 0xa54ff53a;\n\t\th4 = 0x510e527f;\n\t\th5 = 0x9b05688c;\n\t\th6 = 0x1f83d9ab;\n\t\th7 = 0x5be0cd19;\n\n\t\treturn public;\n\tend\n\n\tpublic.update = function(bytes)\n\t\t\n\t\tfor b in bytes do\n\t\t\tqueue.push(b);\n\t\t\tif queue.size() >= 64 then processBlock(); end\n\t\tend\n\t\t\n\t\treturn public;\n\tend\n\n\tpublic.finish = function()\n\t\tlocal bits = queue.getHead() * 8;\n\n\t\tqueue.push(0x80);\n\t\twhile ((queue.size()+7) % 64) < 63 do\n\t\t\tqueue.push(0x00);\n\t\tend\n\n\t\tlocal b0,b1,b2,b3,b4,b5,b6,b7 = dword2bytes(bits);\n\n\t\tqueue.push(b0);\n\t\tqueue.push(b1);\n\t\tqueue.push(b2);\n\t\tqueue.push(b3);\n\t\tqueue.push(b4);\n\t\tqueue.push(b5);\n\t\tqueue.push(b6);\n\t\tqueue.push(b7);\n\n\t\twhile queue.size() > 0 do\n\t\t\tprocessBlock();\n\t\tend\n\n\t\treturn public;\n\tend\n\t\n\tpublic.asBytes = function()\n\t\tlocal  b0, b1, b2, b3 = word2bytes(h0);\n\t\tlocal  b4, b5, b6, b7 = word2bytes(h1);\n\t\tlocal  b8, b9,b10,b11 = word2bytes(h2);\n\t\tlocal b12,b13,b14,b15 = word2bytes(h3);\n\t\tlocal b16,b17,b18,b19 = word2bytes(h4);\n\t\tlocal b20,b21,b22,b23 = word2bytes(h5);\n\t\tlocal b24,b25,b26,b27 = word2bytes(h6);\n\t\tlocal b28,b29,b30,b31 = word2bytes(h7);\n\n\n\t\treturn {  b0, b1, b2, b3, b4, b5, b6, b7, b8, b9,b10,b11,b12,b13,b14,b15\n\t\t\t\t,b16,b17,b18,b19,b20,b21,b22,b23,b24,b25,b26,b27,b28,b29,b30,b31};\t\n\tend\n\n\tpublic.asHex = function()\n\t\tlocal  b0, b1, b2, b3 = word2bytes(h0);\n\t\tlocal  b4, b5, b6, b7 = word2bytes(h1);\n\t\tlocal  b8, b9,b10,b11 = word2bytes(h2);\n\t\tlocal b12,b13,b14,b15 = word2bytes(h3);\n\t\tlocal b16,b17,b18,b19 = word2bytes(h4);\n\t\tlocal b20,b21,b22,b23 = word2bytes(h5);\n\t\tlocal b24,b25,b26,b27 = word2bytes(h6);\n\t\tlocal b28,b29,b30,b31 = word2bytes(h7);\n\n\t\tlocal fmt = \"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\"\t\t\n\n\t\treturn String.format(fmt, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9,b10,b11,b12,b13,b14,b15\n\t\t\t\t,b16,b17,b18,b19,b20,b21,b22,b23,b24,b25,b26,b27,b28,b29,b30,b31);\n\tend\n\n\treturn public;\n\nend\n\nreturn SHA2_256;"
  },
  {
    "path": "src/ServerScriptService/analytics/GameAnalytics/lockbox/init.lua",
    "content": "local lockbox = {ALLOW_INSECURE = false}\n\nfor i,v in pairs(script:GetChildren()) do\n\tlockbox[v.Name] = v\nend\n\nreturn lockbox"
  },
  {
    "path": "src/ServerScriptService/analytics/GameAnalytics/lockbox/mac/hmac.lua",
    "content": "local lockbox = script.Parent.Parent\n\nlocal Bit = require(lockbox.util.bit);\nlocal String = string;\nlocal Stream = require(lockbox.util.stream);\nlocal Array = require(lockbox.util.array);\n\nlocal XOR = Bit.bxor;\n\nlocal HMAC = function()\n\t\n\tlocal public = {};\n\tlocal blockSize = 64;\n\tlocal Digest = nil;\n\tlocal outerPadding = {};\n\tlocal innerPadding = {}\n\tlocal digest;\n\n\tpublic.setBlockSize = function(bytes)\n\t\tblockSize = bytes;\n\t\treturn public;\n\tend\n\n\tpublic.setDigest = function(digestModule)\n\t\tDigest = digestModule;\n\t\tdigest = Digest();\n\t\treturn public;\n\tend\n\n\tpublic.setKey = function(key)\n\t\tlocal keyStream;\n\n\t\tif(Array.size(key) > blockSize) then\n\t\t\tkeyStream = Stream.fromArray(Digest()\n\t\t\t\t\t\t.update(Stream.fromArray(key))\n\t\t\t\t\t\t.finish()\n\t\t\t\t\t\t.asBytes());\n\t\telse\n\t\t\tkeyStream = Stream.fromArray(key);\n\t\tend\t\n\n\t\touterPadding = {};\n\t\tinnerPadding = {};\n\n\t\tfor i=1,blockSize do\n\t\t\tlocal byte = keyStream();\n\t\t\tif byte == nil then byte = 0x00; end\n\t\t\touterPadding[i] = XOR(0x5C,byte);\n\t\t\tinnerPadding[i] = XOR(0x36,byte);\n\t\tend\n\n\t\treturn public;\n\tend\n\n\tpublic.init = function()\n\t\tdigest\t.init()\n\t\t\t\t.update(Stream.fromArray(innerPadding));\n\t\treturn public;\n\tend\n\t\n\tpublic.update = function(messageStream)\n\t\tdigest.update(messageStream);\n\t\treturn public;\n\tend\n\n\tpublic.finish = function()\n\t\tlocal inner = digest.finish().asBytes();\n\t\tdigest\t.init()\n\t\t\t\t.update(Stream.fromArray(outerPadding))\n\t\t\t\t.update(Stream.fromArray(inner))\n\t\t\t\t.finish();\n\n\t\treturn public;\n\tend\n\n\tpublic.asBytes = function()\n\t\treturn digest.asBytes();\n\tend\n\t\n\tpublic.asHex = function()\n\t\treturn digest.asHex();\n\tend\n\n\treturn public;\n\nend\n\nreturn HMAC;\n"
  },
  {
    "path": "src/ServerScriptService/analytics/GameAnalytics/lockbox/mac/init.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/ServerScriptService/analytics/GameAnalytics/lockbox/util/array.lua",
    "content": "local lockbox = script.Parent.Parent\n\nlocal String = string;\nlocal Queue = require(lockbox.util.queue);\nlocal Bit = require(lockbox.util.bit);\n\nlocal XOR = Bit.bxor;\n\nlocal Array = {};\n\nArray.size = function(array)\n\treturn #array;\nend\n\nArray.fromString = function(string)\n\tlocal bytes = {};\n\n\tlocal i=1;\n\tlocal byte = String.byte(string,i);\n\twhile byte ~= nil do\n\t\tbytes[i] = byte;\n\t\ti = i + 1;\n\t\tbyte = String.byte(string,i);\n\tend\n\n\treturn bytes;\n\nend\n\nArray.toString = function(bytes)\n\tlocal chars = {};\n\tlocal i=1;\n\n\tlocal byte = bytes[i];\n\twhile byte ~= nil do\n\t\tchars[i] = String.char(byte);\n\t\ti = i+1;\n\t\tbyte = bytes[i];\n\tend\n\n\treturn table.concat(chars,\"\");\nend\n\t\nArray.fromStream = function(stream)\n\tlocal array = {};\n\tlocal i=1;\n\n\tlocal byte = stream();\n\twhile byte ~= nil do\n\t\tarray[i] = byte;\n\t\ti = i+1;\n\t\tbyte = stream();\n\tend\n\n\treturn array;\nend\n\nArray.readFromQueue = function(queue,size)\n\tlocal array = {};\n\n\tfor i=1,size do\n\t\tarray[i] = queue.pop();\n\tend\n\n\treturn array;\nend\n\nArray.writeToQueue = function(queue,array)\n\tlocal size = Array.size(array);\n\n\tfor i=1,size do\n\t\tqueue.push(array[i]);\n\tend\nend\n\nArray.toStream = function(array)\n\tlocal queue = Queue();\n\tlocal i=1;\n\n\tlocal byte = array[i];\n\twhile byte ~= nil do\n\t\tqueue.push(byte);\n\t\ti=i+1;\n\t\tbyte = array[i];\n\tend\n\n\treturn queue.pop;\t\nend\n\n\nlocal fromHexTable = {};\nfor i=0,255 do\n\tfromHexTable[String.format(\"%02X\",i)]=i;\n\tfromHexTable[String.format(\"%02x\",i)]=i;\nend \n\nArray.fromHex = function(hex)\n\tlocal array = {};\n\n\tfor i=1,String.len(hex)/2 do\n\t\tlocal h = String.sub(hex,i*2-1,i*2);\n\t\tarray[i] = fromHexTable[h];\n\tend\t\t\n\t\n\treturn array;\nend\n\n\nlocal toHexTable = {};\nfor i=0,255 do\n\ttoHexTable[i]=String.format(\"%02X\",i);\nend\n\nArray.toHex = function(array)\n\tlocal hex = {};\n\tlocal i = 1;\n\n\tlocal byte = array[i];\n\twhile byte ~= nil do\n\t\thex[i] = toHexTable[byte];\n\t\ti=i+1;\n\t\tbyte = array[i];\n\tend\n\n\treturn table.concat(hex,\"\");\n\nend\n\nArray.concat = function(a,b)\n\tlocal concat = {};\n\tlocal out=1;\n\n\tlocal i=1;\n\tlocal byte = a[i];\n\twhile byte ~= nil do\n\t\tconcat[out] = byte;\n\t\ti = i + 1;\n\t\tout = out + 1;\n\t\tbyte = a[i];\n\tend\n\n\tlocal i=1;\n\tlocal byte = b[i];\n\twhile byte ~= nil do\n\t\tconcat[out] = byte;\n\t\ti = i + 1;\n\t\tout = out + 1;\n\t\tbyte = b[i];\n\tend\n\n\treturn concat;\nend\n\nArray.truncate = function(a,newSize)\n\tlocal x = {};\n\n\tfor i=1,newSize do\n\t\tx[i]=a[i];\n\tend\n\n\treturn x;\nend\n\nArray.XOR = function(a,b)\n\tlocal x = {};\n\n\tfor k,v in pairs(a) do\n\t\tx[k] = XOR(v,b[k]);\n\tend\n\n\treturn x;\nend\n\nArray.substitute = function(input,sbox)\n\tlocal out = {};\n\n\tfor k,v in pairs(input) do\n\t\tout[k] = sbox[v];\n\tend\n\n\treturn out;\nend\n\nArray.permute = function(input,pbox)\n\tlocal out = {};\n\n\tfor k,v in pairs(pbox) do\n\t\tout[k] = input[v];\n\tend\n\n\treturn out;\nend\n\nArray.copy = function(input)\n\tlocal out = {};\n\n\tfor k,v in pairs(input) do\n\t\tout[k] = v;\n\tend\n\treturn out;\nend\n\nArray.slice = function(input,start,stop)\n\tlocal out = {};\n\n\tfor i=start,stop do\n\t\tout[i-start+1] = input[i];\n\tend\n\treturn out;\nend\n\nreturn Array;\n"
  },
  {
    "path": "src/ServerScriptService/analytics/GameAnalytics/lockbox/util/base64.lua",
    "content": "local lockbox = script.Parent.Parent\n\nlocal String = string;\nlocal Bit = require(lockbox.util.bit);\n\nlocal Array = require(lockbox.util.array);\nlocal Stream = require(lockbox.util.stream);\n\nlocal AND = Bit.band;\nlocal OR  = Bit.bor;\nlocal NOT = Bit.bnot;\nlocal XOR = Bit.bxor;\nlocal LROT = Bit.lrotate;\nlocal RROT = Bit.rrotate;\nlocal LSHIFT = Bit.lshift;\nlocal RSHIFT = Bit.rshift;\n\n\nlocal SYMBOLS = {\n[0]=\"A\",\"B\",\"C\",\"D\",\"E\",\"F\",\"G\",\"H\",\"I\",\"J\",\"K\",\"L\",\"M\",\"N\",\"O\",\"P\",\n    \"Q\",\"R\",\"S\",\"T\",\"U\",\"V\",\"W\",\"X\",\"Y\",\"Z\",\"a\",\"b\",\"c\",\"d\",\"e\",\"f\",\n    \"g\",\"h\",\"i\",\"j\",\"k\",\"l\",\"m\",\"n\",\"o\",\"p\",\"q\",\"r\",\"s\",\"t\",\"u\",\"v\",\n    \"w\",\"x\",\"y\",\"z\",\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\"+\",\"/\"};\n\nlocal LOOKUP = {};\n\nfor k,v in pairs(SYMBOLS) do\n\tLOOKUP[k]=v;\n\tLOOKUP[v]=k;\nend\n\n\nlocal Base64 = {};\n\nBase64.fromStream = function(stream)\n\tlocal bits = 0x00;\n\tlocal bitCount = 0;\n\tlocal base64 = {};\n\n\tlocal byte = stream();\n\twhile byte ~= nil do\n\t\tbits = OR(LSHIFT(bits,8),byte);\n\t\tbitCount = bitCount + 8;\n\t\twhile bitCount >= 6 do\n\t\t\tbitCount = bitCount - 6;\n\t\t\tlocal temp = RSHIFT(bits,bitCount);\n\t\t\ttable.insert(base64,LOOKUP[temp]);\n\t\t\tbits = AND(bits,NOT(LSHIFT(0xFFFFFFFF,bitCount)));\n\t\tend\n\t\tbyte = stream();\n\tend\n\n\tif (bitCount == 4) then\n\t\tbits = LSHIFT(bits,2);\n\t\ttable.insert(base64,LOOKUP[bits]);\n\t\ttable.insert(base64,\"=\");\n\telseif (bitCount == 2) then\n\t\tbits = LSHIFT(bits,4);\n\t\ttable.insert(base64,LOOKUP[bits]);\n\t\ttable.insert(base64,\"==\");\n\tend\n\n\treturn table.concat(base64,\"\");\nend\n\nBase64.fromArray = function(array)\n\tlocal bits = 0x00;\n\tlocal bitCount = 0;\n\tlocal base64 = {};\n\n\tlocal ind = 1;\n\n\tlocal byte = array[ind]; ind = ind + 1;\n\twhile byte ~= nil do\n\t\tbits = OR(LSHIFT(bits,8),byte);\n\t\tbitCount = bitCount + 8;\n\t\twhile bitCount >= 6 do\n\t\t\tbitCount = bitCount - 6;\n\t\t\tlocal temp = RSHIFT(bits,bitCount);\n\t\t\ttable.insert(base64,LOOKUP[temp]);\n\t\t\tbits = AND(bits,NOT(LSHIFT(0xFFFFFFFF,bitCount)));\n\t\tend\n\t\tbyte = array[ind]; ind = ind + 1;\n\tend\n\n\tif (bitCount == 4) then\n\t\tbits = LSHIFT(bits,2);\n\t\ttable.insert(base64,LOOKUP[bits]);\n\t\ttable.insert(base64,\"=\");\n\telseif (bitCount == 2) then\n\t\tbits = LSHIFT(bits,4);\n\t\ttable.insert(base64,LOOKUP[bits]);\n\t\ttable.insert(base64,\"==\");\n\tend\n\n\treturn table.concat(base64,\"\");\t\nend\n\nBase64.fromString = function(string)\n\treturn Base64.fromArray(Array.fromString(string));\nend\n\n\n\nBase64.toStream = function(base64)\n\treturn Stream.fromArray(Base64.toArray(base64));\nend\n\nBase64.toArray = function(base64)\n\tlocal bits = 0x00;\n\tlocal bitCount = 0;\n\n\tlocal bytes = {};\n\n\tfor c in String.gmatch(base64,\".\") do\n\t\tif (c == \"=\") then\n\t\t\tbits = RSHIFT(bits,2); bitCount = bitCount - 2;\n\t\telse\n\t\t\tbits = LSHIFT(bits,6); bitCount = bitCount + 6;\n\t\t\tbits = OR(bits,LOOKUP[c]);\n\t\tend\n\n\t\twhile(bitCount >= 8) do\n\t\t\tbitCount = bitCount - 8;\n\t\t\tlocal temp = RSHIFT(bits,bitCount);\n\t\t\ttable.insert(bytes,temp);\n\t\t\tbits = AND(bits,NOT(LSHIFT(0xFFFFFFFF,bitCount)));\n\t\tend\n\tend\n\t\n\treturn bytes;\nend\n\nBase64.toString = function(base64)\n\tlocal bits = 0x00;\n\tlocal bitCount = 0;\n\n\tlocal chars = {};\n\n\tfor c in String.gmatch(base64,\".\") do\n\t\tif (c == \"=\") then\n\t\t\tbits = RSHIFT(bits,2); bitCount = bitCount - 2;\n\t\telse\n\t\t\tbits = LSHIFT(bits,6); bitCount = bitCount + 6;\n\t\t\tbits = OR(bits,LOOKUP[c]);\n\t\tend\n\n\t\twhile(bitCount >= 8) do\n\t\t\tbitCount = bitCount - 8;\n\t\t\tlocal temp = RSHIFT(bits,bitCount);\n\t\t\ttable.insert(chars,String.char(temp));\n\t\t\tbits = AND(bits,NOT(LSHIFT(0xFFFFFFFF,bitCount)));\n\t\tend\n\tend\n\t\n\treturn table.concat(chars,\"\");\nend\n\nreturn Base64;\n"
  },
  {
    "path": "src/ServerScriptService/analytics/GameAnalytics/lockbox/util/bit.lua",
    "content": "local lockbox = script.Parent.Parent\n\ne = require(lockbox).bit\n\nif not e then\n\terror(\"no bitwise support found\", 2)\nend\n\n-- Workaround to support Lua 5.2 bit32 API with the LuaJIT bit one\nif e.rol and not e.lrotate then\n\te.lrotate = e.rol\nend\nif e.ror and not e.rrotate then\n\te.rrotate = e.ror\nend\n\nreturn e\n"
  },
  {
    "path": "src/ServerScriptService/analytics/GameAnalytics/lockbox/util/init.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/ServerScriptService/analytics/GameAnalytics/lockbox/util/queue.lua",
    "content": "local Queue = function()\n\tlocal queue = {};\t\n\tlocal tail = 0;\n\tlocal head = 0;\n\n\tlocal public = {};\n\n\tpublic.push = function(obj)\n\t\tqueue[head] = obj;\n\t\thead = head + 1;\n\t\treturn;\t\t\n\tend\n\n\tpublic.pop = function()\n\t\tif tail < head\n\t\tthen\n\t\t\tlocal obj = queue[tail];\n\t\t\tqueue[tail] = nil;\n\t\t\ttail = tail + 1;\n\t\t\treturn obj;\n\t\telse\n\t\t\treturn nil;\n\t\tend\n\tend\n\n\tpublic.size = function()\n\t\treturn head - tail;\n\tend\n\n\tpublic.getHead = function()\n\t\treturn head;\n\tend\n\n\tpublic.getTail = function()\n\t\treturn tail;\n\tend\n\n\tpublic.reset = function()\n\t\tqueue = {};\n\t\thead = 0;\n\t\ttail = 0;\n\tend\n\n\treturn public;\nend\n\nreturn Queue;\n"
  },
  {
    "path": "src/ServerScriptService/analytics/GameAnalytics/lockbox/util/stream.lua",
    "content": "local lockbox = script.Parent.Parent\n\nlocal Queue = require(lockbox.util.queue);\nlocal String = string;\n\nlocal Stream = {};\n\n\nStream.fromString = function(string)\n\tlocal i=0;\n\treturn function()\n\t\ti=i+1;\n\t\tif(i <= String.len(string)) then\n\t\t\treturn String.byte(string,i);\n\t\telse\n\t\t\treturn nil;\n\t\tend\n\tend\nend\n\n\nStream.toString = function(stream)\n\tlocal array = {};\n\tlocal i=1;\n\n\tlocal byte = stream();\n\twhile byte ~= nil do\n\t\tarray[i] = String.char(byte);\n\t\ti = i+1;\n\t\tbyte = stream();\n\tend\n\n\treturn table.concat(array,\"\");\nend\n\n\nStream.fromArray = function(array)\n\tlocal queue = Queue();\n\tlocal i=1;\n\n\tlocal byte = array[i];\n\twhile byte ~= nil do\n\t\tqueue.push(byte);\n\t\ti=i+1;\n\t\tbyte = array[i];\n\tend\n\n\treturn queue.pop;\nend\n\n\nStream.toArray = function(stream)\n\tlocal array = {};\n\tlocal i=1;\n\n\tlocal byte = stream();\n\twhile byte ~= nil do\n\t\tarray[i] = byte;\n\t\ti = i+1;\n\t\tbyte = stream();\n\tend\n\n\treturn array;\t\nend\n\n\nlocal fromHexTable = {};\nfor i=0,255 do\n\tfromHexTable[String.format(\"%02X\",i)]=i;\n\tfromHexTable[String.format(\"%02x\",i)]=i;\nend \n\nStream.fromHex = function(hex)\n\tlocal queue = Queue();\n\n\tfor i=1,String.len(hex)/2 do\n\t\tlocal h = String.sub(hex,i*2-1,i*2);\n\t\tqueue.push(fromHexTable[h]);\n\tend\t\t\n\n\treturn queue.pop;\nend\n\n\n\nlocal toHexTable = {};\nfor i=0,255 do\n\ttoHexTable[i]=String.format(\"%02X\",i);\nend\n\nStream.toHex = function(stream)\n\tlocal hex = {};\n\tlocal i = 1;\n\n\tlocal byte = stream();\n\twhile byte ~= nil do\n\t\thex[i] = toHexTable[byte];\n\t\ti=i+1;\n\t\tbyte = stream();\n\tend\n\n\treturn table.concat(hex,\"\");\nend\n\nreturn Stream;\n"
  },
  {
    "path": "src/ServerScriptService/analytics/init.server.lua",
    "content": "\n\nlocal Analytics = require(script.GameAnalytics)\n\nlocal key = require(script.key)\n\nlocal DEBUG = false\n\nlocal errorchache = 3\n\nAnalytics:Init(key.GameKey, key.SecretKey)\n\nlocal replicatedStorage \t= game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t\t= modules.load(\"network\")\n\n\nfunction playerRemoving(Player)\n\tif Player:FindFirstChild(\"SessionEnded\") == nil then\n\t\tif Player:FindFirstChild(\"teleporting\") then\n\t\t\treturn false\n\t\tend\n\t\tif Player:FindFirstChild(\"AnalyticsSessionId\") then\n\t\t\tlocal Length = 0\n\t\t\tif Player:FindFirstChild(\"JoinTime\") then\n\t\t\t\tLength = os.time() - Player.JoinTime.Value\n\t\t\tend\t\n\t\t\tAnalytics:SendEvent({\n\t\t\t\t[\"category\"] = \"session_end\",\n\t\t\t\t[\"length\"] = math.floor(Length),\n\t\t\t}, Player)\n\t\tend\t\n\t\tlocal Tag = Instance.new(\"BoolValue\")\n\t\tTag.Name = \"SessionEnded\"\n\t\tTag.Parent = Player\t\t\t\t\n\tend\n\nend\n\ngame.Players.PlayerRemoving:connect(playerRemoving)\ngame:BindToClose(function()\n\tif game:GetService(\"RunService\"):IsStudio() then return end\n\n\tfor i,Player in pairs(game.Players:GetPlayers()) do\n\t\tplayerRemoving(Player)\n\tend\n\nend)\n\nfunction playerRequestNewSession(Player)\n\t\n\tif Player:FindFirstChild(\"AnalyticsSessionId\") == nil then\n\t\tlocal SessionId = game:GetService(\"HttpService\"):GenerateGUID(false):lower()\n\t\tlocal Tag = Instance.new(\"StringValue\")\n\t\tTag.Name = \"AnalyticsSessionId\"\n\t\tTag.Value = SessionId\n\t\tTag.Parent = Player\t\n\t\t\n\n\t\t\n\t\tlocal playerData = network:invoke(\"getPlayerData\", Player)\n\t\tif playerData then\n\t\t\tplayerData.nonSerializeData.incrementPlayerData(\"sessionCount\", 1)\n\t\tend\n\n\t\t\n\t\tAnalytics:SendEvent({\n\t\t\t[\"category\"] = \"user\",\n\t\t\t[\"session_id\"] = SessionId,\n\t\t\t[\"user_id\"] = tostring(Player.userId),\n\t\t}, Player)\n\t\tlocal TimeStamp = Instance.new(\"NumberValue\")\n\t\tTimeStamp.Value = os.time()\n\t\tTimeStamp.Name = \"JoinTime\"\n\t\tTimeStamp.Parent = Player\t\n\t\treturn SessionId\n\tend\nend\nnetwork:create(\"newSession\",\"BindableFunction\",\"OnInvoke\",playerRequestNewSession)\nnetwork:create(\"requestNewSession\",\"RemoteFunction\",\"OnServerInvoke\",function() end)\n\nfunction playerRequestContinueSessionFromTeleport(Player, SessionId, JoinTimeStamp)\n\tif Player:FindFirstChild(\"AnalyticsSessionId\") == nil then\n\t\tlocal Tag = Instance.new(\"StringValue\")\n\t\tTag.Name = \"AnalyticsSessionId\"\n\t\tTag.Value = SessionId\n\t\tTag.Parent = Player\t\n\t\t\n\t\t-- TimeStamp sanity check\n\t\tif os.time() - JoinTimeStamp < 0 or os.time() - JoinTimeStamp > 250000 then\n\t\t\tJoinTimeStamp = os.time()\n\t\tend\n\t\t\n\t\tlocal TimeStamp = Instance.new(\"NumberValue\")\n\t\tTimeStamp.Value = JoinTimeStamp\n\t\tTimeStamp.Name = \"JoinTime\"\n\t\tTimeStamp.Parent = Player\t\t\t\n\t\tspawn(function()\n\t\t\tPlayer:WaitForChild(\"DataLoaded\",30)\n\t\t\tnetwork:invoke(\"reportAnalyticsEvent\",Player,\"teleport:arrived:\"..(_G.placeName or \"unknown\"))\n\n\t\tend)\n\t\treturn true\n\tend\t\nend\nnetwork:create(\"continueSession\", \"BindableFunction\", \"OnInvoke\", playerRequestContinueSessionFromTeleport)\nnetwork:create(\"requestContinueSession\",\"RemoteFunction\",\"OnServerInvoke\",function() warn(\"requestContinueSession has been moved to the server.\") end)\n\nlocal PurchaseMade = (function(Player, Category, Product, Amount)\n\tlocal Cents = math.floor(Amount * 0.7) * 0.35\n\tProduct = Product or \"unspecified\"\n\tProduct = tostring(Product):gsub('%W','')\n\tAnalytics:SendEvent({\n\t\t\n\t\t\n\t\t[\"category\"] = \"business\",\n\t\t[\"event_id\"] = tostring(Category)..\":\"..Product,\n\t\t[\"amount\"] = math.floor(Cents),\n\t\t[\"currency\"] = \"USD\",\n\t\t[\"transaction_num\"] = 1,\n\n\t}, Player)\n\t\n\tif DEBUG then\n\n\tend\t\nend)\nnetwork:create(\"purchaseMade\", \"BindableFunction\", \"OnInvoke\", PurchaseMade)\n\nlocal CurrencyEvent = (function(Player, Currency, Amount, Source)\n\tlocal flowType = \"Source\"\n\t\n\tif Amount < 0 then\n\t\tflowType = \"Sink\"\n\t\tAmount = math.abs(Amount)\n\tend\n\t\n\tSource = Source or \"unknown:unknown\"\n\t\n\tAnalytics:SendEvent({\n\t\t[\"category\"] = \"resource\",\n\t\t[\"event_id\"] = flowType..\":\"..Currency..\":\"..Source,\n\t\t[\"amount\"] = Amount,\n\n\t}, Player)\t\n\tif DEBUG then\n\n\tend\t\t\t\nend)\nnetwork:create(\"reportCurrency\", \"BindableFunction\", \"OnInvoke\", CurrencyEvent)\n\nlocal ReportProgression = function(Player, Status, EventId, Attempt, Score)\n\tlocal EventId = Status..\":\"..EventId\n\tif Status == \"Start\" then\n\t\tAnalytics:SendEvent({\n\t\t\t[\"category\"] = \"progression\",\n\t\t\t[\"event_id\"] = EventId,\n\t\t}, Player)\t\t\n\telseif Status == \"Fail\" or Status == \"Complete\" then\n\t\tAnalytics:SendEvent({\n\t\t\t[\"category\"] = \"progression\",\n\t\t\t[\"event_id\"] = EventId,\n\t\t\t[\"attempt_num\"] = Attempt,\n\t\t\t[\"score\"] = Score\n\t\t}, Player)\t\t\t\n\tend\nend\n\nlocal ReportEvent = function(Player, EventId, Value)\n\tAnalytics:SendEvent({\n\t\t[\"category\"] = \"design\",\n\t\t[\"event_id\"] = EventId,\n\t\t[\"value\"] = Value,\n\t}, Player)\nend\nnetwork:create(\"reportAnalyticsEvent\",\"BindableFunction\",\"OnInvoke\",ReportEvent)\n\nlocal ReportError = (function(Player, Severity, Message)\n\tif errorchache > 0 then\n\t\terrorchache = errorchache - 1\n\t\tAnalytics:SendEvent({\n\t\t\t[\"category\"] = \"error\",\n\t\t\t[\"severity\"] = Severity,\n\t\t\t[\"message\"] = Message,\n\t\t}, Player)\t\t\n\tend\nend)\n\nnetwork:create(\"reportError\",\"BindableFunction\",\"OnInvoke\",ReportError)\n\nlocal PlayerOpenBox = (function(Player, Box, VintageWin)\n\n\tif VintageWin then\n\t\tAnalytics:SendEvent({\n\t\t\t[\"category\"] = \"design\",\n\t\t\t[\"event_id\"] = \"vintagewin:\"..string.lower(Box),\n\t\t\t[\"value\"] = 1\n\t\t}, Player)\t\t\n\tend\n\tAnalytics:SendEvent({\n\t\t[\"category\"] = \"design\",\n\t\t[\"event_id\"] = \"box:\"..string.lower(Box),\n\t\t[\"value\"] = 1\n\t}, Player)\t\n\tif DEBUG then\n\n\tend\t\t\t\nend)\n--[[\nlocal ErrorCache = {}\n\nfunction ErrorExists(Error)\n\tfor i,Err in pairs(ErrorCache) do\n\t\tif Err == Error then\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\ngame:GetService(\"ScriptContext\").Error:connect(function (message, stack)\n\tlocal Error = tostring(message)..\" : \"..tostring(stack)\t\n\tif not ErrorExists(Error) then\n\t\tAnalytics:SendEvent({\n\t\t\t[\"category\"] = \"error\",\n\t\t\t[\"severity\"] = \"error\",\n\t\t\t[\"message\"] = \"Server: \"..Error\n\t\t})\n\tend\nend)\n]]\n--[[\ngame.ReplicatedStorage.ClientError.OnServerEvent:connect(function(Player, message, stack)\n\tAnalytics:SendEvent({\n\t\t[\"category\"] = \"error\",\n\t\t[\"severity\"] = \"error\",\n\t\t[\"message\"] = tostring(Player.userId)..\": \"..tostring(message)..\" : \"..tostring(stack)\t\n\t})\t\nend)\n]]"
  },
  {
    "path": "src/ServerScriptService/analytics/key.lua",
    "content": "local module = {}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nif game.GameId == 712031239 then\n\tmodule.GameKey = \"3e764352635e9469f42bd2c8a622f32a\"\n\tmodule.SecretKey = \"b0a85553e528578e189756b992e9d8407968e6d7\"\t\t\nelse\n\tmodule.GameKey = \"7018964337c285f95a5e4eb88adee721\"\n\tmodule.SecretKey = \"f4757b325194bf9eab8e8800fd55ec67e002c43f\"\t\nend\n\n\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/analytics/testing.lua",
    "content": "local module = {}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nmodule.GameKey = \"8a70de271d7a3ff4f1bf1008d726e32e\"\nmodule.SecretKey = \"93cad05e13866a6e0d90c92af7cc5cd5c120fecb\"\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/contents/datastoreInterface.lua",
    "content": "local module = {}\nmodule.priority = 3\n-- DATASTORE WRAPPER TO RETURN playerSaveFileData, table storing all there is to know about the player's\n-- file on that save!\n-- Authors: Polymorphic, berezaa\n\nlocal RunService = game:GetService(\"RunService\")\n\n-- set this flag to true to do data modification in studio\n-- misuse of this would be a mistake, so make sure you're\n-- careful with it. search its name to see where you can\n-- do the data modification\nlocal PERFORMING_STUDIO_PLAYER_DATA_MODIFICATION = false\n\nlocal utilities\nlocal network\n\nlocal DataStoreService = game:GetService(\"DataStoreService\")\n\nlocal TESTING = false\n\nlocal isInternal = (game.PlaceId == 2061558182 or game.ReplicatedStorage:FindFirstChild(\"doNotSaveData\") or RunService:IsStudio())\n\n\n--[[\n\tinventorySlotData {}\n\t\tnumber id\n\t\tnumber stacks\n\t\tnumber position [1 - MAX_INVENTORY_SLOTS]\n\t\t\t> position is a unique number, game will violently remove\n\t\t\t  contents that overlap if no extra space is available\n\t\tmodifierData modifierData\n\n\tequipmentSlotData {}\n\t\tnumber id\n\t\tnumber position\n\t\t\t1 = weapon\n\t\t\t2 = helmet\n\t\t\t3 = body\n\t\t\t4 = left arm\n\t\t\t5 = right arm\n\t\t\t6 = left leg\n\t\t\t7 = right leg\n\t\t\t> position is a unique number, game will violently remove\n\t\t\t  contents that overlap if no inventory space is available\n\n\taccessory\n\n\thotbarSlotData {}\n\t\tnumber id\n\t\tdataType dataType [look at mapping]\n\t\tnumber position\n\n\tmodifierData {}\n\n\t\t-- all these refer to *flat* bonuses, you can make them percentage increases of the base stat\n\t\t-- by making the stat [name]Percent, for example: expPercentage = 0.1 would mean a 10% increase of all exp gain\n\t\t-- and dexPercent = 0.1 would mean a 10% increase of dex (after all flat amounts are summed)\n\t\tint... [dex, int, str, vit, luck, exp]\n\n\t\tstring sourceType\n\t\tint sourceId\n\t\tint sourcePlayerId\n\n\tstatisticsModifier {}\n\t\tint expiration\n\t\t{modifierData} modifiers\n\n\taccessoryData {}\n\t\taccessoryType[mapping] accessoryType\n\t\tint id\n\t\tint color\n\n\t\tmodifierData modifierData\n\n\t\t--> hat, skin, undershirt, underwear, face\n\n\t{accessoryData} accessories {}\n--]]\n\n-- 21 = production version 3/3/2020\n\n---WARNING-----------------\n---WARNING-----------------\n---WARNING-----------------\n---WARNING-----------------\n-- the global version of datstores, incrementing this lets you wipe everyone's data or revert back to a previous version\n-- do not change for fun!\n--local datastoreVersion = 21\n\nlocal datastoreVersion = 35\n\n-- 21: main production\n\n-- been set to 21 since alpha-1.0.0\n---WARNING-----------------\n---WARNING-----------------\n---WARNING-----------------\n---WARNING-----------------\n\n-- demo override\n--if game.GameId == 712031239 then\n--\tdatastoreVersion = 30\n--end\n\nlocal function GetMostRecentSaveTime(pages)\n\tfor _, Pair in pairs(pages:GetCurrentPage()) do\n\t\treturn Pair.value\n\tend\nend\n\n\n\n--@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n--@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n\nlocal function __intGetPlayerGlobalSaveFileData(player, providedVersion)\n\n\tlocal PlayerId \t\t\t= player.userId\n\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t-- GODS\n\n\tlocal LastGlobalSave\n\n\t-- obtain latest GDS version data from GODS\n\tlocal GODSSuccess, GODSError = pcall(function()\n\t\tlocal suffix = (game.ReplicatedStorage:FindFirstChild(\"mirrorWorld\") and \"mirror\") or \"\"\n\t\tlocal GlobalOrderedStore = DataStoreService:GetOrderedDataStore(tostring(PlayerId), \"GlobalPlayerSaveTimes\" .. datastoreVersion .. suffix)\n\t\tlocal pages = GlobalOrderedStore:GetSortedAsync(false, 1)\n\t\tLastGlobalSave = GetMostRecentSaveTime(pages)\n\tend)\n\n\t-- GODS call failed, abort.\n\tif not GODSSuccess then\n\t\treturn false, nil, \"(GODS): \"..GODSError\n\tend\n\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t-- GODS -> GDS\n\n\tlocal GlobalData\n\tlocal function loadVersion(version)\n\t\tlocal GDSSuccess, GDSError = pcall(function()\n\t\t\tlocal suffix = (game.ReplicatedStorage:FindFirstChild(\"mirrorWorld\") and \"mirror\") or \"\"\n\t\t\tlocal GlobalDataStore = DataStoreService:GetDataStore(tostring(PlayerId), \"GlobalPlayerData\" .. datastoreVersion .. suffix)\n\t\t\tGlobalData = GlobalDataStore:GetAsync(tostring(version))\n\t\tend)\n\n\t\t-- GDS call failed, abort.\n\t\tif not GDSSuccess then\n\t\t\treturn false, \"(GDS): \"..GDSError\n\t\tend\n\n\t\treturn true, \"\"\n\tend\n\n\tif providedVersion then\n\t\tlocal success, message = loadVersion(providedVersion)\n\n\t\tif not success then\n\t\t\treturn false, nil, message\n\t\tend\n\tend\n\n\t-- if providedVersion failed or we weren't given a providedVersion, do as normal\n\tif not GlobalData then\n\t\tlocal success, message = loadVersion(LastGlobalSave)\n\n\t\tif not success then\n\t\t\treturn false, nil, message\n\t\tend\n\tend\n\n\tif (LastGlobalSave and GlobalData == nil) then\n\t\tlocal issue = \"globalSave-NoData\"\n\t\tspawn(function()\n\t\t\tnetwork:invoke(\"reportError\", player, \"critical\", \"LastGlobalSave provided but no GlobalData??\")\n\t\tend)\n\t\tplayer:Kick(\"Critical error prevented your data from being loaded. The developers have been alerted. Please try again later. Error code: \"..issue)\n\t\terror(\"Game rejected data.\")\n\t\treturn false, nil, \"game rejected data\"\n\telseif providedVersion and LastGlobalSave == nil then\n\t\tlocal issue = \"globalSave-providedNoLast\"\n\t\tspawn(function()\n\t\t\tnetwork:invoke(\"reportError\", player, \"critical\", \"providedVersion but no LastGlobalSave\")\n\t\tend)\n\t\tplayer:Kick(\"Critical error prevented your data from being loaded. The developers have been alerted. Please try again later. Error code: \"..issue)\n\t\terror(\"Game rejected data.\")\n\t\treturn false, nil, \"game rejected data\"\n\tend\n\n\t-- this tool may be useful later\n\n\t--[[\n\t-- ROLLBACK!\n\tlocal minTime = 1583707058\n\tlocal maxTime = 1583710658\n\tif GlobalData and GlobalData.lastSaveTimestamp then\n\t\tif GlobalData.lastSaveTimestamp >= minTime and GlobalData.lastSaveTimestamp <= maxTime then\n\t\t\tif not GlobalData.rollback1 then\n\t\t\t\tgame:GetService(\"TeleportService\"):Teleport(2759159666, player)\n\t\t\t\treturn false, nil, \"Requires rollback\"\n\t\t\tend\n\t\tend\n\tend\n\t]]\n\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\treturn true, GlobalData, \"Successfully loaded!\"\n\nend\n\nfunction module:getPlayerGlobalSaveFileData(player, providedVersion)\n\treturn __intGetPlayerGlobalSaveFileData(player, providedVersion)\nend\n\n\n\n-- internal interface with datastores to save JUST global data\nlocal function __intUpdatePlayerGlobalSaveFileData(PlayerId, playerGlobalData)\n\n\n\n\tif game.PlaceId == 3372071669 or isInternal then\n\t\treturn true, nil, playerGlobalData.version\n\tend\n\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t-- Sanity checks\n\n\tif not playerGlobalData.version then\n\t\treturn false, \"no global version\"\n\tend\n\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t-- GDS\n\tlocal suffix = (game.ReplicatedStorage:FindFirstChild(\"mirrorWorld\") and \"mirror\") or \"\"\n\tlocal GlobalDataStore = DataStoreService:GetDataStore(tostring(PlayerId), \"GlobalPlayerData\" .. datastoreVersion .. suffix)\n\n\tlocal GDSSuccess, GDSError = pcall(function()\n\t\tGlobalDataStore:SetAsync(tostring(playerGlobalData.version), playerGlobalData)\n\tend)\n\n\tif not GDSSuccess then\n\t\twarn(\"GDS update Failed!\")\n\t\treturn false, \"(GDS): \".. GDSError\n\tend\n\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t-- GODS\n\tlocal suffix = (game.ReplicatedStorage:FindFirstChild(\"mirrorWorld\") and \"mirror\") or \"\"\n\tlocal GlobalOrderedDataStore = DataStoreService:GetOrderedDataStore(tostring(PlayerId), \"GlobalPlayerSaveTimes\" .. datastoreVersion .. suffix)\n\n\tlocal GODSSuccess, GODSError = pcall(function()\n\t\tGlobalOrderedDataStore:SetAsync(\"s\" .. tostring(playerGlobalData.version), playerGlobalData.version)\n\tend)\n\n\tif not GODSSuccess then\n\t\twarn(\"GODS update Failed!\")\n\t\treturn false, \"(GODS): \" .. GODSError\n\tend\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\n\treturn true, nil, playerGlobalData.version\nend\n\nfunction module:updatePlayerGlobalSaveFileData(playerId, playerGlobalData)\n\treturn __intUpdatePlayerGlobalSaveFileData(playerId, playerGlobalData)\nend\n\n--@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n--@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n\n-- internal interface with datastores to get data\nlocal function __intGetPlayerSaveFileData(player, saveFileNumber, desiredVersion)\n\n\t-- dont load datastores in studio\n\tif (RunService:IsStudio() or RunService:IsRunMode()) and (not PERFORMING_STUDIO_PLAYER_DATA_MODIFICATION) then\n\n\t\t-- new adventure\n\t\tif game.PlaceId == 4561988219 then\n\t\t\tlocal Data = {}\n\t\t\tData.newb = true\n\t\t\tData.globalData = {}\n\t\t\treturn true, Data\n\t\tend\n\t\tif game.PlaceId == 4041427413 then\n\t\t\tlocal Data = {}\n\t\t\tData.newb = true\n\t\t\tData.globalData = {}\n\t\t\treturn true, Data\n\t\tend\n\n\t\tlocal Tag = player:FindFirstChild(\"dataSlot\")\n\t\tif Tag == nil then\n\t\t\tTag = Instance.new(\"IntValue\")\n\t\t\tTag.Name = \"dataSlot\"\n\t\t\tTag.Parent = player\n\t\t\tTag.Value = 0\n\n\t\t\t--return true, game.HttpService:JSONDecode('{\"quests\":{\"active\":[{\"objectives\":[{\"completion\":{\"amount\":30},\"triggerType\":\"monster-killed\",\"requirement\":{\"amount\":30,\"monsterName\":\"Shroom\"}}],\"id\":2}],\"completed\":[{\"completionTime\":1549512122.07898211479187011719,\"id\":1},{\"completionTime\":1549573249.58895897865295410156,\"id\":4},{\"completionTime\":1549671842.90531301498413085938,\"id\":5}]},\"hotbar\":[{\"position\":2,\"dataType\":1,\"id\":22},{\"position\":6,\"dataType\":2,\"id\":10},{\"position\":1,\"dataType\":1,\"id\":6},{\"position\":3,\"dataType\":1,\"id\":29},{\"position\":4,\"dataType\":1,\"id\":45},{\"position\":7,\"dataType\":1,\"id\":22},{\"position\":8,\"dataType\":1,\"id\":22},{\"position\":9,\"dataType\":1,\"id\":22},{\"position\":10,\"dataType\":1,\"id\":22},{\"position\":5,\"dataType\":1,\"id\":22}],\"professions\":{\"fishing\":{\"level\":1,\"exp\":0}},\"sessionCount\":52,\"holding\":[],\"lastPhysicalPosition\":[-57,38,-381],\"flags\":{\"revokeCheatWeapons\":true,\"statCheck\":true,\"fixcolors2\":true,\"badges2\":true,\"stealthRevoke\":true,\"removeSpiderQueenCrown\":true},\"internalData\":{\"suspicion\":0},\"newb\":false,\"equipment\":[{\"modifierData\":[{\"baseDamage\":3},{\"baseDamage\":3},{\"baseDamage\":3},{\"baseDamage\":3},{\"baseDamage\":3},{\"baseDamage\":3},{\"baseDamage\":3}],\"successfulUpgrades\":7,\"upgrades\":7,\"id\":50,\"position\":1,\"stacks\":1},{\"position\":8,\"stacks\":1,\"id\":84},{\"position\":2,\"stacks\":1,\"id\":85}],\"moneyFlag2\":true,\"abilities\":[{\"rank\":5,\"id\":1},{\"rank\":3,\"id\":2},{\"rank\":1,\"id\":3},{\"rank\":10,\"id\":6},{\"rank\":1,\"id\":7},{\"rank\":5,\"id\":10}],\"statusEffects\":[],\"inventory\":[{\"position\":25,\"stacks\":7,\"id\":89},{\"position\":24,\"stacks\":33,\"id\":88},{\"position\":23,\"stacks\":1,\"id\":14},{\"position\":22,\"stacks\":1,\"id\":6},{\"spawnChance\":0.5,\"id\":60,\"stacks\":1,\"position\":22,\"itemName\":\"goblin necklace\"},{\"spawnChance\":0.0500000000000000027755575615629,\"id\":87,\"stacks\":12,\"position\":21,\"itemName\":\"arrow\"},{\"spawnChance\":0.299999999999999988897769753748,\"id\":88,\"stacks\":7,\"position\":21,\"itemName\":\"mana potion 2\"},{\"position\":20,\"stacks\":1,\"id\":64},{\"position\":19,\"stacks\":1,\"id\":14},{\"position\":18,\"stacks\":22,\"id\":22},{\"spawnChance\":0.0400000000000000008326672684689,\"id\":89,\"stacks\":99,\"position\":17,\"itemName\":\"health potion 3\"},{\"position\":16,\"stacks\":23,\"id\":96},{\"position\":16,\"stacks\":5,\"id\":22},{\"position\":15,\"stacks\":70,\"id\":95},{\"position\":15,\"stacks\":5,\"id\":22},{\"position\":14,\"stacks\":99,\"id\":96},{\"position\":14,\"stacks\":5,\"id\":22},{\"position\":13,\"stacks\":99,\"id\":96},{\"position\":13,\"stacks\":6,\"id\":22},{\"position\":12,\"stacks\":99,\"id\":96},{\"spawnChance\":0.100000000000000005551115123126,\"id\":47,\"stacks\":2,\"position\":12,\"itemName\":\"intelligence potion\"},{\"position\":11,\"stacks\":16,\"id\":22},{\"position\":11,\"stacks\":99,\"id\":96},{\"position\":10,\"stacks\":99,\"id\":96},{\"position\":10,\"stacks\":5,\"id\":22},{\"position\":9,\"stacks\":99,\"id\":96},{\"position\":9,\"stacks\":5,\"id\":22},{\"position\":8,\"stacks\":6,\"id\":22},{\"position\":8,\"stacks\":99,\"id\":96},{\"position\":7,\"stacks\":5,\"id\":22},{\"position\":7,\"stacks\":1,\"id\":50},{\"position\":7,\"stacks\":99,\"id\":95},{\"position\":6,\"stacks\":9,\"id\":22},{\"spawnChance\":0.900000000000000022204460492503,\"id\":86,\"stacks\":99,\"position\":6,\"itemName\":\"hay\"},{\"position\":6,\"stacks\":1,\"id\":81},{\"position\":5,\"stacks\":7,\"id\":77},{\"position\":5,\"stacks\":1,\"id\":37},{\"position\":5,\"stacks\":5,\"id\":22},{\"position\":4,\"stacks\":50,\"id\":31},{\"position\":4,\"stacks\":1,\"id\":21},{\"position\":4,\"stacks\":5,\"id\":22},{\"spawnChance\":0.0100000000000000002081668171172,\"id\":20,\"stacks\":1,\"position\":3,\"itemName\":\"rake\"},{\"spawnChance\":0.00800000000000000016653345369377,\"id\":45,\"stacks\":10,\"position\":3,\"itemName\":\"vitality potion\"},{\"spawnChance\":0.699999999999999955591079014994,\"id\":10,\"stacks\":2,\"position\":3,\"itemName\":\"mushroom beard\"},{\"position\":2,\"stacks\":56,\"id\":86},{\"position\":2,\"stacks\":5,\"id\":22},{\"spawnChance\":0.0400000000000000008326672684689,\"id\":7,\"stacks\":1,\"position\":2,\"itemName\":\"wooden club\"},{\"position\":1,\"stacks\":99,\"id\":6},{\"spawnChance\":1,\"id\":9,\"stacks\":81,\"position\":1,\"itemName\":\"mushroom spore\"},{\"position\":1,\"stacks\":1,\"id\":36}],\"statistics\":{\"pointsUnassigned\":0,\"pointsAssigned\":0,\"modifierData\":[],\"dex\":35,\"int\":1,\"vit\":17,\"str\":25},\"lastLocation\":2471035818,\"treasureChests\":{\"ChestSpiderQnEZ\":{\"time\":1549809595},\"ChestJungleCave\":{\"time\":1549807288},\"ChestJungleHighUp\":{\"time\":1549610051},\"ChestCaveRoom1\":{\"time\":1549513159},\"ChestGhrotto\":{\"time\":1549671139},\"ChestJungleView\":{\"time\":1549803003},\"ChestBar\":{\"time\":1549602753},\"ChestWaterBridge\":{\"time\":1549567414},\"ChestUnderCave\":{\"time\":1549567402},\"ChestJungleCannon\":{\"time\":1549567251},\"IronChestDitch\":{\"chestType\":\"ironChest\",\"disabled\":true},\"ChestJungleBarricade\":{\"time\":1549608072},\"IronChestSpiderCave\":{\"chestType\":\"ironChest\",\"disabled\":true},\"ChestGoldFIsh\":{\"chestType\":\"goldChest\",\"disabled\":true},\"ChestCave\":{\"time\":1549514147},\"ChestBridge\":{\"time\":1549600487},\"ChestUnderBridge\":{\"time\":1549515639},\"ChestCaveRoom2\":{\"time\":1549513160}},\"timestamp\":511,\"level\":26,\"gold\":221250,\"exp\":4377.38674400269519537687301636,\"hasCustomizedCharacter\":true,\"accessories\":{\"hair\":1,\"undershirt\":1,\"shirtColorId\":1,\"face\":1,\"skinColorId\":1,\"hairColorId\":1,\"underwear\":1},\"class\":\"Hunter\",\"userSettings\":{\"keybinds\":{\"W\":\"pick up\",\"Q\":\"interact\"},\"clearingInteraction\":true},\"abilityBooks\":{\"hunter\":{\"pointsAssigned\":16},\"adventurer\":{\"pointsAssigned\":9}}}')\n\t\tend\n\t\tlocal inven = {gold = 10; level = 1; inventory = {}; equipment = {}; moneyFlag2 = true; isTestingDontSave = true; abilityBooks = {}; abilities = {};}\n\t\tinven.globalData = {\n\t\t}\n\t\tinven.flags = inven.flags or {}\n\t\tinven.flags.enchantWipe3 = true\n\t\tinven.flags.completedGauntlet = true\n\t\tinven.inventory = {\n\t\t}\n\t\tinven.equipment = {\n\t\t\t{id = 5, position = 1}, -- main hand\n\t\t\t--{id = 179, position = 2}, -- hat\n\t\t}\n\n\n\n\n\t\treturn true, inven\n\tend\n\n\tlocal Data\n\tlocal PlayerId \t\t\t= player.userId\n\tlocal Slot \t\t\t\t= saveFileNumber\n\n\tlocal providedVersion\n\n\n\tif desiredVersion then\n\t\tprovidedVersion = desiredVersion\n\tend\n\n\n\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t-- GODS + GDS\n\n\tlocal Suffix = \"-slot\"..tostring(Slot)\n\tlocal LastSave\n\n\tlocal GlobalDataSuccess, GlobalData, GlobalDataStatus = __intGetPlayerGlobalSaveFileData(player, providedVersion)\n\n\tif not GlobalDataStatus then\n\t\treturn false, nil, GlobalDataStatus\n\tend\n\n\tif GlobalData then\n\t\tLastSave = GlobalData.lastSave[Suffix]\n\tend\n\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t-- ODS (legacy)\n\n\t-- No data from new method, revert to old method for LastSave\n\tif not LastSave then\n\t\twarn(\"DSI>Obtaining data from legacy method\")\n\t\t-- obtain latest data from ODS\n\t\tlocal ODSSuccess, ODSError = pcall(function()\n\t\t\tlocal suffix = (game.ReplicatedStorage:FindFirstChild(\"mirrorWorld\") and \"mirror\") or \"\"\n\t\t\tlocal OrderedStore \t= DataStoreService:GetOrderedDataStore(tostring(PlayerId), \"PlayerSaveTimes\" .. datastoreVersion .. Suffix .. suffix) -- dont worry i hate this too\n\t\t\tlocal pages = OrderedStore:GetSortedAsync(false, 1)\n\t\t\tLastSave = GetMostRecentSaveTime(pages) or 0\n\t\tend)\n\n\t\t-- ODS call failed, abort.\n\t\tif not ODSSuccess then\n\t\t\treturn false, nil, \"(ODS): \"..ODSError\n\t\tend\n\tend\n\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t-- Sanity Checks\n\t--[[\n\tif (providedTimeStamp and LastSave == nil) then\n\t\tlocal issue = \"lastSave-nil\"\n\t\tspawn(function()\n\t\t\tnetwork:invoke(\"reportError\", player, \"critical\", \"LastSave nil for providedTimeStamp\")\n\t\tend)\n\t\tplayer:Kick(\"Critical error prevented your data from being loaded. The developers have been alerted. Please try again later. Error code: \"..issue)\n\t\terror(\"Game rejected data.\")\n\t\treturn false, nil, \"game rejected data\"\n\telseif (providedTimeStamp and providedTimeStamp > 1) and (LastSave and LastSave <= 1) then\n\t\tlocal issue = \"lastSave-lessThan\"\n\t\tspawn(function()\n\t\t\tnetwork:invoke(\"reportError\", player, \"critical\", \"LastSave lessThan for providedTimeStamp\")\n\t\tend)\n\t\tplayer:Kick(\"Critical error prevented your data from being loaded. The developers have been alerted. Please try again later. Error code: \"..issue)\n\t\terror(\"Game rejected data.\")\n\t\treturn false, nil,\"game rejected data\"\n\tend\n\n\tif providedTimeStamp and providedTimeStamp > LastSave then\n\t\t-- The player passed a timestamp that is newer than what the ODS has recorded\n\n\t\tLastSave = providedTimeStamp\n\tend\n\t]]\n\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t-- Load from DataStore, this should be the same\n\n\tlocal DSSuccess, DSError\n\n\t-- player data is available\n\tif LastSave > 1 then\n\t\tDSSuccess, DSError = pcall(function()\n\t\t\t-- This data slot should have data on it\n\t\t\tlocal suffix = (game.ReplicatedStorage:FindFirstChild(\"mirrorWorld\") and \"mirror\") or \"\"\n\t\t\tlocal DataStore = DataStoreService:GetDataStore(tostring(PlayerId), \"PlayerData\" .. datastoreVersion .. Suffix .. suffix) -- Suffix .. suffix xD\n\t\t\tData = DataStore:GetAsync(tostring(LastSave))\n\n\t\t\tData.globalData = GlobalData or {}\n\n\n\t\t\tlocal issue\n\n\t\t\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t\t\t-- More sanity checks\n\n\t\t\tif Data == nil then\n\t\t\t\tif --[[ providedTimeStamp ]] false then\n\t\t\t\t\tspawn(function()\n\t\t\t\t\t\tnetwork:invoke(\"reportError\", player, \"critical\", \"DataStores returned nil data for provided timestamp\")\n\t\t\t\t\tend)\n\t\t\t\t\tissue = \"nil-provided\"\n\t\t\t\telse\n\t\t\t\t\tspawn(function()\n\t\t\t\t\t\tnetwork:invoke(\"reportError\", player, \"critical\", \"DataStores returned nil data for recorded timestamp\")\n\t\t\t\t\tend)\n\t\t\t\t\tissue = \"nil-recorded\"\n\t\t\t\tend\n\t\t\telseif typeof(Data) ~= \"table\" then\n\t\t\t\tspawn(function()\n\t\t\t\t\tnetwork:invoke(\"reportError\", player, \"critical\", \"DataStores returned invalid (non-table) data\")\n\t\t\t\tend)\n\t\t\t\tissue = \"invalid\"\n\t\t\telseif Data.level == nil or Data.inventory == nil or Data.gold == nil or Data.equipment == nil then\n\t\t\t\tspawn(function()\n\t\t\t\t\tnetwork:invoke(\"reportError\", player, \"critical\", \"DataStores returned data with missing values\")\n\t\t\t\tend)\n\t\t\t\tissue = \"missing\"\n\t\t\tend\n\n\t\t\tif issue then\n\t\t\t\tplayer:Kick(\"Critical error prevented your data from being loaded. The developers have been alerted. Please try again later. Error code: \"..issue)\n\t\t\t\terror(\"Game rejected data.\")\n\t\t\tend\n\n\t\t\t-- apply timestamp (for legacy saves)\n\t\t\tif Data and not Data.timestamp then\n\t\t\t\tData.timestamp = LastSave\n\t\t\tend\n\t\tend)\n\telse\n\t\tDSSuccess = true\n\t\tData = {}\n\t\tData.newb = true\n\t\tData.globalData = GlobalData or {}\n\tend\n\n\t-- DataStore failed, return error:\n\tif not DSSuccess then\n\t\treturn false, nil, \"(DS): \"..DSError\n\tend\n\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t-- All good\n\n\tlocal Tag = player:FindFirstChild(\"dataSlot\")\n\tif Tag == nil then\n\t\tTag = Instance.new(\"IntValue\")\n\t\tTag.Name = \"dataSlot\"\n\t\tTag.Parent = player\n\tend\n\tTag.Value = Slot\n\n\n\treturn true, Data\nend\n\n-- internal interface with datastores to save data\nlocal function __intUpdatePlayerSaveFileData(PlayerId, saveFileNumber, playerSaveFileData, playerGlobalData)\n\tif game.ReplicatedStorage:FindFirstChild(\"doNotSaveData\") then\n\t\treturn true, \"don't save data here\"\n\tend\n\n\tlocal Slot \t\t\t\t= saveFileNumber\n\n\tplayerSaveFileData.lastSaveTimestamp = os.time()\n\tplayerGlobalData.lastSaveTimestamp = os.time()\n\n\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t-- Sanity checks\n\n\tif not playerGlobalData.version then\n\t\treturn false, \"no global version\"\n\tend\n\n\tif not playerSaveFileData.timestamp then\n\t\treturn false, \"no data timestamp\"\n\tend\n\n\n\tlocal Suffix = \"-slot\"..tostring(Slot)\n\n\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t-- GDS\n\tlocal suffix = (game.ReplicatedStorage:FindFirstChild(\"mirrorWorld\") and \"mirror\") or \"\"\n\tlocal GlobalDataStore = DataStoreService:GetDataStore(tostring(PlayerId), \"GlobalPlayerData\" .. datastoreVersion .. suffix)\n\n\tlocal GDSSuccess, GDSError = pcall(function()\n\t\tGlobalDataStore:SetAsync(tostring(playerGlobalData.version), playerGlobalData)\n\tend)\n\n\tif not GDSSuccess then\n\t\twarn(\"GDS update Failed!\")\n\t\treturn false, \"(GDS): \".. GDSError\n\tend\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t-- DS\n\tlocal suffix = (game.ReplicatedStorage:FindFirstChild(\"mirrorWorld\") and \"mirror\") or \"\"\n\tlocal DataStore = DataStoreService:GetDataStore(tostring(PlayerId), \"PlayerData\" .. datastoreVersion .. Suffix .. suffix) -- we need another programmer\n\n\tlocal DSSuccess, DSError = pcall(function()\n\t\tDataStore:SetAsync(tostring(playerSaveFileData.timestamp), playerSaveFileData)\n\tend)\n\n\tif not DSSuccess then\n\t\twarn(\"DS update Failed!\")\n\t\treturn false, \"(DS): \" .. DSError\n\tend\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\t-- GODS\n\tlocal suffix = (game.ReplicatedStorage:FindFirstChild(\"mirrorWorld\") and \"mirror\") or \"\"\n\tlocal GlobalOrderedDataStore = DataStoreService:GetOrderedDataStore(tostring(PlayerId), \"GlobalPlayerSaveTimes\" .. datastoreVersion .. suffix)\n\n\tlocal GODSSuccess, GODSError = pcall(function()\n\t\tGlobalOrderedDataStore:SetAsync(\"s\" .. tostring(playerGlobalData.version), playerGlobalData.version)\n\tend)\n\n\tif not GODSSuccess then\n\t\twarn(\"GODS update Failed!\")\n\t\treturn false, \"(GODS): \" .. GODSError\n\tend\n\t--------------------------------------------------------------------------------------------------------------------------------------------------\n\n\treturn true, nil, playerGlobalData.version\nend\n\nif PERFORMING_STUDIO_PLAYER_DATA_MODIFICATION then\n\tdelay(10, function()\n\t\t-- player setup\n\t\tlocal fakePlayer = {\n\t\t\tuserId = 133827873,\n\t\t\tUserId = 133827873,\n\t\t\tFindFirstChild = function()\n\t\t\t\treturn {}\n\t\t\tend,\n\t\t}\n\n\t\t-- data loading\n\t\tlocal success, data = __intGetPlayerSaveFileData(fakePlayer, 1)\n\n\t\t-- data modification here\n\t\tdata.level = 30\n\n\t\t-- data saving here\n\t\tlocal success, reason, version = __intUpdatePlayerSaveFileData(fakePlayer.UserId, 1, data, data.globalData)\n\t\tprint(string.format(\n\t\t\t\"data modification for UserId %d was %s because %s. Current version: %d.\",\n\t\t\tfakePlayer.UserId,\n\t\t\tsuccess and \"successful\" or \"unsuccessful\",\n\t\t\tsuccess and \"nothing went wrong\" or reason,\n\t\t\tversion\n\t\t))\n\tend)\nend\n\n\n\nlocal function int__modifyPlayerSaveFileIncludeDefaults(player, saveFileNumber, playerSaveFileData)\n\n\tplayerSaveFileData.accessories = playerSaveFileData.accessories or {}\n\n\n\tplayerSaveFileData.professions = playerSaveFileData.professions or {}\n\n\tplayerSaveFileData.statistics \t= playerSaveFileData.statistics or {}\n\t\tplayerSaveFileData.statistics.str = playerSaveFileData.statistics.str or 0\n\t\tplayerSaveFileData.statistics.int = playerSaveFileData.statistics.int or 0\n\t\tplayerSaveFileData.statistics.dex = playerSaveFileData.statistics.dex or 0\n\t\tplayerSaveFileData.statistics.vit = playerSaveFileData.statistics.vit or 0\n\n\t\tplayerSaveFileData.statistics.modifierData \t\t= playerSaveFileData.statistics.modifierData or {}\n\t\tplayerSaveFileData.statistics.pointsAssigned \t= playerSaveFileData.statistics.pointsAssigned or 0\n\t\tplayerSaveFileData.statistics.pointsUnassigned \t= playerSaveFileData.statistics.pointsUnassigned or 0\n\n\tplayerSaveFileData.statusEffects \t= playerSaveFileData.statusEffects or {}\n\tplayerSaveFileData.flags \t\t\t= playerSaveFileData.flags or {\n\t\tdataRecovery11_14 = true,\n\t}\n\n\tplayerSaveFileData.gold \t\t= playerSaveFileData.gold or 10\n\tplayerSaveFileData.exp \t\t\t= playerSaveFileData.exp or 0\n\tplayerSaveFileData.level \t\t= playerSaveFileData.level or 1\n\n\tplayerSaveFileData.internalData = playerSaveFileData.internalData or {}\n\tplayerSaveFileData.internalData.suspicion = playerSaveFileData.internalData.suspicion or 0\n\n\tif not playerSaveFileData.abilitiesReset2 then\n\t\tplayerSaveFileData.abilities = {}\n\t\tplayerSaveFileData.hotbar = {}\n\n\t\tplayerSaveFileData.abilitiesReset2 = true\n\tend\n\n\t-- TODO: REMOVE ON 01/12/2018 [DD/MM/YYYY]\n\tif not playerSaveFileData.flags.stealthRevoke then\n\t\tfor i, abilitySlotData in pairs(playerSaveFileData.abilities) do\n\t\t\tif abilitySlotData.id == 15 and not playerSaveFileData.abilityBooks.Admin then\n\t\t\t\ttable.remove(playerSaveFileData.abilities, i)\n\n\t\t\t\tif playerSaveFileData.abilityBooks.Hunter then\n\t\t\t\t\tplayerSaveFileData.abilityBooks.Hunter.pointsAssigned = playerSaveFileData.abilityBooks.Hunter.pointsAssigned - (abilitySlotData.rank or 0)\n\t\t\t\tend\n\n\t\t\telseif playerSaveFileData.abilityBooks.Admin then\n\t\t\tend\n\t\tend\n\n\t\tplayerSaveFileData.flags.stealthRevoke = true\n\tend\n\n\tif not playerSaveFileData.flags.fixcolors2 then\n\t\tif playerSaveFileData.accessories then\n\t\t\tif playerSaveFileData.accessories.skinColor then\n\t\t\t\tplayerSaveFileData.accessories.skinColorId = playerSaveFileData.accessories.skinColor\n\t\t\tend\n\t\t\tif playerSaveFileData.accessories.faceColor then\n\t\t\t\tplayerSaveFileData.accessories.faceColorId = playerSaveFileData.accessories.faceColor\n\t\t\tend\n\t\t\tif playerSaveFileData.accessories.hairColor then\n\t\t\t\tplayerSaveFileData.accessories.hairColorId = playerSaveFileData.accessories.hairColor\n\t\t\tend\n\t\tend\n\t\tplayerSaveFileData.flags.fixcolors2 = true\n\tend\n\n\tif not playerSaveFileData.flags.fixcolors3 then\n\t\tif playerSaveFileData.accessories then\n\t\t\tif playerSaveFileData.accessories.shirtColor then\n\t\t\t\tplayerSaveFileData.accessories.shirtColorId = playerSaveFileData.accessories.shirtColor\n\t\t\tend\n\t\tend\n\t\tplayerSaveFileData.flags.fixcolors3 = true\n\tend\n\n\tplayerSaveFileData.accessories.shirtColorId = playerSaveFileData.accessories.shirtColorId or 1\n\n\n\n\tif TESTING then\n\t\tplayerSaveFileData.gold \t= 10000\n\t\tplayerSaveFileData.level\t= 10\n\tend\n\n\tplayerSaveFileData.keyPreferences \t= playerSaveFileData.keyPreferences or nil\n\tplayerSaveFileData.health \t\t\t= playerSaveFileData.health or nil\n\tplayerSaveFileData.savePosition \t= playerSaveFileData.savePosition or nil\n\tplayerSaveFileData.class \t\t\t= playerSaveFileData.class or \"Adventurer\"\n\n\tplayerSaveFileData.subclass \t\t= playerSaveFileData.subclass or nil\n\n\tplayerSaveFileData.treasureChests\t= playerSaveFileData.treasureChests or {}\n\n\tplayerSaveFileData.quests \t\t\t= playerSaveFileData.quests or {}\n\t\tplayerSaveFileData.quests.completed = playerSaveFileData.quests.completed or {}\n\t\tplayerSaveFileData.quests.active \t= playerSaveFileData.quests.active or {}\n\n\n\n\t-------------------\n\n\n\tplayerSaveFileData.userSettings = playerSaveFileData.userSettings or {}\n\n\tplayerSaveFileData.sessionCount = playerSaveFileData.sessionCount or 1\n\n\n\t-- information related to player's save, how to handle errors, etc\n\tplayerSaveFileData.nonSerializeData = {}\n\t\tplayerSaveFileData.nonSerializeData.saveFileNumber \t\t= saveFileNumber\n\t\tplayerSaveFileData.nonSerializeData.playerPointer \t\t= player\n\t\tplayerSaveFileData.nonSerializeData.isGlobalPVPEnabled \t= false\n\t\tplayerSaveFileData.nonSerializeData.whitelistPVPEnabled = {}\n\t\tplayerSaveFileData.nonSerializeData.temporaryEquipment \t= {}\n\t\tplayerSaveFileData.nonSerializeData.perksActivated \t\t= {}\nend\n\nfunction module:getLatestSaveVersion(player)\n\tlocal playerId = player.UserId\n\n\tlocal globalOrderedStore = DataStoreService:GetOrderedDataStore(tostring(playerId), \"GlobalPlayerSaveTimes\" .. datastoreVersion)\n\tlocal pages = globalOrderedStore:GetSortedAsync(false, 1)\n\n\tif not pages then\n\t\treturn 0\n\tend\n\n\tpages = pages:GetCurrentPage()\n\n\tif not pages[1] then\n\t\treturn 0\n\tend\n\n\treturn pages[1].value\nend\n\nfunction module:getPlayerSaveFileDataOlderThan(player, saveFileNumber, timestamp)\n\ttimestamp = tonumber(timestamp)\n\n\tprint(string.format(\"Attempting to find save data with timestamp older than %d.\", timestamp))\n\n\tlocal playerId = player.UserId\n\n\tlocal globalOrderedStore = DataStoreService:GetOrderedDataStore(tostring(playerId), \"GlobalPlayerSaveTimes\" .. datastoreVersion)\n\tlocal pages = globalOrderedStore:GetSortedAsync(false, 1)\n\n\tif not pages then\n\t\tprint(\"Failed to acquire pages for save times. Reverting to default behavior.\")\n\n\t\treturn self:getPlayerSaveFileData(player, saveFileNumber)\n\tend\n\n\tpages = pages:GetCurrentPage()\n\n\tif not pages[1] then\n\t\tprint(\"Player does not have any lastSaveId possibilities. Reverting to default behavior.\")\n\n\t\treturn self:getPlayerSaveFileData(player, saveFileNumber)\n\tend\n\n\tlocal lastSaveId = pages[1].value\n\tlastSaveId = tonumber(lastSaveId)\n\n\tlocal lastSaveOlderThanTimestamp\n\tlocal lastSaveKey = \"-slot\"..saveFileNumber\n\n\tlocal globalDataStore = DataStoreService:GetDataStore(tostring(playerId), \"GlobalPlayerData\" .. datastoreVersion)\n\n\tlocal id = lastSaveId\n\twhile id > 0 do\n\t\tlocal globalData = globalDataStore:GetAsync(tostring(id))\n\n\t\tlocal lastSaveTimestamp = tonumber(globalData.lastSaveTimestamp)\n\n\t\tprint(string.format(\n\t\t\t\"Testing id %d: timestamp %d < %d?\",\n\t\t\tid,\n\t\t\tlastSaveTimestamp,\n\t\t\ttimestamp\n\t\t))\n\n\t\tif lastSaveTimestamp <= timestamp then\n\t\t\tprint(\"Found id with older timestamp.\")\n\n\t\t\tlastSaveOlderThanTimestamp = globalData.lastSave[lastSaveKey]\n\n\t\t\t-- this save file didn't exist before this timestamp, it's new\n\t\t\tif not lastSaveOlderThanTimestamp then\n\t\t\t\tprint(\"This id does not have a file for this slot. Reverting to default behavior.\")\n\n\t\t\t\treturn self:getPlayerSaveFileData(player, saveFileNumber)\n\n\t\t\telse\n\t\t\t\tprint(\"Found save file for this id and this slot. Returning it.\")\n\n\t\t\t\tlocal success, playerData, message = self:getPlayerSaveFileData(player, saveFileNumber, lastSaveOlderThanTimestamp)\n\n\t\t\t\tif success then\n\t\t\t\t\tplayerData.globalData.version = lastSaveId\n\t\t\t\tend\n\n\t\t\t\treturn success, playerData, message\n\t\t\tend\n\t\tend\n\n\t\tid = id - 1\n\tend\n\n\tprint(\"Exhausted all lastSaveId possibilities. Reverting to default behavior.\")\n\n\treturn self:getPlayerSaveFileData(player, saveFileNumber)\nend\n\nfunction module:getPlayerSaveFileData(player, saveFileNumber, desiredVersion)\n\tlocal success, playerSaveFileData, errorMsg = __intGetPlayerSaveFileData(player, saveFileNumber, desiredVersion)\n\n\tif not success then\n\t\twarn(\"datastore-failure: \", player, errorMsg)\n\t\treturn success, playerSaveFileData, errorMsg\n\tend\n\n\tint__modifyPlayerSaveFileIncludeDefaults(player, saveFileNumber, playerSaveFileData)\n\n\treturn success, playerSaveFileData\nend\n\nfunction module:updatePlayerSaveFileData(playerId, playerSaveFileData)\n\n\t-- do not save data in testing environment\n\n\tif isInternal then\n\t\t-- return true\n\t\treturn true, nil, 0\n\tend\n\n\tplayerSaveFileData.newb = false\n\n\t-- Record Keeping\n\tlocal Slot = playerSaveFileData.nonSerializeData.saveFileNumber\n\n\tlocal Suffix = \"-slot\"..tostring(Slot)\n\n\tplayerSaveFileData.timestamp = playerSaveFileData.timestamp + 1\n\tplayerSaveFileData.globalData.version = playerSaveFileData.globalData.version + 1\n\n\tplayerSaveFileData.globalData.lastSave = playerSaveFileData.globalData.lastSave or {}\n\tplayerSaveFileData.globalData.lastSave[Suffix] = playerSaveFileData.timestamp\n\n\tplayerSaveFileData.globalData.saveSlotData = playerSaveFileData.globalData.saveSlotData or {}\n\n\tlocal lastLocation = game.ReplicatedStorage:FindFirstChild(\"lastLocationOverride\") and game.ReplicatedStorage.lastLocationOverride.Value or game.PlaceId\n\n\tif playerSaveFileData.lastLocationDeathOverride then\n\t\tlastLocation = playerSaveFileData.lastLocationDeathOverride\n\t\tplayerSaveFileData.lastLocationDeathOverride = nil\n\tend\n\n\tplayerSaveFileData.globalData.saveSlotData[Suffix] = {\n\t\tlevel = playerSaveFileData.level;\n\t\tclass = playerSaveFileData.class;\n\t\tlastLocation = lastLocation;\n\t\tequipment = playerSaveFileData.equipment;\n\t\taccessories = playerSaveFileData.accessories;\n\t\tcustomized = true;\n\t}\n\n\t-- Processing\n\tplayerSaveFileData = utilities.copyTable(playerSaveFileData)\n\n\tlocal nonSerializeData \t\t\t\t= playerSaveFileData.nonSerializeData\n\tplayerSaveFileData.nonSerializeData = nil\n\n\tlocal globalData = playerSaveFileData.globalData\n\n\tplayerSaveFileData.globalData = nil\n\n\t-- don't save lastPhysicalPosition if we're gonna get booted somewhere else\n\tif game:GetService(\"ReplicatedStorage\"):FindFirstChild(\"lastLocationOverride\") then\n\t\tplayerSaveFileData.lastPhysicalPosition = nil\n\tend\n\n\t-- Last Minute Data\n\tplayerSaveFileData.lastLocation = lastLocation\n\n\n\n\t-- Saving\n\treturn __intUpdatePlayerSaveFileData(playerId, Slot, playerSaveFileData, globalData)\n\nend\n\nfunction module:wipePlayerSaveFileData(player, currentPlayerData)\n\tlocal playerSaveFileData = {}\n\n\tint__modifyPlayerSaveFileIncludeDefaults(player, currentPlayerData.nonSerializeData.saveFileNumber, playerSaveFileData)\n\n\treturn module:updatePlayerSaveFileData(player.userId, playerSaveFileData)\nend\n\nfunction module.init(Modules)\n\tutilities = Modules.utilities\n\tnetwork = Modules.network\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/inputStorage.lua",
    "content": "-- Server-side interface for saving player input preferences\n\n\nlocal module = {}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal modules = require(replicatedStorage.modules)\nlocal network = modules.load(\"network\")\n\nmodule.priority = 3\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/contents/manager_ability.lua",
    "content": "local module = {}\n\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal AbilityLookup = require(ReplicatedStorage.abilityLookup)\n\nlocal abilityCooldownLookup = {}\nlocal castedAbilityGUIDs = {}\n--local cachedPlayerAbilityData = {}\n\nlocal latencyForgiveness = 0.25\nlocal maximumAbilityRenderDistance = 100\n\nlocal network\nlocal utilities\nlocal abilityUtilities\n\n\n-------------------------------------------------\n---------------@ Core Functions @----------------\n-------------------------------------------------\n\nlocal function onPlayerAdded(player)\n\tabilityCooldownLookup[player] = {}\n\tcastedAbilityGUIDs[player] = {}\n\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\tplayerData.abilities[1] = {\n\t\tlevel = 1;\n\t\texperience = 0;\n\t\tid = 1;\n\t}\n\n\tplayerData.abilities[2] = {\n\t\tlevel = 1;\n\t\texperience = 0;\n\t\tid = 2;\n\t}\n\n\tplayerData.nonSerializeData.setPlayerData(\"abilities\", playerData.abilities)\nend\n\nlocal function onPlayerRemoving(player)\n\tabilityCooldownLookup[player] = nil\nend\n\nlocal function validateAbilityGUID(player, abilityID, guid)\n\tif castedAbilityGUIDs[player] and castedAbilityGUIDs[player][abilityID] and castedAbilityGUIDs[player][abilityID][guid] then\n\t\treturn castedAbilityGUIDs[player][abilityID][guid]\n\tend\n\n\treturn false\nend\n\nlocal function resetAbilityCooldown(player, abilityID)\n\tif abilityCooldownLookup[player] ~= nil and abilityCooldownLookup[player][abilityID] ~= nil then\n\t\tabilityCooldownLookup[player][abilityID] = nil\n\tend\nend\n\nlocal function returnAbilityCooldown(player, abilityID)\n\tif not abilityCooldownLookup[player] then return nil end\n\tif not abilityCooldownLookup[player][abilityID] then return nil end\n\n\treturn abilityCooldownLookup[player][abilityID]\nend\n\n-------------------------------------------------\n-------------@ Bindable Functions @--------------\n-------------------------------------------------\n\n\n-------------------------------------------------\n--------------@ Remote Functions @---------------\n-------------------------------------------------\n\n--onUpdate Event\nlocal function changeAbilityState(caster, requestedState, executionData)\n\tlocal casterContainer = executionData.casterCharacter\n\tif not casterContainer or not casterContainer.PrimaryPart or not utilities.isEntityManifestValid(casterContainer.PrimaryPart) then return \"invalid_character\" end\n\n\tlocal serverExecuteTick = tick()\n\tlocal casterData = nil\n\n\tlocal player = game.Players:GetPlayerFromCharacter(casterContainer)\n\tif player then\n\t\tcasterData = network:invoke(\"getPlayerData\", player)\n\telse\n\t\t--Get Monsters Data Here\n\tend\n\n\tlocal abilityId = executionData.abilityId\n\tlocal guid = executionData.abilityGuid\n\tlocal abilityData = AbilityLookup[abilityId]\n\n\tif not casterData or not abilityData then return \"failed\" end\n\n\t----@ BEGIN STATE @----\n\tif requestedState == \"begin\" then\n\t\tif castedAbilityGUIDs[player][abilityId] == nil then\n\t\t\texecutionData[\"state\"] = requestedState\n\t\t\texecutionData[\"guid\"] = guid\n\n\t\t\tlocal manaCost = abilityData.statistics.manaCost\n\t\t\tlocal cooldown = abilityData.statistics.cooldown\n\n\t\t\t--Get player level, add the exponent and calculate ability modifier TODO\n\t\t\texecutionData[\"abilityData\"] = abilityData\n\n\t\t\tlocal increasingStat, calculatedStat = abilityUtilities.calculateStats(casterData, abilityId)\n\t\t\tif increasingStat and calculatedStat then\n\t\t\t\texecutionData[\"abilityData\"][increasingStat] = calculatedStat\n\t\t\tend\n\n\t\t\tlocal canCast, error = abilityUtilities.canPlayerCast(player, casterData, abilityId)\n\t\t\tif not canCast then\n\t\t\t\treturn canCast, error\n\t\t\tend\n\n\t\t\tif castedAbilityGUIDs[player][abilityId] and castedAbilityGUIDs[player][abilityId][guid] then return false, \"already_begun\" end\n\t\t\tcastedAbilityGUIDs[player][abilityId] = {}\n\t\t\tcastedAbilityGUIDs[player][abilityId][guid] = true\n\n\t\t\t----@ ABILITY CAN BE CASTED @----\n\n\t\t\t--Remove ManaCost from Players Mana\n\t\t\tplayer.Character.PrimaryPart.mana.Value = (player.Character.PrimaryPart.mana.Value - manaCost)\n\n\t\t\t--Calculate Cooldown Tick from Server vs Client\n\t\t\tlocal clientTick = executionData.castTick\n\t\t\tabilityCooldownLookup[player][abilityId] = serverExecuteTick\n\n\t\t\t-- Cast Ability Server Side\n\t\t\tabilityData:execute_server()\n\n\t\t\t--Send Ability Cast to Clients\n\t\t\tlocal nearbyPlayers = abilityUtilities.returnNearbyPlayers(player.Character.PrimaryPart.CFrame, maximumAbilityRenderDistance)\n\t\t\tif nearbyPlayers then\n\t\t\t\tnetwork:fireClients(\"replicateAbilityLocally\", nearbyPlayers, executionData, false)\n\t\t\tend\n\n\t\t\t--@ ABILITY BEGIN ENDED @--\n\t\telse\n\t\t\twarn(\"Ability already casted, player is attempting to re-cast from remote. Possible Exploiter or False Positive\")\n\t\tend\n\n\t----@ UPDATE STATE @----\n\telseif requestedState == \"update\" then\n\t\tif validateAbilityGUID(player, abilityId, guid) then\n\t\t\texecutionData[\"abilityData\"] = abilityData\n\n\t\t\tlocal increasingStat, calculatedStat = abilityUtilities.calculateStats(casterData, abilityId)\n\t\t\tif increasingStat and calculatedStat then\n\t\t\t\texecutionData[\"abilityData\"][increasingStat] = calculatedStat\n\t\t\tend\n\n\t\t\t-- Cast Ability Server Side\n\t\t\tabilityData:execute_server_update()\n\n\t\t\t--Send Ability Cast to Clients\n\t\t\tlocal nearbyPlayers = utilities.returnNearbyPlayers(player.Character.PrimaryPart.CFrame, maximumAbilityRenderDistance)\n\t\t\tif nearbyPlayers then\n\t\t\t\tnetwork:fireClients(\"replicateAbilityUpdateLocally\", nearbyPlayers, executionData, false)\n\t\t\tend\n\t\telse\n\t\t\treturn false, \"invalid_guid\"\n\t\tend\n\n\t----@ END STATE @----\n\telseif requestedState == \"end\" then\n\t\tif castedAbilityGUIDs[player][abilityId] ~= nil and castedAbilityGUIDs[player][abilityId][guid] ~= nil then\n\t\t\tcastedAbilityGUIDs[player][abilityId] = nil\n\t\tend\n\tend\nend\n-------------------------------------------------\n----------------@ Main Function @----------------\n-------------------------------------------------\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tutilities = Modules.utilities\n\tabilityUtilities = Modules.abilityUtilities\n\n\t--Register Player Added and Remove Functions Defined Above\n\tnetwork:connect(\"playerDataLoaded\", \"Event\", onPlayerAdded)\n\tgame.Players.PlayerRemoving:Connect(onPlayerRemoving)\n\n\t--Register all functions as network events/functions\n\tnetwork:create(\"requestAbilityStateUpdate\", \"RemoteEvent\", \"OnServerEvent\", changeAbilityState)\n\tnetwork:create(\"validateAbilityGUID\", \"BindableFunction\", \"OnInvoke\", validateAbilityGUID)\n\tnetwork:create(\"resetAbilityCooldown\", \"BindableEvent\", \"Event\", resetAbilityCooldown)\n\tnetwork:create(\"requestAbilityCooldown\", \"RemoteFunction\", \"OnServerInvoke\", returnAbilityCooldown)\n\tnetwork:create(\"returnAbilityCooldown\", \"BindableFunction\", \"OnInvoke\", returnAbilityCooldown)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_bounty.lua",
    "content": "local module = {}\n\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal network\nlocal levels\n\nlocal monsterLookup = require(ReplicatedStorage:WaitForChild(\"monsterLookup\"))\n\nlocal function playerRequest_claimBounty(player, monsterName)\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\tif not playerData then\n\t\treturn false, \"PlayerData not found.\"\n\tend\n\tif not player:FindFirstChild(\"bountyHunter\") then\n\t\treturn false, \"Not a bounty hunter.\"\n    end\n\n\tlocal monster = monsterLookup[monsterName]\n\tif monster then\n\t\tlocal page = monster.monsterBookPage\n\t\tlocal bountyData = playerData.bountyBook\n\t\tlocal monsterData = bountyData[monsterName]\n\t\tif page and monsterData then\n\t\t\tlocal bountyPageInfo = levels.bountyPageInfo[tostring(page)]\n\t\t\tlocal lastBounty = monsterData.lastBounty or 0\n\t\t\tlocal bountyInfo = bountyPageInfo[lastBounty + 1]\n\t\t\tif bountyInfo and monsterData.kills >= bountyInfo.kills then\n\t\t\t\t-- this line is duplicated in client monsterBook ui module\n\t\t\t\tlocal money = math.floor(levels.getBountyGoldReward(bountyInfo, monster))\n\t\t\t\tlocal rewards = {}\n\t\t\t\tlocal wasRewarded = network:invoke(\"tradeItemsBetweenPlayerAndNPC\", player, {}, 0, rewards, money, \"etc:bounty\")\n\t\t\t\tif wasRewarded then\n\t\t\t\t\tmonsterData.lastBounty = lastBounty + 1\n\t\t\t\t\tplayerData.nonSerializeData.setPlayerData(\"bountyBook\", bountyData)\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\t\treturn false, \"No room in inventory.\"\n\t\t\tend\n\t\t\treturn false, \"No bounty available.\"\n\t\tend\n\t\treturn false, \"Invalid monster.\"\n\tend\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tlevels = Modules.levels\n\n\tnetwork:create(\"playerRequest_claimBounty\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_claimBounty)\nend\n\n\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_challenge.lua",
    "content": "local module = {}\n\nlocal HttpService = game:GetService(\"HttpService\")\nlocal network\n\n--[[\n\tchallengeRequestData {}\n\t\tinstance challenger\n\t\tinstance playerChallenged\n\n\ttradeSessionData {}\n\t\tstring guid\n\t\tstring state\n\n\t\tplayerTradeSessionData playerTradeSessionData_player1\n\t\tplayerTradeSessionData playerTradeSessionData_player2\n--]]\n\nlocal pendingChallenge_guids = {}\n\nlocal function clearPreviousChallengeRequests(player)\n\tfor guid, challengeRequestData in pairs(pendingChallenge_guids) do\n\t\tif challengeRequestData.challenger == player or challengeRequestData.playerChallenged == player then\n\t\t\tpendingChallenge_guids[guid] = nil\n\t\tend\n\tend\nend\n\nlocal function playerRequest_acceptChallengeRequest(player, guid)\n\n\tif pendingChallenge_guids[guid] then\n\t\t-- cancel all current trades involving player except for this trade.\n\t\tlocal challengeRequestData = pendingChallenge_guids[guid]\n\n\t\tclearPreviousChallengeRequests(challengeRequestData.challenger)\n\t\tclearPreviousChallengeRequests(challengeRequestData.playerChallenged)\n\n\n\t\t-- INVALIDATE THE GUI\n\t\tpendingChallenge_guids[guid] = nil\n\n\t\tif not challengeRequestData.challenger.Parent or not challengeRequestData.playerChallenged.Parent then\n\t\t\treturn false\n\t\tend\n\n\t\tif challengeRequestData.wager then\n\t\t\t-- ensure they both have the gold to do the challenge\n\t\t\tlocal playerData_challenger \t\t= network:invoke(\"getPlayerData\", challengeRequestData.challenger)\n\t\t\tlocal playerData_playerChallenged \t= network:invoke(\"getPlayerData\", challengeRequestData.playerChallenged)\n\t\t\tif playerData_challenger.gold < challengeRequestData.wager or playerData_playerChallenged.gold < challengeRequestData.wager then\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\n\t\t-- do something INSANE!!!!!!!!!!!!!!!!!!!!!!!!!!! (let them fight!)\n\t\tnetwork:invoke(\"requestPVPWhitelistPlayer_server\", challengeRequestData.challenger, challengeRequestData.playerChallenged)\n\t\tnetwork:invoke(\"requestPVPWhitelistPlayer_server\", challengeRequestData.playerChallenged, challengeRequestData.challenger)\n\n\t\tnetwork:fireClient(\"alertPlayerNotification\", challengeRequestData.challenger, {text = \"Your challenge with \" .. challengeRequestData.playerChallenged.Name .. \" was accepted, FIGHT!\"})\n\t\tnetwork:fireClient(\"alertPlayerNotification\", challengeRequestData.playerChallenged, {text = \"You've accepted \" .. challengeRequestData.challenger.Name .. \"'s challenge! Fight!\"})\n\n\t\treturn true\n\tend\n\n\treturn false, \"invalid or inactive guid\"\nend\n\nlocal invitationCD = {}\nlocal function playerRequest_requestChallenge(challenger, playerChallenged, wager)\n\tif challenger and playerChallenged and challenger ~= playerChallenged and (not wager or (type(wager) == \"number\" and wager > 0)) then\n\t\tif true then\n\t\t\tif not invitationCD[challenger] or (tick() - invitationCD[challenger] > 3) then\n\t\t\t\tinvitationCD[challenger] = tick()\n\n\t\t\t\tlocal guid = HttpService:GenerateGUID(false)\n\n\t\t\t\tlocal challengeRequestData = {\n\t\t\t\t\tchallenger \t\t\t= challenger;\n\t\t\t\t\tplayerChallenged \t= playerChallenged;\n\t\t\t\t\twager \t\t\t\t= wager;\n\t\t\t\t}\n\n\t\t\t\tpendingChallenge_guids[guid] = challengeRequestData\n\n\t\t\t\t-- under some circumstances, duel requests are accepted instantly\n\t\t\t\tif (game.PlaceId == 4653017449) or (game.PlaceId == 2061558182) then\n\t\t\t\t\tlocal guildId = game.ReplicatedStorage:FindFirstChild(\"guildId\")\n\t\t\t\t\tif guildId then\n\t\t\t\t\t\tguildId = guildId.Value\n\n\t\t\t\t\t\tlocal challengerGuildId = challenger:FindFirstChild(\"guildId\")\n\t\t\t\t\t\tlocal challengedGuildId = playerChallenged:FindFirstChild(\"guildId\")\n\t\t\t\t\t\tif challengerGuildId and challengedGuildId then\n\t\t\t\t\t\t\tif challengerGuildId.Value == guildId then\n\t\t\t\t\t\t\t\tif challengedGuildId.Value == guildId then\n\t\t\t\t\t\t\t\t\t-- compare ranks\n\t\t\t\t\t\t\t\t\tlocal challengerData = network:invoke(\"getGuildMemberData\", challenger, guildId)\n\t\t\t\t\t\t\t\t\tlocal challengedData = network:invoke(\"getGuildMemberData\", playerChallenged, guildId)\n\t\t\t\t\t\t\t\t\tif\n\t\t\t\t\t\t\t\t\t\tchallengerData and\n\t\t\t\t\t\t\t\t\t\tchallengedData and\n\t\t\t\t\t\t\t\t\t\tchallengerData.rank and\n\t\t\t\t\t\t\t\t\t\tchallengedData.rank and\n\t\t\t\t\t\t\t\t\t\tnetwork:invoke(\"getRankNumberFromRank\", challengerData.rank) > network:invoke(\"getRankNumberFromRank\", challengedData.rank)\n\t\t\t\t\t\t\t\t\tthen\n\t\t\t\t\t\t\t\t\t\t-- a member of this guild has challenged a lower-ranked member of this guild to a duel, instantly accept\n\t\t\t\t\t\t\t\t\t\tplayerRequest_acceptChallengeRequest(playerChallenged, guid)\n\t\t\t\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t-- a member of this hall has challenged a non-member, instantly accept\n\t\t\t\t\t\t\t\t\tplayerRequest_acceptChallengeRequest(playerChallenged, guid)\n\t\t\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telseif challengerGuildId and (not challengedGuildId) then\n\t\t\t\t\t\t\tif challengerGuildId.Value == guildId then\n\t\t\t\t\t\t\t\t-- a member of this hall has challenged a non-member, instantly accept\n\t\t\t\t\t\t\t\tplayerRequest_acceptChallengeRequest(playerChallenged, guid)\n\t\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tnetwork:fireClient(\"signal_playerChallengeRequest\", playerChallenged, challenger, guid)\n\n\t\t\t\treturn true\n\t\t\telse\n\t\t\t\treturn false, \"stop sending challenges too fast\"\n\t\t\tend\n\t\tend\n\telseif challenger == playerChallenged then\n\t\treturn false, \"you can't challenge yourself.\"\n\tend\n\n\treturn false, \"invalid request\"\nend\n\nlocal function onPlayerCharacterDied(player)\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\tif playerData then\n\t\tfor _, whitelistPVPPlayer in pairs(playerData.nonSerializeData.whitelistPVPEnabled) do\n\t\t\tnetwork:invoke(\"revokePVPWhitelistPlayer_server\", player, whitelistPVPPlayer)\n\t\t\tnetwork:invoke(\"revokePVPWhitelistPlayer_server\", whitelistPVPPlayer, player)\n\t\tend\n\tend\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tnetwork:create(\"playerRequest_requestChallenge\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_requestChallenge)\n\tnetwork:create(\"playerRequest_acceptChallengeRequest\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_acceptChallengeRequest)\n\n\tnetwork:create(\"signal_playerChallengeRequest\", \"RemoteEvent\")\n\tnetwork:create(\"signal_playerChallengeState\", \"RemoteEvent\")\n\n\tnetwork:connect(\"playerCharacterDied\", \"Event\", onPlayerCharacterDied)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_chat.lua",
    "content": "local module = {}\nlocal textService = game:GetService(\"TextService\")\nlocal network\n\nlocal function onPlayerChatted(player, message)\n\tif message == \"\" then\n\t\treturn\n\tend\n\n\tlocal successForTextFilterResult, textFilterResult = pcall(function() textService:FilterStringAsync(message, player.userId) end)\n\tif successForTextFilterResult then\n\t\tfor i, oPlayer in pairs(game.Players:GetPlayers()) do\n\t\t\tif oPlayer ~= player then\n\t\t\t\tlocal success, filterMessage = pcall(function() textFilterResult:GetChatForUserAsync(oPlayer.userId) end)\n\t\t\t\tif success then\n\t\t\t\t\tnetwork:fireClient(\"playerChatted\", oPlayer, player.Name, filterMessage)\n\t\t\t\telse\n\t\t\t\t\tnetwork:fireClient(\"playerChatted\", oPlayer, nil, \"A message from \" .. player.Name .. \" failed to send.\")\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tnetwork:create(\"playerChatted\", \"RemoteEvent\", \"OnServerEvent\", onPlayerChatted)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_crafting.lua",
    "content": "local module = {}\n\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal itemLookup = require(ReplicatedStorage:WaitForChild(\"itemData\"))\n\nlocal network\n\nlocal function playerRequest_craftItem(player, itemId)\n\tlocal item = itemLookup[itemId]\n\tif item and item.recipe then\n\t\tlocal success, reason = network:invoke(\"tradeItemsBetweenPlayerAndNPC\",\n\t\t\tplayer,\n\t\t\titem.recipe,\n\t\t\t0,\n\t\t\t{{id = item.id; stacks = 1}},\n\t\t\t0,\n\t\t\t\"etc:crafting\"\n\t\t)\n\t\treturn success, reason\n\tend\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tnetwork:create(\"playerRequest_craftItem\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_craftItem)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_damage.lua",
    "content": "local module = {}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal network\nlocal placeSetup\nlocal utilities\nlocal mapping\nlocal levels\nlocal damage\nlocal detection\nlocal configuration\nlocal projectile\nlocal events\n\nlocal itemLookupContainer = replicatedStorage.itemData\nlocal itemLookup = require(itemLookupContainer)\nlocal itemFolderLookup = replicatedStorage.assets:WaitForChild(\"items\")\nlocal perkLookup = require(replicatedStorage.perkLookup)\nlocal abilityLookup = require(replicatedStorage.abilityLookup)\nlocal monsterLookup = require(replicatedStorage.monsterLookup)\n\nlocal entityManifestCollectionFolder\n\nlocal rand = Random.new()\nlocal critChanceRandom = Random.new()\nlocal dodgeChanceRandom = Random.new()\n\nlocal monsterDamageValidationData = {}\nlocal weaponValidationData = {}\nlocal playerDamageAnimationState = {}\nlocal arrowsShotByPlayers = {}\nlocal playerAbilityHitData = {}\nlocal magicBallsByPlayer = {}\n\nlocal WEAPON_TYPES_TO_SCAN \t= {\"dagger\"; \"staff\"; \"sword\"; \"greatsword\"; \"dual\"; \"swordAndShield\"}\nlocal SAMPLE_POINTS_TO_TAKE = 5\n\n-- how long client has to call for damage of an arrow\n-- note: lifetime of projectile is 3 sec, but allow some latency!\nlocal ARROW_DATA_MAX_LIFETIME = 3.5\n\n-- time players have to chain another slash\nlocal SLASH_CHAIN_WINDOW \t= 0.3\n\n-- 125 ms forgiveness\nlocal DAMAGE_ANIMATION_REQUEST_MISMATCH_LATENCY_FORGIVENESS = 0.125\n\nlocal CRIT_DAMAGE_MULTIPLIER = 2\n\n-- base value to decide how effective defensive stats are (VIT + DEX)\nlocal DEFENSE_MODULATOR = 100\n\nlocal getDamageMitigationByVIT do\n\t-- note for the future: THIS MUST BE IN ORDER (INDEX WISE) BY ORDER OF THRESHOLDS FROM GREATEST TO LEAST!!!!\n\tlocal thresholds = {\n\t\t{threshold = 1.0; value = 0.0 / 3};\n\t\t{threshold = 0.4; value = 0.5 / 3};\n\t\t{threshold = 0.2; value = 1.0 / 3};\n\t\t{threshold = 0.1; value = 2.0 / 3}\n\t}\n\n\tlocal function getThresholdForRatio(ratio)\n\t\tlocal currThreshold\n\t\tfor i, mitigationData in ipairs(thresholds) do\n\t\t\tif not currThreshold or ratio <= mitigationData.threshold then\n\t\t\t\tcurrThreshold \t= mitigationData.threshold\n\t\t\telse\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\treturn currThreshold\n\tend\n\n\tlocal function getNextThreshold(threshold)\n\t\tfor i, mitigationData in ipairs(thresholds) do\n\t\t\tif mitigationData.threshold == threshold then\n\t\t\t\treturn thresholds[i + 1] and thresholds[i + 1].threshold\n\t\t\tend\n\t\tend\n\n\t\treturn nil\n\tend\n\n\tlocal function getPreviousThreshold(threshold)\n\t\tfor i, mitigationData in ipairs(thresholds) do\n\t\t\tif mitigationData.threshold == threshold then\n\t\t\t\treturn thresholds[i - 1] and thresholds[i - 1].threshold\n\t\t\tend\n\t\tend\n\n\t\treturn nil\n\tend\n\n\tfunction getDamageMitigationByVIT(currentHealth, maxHealth, damageBeingDone, vit)\n\t\tlocal startRatio = math.clamp(currentHealth / maxHealth, 0, 1)\n\t\t\tlocal startRatioThreshold = getThresholdForRatio(startRatio)\n\t\tlocal damageRatio = math.clamp((currentHealth - damageBeingDone) / maxHealth, 0, 1)\n\t\t\tlocal damageRatioThreshold = getThresholdForRatio(damageRatio)\n\n\t\tlocal healthAfterDamage = math.clamp(currentHealth - damageBeingDone, 0, maxHealth)\n\n\t\tlocal thresholdsPassed = {}\n\t\tfor i, mitigationData in ipairs(thresholds) do\n\t\t\tif mitigationData.threshold <= startRatioThreshold and mitigationData.threshold >= damageRatioThreshold then\n\t\t\t\ttable.insert(thresholdsPassed, {threshold = mitigationData.threshold; damageMarker = mitigationData.threshold * maxHealth; value = mitigationData.value})\n\t\t\tend\n\t\tend\n\n\t\tlocal healthRemaining \t= currentHealth\n\t\tlocal damageRemaining \t= damageBeingDone\n\t\tlocal damageMitigation \t= 0\n\t\tlocal vitRemaining \t\t= vit\n\n\t\tfor i = #thresholdsPassed, 1, -1 do\n\t\t\tlocal mitigationData = thresholdsPassed[i]\n\n\t\t\tif vitRemaining > 0 then\n\t\t\t\tif i == #thresholdsPassed then\n\t\t\t\t\t-- this is processed first since we're doing it reverse order\n\t\t\t\t\tlocal healthInThisThreshold = math.abs(maxHealth * mitigationData.threshold - (currentHealth - damageBeingDone))\n\t\t\t\t\tlocal vitConsumed = math.clamp(healthInThisThreshold / mitigationData.value, 0, vitRemaining)\n\n\t\t\t\t\tvitRemaining \t\t= vitRemaining - vitConsumed\n\t\t\t\t\tdamageMitigation \t= damageMitigation + vitConsumed * mitigationData.value\n\t\t\t\telseif i == 1 then\n\t\t\t\t\t-- this is processed last since we're doing reverse order\n\t\t\t\t\tlocal nextThreshold = getNextThreshold(mitigationData.threshold)\n\t\t\t\t\tlocal healthInThisThreshold = currentHealth - maxHealth * nextThreshold\n\t\t\t\t\tlocal vitConsumed = math.clamp(healthInThisThreshold / mitigationData.value, 0, vitRemaining)\n\n\t\t\t\t\tvitRemaining \t\t= vitRemaining - vitConsumed\n\t\t\t\t\tdamageMitigation \t= damageMitigation + vitConsumed * mitigationData.value\n\t\t\t\telse\n\t\t\t\t\tlocal nextThreshold = getNextThreshold(mitigationData.threshold)\n\t\t\t\t\tlocal healthInThisThreshold = mitigationData.threshold * maxHealth - nextThreshold * maxHealth\n\t\t\t\t\tlocal vitConsumed = math.clamp(healthInThisThreshold / mitigationData.value, 0, vitRemaining)\n\n\t\t\t\t\tvitRemaining \t\t= vitRemaining - vitConsumed\n\t\t\t\t\tdamageMitigation \t= damageMitigation + vitConsumed * mitigationData.value\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tlocal damageBeingDonePostMitigation = math.clamp(damageBeingDone - damageMitigation, 0, math.huge)\n\n\t\treturn damageMitigation\n\tend\nend\n\nlocal perksMaid = {} do\n\tfunction perksMaid:isEquipmentPerkActivable(playerData, id)\n\n\tend\n\n\tfunction perksMaid:flagPerkAsActivated(player, id)\n\n\tend\nend\n\n\n\n-- called when killing damage is applied to any entity\nlocal function processEntityKillingBlow(serverHitbox, killer, damageData)\n\tlocal killedMonster\n\tlocal killedPlayer\n\tif serverHitbox.entityType.Value == \"monster\" then\n\t\tkilledMonster = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox)\n\telseif serverHitbox.entityType.Value == \"character\" then\n\t\tkilledPlayer = game.Players:GetPlayerFromCharacter(serverHitbox.Parent)\n\tend\n\n\tif killer == nil then\n\t\treturn\n\tend\n\n\tlocal sourcePlayer\n\tif killer.entityType.Value == \"character\" then\n\t\tsourcePlayer = game.Players:GetPlayerFromCharacter(killer.Parent)\n\tend\n\n\tlocal sendNotification = true\n\n\tif killedPlayer or (killedMonster and (killedMonster.boss or killedMonster.resilient or killedMonster.giant or killedMonster.superGiant or killedMonster.gigaGiant)) then\n\n\t\tlocal possibleVerbs = {\"defeated\", \"felled\", \"purged\", \"vanquished\", \"ended\", \"wiped\", \"finished\"}\n\t\tif serverHitbox and serverHitbox:FindFirstChild(\"maxHealth\") and damageData.damage >= serverHitbox.maxHealth.Value * 0.5 then\n\t\t\tpossibleVerbs = {\"absolutely obliterated\", \"completely demolished\", \"undeniably destroyed\", \"OOF`d\"}\n\t\telseif damageData.damageType == \"magical\" then\n\t\t\tpossibleVerbs = {\"disintegrated\", \"melted\", \"burned to a crisp\", \"blasted\", \"vaporized\"}\n\t\telseif damageData.damageType == \"ranged\" then\n\t\t\tpossibleVerbs = {\"sniped\", \"popped\", \"silenced\", \"struck\"}\n\t\tend\n\n\t\tlocal verb = possibleVerbs[rand:NextInteger(1,#possibleVerbs)]\n\t\tverb = verb or \"defeated\"\n\n\t\tif damageData.isCritical then\n\t\t\tverb = \"critically \"..verb\n\t\tend\n\n\t\tif killer then\n\t\t\tlocal killerName = \"???\"\n\t\t\tif killer.entityType.Value == \"character\" then\n\t\t\t\tkillerName = killer.Parent.Name\n\t\t\telseif killer.entityType.Value == \"monster\" then\n\t\t\t\tkillerName = killer.Name\n\t\t\t\tlocal monster = network:invoke(\"getMonsterDataByMonsterManifest_server\", killer)\n\n\t\t\t\tif not monster then\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\tif monster.specialName then\n\t\t\t\t\tkillerName = monster.specialName\n\t\t\t\tend\n\t\t\t\tif monster.gigaGiant then\n\t\t\t\t\tkillerName = \"Colossal \" .. killerName\n\t\t\t\telseif monster.superGiant then\n\t\t\t\t\tkillerName = \"Super Giant \" .. killerName\n\t\t\t\telseif monster.giant then\n\t\t\t\t\tkillerName = \"Giant \" .. killerName\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif killer.state.Value == \"dead\" or killer.health.Value <= 0 or killer.Parent == nil then\n\t\t\t\tkillerName = \"the late \" .. killerName\n\t\t\tend\n\n\t\t\tlocal killedName\n\t\t\tif killedPlayer then\n\t\t\t\tkilledName = killedPlayer.Name\n\t\t\telseif killedMonster then\n\t\t\t\tkilledName = serverHitbox.Name\n\t\t\t\tif killedMonster.specialName then\n\t\t\t\t\tkilledName = killedMonster.specialName\n\t\t\t\tend\n\t\t\t\tif killedMonster.gigaGiant then\n\t\t\t\t\tkilledName = \"Colossal \" .. killedName\n\t\t\t\telseif killedMonster.superGiant then\n\t\t\t\t\tkilledName = \"Super Giant \" .. killedName\n\t\t\t\telseif killedMonster.giant then\n\t\t\t\t\tkilledName = \"Giant \" .. killedName\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal text = \"☠ \" .. killedName .. \" was \" .. verb .. \" by \"..killerName..\" ☠\"\n\n\t\t\t-- i just killed myself... should i be doing something different?\n\t\t\tif sourcePlayer and killedPlayer and sourcePlayer == killedPlayer then\n\t\t\t\tlocal deathTrapKillMessage = sourcePlayer:FindFirstChild(\"deathTrapKillMessage\")\n\t\t\t\tif deathTrapKillMessage then\n\t\t\t\t\tsendNotification = false\n\n\t\t\t\t\ttext = deathTrapKillMessage.Value\n\t\t\t\t\tdeathTrapKillMessage:Destroy()\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\tText = text,\n\t\t\t\tFont = Enum.Font.SourceSansBold,\n\t\t\t\tColor = killedPlayer and Color3.fromRGB(255, 130, 100) or Color3.fromRGB(0, 255, 170),\n\t\t\t})\n\t\tend\n\n\t\tif sendNotification and killer.entityType.Value == \"character\" and killedPlayer then\n\t\t\tnetwork:fire(\"playerKilledByPlayer\", killedPlayer, sourcePlayer, damageData)\n\t\t\tif game.PlaceId == 2103419922 then\n\t\t\t\t-- level up on kill (Fight to survive!)\n\t\t\t\tlocal killedData = network:invoke(\"getPlayerData\", killedPlayer)\n\t\t\t\tlocal gold = (killedData and killedData.gold or 0) + (killedData and killedData.level or 1) * 100\n\t\t\t\tlocal playerData = network:invoke(\"getPlayerData\", sourcePlayer)\n\t\t\t\tlocal expForNextLevel = levels.getEXPToNextLevel(playerData.level)\n\t\t\t\tplayerData.nonSerializeData.incrementPlayerData(\"exp\", expForNextLevel + 1)\n\t\t\t\tpcall(function()\n\t\t\t\t\tif playerData.level == 10 then\n\n\t\t\t\t\t\tgame.BadgeService:AwardBadge(sourcePlayer.userId, 2124528259)\n\n\t\t\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\t\t\t\tText = sourcePlayer.Name .. \" is in purgatory.\",\n\t\t\t\t\t\t\tFont = Enum.Font.SourceSansBold,\n\t\t\t\t\t\t\tColor = Color3.fromRGB(200, 200, 100),\n\t\t\t\t\t\t})\n\t\t\t\t\telseif playerData.level == 100 then\n\t\t\t\t\t\tgame.BadgeService:AwardBadge(sourcePlayer.userId, 2124528261)\n\n\t\t\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\t\t\t\tText = sourcePlayer.Name .. \" is ALIVE!\",\n\t\t\t\t\t\t\tFont = Enum.Font.SourceSansBold,\n\t\t\t\t\t\t\tColor = Color3.fromRGB(150, 255, 30),\n\t\t\t\t\t\t})\n\t\t\t\t\t\tsourcePlayer:Kick(\"YOU ARE ALIVE!\")\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\t\tplayerData.nonSerializeData.incrementPlayerData(\"gold\", gold)\n\t\t\tend\n\t\t\tnetwork:fireAllClients(\"signal_playerKilledByPlayer\", killedPlayer, sourcePlayer, damageData, verb)\n\t\tend\n\tend\nend\n\n\n\n-- monster is dealing damage to a player\n-- player is dealing damage to a player\n-- monster is dealing damage to a monster\n-- todo: clean this up, doesnt make sense what this handles.\nlocal function playerDamageRequest_server(sourcePlayer, serverHitbox, damageData)\n\tif not serverHitbox:FindFirstChild(\"health\") or not serverHitbox:FindFirstChild(\"maxHealth\") then return false end\n\n\t-- sourcePlayer, serverHitbox, damageToDeal, sourceType, sourceId\n\tif serverHitbox.health.Value > 0 and serverHitbox:FindFirstChild(\"killingBlow\") == nil then\n\t\tlocal player = game.Players:GetPlayerFromCharacter(serverHitbox.Parent)\n\t\tif player then\n\t\t\tevents:fireEventLocal(\"playerWillTakeDamage\", player, damageData)\n\t\tend\n\n\t\tevents:fireEventLocal(\"entityWillDealDamage\", sourcePlayer, serverHitbox, damageData)\n\n\t\tlocal newHealth = serverHitbox.health.Value - damageData.damage\n\n\t\tif newHealth <= 0 then\n\n\t\t\tlocal killerGUID = damageData.sourceEntityGUID\n--\t\t\tprint(\"$\",killerGUID)\n\t\t\tlocal killer = utilities.getEntityManifestByEntityGUID(killerGUID)\n--\t\t\tprint(\"$\", killer)\n\n\t\t\tlocal killingBlowTag \t= Instance.new(\"StringValue\")\n\t\t\tkillingBlowTag.Name \t= \"killingBlow\"\n\t\t\tkillingBlowTag.Value \t= \"damage\"\n\n\t\t\tlocal killingBlowSource = Instance.new(\"ObjectValue\")\n\t\t\tkillingBlowSource.Name \t= \"source\"\n\t\t\tkillingBlowSource.Value = killer\n\t\t\tkillingBlowSource.Parent = killingBlowTag\n\n\t\t\tkillingBlowTag.Parent = serverHitbox\n\t\t\tprocessEntityKillingBlow(serverHitbox, killer, damageData)\n\t\tend\n\n\t\tserverHitbox.health.Value = newHealth\n\n\t\tif serverHitbox.health.Value <= 0 then\n\t\t\tevents:fireEventLocal(\"entityDiedTakingDamage\", sourcePlayer, serverHitbox, damageData)\n\n\n\t\tend\n\n\t\tnetwork:fireAllClients(\"signal_damage\", serverHitbox, damageData)\n\tend\n\n\treturn true\nend\n\nlocal function isWithinBounds(value, b1, b2, atkspd)\n\tatkspd = atkspd or 1\n\n\treturn value / atkspd >= b1 / atkspd and value / atkspd <= b2 / atkspd\nend\n\n-- handles the processing for damageRequests made by non-bow weapons\n\n-- ber: removed for causing issues with fast attack speeds\n-- TODO: calculate a rolling average of damage requests and punish if its too high\n\nlocal function isPlayerEquipmentDamageRequestWithinAnimationDamageSequence(player, serverHitbox)\n\treturn true\n\t--[[\n\tlocal currentPlayerDamageAnimationState = playerDamageAnimationState[player]\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\tif currentPlayerDamageAnimationState then\n\t\tif weaponValidationData[currentPlayerDamageAnimationState.weaponType] then\n\t\t\tif not currentPlayerDamageAnimationState.damageBlacklist[serverHitbox] then\n\t\t\t\tlocal success = isWithinBounds(\n\t\t\t\t\ttick() - currentPlayerDamageAnimationState.timestamp,\n\t\t\t\t\tweaponValidationData[currentPlayerDamageAnimationState.weaponType][currentPlayerDamageAnimationState.state .. \"_startDamageSequence_time\"] - DAMAGE_ANIMATION_REQUEST_MISMATCH_LATENCY_FORGIVENESS,\n\t\t\t\t\tweaponValidationData[currentPlayerDamageAnimationState.weaponType][currentPlayerDamageAnimationState.state .. \"_stopDamageSequence_time\"] + DAMAGE_ANIMATION_REQUEST_MISMATCH_LATENCY_FORGIVENESS,\n\t\t\t\t\t1 + playerData.nonSerializeData.statistics_final.attackSpeed\n\t\t\t\t)\n\n\t\t\t\t-- blacklist damaging this hitbox\n\t\t\t\tif success then\n\t\t\t\t\tcurrentPlayerDamageAnimationState.damageBlacklist[serverHitbox] = true\n\t\t\t\tend\n\n\t\t\t\treturn success\n\t\t\tend\n\t\tend\n\telse\n\t\treturn true\n\tend\n\treturn false\n\t]]\nend\n\nlocal function isMagicBallDataUsableForDamageRequest(magicBallData, playerPositionAtDamageRequestTime, targetPositionAtDamageRequestTime)\n\tif tick() - magicBallData.timestamp >= ARROW_DATA_MAX_LIFETIME then return false end\n\n\tlocal playerPositionFromServerAtFiring = magicBallData.serverCharacterPosition\n\tlocal playerPositionFromClientAtFiring = magicBallData.executionData[\"cast-origin\"]\n\n\tlocal unitDirection, targetPositionFromClientPredictive = projectile.getUnitVelocityToImpact_predictiveByAbilityExecutionData(\n\t\tplayerPositionFromClientAtFiring,\n\t\tmagicBallData.sourceWeaponBaseData.projectileSeeed or 50,\n\t\tmagicBallData.executionData,\n\t\t0.0001\n\t)\n\n\tlocal degreesOffPredictedCourse = math.deg(math.acos(((targetPositionAtDamageRequestTime - playerPositionFromClientAtFiring).unit):Dot(unitDirection.unit)))\n\tlocal degreesOffPredictedCourse2 = math.deg(math.acos(((targetPositionFromClientPredictive - playerPositionFromClientAtFiring).unit):Dot(unitDirection.unit)))\n\tlocal degreesOffPredictedCourse3 = math.deg(math.acos(((targetPositionFromClientPredictive * Vector3.new(1, 0, 1) - playerPositionFromClientAtFiring * Vector3.new(1, 0, 1)).unit):Dot((unitDirection * Vector3.new(1, 0, 1)).unit)))\n\n\t-- accept if its within 7 degrees off of perfect\n\treturn degreesOffPredictedCourse2 <= 7\nend\n\nlocal function isArrowDataUsableForDamageRequest(arrowData, playerPositionAtDamageRequestTime, targetPositionAtDamageRequestTime)\n\tif tick() - arrowData.timestamp >= ARROW_DATA_MAX_LIFETIME then return false end\n\n\tlocal playerPositionFromServerAtFiring = arrowData.serverCharacterPosition\n\tlocal playerPositionFromClientAtFiring = arrowData.executionData[\"cast-origin\"]\n\n\tlocal unitDirection, targetPositionFromClientPredictive = projectile.getUnitVelocityToImpact_predictiveByAbilityExecutionData(\n\t\tplayerPositionFromClientAtFiring,\n\t\t(arrowData.sourceWeaponBaseData.projectileSeeed or 200) * math.clamp(arrowData.executionData.bowChargeTime / configuration.getConfigurationValue(\"maxBowChargeTime\"), 0.1, 1),\n\t\tarrowData.executionData,\n\t\tfalse\n\t)\n\n\tlocal degreesOffPredictedCourse = math.deg(math.acos(((targetPositionAtDamageRequestTime - playerPositionFromClientAtFiring).unit):Dot(unitDirection.unit)))\n\tlocal degreesOffPredictedCourse2 = math.deg(math.acos(((targetPositionFromClientPredictive - playerPositionFromClientAtFiring).unit):Dot(unitDirection.unit)))\n\tlocal degreesOffPredictedCourse3 = math.deg(math.acos(((targetPositionFromClientPredictive * Vector3.new(1, 0, 1) - playerPositionFromClientAtFiring * Vector3.new(1, 0, 1)).unit):Dot((unitDirection * Vector3.new(1, 0, 1)).unit)))\n\n\t-- accept if its within 7 degrees off of perfect\n\treturn degreesOffPredictedCourse2 <= 7\nend\n\nlocal function calculatePlayerDamage(player, damageType, targetLevel, isTargetPlayer)\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\tlocal levelDifference \t\t= (playerData.level or 0) - targetLevel\n\tlocal levelMulti \t\t\t= math.clamp((10 + (levelDifference/2)) / 10,0.2,2)\n\tlocal damageRangeMultiplier = rand:NextInteger(95, 105) / 100\n\tlocal stats \t\t\t\t= playerData.nonSerializeData.statistics_final\n\n\t-- pvp has no level difference multiplier\n\t-- very unfair with level differences otherwise.\n\tif isTargetPlayer and configuration.getConfigurationValue(\"doNotApplyLevelMultiToPVP\", player) then\n\t\tlevelMulti = 1\n\tend\n\n\tlocal damage = stats[damageType .. \"Damage\"]\n\n\treturn math.ceil(damage * levelMulti * damageRangeMultiplier)\n\n\t--local damageToDealToMonster = math.floor((stats.equipmentDamage or 1) * levelMulti * damageRangeMultiplier) + stats.str\nend\n\nlocal arrowMultiHitCache = {}\n\nlocal function playerRequest_damageEntity(player, serverHitbox, damagePosition, sourceType, sourceId, sourceTag, guid)\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\tif not playerData then\n\t\treturn false\n\tend\n\tlocal stats = playerData.nonSerializeData.statistics_final\n\tif player.Character and player.Character.PrimaryPart and playerData and serverHitbox and (serverHitbox:IsDescendantOf(entityManifestCollectionFolder) or serverHitbox:IsDescendantOf(entityManifestCollectionFolder)) then\n\t\t-- can this person even attack the target...?\n\t\tif sourceType ~= \"monster\" and not damage.canPlayerDamageTarget(player, serverHitbox) then\n\t\t\treturn false\n\t\tend\n\n\t\tif serverHitbox:FindFirstChild(\"isDamageImmune\") and serverHitbox.isDamageImmune.Value then\n\t\t\treturn false\n\t\tend\n\t\tlocal damageData, attackerStats, defenderStats\n\t\tlocal isServerHitboxPlayer = (serverHitbox.entityType.Value == \"character\")\n\t\tlocal abilityDamageMulti = 1\n\t\tlocal hitsDoneToEntityManifestBySourceTag = 0\n\n\t\tlocal isPlayerAttacker\n\n\t\tif sourceType == \"ability\" or sourceType == \"equipment\" then\n\t\t\tisPlayerAttacker = true\n\t\t\tif sourceType == \"ability\" then\n\t\t\t\tlocal abilityDamageGUIDData = network:invoke(\"validateAbilityGUID\", player, sourceId, guid)\n\t\t\t\t-- if guid is valid then player casted ability properly\n\t\t\t\tif abilityDamageGUIDData then\n\t\t\t\t\tprint(\"2\")\n\t\t\t\t\t-- if this causes problems add abilityExecutionData after playerData\n\t\t\t\t\tlocal abilityBaseData = abilityLookup[sourceId]\n\t\t\t\t\tlocal abilityDamageType = \"physical\"\n\n\t\t\t\t\tattackerStats = playerData.nonSerializeData.statistics_final\n\t\t\t\t\tattackerStats.level = playerData.level\n\n\t\t\t\t\tlocal health, maxHealth, targetLevel, targetDefense, defenderDex, defenderVit, targetPlayer, targetPlayerData = 0, 0, 0, 0, 0.1, 0.1, nil, nil do\n\t\t\t\t\t\tif isServerHitboxPlayer then\n\t\t\t\t\t\t\thealth \t= serverHitbox.health.Value\n\t\t\t\t\t\t\tmaxHealth = serverHitbox.maxHealth.Value\n\t\t\t\t\t\t\ttargetPlayer = game.Players:GetPlayerFromCharacter(serverHitbox.Parent)\n\n\t\t\t\t\t\t\tif targetPlayer then\n\t\t\t\t\t\t\t\ttargetPlayerData = network:invoke(\"getPlayerData\", targetPlayer)\n\n\t\t\t\t\t\t\t\tif targetPlayerData then\n--\t\t\t\t\t\t\t\t\ttargetDefense = targetPlayerData.nonSerializeData.statistics_final[abilityDamageType .. \"Defense\"]\n\t\t\t\t\t\t\t\t\tdefenderStats = targetPlayerData.nonSerializeData.statistics_final\n\t\t\t\t\t\t\t\t\tdefenderStats.level = targetPlayerData.level\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\ttargetLevel = targetPlayer.level.Value\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\thealth = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"health\")\n\t\t\t\t\t\t\tmaxHealth = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"maxHealth\")\n\t\t\t\t\t\t\ttargetLevel = serverHitbox.level.Value\n\n\t\t\t\t\t\t\tdefenderStats = {\n\t\t\t\t\t\t\t\tlevel = serverHitbox.level.Value;\n\t\t\t\t\t\t\t\tdex = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"dex\") or 0;\n\t\t\t\t\t\t\t\tvit = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"vit\") or 0;\n\t\t\t\t\t\t\t\tstr = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"str\") or 0;\n\t\t\t\t\t\t\t\tint = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"int\") or 0;\n\t\t\t\t\t\t\t\tdefense = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"defense\") or 0;\n\t\t\t\t\t\t\t\tphysicalDefense = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"defense\") or 0;\n\t\t\t\t\t\t\t\tmagicalDefense = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"defense\") or 0;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\tabilityDamageMulti  = 1\n\t\t\t\t\t-- note: this stuff does literally nothing with the dmg changes\n\t\t\t\t\tlocal baseDamage = calculatePlayerDamage(player, abilityDamageType, targetLevel, isServerHitboxPlayer)\n\t\t\t\t\tlocal abilityDamage = math.ceil(baseDamage * abilityDamageMulti)\n\n\t\t\t\t\tlocal damageCategory = \"direct\"\n\t\t\t\t\tlocal firstHitDataForSourceTag\n\t\t\t\t\t--[[local WEIRD_BLOCK do\n\t\t\t\t\t\tfor i, v in pairs(abilityDamageGUIDData.previousEntityHits) do\n\t\t\t\t\t\t\tif abilityBaseData.securityData and abilityBaseData.securityData.isDamageContained then\n\t\t\t\t\t\t\t\t-- use any first hit\n\t\t\t\t\t\t\t\tfirstHitDataForSourceTag = v.hitData[1]\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif v.entityManifest == serverHitbox then\n\t\t\t\t\t\t\t\tfor _, hitData in pairs(v.hitData) do\n\t\t\t\t\t\t\t\t\tif hitData.sourceTag == sourceTag then\n\t\t\t\t\t\t\t\t\t\thitsDoneToEntityManifestBySourceTag = hitsDoneToEntityManifestBySourceTag + 1\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend]]\n\n\t\t\t\t\t-- todo: turn this into something more elegant.\n\t\t\t\t\tif configuration.getConfigurationValue(\"doUseAbilitySecurityData\", player) then\n\t\t\t\t\t\tif abilityBaseData.securityData then\n\t\t\t\t\t\t\tlocal adjustServerHitboxPosition = detection.projection_Box(serverHitbox.CFrame, serverHitbox.Size, player.Character.PrimaryPart.Position)\n\n\t\t\t\t\t\t\tif abilityBaseData.securityData.maxHitLockout then\n\t\t\t\t\t\t\t\tlocal sumHits = 0 do\n\t\t\t\t\t\t\t\t\tfor i, v in pairs(abilityDamageGUIDData.previousEntityHits) do\n\t\t\t\t\t\t\t\t\t\tsumHits = sumHits + #v.hitData\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif sumHits >= abilityBaseData.securityData.maxHitLockout then\n\t\t\t\t\t\t\t\t\twarn(player, abilityBaseData.name, \"hit maxHitLockout\")\n\n\t\t\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif abilityBaseData.projectileSpeed and abilityBaseData.securityData.projectileOrigin then\n\t\t\t\t\t\t\t\tlocal posDiff = 0 do\n\t\t\t\t\t\t\t\t\tif abilityBaseData.securityData.projectileOrigin == \"character\" then\n\t\t\t\t\t\t\t\t\t\tposDiff = (adjustServerHitboxPosition - player.Character.PrimaryPart.Position).magnitude\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif posDiff > 3 * abilityBaseData.projectileSpeed * (tick() - abilityDamageGUIDData.timestamp) then\n\t\t\t\t\t\t\t\t\twarn(player, abilityBaseData.name, \"projectile hit way too fast\")\n\n\t\t\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif (abilityBaseData.securityData.playerHitMaxPerTag or math.huge) <= hitsDoneToEntityManifestBySourceTag then\n\t\t\t\t\t\t\t\twarn(player, abilityBaseData.name, \"hit same entity too many times\")\n\n\t\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif abilityBaseData.securityData.isDamageContained and firstHitDataForSourceTag then\n\t\t\t\t\t\t\t\tlocal posDiff \t\t= (adjustServerHitboxPosition - firstHitDataForSourceTag.hitPosition).magnitude\n\t\t\t\t\t\t\t\tlocal containRadius = abilityStatistics[\"blast radius\"] or abilityStatistics[\"range\"] or abilityStatistics[\"radius\"] or abilityStatistics[\"distance\"] or 20\n\n\t\t\t\t\t\t\t\tif posDiff > (containRadius) * 3 then\n\t\t\t\t\t\t\t\t\twarn(player, abilityBaseData.name, \"hit entity outside of containDamage\")\n\n\t\t\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\t--[[for statsName, statsValue in pairs(abilityStatistics) do\n\t\t\t\t\t\tlocal scalingStat = string.match(statsName, \"(%w+)_scaling\")\n\t\t\t\t\t\tif scalingStat and playerData.nonSerializeData.statistics_final[scalingStat] then\n\t\t\t\t\t\t\tabilityDamage = abilityDamage + playerData.nonSerializeData.statistics_final[scalingStat] * statsValue\n\t\t\t\t\t\telseif statsName == \"enemy_missing_health\" then\n\t\t\t\t\t\t\tabilityDamage = abilityDamage + (maxHealth - health) * statsValue\n\t\t\t\t\t\telseif statsName == \"enemy_current_health\" then\n\t\t\t\t\t\t\tabilityDamage = abilityDamage + health * statsValue\n\t\t\t\t\t\telseif statsName == \"enemy_max_health\" then\n\t\t\t\t\t\t\tabilityDamage = abilityDamage + maxHealth * statsValue\n\t\t\t\t\t\telseif statsName == \"user_missing_health\" then\n\t\t\t\t\t\t\twarn(statsName .. \" not yet implemented.\")\n\t\t\t\t\t\telseif statsName == \"user_current_health\" then\n\t\t\t\t\t\t\twarn(statsName .. \" not yet implemented.\")\n\t\t\t\t\t\telseif statsName == \"user_max_health\" then\n\t\t\t\t\t\t\twarn(statsName .. \" not yet implemented.\")\n\t\t\t\t\t\tend\n\t\t\t\t\tend]]\n\n\t\t\t\t\tdamageData = {}\n\t\t\t\t\tdamageData.damage = abilityDamage\n\t\t\t\t\tdamageData.sourceType = sourceType\n\t\t\t\t\tdamageData.sourceId = sourceId\n\t\t\t\t\tdamageData.damageType = abilityDamageType\n\t\t\t\t\tdamageData.isDamageDirect = false\n\t\t\t\t\tdamageData.sourcePlayerId = player.userId\n\t\t\t\t\tdamageData.damageTime = os.time()\n\t\t\t\t\tdamageData.category = damageCategory\n\t\t\t\t\tdamageData.sourceEntityGUID = utilities.getEntityGUIDByEntityManifest(player.Character.PrimaryPart)\n\t\t\t\tend\n\t\t\telseif sourceType == \"equipment\" then\n\t\t\t\tlocal equipmentData = network:invoke(\"getPlayerEquipmentDataByEquipmentPosition\", player, mapping.equipmentPosition.weapon)\n\t\t\t\tif equipmentData then\n\t\t\t\t\tlocal weaponBaseData = itemLookup[equipmentData.id]\n\t\t\t\t\tlocal weaponManfiestFolder = itemFolderLookup[weaponBaseData.module.Name]\n\t\t\t\t\tif weaponBaseData and weaponBaseData.module then\n\n\t\t\t\t\t\tlocal DAMAGE_TYPE_OVERRIDE\n\n\t\t\t\t\t\tlocal passesSecurityCheck, securityData = false, {} do\n\t\t\t\t\t\t\tif weaponBaseData.equipmentType == \"bow\" then\n\t\t\t\t\t\t\t\tif arrowsShotByPlayers[player] and #arrowsShotByPlayers[player] > 0 then\n\t\t\t\t\t\t\t\t\tlocal playerPositionAtDamageRequestTime = player.Character.PrimaryPart.Position\n\t\t\t\t\t\t\t\t\tlocal targetPositionAtDamageRequestTime = serverHitbox.Position\n\n\t\t\t\t\t\t\t\t\tfor i, arrowData in pairs(arrowsShotByPlayers[player]) do\n\t\t\t\t\t\t\t\t\t\tif isArrowDataUsableForDamageRequest(arrowData, playerPositionAtDamageRequestTime, targetPositionAtDamageRequestTime) then\n\t\t\t\t\t\t\t\t\t\t\tpassesSecurityCheck = true\n\t\t\t\t\t\t\t\t\t\t\tsecurityData \t\t= arrowData\n\t\t\t\t\t\t\t\t\t\t\tif not arrowData.canAOE then\n\t\t\t\t\t\t\t\t\t\t\t\tif arrowData.piercesRemaining <= 0  then\n\t\t\t\t\t\t\t\t\t\t\t\t\ttable.remove(arrowsShotByPlayers[player], i)\n\t\t\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\t\t\tarrowData.piercesRemaining = arrowData.piercesRemaining - 1\n\t\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\t\tDAMAGE_TYPE_OVERRIDE = \"magical\"\n\t\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\t\tif (tick() - arrowData.timestamp) > 5 then -- lived too long\n\t\t\t\t\t\t\t\t\t\t\t\ttable.remove(arrowsShotByPlayers[player], i)\n\t\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\telseif weaponBaseData.equipmentType == \"staff\" and sourceTag == \"magic-ball\" then\n\t\t\t\t\t\t\t\tif magicBallsByPlayer[player] and #magicBallsByPlayer[player] > 0 then\n\t\t\t\t\t\t\t\t\tlocal playerPositionAtDamageRequestTime = player.Character.PrimaryPart.Position\n\t\t\t\t\t\t\t\t\tlocal targetPositionAtDamageRequestTime = serverHitbox.Position\n\n\t\t\t\t\t\t\t\t\tfor i, magicBallData in pairs(magicBallsByPlayer[player]) do\n\t\t\t\t\t\t\t\t\t\tif isMagicBallDataUsableForDamageRequest(magicBallData, playerPositionAtDamageRequestTime, targetPositionAtDamageRequestTime) then\n\t\t\t\t\t\t\t\t\t\t\tpassesSecurityCheck = true\n\t\t\t\t\t\t\t\t\t\t\tsecurityData \t\t= magicBallData\n\n\t\t\t\t\t\t\t\t\t\t\ttable.remove(magicBallsByPlayer[player], i)\n\n\t\t\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tlocal manifest = weaponManfiestFolder:FindFirstChild(\"manifest\")\n\t\t\t\t\t\t\t\tif not manifest and weaponManfiestFolder:FindFirstChild(\"container\") then\n\t\t\t\t\t\t\t\t\tlocal container = weaponManfiestFolder.container:FindFirstChild(\"RightHand\") or weaponManfiestFolder.container:FindFirstChild(\"LeftHand\")\n\t\t\t\t\t\t\t\t\tif container then\n\t\t\t\t\t\t\t\t\t\tmanifest = container:FindFirstChild(\"manifest\") or container.PrimaryPart\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tif manifest then\n\t\t\t\t\t\t\t\t\tlocal diameter \t\t\t= math.max(manifest.Size.X, manifest.Size.Y, manifest.Size.Z) + 6 -- add 6 to pad for laggy players\n\t\t\t\t\t\t\t\t\tlocal adjusted_position = detection.projection_Box(serverHitbox.CFrame, serverHitbox.Size, player.Character.PrimaryPart.Position)\n\t\t\t\t\t\t\t\t\tlocal distFromMonster \t= (adjusted_position - player.Character.PrimaryPart.Position).magnitude\n\n\t\t\t\t\t\t\t\t\tif distFromMonster <= diameter * (1.75 + playerData.nonSerializeData.statistics_final.attackRangeIncrease) and isPlayerEquipmentDamageRequestWithinAnimationDamageSequence(player, serverHitbox) then\n\t\t\t\t\t\t\t\t\t\tpassesSecurityCheck = true\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\t\tif passesSecurityCheck then\n\t\t\t\t\t\t\tlocal EQUIPMENT_DAMAGE_TYPE = DAMAGE_TYPE_OVERRIDE or \"physical\"\n\n\t\t\t\t\t\t\tlocal targetLevel, targetDefense, targetPlayer, targetPlayerData = 0, 0, nil do\n\t\t\t\t\t\t\t\tif isServerHitboxPlayer then\n\t\t\t\t\t\t\t\t\t-- temporarily no difference when attacking players!\n\n\t\t\t\t\t\t\t\t\ttargetPlayer = game.Players:GetPlayerFromCharacter(serverHitbox.Parent)\n\t\t\t\t\t\t\t\t\tif targetPlayer then\n\t\t\t\t\t\t\t\t\t\ttargetPlayerData = network:invoke(\"getPlayerData\", targetPlayer)\n\n\t\t\t\t\t\t\t\t\t\tif targetPlayerData then\n\t\t\t\t\t\t\t\t\t\t\ttargetDefense = targetPlayerData.nonSerializeData.statistics_final[\"defense\"]\n\t\t\t\t\t\t\t\t\t\t\tdefenderStats = targetPlayerData.nonSerializeData.statistics_final\n\t\t\t\t\t\t\t\t\t\t\tdefenderStats.level = targetPlayerData.level\n\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\ttargetLevel = targetPlayer.level.Value\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t-- targetLevel = playerData.level\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\ttargetLevel \t= serverHitbox.level.Value\n\t\t\t\t\t\t\t\t\tdefenderStats \t= {\n\t\t\t\t\t\t\t\t\t\tlevel\t= serverHitbox.level.Value;\n\t\t\t\t\t\t\t\t\t\tdex \t= network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"dex\") or 0;\n\t\t\t\t\t\t\t\t\t\tvit \t= network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"vit\") or 0;\n\t\t\t\t\t\t\t\t\t\tstr \t= network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"str\") or 0;\n\t\t\t\t\t\t\t\t\t\tint \t= network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"int\") or 0;\n\t\t\t\t\t\t\t\t\t\tdefense = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"defense\") or 0;\n\t\t\t\t\t\t\t\t\t\tphysicalDefense = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"defense\") or 0;\n\t\t\t\t\t\t\t\t\t\tmagicalDefense = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"defense\") or 0;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tlocal levelDifference \t= playerData.level - targetLevel\n\t\t\t\t\t\t\tlocal stats \t\t\t= playerData.nonSerializeData.statistics_final\n\t\t\t\t\t\t\tattackerStats \t\t\t= stats\n\t\t\t\t\t\t\tattackerStats.level\t\t= playerData.level\n\n\t\t\t\t\t\t\tdamageData = {}\n\t\t\t\t\t\t\t\tdamageData.damage \t\t\t= 0 -- gets overwritten later\n\t\t\t\t\t\t\t\tdamageData.damageType \t\t= sourceTag == \"magic-ball\" and \"magical\" or EQUIPMENT_DAMAGE_TYPE\n\t\t\t\t\t\t\t\tdamageData.sourceType \t\t= sourceType\n\t\t\t\t\t\t\t\tdamageData.sourceId \t\t= sourceId\n\t\t\t\t\t\t\t\tdamageData.sourcePlayerId\t= player.userId\n\t\t\t\t\t\t\t\tdamageData.damageTime\t\t= os.time()\n\t\t\t\t\t\t\t\tdamageData.category \t\t= weaponBaseData.equipmentType == \"bow\" and \"projectile\" or \"direct\"\n\t\t\t\t\t\t\t\tdamageData.sourceEntityGUID = utilities.getEntityGUIDByEntityManifest(player.Character.PrimaryPart)\n\t\t\t\t\t\t\t\tdamageData.equipmentType    = weaponBaseData.equipmentType\n\t\t\t\t\t\tend\n\n\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\telseif sourceType == \"monster\" then\n\t\t\tif player.Character.PrimaryPart.health.Value > 0 then\n\t\t\t\tlocal stats = playerData.nonSerializeData.statistics_final\n\n\t\t\t\tdefenderStats = stats\n\t\t\t\tdefenderStats.level = playerData.level\n\n\t\t\t\tlocal damage = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"damage\")\n\n\t\t\t\tattackerStats = {\n\t\t\t\t\tlevel\t= serverHitbox.level.Value;\n\t\t\t\t\tdex \t= network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"dex\") or 0;\n\t\t\t\t\tvit \t= network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"vit\") or 0;\n\t\t\t\t\tstr \t= network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"str\") or 0;\n\t\t\t\t\tint \t= network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"int\") or 0;\n\t\t\t\t\tdefense = network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"defense\") or 0;\n\t\t\t\t\tdamage \t= network:invoke(\"getMonsterDataByMonsterManifest_server\", serverHitbox, \"damage\") or 0;\n\t\t\t\t\tdamageMulti = 1;\n\t\t\t\t\tmagicalDamage \t= nil;--monsterDamage;\n\t\t\t\t\tphysicalDamage \t= nil;--monsterDamage;\n\t\t\t\t}\n\n\t\t\t\tdamageData = {}\n\t\t\t\t\tdamageData.damage \t\t\t\t= attackerStats.damage\n\t\t\t\t\tdamageData.sourceType \t\t\t= \"monster\"\n\t\t\t\t\tdamageData.sourceId \t\t\t= sourceId\n\t\t\t\t\tdamageData.damageType \t\t\t= nil;--damageType\n\t\t\t\t\tdamageData.category \t\t\t= nil;--damageCategory\n\t\t\t\t\tdamageData.sourceEntityGUID \t= utilities.getEntityGUIDByEntityManifest(serverHitbox)\n\t\t\t\t--[[\n\t\t\t\t\tdamageData.damage \t\t\t= damageToDeal\n\t\t\t\t\tdamageData.sourceType \t\t= sourceType\n\t\t\t\t\tdamageData.sourceId \t\t= sourceId\n\t\t\t\t\tdamageData.sourcePlayerId\t= player.userId\n\t\t\t\t\tdamageData.damageTime\t\t= os.time()\n\t\t\t\t--]]\n\t\t\tend\n\t\tend\n\t\t-- damageData, attackerStats, defenderStats\n\t\tif damageData then\n\t\t\tlocal ATK = attackerStats.damage--(damageData.damageType == \"magical\" and attackerStats.magicalDamage) or attackerStats.physicalDamage\n\t\t\tlocal DEF = defenderStats.defense--(damageData.damageType == \"magical\" and defenderStats.magicalDefense) or defenderStats.physicalDefense\n\n\t\t\tlocal coeffecient = 1\n\t\t\t--[[\n\t\t\tif isPlayerAttacker then\n\t\t\t\tlocal level = playerData.level or 0\n\t\t\t\tcoeffecient = coeffecient * (1 + .02*level)\n\t\t\tend\n\t\t\t]]\n\n--\t\t\tlocal levelBonusDamageMulti = 1 --(1 + .02*attackerStats.level)\n--\t\t\tcoeffecient = coeffecient * levelBonusDamageMulti\n\n--\t\t\tlocal levelDifference \t\t= (attackerStats.level or 0) - (defenderStats.level or 0)\n--\t\t\tlocal levelMulti \t\t\t= math.clamp((10 + (levelDifference/2)) / 10,0.2,2)\n--\t\t\tcoeffecient = coeffecient * levelMulti\n\n\t\t\tif attackerStats.damageMulti then\n\t\t\t\tcoeffecient = coeffecient * attackerStats.damageMulti\n\t\t\tend\n\n\n\n--\t\t\tdamageData.damage = coeffecient * ATK*(1/(1+2.718281828459^(-((ATK-DEF)/ (0.1*DEF) )) ))\n\n\t\t\tdamageData.damage = math.max(0, math.floor(coeffecient * (ATK - DEF)))\n\t--\t\tdamageData.damage = coeffecient * (ATK ^ 2) / (ATK + DEF)\n\t--\t\tdamageData.damage = coeffecient * ((1.87*ATK)^4)/((1.87*ATK+DEF)^3)\n\n\t\t\t-- stuff for monsters idk --\n\t\t\tif sourceType == \"monster\" then\n\t\t\t\tlocal monsterDamage, damageType, damageCategory do\n\t\t\t\t\tif monsterLookup[serverHitbox.Name] then\n\t\t\t\t\t\tlocal statesData = monsterLookup[serverHitbox.Name].statesData\n\n\t\t\t\t\t\tif statesData.processDamageRequest then\n\t\t\t\t\t\t\tmonsterDamage, damageType, damageCategory = statesData.processDamageRequest(sourceId, damageData.damage)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif monsterDamage then\n\t\t\t\t\tdamageData.damage \t\t\t= monsterDamage\n\t\t\t\t\tdamageData.damageType \t\t= damageType;--damageType\n\t\t\t\t\tdamageData.category \t\t= damageCategory;--damageCategory\n\t\t\t\telse\n\t\t\t\t\tdamageData.damageType \t\t= \"physical\";--damageType\n\t\t\t\t\tdamageData.category \t\t= \"direct\";--damageCategory\n\t\t\t\tend\n\t\t\telseif sourceType == \"ability\" then\n\n\t\t\t\t-- if this causes problems add abilityExecutionData after playerData\n\t\t\t\tlocal abilityBaseData = abilityLookup[sourceId]\n\n\t\t\t\tif abilityBaseData._serverProcessDamageRequest then\n\t\t\t\t\tlocal abilityDamage, abilityDamageType, abilityDamageCategory = abilityBaseData._serverProcessDamageRequest(sourceTag, damageData.damage, serverHitbox, hitsDoneToEntityManifestBySourceTag, player)\n\n\t\t\t\t\tif not abilityDamage then\n\t\t\t\t\t\twarn(\"FAILED TO PASS VALID SOURCE-TAG\")\n\n\t\t\t\t\t\treturn false\n\t\t\t\t\telse\n\t\t\t\t\t\tdamageData.damage = abilityDamage;\n\t\t\t\t\t\tdamageData.damageType = abilityDamageType;\n\t\t\t\t\t\tdamageData.category = abilityDamageCategory;\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\t\tdamageData.position = damagePosition\n\n\t\t\t-- ranged damage bonus?\n\t\t\tif sourceType == \"equipment\" then\n\t\t\t\tlocal equipmentData = network:invoke(\"getPlayerEquipmentDataByEquipmentPosition\", player, mapping.equipmentPosition.weapon)\n\t\t\t\tif equipmentData then\n\t\t\t\t\tlocal weaponBaseData = itemLookup[equipmentData.id]\n\t\t\t\t\tif weaponBaseData and weaponBaseData.equipmentType == \"bow\" then\n\t\t\t\t\t\tdamageData.damage = damageData.damage + (attackerStats.rangedDamage or 0)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\t\t-- CRIT!\n\t\t\tif attackerStats.criticalStrikeChance and attackerStats.criticalStrikeChance > 0 and attackerStats.criticalStrikeChance >= critChanceRandom:NextNumber() then\n\t\t\t\tdamageData.damage \t\t= damageData.damage * CRIT_DAMAGE_MULTIPLIER\n\t\t\t\tdamageData.isCritical \t= true\n\t\t\tend\n\n\t\t\t-- ABILITY DMG\n\t\t\tif abilityDamageMulti then\n\t\t\t\tdamageData.damage = damageData.damage * abilityDamageMulti\n\t\t\tend\n\n\t\t\t-- BLOCKED!\n\t\t\tif damageData.damageType ~= \"magical\" then\n\t\t\t\tif defenderStats.blockChance and dodgeChanceRandom:NextNumber() <= defenderStats.blockChance then\n\t\t\t\t\tdamageData.damage = damageData.damage * 0.25\n\t\t\t\t\tdamageData.supressed = true\n\t\t\t\t\tnetwork:fireAllClients(\"replicatePlayerAnimationSequence\", player, \"emoteAnimations\", \"block\")\n\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- PERKS\n\t\t\tif sourceType == \"ability\" or sourceType == \"equipment\" then\n\t\t\t\tif serverHitbox.health.Value == serverHitbox.maxHealth.Value then\n\n\t\t\t\telseif serverHitbox.health.Value / serverHitbox.maxHealth.Value <= 0.3 then\n\n\t\t\t\tend\n\n\t\t\t\tif sourceType == \"equipment\" and playerDamageAnimationState[player] and playerDamageAnimationState[player].state == \"strike2\" then\n\n\t\t\t\telseif sourceType == \"equipment\" and playerDamageAnimationState[player] and playerDamageAnimationState[player].state == \"strike3\" then\n\t\t\t\t\tlocal hasTripleSlash = false\n\n\t\t\t\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\t\t\t\tif playerData and playerData.abilities then\n\t\t\t\t\t\tfor _, ability in pairs(playerData.abilities) do\n\t\t\t\t\t\t\tif ability.id == 3 and ability.variant == \"tripleSlash\" then\n\t\t\t\t\t\t\t\thasTripleSlash = true\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif hasTripleSlash then\n\t\t\t\t\t\tif network:invoke(\"getIsPlayerOfClass_server\", player, \"berserker\") then\n\t\t\t\t\t\t\tdamageData.damage = damageData.damage * 2\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tdamageData.damage = damageData.damage * 1.4\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\t\t-- PARRY\n\t\t\tif damageData.category then\n\t\t\t\tlocal targetPlayer = sourceType == \"monster\" and player or game.Players:GetPlayerFromCharacter(serverHitbox.Parent)\n\n\t\t\t\tif targetPlayer then\n\t\t\t\t\tlocal successAAED, activeAbilityExecutionData = utilities.safeJSONDecode(targetPlayer.Character.PrimaryPart.activeAbilityExecutionData.Value)\n\t\t\t\t\tif successAAED then\n\t\t\t\t\t\tif activeAbilityExecutionData.id == 8 then\n\t\t\t\t\t\t\tif damageData.category == \"direct\" or damageData.category == \"projectile\" then\n\t\t\t\t\t\t\t\tdamageData.damage = damageData.damage * 0.5\n\t\t\t\t\t\t\t\tdamageData.supressed = true\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telseif activeAbilityExecutionData.id == 17 then\n\t\t\t\t\t\t\tif damageData.category == \"direct\" or damageData.category == \"projectile\" then\n\t\t\t\t\t\t\t\tdamageData.damage = damageData.damage * 0.35\n\t\t\t\t\t\t\t\tdamageData.supressed = true\n\n\t\t\t\t\t\t\t\tnetwork:fireAllClients(\"signal_damageModificationByActiveAbility\",\n\t\t\t\t\t\t\t\t\t-- player that is using the ability that modified\n\t\t\t\t\t\t\t\t\ttargetPlayer,\n\t\t\t\t\t\t\t\t\tactiveAbilityExecutionData.id,\n\t\t\t\t\t\t\t\t\tdamageData,\n\t\t\t\t\t\t\t\t\t-- thing damaging you\n\t\t\t\t\t\t\t\t\tplayer.Character.PrimaryPart\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- working with this is actually giving me so much anxiety\n\n\t\t\tlocal attackerData = isPlayerAttacker and playerData\n\t\t\tlocal sourceManifest = isPlayerAttacker and player.Character.PrimaryPart or serverHitbox\n\t\t\tlocal targetManifest = sourceType == \"monster\" and player.Character.PrimaryPart or serverHitbox\n\t\t\tlocal targetPlayer = sourceType == \"monster\" and player or game.Players:GetPlayerFromCharacter(serverHitbox.Parent)\n\t\t\tlocal targetPlayerData = targetPlayer and network:invoke(\"getPlayerData\", targetPlayer)\n\n\t\t\t-- go through attacker perks (if applicable)\n\t\t\tlocal attackerPerks = attackerData and attackerData.nonSerializeData.statistics_final.activePerks or {}\n\t\t\tfor perkName, active in pairs(attackerPerks) do\n\t\t\t\tif active then\n\t\t\t\t\tlocal perkData = perkLookup[perkName]\n\t\t\t\t\tif perkData.onDamageGiven then\n\t\t\t\t\t\tperkData.onDamageGiven(sourceManifest, sourceType, sourceId, targetManifest, damageData)\n\t\t\t\t\tend\n\n\t\t\t\t\tif damageData.isCritical then\n\t\t\t\t\t\tif perkData.onCritGiven then\n\t\t\t\t\t\t\tperkData.onCritGiven(sourceManifest, sourceType, sourceId, targetManifest, damageData)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\t\tif attackerData and attackerData.nonSerializeData.statistics_final.damageGivenMulti then\n\t\t\t\tdamageData.damage = damageData.damage * attackerData.nonSerializeData.statistics_final.damageGivenMulti\n\t\t\tend\n\n\t\t\t-- go through defender perks (if applicable)\n\t\t\tlocal defenderPerks = targetPlayerData and targetPlayerData.nonSerializeData.statistics_final.activePerks or {}\n\t\t\tfor perkName, active in pairs(defenderPerks) do\n\t\t\t\tif active then\n\t\t\t\t\tlocal perkData = perkLookup[perkName]\n\t\t\t\t\tif perkData.onDamageTaken then\n\t\t\t\t\t\tperkData.onDamageTaken(sourceManifest, sourceType, sourceId, targetManifest, damageData)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif targetPlayerData and targetPlayerData.nonSerializeData.statistics_final.damageTakenMulti then\n\t\t\t\tdamageData.damage = damageData.damage * targetPlayerData.nonSerializeData.statistics_final.damageTakenMulti\n\t\t\tend\n\n\t\t\t-- INT BOOST\n\t\t\tif damageData.damageType == \"magical\" then\n\t\t\t\tdamageData.damage = damageData.damage * (1 + (attackerStats.int * 1/160))\n\n\t\t\t-- STR BOOST\n\t\t\telse\n\t\t\t\tdamageData.damage = damageData.damage * (1 + (attackerStats.str * 1/160))\n\t\t\tend\n\n\t\t\t-- nerf rangers\n\t\t\tif guid and (damageData.equipmentType == \"bow\") then\n\t\t\t\tlocal damageLossPerMultiHit = 0.5\n\n\t\t\t\tif not arrowMultiHitCache[guid] then\n\t\t\t\t\tarrowMultiHitCache[guid] = {}\n\n\t\t\t\t\t-- clear this cache to save memory\n\t\t\t\t\tdelay(5, function()\n\t\t\t\t\t\tarrowMultiHitCache[guid] = nil\n\t\t\t\t\tend)\n\t\t\t\tend\n\t\t\t\tif not arrowMultiHitCache[guid][targetManifest] then\n\t\t\t\t\tarrowMultiHitCache[guid][targetManifest] = 0\n\t\t\t\tend\n\t\t\t\tarrowMultiHitCache[guid][targetManifest] = arrowMultiHitCache[guid][targetManifest] + 1\n\n\t\t\t\tlocal loss = damageLossPerMultiHit ^ (arrowMultiHitCache[guid][targetManifest] - 1)\n\t\t\t\tdamageData.damage = damageData.damage * loss\n\t\t\tend\n\t\t\t-- ranger's stance damage implementation\n\t\t\tif (damageData.equipmentType == \"bow\") and sourceTag == \"ranger stance\" then\n\t\t\t\tif not player.Character then return end\n\t\t\t\tlocal manifest = player.Character.PrimaryPart\n\t\t\t\tif not manifest then return end\n\n\t\t\t\tlocal guid = utilities.getEntityGUIDByEntityManifest(manifest)\n\t\t\t\tif not guid then return end\n\n\t\t\t\tlocal statuses = network:invoke(\"getStatusEffectsOnEntityManifestByEntityGUID\", guid)\n\t\t\t\tlocal rangerStanceStatus = nil\n\n\t\t\t\tfor _, status in pairs(statuses) do\n\t\t\t\t\tif status.statusEffectType == \"ranger stance\" then\n\t\t\t\t\t\trangerStanceStatus = status\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif rangerStanceStatus then\n\t\t\t\t\tdamageData.damage = damageData.damage * rangerStanceStatus.statusEffectModifier.damageBonus\n\t\t\t\tend\n\t\t\tend\n\t\t\t-- fire out on the event system to let other things modify the damage at will\n\t\t\tdamageData.target = serverHitbox\n\t\t\tevents:fireEventLocal(\"playerWillDealDamage\", player, damageData)\n\t\t\tlocal successfullyDealtDamage do\n\t\t\t\tif sourceType == \"monster\" then\n\t\t\t\t\t-- monster is damaging player\n\t\t\t\t\tsuccessfullyDealtDamage = network:invoke(\"playerDamageRequest_server\", nil, player.Character.PrimaryPart, damageData)\n\t\t\t\telse\n\t\t\t\t\tif isServerHitboxPlayer then\n\t\t\t\t\t\tdamageData.damage = damageData.damage * configuration.getConfigurationValue(\"abilityPVPDampening\")\n\n\t\t\t\t\t\t-- player is damaging player\n\t\t\t\t\t\tsuccessfullyDealtDamage = network:invoke(\"playerDamageRequest_server\", player, serverHitbox, damageData)\n\t\t\t\t\telse\n\t\t\t\t\t\t-- player is damaging monster\n\t\t\t\t\t\tsuccessfullyDealtDamage = network:invoke(\"monsterDamageRequest_server\", player, serverHitbox, damageData)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif successfullyDealtDamage then\n\t\t\t\tif sourceType == \"ability\" then\n\t\t\t\t\tnetwork:fire(\"abilityDealtDamageToEntity\", player, sourceId, guid, serverHitbox, sourceTag)\n\t\t\t\tend\n\n\t\t\t\t-- VIT LEACH ON BOWS\n\t\t\t\tif sourceType == \"equipment\" then\n\t\t\t\t\tlocal equipmentData = network:invoke(\"getPlayerEquipmentDataByEquipmentPosition\", player, mapping.equipmentPosition.weapon)\n\t\t\t\t\tif equipmentData then\n\t\t\t\t\t\tlocal weaponBaseData = itemLookup[equipmentData.id]\n\t\t\t\t\t\tif weaponBaseData and weaponBaseData.equipmentType == \"bow\" then\n\t\t\t\t\t\t\tlocal healAmt = 0\n\n\t\t\t\t\t\t\tif stats.vit >= 30 then\n\t\t\t\t\t\t\t\thealAmt = 25\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif stats.vit >= 70 then\n\t\t\t\t\t\t\t\thealAmt = 40\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif stats.vit >= 120 then\n\t\t\t\t\t\t\t\thealAmt = 100\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t-- 10% proc chance\n\t\t\t\t\t\t\tif math.random() < 1/10 and healAmt ~= 0 then\n\t\t\t\t\t\t\t\tlocal character = player.Character\n\t\t\t\t\t\t\t\tif not character then return end\n\t\t\t\t\t\t\t\tlocal manifest = character.PrimaryPart\n\t\t\t\t\t\t\t\tif not manifest then return end\n\t\t\t\t\t\t\t\tlocal health = manifest:FindFirstChild(\"health\")\n\t\t\t\t\t\t\t\tif not health then return end\n\t\t\t\t\t\t\t\tlocal maxHealth = manifest:FindFirstChild(\"maxHealth\")\n\t\t\t\t\t\t\t\tif not maxHealth then return end\n\n\t\t\t\t\t\t\t\thealth.Value = math.min(health.Value + healAmt, maxHealth.Value)\n\n\t\t\t\t\t\t\t\tnetwork:fireAllClients(\"effects_requestEffect\", \"bloodHeal\", {\n\t\t\t\t\t\t\t\t\tplayer = player,\n\t\t\t\t\t\t\t\t\tplayerManifest = manifest,\n\t\t\t\t\t\t\t\t\ttarget = serverHitbox,\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\t\tif playerData.class == \"Warrior\" or playerData.class == \"Knight\" or playerData.class == \"Paladin\" or playerData.class == \"Berserker\" then\n\t\t\t\t\t\t\tlocal healAmt = 0\n\t\t\t\t\t\t\tlocal manaAmt = 0\n\t\t\t\t\t\t\tlocal stamAmt = 0\n\n\t\t\t\t\t\t\tif stats.vit >= 30 then\n\t\t\t\t\t\t\t\thealAmt = 2\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif stats.vit >= 70 then\n\t\t\t\t\t\t\t\thealAmt = 4\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif stats.vit >= 120 then\n\t\t\t\t\t\t\t\thealAmt = 6\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif stats.int >= 30 then\n\t\t\t\t\t\t\t\tmanaAmt = 1\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif stats.int >= 70 then\n\t\t\t\t\t\t\t\tmanaAmt = 2\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif stats.int >= 150 then\n\t\t\t\t\t\t\t\tmanaAmt = 3\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif stats.dex >= 50 then\n\t\t\t\t\t\t\t\tstamAmt = 0.5\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif stats.dex >= 120 then\n\t\t\t\t\t\t\t\tstamAmt = 1\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tlocal character = player.Character\n\t\t\t\t\t\t\tif not character then return end\n\t\t\t\t\t\t\tlocal manifest = character.PrimaryPart\n\t\t\t\t\t\t\tif not manifest then return end\n\t\t\t\t\t\t\tlocal health = manifest:FindFirstChild(\"health\")\n\t\t\t\t\t\t\tif not health then return end\n\t\t\t\t\t\t\tlocal maxHealth = manifest:FindFirstChild(\"maxHealth\")\n\t\t\t\t\t\t\tif not maxHealth then return end\n\t\t\t\t\t\t\tlocal mana = manifest:FindFirstChild(\"mana\")\n\t\t\t\t\t\t\tif not mana then return end\n\t\t\t\t\t\t\tlocal maxMana = manifest:FindFirstChild(\"maxMana\")\n\t\t\t\t\t\t\tif not maxMana then return end\n\t\t\t\t\t\t\tlocal stamina = manifest:FindFirstChild(\"stamina\")\n\t\t\t\t\t\t\tif not stamina then return end\n\t\t\t\t\t\t\tlocal maxStamina = manifest:FindFirstChild(\"maxStamina\")\n\t\t\t\t\t\t\tif not maxStamina then return end\n\n\t\t\t\t\t\t\thealth.Value = math.min(health.Value + healAmt, maxHealth.Value)\n\t\t\t\t\t\t\tmana.Value = math.min(mana.Value + manaAmt, maxMana.Value)\n\t\t\t\t\t\t\tstamina.Value = math.min(stamina.Value + stamAmt, maxStamina.Value)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tplayerData.nonSerializeData.setNonSerializeDataValue(\"lastTimeInCombat\", tick())\n\t\t\tend\n\n\t\t\treturn successfullyDealtDamage or false\n\t\tend\n\tend\nend\n\nlocal function onReportMonsterInDamageState_server(monsterName, manifest, player)\n\tlocal characterPosition\nend\n\nlocal function isDamageAnimationSequence(animationSequence)\n\tfor i, weaponType in pairs(WEAPON_TYPES_TO_SCAN) do\n\t\tif animationSequence == weaponType .. \"Animations\" then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false, nil\nend\n\nlocal function isPlayerDamageAnimationStateTransitionValid(player, weaponType, animationSequence, animationName)\n\tlocal currentPlayerDamageAnimationState = playerDamageAnimationState[player]\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\tif currentPlayerDamageAnimationState then\n\t\tif weaponValidationData[weaponType] then\n\t\t\tif animationName == \"strike1\" or animationName == \"strike2\" then\n\t\t\t\tif tick() - currentPlayerDamageAnimationState.timestamp >= weaponValidationData[weaponType][animationName .. \"_animationLength\"] / (1 + playerData.nonSerializeData.statistics_final.attackSpeed) then\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif animationName == \"strike1\" then\n\t\t\tif currentPlayerDamageAnimationState.state == \"strike2\" then\n\t\t\t\t-- compare: minTimeForStrike2ToGetToStrike1\n\t\t\t\tif weaponValidationData[weaponType] then\n\t\t\t\t\tlocal success = isWithinBounds(\n\t\t\t\t\t\ttick() - currentPlayerDamageAnimationState.timestamp,\n\t\t\t\t\t\tweaponValidationData[weaponType].strike1_slash2PeriodStart_time - DAMAGE_ANIMATION_REQUEST_MISMATCH_LATENCY_FORGIVENESS,\n\t\t\t\t\t\tweaponValidationData[weaponType].strike1_slash2PeriodStart_time + SLASH_CHAIN_WINDOW + DAMAGE_ANIMATION_REQUEST_MISMATCH_LATENCY_FORGIVENESS,\n\t\t\t\t\t\t1 + playerData.nonSerializeData.statistics_final.attackSpeed\n\t\t\t\t\t)\n\n\t\t\t\t\treturn success\n\t\t\t\tend\n\n\t\t\t\treturn true\n\n\t\t\telseif currentPlayerDamageAnimationState.state == \"strike3\" then\n\t\t\t\tif weaponValidationData[weaponType] then\n\t\t\t\t\tlocal success = isWithinBounds(\n\t\t\t\t\t\ttick() - currentPlayerDamageAnimationState.timestamp,\n\t\t\t\t\t\tweaponValidationData[weaponType].strike3_stopDamageSequence_time - DAMAGE_ANIMATION_REQUEST_MISMATCH_LATENCY_FORGIVENESS,\n\t\t\t\t\t\tweaponValidationData[weaponType].strike3_stopDamageSequence_time + SLASH_CHAIN_WINDOW  + DAMAGE_ANIMATION_REQUEST_MISMATCH_LATENCY_FORGIVENESS,\n\t\t\t\t\t\t1 + playerData.nonSerializeData.statistics_final.attackSpeed\n\t\t\t\t\t)\n\n\t\t\t\t\treturn success\n\t\t\t\tend\n\n\t\t\t\treturn true\n\t\t\tend\n\t\telseif animationName == \"strike2\" then\n\t\t\tif currentPlayerDamageAnimationState.state == \"strike1\" then\n\t\t\t\t-- compare: minTimeForStrike1ToGetToStrike2\n\t\t\t\tif weaponValidationData[weaponType] then\n\t\t\t\t\tlocal success = isWithinBounds(\n\t\t\t\t\t\ttick() - currentPlayerDamageAnimationState.timestamp,\n\t\t\t\t\t\tweaponValidationData[weaponType].strike2_slash1PeriodStart_time - DAMAGE_ANIMATION_REQUEST_MISMATCH_LATENCY_FORGIVENESS,\n\t\t\t\t\t\tweaponValidationData[weaponType].strike2_slash1PeriodStart_time + SLASH_CHAIN_WINDOW + DAMAGE_ANIMATION_REQUEST_MISMATCH_LATENCY_FORGIVENESS,\n\t\t\t\t\t\t1 + playerData.nonSerializeData.statistics_final.attackSpeed\n\t\t\t\t\t)\n\n\t\t\t\t\treturn success\n\t\t\t\tend\n\n\t\t\t\treturn true\n\t\t\telseif currentPlayerDamageAnimationState.state == \"strike2\" then\n--\t\t\t\t-- compare: minTimeForStrike2ToFinish\n--\t\t\t\tif weaponValidationData[weaponType] then\n--\t\t\t\t\treturn tick() - currentPlayerDamageAnimationState.timestamp >= weaponValidationData[weaponType].minTimeForStrike2ToFinish\n--\t\t\t\tend\n--\n--\t\t\t\treturn true\n\n\t\t\t\treturn false\n\t\t\tend\n\t\telseif animationName == \"strike3\" then\n\t\t\tif currentPlayerDamageAnimationState.state == \"strike2\" then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\telse\n\t\treturn true\n\tend\n\n\treturn false\nend\n\nlocal function playerHasArrowToShoot(player)\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\tlocal equipmentData do\n\t\tfor i, equipmentSlotData in pairs(playerData.equipment) do\n\t\t\tif equipmentSlotData.position == mapping.equipmentPosition.arrow then\n\t\t\t\tequipmentData = equipmentSlotData\n\t\t\tend\n\t\tend\n\tend\n\n\tif equipmentData then\n\t\tfor i, inventorySlotData in pairs(playerData.inventory) do\n\t\t\tif inventorySlotData.id == equipmentData.id and inventorySlotData.stacks >= 1 then\n\t\t\t\treturn true, equipmentData.id\n\t\t\tend\n\t\tend\n\telse\n\t\treturn false, \"arrow not primed\"\n\tend\nend\n\nlocal function getChargeTimeMultiplierFromChargeTime(chargeTime)\n\tchargeTime = chargeTime < 0 and 0 or chargeTime\n\n\tlocal m, b\n\n\tif chargeTime < configuration.getConfigurationValue(\"maxBowChargeTime\") then\n\t\tlocal chargeTime_a = configuration.getConfigurationValue(\"maxBowChargeTime\")\n\t\tlocal multiplier_a = 1\n\n\t\tlocal chargeTime_b = 0\n\t\tlocal multiplier_b = 0\n\n\t\tm = (multiplier_b - multiplier_a) / (chargeTime_b - chargeTime_a)\n\t\tb = multiplier_a - m * chargeTime_a\n\telse\n\t\tlocal chargeTime_a = configuration.getConfigurationValue(\"maxBowChargeTime\")\n\t\tlocal multiplier_a = 1\n\n\t\tlocal chargeTime_b = configuration.getConfigurationValue(\"bowPullBackTime\")\n\t\tlocal multiplier_b = configuration.getConfigurationValue(\"bowMaxChargeDamageMultiplier\")\n\n\t\tm = (multiplier_b - multiplier_a) / (chargeTime_b - chargeTime_a)\n\t\tb = multiplier_a - m * chargeTime_a\n\tend\n\n\tlocal multiplier = math.clamp(chargeTime * m + b, 0.1, configuration.getConfigurationValue(\"bowMaxChargeDamageMultiplier\"))\n\n\treturn multiplier\nend\n\nlocal playerBowAnimationStateData = {}\nlocal function onPlayerAnimationReplicated(player, animationSequence, animationName, executionData)\n\tif not player or not player.Character or not player.Character.PrimaryPart then return false end\n\n\tlocal isValid = isDamageAnimationSequence(animationSequence)\n\n\tif isValid or animationSequence == \"bowAnimations\" then\n\t\tlocal equipmentData = network:invoke(\"getPlayerEquipmentDataByEquipmentPosition\", player, mapping.equipmentPosition.weapon)\n\n\t\tif equipmentData then\n\t\t\tlocal itemBaseData = itemLookup[equipmentData.id]\n\n\t\t\tif itemBaseData.category == \"equipment\" then\n\t\t\t\tlocal weaponType = itemBaseData.equipmentType\n\t\t\t\tif weaponType == \"bow\" then\n\t\t\t\t\tlocal success, info = playerHasArrowToShoot(player)\n\n\t\t\t\t\tif success then\n\t\t\t\t\t\tif animationName == \"stretching_bow\" then\n\t\t\t\t\t\t\tplayerBowAnimationStateData[player] = {\n\t\t\t\t\t\t\t\tweaponType \t\t= weaponType;\n\t\t\t\t\t\t\t\tstate \t\t\t= animationName;\n\t\t\t\t\t\t\t\ttimestamp \t\t= tick();\n\t\t\t\t\t\t\t\tdamageBlacklist = {}\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\telseif animationName == \"firing_bow_stance\" then\n\t\t\t\t\t\t\tlocal success = network:invoke(\"tradeItemsBetweenPlayerAndNPC\", player, {{id = 87; stacks = 1}}, 0, {}, 0, nil)\n\n\t\t\t\t\t\t\tif success then\n\t\t\t\t\t\t\t\tif not arrowsShotByPlayers[player] then\n\t\t\t\t\t\t\t\t\tarrowsShotByPlayers[player] = {}\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\ttable.insert(arrowsShotByPlayers[player], {\n\t\t\t\t\t\t\t\t\tserverCharacterPosition = player.Character.PrimaryPart.Position;\n\t\t\t\t\t\t\t\t\texecutionData \t\t\t= executionData;\n\t\t\t\t\t\t\t\t\ttimestamp \t\t\t\t= tick();\n\t\t\t\t\t\t\t\t\tchargeTimeMultiplier \t= 1;\n\t\t\t\t\t\t\t\t\tpiercesRemaining \t\t= 999;\n\t\t\t\t\t\t\t\t\tcanAOE\t\t\t\t\t= false;\n\t\t\t\t\t\t\t\t\tsourceWeaponBaseData \t= itemBaseData;\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telseif animationName == \"firing_bow\" then\n\t\t\t\t\t\t\tif not executionData.canceled then\n\t\t\t\t\t\t\t\tif playerBowAnimationStateData[player] and playerBowAnimationStateData[player].state == \"stretching_bow\" then\n\t\t\t\t\t\t\t\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\t\t\t\t\t\t\t\tlocal stats = playerData.nonSerializeData.statistics_final\n\n\t\t\t\t\t\t\t\t\tlocal isMagical = stats.int >= 30\n\n\t\t\t\t\t\t\t\t\tlocal attackSpeedScalar = 1 + playerData.nonSerializeData.statistics_final.attackSpeed\n\n\t\t\t\t\t\t\t\t\tlocal timeElapsed \t\t= (tick() - playerBowAnimationStateData[player].timestamp) * attackSpeedScalar\n\t\t\t\t\t\t\t\t\tlocal minBowChargeTime \t= configuration.getConfigurationValue(\"minBowChargeTime\")\n\t\t\t\t\t\t\t\t\tlocal maxBowChargeTime \t= configuration.getConfigurationValue(\"maxBowChargeTime\")\n\n\t\t\t\t\t\t\t\t\tif timeElapsed >= minBowChargeTime --[[change this to real anim time]] then\n\t\t\t\t\t\t\t\t\t\tlocal playerCurrentPosition = player.Character.PrimaryPart.Position\n\n\t\t\t\t\t\t\t\t\t\tplayerBowAnimationStateData[player] = nil\n\n\t\t\t\t\t\t\t\t\t\t-- let other parts of the system make sure we need arrows, eh?\n\t\t\t\t\t\t\t\t\t\tlocal arrowData = {\n\t\t\t\t\t\t\t\t\t\t\tneedsArrow = true\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tevents:fireEventLocal(\"playerWillUseArrow\", player, arrowData)\n\n\t\t\t\t\t\t\t\t\t\tlocal maxNumArrows = utilities.calculateNumArrowsFromDex(stats.dex)\n\t\t\t\t\t\t\t\t\t\tlocal inventory = (network:invoke(\"getPlayerData\", player) or {}).inventory or {}\n\n\t\t\t\t\t\t\t\t\t\tlocal arrowsOwned = 0\n\n\t\t\t\t\t\t\t\t\t\tfor i, inventorySlotData in pairs(inventory) do\n\t\t\t\t\t\t\t\t\t\t\tif inventorySlotData.id == 87 then\n\t\t\t\t\t\t\t\t\t\t\t\tarrowsOwned = inventorySlotData.stacks\n\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\tlocal numArrows = math.min(arrowsOwned, maxNumArrows)\n\n\t\t\t\t\t\t\t\t\t\tlocal success, info = playerHasArrowToShoot(player)\n\n\t\t\t\t\t\t\t\t\t\tif success and arrowData.needsArrow then\n\t\t\t\t\t\t\t\t\t\t\tsuccess = network:invoke(\"tradeItemsBetweenPlayerAndNPC\", player, {{id = info; stacks = numArrows}}, 0, {}, 0, nil)\n\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\tif success then\n\t\t\t\t\t\t\t\t\t\t\tif not arrowsShotByPlayers[player] then\n\t\t\t\t\t\t\t\t\t\t\t\tarrowsShotByPlayers[player] = {}\n\t\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\t\tfor i = 1, numArrows do\n\t\t\t\t\t\t\t\t\t\t\t\ttable.insert(arrowsShotByPlayers[player], {\n\t\t\t\t\t\t\t\t\t\t\t\t\tserverCharacterPosition = playerCurrentPosition;\n\t\t\t\t\t\t\t\t\t\t\t\t\texecutionData \t\t\t= executionData;\n\t\t\t\t\t\t\t\t\t\t\t\t\ttimestamp \t\t\t\t= tick();\n\t\t\t\t\t\t\t\t\t\t\t\t\tchargeTimeMultiplier \t= getChargeTimeMultiplierFromChargeTime(timeElapsed); --math.clamp(timeElapsed / maxBowChargeTime, 0.1, configuration.getConfigurationValue(\"bowMaxChargeDamageMultiplier\"));\n\t\t\t\t\t\t\t\t\t\t\t\t\tpiercesRemaining \t\t= utilities.calculatePierceFromStr(stats.str);\n\t\t\t\t\t\t\t\t\t\t\t\t\tcanAOE\t\t\t\t\t= isMagical;\n\t\t\t\t\t\t\t\t\t\t\t\t\tsourceWeaponBaseData \t= itemBaseData;\n\t\t\t\t\t\t\t\t\t\t\t\t\tarrowId \t\t\t\t= info;\n\t\t\t\t\t\t\t\t\t\t\t\t\tdamageMultiplier\t\t= math.clamp(1 - ((numArrows-1) * 0.15), 0.10, 1)\n\t\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\twarn(\"failed to take arrow(s) from player!\")\n\t\t\t\t\t\t\t\t\t\tend\n\n\t\t--\t\t\t\t\t\t\t\tplayerDamageAnimationState[player] = {\n\t\t--\t\t\t\t\t\t\t\t\tweaponType \t\t\t\t= weaponType;\n\t\t--\t\t\t\t\t\t\t\t\tstate \t\t\t\t\t= animationName;\n\t\t--\t\t\t\t\t\t\t\t\ttimestamp \t\t\t\t= tick();\n\t\t--\t\t\t\t\t\t\t\t\tserverCharacterPosition = playerCurrentPosition;\n\t\t--\t\t\t\t\t\t\t\t\texecutionData \t\t\t= executionData;\n\t\t--\t\t\t\t\t\t\t\t\tdamageBlacklist \t\t= {}\n\t\t--\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tplayerBowAnimationStateData[player] = nil\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tplayerBowAnimationStateData[player] = nil\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\tplayerBowAnimationStateData[player] = nil\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tif not playerDamageAnimationState or isPlayerDamageAnimationStateTransitionValid(player, weaponType, animationSequence, animationName) then\n\t\t\t\t\t\tplayerDamageAnimationState[player] = {\n\t\t\t\t\t\t\tweaponType \t\t= weaponType;\n\t\t\t\t\t\t\tstate \t\t\t= animationName;\n\t\t\t\t\t\t\ttimestamp \t\t= tick();\n\t\t\t\t\t\t\tdamageBlacklist = {}\n\t\t\t\t\t\t}\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function onPlayerRemoving(player)\n\tplayerAbilityHitData \t\t\t\t= nil\n\tplayerBowAnimationStateData[player] = nil\n\tplayerDamageAnimationState[player] \t= nil\n\tarrowsShotByPlayers[player] \t\t= nil\n\tmagicBallsByPlayer[player] \t\t\t= nil\n\n\t-- clear data from arrowsShotByPlayers\n\tfor i, arrowCollectionData in pairs(arrowsShotByPlayers) do\n\t\tfor ii = #arrowCollectionData, 1, -1 do\n\t\t\tif tick() - arrowCollectionData[ii].timestamp >= ARROW_DATA_MAX_LIFETIME then\n\t\t\t\ttable.remove(arrowCollectionData, ii)\n\t\t\tend\n\t\tend\n\tend\n\n\t-- clear data from magicBallsByPlayer\n\tfor i, magicBallCollectionData in pairs(magicBallsByPlayer) do\n\t\tfor ii = #magicBallCollectionData, 1, -1 do\n\t\t\tif tick() - magicBallCollectionData[ii].timestamp >= ARROW_DATA_MAX_LIFETIME then\n\t\t\t\ttable.remove(magicBallCollectionData, ii)\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\tplaceSetup = Modules.placeSetup\n\tutilities = Modules.utilities\n\tmapping = Modules.mapping\n\tlevels = Modules.levels\n\tdamage = Modules.damage\n\tdetection = Modules.detection\n\tconfiguration = Modules.configuration\n\tprojectile = Modules.projectile\n\tevents = Modules.events\n\n\tentityManifestCollectionFolder = placeSetup.getPlaceFolder(\"entityManifestCollection\")\n\n\tnetwork:create(\"entityKillingBlow\", \"BindableEvent\", \"Event\", processEntityKillingBlow)\n\tnetwork:create(\"playerKilledByPlayer\", \"BindableEvent\")\n\tnetwork:create(\"signal_playerKilledByPlayer\", \"RemoteEvent\")\n\tnetwork:create(\"signal_damageModificationByActiveAbility\", \"RemoteEvent\")\n\tnetwork:create(\"playerRequest_damageEntity\", \"RemoteEvent\", \"OnServerEvent\", playerRequest_damageEntity)\n\tnetwork:create(\"playerRequest_damageEntity_server\", \"BindableEvent\", \"Event\", playerRequest_damageEntity)\n\tnetwork:create(\"playerDamageRequest_server\", \"BindableFunction\", \"OnInvoke\", playerDamageRequest_server)\n\tnetwork:create(\"reportMonsterInDamageState_server\", \"BindableEvent\", \"Event\", onReportMonsterInDamageState_server)\n\n--\tif game.PlaceId == 2061558182 then\n\t\tspawn(function()\n\t\t\t-- listen to player animation replication\n\t\t\tnetwork:connect(\"playerAnimationReplicated\", \"Event\", onPlayerAnimationReplicated)\n\n\t\t\tgame.Players.PlayerRemoving:connect(onPlayerRemoving)\n\n\t\t\t-- process player animations here :smile:\n\t\t\tlocal animationsLocation = game.StarterPlayer.StarterPlayerScripts.assets.animations\n\n\t\t\tlocal playerBaseCharacter \t= replicatedStorage.playerBaseCharacter:Clone()\n\t\t\tplayerBaseCharacter.Parent \t= workspace\n\n\t\t\tfor _, obj in pairs(playerBaseCharacter:GetChildren()) do\n\t\t\t\tif obj.Name == \"HumanoidRootPart\" then\n\t\t\t\t\tobj.Anchored = true\n\t\t\t\t\tobj.CanCollide = true\n\t\t\t\t\tobj.Transparency = 0.75\n\t\t\t\telseif obj:IsA(\"BasePart\") then\n\t\t\t\t\tobj.Anchored = false\n\t\t\t\t\tobj.CanCollide = true\n\t\t\t\t\tobj.Transparency = 0.75\n\t\t\t\tend\n\t\t\tend\n\n--\t\t\tplayerBaseCharacter:SetPrimaryPartCFrame(CFrame.new(0, 100, 25))\n\n\t\t\t-- generic melee weapons\n\t\t\tfor i, weaponType in pairs(WEAPON_TYPES_TO_SCAN) do\n\t\t\t\tlocal weaponAnimationModule = animationsLocation[weaponType .. \"Animations\"]\n\t\t\t\tlocal weaponAnimationData \t= require(weaponAnimationModule)\n\n\t\t\t\tlocal validationTable = {}\n\n\t\t\t\t-- startDamageSequence stopDamageSequence\n\t\t\t\tfor name, animationData in pairs(weaponAnimationData) do\n\t\t\t\t\tlocal animation \t\t= Instance.new(\"Animation\")\n\t\t\t\t\tanimation.AnimationId \t= animationData.animationId\n\n\t\t\t\t\tlocal animationTrack = playerBaseCharacter.AnimationController:LoadAnimation(animation)\n\n\t\t\t\t\tanimationTrack:Play()\n\n\t\t\t\t\twhile animationTrack.Length == 0 do wait(0.1) end\n\n\n\n\t\t\t\t\tif name == \"strike1\" then\n\t\t\t\t\t\tvalidationTable.strike1_animationLength \t\t\t= animationTrack.Length\n\t\t\t\t\t\tvalidationTable.strike1_startDamageSequence_time \t= animationTrack:GetTimeOfKeyframe(\"startDamageSequence\")\n\t\t\t\t\t\tvalidationTable.strike1_stopDamageSequence_time \t= animationTrack:GetTimeOfKeyframe(\"stopDamageSequence\")\n\t\t\t\t\t\tvalidationTable.strike1_slash2PeriodStart_time \t\t= animationTrack:GetTimeOfKeyframe(\"slash2PeriodStart\")\n\t\t\t\t\telseif name == \"strike2\" then\n\t\t\t\t\t\tvalidationTable.strike2_animationLength \t\t\t= animationTrack.Length\n\t\t\t\t\t\tvalidationTable.strike2_startDamageSequence_time \t= animationTrack:GetTimeOfKeyframe(\"startDamageSequence\")\n\t\t\t\t\t\tvalidationTable.strike2_stopDamageSequence_time \t= animationTrack:GetTimeOfKeyframe(\"stopDamageSequence\")\n\t\t\t\t\t\tvalidationTable.strike2_slash1PeriodStart_time \t\t= animationTrack:GetTimeOfKeyframe(\"slash1PeriodStart\")\n\t\t\t\t\telseif name == \"strike3\" then\n\t\t\t\t\t\tvalidationTable.strike3_animationLength \t\t\t= animationTrack.Length\n\t\t\t\t\t\tvalidationTable.strike3_startDamageSequence_time \t= animationTrack:GetTimeOfKeyframe(\"startDamageSequence\")\n\t\t\t\t\t\tvalidationTable.strike3_stopDamageSequence_time \t= animationTrack:GetTimeOfKeyframe(\"stopDamageSequence\")\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tweaponValidationData[weaponType] = validationTable\n\t\t\tend\n\n\t\t\t-- process bow animation data here, bows are a bit unique\n\t\t\tdo\n\t\t\t\t-- we're using the actual bow's streching animation and firing animation\n\t\t\t\t-- because this lets us know exactly how long the minimum amount of time\n\t\t\t\t-- must elapse (time of stretching anim + time of firing anim = minimum time)\n\t\t\t\t-- we can offset this later by some multiple to make the speed faster!\n\t\t\t\tlocal bowToolAnimationsModule = animationsLocation.bowToolAnimations_noChar\n\t\t\t\tlocal bowToolAnimations = require(bowToolAnimationsModule)\n\n\t\t\tend\n\n\t\t\t-- process monster animations here :smile:\n\t\t\tfor monsterName, monsterData in pairs(monsterLookup) do\n\t\t\t\tlocal monsterModel = monsterData.entity:Clone()\n\n\t\t\t\tfor _, obj in pairs(monsterModel:GetChildren()) do\n\t\t\t\t\tif obj.Name == \"HumanoidRootPart\" then\n\t\t\t\t\t\tobj.Anchored = true\n\t\t\t\t\t\tobj.CanCollide = true\n\t\t\t\t\t\tobj.Transparency = 0.75\n\t\t\t\t\telseif obj:IsA(\"BasePart\") then\n\t\t\t\t\t\tobj.Anchored = false\n\t\t\t\t\t\tobj.CanCollide = true\n\t\t\t\t\t\tobj.Transparency = 0.75\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tmonsterModel.Parent = workspace\n\t\t\t\tmonsterModel:SetPrimaryPartCFrame(CFrame.new(0, 100, 0))\n\n\t\t\t\t-- open animations folder\n\t\t\t\tmonsterDamageValidationData[monsterName] = {}\n\n\t\t\t\t-- process animations\n\t\t\t\tif monsterModel:FindFirstChild(\"animations\") then\n\t\t\t\t\tlocal animations = monsterModel.animations\n\t\t\t\t\tlocal statesScript = monsterData.module:FindFirstChild(\"states\") and require(monsterData.module:FindFirstChild(\"states\"))\n\t\t\t\t\tif statesScript == nil or not statesScript.states then\n\t\t\t\t\t\tstatesScript = require(replicatedStorage.defaultMonsterState)\n\t\t\t\t\tend\n\t\t\t\t\tfor stateName, stateData in pairs(statesScript.states) do\n\t\t\t\t\t\tif stateData.animationTriggersDamage then\n\t\t\t\t\t\t\tlocal animationName = stateData.animationEquivalent or stateName\n\n\t\t\t\t\t\t\tif animations:FindFirstChild(animationName) then\n\t\t\t\t\t\t\t\tlocal animationTrack = monsterModel.AnimationController:LoadAnimation(monsterModel.animations[animationName])\n\t\t\t\t\t\t\t\tlocal recordingStartTime = animationTrack.Length * 0.3\n\t\t\t\t\t\t\t\tlocal recordingFinishTime = animationTrack.Length * 0.7\n\t\t\t\t\t\t\t\tlocal connections = {}\n\t\t\t\t\t\t\t\tlocal fetched = false\n\t\t\t\t\t\t\t\tlocal animationStartTime = tick()\n\n\t\t\t\t\t\t\t\tlocal damageExtents = {}\n\n\t\t\t\t\t\t\t\tlocal function onAnimationPlayed()\n\t\t\t\t\t\t\t\t\tanimationStartTime = tick()\n\n\t\t\t\t\t\t\t\t\tlocal timeDiff = animationTrack.Length * 0.7 - animationTrack.Length * 0.3\n\t\t\t\t\t\t\t\t\tlocal sampleTimes = {}\n\t\t\t\t\t\t\t\t\tlocal pointsoverride --math.clamp(math.floor(timeDiff / SAMPLE_POINTS_TIME_GRANULARITY + 0.5), 3, math.huge)\n\t\t\t\t\t\t\t\t\tfor i = 1, pointsoverride or SAMPLE_POINTS_TO_TAKE do\n\t\t\t\t\t\t\t\t\t\tsampleTimes[i] = animationTrack.Length * 0.3 + timeDiff * (i - 1) / ((pointsoverride or SAMPLE_POINTS_TO_TAKE) - 1)\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\tlocal maxDistanceAway\n\t\t\t\t\t\t\t\t\twhile animationTrack.IsPlaying do\n\t\t\t\t\t\t\t\t\t\tfor i, sampleTime in pairs(sampleTimes) do\n\t\t\t\t\t\t\t\t\t\t\tif tick() - animationStartTime >= sampleTime then\n\t\t\t\t\t\t\t\t\t\t\t\tfor i, damageHitboxData in pairs(monsterData.damageHitboxCollection) do\n\t\t\t\t\t\t\t\t\t\t\t\t\tlocal boundingBoxCF, boundingBoxSize = monsterModel:GetBoundingBox()\n\t\t\t\t\t\t\t\t\t\t\t\t\tlocal damageHitboxCF = monsterModel[damageHitboxData.partName].CFrame * (damageHitboxData.originOffset or CFrame.new())\n\n\t\t\t\t\t\t\t\t\t\t\t\t\tif not damageExtents[damageHitboxData.partName] then\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tdamageExtents[damageHitboxData.partName] = {}\n\t\t\t\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\t\t\t\ttable.insert(damageExtents[damageHitboxData.partName], damageHitboxCF:toObjectSpace(boundingBoxCF))\n\n\t\t\t\t\t\t\t\t\t\t\t\t\ttable.remove(sampleTimes, i)\n\t\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\twait()\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\tfor i, v in pairs(connections) do\n\t\t\t\t\t\t\t\t\t\tv:disconnect()\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\tfetched = true\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\ttable.insert(connections, monsterModel.AnimationController.AnimationPlayed:connect(onAnimationPlayed))\n\n\t\t\t\t\t\t\t\twhile not (animationTrack.Length > 0) do wait(0.25) end\n\n\t\t\t\t\t\t\t\tanimationTrack.Looped = false\n\n\t\t\t\t\t\t\t\t-- wait for this to update? idk\n\t\t\t\t\t\t\t\twait()\n\n\t\t\t\t\t\t\t\tanimationTrack:Play()\n\n\t\t\t\t\t\t\t\t-- wait for animation stop\n\t\t\t\t\t\t\t\twhile not fetched do wait(0.25) end\n\n--\t\t\t\t\t\t\t\tlocal boundingBoxCF, boundingBoxSize = monsterModel:GetBoundingBox()\n--\t\t\t\t\t\t\t\tfor i, v in pairs(damageExtents) do\n--\t\t\t\t\t\t\t\t\tfor i, vv in pairs(v) do\n--\t\t\t\t\t\t\t\t\t\tlocal line = Instance.new(\"Part\")\n--\t\t\t\t\t\t\t\t\t\tline.Anchored = true\n--\t\t\t\t\t\t\t\t\t\tline.CanCollide = false\n--\t\t\t\t\t\t\t\t\t\tline.TopSurface = Enum.SurfaceType.Smooth\n--\t\t\t\t\t\t\t\t\t\tline.BottomSurface = Enum.SurfaceType.Smooth\n--\t\t\t\t\t\t\t\t\t\tline.Material = Enum.Material.Neon\n--\t\t\t\t\t\t\t\t\t\tline.BrickColor = BrickColor.new(\"Institutional white\")\n--\n--\t\t\t\t\t\t\t\t\t\tline.Size = Vector3.new(0.25, 0.25, 0.25)\n--\t\t\t\t\t\t\t\t\t\tline.CFrame = boundingBoxCF * vv:inverse()\n--\n--\t\t\t\t\t\t\t\t\t\tline.Parent = workspace\n--\n--\t\t\t\t\t\t\t\t\t\tgame:GetService(\"Debris\"):AddItem(line, 2.5)\n--\t\t\t\t\t\t\t\t\tend\n--\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tmonsterDamageValidationData[monsterName] = damageExtents\n\n\t\t\t\t\t\t\t\twait(3)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\twarn(\"invalid monster\", monsterName)\n\t\t\t\tend\n\n\t\t\t\tmonsterModel:Destroy()\n\t\t\tend\n\n\t\t\tplayerBaseCharacter:Destroy()\n\t\tend)\n--\tend\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_dayNightCycle.lua",
    "content": "-- Fluid day and night script\n-- berezaa 6/26/18\n\nlocal DAY_TIME_SECONDS = 1200\n\nif game.PlaceId == 4561988219 or game.PlaceId == 4041427413 then\n\tDAY_TIME_SECONDS = 600\nend\n\nlocal module = {}\n\nlocal network\n\nlocal dynamicParts = {}\nlocal ReplicatedStorage = game.ReplicatedStorage\nlocal assets = ReplicatedStorage.assets\nlocal sunRays = assets.misc.SunRays\nlocal depthOfField = assets.misc.DepthOfField\nlocal sky = assets.misc.Sky\nlocal atmosphere = assets.misc.Atmosphere\n\n-- Run the clock and handle dynamic map elements on the server.\n-- Set Lighting.ClockTime on the client. Tween using the server\n-- time only as a reference.\n\nlocal Rand = Random.new(os.time())\n\nfor _, v in pairs(workspace:GetDescendants()) do\n\tif v.Name == \"WindowPart\" or (v.Name == \"Light\" and v.Parent.Name == \"Lantern\") then\n\t\ttable.insert(dynamicParts,{Part = v, Num = Rand:NextNumber()})\n\tend\nend\n\n-- Sunrise: 5.0 - 6.5\n-- Sunset: 17.6 - 18.6\n\n--[[\ngame.ReplicatedStorage.timeOfDay.Value = 10\ngame.Lighting.ClockTime = 10\ngame.Lighting.ShadowSoftness = 0.2\n]]\n\n\nlocal ClockTime = game.Lighting.ClockTime\ngame.ReplicatedStorage.timeOfDay.Value = ClockTime\n\n\t\tif game.ReplicatedStorage:FindFirstChild(\"lightingSettings\") and game.ReplicatedStorage.lightingSettings:FindFirstChild(\"timeLock\") then\n\t\t\tgame.ReplicatedStorage.timeOfDay.Value = game.ReplicatedStorage.lightingSettings.timeLock.Value\n\t\t\tgame.Lighting.ClockTime = game.ReplicatedStorage.timeOfDay.Value\n\t\tend\n\n-- Start at sunset\n\nlocal function on(Part)\n\tif Part.Name == \"WindowPart\" then\n\t\tPart.Material = Enum.Material.Neon\n\t\tPart.Color = Part:FindFirstChild(\"WindowColor\") and Part.WindowColor.Value or Color3.fromRGB(141, 140, 108)\n\t\tlocal Light = Part:FindFirstChild(\"WindowLight\")\n\t\tif Light == nil then\n\t\t\tLight = Instance.new(\"PointLight\")\n\t\t\tLight.Name = \"WindowLight\"\n\t\t\tLight.Range = 10\n\t\t\tLight.Parent = Part\n\t\tend\n\t\tLight.Enabled = true\n\telseif Part.Name == \"Light\" and Part.Parent and Part.Parent.Name == \"Lantern\" then\n\t\tPart.Color = Color3.fromRGB(248,217,109)\n\t\tPart.Material = Enum.Material.Neon\n\t\tlocal Light = Part:FindFirstChild(\"LanternLight\")\n\t\tPart.Transparency = 0.2\n\t\tif Light then\n\t\t\tLight.Enabled = true\n\t\t\tLight.Range = 25\n\t\t\tLight.Brightness = 1\n\t\tend\n\n\tend\nend\n\nlocal function off(Part)\n\tif Part.Name == \"WindowPart\" then\n\t\tPart.Material = Enum.Material.Plastic\n\t\tPart.Color = Color3.fromRGB(27,42,53)\n\t\tlocal Light = Part:FindFirstChild(\"WindowLight\")\n\t\tif Light then\n\t\t\tLight.Enabled = false\n\t\tend\n\telseif Part.Name == \"Light\" and Part.Parent and Part.Parent.Name == \"Lantern\" then\n\t\tPart.Color= Color3.fromRGB(180, 210, 228)\n\t\tPart.Material = Enum.Material.Glass\n\t\tPart.Transparency = 0.4\n\t\tlocal Light = Part:FindFirstChild(\"LanternLight\")\n\t\tif Light then\n\t\t\tLight.Enabled = false\n\t\tend\n\tend\nend\n\nlocal initiated = false\n\nlocal function mergeColors(dayColor, nightColor, Brightness)\n\tlocal dr, dg, db = Color3.toHSV(dayColor)\n\tlocal nr, ng, nb = Color3.toHSV(nightColor)\n\n\treturn Color3.fromHSV(nr + (dr - nr) * Brightness, ng + (dg - ng) * Brightness, nb + (db - nb) * Brightness)\nend\n\n\n\nif game.Lighting:FindFirstChild(\"SunRays\") == nil then\n\tsunRays:Clone().Parent = game.Lighting\nend\nif game.Lighting:FindFirstChild(\"DepthOfField\") == nil then\n\tdepthOfField:Clone().Parent = game.Lighting\nend\nif game.Lighting:FindFirstChild(\"Sky\") == nil then\n\tsky:Clone().Parent = game.Lighting\nend\nif game.Lighting:FindFirstChild(\"Atmosphere\") == nil then\n\tatmosphere:Clone().Parent = game.Lighting\nend\n\n\ngame.Lighting.EnvironmentDiffuseScale = 0.8\ngame.Lighting.EnvironmentSpecularScale = 0.1\n\nlocal function getVesterianDay()\n\treturn os.time() / DAY_TIME_SECONDS\nend\n\nlocal vesterianDayTag = Instance.new(\"NumberValue\")\nvesterianDayTag.Name = \"vesterianDay\"\nvesterianDayTag.Value = getVesterianDay()\nvesterianDayTag.Parent = game.ReplicatedStorage\n\nspawn(function()\n--\tif game.PlaceId == 2061558182 then return end\n\n\twhile wait(1/5) do\n\n\t\tlocal vesterianDay = getVesterianDay()\n\t\tvesterianDayTag.Value = vesterianDay\n\t\tlocal vesterianDayProgress = vesterianDay - math.floor(vesterianDay)\n\n\t\tClockTime = vesterianDayProgress * 24\n\n\t\tif game.ReplicatedStorage:FindFirstChild(\"lightingSettings\") and game.ReplicatedStorage.lightingSettings:FindFirstChild(\"timeLock\") then\n\t\t\tClockTime = game.ReplicatedStorage.lightingSettings.timeLock.Value\n\t\tend\n\n\t\tgame.ReplicatedStorage.timeOfDay.Value = ClockTime\n\n\n\n--\t\tgame.Lighting.ClockTime = ClockTime\n\n\t\tlocal Brightness = 0\n\t\t-- Night\n\t\tif ClockTime < 5.0 or ClockTime > 18.5 then\n\n\n\t\t\tif not initiated then\n\t\t\t\tinitiated = true\n\t\t\t\tfor i,Object in pairs(dynamicParts) do\n\t\t\t\t\tlocal Part = Object.Part\n\t\t\t\t\tif Part.Name == \"Light\" and Part.Parent.Name == \"Lantern\" then\n\t\t\t\t\t\ton(Part)\n\t\t\t\t\telseif Part.Name == \"WindowPart\" then\n\t\t\t\t\t\toff(Part)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tBrightness = 0\n\t\t\t-- Turn off the window lights throughout the night.\n\t\t\tif ClockTime >= 22 and ClockTime <= 24 then\n\t\t\t\tlocal Progress = (ClockTime - 22) / 2\n\t\t\t\tfor i,Object in pairs(dynamicParts) do\n\t\t\t\t\tlocal Part = Object.Part\n\t\t\t\t\tif Part and Progress >= Object.Num then\n\t\t\t\t\t\tif Part.Name == \"WindowPart\" then\n\t\t\t\t\t\t\toff(Part)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\t-- Sunrise\n\t\telseif ClockTime >= 5.0 and ClockTime <= 6.5 then\n\t\t\tlocal Progress = (ClockTime - 5.0) / 1.5\n\t\t\tBrightness = Progress\n\t\t\tfor i,Object in pairs(dynamicParts) do\n\t\t\t\tlocal Part = Object.Part\n\t\t\t\tif Part and Progress >= Object.Num then\n\t\t\t\t\t-- Turn off everything in the morning\n\t\t\t\t\toff(Part)\n\t\t\t\tend\n\t\t\tend\n\t\t-- Sunset\n\t\telseif ClockTime >= 17.5 and ClockTime <= 18.5 then\n\t\t\tlocal Progress = (ClockTime - 17.5)\n\t\t\tBrightness = 1 - Progress\n\t\t\tfor i,Object in pairs(dynamicParts) do\n\t\t\t\tlocal Part = Object.Part\n\t\t\t\tif Part and Progress >= Object.Num then\n\t\t\t\t\t-- Turn on lanterns and windows at sunset\n\t\t\t\t\ton(Part)\n\t\t\t\tend\n\t\t\tend\n\t\t-- Day\n\t\telse\n\t\t\tBrightness = 1\n\n\t\t\tif not initiated then\n\t\t\t\tinitiated = true\n\t\t\t\tfor i,Object in pairs(dynamicParts) do\n\t\t\t\t\tlocal Part = Object.Part\n\t\t\t\t\tif Part.Name == \"Light\" and Part.Parent.Name == \"Lantern\" then\n\t\t\t\t\t\toff(Part)\n\t\t\t\t\telseif Part.Name == \"WindowPart\" then\n\t\t\t\t\t\toff(Part)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\tend\n\n\n\t\tgame.Lighting.Brightness = 1\n\n\n\t\tlocal fogEndMulti = 0.47\n\t\tif game.ReplicatedStorage:FindFirstChild(\"fogEndMulti\") then\n\t\t\tfogEndMulti = game.ReplicatedStorage.fogEndMulti.Value\n\t\tend\n\n\t\tlocal light = game.ReplicatedStorage:FindFirstChild(\"lightingSettings\")\n\n\t\tlocal dayAmbient = light and light:FindFirstChild(\"dayAmbient\") and light.dayAmbient.Value or (Color3.fromRGB(100, 100, 100))\n\t\tlocal nightAmbient = light and light:FindFirstChild(\"nightAmbient\") and light.nightAmbient.Value or Color3.fromRGB(50, 50, 100)\n\n\n--\t\tlocal dayOutdoorAmbient = light and light:FindFirstChild(\"dayOutdoorAmbient\") and light.dayOutdoorAmbient.Value or Color3.fromRGB(140, 140, 140)\n--\t\tlocal nightOutdoorAmbient = light and light:FindFirstChild(\"nightOutdoorAmbient\") and light.nightOutdoorAmbient.Value or Color3.fromRGB(110, 110, 120)\n--\t\tgame.Lighting.OutdoorAmbient = mergeColors(dayOutdoorAmbient, nightOutdoorAmbient, Brightness)\n\t\tgame.Lighting.OutdoorAmbient = Color3.new(0,0,0)\n\t\t--[[\n\t\tgame.Lighting.Ambient = mergeColors(dayAmbient, nightAmbient, Brightness)\n\t\tlocal dayFogColor = light and light:FindFirstChild(\"dayFogColor\") and light.dayFogColor.Value or Color3.fromRGB(151, 213, 214)\n\t\tlocal nightFogColor = light and light:FindFirstChild(\"nightFogColor\") and light.nightFogColor.Value or Color3.fromRGB(0, 66, 120)\n\t\tgame.Lighting.FogColor = mergeColors(dayFogColor, nightFogColor, Brightness)\n\n\t\tgame.Lighting.Atmosphere.Density = 0.438 - 0.164 * Brightness\n\t\tgame.Lighting.Atmosphere.Color = mergeColors(dayFogColor, nightFogColor, Brightness)\n\t\tgame.Lighting.Atmosphere.Haze = 2.15 - 2.15 * Brightness\n\t\tgame.Lighting.Atmosphere.Glare = 10 * Brightness\n\n\t\tgame.Lighting.ExposureCompensation = Brightness\n\n\t\tgame.Lighting.FogEnd = (500 + 2500 * Brightness) * fogEndMulti\n\t\t]]\n--\t\tgame.Lighting.Ambient = Color3.fromRGB(50 + 50 * Brightness, 50 + 50 * Brightness, 50 + 50 * Brightness)\n--\t\tgame.Lighting.OutdoorAmbient = Color3.fromRGB(90 + 50 * Brightness, 90 + 50 * Brightness, 90 + 50 * Brightness)\n\tend\nend)\n\n\n-- some time of day perks are handled here\n\n\nlocal perkLookup = require(game.ReplicatedStorage.perkLookup)\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\n\tlocal function updateTimeOfDayPerksForPlayer(player, dt)\n\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\tif not playerData then return end\n\n\t\tlocal perks = playerData.nonSerializeData.statistics_final.activePerks\n\t\tfor perkName, active in pairs(perks) do\n\t\t\tif active then\n\t\t\t\tlocal perkData = perkLookup[perkName]\n\t\t\t\tif perkData.onTimeOfDayUpdated then\n\t\t\t\t\tperkData.onTimeOfDayUpdated(player, game.Lighting.ClockTime, dt)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function update(dt)\n\t\tfor _, player in pairs(game:GetService(\"Players\"):GetPlayers()) do\n\t\t\tupdateTimeOfDayPerksForPlayer(player, dt)\n\t\tend\n\tend\n\n\tlocal function startUpdating()\n\t\twhile true do\n\t\t\tupdate(wait(1))\n\t\tend\n\tend\n\n\tspawn(startUpdating)\nend\n\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/contents/manager_effects.lua",
    "content": "local collectionService = game:GetService(\"CollectionService\")\n\nlocal module = {}\n\nlocal function playMapSound(sound)\n\t--assert(sound:IsA(\"Sound\"), \"Non-sound tagged as a mapSound: \"..sound:GetFullName())\n\n\tif sound:IsA(\"Sound\") then\n\t\tsound:Play()\n\tend;\nend\n\ncollectionService:GetInstanceAddedSignal(\"mapSound\"):Connect(playMapSound)\nfor _, sound in pairs(collectionService:GetTagged(\"mapSound\")) do\n\tplayMapSound(sound)\nend\n\nfunction module.init(Modules)\n\tlocal network = Modules.network\n\tnetwork:create(\"effects_requestEffect\", \"RemoteEvent\")\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_enchant.lua",
    "content": "local module = {}\n\nlocal network\n\nlocal numberGenerator_enchantment = Random.new()\n-- item location view is either \"inventory\" or \"equipment\"\nlocal function onEnchantEquipmentRequestReceived(player, enchantmentInventorySlotDataFromPlayer, equipmentInventorySlotDataFromPlayer, itemLocationView, playerInput)\n\titemLocationView = itemLocationView or \"inventory\"\n\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\tif playerData and equipmentInventorySlotDataFromPlayer.id and (itemLocationView == \"inventory\" or itemLocationView == \"equipment\") then\n\t\tlocal itemLocationViewSlot_equipment, itemLocationViewSlotData_equipment do\n\t\t\tif (itemLocationView == \"inventory\") then\n\t\t\t\titemLocationViewSlot_equipment, itemLocationViewSlotData_equipment = getTrueInventorySlotDataByInventorySlotDataFromPlayer(player, equipmentInventorySlotDataFromPlayer)\n\t\t\telseif (itemLocationView == \"equipment\") then\n\t\t\t\titemLocationViewSlot_equipment, itemLocationViewSlotData_equipment = getTrueEquipmentSlotDataByEquipmentSlotDataFromPlayer(player, equipmentInventorySlotDataFromPlayer)\n\t\t\tend\n\t\tend\n\n\t\tif not itemLocationViewSlotData_equipment then\n\t\t\treturn false, \"could not find equipment to encahnt\"\n\t\tend\n\n\t\tif typeof(enchantmentInventorySlotDataFromPlayer) == \"Instance\" and enchantmentInventorySlotDataFromPlayer:FindFirstChild(\"itemEnchanted\") then\n\t\t\t-- enchant menu\n\t\t\tlocal itemBaseData_equipment = itemLookup[itemLocationViewSlotData_equipment.id]\n\t\t\tlocal cost = enchantment.enchantmentPrice(itemLocationViewSlotData_equipment)\n\n\t\t\tif not cost then\n\t\t\t\treturn false, \"invalid enchantment item\"\n\t\t\tend\n\n\t\t\tif not (playerData.gold >= cost) then -- structured this way to prevent nonsense with NaN\n\t\t\t\treturn false, \"not enough monies\"\n\t\t\tend\n\n\t\t\tif (itemLocationViewSlotData_equipment.upgrades or 0) >= (itemLocationViewSlotData_equipment.maxUpgrades or 7) then\n\t\t\t\treturn false, \"max upgraded\"\n\t\t\tend\n\n\t\t\tlocal success = enchantment.applyEnchantment(itemLocationViewSlotData_equipment)\n\n\t\t\tif success then\n\t\t\t\tlocal wasSpent = network:invoke(\"tradeItemsBetweenPlayerAndNPC\", player, {}, cost, {}, 0, \"etc:enchant\")\n\t\t\t\tif not wasSpent then\n\t\t\t\t\tnetwork:invoke(\"reportError\", player, \"error\", \"Enchantment went through but money wasn't spent??\")\n\t\t\t\tend\n\t\t\t\tif itemLocationView == \"equipment\" then\n\t\t\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"equipment\")\n\t\t\t\telse\n\t\t\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"inventory\")\n\t\t\t\tend\n\n\n\t\t\t\tenchantmentInventorySlotDataFromPlayer.itemEnchanted:Play()\n\t\t\t\tif enchantmentInventorySlotDataFromPlayer:FindFirstChild(\"steady\") then\n\t\t\t\t\tenchantmentInventorySlotDataFromPlayer.steady:Emit(50)\n\t\t\t\tend\n\n\t\t\t\tif itemLocationViewSlotData_equipment.blessed then\n\t\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {Text = player.Name .. \"'s \".. (itemBaseData_equipment.name or \"equipment\") ..\" is blessed by The Orb.\"; Font = Enum.Font.SourceSansBold; Color = Color3.fromRGB(0, 81, 255)}\t)\n\t\t\t\tend\n\n\t\t\t\treturn true\n\t\t\tend\n\n\t\t\treturn false, \"idk what went wrong\"\n\t\telseif enchantmentInventorySlotDataFromPlayer.id then\n\t\t\t-- scroll item\n\t\t\tlocal itemBaseData_enchantment = itemLookup[enchantmentInventorySlotDataFromPlayer.id]\n\t\t\tlocal itemBaseData_equipment = itemLookup[equipmentInventorySlotDataFromPlayer.id]\n\n\t\t\tif not itemLocationViewSlotData_equipment.enchantments then\n\t\t\t\titemLocationViewSlotData_equipment.enchantments = {}\n\t\t\tend\n\n\t\t\tif itemBaseData_enchantment and itemBaseData_enchantment.enchantsEquipment and itemBaseData_enchantment.applyScroll and itemBaseData_equipment then\n\t\t\t\tlocal trueInventorySlot_enchantment, inventorySlotData_enchantment = getTrueInventorySlotDataByInventorySlotDataFromPlayer(player, enchantmentInventorySlotDataFromPlayer)\n\n\t\t\t\tif inventorySlotData_enchantment and itemLocationViewSlotData_equipment then\n\t\t\t\t\tlocal upgradeCost = itemBaseData_enchantment.upgradeCost or 1\n\t\t\t\t\tlocal canEnchant, indexToRemove = enchantment.enchantmentCanBeAppliedToItem(inventorySlotData_enchantment, itemLocationViewSlotData_equipment)\n\n\t\t\t\t\tif canEnchant then\n\t\t\t\t\t\tlocal successfullyApplyScroll = numberGenerator_enchantment:NextNumber() <= itemBaseData_enchantment.successRate\n\t\t\t\t\t\t-- .applyScroll is still used on state scrolls to determine if the equipment matches\n\t\t\t\t\t\tlocal success, statusText, additionalInfo = itemBaseData_enchantment.applyScroll(player, itemLocationViewSlotData_equipment, successfullyApplyScroll, playerInput, itemBaseData_enchantment)\n\n\t\t\t\t\t\tadditionalInfo = additionalInfo or {}\n\n\t\t\t\t\t\tif additionalInfo.successfullyApplied ~= nil then\n\t\t\t\t\t\t\tsuccessfullyApplyScroll =  additionalInfo.successfullyApplied\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tlocal status\n\n\t\t\t\t\t\tif success then\n\t\t\t\t\t\t\ttable.remove(playerData.inventory, trueInventorySlot_enchantment)\n\n\t\t\t\t\t\t\tlocal itemDestroyed\n\n\t\t\t\t\t\t\tif successfullyApplyScroll then\n\t\t\t\t\t\t\t\tif itemBaseData_enchantment.enchantments then\n\t\t\t\t\t\t\t\t\tlocal enchantmentWeightTable = {}\n\t\t\t\t\t\t\t\t\tfor i, enchantment in pairs(itemBaseData_enchantment.enchantments) do\n\t\t\t\t\t\t\t\t\t\tif enchantment.selectionWeight and enchantment.selectionWeight > 0 then\n\t\t\t\t\t\t\t\t\t\t\tif not enchantment.manual then\n\t\t\t\t\t\t\t\t\t\t\t\ttable.insert(enchantmentWeightTable, enchantment)\n\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\tlocal selection, index = utilities.selectFromWeightTable(enchantmentWeightTable)\n\n\t\t\t\t\t\t\t\t\tlocal trueIndex\n\t\t\t\t\t\t\t\t\tfor i, enchantment in pairs(itemBaseData_enchantment.enchantments) do\n\t\t\t\t\t\t\t\t\t\tif enchantment == selection then\n\t\t\t\t\t\t\t\t\t\t\ttrueIndex = i\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\tif selection and trueIndex then\n\n\t\t\t\t\t\t\t\t\t\t--local upgradeTier = itemBaseData_enchantment.enchantments[index].tier or 1\n\t\t\t\t\t\t\t\t\t\t--local tierColor = enchantment.tierColors[upgradeTier + 1]\n\t\t\t\t\t\t\t\t\t\tlocal tierColor = Color3.fromRGB(112, 241, 255)\n\t\t\t\t\t\t\t\t\t\tstatus = {Text = \"The magic scroll lights up and its power is transferred to your equipment.\"; Color = additionalInfo.textColor3 or tierColor; Font = Enum.Font.SourceSansBold}\n\n\n\t\t\t\t\t\t\t\t\t\t-- if a weaker upgrade is overridden\n\t\t\t\t\t\t\t\t\t\t-- (this no longer occurs)\n\t\t\t\t\t\t\t\t\t\tif indexToRemove then\n\t\t\t\t\t\t\t\t\t\t\tstatus = {Text = \"The scroll lights up and its power replaces an existing upgrade on your equipment.\"; Color = additionalInfo.textColor3 or tierColor; Font = Enum.Font.SourceSansBold}\n\t\t\t\t\t\t\t\t\t\t\ttable.remove(itemLocationViewSlotData_equipment.enchantments, indexToRemove)\n\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\ttable.insert(itemLocationViewSlotData_equipment.enchantments, {\n\t\t\t\t\t\t\t\t\t\t\tid \t\t= itemBaseData_enchantment.id;\n\t\t\t\t\t\t\t\t\t\t\tstate \t= trueIndex;\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\terror(\"Enchantment data state not found!\")\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t-- scroll failed and it wants to destroy the item\n\t\t\t\t\t\t\t\tif itemBaseData_enchantment.destroyItemOnFail and numberGenerator_enchantment:NextNumber() <= (itemBaseData_enchantment.destroyItemRate or 1) then\n\t\t\t\t\t\t\t\t\t-- update position\n\t\t\t\t\t\t\t\t\tif (itemLocationView == \"inventory\") then\n\t\t\t\t\t\t\t\t\t\titemLocationViewSlot_equipment, itemLocationViewSlotData_equipment = getTrueInventorySlotDataByInventorySlotDataFromPlayer(player, equipmentInventorySlotDataFromPlayer)\n\t\t\t\t\t\t\t\t\telseif (itemLocationView == \"equipment\") then\n\t\t\t\t\t\t\t\t\t\titemLocationViewSlot_equipment, itemLocationViewSlotData_equipment = getTrueEquipmentSlotDataByEquipmentSlotDataFromPlayer(player, equipmentInventorySlotDataFromPlayer)\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\tstatus = {Text = \"The magic scroll's curse destroys your equipment.\"; Color = additionalInfo.textColor3 or Color3.fromRGB(255, 96, 99); Font = Enum.Font.SourceSansBold}\n\t\t\t\t\t\t\t\t\tif itemLocationView == \"inventory\" then\n\t\t\t\t\t\t\t\t\t\ttable.remove(playerData.inventory, itemLocationViewSlot_equipment)\n\t\t\t\t\t\t\t\t\telseif itemLocationView == \"equipment\" then\n\t\t\t\t\t\t\t\t\t\ttable.remove(playerData.equipment, itemLocationViewSlot_equipment)\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\titemDestroyed = true\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t-- let a state of -1 indicate a failed upgrade\n\t\t\t\t\t\t\t\t\tif itemBaseData_enchantment.enchantments then\n\t\t\t\t\t\t\t\t\t\ttable.insert(itemLocationViewSlotData_equipment.enchantments, {\n\t\t\t\t\t\t\t\t\t\t\tid \t\t= itemBaseData_enchantment.id;\n\t\t\t\t\t\t\t\t\t\t\tstate \t= -1;\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tstatus = {Text = \"The magic scroll lights up and explodes into tiny shreds.\"; Color = additionalInfo.textColor3 or Color3.fromRGB(167, 167, 167); Font = Enum.Font.SourceSansBold}\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif not itemDestroyed then\n\t\t\t\t\t\t\t\t-- update the upgrades on the item\n\t\t\t\t\t\t\t\tlocal upgrades = 0\n\t\t\t\t\t\t\t\tlocal successfulUpgrades = 0\n\t\t\t\t\t\t\t\tfor i, enchantmentData in pairs(itemLocationViewSlotData_equipment.enchantments or {}) do\n\t\t\t\t\t\t\t\t\tlocal enchantmentSource = itemLookup[enchantmentData.id]\n\t\t\t\t\t\t\t\t\tlocal upgradeCost = enchantmentSource.upgradeCost or 1\n\t\t\t\t\t\t\t\t\tlocal enchant = enchantmentSource.enchantments[enchantmentData.state]\n\t\t\t\t\t\t\t\t\tif enchant and enchant.manual then\n\t\t\t\t\t\t\t\t\t\tupgradeCost = 0\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tupgrades = upgrades + upgradeCost\n\t\t\t\t\t\t\t\t\t-- state less than 0 indicates a failed upgrade\n\t\t\t\t\t\t\t\t\tif enchantmentData.state >= 0 then\n\t\t\t\t\t\t\t\t\t\tsuccessfulUpgrades = successfulUpgrades + upgradeCost\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\titemLocationViewSlotData_equipment.upgrades = upgrades\n\t\t\t\t\t\t\t\titemLocationViewSlotData_equipment.successfulUpgrades = successfulUpgrades\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif statusText then\n\t\t\t\t\t\t\t\tstatus = {Text = statusText; Color = additionalInfo.textColor3 or Color3.fromRGB(190,190,190); Font = Enum.Font.SourceSansBold}\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"inventory\")\n\n\t\t\t\t\t\t\t-- update item stats if it was equipment\n\t\t\t\t\t\t\tif itemLocationView == \"equipment\" then\n\t\t\t\t\t\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"equipment\")\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tnetwork:fireAllClients(\"playerAppliedScroll\", player, itemBaseData_enchantment.id, successfullyApplyScroll)\n\n\t\t\t\t\t\t\treturn true, successfullyApplyScroll, itemLocationViewSlotData_equipment, status\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif statusText then\n\t\t\t\t\t\t\tstatus = {Text = statusText; Color = additionalInfo.textColor3 or Color3.fromRGB(255, 60, 60); Font = Enum.Font.SourceSansBold}\n\t\t\t\t\t\t\treturn false, false, nil, status\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\treturn false\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tnetwork:create(\"playerRequest_enchantEquipment\", \"RemoteFunction\", \"OnServerInvoke\", onEnchantEquipmentRequestReceived)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_events.lua",
    "content": "local module = {}\n\nlocal network\nlocal events\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tevents = Modules.events\n\tnetwork:create(\"fireEvent\", \"RemoteEvent\", \"OnServerEvent\", function(player, ...)\n\t\tevents:fireEventLocal(...)\n\tend)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_exploit.lua",
    "content": "\nlocal module = {}\n\nlocal network\n\nlocal function onPlayerAdded()\n\nend\n\nlocal function onPlayerRemoving(player)\n\nend\n\nlocal function init__exploiterCheckHeartbeat()\n\nend\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\n\tnetwork:create(\"raisePlayerSuspicion\", \"BindableFunction\", \"OnServerInvoke\", nil)\n\n\tgame.Players.PlayerAdded:connect(onPlayerAdded)\n\tgame.Players.PlayerRemoving:connect(onPlayerRemoving)\n\n\tspawn(init__exploiterCheckHeartbeat)\nend\n\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_firepit.lua",
    "content": "local module = {}\n\nlocal CollectionService = game:GetService(\"CollectionService\")\n\nlocal network\n\nlocal statusEffectsV3 = require(game.ServerStorage.statusEffectsV3)\n\nlocal firepits = CollectionService:GetTagged(\"firepit\")\n\nlocal isPlayerNearFirepit = {}\nlocal DISTANCE_TO_BE_CLOSE = 15\n\nlocal buff_data = {\n\ticon = \"rbxassetid://595268981\";\n\tmodifierData = {\n\t\tmanaRegen \t\t= 2;\n\t\thealthRegen \t= 80;\n\t};\n\n\tDO_NOT_SAVE = true;\n}\n\nlocal function onPlayerRemoving(player)\n\tisPlayerNearFirepit[player] = nil\nend\n\nlocal function loop()\n\twhile wait(1/2) do\n\t\tif #firepits > 0 then\n\t\t\tfor _, player in pairs(game.Players:GetPlayers()) do\n\t\t\t\tif player.Character and player.Character.PrimaryPart then\n\t\t\t\t\tlocal isNearFirepit, distanceAway = false, DISTANCE_TO_BE_CLOSE\n\n\t\t\t\t\tfor _, firepit in pairs(firepits) do\n\t\t\t\t\t\tlocal distFrom = (player.Character.PrimaryPart.Position - firepit.Position).magnitude\n\n\t\t\t\t\t\tif distFrom < DISTANCE_TO_BE_CLOSE and (not firepit:FindFirstChild(\"Fire\") or firepit.Fire.Enabled) then\n\t\t\t\t\t\t\tisNearFirepit \t= true\n\t\t\t\t\t\t\tdistanceAway \t= distFrom\n\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif isNearFirepit ~= not not isPlayerNearFirepit[player] then\n\t\t\t\t\t\tif isNearFirepit then\n\t\t\t\t\t\t\tlocal wasApplied, statusEffectGUID = network:invoke(\"applyStatusEffectToEntityManifest\", player.Character.PrimaryPart, \"empower\", buff_data, player.Character.PrimaryPart, \"firepit\", 0)\n\n\t\t\t\t\t\t\tif wasApplied then\n\t\t\t\t\t\t\t\tlocal untarget \t= Instance.new(\"BoolValue\")\n\t\t\t\t\t\t\t\tuntarget.Name \t= \"isTargetImmune\"\n\t\t\t\t\t\t\t\tuntarget.Value \t= true\n\t\t\t\t\t\t\t\tuntarget.Parent = player.Character.PrimaryPart\n\n\t\t\t\t\t\t\t\tisPlayerNearFirepit[player] = statusEffectGUID\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tif player.Character.PrimaryPart:FindFirstChild(\"isTargetImmune\") then\n\t\t\t\t\t\t\t\tplayer.Character.PrimaryPart.isTargetImmune:Destroy()\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tlocal wasRevoked = network:invoke(\"revokeStatusEffectByStatusEffectGUID\", isPlayerNearFirepit[player])\n\n\t\t\t\t\t\t\t--if wasRevoked then\n\t\t\t\t\t\t\t\tisPlayerNearFirepit[player] = nil\n\t\t\t\t\t\t\t--end\n\t\t\t\t\t\tend\n\t\t\t\t\telseif isNearFirepit and distanceAway < 3 then\n\t\t\t\t\t\t-- is touching firepit, add stacks of ablaze\n\t\t\t\t\t\tlocal statusEffect = statusEffectsV3.createStatusEffect(\n\t\t\t\t\t\t\tplayer.Character.PrimaryPart,\n\t\t\t\t\t\t\tnil,\n\t\t\t\t\t\t\t\"ablaze\",\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tdamage \t\t= 10;\n\t\t\t\t\t\t\t\tduration \t= 10;\n\t\t\t\t\t\t\t}, \"firepit-ablaze\"\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\tif statusEffect then\n\t\t\t\t\t\t\tstatusEffect:start()\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\n\tgame.Players.PlayerRemoving:connect(onPlayerRemoving)\n\n\tspawn(loop)\nend\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/contents/manager_fishing.lua",
    "content": "local module = {}\n\nlocal CollectionService = game:GetService(\"CollectionService\")\n\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal network\nlocal placeSetup\nlocal physics\n\nlocal assetsFolder = ReplicatedStorage.assets\n\nlocal httpService = game:GetService(\"HttpService\")\nlocal itemLookupContainer = ReplicatedStorage.itemData\nlocal itemLookup = require(itemLookupContainer)\n\nlocal LATENCY_FORGIVENESS_AFTER_BOBBING = 2.2\n\n\nlocal spawnRegionCollectionsFolder\n\n-- keep leveling data for the fish here\nlocal fishpedia = {\n\t[\"Fresh Fish\"] = {id = \"fish\"; level = 1; expMulti = 1; favoredRodLevel = 1;};\n\t[\"Zebra Fish\"] = {id = \"pretty pink fish\"; level = 1; expMulti = 1; favoredRodLevel = 1;};\n\t[\"Rock Fish\"] = {id = \"tall blue fish\";   level = 2; expMulti = 1; favoredRodLevel = 1;};\n\n}\n\nlocal function getPoolEntry(fishName, chanceRolls)\n\tif fishpedia[fishName] then\n\t\treturn {\n\t\t\tid = fishpedia[fishName].id;\n\t\t\tlevel = fishpedia[fishName].level;\n\t\t\trolls = chanceRolls;\n\t\t\tfavoredRodLevel = fishpedia[fishName].favoredRodLevel;\n\t\t\texpMulti = fishpedia[fishName].expMulti;\n\t\t}\n\tend\n\treturn false, \"entry does not exist in fishpedia\"\nend\n\n\n-- have the rolls add up to approximately 100 for each map for consistent balance\nlocal fishPool = {\n\t[\"2061558182\"] = {\n\t\t getPoolEntry(\"Fresh Fish\", 50);\n\t\t getPoolEntry(\"Zebra Fish\", 50);\n\n\t};\n\n\t[\"2119298605\"] = {\n\t\t {id = \"fish\"; level = 1; rolls = 20;}\n\t};\n}\n\nlocal baseHitbox\n\n\nlocal playerFishingDataContainer = {}\n\nlocal rand = Random.new()\n\nlocal function tick__fishBobbing(player, guid)\n\twait(rand:NextInteger(4, 10))\n\n\t-- make sure we don't fire for an old fish attempt\n\tif playerFishingDataContainer[player] and playerFishingDataContainer[player].guid == guid then\n\t\t-- check if the bobby is actual in a pile still\n\t\tlocal inRange = false\n\t\tfor _, spawnRegionCollection in pairs(spawnRegionCollectionsFolder:GetChildren()) do\n\t\t\tfor _, child in pairs(spawnRegionCollection.FishFolder:GetChildren()) do\n\t\t\t\tif child.Parent and child:isA(\"Part\")  then\n\t\t\t\t\tif (child.Position - playerFishingDataContainer[player].castPosition).magnitude <= script.spot.Size.Y / 2 +.1 then\n\t\t\t\t\t\tinRange = true\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif inRange then\n\t\t\tplayerFishingDataContainer[player].lastTimeBobbed = tick()\n\t\t\tnetwork:fireClient(\"signal_fishingBobBobbed\", player)\n\t\tend\n\n\t\t-- tick again\n\t\ttick__fishBobbing(player, guid)\n\tend\nend\n\nlocal function playerRequest_startFishing(player, castPosition)\n\tif not playerFishingDataContainer[player] then\n\t\t-- tracking cast position now for security and practical reasons\n\t\tlocal char = player.Character\n\t\tif char then\n\t\t\tif (char.PrimaryPart.Position - castPosition).magnitude > 100 then return end\n\t\telse\n\t\t\treturn\n\t\tend\n\n\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\t\tif playerData then\n\t\t\t-- todo: check for rod, account for strength, etc\n\t\t\tlocal guid = httpService:GenerateGUID(false)\n\n\t\t\tlocal playerFishingData = {}\n\t\t\t\t--playerFishingData.rod \t\t\t= nil\n\t\t\t\tplayerFishingData.lastTimeBobbed \t= 0\n\t\t\t\tplayerFishingData.guid \t\t\t\t= guid\n\t\t\t\tplayerFishingData.castPosition      = castPosition\n\n\n\t\t\tplayerFishingDataContainer[player] = playerFishingData\n\n\t\t\tspawn(function()\n\t\t\t\twait(1)\n\t\t\t\tif playerFishingDataContainer[player] and playerFishingDataContainer[player].guid == guid then\n\t\t\t\t\ttick__fishBobbing(player, guid)\n\t\t\t\tend\n\t\t\tend)\n\n\t\t\treturn true\n\t\tend\n\tend\nend\n\n-- added security\nlocal function playerRequest_reelFishingRod(player, bobPosition)\n\tif playerFishingDataContainer[player] then\n\t\tif tick() - playerFishingDataContainer[player].lastTimeBobbed <= LATENCY_FORGIVENESS_AFTER_BOBBING then -- latency + being quick enough to react, ya dig\n\t\t\tplayerFishingDataContainer[player] = nil\n\n\t\t\t--local fishToSpawnId = {30;30;30;30;30;30;30;30; 38; 39; 40; 41; 42}\n\n\t\t\t-- grab dat fish\n\t\t\tlocal mapPool = fishPool[tostring(game.PlaceId)]\n\t\t\tif mapPool == nil then return end\n\n\t\t\tlocal fishingLevel = network:invoke(\"getProfessionLevel\", player, \"fishing\") or 1\n\n\t\t\t-- do a check with the rod to give a slight edge to the rarer fish. level will unlock the fish and the rod will give you a better chance at catching better fish\n\t\t\t-- formula will be: check if player's rod is >= to the fish's favoredRodLevel, and if so, increase the chances of that specific fish by x% by creating more roll entries\n\t\t\t-- % increase determined by how many rod levels you are above the favored rod level\n\t\t\t-- so if a high level player for whatever reason wants to catch a lower level fish, they have the option to do it by changing rods versus the game forcing them to catch a high level fish even more based off of their level\n\n\t\t\tlocal primaryEquipment = network:invoke(\"getPlayerEquipmentDataByEquipmentPosition\", player, 1)\n\t\t\tif primaryEquipment == nil then return end\n\t\t\tlocal primaryId = primaryEquipment.id\n\n\t\t\tlocal rodLevel = 0\n\n\t\t\tif primaryId == 37 then\n\t\t\t\t-- old fishing pole\n\t\t\t\trodLevel = 1\n\t\t\telse\n\t\t\t\treturn -- that aint no rod\n\t\t\tend\n\n\t\t\tlocal validPool = {}\n\n\t\t\tfor _, fish in pairs(mapPool) do\n\t\t\t\tif fish.level <= fishingLevel then\n\t\t\t\t\tif fish.favoredRodLevel < rodLevel then\n\t\t\t\t\t\tfor _ = 1, fish.rolls do\n\t\t\t\t\t\t\ttable.insert(validPool, fish.id)\n\t\t\t\t\t\tend\n\n\t\t\t\t\telse\n\t\t\t\t\t\tfor _ = 1, fish.rolls + math.clamp((fishingLevel - fish.level)*2, 0,  fish.rolls)  do -- can up to double the rolls on a given map\n\t\t\t\t\t\t\ttable.insert(validPool, fish.id)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif #validPool == 0 then return end\n\n\t\t\tlocal lottery = validPool[rand:NextInteger(1, #validPool)]\n\t\t\tlocal fishToSpawnItemId = itemLookup[lottery].id\n\n\t\t\t-- todo: validation\n\t\t\tlocal fish = network:invoke(\"spawnItemOnGround\", {id = fishToSpawnItemId}, bobPosition + Vector3.new(0, 0.5, 0), {player})\n\t\t\tlocal facingVelocity = (bobPosition - player.Character.PrimaryPart.Position).unit\n\n\t\t\tlocal r = CFrame.new(Vector3.new(), facingVelocity) * CFrame.Angles(-math.pi / 4, math.pi / 8 * (rand:NextInteger() - 0.5) * 2, 0)\n\n\t\t\tlocal fishVelocity \t= -Vector3.new(r.lookVector.X, r.lookVector.Y * 1.4, r.lookVector.Z) * 50\n\t\t\tfish.Velocity \t\t= fishVelocity\n\n\t\t\tlocal attachment = Instance.new(\"Attachment\")\n\t\t\tattachment.Parent = fish\n\n\t\t\tfish:SetNetworkOwner(player)\n\t\t\tfish.HumanoidRootPart:SetNetworkOwner(player)\n\n\n\t\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\t\tif playerData then\n\t\t\t\t-- award xp\n\t\t\t\t--playerData.nonSerializeData.incrementPlayerData(\"exp\",xp)\n\t\t\tend\n\n\t\t\treturn true, fish, fishVelocity\n\t\telse\n\t\t\tplayerFishingDataContainer[player] = nil\n\t\tend\n\tend\nend\n\nlocal function onPlayerRemoving(player)\n\tplayerFishingDataContainer[player] = nil\nend\n\nlocal FISH_SPAWN_CYCLE_TIME \t\t\t= 1.5\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\tplaceSetup = Modules.placeSetup\n\tphysics = Modules.physics\n\n\tspawnRegionCollectionsFolder = placeSetup.getPlaceFolder(\"fishingRegionCollections\")\n\n\tbaseHitbox = assetsFolder.entities.spot:Clone()\n\tbaseHitbox.CanCollide = true\n\tbaseHitbox.Anchored = true\n\tCollectionService:AddTag(baseHitbox, \"fishingSpot\")\n\tphysics:setWholeCollisionGroup(baseHitbox, \"fishingSpots\")\n\n\tgame.Players.PlayerRemoving:connect(onPlayerRemoving)\n\n\tnetwork:create(\"playerRequest_startFishing\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_startFishing)\n\tnetwork:create(\"playerRequest_reelFishingRod\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_reelFishingRod)\n\tnetwork:create(\"signal_fishingBobBobbed\", \"RemoteEvent\")\n\n\tspawn(function()\n\t\twhile wait(0 or FISH_SPAWN_CYCLE_TIME ) do\n\t\t\tif spawnRegionCollectionsFolder == nil then return end\n\n\t\t\tfor _, spawnRegionCollection in pairs(spawnRegionCollectionsFolder:GetChildren()) do\n\t\t\t\t--monsterType, monsterSpawnAmount\n\t\t\t\tlocal _, monsterSpawnAmount = string.match(spawnRegionCollection.Name, \"(.+)-(%d+)\")\n\t\t\t\tmonsterSpawnAmount = tonumber(monsterSpawnAmount)\n\n\t\t\t\tif monsterSpawnAmount then\n\t\t\t\t\tlocal amntToSpawn = monsterSpawnAmount - #spawnRegionCollection.FishFolder:GetChildren()\n\n\t\t\t\t\tif amntToSpawn > 0 then\n\t\t\t\t\t\t-- look up fish stuff to get the stats\n\t\t\t\t\t\tlocal fish = baseHitbox:Clone()\n\n\t\t\t\t\t\tlocal spawnParts = {}\n\t\t\t\t\t\tfor _, child in pairs(spawnRegionCollection:GetChildren()) do\n\t\t\t\t\t\t\tif child:isA(\"Part\") then\n\t\t\t\t\t\t\t\ttable.insert(spawnParts, child)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tlocal randPart = spawnParts[rand:NextInteger(1,#spawnParts)]\n\n\t\t\t\t\t\t-- fix the randomization here\n\t\t\t\t\t\tlocal randomX = rand:NextInteger(randPart.Position.X - randPart.Size.X/2, randPart.Position.X + randPart.Size.X/2)\n\t\t\t\t\t\tlocal randomZ = rand:NextInteger(randPart.Position.Z - randPart.Size.Z/2, randPart.Position.Z + randPart.Size.Z/2)\n\n\t\t\t\t\t\tfish.Position = Vector3.new(randomX, randPart.Position.Y + randPart.Size.Y/2 -.25 , randomZ) --- .75\n\t\t\t\t\t\tfish.effect.Position = fish.Position - Vector3.new(0,.75,0)\n\t\t\t\t\t\tfish.Parent = spawnRegionCollection.FishFolder\n\n\t\t\t\t\t\tgame.Debris:AddItem(fish, rand:NextInteger(20, 60))\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_gift.lua",
    "content": "local module = {}\n\nlocal network\n\n-- TODO: hook back into manager_player\nlocal function playerRequest_claimGifts(player)\n\tlocal playerData = playerDataContainer[player]\n\tif playerData and playerData.globalData then\n\t\tif ((not playerData.globalData.alphaGiftClaimed2) and (not playerData.alphaGiftClaimed2)) or ((not playerData.globalData.alphaGiftClaimed) and (not playerData.alphaGiftClaimed)) then\n\t\t\tif game:GetService(\"BadgeService\"):UserHasBadgeAsync(player.userId, 2124445897) then\n\t\t\t\tif int__tradeItemsBetweenPlayerAndNPC(player, {}, 0, {{id = 158, stacks = 1}}, 50000, \"gift:alpha\") then\n\t\t\t\t\tplayerData.globalData.alphaGiftClaimed2 = true\n\t\t\t\t\tplayerData.alphaGiftClaimed2 = true\n\t\t\t\t\tplayerData.globalData.alphaGiftClaimed = true\n\t\t\t\t\tplayerData.alphaGiftClaimed = true\n\t\t\t\t\treturn true\n\t\t\t\telse\n\t\t\t\t\treturn false, \"Hmm, is your inventory full or something?\"\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif ((not playerData.globalData.spiderGiftClaimed) and (not playerData.spiderGiftClaimed)) then\n\t\t\tif player.Name == \"berezaa\" or game:GetService(\"MarketplaceService\"):PlayerOwnsAsset(player, 4027043310) then\n\t\t\t\tif int__tradeItemsBetweenPlayerAndNPC(player, {}, 0, {{id = 177, stacks = 1}}, 50000, \"gift:spider\") then\n\t\t\t\t\tplayerData.globalData.spiderGiftClaimed = true\n\t\t\t\t\tplayerData.spiderGiftClaimed = true\n\t\t\t\t\treturn true\n\t\t\t\telse\n\t\t\t\t\treturn false, \"Hmm, is your inventory full or something?\"\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\n\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tnetwork:create(\"playerRequest_claimGifts\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_claimGifts)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_guild.lua",
    "content": "local module = {}\n\nlocal HttpService = game:GetService(\"HttpService\")\nlocal TeleportService = game:GetService(\"TeleportService\")\n\nlocal network\n\nmodule.priority = 3\n\nlocal guildRankValues = {\n    member = 1;\n    officer = 2;\n    general = 3;\n    leader = 4;\n}\n\nlocal function getRankNumberFromRank(rank)\n\treturn guildRankValues[rank]\nend\n\n\nlocal DATA_MODIFY_COOLDOWN = 5\n\nlocal guildNameStore = game:GetService(\"DataStoreService\"):GetDataStore(\"guildNames\")\n\nlocal guildPurchases = \t{\n\tcreateGuild = 1e6;\n\tlevel = {\n\t\t[1] = {\n\t\t\tmembers = 10;\n\t\t};\n\t\t[2] = {\n\t\t\tmembers = 20;\n\t\t\tcost = 10e6,\n\t\t};\n\t\t[3] = {\n\t\t\tmembers = 40;\n\t\t\tcost = 100e6,\n\t\t};\n\t\t[4] = {\n\t\t\tmembers = 80;\n\t\t\tcost = 1e9,\n\t\t};\n\t\t[5] = {\n\t\t\tmembers = 140;\n\t\t\tcost = 10e9,\n\t\t}\n\t}\n};\n\nlocal guildDataFolder = Instance.new(\"Folder\")\nguildDataFolder.Name = \"guildDataFolder\"\nguildDataFolder.Parent = game.ReplicatedStorage\n\nlocal messagingService = game:GetService(\"MessagingService\")\n\n\nlocal guildDataCache = {}\nlocal guildMessagingConnections = {}\n\n--[[\n\tsendGuildMessage(guid, {\n\t\tmessageType = \"data_update\";\n\t\tkey = key;\n\t\tvalue = value;\n\t\tsender = player.Name;\n\t\tsenderId = player.userId;\n\t})\n]]\n\n-- Update cache and replicated storage entry with new data.\n\n\nlocal function guildDataUpdated(guid, guildDataEntry)\n\tguildDataCache[guid] = guildDataEntry\n\tlocal dataFolderEntry = guildDataFolder:FindFirstChild(guid)\n\tif dataFolderEntry == nil then\n\t\tdataFolderEntry = Instance.new(\"StringValue\")\n\t\tdataFolderEntry.Name = guid\n\t\tdataFolderEntry.Parent = guildDataFolder\n\tend\n\tdataFolderEntry.Value = HttpService:JSONEncode(guildDataEntry)\n\tfor i,player in pairs(game.Players:GetPlayers()) do\n\t\tif player:FindFirstChild(\"guildId\") and player.guildId.Value == guid then\n\t\t\tnetwork:fireClient(\"signal_guildDataUpdated\", player, guildDataEntry)\n\t\tend\n\tend\nend\n\nlocal function guildMessageRecieved(guid, message)\n\tlocal data = message.Data\n\tlocal timestamp = message.Sent\n\t-- Message to guild members.\n\tif data.messageType == \"chat\" then\n\t\tlocal sender = data.sender\n\t\tlocal message = data.message\n\n\t\tfor i,player in pairs(game.Players:GetPlayers()) do\n\t\t\tif player:FindFirstChild(\"guildId\") and player.guildId.Value == guid then\n\t\t\t\tlocal chatMessage = \"[Guild] \" .. sender .. \": \"..data.message\n\t\t\t\tnetwork:fireClient(\"signal_alertChatMessage\", player, {Text = chatMessage; Font = Enum.Font.SourceSansBold; Color = Color3.fromRGB(160, 116, 255)} )\n\t\t\tend\n\t\tend\n\telseif data.messageType == \"data_update\" then\n\t\tlocal guildData = guildDataCache[guid]\n\t\tif guildData then\n\t\t\tguildData.lastUpdated = timestamp\n\t\t\tguildData[data.key] = data.value\n\t\t\tguildDataUpdated(guid, guildData)\n\t\tend\n\n    elseif data.messageType == \"member_data\" then\n        -- soft update for things like leveling up, changing class\n\t\tlocal guildData = guildDataCache[guid]\n        if guildData then\n            local userId = data.userId\n            guildData.members[tostring(userId)] = data.memberData\n            guildDataUpdated(guid, guildData)\n        end\n\tend\n\t-- allow an optional notice to all guild members via chat\n\tif data.notice then\n\t\tlocal sender = data.sender or \"???\"\n\t\t-- \"Out\" means don't include the message on the server that sent it\n\t\t-- (for level up message)\n\t\tif game.Players:FindFirstChild(sender) == nil or not data.notice.Out then\n\t\t\tif data.notice.Color then\n\t\t\t\tdata.notice.Color = Color3.fromRGB(data.notice.Color.r, data.notice.Color.g, data.notice.Color.b)\n\t\t\tend\n\t\t\tfor i,player in pairs(game.Players:GetPlayers()) do\n\t\t\t\tif player:FindFirstChild(\"guildId\") and player.guildId.Value == guid then\n\n\t\t\t\t\t-- data.notice: {Text = chatMessage; Font = Enum.Font.SourceSansBold; Color = Color3.fromRGB(145, 71, 255)}\n\t\t\t\t\tnetwork:fireClient(\"signal_alertChatMessage\", player, data.notice)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\n-- Send a message via messaging service.\nlocal function sendGuildMessage(guid, messageData)\n\tlocal messageSuccess, messageError = pcall(function()\n\t\tmessagingService:PublishAsync(\"guild-\"..guid, messageData)\n\tend)\n\treturn messageSuccess, messageError\nend\n\nlocal function sendGuildChat(player, message)\n\tlocal guildId = player:FindFirstChild(\"guildId\") and player.guildId.Value\n\tif guildId and guildId ~= \"\" then\n\t\tlocal filteredText\n\t\tlocal filterSuccess, filterError = pcall(function()\n\t\t\tfilteredText = game.Chat:FilterStringForBroadcast(message, player)\n\t\tend)\n\t\tif not filterSuccess then\n\t\t\treturn false, \"filter error: \"..filterError\n\t\telseif #filteredText > 200 then\n\t\t\treturn false, \"Message may not be longer than 200 characters.\"\n\t\telseif #filteredText < 2 then\n\t\t\treturn false, \"Message must be at least 2 characters long\"\n\t\tend\n\t\treturn sendGuildMessage(guildId, {\n\t\t\tmessageType = \"chat\";\n\t\t\tsender = player.Name;\n\t\t\tsenderId = player.userId;\n\t\t\tmessage = filteredText;\n\t\t})\n\telse\n\t\treturn false, \"You're not in a guild!\"\n\tend\nend\n\n-- Used to add/remove a player from a guild.\nlocal function setPlayerGuildId(player, guildId)\n\tif player:FindFirstChild(\"guildId\") then\n\t\tplayer.guildId.Value = guildId or \"\"\n\tend\n\tlocal guildFounderData = network:invoke(\"getPlayerData\", player)\n\tif guildFounderData and guildFounderData.globalData then\n\t\tguildFounderData.globalData.guildId = guildId\n\t\tguildFounderData.nonSerializeData.setPlayerData(\"globalData\", guildFounderData.globalData)\n\tend\nend\n\n-- Fetches guild data, returns cache data if available.\nlocal function getGuildData(player, guid)\n\tif guid == \"\" then\n\t\treturn nil\n\tend\n\tlocal guildDataEntry = guildDataCache[guid]\n\tif guildDataEntry then\n\t\treturn guildDataEntry\n\telse\n\t\tlocal guildDataStore = game:GetService(\"DataStoreService\"):GetDataStore(\"guild\", guid)\n\t\tlocal dataSuccess, dataError = pcall(function()\n\t\t\tguildDataEntry = guildDataStore:GetAsync(\"guildData\")\n\t\tend)\n\t\tif dataSuccess then\n\t\t\tif guildDataEntry then\n\t\t\t\t-- if this guild no longer has me as a member, purge my id\n\t\t\t\tlocal members = guildDataEntry.members\n\t\t\t\tif members and (not members[tostring(player.UserId)]) then\n\t\t\t\t\tsetPlayerGuildId(player, nil)\n\t\t\t\t\treturn nil\n\t\t\t\tend\n\n\t\t\t\t-- okay, we're good to go, return properly\n\t\t\t\tguildDataUpdated(guid, guildDataEntry)\n\t\t\t\treturn guildDataEntry\n\t\t\telse\n\t\t\t\t-- guild no longer exists, purge my id\n\t\t\t\tsetPlayerGuildId(player, nil)\n\t\t\t\treturn nil\n\t\t\tend\n\t\telse\n\t\t\tnetwork:invoke(\"reportError\", player, \"warning\", \"Guild Get Fail: \".. (dataError or \"???\"))\n\t\tend\n\tend\nend\n\nlocal function playerRequest_getGuildData(requestingPlayer, player)\n\tlocal guildId = player:FindFirstChild(\"guildId\")\n\tif not guildId then\n\t\treturn false, \"No guildId found.\"\n\tend\n\treturn getGuildData(player, guildId.Value)\nend\n\n\n\n-- Fetches a player's data entry within a guild, if it exists\nlocal function getGuildPlayerData(player, guid)\n--[[\n\tuserId = partyMemberPlayer.userId;\n\tlevel = partyMemberPlayer.level.Value;\n\tclass = partyMemberPlayer.class.Value;\n\trank = (partyMemberData.isLeader and \"leader\") or \"member\";\n\tfounder = true;\n\tpoints = 0;\n]]\n\tlocal guildData = getGuildData(player, guid)\n\tif guildData == nil then\n\t\treturn false, \"Guild data not found.\"\n\tend\n\tlocal members = guildData.members\n\tlocal guildPlayerData = members[tostring(player.userId)]\n\tif guildPlayerData == nil then\n\t\treturn false, \"Not a member of guild.\"\n\tend\n\treturn guildPlayerData\nend\n\n\nlocal function playerRequest_getGuildMemberData(requestingPlayer, player)\n\tlocal guildId = player:FindFirstChild(\"guildId\")\n\tif not guildId then\n\t\treturn false, \"No guildId found.\"\n\tend\n\treturn getGuildPlayerData(player, guildId.Value)\nend\n\n\n-- Master function used for donating to the guild, other actions\nlocal function modifyGuildDataValue(player, guid, action, key, value, atomic, notice)\n\taction = action or \"set\"\n\tlocal guildData = getGuildData(player, guid)\n\tif guildData == nil then\n\t\treturn false, \"No guild data.\"\n\tend\n\tif os.time() - guildData.lastModified >= DATA_MODIFY_COOLDOWN then\n\t\tlocal guildDataStore = game:GetService(\"DataStoreService\"):GetDataStore(\"guild\", guid)\n\t\tlocal canceledReason\n\t\tlocal dataSuccess, dataError = pcall(function()\n\t\t\tguildDataStore:UpdateAsync(\"guildData\", function(existingData)\n                if atomic then\n --                   if guildData.lastModified == existingData.lastModified then\n                        -- allow messagingService \"soft updates\" to sneak in through here\n\t\t\t\t\t\tguildData.lastModified = os.time()\n                        existingData = guildData\n\n --                   else\n --                       canceledReason = \"Atomic transaction out of sync\"\n --                       return nil\n --                   end\n\t\t\t\tend\n\t\t\t\tif action == \"increment\" then\n\t\t\t\t\texistingData[key] = (existingData[key] or 0) + value\n\t\t\t\telseif action == \"set\" then\n\t\t\t\t\texistingData[key] = value\n\t\t\t\telse\n\t\t\t\t\tcanceledReason = \"Invalid operation\"\n\t\t\t\t\treturn nil\n\t\t\t\tend\n\t\t\t\tguildDataUpdated(guid, existingData)\n\t\t\t\treturn existingData\n\t\t\tend)\n\t\tend)\n\t\tif not dataSuccess then\n\t\t\tnetwork:invoke(\"reportError\", player, \"warning\", \"Failed to update guild value: \"..dataError)\n\t\t\treturn false, \"DataStore error\"\n\t\telseif canceledReason then\n\t\t\treturn false, canceledReason\n\t\telse\n\t\t\treturn true, \"Success!\"\n\t\tend\n\telse\n\t\treturn false, \"Try again later.\"\n\tend\nend\n\n\n-- Removes guild data cache and closes subscriptions.\nlocal function unsubscribeGuildData(guid)\n\tguildDataCache[guid] = nil\n\tif guildMessagingConnections[guid] then\n\t\tguildMessagingConnections[guid]:Disconnect()\n\t\tguildMessagingConnections[guid] = nil\n\tend\n\tlocal dataFolderEntry = guildDataFolder:FindFirstChild(guid)\n\tif dataFolderEntry then\n\t\tdataFolderEntry:Destroy()\n\tend\nend\n\n-- Build a subscription for a guild.\nlocal function subscribeGuild(guid)\n\tif guildMessagingConnections[guid] then\n\t\treturn false, \"Guild subscription already exists.\"\n\tend\n\tif guildDataCache[guid] == nil then\n\t\treturn false, \"Guild data does not exist.\"\n\tend\n\n\tlocal subscribeSuccess, subscribeError = pcall(function()\n\t\tlocal subscription = messagingService:SubscribeAsync(\"guild-\"..guid, function(message)\n\t\t\tguildMessageRecieved(guid, message)\n\t\tend)\n\t\tguildMessagingConnections[guid] = subscription\n\tend)\n\n\tif subscribeSuccess then\n\t\treturn true, \"Subscribed!\"\n\telse\n\t\treturn false, subscribeError\n\tend\nend\n\n\n\n-- Fires whenever a player joins or leaves the server or a guild, used to subscribe/unsubscribe.\nlocal function playerGuildChanged(player, guid)\n\n\tlocal activeGuilds = {}\n\tif guid then\n\t\tactiveGuilds[guid] = true\n\tend\n\n\t-- Player needs a guild but guild data is not active?\n\tif guid and not guildDataCache[guid] then\n\t\tgetGuildData(player, guid)\n\tend\n\n\t-- Cycle through all other players and see which guilds are active.\n\tfor i, otherPlayer in pairs(game.Players:GetPlayers()) do\n\t\tif otherPlayer ~= player  and otherPlayer:FindFirstChild(\"teleporting\") == nil and otherPlayer:FindFirstChild(\"DataSaveFailed\") == nil then\n\t\t\tlocal guildTag = otherPlayer:FindFirstChild(\"guildId\")\n\t\t\tif guildTag and guildTag.Value ~= \"\" then\n\t\t\t\tactiveGuilds[guildTag.Value] = true\n\t\t\tend\n\t\tend\n\tend\n\t-- Cycle through guild cache and remove subscriptions for un-used guilds.\n\tfor guildId, guildData in pairs(guildDataCache) do\n\t\tif not activeGuilds[guildId] then\n\t\t\tunsubscribeGuildData(guildId)\n\t\tend\n\tend\n\t-- Cycle through active guilds and build subscription for any guilds not yet initiated.\n\tfor guildId, active in pairs(activeGuilds) do\n\t\tif active and guildMessagingConnections[guildId] == nil then\n\t\t\t-- Check to confirm cached data exists (it should!)\n\t\t\tlocal guildData = guildDataCache[guildId]\n\t\t\tif guildData then\n\t\t\t\tlocal subscriptionSuccess, subscriptionError = subscribeGuild(guid)\n\t\t\t\tif not subscriptionSuccess then\n\t\t\t\t\tnetwork:invoke(\"reportError\", player, \"warning\", \"Failed to build guild subscription: \"..subscriptionError)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tnetwork:invoke(\"reportError\", player, \"critical\", \"Attempt to build subscription for guild with no data!\")\n\t\t\t\treturn\n\t\t\tend\n\t\tend\n\tend\nend\n\n-- Slap 'em with that guildId tag\nlocal function onPlayerAdded(player)\n\tif player:FindFirstChild(\"guildId\") == nil then\n\t\tlocal guildTag = Instance.new(\"StringValue\")\n\t\tguildTag.Name = \"guildId\"\n\t\tguildTag.Parent = player\n\tend\nend\nfor i,player in pairs(game.Players:GetPlayers()) do\n\tonPlayerAdded(player)\nend\ngame.Players.PlayerAdded:connect(onPlayerAdded)\n\nlocal function onPlayerLoaded(player, playerData)\n\t-- Tag the player's guild so others can see it.\n\tlocal guildTag = player.guildId\n\n\tlocal globalData = playerData.globalData\n\tif globalData and globalData.guildId then\n\t\tlocal guid =  globalData.guildId\n\t\tlocal guildData = getGuildData(player, guid)\n\n\t\tif guildData then\n\t\t\t-- Check that the player is still a member of the guild.\n\t\t\tif guildData.members[tostring(player.userId)] then\n\t\t\t\tguildTag.Value = guid\n\t\t\t\tplayerGuildChanged(player, guid) -- The Msg. subscription isn't made until this point.\n\t\t\telse\n\t\t\t\t-- Removed from guild.\n\t\t\t\tlocal updatedPlayerData = network:invoke(\"getPlayerData\", player)\n\t\t\t\tif updatedPlayerData.globalData then\n\t\t\t\t\tupdatedPlayerData.globalData.guildId = nil\n\t\t\t\t\tupdatedPlayerData.nonSerializeData.setPlayerData(\"globalData\", updatedPlayerData.globalData)\n\t\t\t\tend\n\t\t\t\tnetwork:fireClient(\"alertPlayerNotification\", player, {text = \"You were removed from your guild.\"; textColor3 = Color3.fromRGB(255, 57, 60)}, 10)\n\t\t\t\tplayerGuildChanged(player, nil)\n\t\t\tend\n\t\tend\n    end\n    -- hook up some connections\n    player.level.Changed:connect(function()\n        local guid = player.guildId.Value\n        if guid ~= \"\" then\n            local guildData = getGuildData(player, guid)\n            if guildData then\n                local guildMemberData = guildData.members[tostring(player.userId)]\n                if guildMemberData then\n                    guildMemberData.level = player.level.Value\n\t\t\t\t\tguildMemberData.lastUpdate = os.time();\n                    local notice = {Text = player.Name .. \" has reached Lvl.\"..player.level.Value..\"!\"; Font = \"SourceSansBold\"; Color = {r=161, g=132, b=194}; Out = true}\n\n\t\t\t\t\tsendGuildMessage(guid, {\n                        messageType = \"member_data\";\n                        memberData = guildMemberData;\n                        sender = player.Name;\n                        senderId = player.userId;\n                        notice = notice;\n                    })\n                end\n            end\n        end\n    end)\n    player.class.Changed:connect(function()\n        local guid = player.guildId.Value\n        if guid ~= \"\" then\n            local guildData = getGuildData(player, guid)\n            if guildData then\n                local guildMemberData = guildData.members[tostring(player.userId)]\n                if guildMemberData then\n                    guildMemberData.class = player.class.Value\n                    local notice = {Text = player.Name .. \" has become a \"..player.class.Value..\"!\"; Font = \"SourceSansBold\"; Color = {r=161, g=132, b=194}}\n                    sendGuildMessage(guid, {\n                        messageType = \"member_data\";\n                        memberData = guildMemberData;\n                        sender = player.Name;\n                        senderId = player.userId;\n                        notice = notice;\n                    })\n                end\n            end\n        end\n    end)\nend\n\n\nlocal function onPlayerRemoving(player)\n\tplayerGuildChanged(player, nil)\nend\ngame.Players.PlayerRemoving:connect(onPlayerRemoving)\n\n-- Creating a guild.\nlocal function playerRequest_createGuild(player, properties)\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\tif playerData == nil then\n\t\treturn false, \"no player data.\"\n\tend\n\tlocal globalData = playerData.globalData\n\tif globalData == nil then\n\t\treturn false, \"no global data.\"\n\tend\n\n\tlocal isStudio = game:GetService(\"RunService\"):IsStudio()\n\n\t-- davidii doesn't think this is necessary a'ight? who cares if they make a lot of guilds\n--\tif globalData.lastCreatedGuild and (os.time() - globalData.lastCreatedGuild <= 60 * 60 * 48) and (not isStudio) then\n--\t\treturn false, \"You must wait 48 hours before founding a new guild.\"\n--\tend\n\n\tif player:FindFirstChild(\"teleporting\") or player:FindFirstChild(\"DataSaveFail\") then\n\t\treturn false, \"There is an issue accessing your data, please try again later.\"\n\tend\n\n\tif globalData.guildId then\n\t\treturn false, \"Already in a guild.\"\n\tend\n\n\tlocal partyData = network:invoke(\"getPartyDataByPlayer\", player)\n\tif partyData == nil then\n\t\treturn false, \"Only the leader of a full party can found a guild.\"\n\tend\n\n\tlocal numPlayers = 0\n\n\tlocal guildFounders = {}\n\n\tfor i, partyMemberData in pairs(partyData.members) do\n\t\tnumPlayers = numPlayers + 1\n\t\tlocal partyMemberPlayer = partyMemberData.player\n\t\tlocal partyMemberPlayerData = network:invoke(\"getPlayerData\", partyMemberPlayer)\n\t\tif partyMemberPlayerData == nil or partyMemberPlayerData.globalData == nil then\n\t\t\treturn false, \"A player in your party is missing data.\"\n\t\telseif partyMemberPlayerData.globalData.guildId then\n\t\t\treturn false, \"A player in your party is already in a guild.\"\n\t\telseif partyMemberData.isLeader and partyMemberPlayer ~= player then\n\t\t\treturn false, \"Only the leader of a full party can found a guild.\"\n\t\telseif partyMemberPlayer.level.Value < 10 then\n\t\t\treturn false, \"All players in your party must be at least Lvl. 10 to found a guild.\"\n\t\tend\n\t\tguildFounders[tostring(partyMemberPlayer.userId)] = {\n            lastUpdate = os.time();\n\t\t\tname = partyMemberPlayer.Name;\n\t\t\tuserId = partyMemberPlayer.userId;\n\t\t\tlevel = partyMemberPlayer.level.Value;\n\t\t\tclass = partyMemberPlayer.class.Value;\n\t\t\trank = (partyMemberData.isLeader and \"leader\") or \"member\";\n\t\t\tfounder = true;\n\t\t\tpoints = 0;\n\t\t}\n\tend\n\n\tif (numPlayers ~= 6) and (not isStudio) then\n\t\treturn false, \"There must be exactly six members in your party to found a guild.\"\n\tend\n\n\tlocal guildCreationCost = guildPurchases.createGuild\n\n\tif playerData.gold >= guildCreationCost then\n\n\t\tlocal guildId = HttpService:GenerateGUID(false)\n\n\n\t\t-- Run the desired guild name through the Roblox Filter.\n\t\tlocal desiredGuildName = properties.name\n\t\tif properties.name == nil then\n\t\t\treturn false, \"No guild name provided.\"\n\t\telseif typeof(properties.name) ~= \"string\" then\n\t\t\treturn false, \"Guild name must be a string.\"\n\t\tend\n\t\tlocal filteredText\n\t\tlocal filterSuccess, filterError = pcall(function()\n\t\t\tfilteredText = game.Chat:FilterStringForBroadcast(desiredGuildName, player)\n\t\tend)\n\t\tif not filterSuccess then\n\t\t\treturn false, \"filter error: \"..filterError\n\t\telseif not filteredText or string.find(filteredText, \"#\") then\n\t\t\treturn false, \"Guild name rejected by Roblox filter.\"\n\t\telseif #filteredText > 20 then\n\t\t\treturn false, \"Guild name must be no more than 20 characters long.\"\n\t\telseif #filteredText < 3 then\n\t\t\treturn false, \"Guild name must be at least 3 characters long.\"\n\t\tend\n\n\t\t-- Check the guild name database(TM) for a conflict.\n\t\tlocal isNameTaken\n\t\tlocal nameCheckSuccess, nameCheckError = pcall(function()\n\t\t\tisNameTaken = guildNameStore:GetAsync(filteredText)\n\t\tend)\n\t\tif not nameCheckSuccess then\n\t\t\tnetwork:invoke(\"reportError\", player, \"warning\", \"Guild namestore fail: \".. (nameCheckError or \"???\"))\n\t\t\treturn false, \"Guild name lookup failed due to DataStore error.\"\n\t\telseif isNameTaken then\n\t\t\treturn false, \"That Guild name is already taken.\"\n\t\tend\n\t\tlocal guildName = filteredText\n\n\t\t-- now we jump into the meat.\n\t\tlocal guildDataStore = game:GetService(\"DataStoreService\"):GetDataStore(\"guild\", guildId)\n\t\tlocal abortDueToExistingData\n\t\tlocal guildData\n\t\tlocal guildSuccessfullyCreated\n\n\t\t-- oopsies required from here on out.\n\t\tlocal function applyGuildIdToFounders(guildIdToApply)\n\t\t\tfor userId, userData in pairs(guildFounders) do\n\t\t\t\tlocal guildFounder = game.Players:GetPlayerByUserId(tonumber(userId))\n\t\t\t\tif guildFounder and guildFounder.Parent then\n\t\t\t\t\tsetPlayerGuildId(guildFounder, guildIdToApply)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t-- apply guild membership to all other users (this happens before yielding update async - may cause problems).\n\t\tapplyGuildIdToFounders(guildId)\n\t\tlocal dataSuccess, dataError = pcall(function()\n\t\t\tguildDataStore:UpdateAsync(\"guildData\", function(existingData)\n\t\t\t\t-- In the astronomically rare chance a guild with this data already exists.\n\t\t\t\tif existingData ~= nil then\n\t\t\t\t\tabortDueToExistingData = true\n\t\t\t\t\treturn nil\n\t\t\t\tend\n\t\t\t\t-- We're good.\n\n\t\t\t\tguildData = {\n\t\t\t\t\tname = guildName;\n\t\t\t\t\tpreviousNames = {};\n\t\t\t\t\tmembers = guildFounders;\n\t\t\t\t\tlastModified = os.time();\n\t\t\t\t\tid = guildId;\n\t\t\t\t\tversion = 1;\n\n\t\t\t\t\tlevel = 1;\n\t\t\t\t\tpoints = 0;\n\t\t\t\t\tbank = 0;\n\t\t\t\t}\n\n\t\t\t\t-- we did it!.\n\t\t\t\tguildSuccessfullyCreated = true\n\n\t\t\t\treturn guildData\n\t\t\tend)\n\t\tend)\n\t\tif not dataSuccess then\n\t\t\tnetwork:invoke(\"reportError\", player, \"warning\", \"Guild DataStore fail: \".. (dataError or \"???\"))\n\t\t\tapplyGuildIdToFounders(nil) -- oopsie!\n\t\t\treturn false, \"DataStore failure!\"\n\t\telseif abortDueToExistingData then\n\t\t\tapplyGuildIdToFounders(nil) -- oopsie!\n\t\t\treturn false, \"Guild ID already exists!\"\n\t\tend\n\n\t\t-- final stretch.\n\t\tif guildSuccessfullyCreated then\n\t\t\tlocal globalData = playerData.globalData\n\t\t\tif globalData == nil or player.Parent == nil or player:FindFirstChild(\"teleporting\") or player:FindFirstChild(\"DataSaveFail\") then\n\t\t\t\tapplyGuildIdToFounders(nil) -- oopsie!\n\t\t\t\treturn false, \"Issue with founder data\"\n\t\t\tend\n\n\t\t\t-- take money and apply guild leader data.\n\t\t\tglobalData.lastCreatedGuild = os.time()\n\t\t\tplayerData.nonSerializeData.incrementPlayerData(\"gold\",-guildCreationCost,\"guild:create\")\n\t\t\tglobalData.guildId = guildId\n\t\t\tplayerData.nonSerializeData.setPlayerData(\"globalData\", globalData)\n\n\t\t\t-- run some really scary code that claims the guild name.\n\t\t\tspawn(function()\n\t\t\t\tlocal i = 1\n\t\t\t\tlocal guildNameClaimed\n\t\t\t\tlocal guildNameClaimError\n\t\t\t\trepeat\n\t\t\t\t\tlocal guildSuccess, guildError = pcall(function()\n\t\t\t\t\t\tguildNameStore:SetAsync(filteredText, true)\n\t\t\t\t\tend)\n\t\t\t\t\tif guildSuccess then\n\t\t\t\t\t\tguildNameClaimed = true\n\t\t\t\t\telse\n\t\t\t\t\t\tnetwork:invoke(\"reportError\", player, \"warning\", \"Guild name claim fail: \".. (guildError or \"???\"))\n\t\t\t\t\t\twait(2^i)\n\t\t\t\t\tend\n\t\t\t\tuntil guildNameClaimed\n\t\t\tend)\n\n\t\t\tplayerGuildChanged(player, guildId)\n\n\t\t\t-- we did it guys.\n\t\t\treturn true, \"A new guild has been born!\"\n\t\telse\n\t\t\tapplyGuildIdToFounders(nil) -- oopsie!\n\t\t\treturn false, \"Guild failed to be created for some reason\"\n\t\tend\n\n\n\telse\n\t\treturn false, \"You don't have enough money to found a guild.\"\n\tend\nend\n\nlocal pendingGuildInvites = {}\n\n\nlocal function playerRequest_invitePlayerToGuild(player, invitedPlayer)\n\tif invitedPlayer.guildId.Value ~= \"\" then\n\t\treturn false, \"Player is already in a guild.\"\n\tend\n\tlocal guid = player.guildId.Value\n\tlocal guildPlayerData = getGuildPlayerData(player, guid)\n\tif guildPlayerData == nil then\n\t\treturn false, \"Could not find your guild.\"\n\telseif guildPlayerData.rank ~= \"leader\" and guildPlayerData.rank ~= \"officer\" and guildPlayerData.rank ~= \"general\" then\n\t\treturn false, \"You do not have permission to invite new members.\"\n\tend\n\n\tlocal guildData = getGuildData(player, guid)\n\tif guildData == nil then\n\t\treturn false, \"Could not find guild data.\"\n\tend\n\n\tlocal memberCap = guildPurchases.level[guildData.level].members\n\tlocal existingMembers = 0\n\tfor userId, userData in pairs(guildData.members) do\n\t\texistingMembers = existingMembers + 1\n\tend\n\tif existingMembers >= memberCap then\n\t\treturn false, \"Your guild is already at full capacity.\"\n\tend\n\n\tlocal playerResponse = network:invokeClient(\"serverPrompt_playerInvitedToServer\", invitedPlayer, player, guid)\n\tif playerResponse then\n\t\t-- check nothing changed\n\t\tif player and player.Parent and invitedPlayer and invitedPlayer.Parent and invitedPlayer.guildId.Value == \"\" then\n\t\t\tlocal guildData = getGuildData(player, guid)\n\t\t\tlocal guildPlayerData = getGuildPlayerData(player, guid)\n\t\t\tif guildData and guildPlayerData then\n\t\t\t\t-- add player to the guild\n\t\t\t\tlocal guildMembers = guildData.members\n\t\t\t\t-- failsafe to allow someone to be re-invited if their lost their guild id for whatever reason\n\t\t\t\tguildMembers[tostring(invitedPlayer.userId)] = guildMembers[tostring(invitedPlayer.userId)] or {\n                    lastUpdate = os.time();\n\t\t\t\t\tname = invitedPlayer.Name;\n\t\t\t\t\tuserId = invitedPlayer.userId;\n\t\t\t\t\tlevel = invitedPlayer.level.Value;\n\t\t\t\t\tclass = invitedPlayer.class.Value;\n\t\t\t\t\trank = \"member\";\n\t\t\t\t\tpoints = 0;\n\t\t\t\t}\n\t\t\t\tlocal notice = {Text = invitedPlayer.Name .. \" has joined the Guild. (Invited by \"..player.Name..\")\"; Font = \"SourceSansBold\"; Color = {r=161, g=132, b=194}}\n\t\t\t\tlocal success, reason = modifyGuildDataValue(player, guid, \"set\", \"members\", guildMembers, true, notice)\n\t\t\t\tif success then\n\n\t\t\t\t\tsendGuildMessage(guid, {\n\t\t\t            messageType = \"member_data\";\n\t\t\t            memberData = guildMembers[tostring(invitedPlayer.userId)];\n\t\t\t            sender = player.Name;\n\t\t\t            senderId = player.userId;\n\t\t\t            notice = notice;\n\t\t\t        })\n\n\t\t\t\t\tsetPlayerGuildId(invitedPlayer, guid)\n\t\t\t\t\tguildDataUpdated(guid, guildData)\n\t\t\t\tend\n\t\t\t\treturn success, reason\n\n\t\t\tend\n\t\telse\n\t\t\treturn false, \"Something changed during the player input and they can no longer be added.\"\n\t\tend\n\telse\n\t\treturn false, \"Player did not accept the guild invite.\"\n\tend\nend\n\n\nlocal function playerRequest_exileUserIdFromGuild(player, exiledUserId)\n\tlocal guid = player.guildId.Value\n\tlocal guildPlayerData = getGuildPlayerData(player, guid)\n\tif not guildPlayerData then\n\t\treturn false, \"Could not find your guild.\"\n\tend\n\n\tlocal guildData = getGuildData(player, guid)\n\tif guildData == nil then\n\t\treturn false, \"Guild data not found\"\n\tend\n\n    local exiledGuildPlayerData = guildData.members[tostring(exiledUserId)]\n    if exiledGuildPlayerData == nil then\n        return false, \"Player is not in your guild.\"\n    end\n\n    local exiledUsername = exiledGuildPlayerData.name\n    local playerGuildRankValue = guildRankValues[guildPlayerData.rank]\n    local exiledGuildRankValue = guildRankValues[exiledGuildPlayerData.rank]\n\n    if playerGuildRankValue <= exiledGuildRankValue then\n        return false, \"You cannot exile someone who does not rank beneath you.\"\n    elseif exiledGuildPlayerData.founder and guildPlayerData.rank ~= \"leader\" then\n        return false, \"Only the leader can exile a founding member.\"\n    end\n\n    local guildMembers = guildData.members\n    guildMembers[tostring(exiledUserId)] = nil;\n    local notice = {Text = exiledUsername .. \" has been exiled from the guild by \"..player.Name..\"!\"; Font = \"SourceSansBold\"; Color = {r=161, g=132, b=194}}\n    local success, reason = modifyGuildDataValue(player, guid, \"set\", \"members\", guildMembers, true, notice)\n    if success then\n        local exiledPlayer = game.Players:GetPlayerByUserId(exiledUserId)\n        -- ez boot if they are in the same server.\n        if exiledPlayer then\n            setPlayerGuildId(exiledPlayer, nil)\n        end\n\n        -- booted\n\t\tsendGuildMessage(guid, {\n            messageType = \"member_data\";\n            memberData = nil;\n            sender = player.Name;\n            senderId = player.userId;\n            notice = notice;\n        })\n\t\tguildDataUpdated(guid, guildData)\n        return true, \"Successfully exiled player.\"\n    else\n        return false, \"Failed to exile: \"..reason\n    end\nend\n\n\nlocal function abandonGuild(player, guid)\n\tlocal success, problem = pcall(function()\n\t\tsetPlayerGuildId(player, nil)\n\t\tlocal guildDataStore = game:GetService(\"DataStoreService\"):GetDataStore(\"guild\", guid)\n\t\tlocal guildData = guildDataStore:RemoveAsync(\"guildData\")\n\t\tguildNameStore:RemoveAsync(guildData.name)\n\tend)\n\treturn success, problem\nend\n\nlocal function playerRequest_leaveMyGuild(player, confirmAbandon)\n\tlocal guid = player.guildId.Value\n\tlocal guildPlayerData = getGuildPlayerData(player, guid)\n\tif guildPlayerData == nil then\n\t\treturn false, \"Could not find your guild.\"\n\tend\n\n\tlocal guildData = getGuildData(player, guid)\n\tif guildData == nil then\n\t\treturn false, \"Guild data not found\"\n\tend\n\n\tif guildPlayerData.rank == \"leader\" then\n\t\tlocal memberCount = 0\n\t\tfor _, member in pairs(guildData.members) do\n\t\t\tmemberCount = memberCount + 1\n\t\tend\n\n\t\tif memberCount > 1 then\n\t\t\treturn false, \"If you wish to abandon your guild, you must first exile all other members.\"\n\t\tend\n\n\t\tif confirmAbandon then\n\t\t\treturn abandonGuild(player, guid)\n\t\tend\n\n\t\treturn false, \"confirmAbandon\"\n\tend\n\n\tlocal exiledUserId = player.userId\n    local guildMembers = guildData.members\n    guildMembers[tostring(exiledUserId)] = nil;\n    local notice = {Text = player.Name .. \" has left the guild.\"; Font = \"SourceSansBold\"; Color = {r=161, g=132, b=194}}\n    local success, reason = modifyGuildDataValue(player, guid, \"set\", \"members\", guildMembers, true, notice)\n    if success then\n\n\t\t-- alert other servers that the guild has been left\n\t\tsendGuildMessage(guid, {\n            messageType = \"member_data\";\n            memberData = nil;\n            sender = player.Name;\n            senderId = player.userId;\n            notice = notice;\n        })\n\n        setPlayerGuildId(player, nil)\n\t\tguildDataUpdated(guid, guildData)\n        return true, \"Successfully left the guild.\"\n    else\n        return false, \"Failed to leave: \"..reason\n    end\nend\n\nlocal function playerRequest_changeUserIdRankValue(player, rankedUserId, newRankValue)\n    local guid = player.guildId.Value\n\tlocal guildPlayerData = getGuildPlayerData(player, guid)\n\tif not guildPlayerData then\n\t\treturn false, \"Could not find your guild.\"\n\tend\n\n\tlocal guildData = getGuildData(player, guid)\n\tif guildData == nil then\n\t\treturn false, \"Guild data not found\"\n\tend\n\n    local rankedGuildPlayerData = guildData.members[tostring(rankedUserId)]\n    if rankedGuildPlayerData == nil then\n        return false, \"Player is not in your guild.\"\n    end\n    local rankedUsername = rankedGuildPlayerData.name\n    local playerGuildRankValue = guildRankValues[guildPlayerData.rank]\n    local rankedGuildRankValue = guildRankValues[rankedGuildPlayerData.rank]\n\tlocal isChangingLeaders = (playerGuildRankValue == 4) and (newRankValue == 4)\n    if (newRankValue >= playerGuildRankValue) and (not isChangingLeaders) then\n        return false, \"You cannot rank someone to a rank that is not beneath you.\"\n    elseif rankedGuildRankValue >= playerGuildRankValue then\n        return false, \"You cannot change the rank of someone who does not rank beneath you.\"\n    end\n\n    local newRank = \"member\"\n    for rankName, rankValue in pairs(guildRankValues) do\n        if newRankValue == rankValue then\n            newRank = rankName\n        end\n    end\n\n    local guildMembers = guildData.members\n    guildMembers[tostring(rankedUserId)].rank = newRank\n\tlocal notice = {Text = rankedUsername .. \"'s rank has been changed to \"..newRank..\" by \"..player.Name..\".\"; Font = \"SourceSansBold\"; Color = {r=161, g=132, b=194}}\n\n\tif isChangingLeaders then\n\t\tguildMembers[tostring(player.UserId)].rank = 3\n\t\tnotice.Text = player.Name..\" has stepped down as leader, appointing \"..rankedUsername..\" as the new leader!\"\n\tend\n\n    local success, reason = modifyGuildDataValue(player, guid, \"set\", \"members\", guildMembers, true, notice)\n    if success then\n\t\tguildDataUpdated(guid, guildData)\n\n\t\t-- tell other servers about the promotion\n\t\tsendGuildMessage(guid, {\n            messageType = \"member_data\";\n            memberData = guildMembers[tostring(rankedUserId)];\n            sender = player.Name;\n            senderId = player.userId;\n            notice = notice;\n        })\n\n        return true, \"Successfully ranked player.\"\n    else\n        return false, \"Failed to rank: \"..reason\n    end\nend\n\n\nlocal guildHallPlaceId = 4653017449\n\nlocal hallLocationsByPlaceId = {\n\t[2064647391] = \"mushtown\",\n\t[2119298605] = \"nilgarf\",\n\t[2470481225] = \"warriorStronghold\",\n\t[3112029149] = \"treeOfLife\",\n\t[2546689567] = \"portFidelio\",\n}\nlocal properNamesByHallLocation = {\n\t[\"mushtown\"] = \"Mushtown\",\n\t[\"nilgarf\"] = \"Nilgarf\",\n\t[\"warriorStronghold\"] = \"Warrior Stronghold\",\n\t[\"treeOfLife\"] = \"Tree of Life\",\n\t[\"portFidelio\"] = \"Port Fidelio\",\n}\n\nlocal function getHallLocationFromPlaceId()\n\treturn hallLocationsByPlaceId[game.PlaceId]\nend\n\nlocal function getPlaceIdFromHallLocation(hallLocationIn)\n\tfor placeId, hallLocation in pairs(hallLocationsByPlaceId) do\n\t\tif hallLocation == hallLocationIn then\n\t\t\treturn placeId\n\t\tend\n\tend\n\treturn nil\nend\n\nlocal function playerRequest_getPlaceIdFromHallLocation(player, hallLocation)\n\treturn getPlaceIdFromHallLocation(hallLocation)\nend\n\n\nlocal function resetGuildHallServer()\n\t-- TODO: broadcast a message which boots everyone from guild hall in event of update, move, etc.\nend\n\nlocal function changeGuildHallLocation(player)\n\tlocal guildId = player:FindFirstChild(\"guildId\")\n\tif not guildId then\n\t\treturn false, \"No guildId.\"\n\tend\n\tlocal guid = guildId.Value\n\n\tlocal guildData = getGuildData(player, guid)\n\tif not guildData then\n\t\treturn false, \"No guildData.\"\n\tend\n\n\tlocal memberData = getGuildPlayerData(player, guid)\n\tif not memberData then\n\t\treturn false, \"No memberData.\"\n\tend\n\n\tif memberData.rank ~= \"leader\" then\n\t\treturn false, \"Not leader.\"\n\tend\n\n\tlocal hallLocation = getHallLocationFromPlaceId()\n\tif not hallLocation then\n\t\treturn false, \"No guild hall at this location.\"\n\tend\n\n\tlocal notice\n\tif guildData.hallLocation then\n\t\tnotice = {Text = \"The Guild Hall has been moved from \"..properNamesByHallLocation[guildData.hallLocation]..\" to \"..properNamesByHallLocation[hallLocation]..\"!\"}\n\telse\n\t\tnotice = {Text = \"The Guild Hall has been established at \"..properNamesByHallLocation[hallLocation]..\"!\"}\n\tend\n\tnotice.Font = \"SourceSansBold\"\n\tnotice.Color = {r = 161, g = 132, b = 194}\n\n\tlocal hallServerId = TeleportService:ReserveServer(guildHallPlaceId)\n\tlocal serverIdSuccess, serverIdProblem = modifyGuildDataValue(player, guid, \"set\", \"hallServerId\", hallServerId, false)\n\tif not serverIdSuccess then\n\t\treturn false, serverIdProblem\n\tend\n\n\tlocal success, reason = modifyGuildDataValue(player, guid, \"set\", \"hallLocation\", hallLocation, false, notice)\n\tif success then\n\t\tsendGuildMessage(guid, {\n\t\t\tmessageType = \"data_update\",\n\t\t\tkey = \"hallLocation\",\n\t\t\tvalue = hallLocation,\n\t\t\tsender = player.Name,\n\t\t\tsenderId = player.userId,\n\t\t\tnotice = notice,\n\t\t})\n\n\t\tresetGuildHallServer()\n\n\t\treturn true, \"\"\n\telse\n\t\treturn false, reason\n\tend\nend\n\nlocal function getGuildUpgradeCost(player)\n\tlocal guildId = player:FindFirstChild(\"guildId\")\n\tif not guildId then\n\t\treturn nil, \"No guildId.\"\n\tend\n\tlocal guid = guildId.Value\n\n\tlocal guildData = getGuildData(player, guid)\n\tif not guildData then\n\t\treturn nil, \"No guildData.\"\n\tend\n\n\tlocal nextLevel = guildData.level + 1\n\tif guildPurchases.level[nextLevel] then\n\t\treturn guildPurchases.level[nextLevel].cost, \"\"\n\telse\n\t\treturn nil, \"No next level.\"\n\tend\nend\n\nlocal function upgradeGuild(player)\n\tif (game.PlaceId ~= 2546689567) and (game.PlaceId ~= 2061558182) and (game.PlaceId ~= 3372071669) then\n\t\treturn false, \"Not in Port Fidelio.\"\n\tend\n\n\tlocal guildId = player:FindFirstChild(\"guildId\")\n\tif not guildId then\n\t\treturn false, \"No guildId.\"\n\tend\n\tlocal guid = guildId.Value\n\n\tlocal guildData = getGuildData(player, guid)\n\tif not guildData then\n\t\treturn false, \"No guildData.\"\n\tend\n\n\tlocal memberData = getGuildPlayerData(player, guid)\n\tif not memberData then\n\t\treturn false, \"No memberData.\"\n\tend\n\n\tif memberData.rank ~= \"leader\" then\n\t\treturn false, \"Not leader.\"\n\tend\n\n\tlocal upgradeCost, reason = getGuildUpgradeCost(player)\n\tif not upgradeCost then\n\t\treturn false, reason\n\tend\n\n\tif guildData.bank < upgradeCost then\n\t\treturn false, \"Not enough funds in guild bank.\"\n\tend\n\n\t-- customized logic here since I don't want to do two separate update calls\n\tlocal guildDataStore = game:GetService(\"DataStoreService\"):GetDataStore(\"guild\", guid)\n\tlocal dataSuccess, dataError = pcall(function()\n\t\tguildDataStore:UpdateAsync(\"guildData\", function(data)\n\t\t\tif not data then return end\n\t\t\tif not data.level then return end\n\t\t\tif not data.bank then return end\n\n\t\t\tif data.bank < upgradeCost then return end\n\n\t\t\tdata.bank = data.bank - upgradeCost\n\t\t\tdata.level = data.level + 1\n\n\t\t\tguildDataUpdated(guid, data)\n\t\t\treturn data\n\t\tend)\n\tend)\n\tif not dataSuccess then\n\t\tnetwork:invoke(\"reportError\", player, \"warning\", \"Failed to upgrade guild level: \"..dataError)\n\t\treturn false, \"DataStore error\"\n\tend\n\n\treturn true, \"\"\nend\n\nlocal function donateToGuild(player, amount)\n\tif typeof(amount) ~= \"number\" then\n\t\treturn false, \"Trying to donate not a number.\"\n\tend\n\tamount = math.floor(amount)\n\n\tif amount < 0 then\n\t\treturn false, \"Trying to donate a negative number.\"\n\tend\n\n\tlocal guildId = player:FindFirstChild(\"guildId\")\n\tif not guildId then\n\t\treturn false, \"No guildId.\"\n\tend\n\tlocal guid = guildId.Value\n\n\tlocal guildData = getGuildData(player, guid)\n\tif not guildData then\n\t\treturn false, \"No guildData.\"\n\tend\n\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\tif not playerData then\n\t\treturn false, \"No playerData.\"\n\tend\n\n\tif playerData.gold < amount then\n\t\treturn false, \"Not enough gold.\"\n\tend\n\n\tplayerData.nonSerializeData.incrementPlayerData(\"gold\", -amount, \"guild:donate\")\n\tlocal success, reason = modifyGuildDataValue(player, guid, \"increment\", \"bank\", amount, true)\n\tif not success then\n\t\tplayerData.nonSerializeData.incrementPlayerData(\"gold\", amount, \"guild:donateFailed\")\n\t\treturn false, reason\n\tend\n\n\treturn true, \"\"\nend\n\nlocal function teleportPlayersToGuildHall(players, guid)\n\tlocal leadPlayer = players[1]\n\tlocal guildData = getGuildData(leadPlayer, guid)\n\tif not guildData then\n\t\treturn false, \"No guildData.\"\n\tend\n\n\tlocal hallServerId = guildData.hallServerId\n\tif not hallServerId then\n\t\treturn false, \"No hallServerId.\"\n\tend\n\n\tnetwork:invoke(\"teleportPlayersToReserveServer\", players, guildHallPlaceId, guid, nil, nil, hallServerId)\n\n\treturn true, \"\"\nend\n\nlocal function playerRequest_teleportToGuildHall(player)\n\tlocal guildId = player:FindFirstChild(\"guildId\")\n\tif not guildId then\n\t\treturn false, \"No guildId.\"\n\tend\n\n\tlocal guid = guildId.Value\n\tif guid == \"\" then\n\t\treturn false, \"No guild.\"\n\tend\n\n\tlocal partyData = network:invoke(\"getPartyDataByPlayer\", player)\n\tif partyData then\n\t\t-- teleport the party\n\t\tlocal players = {}\n\n\t\tlocal leader\n\t\tfor _, memberInfo in pairs(partyData.members) do\n\t\t\tlocal member = memberInfo.player\n\t\t\tif memberInfo.isLeader then\n\t\t\t\tleader = member\n\t\t\telse\n\t\t\t\ttable.insert(players, member)\n\t\t\tend\n\t\tend\n\n\t\tif player ~= leader then\n\t\t\treturn false, \"Only the party leader may teleport everyone to a guild hall.\"\n\t\tend\n\n\t\ttable.insert(players, 1, leader)\n\t\tteleportPlayersToGuildHall(players, guid)\n\telse\n\t\t-- teleport the individual\n\t\tteleportPlayersToGuildHall({player}, guid)\n\tend\nend\n\n\nlocal function expelPlayerHelper(player, target)\n\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\tText = target.Name..\" has been expelled by \"..player.Name,\n\t\tFont = Enum.Font.SourceSansBold,\n\t\tColor = Color3.fromRGB(255, 127, 127),\n\t})\n\tnetwork:invoke(\"teleportPlayer\", target, game.ReplicatedStorage.lastLocationOverride.Value, \"guildHall\")\nend\nlocal function expelPlayer(player, target)\n\tif (game.PlaceId ~= guildHallPlaceId) and (game.PlaceId ~= 2061558182) then\n\t\treturn false, \"This command can only be used in the guild hall.\"\n\tend\n\n\tlocal guildId = game.ReplicatedStorage:FindFirstChild(\"guildId\")\n\tif guildId then\n\t\tguildId = guildId.Value\n\telse\n\t\tif game.PlaceId == 2061558182 then\n\t\t\tguildId = \"73370A17-4486-4E7D-AD49-4597D93C52B0\"\n\t\telse\n\t\t\treturn false, \"Couldn't find guildId.\"\n\t\tend\n\tend\n\n\tlocal playerGuildId = player:FindFirstChild(\"guildId\")\n\tlocal targetGuildId = target:FindFirstChild(\"guildId\")\n\tif (not playerGuildId) then\n\t\treturn false, \"You are not a member of a guild.\"\n\telse\n\t\tif playerGuildId.Value ~= guildId then\n\t\t\treturn false, \"You are not a member of this guild.\"\n\t\tend\n\n\t\tif (not targetGuildId) then\n\t\t\texpelPlayerHelper(player, target)\n\t\t\treturn true, \"\"\n\t\telse\n\t\t\tif targetGuildId.Value ~= guildId then\n\t\t\t\texpelPlayerHelper(player, target)\n\t\t\t\treturn true, \"\"\n\t\t\telse\n\t\t\t\tlocal playerData = getGuildPlayerData(player, guildId)\n\t\t\t\tlocal targetData = getGuildPlayerData(target, guildId)\n\n\t\t\t\tif (not playerData) or (not targetData) or (not playerData.rank) or (not targetData.rank) then\n\t\t\t\t\treturn false, \"There was an issue with player data.\"\n\t\t\t\tend\n\n\t\t\t\tlocal playerRank = getRankNumberFromRank(playerData.rank)\n\t\t\t\tlocal targetRank = getRankNumberFromRank(targetData.rank)\n\t\t\t\tif playerRank <= targetRank then\n\t\t\t\t\treturn false, \"You must be a higher rank than your target in order to expel them.\"\n\t\t\t\telse\n\t\t\t\t\texpelPlayerHelper(player, target)\n\t\t\t\t\treturn true, \"\"\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function leaveGuildHall(player)\n\tnetwork:invoke(\"teleportPlayer\", player, game.ReplicatedStorage.lastLocationOverride.Value, \"guildHall\")\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\n\tnetwork:connect(\"playerDataLoaded\", \"Event\", onPlayerLoaded)\n\n\tnetwork:create(\"signal_guildDataUpdated\", \"RemoteEvent\")\n\tnetwork:create(\"sendGuildMessage\", \"BindableFunction\", \"OnInvoke\", sendGuildMessage)\n\tnetwork:create(\"sendGuildChat\", \"BindableFunction\", \"OnInvoke\", sendGuildChat)\n\tnetwork:create(\"getGuildData\", \"BindableFunction\", \"OnInvoke\", getGuildData)\n\tnetwork:create(\"playerRequest_getGuildData\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_getGuildData)\n\tnetwork:create(\"getGuildMemberData\", \"BindableFunction\", \"OnInvoke\", getGuildPlayerData)\n\tnetwork:create(\"playerRequest_getGuildMemberData\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_getGuildMemberData)\n\tnetwork:create(\"playerRequest_createGuild\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_createGuild)\n\tnetwork:create(\"serverPrompt_playerInvitedToServer\", \"RemoteFunction\")\n\tnetwork:create(\"playerRequest_invitePlayerToGuild\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_invitePlayerToGuild)\n\tnetwork:create(\"playerRequest_exileUserIdFromGuild\",\"RemoteFunction\",\"OnServerInvoke\", playerRequest_exileUserIdFromGuild)\n\tnetwork:create(\"playerRequest_leaveMyGuild\",\"RemoteFunction\",\"OnServerInvoke\",playerRequest_leaveMyGuild)\n\tnetwork:create(\"playerRequest_changeUserIdRankValue\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_changeUserIdRankValue)\n\tnetwork:create(\"playerRequest_getHallLocationFromPlaceId\", \"RemoteFunction\", \"OnServerInvoke\", getHallLocationFromPlaceId)\n\tnetwork:create(\"playerRequest_getPlaceIdFromHallLocation\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_getPlaceIdFromHallLocation)\n\tnetwork:create(\"getPlaceIdFromHallLocation\", \"BindableFunction\", \"OnInvoke\", getPlaceIdFromHallLocation)\n\tnetwork:create(\"playerRequest_changeGuildHallLocation\", \"RemoteFunction\", \"OnServerInvoke\", changeGuildHallLocation)\n\tnetwork:create(\"playerRequest_getGuildUpgradeCost\", \"RemoteFunction\", \"OnServerInvoke\", getGuildUpgradeCost)\n\tnetwork:create(\"playerRequest_upgradeGuild\", \"RemoteFunction\", \"OnServerInvoke\", upgradeGuild)\n\tnetwork:create(\"playerRequest_donateToGuild\", \"RemoteFunction\", \"OnServerInvoke\", donateToGuild)\n\tnetwork:create(\"teleportPlayersToGuildHall\", \"BindableFunction\", \"OnInvoke\", teleportPlayersToGuildHall)\n\tnetwork:create(\"playerRequest_teleportToGuildHall\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_teleportToGuildHall)\n\tnetwork:create(\"playerRequest_expelPlayer\", \"RemoteFunction\", \"OnServerInvoke\", expelPlayer)\n\tnetwork:create(\"playerRequest_leaveGuildHall\", \"RemoteFunction\", \"OnServerInvoke\", leaveGuildHall)\n\tnetwork:create(\"getRankNumberFromRank\", \"BindableFunction\", \"OnInvoke\", getRankNumberFromRank)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_interact.lua",
    "content": "local module = {}\n\nlocal CollectionService = game:GetService(\"CollectionService\")\n\nlocal network\n\nlocal function attackInteractionSoundPlayed(player, part, soundName)\n\tif not player then return end\n\tif not player.Character then return end\n\tif not player.Character.PrimaryPart then return end\n\n\tlocal distance = (part.Position - player.Character.PrimaryPart.Position).Magnitude\n\tlocal range = 4 + math.max(part.Size.X, part.Size.Y, part.Size.Z)\n\tif distance < range then\n\t\tnetwork:fireAllClientsExcludingPlayer(\"attackInteractionSoundPlayed\", player, player.Character.PrimaryPart.Position, soundName)\n\tend\nend\n\n\n\nlocal function attackInteractionAttackableAttacked(player, part, hitPosition)\n\tif not player then return end\n\tif not player.Character then return end\n\tif not player.Character.PrimaryPart then return end\n\n\tif not CollectionService:HasTag(part, \"attackable\") then return end\n\tlocal attackableModule = part:FindFirstChild(\"attackableScript\")\n\tif not attackableModule then return end\n\tlocal attackable = require(attackableModule)\n\n\tlocal distance = (part.Position - player.Character.PrimaryPart.Position).Magnitude\n\tlocal range = 4 + math.max(part.Size.X, part.Size.Y, part.Size.Z)\n\tif distance > range then return end\n\n\tattackable.onAttackedServer(player)\n\tnetwork:fireAllClientsExcludingPlayer(\"attackInteractionAttackableAttacked\", player, player, part, hitPosition)\nend\n\n\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tnetwork:create(\"attackInteractionAttackableAttacked\", \"RemoteEvent\", \"OnServerEvent\", attackInteractionAttackableAttacked)\n\tnetwork:create(\"attackInteractionSoundPlayed\", \"RemoteEvent\", \"OnServerEvent\", attackInteractionSoundPlayed)\nend\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/contents/manager_item.lua",
    "content": "-- manages items, duh\n-- author: Polymorphic\n-- editor: berezaa\n\nlocal module = {}\n\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal network\nlocal placeSetup\nlocal physics\nlocal utilities\nlocal configuration\nlocal economy\n\nlocal itemLookupContainer = ReplicatedStorage.itemData\n\nlocal itemLookup = require(itemLookupContainer)\nlocal itemsFolder\n\nlocal assetFolder = ReplicatedStorage:FindFirstChild(\"assets\")\nlocal itemManfiests = assetFolder.items\nlocal particleStorage = assetFolder.particles\nlocal entityStorage = assetFolder.entities\n\nlocal function getItemBaseDataFromItemsPart(itemPart)\n\tlocal itemDataModule = itemLookupContainer:FindFirstChild(itemPart.Name)\n\tif itemDataModule then\n\t\treturn require(itemDataModule)\n\tend\nend\n\nlocal IGNORE_LIST = {}\n\n-- TODO: make important settings like below in a module to be easily sync'd between server and client scripts\nlocal _SERVER_ACQUISITION_RANGE = 10\nlocal itemPickupDebounce = {}\nlocal CONSUMABLE_COOLDOWN_TIME = 1\nlocal playerConsumeCooldownTable = {}\n\nlocal function processPlayerPickUpItem(player, itemPart, isPickUpFromPet)\n\tif not itemPart or itemPart.Parent ~= itemsFolder then\n\t\treturn false, \"item does not exist\"\n\telseif itemPickupDebounce[itemPart] then\n\t\treturn false, \"attempt to pick-up an item someone else is picking up\"\n\telseif not itemPart:FindFirstChild(\"metadata\") then\n\t\treturn false, \"attempt to pick-up an invalid item\"\n\telseif not utilities.playerCanPickUpItem(player, itemPart, isPickUpFromPet) then\n\t\treturn false, \"can't pick-up this item\"\n\tend\n\n\tlocal itemBaseData = getItemBaseDataFromItemsPart(itemPart)\n\tif itemBaseData then\n\t\titemPickupDebounce[itemPart] = true\n\n\t\tlocal metadata\n\n\t\tlocal success, resultMessage, value\n\t\tif not itemBaseData.autoConsume then\n\t\t\t-- request the inventory service to add this item into the inventory\n\t\t\tlocal success2, returnValue = utilities.safeJSONDecode(itemPart.metadata.Value)\n\n\t\t\tif success2 then\n\t\t\t\tmetadata = returnValue\n\t\t\t\tsuccess, resultMessage = network:invoke(\"tradeItemsBetweenPlayerAndNPC\",  player, {}, 0, {returnValue}, 0, nil, {overrideItemsRecieved = true})\n\t\t\telse\n\t\t\t\tmetadata = itemBaseData\n\t\t\t\tsuccess, resultMessage = false, \"failed to decode metadata\"\n\t\t\tend\n\t\telse\n\t\t\t-- added physical item part to function for gold\n\t\t\tmetadata = itemBaseData\n\t\t\tsuccess, resultMessage, value = itemBaseData.activationEffect(player, itemPart)\n\t\tend\n\n\t\t-- let the client know if they picked it up\n\t\tnetwork:fireClient(\"notifyPlayerPickUpItem\", player, metadata, success, value, nil, resultMessage)\n\n\t\tif success then\n\t\t\t-- fire quest trigger occured\n\t\t\tnetwork:fire(\"questTriggerOccurred\", player, \"item-collected\", {id = itemBaseData.id; amount = 1})\n\n\t\t\t-- remove player from owners\n\t\t\tif itemPart:FindFirstChild(\"owners\") then\n\t\t\t\tif itemPart.owners:FindFirstChild(tostring(player.userId)) then\n\t\t\t\t\titemPart.owners[tostring(player.userId)]:Destroy()\n\t\t\t\telse\n\t\t\t\t\tfor i, ownerTag in pairs(itemPart.owners:GetChildren()) do\n\t\t\t\t\t\tif ownerTag.Value == player then\n\t\t\t\t\t\t\townerTag:Destroy()\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif not itemPart:FindFirstChild(\"pickupBlacklist\") then\n\t\t\t\tlocal pickupBlacklist \t= Instance.new(\"Folder\")\n\t\t\t\tpickupBlacklist.Name \t= \"pickupBlacklist\"\n\t\t\t\tpickupBlacklist.Parent \t= itemPart\n\t\t\tend\n\n\t\t\tlocal blacklistTag \t= Instance.new(\"BoolValue\")\n\t\t\tblacklistTag.Name \t= tostring(player.userId)\n\t\t\tblacklistTag.Value \t= true\n\t\t\tblacklistTag.Parent = itemPart.pickupBlacklist\n\n\t\t\t-- destroy the itemPart on the server, can no longer be picked up\n\t\t\tif itemPart:FindFirstChild(\"singleOwnerPickup\") or not itemPart:FindFirstChild(\"owners\") or #itemPart.owners:GetChildren() == 0 then\n\t\t\t\titemPart:Destroy()\n\t\t\telseif itemPart:FindFirstChild(\"created\") and (os.time() - itemPart.created.Value) >= configuration.getConfigurationValue(\"timeForAnyonePickupItem\") then\n\t\t\t\titemPart:Destroy()\n\t\t\tend\n\t\tend\n\n\t\t-- free the item up\n\t\titemPickupDebounce[itemPart] = nil\n\n\t\treturn success, resultMessage, value\n\tend\n\nend\n\nlocal function playerRequest_pickUpItem(player, itemPart)\n\n\tif player.Character == nil or player.Character.PrimaryPart == nil or player.Character.PrimaryPart:FindFirstChild(\"state\") == nil or player.Character.PrimaryPart.state.Value == \"dead\" then\n\t\treturn false, \"Invalid player or dead\"\n\tend\n\n\tif not itemPart or not utilities.playerCanPickUpItem(player, itemPart) then\n\t\treturn false, \"Unable to pick up\"\n\tend\n\n\tif player.Character and player.Character.PrimaryPart and itemPart and typeof(itemPart) == \"Instance\" and itemPart:IsDescendantOf(itemsFolder) then\n\t\tif utilities.magnitude(player.Character.PrimaryPart.Position - itemPart.Position) <= _SERVER_ACQUISITION_RANGE * 1.1 then\n\t\t\treturn processPlayerPickUpItem(player, itemPart, false)\n\t\telse\n\t\t\treturn false, \"Too far away\"\n\t\tend\n\tend\n\n\treturn false, \"Invalid\"\nend\n\nlocal function prepareManifest(physItem)\n\n\tif physItem:IsA(\"BasePart\") then\n\t\t-- damiens weld thingie\n\t\tfor _, Child in pairs(physItem:GetChildren()) do\n\t\t\tif Child:IsA(\"BasePart\") then\n\t\t        local motor6d = Instance.new(\"Motor6D\")\n\t\t        motor6d.Part0 = physItem\n\t\t        motor6d.Part1 = Child\n\t\t        motor6d.C0 = CFrame.new()\n\t\t        motor6d.C1 = Child.CFrame:toObjectSpace(physItem.CFrame)\n\t\t\t\tmotor6d.Parent = Child\n\t\t\t\tChild.CanCollide = false\n\t\t\t\tChild.Anchored = false\n\t\t\tend\n\t\tend\n\telseif physItem:IsA(\"Model\") then\n\t\tlocal primaryPartForPhysItem = physItem.PrimaryPart\n\n\t\t-- transform into the structure we know and love!\n\t\tfor _, child in pairs(physItem:GetChildren()) do\n\t\t\tif child:IsA(\"BasePart\") and child ~= primaryPartForPhysItem then\n\t\t        local motor6d = Instance.new(\"Motor6D\")\n\t\t        motor6d.Part0 = primaryPartForPhysItem\n\t\t        motor6d.Part1 = child\n\t\t        motor6d.C0 = CFrame.new()\n\t\t        motor6d.C1 = child.CFrame:toObjectSpace(primaryPartForPhysItem.CFrame)\n\t\t\t\tmotor6d.Parent = child\n\n\t\t\t\tchild.CanCollide = false\n\t\t\t\tchild.Anchored = false\n\t\t\t\tchild.Parent = primaryPartForPhysItem\n\t\t\tend\n\t\tend\n\n\t\tphysItem = primaryPartForPhysItem\n\tend\n\n\treturn physItem\nend\n\nlocal function generateItemManifest(itemDropData, physItem)\n\tlocal itemBaseData = itemLookup[itemDropData.id]\n\n\tlocal itemAsset = itemManfiests:FindFirstChild(itemBaseData.module.Name)\n\tassert(itemAsset, \"Item manifest not found for \" .. itemBaseData.module.Name)\n\n\tif physItem then\n\t\tphysItem = prepareManifest(physItem)\n\telse\n\t\tif itemBaseData.equipmentType == \"arrow\" then\n\t\t\t-- arrows get special treatment\n\t\t\tif itemDropData.stacks == 1 then\n\t\t\t\tphysItem = itemAsset.manifest:Clone()\n\t\t\t\tphysItem.CanCollide = false\n\t\t\t\tphysItem.Anchored = false\n\t\t\telse\n\t\t\t\t-- todo: how to do multiple quivers?\n\t\t\t\tphysItem = assetFolder.entities.ArrowUpperTorso2.quiver:Clone()\n\t\t\t\tphysItem.Anchored = false\n\t\t\t\tphysItem.CanCollide = false\n\n\t\t\t\tlocal arrows = itemDropData.stacks or 0\n\t\t\t\tlocal arrowParts = math.clamp(math.floor(arrows / configuration.getConfigurationValue(\"arrowsPerArrowPartVisualization\")) + 1, 0, configuration.getConfigurationValue(\"maxArrowPartsVisualization\"))\n\t\t\t\tlocal degPerRot = 360 / configuration.getConfigurationValue(\"maxArrowPartsVisualization\")\n\t\t\t\tfor ai = 1, arrowParts do\n\t\t\t\t\tlocal arrow = itemAsset.manifest:Clone()\n\t\t\t\t\tarrow.CanCollide = false\n\t\t\t\t\tarrow.Anchored = false\n\t\t\t\t\tarrow.Parent = physItem\n\n\t\t\t\t\tlocal xRan, yRan = math.random() * 2 - 1, math.random() * 2 - 1\n\n\t\t\t\t\tlocal arrowWeld = Instance.new(\"Motor6D\", physItem)\n\t\t\t\t\tarrowWeld.Name = \"projectionWeld\"\n\t\t\t\t\tarrowWeld.Part0 = physItem\n\t\t\t\t\tarrowWeld.Part1 = arrow\n\t\t\t\t\tarrowWeld.C0 = physItem.Attachment.CFrame\n\t\t\t\t\tarrowWeld.C1 = CFrame.Angles(xRan * math.rad(15), 0, yRan * math.rad(15))-- * CFrame.Angles(0.25 * math.rad(degPerRot * ai), 0, 0.25 * math.rad(degPerRot * ai))\n\t\t\t\tend\n\t\t\tend\n\t\telseif itemAsset:FindFirstChild(\"manifest\", true) then\n\t\t\tphysItem = itemAsset:FindFirstChild(\"manifest\", true):Clone()\n\t\t\tphysItem = prepareManifest(physItem)\n\t\telseif itemAsset:FindFirstChild(\"container\") then\n\t\t\tlocal primaryPart = itemAsset.container.PrimaryPart\n\n\t\t\tphysItem = primaryPart:Clone()\n\t\t\tphysItem:ClearAllChildren()\n\n\t\t\tfor _, vv in pairs(primaryPart:GetChildren()) do\n\t\t\t\tif vv:IsA(\"BasePart\") then\n\t\t\t\t\tlocal part = vv:Clone()\n\n\t\t\t\t\tlocal motor6d = Instance.new(\"Motor6D\")\n\t\t\t\t        motor6d.Part0 = part\n\t\t\t\t        motor6d.Part1 = physItem\n\t\t\t\t        motor6d.C0 = CFrame.new()\n\t\t\t\t        motor6d.C1 = primaryPart.CFrame:toObjectSpace(vv.CFrame)\n\t\t\t\t\t\tmotor6d.Parent = part\n\n\t\t\t\t\tpart.Anchored = false\n\t\t\t\t\tpart.CanCollide = false\n\n\t\t\t\t\tpart.Parent = physItem\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tfor _, v in pairs(itemAsset.container:GetChildren()) do\n\t\t\t\tif v ~= primaryPart then\n\t\t\t\t\tlocal part = v:Clone()\n\n\t\t\t\t\tlocal motor6d = Instance.new(\"Motor6D\")\n\t\t\t\t        motor6d.Part0 = physItem\n\t\t\t\t        motor6d.Part1 = part\n\t\t\t\t        motor6d.C0 = CFrame.new()\n\t\t\t\t        motor6d.C1 = v.CFrame:toObjectSpace(primaryPart.CFrame)\n\t\t\t\t\t\tmotor6d.Parent = physItem\n\n\t\t\t\t\tpart.Anchored = false\n\t\t\t\t\tpart.CanCollide = false\n\n\t\t\t\t\tfor _, vv in pairs(part:GetChildren()) do\n\t\t\t\t\t\tif vv:IsA(\"BasePart\") then\n\t\t\t\t\t        local motor6d = Instance.new(\"Motor6D\")\n\t\t\t\t\t        motor6d.Part0 = part\n\t\t\t\t\t        motor6d.Part1 = vv\n\t\t\t\t\t        motor6d.C0 = CFrame.new()\n\t\t\t\t\t        motor6d.C1 = vv.CFrame:toObjectSpace(part.CFrame)\n\t\t\t\t\t\t\tmotor6d.Parent = vv\n\n\t\t\t\t\t\t\tvv.CanCollide = false\n\t\t\t\t\t\t\tvv.Anchored = false\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tpart.Parent = physItem\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\terror(\"attempt to drop item with invalid drop format\")\n\t\tend\n\tend\n\n\tphysItem.Name = itemBaseData.module.Name\n\tphysItem.Anchored = false\n\tphysItem.CanCollide = false\n\n\tif itemDropData.id == 1 then\n\t\tif itemDropData.value >= 1000 then\n\t\t\tphysItem.Color = Color3.fromRGB(160,160,160)\n\t\tend\n\tend\n\n\tlocal itemMask = assetFolder.entities.itemMask:Clone()\n\n\titemMask.Name = \"HumanoidRootPart\"\n\titemMask.RootPriority = 100\n\titemMask.CustomPhysicalProperties = PhysicalProperties.new(5, 1, 0.7)\n\n\titemMask.Parent = physItem\n\titemMask.Size = Vector3.new(physItem.Size.X * 1.1, physItem.Size.Y * 1.1, physItem.Size.Z * 1.1)\n\n\tlocal weld = Instance.new(\"Motor6D\")\n\tweld.Part0 = physItem\n\tweld.Part1 = itemMask\n\tweld.Parent = physItem\n\tweld.Name = \"MASK_MOTOR\"\n\n\tlocal dye = itemDropData.dye\n\n\tif dye then\n\t\tphysItem.Color = Color3.new(physItem.Color.r * dye.r/255, physItem.Color.g * dye.g/255, physItem.Color.b * dye.b/255)\n\t\tfor _, v in pairs(physItem:GetDescendants()) do\n\t\t\tif v:IsA(\"BasePart\") then\n\t\t\t\tif dye then\n\t\t\t\t\tv.Color = Color3.new(v.Color.r * dye.r/255, v.Color.g * dye.g/255, v.Color.b * dye.b/255)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal size = (math.sqrt(physItem.Size.X * physItem.Size.Y) + math.sqrt(physItem.Size.Z, physItem.Size.Y))\n\n\tif not itemDropData.isNotDropping then\n\t\tlocal itemInfo = itemBaseData\n\t\tif (itemInfo.rarity and itemInfo.rarity == \"Rare\") or (itemInfo.category and itemInfo.category == \"equipment\") then\n\t\t\titemInfo.soulboundDrop = true\n\t\t\t--[[\n\t\t\tlocal attach = script.rareItem.Attachment:Clone()\n\t\t\tattach.Parent = physItem\n\t\t\t]]\n\t\t\tfor _, child in pairs(entityStorage.rareItem:GetChildren()) do\n\t\t\t\tchild:Clone().Parent = physItem\n\t\t\tend\n\t\telse\n\t\t\tlocal rays = particleStorage.Rays:Clone()\n\t\t\tlocal attach = Instance.new(\"Attachment\")\n\t\t\tattach.Axis = Vector3.new(1,0,0)\n\t\t\tattach.SecondaryAxis = Vector3.new(0,1,0)\n\t\t\trays.Parent = attach\n\t\t\tattach.Parent = physItem\n\t\t\trays.Size = NumberSequence.new(size * 1.3)\n\n\t\t\tlocal sparkles = particleStorage.Sparkles:Clone()\n\t\t\tsparkles.Parent = attach\n\t\tend\n\n\t\tlocal light = Instance.new(\"PointLight\")\n\t\tlight.Brightness = 1.5\n\t\tlight.Range = 8\n\t\tlight.Parent = itemMask\n\n\t\tlocal attachmentTarget\n\n\t\tif physItem:IsA(\"BasePart\") then\n\t\t\tattachmentTarget = physItem\n\t\telseif physItem:IsA(\"Model\") and (physItem.PrimaryPart or physItem:FindFirstChild(\"HumanoidRootPart\")) then\n\t\t\tlocal primaryPart = physItem.PrimaryPart or physItem:FindFirstChild(\"HumanoidRootPart\")\n\t\t\tif primaryPart then\n\t\t\t\tattachmentTarget = primaryPart\n\t\t\tend\n\t\tend\n\n\t\tlocal topAttachment = Instance.new(\"Attachment\", physItem)\n\t\t\ttopAttachment.Position = Vector3.new(0, itemMask.Size.Y / 2, 0)\n\n\t\tlocal bottomAttachment = Instance.new(\"Attachment\", physItem)\n\t\t\tbottomAttachment.Position = Vector3.new(0, -itemMask.Size.Y / 2, 0)\n\n\t\tlocal trail = assetFolder.misc.Trail:Clone()\n\t\ttrail.Attachment0 = topAttachment\n\t\ttrail.Attachment1 = bottomAttachment\n\t\ttrail.Enabled = true\n\t\ttrail.Parent = physItem\n\n\tend\n\n\tphysItem.Anchored = false\n\n\treturn physItem\nend\n\nlocal function spawnItemOnGround(lootDrop, dropPosition, owners, physItem)\n\tif itemLookup[lootDrop.id] then\n\t\t-- todo: refactor\n\n\t\tphysItem = generateItemManifest(lootDrop, physItem)\n\n\t\tlocal hitPart, hitPosition\n\t\tlocal tries = 0\n\n\t\tlocal success, val = utilities.safeJSONEncode(lootDrop or {})\n\n\t\tlocal metadataTag \t= Instance.new(\"StringValue\", physItem)\n\t\tmetadataTag.Name \t= \"metadata\"\n\t\tmetadataTag.Value \t= val\n\n\t\tphysics:setWholeCollisionGroup(physItem, \"items\")\n\n\t\twhile not hitPart and tries < 5 do\n\t\t\ttries = tries + 1\n\n\t\t\tlocal ray = Ray.new(\n\t\t\t\t(CFrame.new(dropPosition)\n\t\t\t\t* CFrame.Angles(0, math.rad(math.random(1, 360)), 0)\n\t\t\t\t* CFrame.Angles(0, 0, math.rad(20))\n\t\t\t\t* CFrame.new(0, 0, -math.random() * 3)).p,\n\t\t\t\tVector3.new(0, -5, 0)\n\t\t\t)\n\n\t\t\thitPart, hitPosition = workspace:FindPartOnRayWithIgnoreList(ray, IGNORE_LIST)\n\t\tend\n\n\t\tif not hitPosition then\n\t\t\thitPosition = dropPosition\n\t\tend\n\n\t\tphysItem.CFrame \t\t\t\t\t= CFrame.new(hitPosition) + Vector3.new(0,0.5,0)\n\t\tphysItem.HumanoidRootPart.CFrame \t= CFrame.new(hitPosition) + Vector3.new(0,0.5,0)\n\n\t\tif owners and #owners > 0 then\n\t\t\tlocal ownersFolder = Instance.new(\"Folder\")\n\t\t\townersFolder.Name = \"owners\"\n\n\t\t\tfor _, owner in pairs(owners) do\n\t\t\t\tif owner and owner.Parent == game.Players then\n\t\t\t\t\tlocal ownerTag = Instance.new(\"ObjectValue\")\n\t\t\t\t\townerTag.Name = tostring(owner.userId)\n\t\t\t\t\townerTag.Value = owner\n\t\t\t\t\townerTag.Parent = ownersFolder\n\t\t\t\tend\n\t\t\tend\n\n\t\t\townersFolder.Parent = physItem\n\t\tend\n\n\t\tlocal creationTimeTag \t= Instance.new(\"IntValue\")\n\t\tcreationTimeTag.Name \t= \"created\"\n\t\tcreationTimeTag.Value \t= os.time()\n\t\tcreationTimeTag.Parent \t= physItem\n\n\t\tif itemLookup[lootDrop.id].petsIgnore then\n\t\t\tlocal petsIgnoreTag \t= Instance.new(\"BoolValue\")\n\t\t\tpetsIgnoreTag.Name \t\t= \"petsIgnore\"\n\t\t\tpetsIgnoreTag.Value \t= true\n\t\t\tpetsIgnoreTag.Parent \t= physItem\n\t\tend\n\n\t\t-- 4 minute despawn\n\t\tgame.Debris:AddItem(physItem, 4 * 60)\n\n\t\t-- apply value to numbered items like gold\n\t\tif lootDrop.value then\n\t\t\tlocal valueTag = Instance.new(\"IntValue\")\n\t\t\tvalueTag.Name = \"itemValue\"\n\t\t\tvalueTag.Value = lootDrop.value\n\t\t\tvalueTag.Parent = physItem\n\t\tend\n\n\t\tif lootDrop.source then\n\t\t\tlocal sourceTag = Instance.new(\"StringValue\")\n\t\t\tsourceTag.Name = \"itemSource\"\n\t\t\tsourceTag.Value = lootDrop.source\n\t\t\tsourceTag.Parent = physItem\n\t\tend\n\n\t\tphysItem.Parent = itemsFolder\n\n\t\treturn physItem\n\tend\nend\n\nlocal function onActivateItemRequestReceived(player, category, inventorySlotPosition, _itemId, playerInput)\n\tprint('recieve funny acitavtion :3')\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\tlocal inventorySlotData = network:invoke(\"getPlayerInventorySlotDataByInventorySlotPosition\",\n\t\tplayer,\n\t\tcategory,\n\t\tinventorySlotPosition\n\t)\n\tif playerData and inventorySlotData and (not _itemId or inventorySlotData.id == _itemId) then\n\t\tlocal itemBaseData = itemLookup[inventorySlotData.id]\n\t\tif itemBaseData.category == \"consumable\" or itemBaseData.activationEffect ~= nil then\n\t\t\tif network:invoke(\"getIsManifestStunned\", player.Character and player.Character.PrimaryPart) then\n\t\t\t\treturn false, \"User is stunned.\"\n\t\t\tend\n\t\t\tlocal stats = playerData.nonSerializeData.statistics_final\n\t\t\tlocal cooldown = CONSUMABLE_COOLDOWN_TIME * math.clamp(1 - stats.consumeTimeReduction, 0, 1)\n\t\t\tif tick() - (playerConsumeCooldownTable[player] or 0) >= cooldown then\n\t\t\t\tplayerConsumeCooldownTable[player] = tick()\n\n\t\t\t\t-- item is consumable -- get rid of a stack of the item then activate it\n\t\t\t\tlocal worked, status = itemBaseData.activationEffect(player, playerInput)\n\t\t\t\tif worked then\n\n\t\t\t\t\tlocal source = \"item:\"..itemBaseData.module.Name\n\n\t\t\t\t\tif itemBaseData.category ~= \"miscellaneous\" then\n\t\t\t\t\t\tnetwork:invoke(\"tradeItemsBetweenPlayerAndNPC\",\n\t\t\t\t\t\t\tplayer,\n\t\t\t\t\t\t\t{{id = inventorySlotData.id; position = inventorySlotData.position; stacks = 1}},\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\t{},\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tsource\n\t\t\t\t\t\t)\n\t\t\t\t\tend\n\n\t\t\t\telse\n\t\t\t\t\tnetwork:fireClient(\"signal_alertChatMessage\",\n\t\t\t\t\t\tplayer,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tText = \"Failed to use item: \"..status or \"no error.\";\n\t\t\t\t\t\t\tFont = Enum.Font.SourceSans;\n\t\t\t\t\t\t\tColor = Color3.fromRGB(216, 161, 107)\n\t\t\t\t\t\t}\n\t\t\t\t\t)\n\t\t\t\tend\n\n\t\t\t\treturn worked, status\n\t\t\tend\n\n\t\t\treturn false, \"consume on cooldown\"\n\t\tend\n\n\t\treturn false, \"Item is not activatable.\"\n\tend\n\n\treturn false, \"Failed to activate item.\"\nend\n\n-- todo: sanity check to make sure player is near shop!\nlocal function onPlayerRequest_buyItemFromShop(player, inventorySlotData, stacksBeingRequested, inventoryModule)\n\tif not inventoryModule then warn(\"Failed to supply inventoryModule\") return false end\n\tif not inventoryModule.Parent:IsA(\"BasePart\") then warn(\"inventoryModule.Parent is not BasePart\") return false end\n\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\tif not playerData then return false end\n\t-- validate the inventory of the shopkeeper to what is trying to be purchased\n\n\tlocal costInfo = inventorySlotData.costInfo\n\n\tlocal confirmedItemInfo\n\n\tlocal itemCost\n\n\tlocal shopkeeperInventory = require(inventoryModule) do\n\t\tlocal isMatched = false\n\n\t\tfor _, shopkeeperItemInfo in pairs(shopkeeperInventory) do\n\n\t\t\tlocal v\n\t\t\tlocal shopCostData\n\t\t\tif typeof(shopkeeperItemInfo) == \"string\" then\n\t\t\t\tv = shopkeeperItemInfo\n\t\t\telseif typeof(shopkeeperItemInfo) == \"table\" then\n\t\t\t\tv = shopkeeperItemInfo.itemName\n\t\t\t\tshopCostData = shopkeeperItemInfo\n\t\t\tend\n\n\t\t\tif itemLookup[v] == itemLookup[inventorySlotData.id] then\n\t\t\t\tif costInfo and costInfo.costType then\n\t\t\t\t\tif shopCostData and costInfo.costType == shopCostData.costInfo.costType then\n\t\t\t\t\t\titemCost = shopkeeperItemInfo.cost\n\t\t\t\t\t\tconfirmedItemInfo = shopkeeperItemInfo\n\t\t\t\t\t\tisMatched = true\n\t\t\t\t\tend\n\t\t\t\telseif not (shopCostData and shopCostData.costInfo and shopCostData.costInfo.costType) then\n\n\t\t\t\t\tisMatched = true\n\t\t\t\tend\n\n\t\t\tend\n\t\tend\n\n\t\tif not isMatched then\n\t\t\treturn false, \"could not find item in shop inventory!\"\n\t\tend\n\tend\n\n\tif typeof(inventorySlotData) ~= \"table\" or not inventorySlotData.id then return false end\n\n\tlocal itemBaseData = itemLookup[inventorySlotData.id]\n\tlocal clamp_stacksBeingRequested = math.clamp(math.floor(stacksBeingRequested or 1), 1, (itemBaseData.stackSize or 99) * 20)\n\n\tif player and inventorySlotData and typeof(inventorySlotData) == \"table\" and stacksBeingRequested and type(stacksBeingRequested) == \"number\" and stacksBeingRequested >= 1 and stacksBeingRequested == clamp_stacksBeingRequested then\n\t\tstacksBeingRequested = clamp_stacksBeingRequested\n\n\t\t-- kill switch in case we decide we need one\n\t\tif not itemBaseData or itemBaseData.cantBuy then\n\t\t\treturn false\n\t\tend\n\n\t\t-- edit: jesus don't put \"stacks\" in the amount removed.\n\t\tlocal source = \"shop:\"..itemBaseData.module.Name\n\n\t\tlocal success, reason\n\n\t\tlocal confirmedCostInfo\n\n\t\tlocal itemBeingBought = {id = inventorySlotData.id}\n\n\t\tif confirmedItemInfo then\n\t\t\tconfirmedCostInfo = confirmedItemInfo.costInfo\n\t\t\tif confirmedItemInfo.attributes then\n\t\t\t\tfor attribute, value in pairs(confirmedItemInfo.attributes) do\n\t\t\t\t\tif not itemBeingBought[attribute] then\n\t\t\t\t\t\titemBeingBought[attribute] = value\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif confirmedItemInfo and confirmedCostInfo and confirmedCostInfo.costType and itemCost then\n\t\t\tif confirmedCostInfo.costType == \"item\" then\n\t\t\t\tlocal stacksBeingBought = math.clamp(stacksBeingRequested, 1, itemBaseData.stackSize or 99)\n\t\t\t\titemBeingBought.stacks \t= stacksBeingBought\n\n\t\t\t\tsuccess, reason = network:invoke(\"tradeItemsBetweenPlayerAndNPC\",\n\t\t\t\t\tplayer,\n\t\t\t\t\t{{id = confirmedCostInfo.costId; stacks = itemCost * stacksBeingRequested}},\n\t\t\t\t\t0,\n\t\t\t\t\t{itemBeingBought},\n\t\t\t\t\t0,\n\t\t\t\t\tsource\n\t\t\t\t)\n\t\t\t\treturn success, reason\n\t\t\telseif confirmedCostInfo.costType == \"ethyr\" then\n\t\t\t\tlocal globalData \t\t= playerData.globalData\n\t\t\t\tlocal stacksBeingBought = math.clamp(stacksBeingRequested, 1, itemBaseData.stackSize or 99)\n\n\t\t\t\tif globalData.ethyr and globalData.ethyr >= itemCost * stacksBeingBought then\n\t\t\t\t\titemBeingBought.stacks = stacksBeingBought\n\t\t\t\t\tsuccess, reason = network:invoke(\"tradeItemsBetweenPlayerAndNPC\",  player, {}, 0, {itemBeingBought}, 0, source)\n\t\t\t\t\tif success then\n\t\t\t\t\t\tglobalData.ethyr = globalData.ethyr - itemCost * stacksBeingRequested\n\t\t\t\t\t\tplayerData.nonSerializeData.setPlayerData(\"globalData\", globalData)\n\t\t\t\t\t\tspawn(function()\n\t\t\t\t\t\t\tnetwork:invoke(\"reportCurrency\", player, \"ethyr\", - itemCost * stacksBeingRequested, source)\n\t\t\t\t\t\tend)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\treturn success, reason\n\t\t\tend\n\t\tend\n\n\t\tlocal coinCostReduction = 1 - math.clamp(playerData.nonSerializeData.statistics_final.merchantCostReduction, 0, 1)\n\n\t\titemBeingBought.stacks = stacksBeingRequested\n\t\tsuccess, reason = network:invoke(\"tradeItemsBetweenPlayerAndNPC\",\n\t\t\tplayer,\n\t\t\t{},\n\t\t\tmath.clamp((itemCost or itemBaseData.buyValue or 1) * coinCostReduction, 1, math.huge) * stacksBeingRequested,\n\t\t\t{itemBeingBought},\n\t\t\t0,\n\t\t\tsource\n\t\t)\n\n\n\t\treturn success, reason\n\tend\n\n\treturn false, \"Failed to purchase item.\"\nend\n\n-- only will sell up to the stack size of an item\nlocal function onPlayerRequest_sellItem(player, unsafeInventorySlotData, stacksToRemove)\n\tif not player then return false end\n\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\tif not playerData then return false end\n\n\tlocal inventorySlotData = nil\n\tfor _, slotData in pairs(playerData.inventory) do\n\t\tif\n\t\t\tslotData.position == unsafeInventorySlotData.position and\n\t\t\tslotData.id == unsafeInventorySlotData.id\n\t\tthen\n\t\t\tinventorySlotData = slotData\n\t\t\tbreak\n\t\tend\n\tend\n\n\tif inventorySlotData and typeof(inventorySlotData) == \"table\" and stacksToRemove == stacksToRemove and stacksToRemove and type(stacksToRemove) == \"number\" then\n\t\tstacksToRemove = math.floor(math.clamp(stacksToRemove or 1, 1, 999))\n\n\t\tlocal itemBaseData \t= itemLookup[inventorySlotData.id]\n\n\t\t-- kill switch in case we decide we need one\n\t\tif not itemBaseData or itemBaseData.cantSell then\n\t\t\treturn false\n\t\telseif itemBaseData and not itemBaseData.canStack then\n\t\t\tstacksToRemove = 1\n\t\tend\n\n\t\t-- edit: jesus don't put \"stacks\" in the amount removed.\n\t\tlocal source = \"shop:\"..itemBaseData.module.Name\n\t\tlocal sellValue = economy.getSellValue(itemBaseData, inventorySlotData)\n\t\tlocal success = network:invoke(\"tradeItemsBetweenPlayerAndNPC\",\n\t\t\tplayer,\n\t\t\t{{\n\t\t\t\tid = inventorySlotData.id;\n\t\t\t\tposition = inventorySlotData.position;\n\t\t\t\tstacks = stacksToRemove\n\t\t\t}},\n\t\t\t0,\n\t\t\t{},\n\t\t\t(sellValue) * stacksToRemove,\n\t\t\tsource\n\t\t)\n\n\t\tif success and inventorySlotData.id == 138  then\n\t\t\t-- xero's tablet. fail treasure hunt quest\n\t\t\tnetwork:fire(\"playerFailedQuest\", player, 10)\n\t\tend\n\t\treturn success\n\tend\n\n\treturn false, \"Failed to sell item.\"\nend\n\nlocal function applyPotionStatusEffectToEntityManifest_server(entityManifest, healthToRestore, manaToRestore, sourceType, sourceId)\n\tprint(\"POTPOTPOTPOTPOTPOTPOTPOTPOTPOTPOTPOT\")\n\n\tprint(entityManifest, healthToRestore,manaToRestore,sourceType, sourceId)\n\n\tif healthToRestore and healthToRestore > 0 then\n\t\tutilities.healEntity(entityManifest, entityManifest, healthToRestore)\n\t\tutilities.playSound(\"item_heal\", entityManifest)\n\tend\n\tif manaToRestore and manaToRestore > 0 then\n\t\tentityManifest.mana.Value = math.min(entityManifest.mana.Value + manaToRestore, entityManifest.maxMana.Value)\n\t\tutilities.playSound(\"item_mana\", entityManifest)\n\tend\n\n\treturn true\nend\n\nlocal function onPlayerAdded(player)\n\tplayerConsumeCooldownTable[player] = 0\nend\n\nlocal function onPlayerRemoving(player)\n\tplayerConsumeCooldownTable[player] = nil\nend\n\nlocal function playerRequest_dropItem(player, inventorySlotData)\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\tif player:FindFirstChild(\"DataSaveFailed\") then\n\t\tnetwork:fireClient(\"alertPlayerNotification\", player, {\n\t\t\ttext = \"Cannot drop items during DataStore outage.\";\n\t\t\ttextColor3 = Color3.fromRGB(255, 57, 60)\n\t\t})\n\t\treturn false, \"This feature is temporarily disabled\"\n\tend\n\n\tif player:FindFirstChild(\"DataLoaded\") == nil then\n\t\treturn false\n\tend\n\n\tif not configuration.getConfigurationValue(\"isTradingEnabled\", player) then\n\t\treturn false\n\tend\n\n\tif playerData and player and player.Character and player.Character.PrimaryPart then\n\t\tlocal isInInventory, pos = false, nil\n\t\tfor i, trueInventorySlotData in pairs(playerData.inventory) do\n\t\t\tif trueInventorySlotData.id == inventorySlotData.id and trueInventorySlotData.position == inventorySlotData.position then\n\t\t\t\tisInInventory = true\n\t\t\t\tpos = i\n\t\t\tend\n\t\tend\n\n\t\tif isInInventory then\n\t\t\tlocal trueMetadata = table.remove(playerData.inventory, pos)\n\n\t\t\tlocal itemBaseData = itemLookup[trueMetadata.id]\n\n\t\t\tlocal drop\n\t\t\tif not (trueMetadata.soulbound or itemBaseData.soulbound) then\n\t\t\t\tdrop = network:invoke(\"spawnItemOnGround\",\n\t\t\t\t\ttrueMetadata,\n\t\t\t\t\tplayer.Character.PrimaryPart.Position + player.Character.PrimaryPart.CFrame.lookVector * 5,\n\t\t\t\t\tnil\n\t\t\t\t)\n\t\t\tend\n\n\t\t\tif drop then\n\t\t\t\tlocal playerDropSource = Instance.new(\"NumberValue\")\n\t\t\t\tplayerDropSource.Name = \"playerDropSource\"\n\t\t\t\tplayerDropSource.Value = player.userId\n\t\t\t\tplayerDropSource.Parent = drop\n\t\t\telse\n\t\t\t\ttable.insert(playerData.inventory, trueMetadata)\n\t\t\tend\n\n\t\t\tplayerData.nonSerializeData.setPlayerData(\"inventory\", playerData.inventory)\n\t\tend\n\tend\n\n\treturn false, \"invalid player data\"\nend\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\tplaceSetup = Modules.placeSetup\n\tphysics = Modules.physics\n\tutilities = Modules.utilities\n\tconfiguration = Modules.configuration\n\teconomy = Modules.economy\n\n\tIGNORE_LIST = {placeSetup.getPlaceFoldersFolder()}\n\titemsFolder = placeSetup.getPlaceFolder(\"items\")\n\n\tgame.Players.PlayerAdded:connect(onPlayerAdded)\n\tfor _, player in pairs(game.Players:GetPlayers()) do\n\t\tonPlayerAdded(player)\n\tend\n\tgame.Players.PlayerRemoving:connect(onPlayerRemoving)\n\n\tnetwork:create(\"itemsRecieved\", \"RemoteEvent\")\n\n\tnetwork:create(\"applyPotionStatusEffectToEntityManifest_server\", \"BindableFunction\", \"OnInvoke\", applyPotionStatusEffectToEntityManifest_server)\n\tnetwork:create(\"generateItemManifest_server\", \"BindableFunction\", \"OnInvoke\", generateItemManifest)\n\tnetwork:create(\"activateItemRequest\", \"RemoteFunction\", \"OnServerInvoke\", onActivateItemRequestReceived)\n\tnetwork:create(\"spawnItemOnGround\", \"BindableFunction\", \"OnInvoke\", spawnItemOnGround)\n\tnetwork:create(\"playerRequest_buyItemFromShop\", \"RemoteFunction\", \"OnServerInvoke\", onPlayerRequest_buyItemFromShop)\n\tnetwork:create(\"playerRequest_sellItemToShop\", \"RemoteFunction\", \"OnServerInvoke\", onPlayerRequest_sellItem)\n\tnetwork:create(\"pickUpItemRequest\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_pickUpItem)\n\tnetwork:create(\"playerRequest_pickUpItem\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_pickUpItem)\n\tnetwork:create(\"pickUpItemForPlayer_server\", \"BindableFunction\", \"OnInvoke\", processPlayerPickUpItem)\n\tnetwork:create(\"playerRequest_dropItem\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_dropItem)\n\tnetwork:create(\"notifyPlayerPickUpItem\", \"RemoteEvent\")\n\n\t-- todo: snip snip\n\tnetwork:create(\"onMonsterDeath\", \"BindableEvent\", \"Event\", function() end)\nend\n\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_messagingService.lua",
    "content": "\nlocal module = {}\n\nlocal network\n\n\nlocal messagingConnections = {}\n\nlocal RunService = game:GetService(\"RunService\")\n\nlocal function reportError(player, Type, Error)\n\tif not RunService:IsRunMode() then\n\t\tnetwork:invoke(\"reportError\", player, Type, Error)\n\tend\nend\n\nlocal function onPlayerAdded(player)\n\n\tlocal iniSuccess, iniErr = pcall(function()\n\n\t\tlocal referrals = Instance.new(\"IntValue\")\n\t\treferrals.Name = \"referrals\"\n\n\n\t\tlocal data = network:invoke(\"getPlayerData\", player)\n\t\tif data and data.globalData and data.globalData.referrals then\n\t\t\treferrals.Value = data.globalData.referrals\n\t\tend\n\n\t\treferrals.Parent = player\n\n\t\tlocal success, err\n\n\t\trepeat\n\t\t\tsuccess, err = pcall(function()\n\n\t\t\t\tif messagingConnections[player.Name] then\n\t\t\t\t\tmessagingConnections[player.Name]:Disconnect()\n\t\t\t\t\tmessagingConnections[player.Name] = nil\n\t\t\t\tend\n\n\t\t\t\tmessagingConnections[player.Name] = game:GetService(\"MessagingService\"):SubscribeAsync(\"user-\"..tostring(player.userId), function(message)\n\n\t\t\t\t\tlocal msgSuccess, msgError = pcall(function()\n\n\n\t\t\t\t\t\t-- REFERRAL!!!\n\t\t\t\t\t\tlocal data = message.Data\n\t\t\t\t\t\tlocal referredUserId = data.referredUserId\n\t\t\t\t\t\tlocal referredUsername = data.referredUsername\n\n\t\t\t\t\t\tif referredUserId then\n\n\t\t\t\t\t\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\t\t\t\t\t\tif playerData then\n\t\t\t\t\t\t\t\tlocal globalData = playerData.globalData\n\t\t\t\t\t\t\t\tif globalData then\n\n\t\t\t\t\t\t\t\t\tif globalData.referredUserIds then\n\t\t\t\t\t\t\t\t\t\tfor i,existingReferral in pairs(globalData.referredUserIds) do\n\t\t\t\t\t\t\t\t\t\t\tif existingReferral == referredUserId then\n\n\t\t\t\t\t\t\t\t\t\t\t\tlocal Error = \"A user (\"..player.Name..\") attempted to double refer.\"\n\t\t\t\t\t\t\t\t\t\t\t\tnetwork:invoke(\"reportError\", player, \"debug\", Error)\n\n\t\t\t\t\t\t\t\t\t\t\t\tplayer:Kick(\"Attempt to refer an already-referred user\")\n\t\t\t\t\t\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\tlocal msuccess, merr = pcall(function()\n\t\t\t\t\t\t\t\t\t\tgame:GetService(\"MessagingService\"):PublishAsync(\"acceptedReferrals\", data.referredUserId)\n\t\t\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\t\t\tif msuccess then\n\t\t\t\t\t\t\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {Text = \"✉️ \" .. player.Name .. \" just referred \".. (referredUsername or \"???\") .. \"!\"; Font = Enum.Font.SourceSansBold; Color = Color3.fromRGB(23, 234, 118)} )\n\n\t\t\t\t\t\t\t\t\t\tlocal rewards = {\n\n\t\t\t\t\t\t\t\t\t\t\t-- megaphones\n\t\t\t\t\t\t\t\t\t\t\t{id = 166; stacks = 1};\n\n\t\t\t\t\t\t\t\t\t\t}\n\n\n\t\t\t\t\t\t\t\t\t\tspawn(function()\n\t\t\t\t\t\t\t\t\t\t\tgame.BadgeService:AwardBadge(player.userId,2124469284)\n\t\t\t\t\t\t\t\t\t\tend)\n\n\t\t\t\t\t\t\t\t\t\tlocal success = network:invoke(\"tradeItemsBetweenPlayerAndNPC\", player, {}, 0, rewards, 0, \"gift:referral\", {})\n\t\t\t\t\t\t\t\t\t\tif not success then\n\t\t\t\t\t\t\t\t\t\t\tif player.Character and player.Character.PrimaryPart then\n\t\t\t\t\t\t\t\t\t\t\t\tlocal gift = network:invoke(\"spawnItemOnGround\", {id = 166}, player.Character.PrimaryPart.Position + player.Character.PrimaryPart.CFrame.lookVector * 2, {player})\n\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\tnetwork:invoke(\"reportAnalyticsEvent\",player,\"referral:accepted\")\n\n\n\t\t\t\t\t\t\t\t\t\tglobalData.referrals = (globalData.referrals or 0) + 1\n\n\t\t\t\t\t\t\t\t\t\tglobalData.referredUserIds = globalData.referredUserIds or {}\n\t\t\t\t\t\t\t\t\t\ttable.insert(globalData.referredUserIds, referredUserId)\n\n\t\t\t\t\t\t\t\t\t\tplayerData.nonSerializeData.setPlayerData(\"globalData\", globalData)\n\t\t\t\t\t\t\t\t\t\treferrals.Value = globalData.referrals\n\n\n\t\t\t\t\t\t\t\t\t\tnetwork:fireClient(\"alertPlayerNotification\", player,{\n\t\t\t\t\t\t\t\t\t\t\ttext \t\t\t\t\t= \"You referred \"..(referredUsername or \"???\")..\" to Vesteria!\";\n\t\t\t\t\t\t\t\t\t\t\ttextColor3 \t\t\t\t= Color3.new(0,0,0);\n\t\t\t\t\t\t\t\t\t\t\tbackgroundColor3 \t\t= Color3.fromRGB(23, 234, 118);\n\t\t\t\t\t\t\t\t\t\t\tbackgroundTransparency \t= 0;\n\t\t\t\t\t\t\t\t\t\t\ttextStrokeTransparency \t= 1;\n\t\t\t\t\t\t\t\t\t\t\tfont \t\t\t\t\t= Enum.Font.SourceSansBold;\n\t\t\t\t\t\t\t\t\t\t}, 6, \"ethyr1\" )\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\tend)\n\n\t\t\t\t\tif not msgSuccess then\n\t\t\t\t\t\treportError(player, \"error\", \"messagingService subscription error: \"..msgError)\n\t\t\t\t\t\twarn(\"Subscription error:\",msgError)\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\tend)\n\n\t\t\tif not success then\n\t\t\t\treportError(player, \"error\", \"messagingService subscription failed: \"..err)\n\t\t\tend\n\n\t\t\twait(15)\n\n\t\tuntil success or player.Parent ~= game.Players\n\n\tend)\n\tif not iniSuccess then\n\t\treportError(player, \"error\", \"Error setting up messagingService: \"..iniErr)\n\tend\nend\n\nlocal function onPlayerRemoving(player)\n\tlocal connections = 0\n\tlocal removedConnections = 0\n\tfor playerName, connection in pairs(messagingConnections) do\n\t\tconnections = connections + 1\n\t\tif connection and (playerName == player.Name or game.Players:FindFirstChild(playerName) == nil) then\n\t\t\tconnection:Disconnect()\n\t\t\tmessagingConnections[playerName] = nil\n\t\t\tremovedConnections = removedConnections + 1\n\t\tend\n\tend\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tgame.Players.PlayerRemoving:connect(onPlayerRemoving)\n\tnetwork:connect(\"playerDataLoaded\", \"Event\", onPlayerAdded)\nend\n\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/contents/manager_monster.lua",
    "content": "--[[\n\tTODO\n\t- ensure attackSpeed and attacking in general properly works\n\t- prevent monsters from stacking on top of each other\n\t\t> this probably will stem from another issue where the monsters\n\t\tare thrown up sometimes when they cross terrain, likely due to\n\t\troblox physics attempting to prevent the monsters from phasing into\n\t\tthe terrain around it.\n\t-test\n--]]\n\n--[[ NOTICE!!!\n\t> a bit of terminology so its not as confusing as it seems\n\t'monster' refers to the in-script class that contains all the monster's\n\tdata\n\n\t'manifest' or 'monster manifest' refers to the server representation\n\n\t'monster manifest render', 'client entity', 'manifest render' refers\n\tto what the client sees\n\n\t'spawnRegion' is the individual parts that make up a whole region\n\n\t'spawnRegionCollection' is a collection of 'spawnRegion'\n--]]\n\n-- manages monsters, duh\n-- author: Polymorphic\nlocal module = {}\nlocal monsterClass = {}\n-- 'true' makes it so when you get too close\n-- it will start moving towards you to attack\n-- 'false' makes it so players must attack first\nmonsterClass.isAggressive = true\n\n-- how close before the monster will aggro\n-- onto players (aggressionType must be 'aggressive')\nmonsterClass.aggressionRange = 35\n\n-- how close the monster will try to get near you\n-- before attacking\nmonsterClass.attackRange = 10\n\n-- time in seconds between each attack\nmonsterClass.attackSpeed = 10\n\n-- if you're within this amount of studs of the monster, itll detect you regardless of if you're in sight\nmonsterClass.detectionFromOutOfVisionRange = monsterClass.attackRange * 1.5\n\n-- the amount of radians the monster can see from the front part\n-- (this is HALF the full vision length, so double this number to get the real cone)\nmonsterClass.visionAngle = math.rad(75)\n\n-- once aggro'd, the monster must maintain\n-- direct sight of you within this range\n-- to continue tracking you, else it'll follow you\n-- up until the last position it saw of you.\nmonsterClass.sightRange = 200\n\n-- 'projectile' makes it so when the monster is\n-- within attackRange it will start to shoot projectiles\n-- at the player\n-- 'physical' makes it so when the monster is within attackRange\n-- it will do a physical attack\nmonsterClass.attackType = \"physical\"\n\n-- default level of monsters\nmonsterClass.level = 1\n\n-- the furthest distance the monster will\n-- follow the player from where it was before the player ran into it\nmonsterClass.playerFollowDistance = 50\n\n-- how fast does it walk towards you\nmonsterClass.baseSpeed = 10\n\n-- TODO: make this stuff work (likely involve creating an internal\n-- walkspeed class which is the current speed of the monster\n-- and update that based on if its bursting, should be easy)\nmonsterClass.burstSpeed = 9 --\nmonsterClass.burstDuration = 0 --\nmonsterClass.burstCooldown = 0 --\n\nlocal LAST_MONSTER_UPDATE_CYCLE = tick()\nlocal LAST_MONSTER_UPDATE_CYCLE_END = tick()\n\nlocal httpService = game:GetService(\"HttpService\")\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal network\nlocal utilities\nlocal physics\nlocal placeSetup\nlocal projectile\nlocal configuration\nlocal events\n\nlocal monsterLookup = require(ReplicatedStorage.monsterLookup)\nlocal defaultMonsterState = require(ReplicatedStorage.defaultMonsterState)\n\n-- set the metatable up so that values not set personally\n-- to each class are redirected to the\nmonsterClass.__index = monsterClass\n\nlocal MONSTER_DEATH_TIME = 5\n\n-- how much it scales down the monster's hitbox from the model size\n-- todo: make this a monster stat, some monster might have odd dilutions\nlocal MONSTER_HITBOX_DILUTION = 0.96\n\nlocal MONSTER_SPAWN_CYCLE_TIME = 10\n\nlocal MONSTER_COLLECTION = {}\n\nlocal itemLookupContainer = ReplicatedStorage.itemData\nlocal itemLookup = require(itemLookupContainer)\n\nlocal runService = game:GetService(\"RunService\")\n\nlocal collectionService = game:GetService(\"CollectionService\")\n\nlocal spawnRegionCollectionsFolder\nlocal entityManifestCollectionFolder\nlocal entityRenderCollectionFolder\nlocal itemsFolder\nlocal entitiesFolder\nlocal foilage\n\nlocal MONSTER_RAYCAST_IGNORE_LIST = {}\n\n------------------------\n-- INTERNAL FUNCTIONS --\n------------------------\n\n\nlocal function _isTargetEntityInVisionCone(monster)\n\tlocal targetEntity = monster.targetEntity or monster.closestEntity\n\tif not targetEntity then return false, \"no HRP\" end\n\n\t-- point a cframe in the direction of the part, and get the lookVector\n\tlocal rLookVector = CFrame.new(monster.manifest.Position, targetEntity.Position).lookVector\n\n\t-- find out the degree difference in where the part is facing\n\t-- to the vector that points directly to the target part from the main part\n\tlocal res = math.acos(rLookVector:Dot(monster.manifest.CFrame.lookVector))\n\n\t-- return true if vectors are 30 degrees off each other at maximum\n\t-- (for either side, giving a cone of 60 degrees)\n\treturn res <= monster.visionAngle\nend\n\n-- return if the monster's targetPlayer is in it's line of sight\nfunction monsterClass:isTargetEntityInLineOfSight(overrideSightRange, useVisionCone, predictiveTargetEntity)\n\tif self.targetEntity and (self.targetEntityLockType or 0) >= 1 then return true end\n\n\tlocal targetEntity = self.targetEntity or self.closestEntity do\n\t\tif predictiveTargetEntity then\n\t\t\ttargetEntity = predictiveTargetEntity\n\t\tend\n\tend\n\n\tlocal function wasHit(targetEntity)\n\t\tif not targetEntity then return false, \"no target HRP\" end\n\n\t\tif useVisionCone and not _isTargetEntityInVisionCone(self) and utilities.magnitude(targetEntity.Position - self.manifest.Position) > self.detectionFromOutOfVisionRange then\n\t\t\treturn false, \"vision cone fail\"\n\t\tend\n\n\t\tif targetEntity:FindFirstChild(\"isStealthed\") then return false, \"player stealthed\" end\n\n\t\tlocal monsterPosition \t= self.manifest.Position\n\t\tlocal dir \t\t\t\t= targetEntity.Position - monsterPosition\n\t\tlocal ray \t\t\t\t= Ray.new(\n\t\t\tmonsterPosition,\n\t\t\tdir.unit * math.min(dir.magnitude, overrideSightRange and overrideSightRange or self.sightRange)\n\t\t)\n\n\t\tlocal hitPart, hitPosition = projectile.raycast(\n\t\t\tray,\n\t\t\t{spawnRegionCollectionsFolder, entityManifestCollectionFolder, entityRenderCollectionFolder, itemsFolder, entitiesFolder, foilage}\n\t\t)\n\n\t\t-- return if the hitPart is nil (nothing obstructing) or if the hitPart is\n\t\t-- a descendant of the target character (this will include hats and tools)\n\n\t\treturn\n\t\t\thitPart == nil\n\t\t\tor hitPart == targetEntity, tostring(hitPart) .. \"was hit\"\n\n\tend\n\n\tif targetEntity then\n\t\tif wasHit(targetEntity) then\n\t\t\tself.closestEntity = targetEntity\n\t\t\tself.targetEntity = targetEntity\n\t\t\treturn true\n\t\tend\n\tend\n\n\tlocal aggressionRange = self.aggressionRange\n\n\n\n\tfor i, targetEntity in pairs(self.nearbyTargets) do\n\t\tif utilities.magnitude(targetEntity.Position - self.manifest.Position) <= aggressionRange  then\n\t\t\tif wasHit(targetEntity) then\n\t\t\t\tself.closestEntity = targetEntity\n\t\t\t\tself.targetEntity = targetEntity\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\n\tend\nend\n\n-- this functions tells the monsters if the target entity is still good to follow around\nfunction monsterClass:isTargetEntityValid()\n\nend\n\nlocal function purgeNumericKeys(t)\n\tlocal keysToPurge = {}\n\n\tfor key, value in pairs(t) do\n\t\tif typeof(key) ~= \"string\" then\n\t\t\ttable.insert(keysToPurge, key)\n\t\tend\n\n\t\tif typeof(value) == \"table\" then\n\t\t\tpurgeNumericKeys(value)\n\t\tend\n\tend\n\n\tfor _, key in pairs(keysToPurge) do\n\t\tlocal value = t[key]\n\t\tt[key] = nil\n\t\tt[tostring(key)] = value\n\tend\nend\n\nlocal function _getMonsterByManifest(monsterManifest, specificProperty)\n\tfor i, monster in pairs(MONSTER_COLLECTION) do\n\t\tif monster.manifest == monsterManifest then\n\t\t\tif specificProperty then\n\t\t\t\treturn monster[specificProperty]\n\t\t\telse\n\t\t\t\treturn monster\n\t\t\tend\n\t\tend\n\tend\n\n\treturn nil\nend\n\nlocal function _getMonsterCountInSpawnRegionCollection(spawnRegionCollection)\n\tlocal count = 0\n\tfor i, monster in pairs(MONSTER_COLLECTION) do\n\t\tif monster.__SPAWN_REGION_COLLECTION == spawnRegionCollection then\n\t\t\tcount = count + 1\n\t\tend\n\tend\n\n\treturn count\nend\n\nlocal function _getSpawnRegionFromSpawnRegionCollection(spawnRegionCollection)\n\tlocal weightTable = {}\n\n\tfor i, spawnRegion in pairs(spawnRegionCollection:GetChildren()) do\n\t\tif spawnRegion:IsA(\"BasePart\") then\n\t\t\ttable.insert(weightTable, {\n\t\t\t\tspawnRegion \t= spawnRegion;\n\n\t\t\t\t-- weight based on the volume of the spawnRegion\n\t\t\t\t-- ignoring the Y axis, that could be extremely variable\n\t\t\t\tselectionWeight = math.floor(spawnRegion.Size.X * spawnRegion.Size.Z + 0.5);\n\t\t\t})\n\t\tend\n\tend\n\n\treturn utilities.selectFromWeightTable(weightTable)\nend\n\nlocal spawnPositionGenerator = Random.new()\nlocal function _getPositionFromSpawnRegion(spawnRegion)\n\tlocal hitPart, hitPosition\n\tfor i=1,5 do\n\t\tlocal ray = Ray.new(\n\t\t\t(spawnRegion.CFrame * CFrame.new(\n\t\t\t\t0.9 * spawnPositionGenerator:NextInteger(-spawnRegion.Size.X / 2, spawnRegion.Size.X / 2),\n\t\t\t\tspawnRegion.Size.Y / 2,\n\t\t\t\t0.9 * spawnPositionGenerator:NextInteger(-spawnRegion.Size.Z / 2, spawnRegion.Size.Z / 2)\n\t\t\t)).p,\n\t\t\tVector3.new(0, -spawnRegion.Size.Y, 0)\n\t\t)\n\n\t\thitPart, hitPosition = workspace:FindPartOnRayWithIgnoreList(ray, MONSTER_RAYCAST_IGNORE_LIST)\n\n\t\tif hitPart then\n\t\t\tbreak\n\t\tend\n\tend\n\n\treturn hitPosition\nend\n\n-- Used to figure out how players are spread out around the monster\n-- Basically just the magnitude of the average of a bunch of directional unit vectors\n-- Includes other useful data like a vector aimed at the point of highest density\n-- Used for attack logic that depends on how players are spread out\nlocal function getEntityDensity(monster)\n\tlocal directionVector = Vector3.new()\n\tlocal avgDistance = 0\n\tlocal monsterPos = monster.manifest.Position\n\tlocal entities = monster.nearbyTargets\n\n\tfor _, entity in pairs (entities) do\n\t\tlocal entityPos = entity.position\n\t\tlocal monsterToPlayerCf = CFrame.new(monsterPos, Vector3.new(entityPos.X, monsterPos.Y, entityPos.Z))\n\t\tdirectionVector = directionVector + monsterToPlayerCf.LookVector\n\t\tavgDistance = avgDistance + (monsterPos - entityPos).Magnitude\n\tend\n\tdirectionVector = directionVector / #entities\n\n\t-- As density approaches 0, players are more evenly spread out\n\t-- As density approaches 1, players are more concentrated in one area\n\treturn {\n\t\tdensity = directionVector.Magnitude,\n\t\tdirection = directionVector,\n\t\tdistance = avgDistance\n\t}\nend\n\n---------------------\n-- CLASS FUNCTIONS --\n---------------------\n\nfunction monsterClass:getRoamPositionInSpawnRegion()\n\treturn _getPositionFromSpawnRegion(self.__SPAWN_REGION)\nend\n\n--[[\n\tdropItem(\n\t\tdropInformation = {\n\t\t\tlootDropData \t= {id = 1}; -- can include dye, attribute, etc\n\t\t\tdropPosition \t= Vector3.new();\n\t\t\titemOwners \t\t= {players}\n\t\t},\n\n\t\tphysItem = nil,\n\t\tlootMulti = 1\n\t)\n]]--\n\nlocal randoGen = Random.new()\nfunction monsterClass:dropItem(dropInformation, physItem, lootMulti)\n\tphysItem = physItem or nil\n\tlootMulti = lootMulti or 1\n\n\tlocal item = network:invoke(\n\t\t\"spawnItemOnGround\",\n\t\tdropInformation.lootDropData,\n\t\tdropInformation.dropPosition,\n\t\tdropInformation.itemOwners,\n\t\tphysItem\n\t)\n\n\tif item == nil then return false end\n\n\t-- monster idol\n\tif dropInformation.lootDropData.id == 181 then\n\t\tlocal monsterNameTag = Instance.new(\"StringValue\")\n\t\tmonsterNameTag.Name = \"monsterName\"\n\t\tmonsterNameTag.Value = self.module.Name\n\t\tmonsterNameTag.Parent = item\n\tend\n\n\tlocal velo = Vector3.new((randoGen:NextNumber() - 0.5) * 24, (2 + randoGen:NextNumber()) * 30, (randoGen:NextNumber() - 0.5) * 24)\n\tvelo = velo * (1 + ((lootMulti - 1) / 27))\n\n\tif item:IsA(\"BasePart\") then\n\t\titem.Velocity = velo\n\telseif item:IsA(\"Model\") and (item.PrimaryPart or item:FindFirstChild(\"HumanoidRootPart\")) then\n\t\tlocal primaryPart = item.PrimaryPart or item:FindFirstChild(\"HumanoidRootPart\")\n\t\tif primaryPart then\n\t\t\tprimaryPart.Velocity = velo\n\t\tend\n\tend\n\n\treturn true, item\nend\n\n-- internally sets targetPlayer (player monster is following)\n-- targetEntityLockType\n\t-- 0 = regular behaviour\n\t-- 1 = soft lock, damage will allow targetentity to swap\n\t-- 2 = semi-soft lock, only damage will allow TargetEntity to be set and manually setting it. will never just roam, will always default to defaulttargetentity\n\t-- 3 = hard lock, nothing but manually setting target entity will allow targetEntity to be set\nfunction monsterClass:setTargetEntity(targetEntity, resetDueToDeath, targetEntitySetSource, targetEntityLockType)\n\n\tif resetDueToDeath then\n\t\tself.closestEntity \t\t\t\t\t= nil\n\tend\n\n\tself.targetEntityLockType = self.targetEntityLockType or 0\n\n\tif self.targetEntityLockType >= 3 and self.targetEntityLockType > (targetEntityLockType or 0) then\n\t\treturn false\n\tend\n\n\tif self.targetEntityLockType <= 2 and targetEntityLockType and targetEntityLockType >= 2 then\n\t\tself.defaultTargetEntity = targetEntity\n\tend\n\n\tif not targetEntity and self.defaultTargetEntity then\n\t\tself.targetEntity = self.defaultTargetEntity\n\telse\n\t\tself.targetEntity = targetEntity\n\tend\n\n\n\tself.targetEntitySetSource \t= targetEntitySetSource or nil\n\n\tif targetEntityLockType then\n\t\tself.targetEntityLockType \t= targetEntityLockType\n\tend\n\n\t-- update objectValue for target\n\tself.manifest.targetEntity.Value = targetEntity\n\n\nend\n\n-- set the current state of the monster\nfunction monsterClass:setState(newState, newStateData)\n\tif self.state == \"dead\" then return end\n\n\tif self.state ~= newState then\n\t\tlocal success, newStateDataJSON = utilities.safeJSONEncode(newStateData or {})\n\n\t\tself.manifest.stateData.Value \t= success and newStateDataJSON or \"[]\"\n\t\tself.manifest.state.Value \t\t= newState\n\t\tself.state \t\t\t\t\t\t= newState\n\tend\n\n\tif newState == \"dead\" then\n\t\tnetwork:fire(\"onMonsterDeath\", self.manifest)\n\n\t\tself.manifest.Anchored \t\t= true\n\t\tself.manifest.CanCollide \t= false\n\n\t\tif self.stateMachine and self.stateMachine.onTransition then\n\t\t\tself.stateMachine.onTransition:Destroy()\n\t\tend\n\n\t\t-- step dead once\n\t\tif self.stateMachine.states.dead and self.stateMachine.states.dead.step then\n\t\t\tself.stateMachine.states.dead.step(self)\n\t\tend\n\n\t\tfor i, monster in pairs(MONSTER_COLLECTION) do\n\t\t\tif monster == self then\n\t\t\t\t-- remove from update queue\n\n\t\t\t\ttable.remove(MONSTER_COLLECTION, i)\n\t\t\t\tfor i, event in pairs(monster.__EVENTS) do\n\t\t\t\t\tevent:disconnect()\n\t\t\t\tend\n\n\t\t\t\tmonster.__EVENTS = nil\n\n\t\t\t\tdelay(MONSTER_DEATH_TIME, function()\n\t\t\t\t\tself.stateMachine.states = nil\n\n\t\t\t\t\twait(30)\n\n\t\t\t\t\t-- destroy manifest after death timer.\n\t\t\t\t\tself.manifest:Destroy()\n\n\n\t\t\t\t\t-- get rid of states\n\n\t\t\t\t\t-- clear table.\n\t\t\t\t\tfor i, v in pairs(self) do\n\t\t\t\t\t\tself[i] = nil\n\t\t\t\t\tend\n\t\t\t\tend)\n\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\tend\nend\n\n-- return mass of the manifest\nfunction monsterClass:getMass(monster)\n\treturn self.manifest:GetMass()\nend\n\nfunction monsterClass:resetPathfinding()\n\tif self.pathfindingTrigger == \"roaming\" then\n\t\tself.__LAST_ROAM_TIME = tick()\n\tend\n\n\tself.isProcessingPath \t= false\n\tself.pathfindingTrigger = nil\n\tself.path \t\t\t\t= nil\n\tself.currentNode \t\t= 1\nend\n\n-------------------------\n-- STATE INSTANTIATION --\n-------------------------\n\nlocal stateMachineFactory = {}\n\nlocal stateMachine = {}\n\tstateMachine.__index = stateMachine\n\nlocal function isMonsterStunned(monster)\n\tlocal manifest = monster.manifest\n\n\tlocal guid = utilities.getEntityGUIDByEntityManifest(manifest)\n\tif not guid then return false end\n\n\tlocal statuses = network:invoke(\"getStatusEffectsOnEntityManifestByEntityGUID\", guid)\n\n\tfor _, status in pairs(statuses) do\n\t\tif status.statusEffectType == \"stunned\" then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal function updateMonsterStateMachine(monster)\n\n\n\n\t-- this should be better, I need more information about how the state machine works\n\tif isMonsterStunned(monster) then\n\t\tmonster.manifest.BodyVelocity.Velocity = Vector3.new()\n\t\treturn\n\tend\n\n\tlocal stateMachine \t\t= monster.stateMachine\n\n\t-- dead\n\tif (not stateMachine) or (not stateMachine.states) then\n\t\treturn\n\tend\n\n\tlocal currentStateData \t= stateMachine.states[stateMachine.currentState]\n\tlocal canSwitchState \t= (not currentStateData.lockTimeForPreventStateTransition or tick() - currentStateData.__START_TIME > currentStateData.lockTimeForPreventStateTransition)\n\n\t-- potential stuckage\n\tlocal nextState, stateData = currentStateData.step(monster, canSwitchState)\n\n\tif nextState and canSwitchState then\n\t\tlocal nextStateData = stateMachine.states[nextState]\n\n\t\tif nextStateData and (not currentStateData.lockTimeForLowerTransition or nextStateData.transitionLevel >= currentStateData.transitionLevel or tick() - currentStateData.__START_TIME >= currentStateData.lockTimeForLowerTransition) then\n\t\t\tstateMachine.onTransition:Fire(stateMachine.currentState, nextState, stateData)\n\n\t\t\tif currentStateData.verify then\n\t\t\t\tif monster.targetEntity and monster.targetEntity:FindFirstChild(\"entityType\") and monster.targetEntity.entityType.Value == \"monster\" then\n\t\t\t\t\tcurrentStateData.verify(monster)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tnextStateData.__START_TIME \t= tick()\n\t\t\tstateMachine.previousState \t\t\t= stateMachine.currentState\n\t\t\tstateMachine.currentState \t\t\t= nextState\n\t\tend\n\tend\nend\n\nfunction stateMachine:forceStateChange(newState)\n\tif self.states[newState] then\n\t\tself.onTransition:Fire(self.currentState, newState)\n\n\t\tself.states[newState].__START_TIME = tick()\n\t\tself.previousState \t= self.currentState\n\t\tself.currentState \t= newState\n\tend\nend\n\nfunction stateMachineFactory.create(monster, startingState, states)\n\tlocal newStateMachine = {}\n\t\tnewStateMachine.states \t\t\t= states and utilities.copyTable(states) or {}\n\t\tnewStateMachine.previousState \t= \"initializing\"\n\t\tnewStateMachine.currentState \t= startingState\n\t\tnewStateMachine.onTransition \t= Instance.new(\"BindableEvent\")\n\n\tlocal defaultMonsterStateMachine = utilities.copyTable(require(ReplicatedStorage.defaultMonsterState))\n\n\tsetmetatable(newStateMachine.states, {\n\t\t__index = function(_, index)\n\t\t\treturn defaultMonsterStateMachine.states[index]\n\t\tend\n\t})\n\n\tstartingState = startingState or \"idling\"\n\n\t-- initialize the first state\n\tnewStateMachine.states[startingState].__START_TIME = tick()\n\n\treturn setmetatable(newStateMachine, stateMachine)\nend\n\n-------------------------\n-- CLASS INSTANTIATION --\n-------------------------\n\nlocal baseHitbox do\n\tbaseHitbox \t\t\t\t\t= Instance.new(\"Part\")\n\tbaseHitbox.TopSurface \t\t= Enum.SurfaceType.Smooth\n\tbaseHitbox.BottomSurface \t= Enum.SurfaceType.Smooth\n\tbaseHitbox.Shape \t\t\t= Enum.PartType.Ball\n\tbaseHitbox.Transparency \t= 1\n\tbaseHitbox.CanCollide \t\t= true\n\tbaseHitbox.Anchored \t\t= false\n\n\tlocal bodyVelocity \t\t= Instance.new(\"BodyVelocity\", baseHitbox)\n\tbodyVelocity.MaxForce \t= Vector3.new(100000, 0, 100000)\n\tbodyVelocity.Velocity \t= Vector3.new(0, 0, 0)\n\n\t-- todo: keep bodyGyro so hitbox is always upright, ie cant\n\t-- be toppled over.\n\tlocal bodyGyro \t\t= Instance.new(\"BodyGyro\", baseHitbox)\n\tbodyGyro.MaxTorque \t= Vector3.new(1e5, 1e5, 1e5)\n\tbodyGyro.P \t\t\t= 7000\n\tbodyGyro.D \t\t\t= 500\n\n\tlocal bodyForce = Instance.new(\"BodyForce\", baseHitbox)\n\n\t-- todo: convert all these ValueBase classes to be stored within a folder\n\t-- to look cleaner\n\tlocal stateValue \t= Instance.new(\"StringValue\", baseHitbox)\n\tstateValue.Name \t= \"state\"\n\tstateValue.Value \t= \"sleeping\"\n\n\tlocal targetEntity \t= Instance.new(\"ObjectValue\", baseHitbox)\n\ttargetEntity.Name \t= \"targetEntity\"\n\ttargetEntity.Value \t= nil\n\n\tlocal stateData \t= Instance.new(\"StringValue\", baseHitbox)\n\tstateData.Name \t\t= \"stateData\"\n\tstateData.Value \t= \"[]\"\n\n\tlocal entityType \t= Instance.new(\"StringValue\", baseHitbox)\n\tentityType.Name \t= \"entityType\"\n\tentityType.Value \t= \"monster\"\n\n\tlocal entityId \t= Instance.new(\"StringValue\", baseHitbox)\n\tentityId.Name \t= \"entityId\"\n\tentityId.Value \t= \"\"\n\n\tlocal statusEffects = Instance.new(\"StringValue\", baseHitbox)\n\tstatusEffects.Name \t= \"statusEffectsV2\"\n\tstatusEffects.Value = \"{}\"\n\n\tlocal stanceValue \t= Instance.new(\"StringValue\", baseHitbox)\n\tstanceValue.Name \t= \"stance\"\n\n\tlocal maxHealthValue \t= Instance.new(\"NumberValue\", baseHitbox)\n\tmaxHealthValue.Name \t= \"maxHealth\"\n\tmaxHealthValue.Value \t= 100\n\n\tlocal healthValue \t= Instance.new(\"NumberValue\", baseHitbox)\n\thealthValue.Name \t= \"health\"\n\thealthValue.Value \t= 100\n\n\tlocal levelValue \t= Instance.new(\"IntValue\", baseHitbox)\n\tlevelValue.Name\t\t= \"level\"\n\tlevelValue.Value \t= 1\n\n\tcollectionService:AddTag(baseHitbox, \"monster\")\nend\n\nlocal blacklistedSpawnRegion = {}\nlocal function setIsSpawnRegionCollectionDisabled(spawnRegionCollection, isDisabled)\n\tblacklistedSpawnRegion[spawnRegionCollection.Name] = isDisabled\nend\n\nlocal function getSpawnRegionsUnderpopulated()\n\tlocal spawnRegionsCollection_underpopulated = {}\n\n\tfor i, spawnRegionCollection in pairs(spawnRegionCollectionsFolder:GetChildren()) do\n\t\tif spawnRegionCollection.Name ~= \"Pets\" and not blacklistedSpawnRegion[spawnRegionCollection.Name] then\n\t\t\tlocal monsterType, monsterSpawnAmount = string.match(spawnRegionCollection.Name, \"(.+)-(%d+)\")\n\n\t\t\tlocal isUnderpopulated = false\n\n\n\t\t\t--local playerDensityFactor = 0.25 + (#game.Players:GetPlayers() / (game.Players.MaxPlayers * 0.75))\n\t\t\t--playerDensityFactor = math.min(playerDensityFactor, 1.25)\n\t\t\tlocal playerDensityFactor = 1\n\n\t\t\tif runService:IsRunMode() or runService:IsStudio() then\n\t\t\t\tplayerDensityFactor = 0.75\n\t\t\tend\n\n\t\t\t-- night time!\n\t\t\tif game.Lighting.ClockTime < 5.9 or game.Lighting.ClockTime > 18.6 then\n\t\t\t\tplayerDensityFactor = playerDensityFactor * 1.5\n\t\t\tend\n\n\t\t\tif monsterType and monsterSpawnAmount then\n\t\t\t\tmonsterSpawnAmount = math.ceil(tonumber(monsterSpawnAmount) * playerDensityFactor)\n\n\t\t\t\tif _getMonsterCountInSpawnRegionCollection(spawnRegionCollection) < monsterSpawnAmount then\n\t\t\t\t\tisUnderpopulated = true\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif isUnderpopulated then\n\t\t\t\ttable.insert(spawnRegionsCollection_underpopulated, {monsterNameToSpawn = monsterType; spawnRegionCollection = spawnRegionCollection})\n\t\t\tend\n\t\tend\n\tend\n\n\treturn spawnRegionsCollection_underpopulated\nend\n\n-- fetches the real clientEntity (not a clone!)\nlocal function getRealClientEntity(monsterName)\n\tif monsterLookup[monsterName] then\n\t\treturn monsterLookup[monsterName].entity:Clone()\n\tend\nend\n\nlocal extentsCache = {}\n\n-- generates the server entity of the monster\nlocal function getManifestFromClientEntity(realMonsterClientEntity, baseStats, monsterName)\n\tif realMonsterClientEntity and baseStats then\n\t\t-- generate hitbox\n\t\tlocal hitBox = baseHitbox:Clone()\n\t\thitBox.Name = monsterName\n\n\t\tlocal extent = extentsCache[monsterName]\n\t\tif extent == nil then\n\t\t\textent = realMonsterClientEntity:GetExtentsSize()\n\t\t\textentsCache[monsterName] = extent\n\t\tend\n\n\t\thitBox.Size = Vector3.new(extent.Y, extent.Y, extent.Y) * (baseStats.hitboxDilution or MONSTER_HITBOX_DILUTION)\n\n\t\treturn hitBox\n\telse\n\t\twarn(\"realMonsterClientEntity\", realMonsterClientEntity, \"baseStats\", baseStats)\n\tend\nend\n\n-- returns the baseStats of the monster\n-- based on the moduleScript of that name\nlocal function getMonsterBaseStats(monsterName)\n\treturn monsterLookup[monsterName]\nend\n\nlocal rand = Random.new(os.time())\n\nlocal function isNightTime(guard)\n\n\t-- DANGER!\n\tif game.Lighting.ClockTime < 5.9 or game.Lighting.ClockTime > 18.6 then\n\t\treturn true\n\tend\n\n\tif not guard and not configuration.getConfigurationValue(\"doUseNightTimeGiantSpawn\") then\n\t\t-- its always nighttime in derry\n\t\treturn true\n\telseif guard and not configuration.getConfigurationValue(guard) then\n\t\t-- you'll float too!\n\t\treturn true\n\tend\n\n\treturn game.Lighting.ClockTime > 18.3 or game.Lighting.ClockTime < 6\nend\n\n-- generate new monster class to interface with the\n-- control script\nlocal function newMonster(monsterName, spawnLocation, spawnRegionCollection, spawnRegion, _additionalStats, postInitCallback)\n\tif not monsterName then return end\n\n\tlocal baseStats = getMonsterBaseStats(monsterName)\n\tlocal clientEntity = getRealClientEntity(monsterName)\n\tlocal manifest = getManifestFromClientEntity(clientEntity, baseStats, monsterName)\n\n\t-- return if manifest is nil (no model for this thing)\n\tif not manifest then warn(\"no manifest for \" .. tostring(monsterName)) return end\n\n\t-- default baseStats to internal defaults\n\tif not baseStats then baseStats = {} end\n\n\tif not clientEntity then return false end\n\n\t-- set name of manifest\n\tmanifest.Name = monsterName\n\tmanifest.entityId.Value = monsterName\n\n\tlocal newMonster = {}\n\tnewMonster.monsterName = monsterName\n\n\t-- internal variables --\n\tnewMonster.__LAST_UPDATE = tick()\n\tnewMonster.__LAST_ATTACK_TIME = 0\n\tnewMonster.__LAST_GRAVITY_RAYCAST_UPDATE = 0\n\tnewMonster.__LAST_POSITION_SEEN = nil\n\tnewMonster.__LAST_MOVE_DIRECTION = nil\n\tnewMonster.__SPAWN_REGION_COLLECTION = spawnRegionCollection\n\tnewMonster.__SPAWN_REGION = spawnRegion\n\tnewMonster.__LAST_ROAM_TIME = 0\n\tnewMonster.__IS_WAITING_FOR_PATH_FINDING = false\n\tnewMonster.__EVENTS = {}\n\tnewMonster.__MONSTER_EVENTS = baseStats.monsterEvents or {}\n\tnewMonster.__STATE_OVERRIDES = baseStats.stateOverrides or {}\n\n\t-- state variables --\n\tnewMonster.currentNode = 1\n\tnewMonster.specialsUsed = 0\n\tnewMonster.isProcessingPath = false\n\tnewMonster.origin = nil\n\tnewMonster.state = \"sleeping\"\n\tnewMonster.roamingTargetPosition = nil\n\tnewMonster.targetEntity = nil\n\tnewMonster.maxHealth = baseStats.maxHealth\n\tnewMonster.health = baseStats.maxHealth\n\tnewMonster.level = baseStats.level\n\n\t-- identity variables\n\tnewMonster.manifest = manifest\n\tnewMonster.clientEntity = getRealClientEntity(monsterName)\n\n\t-- apply monster baseStats\n\tfor baseStat, baseStatValue in pairs(baseStats) do\n\t\tnewMonster[baseStat] = baseStatValue\n\t\tnewMonster[\"_\" .. baseStat] = baseStatValue\n\tend\n\n\tif (not _additionalStats or not _additionalStats.variation) and isNightTime(\"doSpawnNightTimeVariants\") then\n\t\t-- todo: merge this draft too\n\tend\n\n\tif _additionalStats then\n\t\tlocal variation = _additionalStats.variation\n\t\tlocal variationStats = variation and baseStats.variations[variation]\n\n\t\t-- signal to the game what kind of variation this is\n\t\tnewMonster.variation = variation\n\n\t\tif variationStats then\n\t\t\tfor stat, statValue in pairs(variationStats) do\n\t\t\t\tnewMonster[stat] = statValue\n\t\t\tend\n\t\tend\n\n\t\tfor additionalStat, additionalStateValue in pairs(_additionalStats) do\n\t\t\tnewMonster[additionalStat] = additionalStateValue\n\t\tend\n\tend\n\n\tlocal statesLookup = monsterLookup[monsterName].statesData\n\tlocal monsterStateMachine = stateMachineFactory.create(newMonster, statesLookup.default, statesLookup.states)\n\tnewMonster.stateMachine = monsterStateMachine\n\n\tlocal spawnCFrame\n\tif spawnLocation then\n\t\tif typeof(spawnLocation) == \"CFrame\" then\n\t\t\tspawnCFrame = spawnLocation + Vector3.new(0, newMonster.manifest.Size.Y, 0)\n\t\telseif typeof(spawnLocation) == \"Vector3\" then\n\t\t\tspawnCFrame = CFrame.new(spawnLocation) + Vector3.new(0, newMonster.manifest.Size.Y, 0)\n\t\telseif typeof(spawnLocation) == \"Instance\" and spawnLocation:IsA(\"BasePart\") then\n\t\t\tspawnCFrame = spawnLocation.CFrame + Vector3.new(0, newMonster.manifest.Size.Y, 0)\n\t\tend\n\n\t\tspawnCFrame = spawnCFrame\n\telse\n\t\twarn(\"spawnLocation was nil\")\n\tend\n\n\tspawnCFrame = spawnCFrame * CFrame.Angles(0, math.rad(math.random(1,360)), 0)\n\n\tif spawnCFrame then\n\t\tnewMonster.manifest.CFrame = spawnCFrame\n\t\tnewMonster.origin = spawnCFrame\n\telse\n\t\twarn(\"invalid spawnLocation given\")\n\tend\n\n\tlocal transitionEvent = monsterStateMachine.onTransition.Event:connect(function(old, new, newStateData)\n\t\tif manifest:FindFirstChild(\"ParticleEmitter\") then\n\n\t\tend\n\n\n\n\t\tif monsterStateMachine.states[new].execute_server then\n\t\t\tspawn(function()\n\t\t\t\tmonsterStateMachine.states[new].execute_server(newMonster)\n\t\t\tend)\n\t\tend\n\n\t\tnewMonster:setState(new, newStateData)\n\n\t\tif baseStats.monsterEvents and baseStats.monsterEvents.onStateChanged then\n\t\t\tbaseStats.monsterEvents.onStateChanged(newMonster, old, new)\n\t\tend\n\tend)\n\n\ttable.insert(newMonster.__EVENTS, transitionEvent)\n\n\t-- set the metatable\n\tsetmetatable(newMonster, monsterClass)\n\n\t-- step it up\n\tupdateMonsterStateMachine(newMonster)\n\n\t-- spawn the monster based on the data type of the\n\t-- spawnLocation given\n\n\t-- attempt to correct for the monsters constantly being thrusted upward\n\n\tnewMonster.healthMulti = newMonster.healthMulti or 1\n\tnewMonster.bonusXPMulti = newMonster.bonusXPMulti or 1\n\tnewMonster.damageMulti = newMonster.damageMulti or 1\n\tnewMonster.goldMulti = newMonster.goldMulti or 1\n\tnewMonster.bonusLootMulti = newMonster.bonusLootMulti or 1\n\tnewMonster.attackRange = newMonster.attackRange or 1\n\n\t-- night time!\n\n\tlocal nighttime\n\n\tif game.Lighting.ClockTime < 5.9 or game.Lighting.ClockTime > 18.6 then\n\t\tif not ( _additionalStats and _additionalStats.level) then\n\t\t\tnewMonster.nightboosted = true\n\t\t\tnewMonster.level = newMonster.level + 1\n\t\t\tnewMonster.healthMulti = newMonster.healthMulti * 1.25\n\t\t\tnewMonster.damageMulti = newMonster.damageMulti * 1.25\n\t\t\tnewMonster.bonusXPMulti = newMonster.bonusXPMulti * 1.25\n\t\t\tnewMonster.bonusLootMulti = newMonster.bonusLootMulti * 1.25\n\t\t\tnewMonster.goldMulti = newMonster.goldMulti * 1.25\n\t\t\tnewMonster.aggressionRange = newMonster.aggressionRange * 2\n\t\t\tnewMonster.attackSpeed = newMonster.attackSpeed * 0.8\n\t\t\tnewMonster.playerFollowDistance = newMonster.playerFollowDistance * 2\n\t\t\tnewMonster.baseSpeed = newMonster.baseSpeed * 1.1\n\t\t\tnewMonster.attackRange = newMonster.attackRange * 1.1\n\t\t\tnewMonster.detectionFromOutOfVisionRange = newMonster.detectionFromOutOfVisionRange * 2\n\t\t\tnewMonster.visionAngle = newMonster.visionAngle * 1.25\n\t\tend\n\tend\n\n\tif newMonster.gigaGiant then\n\t\tif not ( _additionalStats and _additionalStats.level) then\n\t\t\tnewMonster.level = newMonster.level + 3\n\t\tend\n\n\t\tnewMonster.scale \t\t\t\t= 5\n\t\tnewMonster.IS_MONSTER_ENRAGED \t= true\n\n\t\tlocal monsterManifest \t= newMonster.manifest\n\t\tmonsterManifest.Size \t= monsterManifest.Size * 5\n\n\t\tlocal scaleTag \t\t= Instance.new(\"NumberValue\")\n\t\tscaleTag.Name \t\t= \"monsterScale\"\n\t\tscaleTag.Value \t\t= 5\n\t\tscaleTag.Parent \t= monsterManifest\n\n\t\tnewMonster.healthMulti = newMonster.healthMulti * 1000\n\n\t\tnewMonster.bonusLootMulti \t= 30\n\t\tnewMonster.bonusXPMulti \t= newMonster.bonusXPMulti * 500\n\t\tnewMonster.goldMulti \t\t= (newMonster.goldMulti or 1) * 3.5\n\n\t\tmonsterManifest.maxHealth.Value = newMonster.maxHealth\n\t\tmonsterManifest.health.Value \t= newMonster.health\n\n\t\tnewMonster.damageMulti = (newMonster.damageMulti or 1) * 5\n\n\t\tnewMonster.attackRange = (newMonster.attackRange or 0) * 5.1\n\telseif newMonster.superGiant or ((not newMonster.dontScale and not newMonster.boss) and (isNightTime() and rand:NextInteger(1,7500) == 13)) then\n\n\t\tnewMonster.scale = 3\n\t\tnewMonster.superGiant = true\n\t\tnewMonster.IS_MONSTER_ENRAGED \t= true\n\n\t\tif not ( _additionalStats and _additionalStats.level) then\n\t\t\tnewMonster.level = newMonster.level + 2\n\t\tend\n\n\t\tlocal monsterManifest \t= newMonster.manifest\n\t\tmonsterManifest.Size \t= monsterManifest.Size * 3\n\n\t\tlocal scaleTag \t\t= Instance.new(\"NumberValue\")\n\t\tscaleTag.Name \t\t= \"monsterScale\"\n\t\tscaleTag.Value \t\t= 3\n\t\tscaleTag.Parent \t= monsterManifest\n\n\t\tnewMonster.healthMulti = newMonster.healthMulti * 250\n\n\n\t\tnewMonster.bonusLootMulti \t= 20\n\t\tnewMonster.bonusXPMulti \t= newMonster.bonusXPMulti * 125\n\t\tnewMonster.goldMulti \t\t= (newMonster.goldMulti or 1) * 3\n\n\t\tmonsterManifest.maxHealth.Value = newMonster.maxHealth\n\t\tmonsterManifest.health.Value \t= newMonster.health\n\n\t\tnewMonster.damageMulti = (newMonster.damageMulti or 1) * 2.5\n\n\t\tnewMonster.attackRange = (newMonster.attackRange or 0) * 3\n\telseif newMonster.giant or ((not newMonster.dontScale and not newMonster.boss) and (isNightTime() and rand:NextInteger(1, 750) == 13)) then -- 1, 1000, 777\n\n\t\tnewMonster.giant = true\n\t\tnewMonster.IS_MONSTER_ENRAGED \t= true\n\n\t\tif not ( _additionalStats and _additionalStats.level) then\n\t\t\tnewMonster.level = newMonster.level + 1\n\t\tend\n\n\t\tnewMonster.scale = 2\n\n\t\tlocal monsterManifest \t= newMonster.manifest\n\t\tmonsterManifest.Size \t= monsterManifest.Size * 2\n\n\t\tlocal scaleTag \t\t= Instance.new(\"NumberValue\")\n\t\tscaleTag.Name \t\t= \"monsterScale\"\n\t\tscaleTag.Value \t\t= 2\n\t\tscaleTag.Parent \t= monsterManifest\n\n\t\tnewMonster.healthMulti = newMonster.healthMulti * 70\n\n\n\n\t\tnewMonster.bonusLootMulti \t= 10\n\n\t\tnewMonster.bonusXPMulti \t= newMonster.bonusXPMulti * 35\n\t\tnewMonster.goldMulti \t\t= (newMonster.goldMulti or 1) * 2.5\n\n\t\tmonsterManifest.maxHealth.Value = newMonster.maxHealth\n\t\tmonsterManifest.health.Value \t= newMonster.health\n\n\t\tnewMonster.damageMulti = (newMonster.damageMulti or 1) * 1.75\n\n\n\t\tnewMonster.attackRange = (newMonster.attackRange or 0) * 2\n\telseif newMonster.scale then\n\t\tlocal scale = newMonster.scale\n\n\t\tlocal monsterManifest \t= newMonster.manifest\n\t\tmonsterManifest.Size \t= monsterManifest.Size * scale\n\n\t\tlocal scaleTag \t\t= Instance.new(\"NumberValue\")\n\t\tscaleTag.Name \t\t= \"monsterScale\"\n\t\tscaleTag.Value \t\t= scale\n\t\tscaleTag.Parent \t= monsterManifest\n\n\t\tnewMonster.attackRange = (newMonster.attackRange or 0) * scale\n\telse\n\n\t\tlocal scale = 1 + rand:NextInteger(-5,5)/100\n\n\t\tnewMonster.scale = scale\n\n\t\tlocal monsterManifest \t= newMonster.manifest\n\t\tmonsterManifest.Size \t= monsterManifest.Size * scale\n\n\t\tlocal scaleTag \t\t= Instance.new(\"NumberValue\")\n\t\tscaleTag.Name \t\t= \"monsterScale\"\n\t\tscaleTag.Value \t\t= scale\n\t\tscaleTag.Parent \t= monsterManifest\n\n\t\tnewMonster.attackRange = (newMonster.attackRange or 0) * scale\n\tend\n\n\tif newMonster.giant or newMonster.superGiant or newMonster.gigaGiant or newMonster.boss or newMonster.resilient then\n\t\tlocal resilientTag = Instance.new(\"BoolValue\")\n\t\tresilientTag.Name = \"resilient\"\n\t\tresilientTag.Value = true\n\t\tresilientTag.Parent = newMonster.manifest\n\tend\n\n\tif postInitCallback then\n\t\tpostInitCallback(newMonster)\n\tend\n\n\tnewMonster.maxHealth = newMonster.maxHealth * (newMonster.healthMulti or 1);\n\tnewMonster.health = newMonster.maxHealth * (newMonster.healthMulti or 1);\n\tnewMonster.damage = newMonster.damage * (newMonster.damageMulti or 1);\n\n\tmanifest.BodyForce.Force = Vector3.new(0, newMonster.floats and 0 or -196.2 * manifest:getMass(), 0)\n\tmanifest.BodyVelocity.MaxForce = Vector3.new(100 * manifest:getMass(), newMonster.flies and 100 * manifest:getMass() or 0, 100 * manifest:getMass()) * 100 * (baseStats.velocityMaxForceMultiplier or 1)\n\n\tmanifest.CustomPhysicalProperties = PhysicalProperties.new(newMonster.density or 5, newMonster.friction or 0.4, newMonster.elasticity or 0.2)\n\n\tif newMonster.specialName then\n\t\tlocal nameTag = Instance.new(\"StringValue\")\n\t\tnameTag.Name = \"specialName\"\n\t\tnameTag.Value = newMonster.specialName\n\t\tnameTag.Parent = manifest\n\tend\n\n\tif newMonster.notGiant then\n\t\tlocal notGiantTag = Instance.new(\"BoolValue\")\n\t\tnotGiantTag.Name = \"notGiant\"\n\t\tnotGiantTag.Value = true\n\t\tnotGiantTag.Parent = manifest\n\tend\n\n\tif newMonster.alwaysRendered then\n\t\tlocal alwaysRenderedTag = Instance.new(\"BoolValue\")\n\t\talwaysRenderedTag.Name = \"alwaysRendered\"\n\t\talwaysRenderedTag.Value = true\n\t\talwaysRenderedTag.Parent = manifest\n\tend\n\n\tif newMonster.isPassive then\n\t\tlocal isPassiveTag \t= Instance.new(\"BoolValue\")\n\t\tisPassiveTag.Name \t= \"isPassive\"\n\t\tisPassiveTag.Value \t= true\n\t\tisPassiveTag.Parent = manifest\n\tend\n\n\tif newMonster.hideLevel then\n\t\tlocal hideLevelTag \t= Instance.new(\"BoolValue\")\n\t\thideLevelTag.Name \t= \"hideLevel\"\n\t\thideLevelTag.Value \t= true\n\t\thideLevelTag.Parent = manifest\n\tend\n\n\tif newMonster.isDamageImmune then\n\t\tlocal isDamageImmuneTag \t= Instance.new(\"BoolValue\")\n\t\tisDamageImmuneTag.Name \t\t= \"isDamageImmune\"\n\t\tisDamageImmuneTag.Value \t= true\n\t\tisDamageImmuneTag.Parent \t= manifest\n\tend\n\n\tif newMonster.dye then\n\t\tlocal monsterColorVariantTag \t= Instance.new(\"Color3Value\")\n\t\tmonsterColorVariantTag.Name \t= \"colorVariant\"\n\t\tmonsterColorVariantTag.Value \t= Color3.fromRGB(newMonster.dye.r, newMonster.dye.g, newMonster.dye.b)\n\t\tmonsterColorVariantTag.Parent \t= manifest\n\tend\n\n\tif newMonster.isTargetImmune then\n\t\tlocal isTargetImmune \t= Instance.new(\"BoolValue\")\n\t\tisTargetImmune.Name \t= \"isTargetImmune\"\n\t\tisTargetImmune.Value \t= true\n\t\tisTargetImmune.Parent \t= manifest\n\tend\n\n\tmanifest.maxHealth.Value \t\t= newMonster.maxHealth\n\tmanifest.health.Value \t\t\t= newMonster.health\n\tmanifest.level.Value \t\t\t= newMonster.level\n\n\tlocal entityGUIDTag = Instance.new(\"StringValue\")\n\tentityGUIDTag.Name = \"entityGUID\"\n\tentityGUIDTag.Value = httpService:GenerateGUID(false)\n\tentityGUIDTag.Parent = manifest\n\n\tmanifest.Parent = entityManifestCollectionFolder\n\tmanifest:SetNetworkOwner(nil)\n\n\tif (newMonster.scale >= 1.4) and (not newMonster.notGiant) then\n\t\tgame.CollectionService:AddTag(manifest, \"giantEnemy\")\n\tend\n\n\tif baseStats.boss then\n\t\tmanifest.BodyForce.Force = Vector3.new()\n\n\t\tlocal hitPart, hitPos = projectile.raycastForProjectile(\n\t\t\tRay.new(\n\t\t\t\tspawnCFrame.p,\n\t\t\t\tVector3.new(0, -300, 0)\n\t\t\t), {workspace.placeFolders}\n\t\t)\n\t\t--[[\n\t\tlocal downForce \t= Instance.new(\"BodyPosition\")\n\t\tdownForce.Name \t\t= \"bossDownForce\"\n\t\tdownForce.Position \t= Vector3.new(0, hitPos.Y + newMonster.manifest.Size.Y / 2 + 0.5, 0)\n\t\tdownForce.MaxForce \t= Vector3.new(0, math.huge, 0)\n\t\tdownForce.Parent \t= manifest\n\t\t]]\n\tend\n\n\t-- give ownership to the server if we're using accuratemonsterhitbox\n\t-- we don't want it falling through the ground :smile:\n\tif baseStats.useAccurateMonsterHitbox then\n\t\tmanifest:SetNetworkOwner(nil)\n\tend\n\n\t-- handle monster variant stuff --\n\tevents:fireEventLocal(\"monsterEntitySpawning\", newMonster)\n\n\t-- register this new monster internally\n\t-- to be updated by the scheduler\n\ttable.insert(MONSTER_COLLECTION, newMonster)\n\n\treturn newMonster\nend\n\nlocal function newPet(player, id, petEquipmentData)\n\tif not id then return end\n\tif not itemLookup[id] then return end\n\n\tlocal baseStats \t= itemLookup[id]\n\tlocal clientEntity \t= itemLookup[id].entity\n\tlocal manifest \t\t= getManifestFromClientEntity(clientEntity, baseStats, clientEntity.Name)\n\n\t-- return if manifest is nil (no model for this thing)\n\tif not manifest then warn(\"no manifest for pet \" .. tostring(baseStats.name)) return end\n\n\t-- default baseStats to internal defaults\n\tif not baseStats then baseStats = {} end\n\n\tif not clientEntity then return false end\n\n\t-- name manifest\n\tmanifest.Name \t\t\t= baseStats.name\n\tmanifest.entityId.Value = id\n\n\tlocal newMonster = {}\n\t\tnewMonster.monsterName = baseStats.name\n\n\t\t-- internal variables --\n\t\tnewMonster.__LAST_UPDATE \t\t\t\t\t= tick()\n\t\tnewMonster.__EVENTS \t\t\t\t\t\t= {}\n\n\t\t-- state variables --\n\t\tnewMonster.owner \t\t= player\n\t\tnewMonster.isMonsterPet = true\n\n\t\t-- identity variables\n\t\tnewMonster.manifest \t\t= manifest\n\t\tnewMonster.clientEntity \t= clientEntity\n\n\tmanifest.BodyForce.Force \t\t= Vector3.new(0, -196.2 * manifest:getMass(), 0)\n\tmanifest.BodyVelocity.MaxForce \t= Vector3.new(100 * manifest:getMass(), 0, 100 * manifest:getMass()) * 100 * (baseStats.velocityMaxForceMultiplier or 1)\n\n\tlocal statesLookup \t\t\t= baseStats.statesData\n\tlocal monsterStateMachine \t= stateMachineFactory.create(newMonster, statesLookup.default, statesLookup.states)\n\tnewMonster.stateMachine \t= monsterStateMachine\n\n\n\n\tmanifest.CustomPhysicalProperties = PhysicalProperties.new(newMonster.density or 5, newMonster.friction or 0.4, newMonster.elasticity or 0.2)\n\n\tlocal transitionEvent = monsterStateMachine.onTransition.Event:connect(function(old, new, newStateData)\n\t\tnewMonster:setState(new, newStateData)\n\tend)\n\n\ttable.insert(newMonster.__EVENTS, transitionEvent)\n\n\t-- set the metatable\n\tsetmetatable(newMonster, monsterClass)\n\n\t-- tag it as a pet so clients know..\n\tlocal monsterPetTag \t= Instance.new(\"IntValue\")\n\tmonsterPetTag.Name \t\t= \"pet\"\n\tmonsterPetTag.Value \t= id\n\tmonsterPetTag.Parent \t= manifest\n\n\tmanifest.entityType.Value = \"pet\"\n\n\tphysics:setWholeCollisionGroup(manifest, \"passthrough\")\n\n\tif petEquipmentData and petEquipmentData.customName then\n\t\tlocal monsterNicknameTag \t= Instance.new(\"StringValue\")\n\t\tmonsterNicknameTag.Name \t= \"nickname\"\n\t\tmonsterNicknameTag.Value \t= petEquipmentData.customName\n\t\tmonsterNicknameTag.Parent \t= manifest.entityId\n\tend\n\n\tif petEquipmentData and petEquipmentData.dye then\n\t\tlocal monsterColorVariantTag \t= Instance.new(\"Color3Value\")\n\t\tmonsterColorVariantTag.Name \t= \"colorVariant\"\n\t\tmonsterColorVariantTag.Value \t= Color3.fromRGB(petEquipmentData.dye.r, petEquipmentData.dye.g, petEquipmentData.dye.b)\n\t\tmonsterColorVariantTag.Parent \t= manifest\n\tend\n\n\t-- step it up\n\tupdateMonsterStateMachine(newMonster)\n\n\tlocal entityGUIDTag = Instance.new(\"StringValue\")\n\tentityGUIDTag.Name = \"entityGUID\"\n\tentityGUIDTag.Value = httpService:GenerateGUID(false)\n\tentityGUIDTag.Parent = manifest\n\n\t-- parent and set ownership to server\n\tmanifest.Parent = entityManifestCollectionFolder\n\tmanifest:SetNetworkOwner(nil)\n\n\t-- register this new monster internally\n\t-- to be updated by the scheduler\n\ttable.insert(MONSTER_COLLECTION, newMonster)\n\n\treturn newMonster\nend\n\n-- NOTE: This spawns a monster while respecting spawnRegion's\n-- spawn limits.\n-- TODO: finish this\nlocal function spawnMonster(monsterName, spawnRegionCollection, _spawnPosition, additionalData, postInitCallback)\n\t-- make sure we can spawn the monster here...\n\tif spawnRegionCollection then\n\t\tlocal spawnRegionSelection = _getSpawnRegionFromSpawnRegionCollection(spawnRegionCollection)\n\n\t\tif spawnRegionSelection then\n\t\t\tlocal spawnRegion = spawnRegionSelection.spawnRegion\n\t\t\tadditionalData = additionalData or {}\n\n\t\t\tfor _,valueObject in pairs(spawnRegionCollection:GetChildren()) do\n\t\t\t\tif valueObject:IsA(\"ValueBase\") then\n\t\t\t\t\tif valueObject:FindFirstChild(\"JSON\") then\n\t\t\t\t\t\tadditionalData[valueObject.Name] = game.HttpService:JSONDecode(valueObject.Value)\n\t\t\t\t\telse\n\t\t\t\t\t\tadditionalData[valueObject.Name] = valueObject.Value\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal spawnPosition = _spawnPosition or _getPositionFromSpawnRegion(spawnRegion)\n\t\t\tlocal newMonster = newMonster(monsterName, spawnPosition, spawnRegionCollection, spawnRegion, additionalData, postInitCallback)\n\n\t\t\t----------------- handle monster stuff\n\n\t\t\treturn newMonster\n\t\tend\n\telseif _spawnPosition then\n\t\tlocal newMonster = newMonster(monsterName, _spawnPosition, nil, nil, additionalData, postInitCallback)\n\n\t\treturn newMonster\n\tend\nend\n\nlocal function spawnMonsterPet(player, id, ...)\n\treturn newPet(player, id, ...)\nend\n\nlocal function onSpawnMonster(monsterName, spawnPosition, spawnRegionCollection, additionalData, postInitCallback)\n\treturn spawnMonster(monsterName, spawnRegionCollection, spawnPosition, additionalData, postInitCallback)\nend\n\n\n\nlocal function onSpawnMonsterPet(player, id, ...)\n\tlocal monsterPet = spawnMonsterPet(player, id, ...)\n\n\treturn monsterPet.manifest\nend\n\nlocal monsterIdolModelCache = Instance.new(\"Folder\")\nmonsterIdolModelCache.Name = \"monsterIdolModelCache\"\nmonsterIdolModelCache.Parent = game.ReplicatedStorage\n\n------------------------\n-- MAIN PROCESS LOGIC --\n------------------------\n\nlocal function onMonsterRemoved(monsterManifest)\n\tfor i, monster in pairs(MONSTER_COLLECTION) do\n\t\tif monster.manifest == monsterManifest then\n\t\t\ttable.remove(MONSTER_COLLECTION, i)\n\n\t\t\tif monster.__EVENTS then\n\t\t\t\tfor i, event in pairs(monster.__EVENTS) do\n\t\t\t\t\tevent:disconnect()\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif monster.stateMachine and monster.stateMachine.onTransition then\n\t\t\t\tmonster.stateMachine.onTransition:Destroy()\n\t\t\tend\n\n\t\t\tmonster.__EVENTS = nil\n\n\t\t\t-- get rid of states\n\t\t\tmonster.stateMachine.states = nil\n\n\t\t\t-- clear table.\n\t\t\tfor i, v in pairs(monster) do\n\t\t\t\tmonster[i] = nil\n\t\t\tend\n\n\t\t\tbreak\n\t\tend\n\tend\nend\n\nlocal rand = Random.new(os.time())\n\nlocal monsterStateRegistry = {}\n\nlocal function int__getMonsterStateInformation(monster)\n\treturn {\n\t\t[\"previous-state\"] \t= monster.stateMachine.previousState;\n\t\t[\"current-state\"] \t= monster.stateMachine.currentState;\n\t\t[\"name\"] \t\t\t= monster.monsterName;\n\t\t[\"target-player\"] \t= monster.targetPlayer and monster.targetPlayer.Name;\n\t\t[\"closest-player\"] \t= monster.closestPlayer and monster.closestPlayer.Name;\n\t\t[\"last-updated\"] \t= math.floor((tick() - monster.__LAST_UPDATE)*1000*100)/100;\n\t}\nend\n\nlocal monsterDebugStateData = {}\n\tmonsterDebugStateData.phase = \"init\"\n\tmonsterDebugStateData.monster = \"\"\n\tmonsterDebugStateData.stateBefore = \"\"\n\nlocal function onDumpMonsterManagerDebugInformation()\n\tlocal timeSinceLastMonsterUpdateCycle \t= math.floor((tick() - LAST_MONSTER_UPDATE_CYCLE)*1000*100)/100\n\tlocal timeSinceLastMOnsterUpdateCycleEnd = math.floor((tick() - LAST_MONSTER_UPDATE_CYCLE_END)*1000*100)/100\n\n\tlocal numberMonsterInMemory \t\t\t= #MONSTER_COLLECTION\n\n\twarn(\"MONSTER-MANAGER-DEBUG-DUMP\")\n\t\twarn(\"MONSTERS IN MEMORY:\", numberMonsterInMemory)\n\t\twarn(\"TIME SINCE LAST UPDATE CYCLE:\", timeSinceLastMonsterUpdateCycle,\"ms\")\n\t\twarn(\"TIME SINCE LAST UPDATE CYCLE:\", timeSinceLastMOnsterUpdateCycleEnd,\"ms\")\n\t\twarn(\"CURRENT STATE DATA --\", \"phase:\", monsterDebugStateData.phase, \"|\", \"monster:\", monsterDebugStateData.monster, \"|\", \"stateBefore:\", monsterDebugStateData.stateBefore)\n\t\twarn(\"MONSTER SPECIFIC INFORMATION\")\n\n\t\tfor i, v in pairs(MONSTER_COLLECTION) do\n\t\t\tlocal monsterDebugData = int__getMonsterStateInformation(v)\n\t\t\twarn(\n\t\t\t\tmonsterDebugData[\"name\"],\n\t\t\t\t\"|\", \"cState:\", monsterDebugData[\"current-state\"],\n\t\t\t\t\"|\", \"target:\", v.targetPlayer, v.targetEntity, v.targetEntity.Parent,\n\t\t\t\t\"|\", \"last updated:\", monsterDebugData[\"last-updated\"], \"ms\"\n\t\t\t)\n\t\tend\nend\n\nlocal function onRegisterToMonsterState(player, monsterManifest)\n\tlocal monster = _getMonsterByManifest(monsterManifest)\n\tif monster then\n\t\tif monsterStateRegistry[player] then\n\t\t\tmonsterStateRegistry[player]:disconnect()\n\t\tend\n\n\t\tmonsterStateRegistry[player] = monster.stateMachine.onTransition.Event:connect(function(old, new)\n\t\t\tnetwork:fireClient(\"monsterStateChanged\", player, int__getMonsterStateInformation(monster))\n\n\t\t\tif new == \"dead\" then\n\t\t\t\tif monsterStateRegistry[player] then\n\t\t\t\t--\tmonsterStateRegistry[player]:disconnect()\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\n\t\treturn true, int__getMonsterStateInformation(monster)\n\tend\n\n\treturn false, nil\nend\n\nlocal function triggerMonsterRewardsSequence(monster, playerWhoKilled, damageData)\n\tif monster.deathRewardsApplied then\n\t\treturn\n\tend\n\n\tmonster.deathRewardsApplied = true\n\t----------------\n\t----------------\n\t----------------\n\t----------------\n\tif playerWhoKilled then\n\t\tnetwork:fire(\"playerKilledMonster\", playerWhoKilled, monster.manifest, damageData.sourceType, damageData.sourceId)\n\tend\n\n\tlocal killer\n\tif playerWhoKilled then\n\t\tkiller = playerWhoKilled.Character and playerWhoKilled.Character.PrimaryPart\n\tend\n\n\tnetwork:fire(\"entityKillingBlow\", monster.manifest, killer, damageData)\n\n\tmonster.deathRewardsApplied = true\n\n\tlocal XPMulti = 1\n\tlocal lootMulti =  1\n\tlocal goldMulti = 1\n\n\tlocal totalDamageDealt \t= 0\n\tlocal damageDealers = 0\n\tfor player, damageDealt in pairs(monster.damageTable) do\n\t\tif player and damageDealt > 1 then\n\t\t\tlocal char = player.Character and player.Character.PrimaryPart\n\t\t\tif char and char:FindFirstChild(\"state\") and char.state.Value ~= \"dead\" then\n\t\t\t\ttotalDamageDealt = totalDamageDealt + damageDealt\n\t\t\t\tdamageDealers = damageDealers + 1\n\t\t\tend\n\t\tend\n\tend\n\tlocal playerInvolvementMulti = damageDealers ^ (1/3)\n\tXPMulti = XPMulti * playerInvolvementMulti\n\tgoldMulti = goldMulti * playerInvolvementMulti\n\tlootMulti = lootMulti * playerInvolvementMulti\n\n\tlocal totalEXP \t= monster.EXP * XPMulti\n\n\tlocal owners = {}\n\tlocal EXPTable = {}\n\t-- award EXP\n\tfor player, damageDealt in pairs(monster.damageTable) do\n\t\tif player and player.Parent and damageDealt > 0 then\n\t\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\t\tif playerData then\n\t\t\t\tif totalEXP > 0 then\n\t\t\t\t\tlocal xp = (totalEXP / damageDealers) * (playerData.nonSerializeData.statistics_final.wisdom or 1)\n\t\t\t\t\t-- party bonus\n\t\t\t\t\tlocal partyData = network:invoke(\"getPartyDataByPlayer\", player)\n\t\t\t\t\tif partyData and partyData.members then\n\t\t\t\t\t\tif #partyData.members >= 6 then\n\t\t\t\t\t\t\txp = xp * 1.2\n\t\t\t\t\t\telseif #partyData.members > 1 then\n\t\t\t\t\t\t\txp = xp * 1.1\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tEXPTable[player.Name] = xp\n\t\t\t\t\txp = math.ceil(xp)\n\n\t\t\t\t\tplayerData.nonSerializeData.incrementPlayerData(\"exp\",xp)\n\t\t\t\tend\n\n\t\t\t\tlocal monsterReference = {}\n\t\t\t\tfor i,v in pairs(monster) do\n\t\t\t\t\tif typeof(i) == \"string\" and typeof(v) ~= \"table\" then\n\t\t\t\t\t\tmonsterReference[i] = v\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tnetwork:fire(\n\t\t\t\t\t\"questTriggerOccurred\",\n\t\t\t\t\tplayer,\n\t\t\t\t\t\"monster-killed\",\n\t\t\t\t\t{[\"monsterName\"] = monster.monsterName; [\"monster\"] = monsterReference}\n\t\t\t\t)\n\n\t\t\t\tif monster.module.Name == \"Spider Queen\" then\n\t\t\t\t\tspawn(function()\n\t\t\t\t\t\tgame.BadgeService:AwardBadge(player.userId, 2124454074)\n\t\t\t\t\tend)\n\t\t\t\tend\n\n\t\t\t\ttable.insert(owners, player)\n\t\t\tend\n\t\tend\n\n\tend\n\n\tnetwork:fireAllClients(\"signal_exp\", EXPTable, monster.manifest)\n\t-- drop item loot\n\n\t-- super hacky todo: dont do this xD\n\n\tlocal lootDrops = utilities.copyTable(monster.lootDrops)\n\n\tlocal dropPosition = monster.manifest.Position\n\tlocal monsterName = monster.manifest.Name\n\n\tif monster.additionalLootDrops then\n\t\tfor _, lootDrop in pairs(monster.additionalLootDrops) do\n\t\t\ttable.insert(lootDrops, lootDrop)\n\t\tend\n\tend\n\tif not monster.dropsDisabled then\n\t\tspawn(function()\n\t\t\tfor n = 1, math.ceil(lootMulti) do\n\t\t\t\tfor _, lootDrop in pairs(lootDrops) do\n\t\t\t\t\tif n <= math.floor(lootMulti) or rand:NextNumber() < (lootMulti - math.floor(lootMulti)) then\n\n\t\t\t\t\t\tlocal lootSpawnChance = lootDrop.spawnChance or 0.001\n\t\t\t\t\t\twhile lootSpawnChance > rand:NextNumber() do\n\t\t\t\t\t\t\tlootSpawnChance = lootSpawnChance - 1\n\n\t\t\t\t\t\t\tlocal itemInfo\n\t\t\t\t\t\t\tif lootDrop.itemName then\n\t\t\t\t\t\t\t\tlocal real = game.ReplicatedStorage.itemData:FindFirstChild(lootDrop.itemName)\n\t\t\t\t\t\t\t\tif real then\n\t\t\t\t\t\t\t\t\titemInfo = require(real)\n\t\t\t\t\t\t\t\t\tlootDrop.id = itemInfo.id\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif itemInfo == nil then\n\t\t\t\t\t\t\t\titemInfo = itemLookup[lootDrop.id]\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tlocal itemOwners = {}\n\n\t\t\t\t\t\t\t-- ber: equal loot drop\n\t\t\t\t\t\t\tfor _, owner in pairs(owners) do\n\t\t\t\t\t\t\t\ttable.insert(itemOwners, owner)\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tlocal isUnique = itemInfo and (itemInfo.rarity and (itemInfo.rarity == \"Rare\" or itemInfo.rarity == \"Legendary\")) or (itemInfo.category and itemInfo.category == \"equipment\")\n\n\t\t\t\t\t\t\t-- ber: equal chance for unique drops\n\t\t\t\t\t\t\tif isUnique then\n\t\t\t\t\t\t\t\titemOwners = {itemOwners[rand:NextInteger(1, #itemOwners)]}\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t--assign value for gold\n\t\t\t\t\t\t\tif lootDrop.id == 1 then\n\t\t\t\t\t\t\t\tlocal baseMulti = (goldMulti or 1)\n\n\t\t\t\t\t\t\t\tlootDrop.value = math.ceil(baseMulti * monster.gold)\n\t\t\t\t\t\t\t\tlootDrop.source = \"monster:\"..monsterName:lower()\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t-- remove itemName and other non-important values from being saved\n\t\t\t\t\t\t\tlocal lootDropClone = utilities.copyTable(lootDrop)\n\t\t\t\t\t\t\tlootDropClone.itemName = nil\n\t\t\t\t\t\t\tlootDropClone.spawnChance = nil\n\n\t\t\t\t\t\t\tlocal dropInformation = {\n\t\t\t\t\t\t\t\titemOwners = itemOwners;\n\t\t\t\t\t\t\t\tdropPosition = dropPosition;\n\t\t\t\t\t\t\t\tlootDropData = lootDropClone;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif lootDrop.dropCircumstance then\n\t\t\t\t\t\t\t\tdropInformation = lootDrop.dropCircumstance(dropInformation, {network = network})\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tlocal physItem\n\t\t\t\t\t\t\tif lootDrop.id == 181 then\n\t\t\t\t\t\t\t\tlocal monsterIdolModel = monsterIdolModelCache:FindFirstChild(monster.module.Name)\n\n\t\t\t\t\t\t\t\tif monsterIdolModel == nil then\n\n\t\t\t\t\t\t\t\t\tif game.ReplicatedStorage.monsterLookup:FindFirstChild(monster.module.Name):FindFirstChild(\"displayEntity\") then\n\t\t\t\t\t\t\t\t\tmonsterIdolModel = game.ReplicatedStorage.monsterLookup:FindFirstChild(monster.module.Name).displayEntity:Clone()\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\tmonsterIdolModel = game.ReplicatedStorage.monsterLookup:FindFirstChild(monster.module.Name).entity:Clone()\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\tlocal extents = monsterIdolModel:GetExtentsSize()\n\t\t\t\t\t\t\t\t\tlocal min = math.min(extents.X, extents.Y, extents.Z)\n\t\t\t\t\t\t\t\t\tlocal max = math.max(extents.X, extents.Y, extents.Z)\n\t\t\t\t\t\t\t\t\tlocal reductionFactor = 4/(min+max)\n\n\t\t\t\t\t\t\t\t\tutilities.scale(monsterIdolModel, reductionFactor)\n\t\t\t\t\t\t\t\t\tlocal primary = monsterIdolModel.PrimaryPart\n\t\t\t\t\t\t\t\t\tprimary.Name = \"manifest\"\n\t\t\t\t\t\t\t\t\tfor i,v in pairs(monsterIdolModel:GetChildren()) do\n\t\t\t\t\t\t\t\t\t\tif v:IsA(\"BasePart\") then\n\t\t\t\t\t\t\t\t\t\t\tv.Material = Enum.Material.Glass\n\t\t\t\t\t\t\t\t\t\t\tv.Reflectance = v.Reflectance + 0.2\n\t\t\t\t\t\t\t\t\t\t\tv.Transparency = 0\n\t\t\t\t\t\t\t\t\t\t\tv.Color = Color3.new(0,0,0)\n\t\t\t\t\t\t\t\t\t\t\tif v ~= primary then\n\t\t\t\t\t\t\t\t\t\t\t\tv.Parent = primary\n\t\t\t\t\t\t\t\t\t\t\t\tv.Massless = true\n\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tlocal oldModel = monsterIdolModel\n\t\t\t\t\t\t\t\t\tprimary.Size = Vector3.new(2.2,2.2,2.2)\n\t\t\t\t\t\t\t\t\tprimary.Name = monster.module.Name\n\t\t\t\t\t\t\t\t\tprimary.Parent = monsterIdolModelCache\n\t\t\t\t\t\t\t\t\tmonsterIdolModel = primary\n\t\t\t\t\t\t\t\t\toldModel:Destroy()\n\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tphysItem = monsterIdolModel:Clone()\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif #dropInformation.itemOwners > 0 then\n\t\t\t\t\t\t\t\t-- drop the item!\n\t\t\t\t\t\t\t\tlocal success = monster:dropItem(dropInformation, physItem, lootMulti)\n\t\t\t\t\t\t\t\tassert(success, \"failed to drop monster loot\")\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\t\twait()\n\t\tend)\n\tend\nend\n\n-- give api function to drop rewards for a monster\n-- ie, special way to grant exp/rewards without death of monster\nfunction monsterClass:dropRewards(playerWhoKilled, damageData)\n\tif self.deathRewardsApplied then\n\t\treturn false\n\tend\n\n\ttriggerMonsterRewardsSequence(self, playerWhoKilled, damageData)\n\n\treturn true\nend\n\nlocal function onMonsterDamageRequestReceived(player, monsterManifest, damageData)\n\tlocal monster\n\tfor _, monsterToCheck in pairs(MONSTER_COLLECTION) do\n\t\tif monsterToCheck.manifest == monsterManifest then\n\t\t\tmonster = monsterToCheck\n\t\t\tbreak\n\t\tend\n\tend\n\n\tif monster then\n\t\tif monster.health > 0 then\n\t\t\tlocal monsterData \t= monsterLookup[monster.monsterName]\n\t\t\tlocal statesData \t= monsterData.statesData\n\n\t\t\t-- process status effects that may affect damage here\n\t\t\t-- unfortunately we don't have as robust a system for mobs\n\t\t\t-- as we do for players, but this will have to suffice for now\n\t\t\t-- Davidii wrote this\n\t\t\tdo\n\t\t\t\tlocal manifest = monster.manifest\n\t\t\t\tlocal guid = utilities.getEntityGUIDByEntityManifest(manifest)\n\n\t\t\t\tif guid then\n\t\t\t\t\tlocal statuses = network:invoke(\"getStatusEffectsOnEntityManifestByEntityGUID\", guid)\n\n\t\t\t\t\tfor _, status in pairs(statuses) do\n\t\t\t\t\t\tfor stat, value in pairs(status.statusEffectModifier.modifierData or {}) do\n\t\t\t\t\t\t\tif stat == \"damageTakenMulti\" then\n\t\t\t\t\t\t\t\tdamageData.damage = damageData.damage * (1 + value)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- allow monsters to modify damage done to them\n\t\t\tif statesData.processDamageRequestToMonster then\n\t\t\t\tdamageData = statesData.processDamageRequestToMonster(monster, damageData) or damageData\n\t\t\tend\n\n\t\t\t-- player is hitting monster, or monster is hitting monster?\n\t\t\tevents:fireEventLocal(\"entityWillDealDamage\", player, monsterManifest, damageData)\n\n\t\t\tlocal newHealth \t\t\t\t\t\t= math.clamp(monster.health - damageData.damage, -99999999, monster.maxHealth)\n\t\t\tlocal actualDamage \t\t\t\t\t\t= monster.health - newHealth\n\t\t\tlocal actualDamageForLootCalculation \t= monster.health - math.clamp(newHealth, 0, monster.maxHealth)\n\n\t\t\tmonster.health \t\t\t\t\t\t= newHealth\n\n\t\t\tmonster.manifest.health.Value \t\t= monster.health\n\t\t\tmonster.manifest.maxHealth.Value\t= monster.maxHealth\n\n\t\t\tmonster.damageTable = monster.damageTable or {}\n\n\t\t\tif monster.__MONSTER_EVENTS.onMonsterDamaged then\n\t\t\t\tmonster.__MONSTER_EVENTS.onMonsterDamaged(monster, damageData.damage, player)\n\t\t\tend\n\n\t\t\tif player then\n\t\t\t\tif damageData.sourceType == \"ability\" then\n\t\t\t\t\tif not monster.playersWithAbilityUse then\n\t\t\t\t\t\tmonster.playersWithAbilityUse = {}\n\t\t\t\t\tend\n\n\t\t\t\t\tmonster.playersWithAbilityUse[player.userId] = true\n\t\t\t\tend\n\n\t\t\t\tif statesData.onDamageReceived then\n\t\t\t\t\tstatesData.onDamageReceived(monster, \"player\", player, damageData.damage)\n\t\t\t\tend\n\n\n\n\t\t\t\tif damageData.damage > 0 then\n\n\t\t\t\t\t-- make monsters switch targets if attacked by someone closer\n\t\t\t\t\tif player.Character and player.Character.PrimaryPart then\n\t\t\t\t\t\tif monster.targetEntity and monster.targetEntity ~= player.Character.PrimaryPart then\n\n\t\t\t\t\t\t\tlocal monsterPosition = monster.entity:IsA(\"BasePart\") and monster.entity.Position or monster.entity:IsA(\"Model\") and monster.entity.PrimaryPart.Position\n\t\t\t\t\t\t\tlocal targetPosition = monster.targetEntity:IsA(\"BasePart\") and monster.targetEntity.Position or monster.targetEntity:IsA(\"Model\") and monster.targetEntity.PrimaryPart.Position\n\n\t\t\t\t\t\t\tif (player.Character.PrimaryPart.Position -  monsterPosition).magnitude < (targetPosition -  monsterPosition).magnitude or rand:NextNumber() <= 0.3 then\n\t\t\t\t\t\t\t\tmonster.targetEntity = player.Character.PrimaryPart\n\t\t\t\t\t\t\t\tmonster.entityMonsterWasAttackedBy = player.Character.PrimaryPart\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tmonster.damageTable[player] = (monster.damageTable[player] or 0) + actualDamageForLootCalculation\n\n\t\t\t\t\tif not monster.targetEntity and player.Character.PrimaryPart then\n\t\t\t\t\t\tif statesData.states[\"attacked-by-player\"] and statesData.states[\"idling\"] and statesData.states[\"following\"] then\n\t\t\t\t\t\t\tmonster.entityMonsterWasAttackedBy = player.Character.PrimaryPart\n\t\t\t\t\t\t\tmonster.stateMachine:forceStateChange(\"attacked-by-player\")\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tnetwork:fireAllClients(\"signal_damage\", monster.manifest, damageData)\n\t\tif monster.health <= 0 then\n\t\t\t-- trigger the rewards\n\t\t\tif not monster.deathRewardsApplied then\n\t\t\t\ttriggerMonsterRewardsSequence(monster, player, damageData)\n\t\t\tend\n\t\t\t-- monster died. kill it.\n\t\t\tmonster.stateMachine:forceStateChange(\"dead\")\n\t\telse\n\t\t\tif monster.stateMachine.states.hurt then\n\t\t\t\tmonster.stateMachine:forceStateChange(\"hurt\")\n\t\t\tend\n\t\tend\n\n\t\treturn true, monster.health <= 0\n\tend\n\n\treturn false, false\nend\n\nlocal function updateMonsterLogic()\n\tLAST_MONSTER_UPDATE_CYCLE = tick()\n\n\tmonsterDebugStateData.phase = \"start\"\n\tmonsterDebugStateData.monster = \"\"\n\tmonsterDebugStateData.stateBefore = \"\"\n\n\tlocal lastMonsterUpdated \t= nil\n\tlocal preState \t\t\t\t= nil\n\n\tlocal currTime = tick()\n\tfor i, monster in pairs(MONSTER_COLLECTION) do\n\t\tlocal success, err \t\t\t= pcall(function()\n\t\t\tif monster.manifest and monster.manifest.Parent == entityManifestCollectionFolder and monster.state ~= \"dead\" then\n\n\t\t\t\t-- update the brain\n\t\t\t\tlastMonsterUpdated = monster\n\n\t\t\t\tif not monster.isMonsterPet then\n\n\t\t\t\t\t-- fetch enemies within range\n\t\t\t\t\tlocal entities do\n\t\t\t\t\t\tlocal monsterData \t= monsterLookup[monster.monsterName]\n\t\t\t\t\t\tlocal statesData \t= monsterData.statesData\n\n\t\t\t\t\t\tif statesData.getClosestEntities then\n\t\t\t\t\t\t\tentities = statesData.getClosestEntities(monster)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tentities = defaultMonsterState.getClosestEntities(monster)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal nearbyTargets = {}\n\n\t\t\t\t\tlocal closestEntity\n\t\t\t\t\tlocal nearestDist = 999\n\n\t\t\t\t\t-- find nearby targets\n\t\t\t\t\tfor i, entityManifest in pairs(entities) do\n\t\t\t\t\t\tif (not entityManifest:FindFirstChild(\"isStealthed\")) and (not entityManifest:FindFirstChild(\"isTargetImmune\")) then\n\t\t\t\t\t\t\tif entityManifest.state.Value ~= \"dead\" and entityManifest.state.Value ~= \"sitting\" and entityManifest.entityType.Value ~= \"pet\" then\n\t\t\t\t\t\t\t\tlocal dist = (entityManifest.Position - monster.manifest.Position).magnitude\n\t\t\t\t\t\t\t\tif dist < monster.sightRange then\n\t\t\t\t\t\t\t\t\tnearbyTargets[#nearbyTargets + 1] = entityManifest\n\t\t\t\t\t\t\t\t\tif dist <= nearestDist then\n\t\t\t\t\t\t\t\t\t\tclosestEntity = entityManifest\n\t\t\t\t\t\t\t\t\t\tnearestDist = dist\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\n\t\t\t\t\tif monster.closestEntity and (monster.closestEntity.Parent == nil or monster.closestEntity.state.Value == \"dead\") then\n\t\t\t\t\t\tmonster.closestEntity = nil\n\t\t\t\t\tend\n\n\t\t\t\t\t--legacy\n\t\t\t\t\tmonster.closestEntity = (monster.closestEntity == monster.targetEntity and monster.closestEntity) or closestEntity\n\t\t\t\t\tmonster.nearbyTargets = nearbyTargets\n\n\t\t\t\t\t-- ITS TIME FOR G-G-G-GALAXY BRAIN MMMMMONSTERRRRR AI ! ! !\n\t\t\t\t\tmonster.entityDensityData = getEntityDensity(monster)\n\n\t\t\t\t\t-- targetEntity\n\t\t\t\t\tif monster.targetEntity and (monster.targetEntity.Parent == nil or monster.targetEntity.state.Value == \"dead\") then\n\t\t\t\t\t\tmonster:setTargetEntity(nil, nil)\n\t\t\t\t\tend\n\n\t\t\t\t\t-- despawn night mobs\n\t\t\t\t\tif nearestDist >= 100 and monster.nightBoosted and not (game.Lighting.ClockTime < 5.9 or game.Lighting.ClockTime > 18.6) then\n\t\t\t\t\t\tif monster and monster.manifest and monster.manifest.Parent then\n\t\t\t\t\t\t\tmonster.manifest:Destroy()\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tmonsterDebugStateData.phase = \"before-stateUpdate\"\n\n\t\t\t\tmonster.__LAST_UPDATE = currTime\n\n\t\t\t\tpreState = monster.stateMachine.currentState\n\t\t\t\tupdateMonsterStateMachine(monster)\n\n\t\t\t\tmonsterDebugStateData.phase = \"after-stateUpdate\"\n\t\t\tend\n\t\tend)\n\t\tif not success then\n\t\t\twarn(\"manager_monster::\" .. tostring(lastMonsterUpdated.monsterName) .. \"::\" .. tostring(lastMonsterUpdated.stateMachine.currentState) .. \"::\", err)\n\t\t\twarn(\"killing entity\")\n\t\t\tif monster and monster.manifest and monster.manifest.Parent then\n\t\t\t\tmonster.manifest:Destroy()\n\t\t\tend\n\t\tend\n\tend\n\n\tmonsterDebugStateData.phase = \"end\"\n\tmonsterDebugStateData.monster = \"\"\n\tmonsterDebugStateData.stateBefore = \"\"\n\n\tLAST_MONSTER_UPDATE_CYCLE_END = tick()\nend\n\nlocal function onPlayerCharacterDied(player)\n\tif not player.Character or not player.Character.PrimaryPart then return end\n\n\tfor i, monster in pairs(MONSTER_COLLECTION) do\n\t\tif monster.targetEntity == player.Character.PrimaryPart then\n\t\t\tmonster:setTargetEntity(nil, nil, true)\n\n\t\t\tupdateMonsterStateMachine(monster)\n\t\tend\n\tend\nend\n\nlocal function onPlayerRemoving(player)\n\tplayerLastMonsterKill[player] = nil\nend\n\nlocal function monsterHasAttribute(monster, attribute)\n\tif attribute == \"enraged\" then\n\t\treturn not not monster.IS_MONSTER_ENRAGED\n\tend\n\n\treturn monster.variation == attribute\nend\n\nlocal function main()\n\t-- register for variants --\n\tevents:registerForEvent(\"entityDiedTakingDamage\", function(sourcePlayer, entityHitboxDamaged, damageData)\n\t\tif not configuration.getConfigurationValue(\"doSpawnNightTimeVariants\") then return end\n\n\t\tlocal sourceEntityHitbox = utilities.getEntityManifestByEntityGUID(damageData.sourceEntityGUID)\n\n\t\tif sourceEntityHitbox then\n\t\t\tlocal monsterData = _getMonsterByManifest(sourceEntityHitbox)\n\n\t\t\tif monsterData then\n\n\t\t\tend\n\t\tend\n\tend)\n\n\tevents:registerForEvent(\"monsterEntitySpawning\", function(monster)\n\t\tif not configuration.getConfigurationValue(\"doSpawnNightTimeVariants\") then return end\n\n\t\tif monsterHasAttribute(monster, \"stalker\") then\n\t\t\tmonster.autoStealthTimer = tick()\n\n\t\t\tlocal wasApplied, reason = network:invoke(\n\t\t\t\t\"applyStatusEffectToEntityManifest\",\n\t\t\t\tmonster.manifest,\n\t\t\t\t\"stealth\",\n\t\t\t\t{\n\t\t\t\t\tduration = 9999,\n\t\t\t\t},\n\t\t\t\tmonster.manifest,\n\t\t\t\t\"autoStealth_stealth\",\n\t\t\t\t0\n\t\t\t)\n\t\tend\n\tend)\n\n\tevents:registerForEvent(\"entityWillDealDamage\", function(sourcePlayer, entityHitboxDamaged, damageData)\n\t\tif not configuration.getConfigurationValue(\"doSpawnNightTimeVariants\") then return end\n\n\t\tlocal sourceEntityHitbox = utilities.getEntityManifestByEntityGUID(damageData.sourceEntityGUID)\n\n\t\tif sourceEntityHitbox then\n\t\t\tlocal monsterDealingDamageData \t= _getMonsterByManifest(sourceEntityHitbox)\n\n\t\t\t-- note: not sure if this exists, make sure to check!\n\t\t\tlocal monsterTakingDamageData \t= entityHitboxDamaged and _getMonsterByManifest(entityHitboxDamaged)\n\n\t\t\tif monsterDealingDamageData then\n\t\t\t\tif monsterHasAttribute(monsterDealingDamageData, \"cursed\") then\n\t\t\t\t\tif math.random() < (1 / 3) then\n\t\t\t\t\t\tlocal wasApplied, reason = require(game.ReplicatedStorage.modules.network):invoke(\n\t\t\t\t\t\t\t\"applyStatusEffectToEntityManifest\",\n\t\t\t\t\t\t\tentityHitboxDamaged,\n\t\t\t\t\t\t\t\"poison\",\n\t\t\t\t\t\t\t{duration = 3; healthLost = 0.2 * damageData.damage},\n\t\t\t\t\t\t\tsourceEntityHitbox,\n\t\t\t\t\t\t\t\"monster-variant\",\n\t\t\t\t\t\t\t\"poison\"\n\t\t\t\t\t\t)\n\t\t\t\t\tend\n\t\t\t\telseif monsterHasAttribute(monsterDealingDamageData, \"stalker\") or (monsterTakingDamageData and monsterHasAttribute(monsterTakingDamageData, \"stalker\")) then\n\t\t\t\t\t-- this will revoke stealth regardless of if the monster is being damaged or dealing damage\n\t\t\t\t\tlocal monsterDataToRevokeStealthFrom = monsterHasAttribute(monsterDealingDamageData, \"stalker\") and monsterDealingDamageData or monsterTakingDamageData\n\n\t\t\t\t\tif network:invoke(\"doesEntityManifestHaveStatusEffectBySourceType\", monsterDataToRevokeStealthFrom.manifest, \"autoStealth_stealth\") then\n\t\t\t\t\t\tnetwork:invoke(\"removeStatusEffectFromEntityManifestBySourceType\", monsterDataToRevokeStealthFrom.manifest, \"autoStealth_stealth\")\n\n\t\t\t\t\t\tmonsterDataToRevokeStealthFrom.autoStealthTimer = tick()\n\n\t\t\t\t\t\tdelay(5, function()\n\t\t\t\t\t\t\tif tick() - monsterDataToRevokeStealthFrom.autoStealthTimer >= 5 and monsterDataToRevokeStealthFrom.manifest.Parent and not network:invoke(\"doesEntityManifestHaveStatusEffectBySourceType\", monsterDataToRevokeStealthFrom.manifest, \"autoStealth_stealth\") then\n\t\t\t\t\t\t\t\tlocal wasApplied, reason = network:invoke(\n\t\t\t\t\t\t\t\t\t\"applyStatusEffectToEntityManifest\",\n\t\t\t\t\t\t\t\t\tmonsterDataToRevokeStealthFrom.manifest,\n\t\t\t\t\t\t\t\t\t\"stealth\",\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tduration = 9999,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tmonsterDataToRevokeStealthFrom.manifest,\n\t\t\t\t\t\t\t\t\t\"autoStealth_stealth\",\n\t\t\t\t\t\t\t\t\t0\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif monsterTakingDamageData and monsterHasAttribute(monsterTakingDamageData, \"short-fused\") then\n\t\t\t\tlocal wasApplied, reason = require(game.ReplicatedStorage.modules.network):invoke(\n\t\t\t\t\t\"applyStatusEffectToEntityManifest\",\n\t\t\t\t\tentityHitboxDamaged,\n\t\t\t\t\t\"explode\",\n\t\t\t\t\t{duration = 1},\n\t\t\t\t\tsourceEntityHitbox,\n\t\t\t\t\t\"monster-variant\",\n\t\t\t\t\t\"explosion\"\n\t\t\t\t)\n\t\t\tend\n\n\t\t\tif monsterTakingDamageData and monsterHasAttribute(monsterTakingDamageData, \"enraged\") then\n\t\t\t\tif not monsterTakingDamageData.ENRAGED_AGGRO_TIMER then\n\t\t\t\t\tmonsterTakingDamageData.ENRAGED_AGGRO_TIMER = 0\n\t\t\t\tend\n\n\t\t\t\tif tick() - monsterTakingDamageData.ENRAGED_AGGRO_TIMER > 5 then\n\t\t\t\t\tmonsterTakingDamageData.ENRAGED_AGGRO_TIMER = tick()\n\n\t\t\t\t\tfor i, oMonsterData in pairs(MONSTER_COLLECTION) do\n\t\t\t\t\t\tif oMonsterData.manifest and (oMonsterData.manifest.Position - monsterTakingDamageData.manifest.Position).magnitude < 100 then\n\t\t\t\t\t\t\toMonsterData:setTargetEntity(sourceEntityHitbox)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\n\t-- end register for variants --\n\n\tgame.Players.PlayerRemoving:connect(onPlayerRemoving)\n\tentityManifestCollectionFolder.ChildRemoved:connect(onMonsterRemoved)\n\n\tnetwork:create(\"setIsSpawnRegionCollectionDisabled_server\", \"BindableFunction\", \"OnInvoke\", setIsSpawnRegionCollectionDisabled)\n\n\tnetwork:create(\"monsterDamageRequest_server\", \"BindableFunction\", \"OnInvoke\", onMonsterDamageRequestReceived)\n\tnetwork:create(\"getMonsterDataByMonsterManifest_server\", \"BindableFunction\", \"OnInvoke\", function(monsterManifest, specificProperty)\n\t\tif specificProperty then\n\t\t\treturn _getMonsterByManifest(monsterManifest, specificProperty)\n\t\telse\n\t\t\tlocal monster = _getMonsterByManifest(monsterManifest)\n\n\t\t\tif not monster then\n\t\t\t\treturn nil\n\t\t\tend\n\n\t\t\tmonster = utilities.copyTable(monster)\n\t\t\tpurgeNumericKeys(monster)\n\t\t\treturn monster\n\t\tend\n\tend)\n\tnetwork:create(\"registerToMonsterState\", \"RemoteFunction\", \"OnServerInvoke\", onRegisterToMonsterState)\n\tnetwork:create(\"monsterStateChanged\", \"RemoteEvent\")\n\tnetwork:create(\"playerKilledMonster\", \"BindableEvent\")\n\n\tnetwork:create(\"spawnMonsterPet\", \"BindableFunction\", \"OnInvoke\", onSpawnMonsterPet)\n\tnetwork:create(\"dumpMonsterManagerDebugInformation\", \"BindableFunction\", \"OnInvoke\", onDumpMonsterManagerDebugInformation)\n\n\tnetwork:connect(\"playerCharacterDied\", \"Event\", onPlayerCharacterDied)\n\n\tnetwork:create(\"getMonsterEntityDataTag\", \"BindableFunction\", \"OnInvoke\", function(monsterEntity, nameTag)\n\t\tlocal monsterData do\n\t\t\tfor i, monster in pairs(MONSTER_COLLECTION) do\n\t\t\t\tif monster.manifest == monsterEntity then\n\t\t\t\t\tmonsterData = monster\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif monsterData then\n\t\t\treturn monsterData[nameTag]\n\t\tend\n\tend)\n\n\tnetwork:create(\"setMonsterEntityDataTag\", \"BindableFunction\", \"OnInvoke\", function(monsterEntity, nameTag, value)\n\t\tlocal monsterData do\n\t\t\tfor i, monster in pairs(MONSTER_COLLECTION) do\n\t\t\t\tif monster.manifest == monsterEntity then\n\t\t\t\t\tmonsterData = monster\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif monsterData then\n\t\t\tmonsterData[nameTag] = value\n\t\tend\n\tend)\n\n\tnetwork:create(\"setMonsterTargetEntity\", \"BindableFunction\", \"OnInvoke\", function(monsterEntity, targetEntity, targetEntitySetSource, targetEntityLockType)\n\t\tlocal monsterData do\n\t\t\tfor i, monster in pairs(MONSTER_COLLECTION) do\n\t\t\t\tif monster.manifest == monsterEntity then\n\t\t\t\t\tmonsterData = monster\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif monsterData and targetEntity then\n\t\t\tmonsterData:setTargetEntity(targetEntity, nil, targetEntitySetSource, targetEntityLockType)\n\t\tend\n\tend)\n\tlocal function isNight()\n\t\treturn game.Lighting.ClockTime < 5.9 or game.Lighting.ClockTime > 18.6\n\tend\n\tspawn(function() while wait(isNight() and MONSTER_SPAWN_CYCLE_TIME * 0.5 or MONSTER_SPAWN_CYCLE_TIME) do\n\t\tfor i, monsterSpawnInformation in pairs(getSpawnRegionsUnderpopulated()) do\n\t\t\tspawnMonster(monsterSpawnInformation.monsterNameToSpawn, monsterSpawnInformation.spawnRegionCollection)\n\t\tend\n\tend end)\n\n\twhile wait(1/5) do\n\t\tupdateMonsterLogic()\n\tend\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tutilities = Modules.utilities\n\tphysics = Modules.physics\n\tplaceSetup = Modules.placeSetup\n\tprojectile = Modules.projectile\n\tconfiguration = Modules.configuration\n\tevents = Modules.events\n\n\tspawnRegionCollectionsFolder = placeSetup.getPlaceFolder(\"spawnRegionCollections\")\n\tentityManifestCollectionFolder = placeSetup.getPlaceFolder(\"entityManifestCollection\")\n\tentityRenderCollectionFolder = placeSetup.getPlaceFolder(\"entityRenderCollection\")\n\titemsFolder = placeSetup.getPlaceFolder(\"items\")\n\tentitiesFolder = placeSetup.getPlaceFolder(\"entities\")\n\tfoilage = placeSetup.getPlaceFolder(\"foilage\")\n\n\tMONSTER_RAYCAST_IGNORE_LIST = {\n\t\tspawnRegionCollectionsFolder,\n\t\tentityManifestCollectionFolder,\n\t\tentityRenderCollectionFolder,\n\t\titemsFolder,\n\t\tentitiesFolder,\n\t\tfoilage\n\t}\n\n\tphysics:setWholeCollisionGroup(baseHitbox, \"monsters\")\n\n\tnetwork:create(\"signal_damage\", \"RemoteEvent\")\n\tnetwork:create(\"spawnMonster\", \"BindableFunction\", \"OnInvoke\", onSpawnMonster)\n\n\tspawn(main)\nend\n\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_network.lua",
    "content": "-- Routes all client->server remote event and function calls through a secure channel\n-- Author: berezaa\nlocal module = {}\n\nmodule.priority = 2\n\nlocal network\n\nlocal RunService = game:GetService(\"RunService\")\n\nlocal signal = Instance.new(\"RemoteEvent\")\nsignal.Name = \"signal\"\nsignal.Parent = game.ReplicatedStorage\n\nlocal playerRequest = Instance.new(\"RemoteFunction\")\nplayerRequest.Name = \"playerRequest\"\nplayerRequest.Parent = game.ReplicatedStorage\n\nlocal function getServerNetworkLog(player)\n\tif player:FindFirstChild(\"developer\") then\n\t\treturn network.log\n\tend\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\n\tsignal.OnServerEvent:connect(function(player, eventName, ...)\n\t\tlocal event = game.ServerStorage.serverNetwork:FindFirstChild(eventName)\n\t\tif event then\n\t\t\treturn event:Fire(player, ...)\n\t\telse\n\t\t\tif network then\n\t\t\t\tif RunService:IsStudio() then\n\t\t\t\t\terror(\"MISSING REMOTE EVENT HIT! WOULD BAN IN PROD. Name: \" .. eventName)\n\t\t\t\telse\n\t\t\t\t\twarn(player.Name, \"kicked for bad remote event request:\", eventName)\n\t\t\t\t\tnetwork:invoke(\"banPlayer\", player, 60 * 60 * 24, \"Compromised client\", \"network\")\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\n\n\tplayerRequest.OnServerInvoke = function(player, requestName, ...)\n\t\tlocal request = game.ServerStorage.serverNetwork:FindFirstChild(requestName)\n\t\tif request then\n\t\t\treturn request:Invoke(player, ...)\n\t\telse\n\t\t\tif network then\n\t\t\t\tif RunService:IsStudio() then\n\t\t\t\t\terror(\"MISSING REMOTE FUNCTION HIT! WOULD BAN IN PROD. Name: \" .. requestName)\n\t\t\t\telse\n\t\t\t\t\twarn(player.Name, \"kicked for bad remote function request:\", requestName)\n\t\t\t\t\tnetwork:invoke(\"banPlayer\", player, 60 * 60 * 24, \"Compromised client\", \"network\")\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tnetwork:create(\"getServerNetworkLog\", \"RemoteFunction\", \"OnServerInvoke\", getServerNetworkLog)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_orb.lua",
    "content": "local module = {}\n\nlocal CollectionService = game:GetService(\"CollectionService\")\n--\nlocal network\n\nlocal assetsFolder = game.ReplicatedStorage:WaitForChild(\"assets\")\n\nlocal placeIdList do\n\t-- main game\n\tif game.GameId == 833209132 then\n\t\tplaceIdList = {\n\t\t\t2119298605, -- nilgarf\n\t\t}\n\n\t-- free to play\n\telseif game.GameId == 712031239 then\n\t\tplaceIdList = {\n\t\t\t4787415375, -- crabby den\n\t\t\t4042431927, -- enchanted forest\n\t\t\t4041618739, -- great crossroads\n\t\t\t4042399045, -- lost corridor\n\t\t\t4041616995, -- mushroom forest\n\t\t\t4041642879, -- mushroom grotto\n\t\t\t4041449372, -- mushtown\n\t\t\t4042577479, -- nilgarf\n\t\t\t4042595899, -- nilgarf sewers\n\t\t\t4042356215, -- port fidelio\n\t\t\t4042533453, -- redwood pass\n\t\t\t4042327457, -- scallop shores\n\t\t\t4784800626, -- seaside path\n\t\t\t4787417227, -- shiprock bottom\n\t\t\t4786263828, -- spider queen's lair\n\t\t\t4784798551, -- the clearing\n\t\t\t4042381342, -- colosseum\n\t\t\t4042493740, -- tree of life\n\t\t\t4042553675, -- warrior stronghold\n\t\t}\n\tend\nend\n\nlocal orbSpawn do\n\tlocal orbSpawnParts = CollectionService:GetTagged(\"orbSpawn\")\n\tif #orbSpawnParts == 1 then\n\t\torbSpawn = Instance.new(\"Vector3Value\")\n\t\torbSpawn.Name = \"orbSpawn\"\n\t\torbSpawn.Value = orbSpawnParts[1].Position\n\t\torbSpawn.Parent = game:GetService(\"ReplicatedStorage\")\n\n\t\torbSpawnParts[1]:Destroy()\n\telse\n\t\twarn(\"Orb manager requires exactly 1 orb spawn location\")\n\tend\nend\n\nlocal orb = assetsFolder.entities.orb\nlocal checkForOrbSpawn = true\n\nlocal function onPlayerAdded(player)\n\tif orb.Parent == workspace then\n\t\tnetwork:fireClient(\"effects_requestEffect\", player, \"orbAnnoucement\")\n\tend\nend\ngame.Players.PlayerAdded:Connect(onPlayerAdded)\n\nlocal function spawnOrb()\n\torb:SetPrimaryPartCFrame(CFrame.new(orbSpawn.Value))\n\torb.Parent = workspace\n\torb.PrimaryPart.loop:Play()\n\tnetwork:fireAllClients(\"effects_requestEffect\", \"orbArrival\", {orb = orb})\nend\n\nlocal function despawnOrb()\n\tnetwork:fireAllClients(\"effects_requestEffect\", \"orbDeparture\", {orb = orb})\n\tdelay(5, function()\n\t\torb.Parent = script\n\tend)\nend\n\nlocal function getCurrentDay()\n\tlocal vesterianDay = game:GetService(\"ReplicatedStorage\"):FindFirstChild(\"vesterianDay\")\n\tif not vesterianDay then\n\t\treturn 0\n\tend\n\n\treturn math.floor(vesterianDay.Value + 0.5)\nend\n\nlocal function update()\n\tlocal clockTime = game.Lighting.ClockTime\n\tlocal isNight = (clockTime < 5.9) or (clockTime > 18.6) -- keep synchronized with day night cycle\n\n\tif isNight then\n\t\tif checkForOrbSpawn then\n\t\t\tcheckForOrbSpawn = false\n\n\t\t\tlocal day = getCurrentDay()\n\t\t\tlocal rand = Random.new(day)\n\n\t\t\tlocal placeId = placeIdList[rand:NextInteger(1, #placeIdList)]\n\t\t\tif game.PlaceId == placeId then\n\t\t\t\tspawnOrb()\n\t\t\tend\n\n\t\t\tprint(\"manager_orb chose \"..placeId..\" as the spawn location. This place is \"..game.PlaceId..\".\")\n\t\tend\n\telse\n\t\tif not checkForOrbSpawn then\n\t\t\tcheckForOrbSpawn = true\n\n\t\t\tif orb.Parent ~= script then\n\t\t\t\tdespawnOrb()\n\t\t\tend\n\t\tend\n\tend\nend\n\n\n-- defunct\nlocal function playerRequest_enchantAbility(player, orb, requestData)\n\tlocal playerData = playerDataContainer[player]\n\tif playerData then\n\t\tif typeof(orb) == \"Instance\" and orb:FindFirstChild(\"itemEnchanted\") and orb:IsDescendantOf(workspace) then\n\t\t\tif player.Character and player.Character.PrimaryPart and (player.Character.PrimaryPart.Position - orb.Position).magnitude <= 100 then\n\t\t\t\tlocal abilitySlotData\n\t\t\t\tfor i, abilityData in pairs(playerData.abilities) do\n\t\t\t\t\tif abilityData.id == requestData.id then\n\t\t\t\t\t\tabilitySlotData = abilityData\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tlocal abilityBaseData = abilityLookup[abilitySlotData.id](playerData)\n\t\t\t\tif abilitySlotData and abilityBaseData then\n\t\t\t\t\tlocal metadata = abilityBaseData.metadata\n\t\t\t\t\tif metadata then\n\t\t\t\t\t\tlocal AP = playerData.level - 1\n\t\t\t\t\t\tlocal availablePoints = AP - getPlayerDataSpentAP(playerData)\n\t\t\t\t\t\tif requestData.request == \"upgrade\" then\n\t\t\t\t\t\t\tif metadata.upgradeCost and metadata.maxRank and availablePoints >= metadata.upgradeCost then\n\t\t\t\t\t\t\t\tif abilitySlotData.rank > 0 and abilitySlotData.rank < metadata.maxRank then\n\t\t\t\t\t\t\t\t\tabilitySlotData.rank = abilitySlotData.rank + 1\n\t\t\t\t\t\t\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"abilities\")\n\t\t\t\t\t\t\t\t\torb.itemEnchanted:Play()\n\t\t\t\t\t\t\t\t\tif orb:FindFirstChild(\"steady\") then\n\t\t\t\t\t\t\t\t\t\torb.steady:Emit(50)\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\treturn true, \"upgrade applied\"\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telseif requestData.request == \"variant\" then\n\t\t\t\t\t\t\tif abilitySlotData.variant == nil or metadata.variants[abilitySlotData.variant].default then\n\t\t\t\t\t\t\t\tlocal variantData = metadata.variants[requestData.variant]\n\t\t\t\t\t\t\t\tif variantData and variantData.cost and availablePoints >= variantData.cost then\n\t\t\t\t\t\t\t\t\tif variantData.requirement and variantData.requirement(playerData) then\n\t\t\t\t\t\t\t\t\t\tabilitySlotData.variant = requestData.variant\n\t\t\t\t\t\t\t\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"abilities\")\n\t\t\t\t\t\t\t\t\t\torb.itemEnchanted:Play()\n\t\t\t\t\t\t\t\t\t\tif orb:FindFirstChild(\"steady\") then\n\t\t\t\t\t\t\t\t\t\t\torb.steady:Emit(50)\n\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\treturn true, \"variant applied\"\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\treturn false, \"requirements not fufilled\"\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\treturn false, \"not enough points\"\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\treturn false, \"ability already has variant\"\n\t\t\t\t\t\tend\n\t\t\t\t\t\treturn false, \"invalid request\"\n\t\t\t\t\tend\n\t\t\t\t\treturn false, \"unsupported ability\"\n\t\t\t\tend\n\t\t\t\treturn false, \"no data for ability\"\n\t\t\tend\n\t\t\treturn false, \"too far from orb\"\n\t\tend\n\t\treturn false, \"invalid orb\"\n\tend\nend\n\nlocal function startUpdating()\n\twhile wait(1) do\n\t\tupdate()\n\tend\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tnetwork:create(\"playerRequest_enchantAbility\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_enchantAbility)\n\tspawn(startUpdating)\nend\n\n\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_party.lua",
    "content": "-- author: Polymorphic\n\nlocal module = {}\n\nlocal HttpService = game:GetService(\"HttpService\")\n\nlocal network\nlocal utilities\n\nlocal MAX_PARTY_MEMBERS = 6\n\n--[[\n\n\tpartyMemberData {}\n\t\tbool isLeader\n\t\tbool isPowerUser\n\n\t\tplayer player\n\n\tpartyData {}\n\t\t{partyMemberData} members\n\n\tpartyDataContainer {partyData}\n--]]\n\nlocal partyDataContainer = {}\nlocal pendingPartyInvites = {}\nlocal invitationCD = {}\n\nlocal function getPartyDataByPlayer(player)\n\tfor _, partyData in pairs(partyDataContainer) do\n\t\tfor _, partyMemberData in pairs(partyData.members) do\n\t\t\tif partyMemberData.player == player then\n\t\t\t\treturn partyData, partyMemberData\n\t\t\tend\n\t\tend\n\tend\n\n\treturn nil, nil\nend\n\nlocal function playerRequest_getMyPartyData(player)\n\treturn getPartyDataByPlayer(player)\nend\n\nlocal function getPartyMembersInMyParty(player)\n\tlocal partyData = getPartyDataByPlayer(player)\n\n\tif partyData then\n\t\treturn partyData.members\n\tend\n\n\treturn nil\nend\n\nlocal function propogatePartyDataChangedToPartyMembers(partyData)\n\n\t-- let players know if a new party is created\n\tif partyData.isNew then\n\t\tif partyData.removeIsNewNextPropogate then\n\t\t\tpartyData.isNew = false\n\t\t\tpartyData.removeIsNewNextPropogate = false\n\t\telse\n\t\t\tpartyData.removeIsNewNextPropogate = true\n\t\tend\n\tend\n\n\tfor i, partyPlayerData in pairs(partyData.members) do\n\t\tnetwork:fireClient(\"signal_myPartyDataChanged\", partyPlayerData.player, partyData)\n\tend\nend\n\n-- makes `playerInvited` join the party of `playerInviting`\n\nlocal function invitePlayerToMyParty(playerInviting, playerInvited)\n\tlocal partyData_playerInviting, partyMemberData_playerInviting \t= getPartyDataByPlayer(playerInviting)\n\tlocal partyData_playerInvited, \t________________________ \t\t= getPartyDataByPlayer(playerInvited)\n\n\tif playerInviting ~= playerInvited and not partyData_playerInvited then\n\t\tif not invitationCD[playerInviting] or (tick() - invitationCD[playerInviting] > 3) then\n\t\t\tinvitationCD[playerInviting] = tick()\n\n\t\t\tlocal guid \t\t\t\t\t= HttpService:GenerateGUID(false)\n\t\t\tpendingPartyInvites[guid] \t= {\n\t\t\t\tpartyData \t\t= partyData_playerInviting;\n\t\t\t\tplayerInviting \t= playerInviting;\n\t\t\t\tplayer \t\t\t= playerInvited;\n\t\t\t}\n\n\t\t\tnetwork:fireClient(\"signal_playerInvitedToParty\", playerInvited, playerInviting, guid)\n\n\t\t\treturn true, \"Invitation sent.\"\n\t\telse\n\t\t\treturn false, \"Sending invites too fast!\"\n\t\tend\n\tend\n\n\treturn false, \"Invalid player.\"\nend\n\nlocal function isPartyDataValid(partyData)\n\tfor _, _partyData in pairs(partyDataContainer) do\n\t\tif partyData == _partyData then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal function playerRequest_acceptMyPartyInvitation(player, guid)\n\tlocal partyData = getPartyDataByPlayer(player)\n\n\tif not partyData then\n\t\tlocal partyInvitationData = pendingPartyInvites[guid]\n\n\t\tif partyInvitationData and partyInvitationData.player == player then\n\t\t\tif isPartyDataValid(partyInvitationData.partyData) or partyInvitationData.partyData == nil then\n\n\t\t\t\t-- do this instead of reading the party data of the invitation because they mightve invited multiple\n\t\t\t\t-- people and then had the party made.\n\t\t\t\tlocal invitingPlayerPartyData = getPartyDataByPlayer(partyInvitationData.playerInviting)\n\n\t\t\t\tif partyInvitationData.playerInviting and partyInvitationData.playerInviting.Parent then\n\t\t\t\t\tif invitingPlayerPartyData then\n\t\t\t\t\t\tpendingPartyInvites[guid] = nil\n\n\t\t\t\t\t\tif #invitingPlayerPartyData.members < MAX_PARTY_MEMBERS then\n\t\t\t\t\t\t\t-- invalidate the guid\n\n\t\t\t\t\t\t\tlocal partyMemberData = {}\n\t\t\t\t\t\t\t\tpartyMemberData.isLeader \t= false\n\t\t\t\t\t\t\t\tpartyMemberData.isPowerUser = false\n\t\t\t\t\t\t\t\tpartyMemberData.player \t\t= player\n\n\t\t\t\t\t\t\ttable.insert(invitingPlayerPartyData.members, partyMemberData)\n\n\t\t\t\t\t\t\tinvitingPlayerPartyData.newestMember = partyMemberData\n\n\t\t\t\t\t\t\tlocal invitingPlayer = partyInvitationData.playerInviting\n\n\n\t\t\t\t\t\t\tinvitingPlayerPartyData.status = {text = player.Name .. \" joined the party. (Invited by \" .. invitingPlayer.Name .. \")\" }\n\n\n\t\t\t\t\t\t\t-- update players\n\t\t\t\t\t\t\tpropogatePartyDataChangedToPartyMembers(invitingPlayerPartyData)\n\n\t\t\t\t\t\t\treturn true, \"Joined party\"\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\treturn false, \"Party is full\"\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\tlocal partyData = {}\n\t\t\t\t\t\t\tpartyData.guid \t\t= HttpService:GenerateGUID(false)\n\t\t\t\t\t\t\tpartyData.members \t= {}\n\n\t\t\t\t\t\tlocal partyMemberData_player = {}\n\t\t\t\t\t\t\tpartyMemberData_player.isLeader \t= false\n\t\t\t\t\t\t\tpartyMemberData_player.isPowerUser \t= false\n\t\t\t\t\t\t\tpartyMemberData_player.player \t\t= player\n\n\t\t\t\t\t\ttable.insert(partyData.members, partyMemberData_player)\n\n\t\t\t\t\t\tpartyData.newestMember = partyMemberData_player\n\n\t\t\t\t\t\tlocal partyMemberData_playerInviting = {}\n\t\t\t\t\t\t\tpartyMemberData_playerInviting.isLeader \t= true\n\t\t\t\t\t\t\tpartyMemberData_playerInviting.isPowerUser \t= true\n\t\t\t\t\t\t\tpartyMemberData_playerInviting.player \t\t= partyInvitationData.playerInviting\n\n\t\t\t\t\t\ttable.insert(partyData.members, partyMemberData_playerInviting)\n\t\t\t\t\t\ttable.insert(partyDataContainer, partyData)\n\n\n\t\t\t\t\t\tpartyData.isNew = true\n\t\t\t\t\t\tpartyData.status = {text = player.Name .. \" joined the party. (Invited by \" .. partyInvitationData.playerInviting.Name .. \")\" }\n\t\t\t\t\t\t-- update players\n\t\t\t\t\t\tpropogatePartyDataChangedToPartyMembers(partyData)\n\n\t\t\t\t\t\treturn true, \"Joined party\"\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\treturn false, \"Invalid leader.\"\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\treturn nil, \"eof\"\nend\n\nlocal function int__checkIfCleanUpPartyData(givenPartyData)\n\tfor i, partyData in pairs(partyDataContainer) do\n\t\tif partyData == givenPartyData then\n\t\t\tif #partyData.members <= 1 then\n\t\t\t\tif #partyData.members == 1 then\n\t\t\t\t\tnetwork:fireClient(\"signal_myPartyDataChanged\", partyData.members[1].player, nil)\n\n\t\t\t\t\tpartyData.members[1].player = nil\n\t\t\t\t\tpartyData.members[1] \t\t= nil\n\t\t\t\t\tpartyData.members \t\t\t= {}\n\t\t\t\tend\n\n\t\t\t\ttable.remove(partyDataContainer, i)\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function playerRequest_leaveParty(player, playerToKickOut)\n\tlocal partyData_player, partyMemberData_player = getPartyDataByPlayer(player)\n\n\n\tif partyData_player then\n\t\tif playerToKickOut and partyMemberData_player and partyMemberData_player.isPowerUser then\n\t\t\tlocal partyData_playerToKickOut, partyMemberData_playerToKickOut = getPartyDataByPlayer(playerToKickOut)\n\n\t\t\tif partyData_playerToKickOut == partyData_player and not partyMemberData_playerToKickOut.isLeader then\n\t\t\t\tfor i, partyMemberData in pairs(partyData_player.members) do\n\t\t\t\t\tif partyMemberData.player == playerToKickOut then\n\t\t\t\t\t\ttable.remove(partyData_player.members, i)\n\n\t\t\t\t\t\t-- reset newest member\n\t\t\t\t\t\tpartyData_player.newestMember = nil\n\n\t\t\t\t\t\tif player == playerToKickOut then\n\t\t\t\t\t\t\tpartyData_player.status = {text = player.Name .. \" left the party.\" }\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tpartyData_player.status = {text = playerToKickOut.Name .. \" was kicked from the party.\" }\n\t\t\t\t\t\tend\n\n\n\t\t\t\t\t\t-- update players\n\t\t\t\t\t\tpropogatePartyDataChangedToPartyMembers(partyData_player)\n\t\t\t\t\t\tnetwork:fireClient(\"signal_myPartyDataChanged\", playerToKickOut, nil)\n\n\t\t\t\t\t\tint__checkIfCleanUpPartyData(partyData_player)\n\n\t\t\t\t\t\treturn true\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\telseif not playerToKickOut then\n\t\t\tfor i, partyMemberData in pairs(partyData_player.members) do\n\t\t\t\tif partyMemberData.player == player then\n\t\t\t\t\ttable.remove(partyData_player.members, i)\n\n\t\t\t\t\t-- reset newest member\n\t\t\t\t\tpartyData_player.newestMember = nil\n\n\t\t\t\t\tpartyData_player.status = {text = player.Name .. \" left the party.\" }\n\n\t\t\t\t\t-- update players\n\t\t\t\t\tpropogatePartyDataChangedToPartyMembers(partyData_player)\n\t\t\t\t\tnetwork:fireClient(\"signal_myPartyDataChanged\", player, nil)\n\n\t\t\t\t\tint__checkIfCleanUpPartyData(partyData_player)\n\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\tend\n\tend\n\n\n\treturn false\nend\n\nlocal function playerRequest_startGroupTeleport(player, teleportPart)\n\tlocal partyData_player, partyMemberData_player = getPartyDataByPlayer(player)\n\n\tif partyData_player and partyMemberData_player.isLeader and teleportPart and teleportPart:FindFirstChild(\"teleportDestination\") then\n\t\tif partyData_player.teleportState == \"none\" or partyData_player.teleportState == nil then\n\n\t\t\tpartyData_player.teleportState \t\t\t= \"pending\"\n\t\t\tpartyData_player.teleportDestination \t= teleportPart.teleportDestination.Value\n\t\t\tpartyData_player.status \t\t\t\t= nil\n\t\t\tpartyData_player.teleportPosition\t\t= teleportPart.Position\n\t\t\tpartyData_player.teleportPart\t\t\t= teleportPart\n\n\t\t\tpropogatePartyDataChangedToPartyMembers(partyData_player)\n\n\t\t\tspawn(function()\n\t\t\t\twhile partyData_player.teleportState == \"pending\" and player.Parent do\n\t\t\t\t\tlocal canTeleport = true\n\t\t\t\t\tfor i, partyMemberData in pairs(partyData_player.members) do\n\t\t\t\t\t\tif not partyMemberData.player.Character or not partyMemberData.player.Character.PrimaryPart or utilities.magnitude(partyMemberData.player.Character.PrimaryPart.Position - teleportPart.Position) > 20 then\n\t\t\t\t\t\t\tcanTeleport = false\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif canTeleport then\n\t\t\t\t\t\tpartyData_player.teleportState = \"teleporting\"\n\t\t\t\t\t\tbreak\n\t\t\t\t\telse\n\t\t\t\t\t\twait(1 / 2)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif partyData_player.teleportState == \"teleporting\" then\n\n\t\t\t\t\tpropogatePartyDataChangedToPartyMembers(partyData_player)\n\n\t\t\t\t\tlocal startTime \t\t\t\t= tick()\n\t\t\t\t\tlocal isWaitingForTeleportReady = true\n\n\t\t\t\t\twhile tick() - startTime < 15 and isWaitingForTeleportReady do\n\t\t\t\t\t\tisWaitingForTeleportReady = false\n\t\t\t\t\t\tfor i, partyMemberData in pairs(partyData_player.members) do\n\t\t\t\t\t\t\tif not partyMemberData.isReadyToTeleport then\n\t\t\t\t\t\t\t\tisWaitingForTeleportReady = true\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif isWaitingForTeleportReady then\n\t\t\t\t\t\t\twait(1 / 4)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal playersToTeleport = {}\n\t\t\t\t\tlocal partyLeaderUserId = 0\n\t\t\t\t\tfor i, partyMemberData in pairs(partyData_player.members) do\n\t\t\t\t\t\tif partyMemberData.isReadyToTeleport then\n\t\t\t\t\t\t\ttable.insert(playersToTeleport, partyMemberData.player)\n\n\t\t\t\t\t\t\tif partyMemberData.isLeader then\n\t\t\t\t\t\t\t\tpartyLeaderUserId = partyMemberData.player.userId\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\t-- drop the teleport if anyone in the party is experiencing a DataStore outage\n\t\t\t\t\tlocal teleportFail = false\n\t\t\t\t\tlocal failedPlayers = {}\n\t\t\t\t\tfor i,player in pairs(playersToTeleport) do\n\t\t\t\t\t\tif player:FindFirstChild(\"DataSaveFailed\") then\n\t\t\t\t\t\t\tteleportFail = true\n\t\t\t\t\t\t\ttable.insert(failedPlayers, player)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif teleportFail then\n\t\t\t\t\t\tlocal errorMsg = \"Teleport failed: \"\n\t\t\t\t\t\tfor i,player in pairs(failedPlayers) do\n\t\t\t\t\t\t\terrorMsg = errorMsg .. player.Name .. (failedPlayers[i+1] and \", \" or \"\")\n\t\t\t\t\t\tend\n\t\t\t\t\t\terrorMsg = errorMsg .. \" \" .. (#failedPlayers == 1 and \"is\" or \"are\") ..  \" experiencing a DataStore outage.\"\n\n\t\t\t\t\t\tpartyData_player.status = {text = errorMsg; textColor3 = Color3.fromRGB(255, 57, 60)}\n\t\t\t\t\t\tpartyData_player.teleportState = \"none\"\n\t\t\t\t\t\tpropogatePartyDataChangedToPartyMembers(partyData_player)\n\t\t\t\t\t\treturn false, \"Datastore outage.\"\n\t\t\t\t\tend\n\n\t\t\t\t\tif #playersToTeleport > 0 and partyLeaderUserId then\n\t\t\t\t\t\tlocal success, message = network:invoke(\"teleportParty\", playersToTeleport, teleportPart.teleportDestination.Value, partyLeaderUserId)\n\t\t\t\t\t\treturn success, message\n\t\t\t\t\telse\n\t\t\t\t\t\twarn(\"manager_party::no leader or no one ready\")\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend)\n\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal function getPartyDataByGUID(guid)\n\tfor _, partyData in pairs(partyDataContainer) do\n\t\tif partyData.guid == guid then\n\t\t\treturn partyData\n\t\tend\n\tend\n\n\treturn nil\nend\n\nlocal function getPartyLeaderForPartyData(partyData)\n\tfor _, partyMemberData in pairs(partyData.members) do\n\t\tif partyMemberData.isLeader then\n\t\t\treturn partyMemberData\n\t\tend\n\tend\n\n\treturn nil\nend\n\n-- todo: only accept from JoinData.members\nlocal function playerRequest_acceptMyPartyInvitationByTeleportation(player, guid, partyLeaderUserId)\n\tif not guid or not partyLeaderUserId then return end\n\n\tlocal partyData_guid = getPartyDataByGUID(guid)\n\n\tif not partyData_guid then\n\t\tlocal joinData = player:GetJoinData()\n\t\tlocal partyData = {}\n\t\t\tpartyData.guid \t\t\t\t= guid\n\t\t\tpartyData.isTeleportParty \t= true\n\t\t\tpartyData.partyLeaderUserId = partyLeaderUserId\n\t\t\tpartyData.members \t\t\t= {}\n\n\t\tlocal partyMemberData = {}\n\t\t\tpartyMemberData.isLeader \t= player.userId == partyLeaderUserId\n\t\t\tpartyMemberData.isPowerUser = true\n\t\t\tpartyMemberData.player \t\t= player\n\n\t\ttable.insert(partyData.members, partyMemberData)\n\t\ttable.insert(partyDataContainer, partyData)\n\n\t\tpropogatePartyDataChangedToPartyMembers(partyData)\n\telseif partyData_guid and partyData_guid.isTeleportParty then\n\t\tlocal partyMemberData = {}\n\t\t\tpartyMemberData.isLeader \t= partyData_guid.partyLeaderUserId == player.userId\n\t\t\tpartyMemberData.isPowerUser = true\n\t\t\tpartyMemberData.player \t\t= player\n\n\t\ttable.insert(partyData_guid.members, partyMemberData)\n\n\t\tpropogatePartyDataChangedToPartyMembers(partyData_guid)\n\tend\nend\n\nlocal function playerRequest_cancelGroupTeleport(player)\n\tlocal partyData_player, partyMemberData_player = getPartyDataByPlayer(player)\n\n\tif partyData_player and partyData_player.teleportState == \"pending\" then\n\t\tpartyData_player.teleportState = \"none\"\n\t\tpropogatePartyDataChangedToPartyMembers(partyData_player)\n\tend\nend\n\nlocal function signal_playerReadyToGroupTeleport(player)\n\tlocal partyData_player, partyMemberData_player = getPartyDataByPlayer(player)\n\n\tif partyData_player and partyMemberData_player then\n\t\tpartyMemberData_player.isReadyToTeleport = true\n\tend\nend\n\nlocal function onPlayerAdded(player)\n\tlocal joinData = player:GetJoinData()\nend\n\nlocal function onPlayerRemoving(player)\n\tinvitationCD[player] = nil\n\n\tif player:FindFirstChild(\"teleporting\") == nil then\n\t\tlocal partyData_player, partyMemberData_player = getPartyDataByPlayer(player)\n\n\t\tif partyData_player then\n\t\t\tfor i, partyMemberData in pairs(partyData_player.members) do\n\t\t\t\tif partyMemberData.player == player then\n\t\t\t\t\ttable.remove(partyData_player.members, i)\n\n\t\t\t\t\t-- reassign leader if leaver was leader\n\t\t\t\t\tif partyMemberData.isLeader then\n\t\t\t\t\t\tif partyData_player.members[1] then\n\t\t\t\t\t\t\tpartyData_player.members[1].isLeader = true\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\t-- reset newest member\n\t\t\t\t\tpartyData_player.newestMember = nil\n\n\t\t\t\t\tpartyData_player.status = {text = player.Name .. \" left the party.\" }\n\t\t\t\t\t-- update players\n\t\t\t\t\tpropogatePartyDataChangedToPartyMembers(partyData_player)\n\n\t\t\t\t\tint__checkIfCleanUpPartyData(partyData_player)\n\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tutilities = Modules.utilities\n\n\tgame.Players.PlayerAdded:connect(onPlayerAdded)\n\tfor _, player in pairs(game.Players:GetPlayers()) do\n\t\tonPlayerAdded(player)\n\tend\n\tgame.Players.PlayerRemoving:connect(onPlayerRemoving)\n\n\tnetwork:create(\"resumePartyAfterTeleport\", \"BindableFunction\", \"OnInvoke\", playerRequest_acceptMyPartyInvitationByTeleportation)\n\tnetwork:create(\"playerRequest_getPartyMembersInMyParty\", \"RemoteFunction\", \"OnServerInvoke\", getPartyMembersInMyParty)\n\tnetwork:create(\"playerRequest_getMyPartyData\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_getMyPartyData)\n\tnetwork:create(\"playerRequest_invitePlayerToMyParty\", \"RemoteFunction\", \"OnServerInvoke\", invitePlayerToMyParty)\n\n\tnetwork:create(\"playerRequest_acceptMyPartyInvitation\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_acceptMyPartyInvitation)\n\tnetwork:create(\"playerRequest_leaveParty\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_leaveParty)\n\n\tnetwork:create(\"playerRequest_startGroupTeleport\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_startGroupTeleport)\n\tnetwork:create(\"playerRequest_cancelGroupTeleport\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_cancelGroupTeleport)\n\tnetwork:create(\"signal_playerReadyToGroupTeleport\", \"RemoteEvent\", \"OnServerEvent\", signal_playerReadyToGroupTeleport)\n\n\tnetwork:create(\"getPartyDataByPlayer\", \"BindableFunction\", \"OnInvoke\", getPartyDataByPlayer)\n\n\tnetwork:create(\"signal_playerInvitedToParty\", \"RemoteEvent\")\n\tnetwork:create(\"signal_myPartyDataChanged\", \"RemoteEvent\")\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_pet.lua",
    "content": "-- pets service, duh!\n\t-- acts as the interfacing between manager_monster and manager_player\n\t-- to keep track of pets and spawning/despawning them.\n\nlocal module = {}\n\nlocal network\nlocal mapping\n\nlocal playerPetDataCollection = {}\n--[[\n\tplayerPetData {}\n\t\tint id\n\t\tinstance manifest\n]]--\n\nlocal function despawnPlayerPet(player)\n\tif playerPetDataCollection[player] then\n\t\tplayerPetDataCollection[player].manifest:Destroy()\n\t\tplayerPetDataCollection[player] = nil\n\tend\nend\n\nlocal function spawnPlayerPet(player, petEquipmentData)\n\tif not playerPetDataCollection[player] then\n\t\tlocal petManifest = network:invoke(\"spawnMonsterPet\", player, petEquipmentData.id, petEquipmentData)\n\n\t\tlocal playerPetData = {}\n\t\t\tplayerPetData.id \t\t= petEquipmentData.id\n\t\t\tplayerPetData.manifest \t= petManifest\n\n\t\tplayerPetDataCollection[player] = playerPetData\n\tend\nend\n\nlocal function getPlayerPetEquipment(player, equipment)\n\tfor i, equipmentData in pairs(equipment) do\n\t\tif equipmentData.position == mapping.equipmentPosition.pet then\n\t\t\treturn equipmentData\n\t\tend\n\tend\n\n\treturn nil\nend\n\nlocal function int__processPlayerPetEquipmentData(player, petEquipmentData)\n\tif petEquipmentData and not playerPetDataCollection[player] then\n\t\tspawnPlayerPet(player, petEquipmentData)\n\telseif not petEquipmentData and playerPetDataCollection[player] then\n\t\tdespawnPlayerPet(player, petEquipmentData)\n\tend\nend\n\nlocal function onPlayerEquipmentChanged_server(player, equipment)\n\tlocal playerPetEquipmentData = getPlayerPetEquipment(player, equipment)\n\n\tint__processPlayerPetEquipmentData(player, playerPetEquipmentData)\nend\n\nlocal function onPlayerAdded(player)\n\t-- get the player's data, waiting until they either leave or\n\t-- we successfully fetch their player data\n\tlocal playerData do\n\t\twhile not playerData and player.Parent == game.Players do\n\t\t\tplayerData = network:invoke(\"getPlayerData\", player)\n\n\t\t\twait(0.1)\n\t\tend\n\tend\n\n\tif playerData then\n\t\tonPlayerEquipmentChanged_server(player, playerData.equipment)\n\tend\nend\n\nlocal function onPlayerRemoving(player)\n\tif playerPetDataCollection[player] then\n\t\t-- despawn pet here!\n\n\t\tdespawnPlayerPet(player)\n\tend\n\n\tplayerPetDataCollection[player] = nil\nend\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\tmapping = Modules.mapping\n\n\t-- todo: this is really icky, should just hook into player data loaded\n\tspawn(function()\n\t\tfor _, player in pairs(game.Players:GetPlayers()) do\n\t\t\tonPlayerAdded(player)\n\t\tend\n\tend)\n\n\tgame.Players.PlayerAdded:connect(onPlayerAdded)\n\tgame.Players.PlayerRemoving:connect(onPlayerRemoving)\n\n\tnetwork:connect(\"playerEquipmentChanged_server\", \"Event\", onPlayerEquipmentChanged_server)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_player.lua",
    "content": "\nlocal module = {}\n\nmodule.priority = 3\n\nlocal playerDataContainer = {}\nlocal playerPositionDataContainer = {}\n\nlocal shuttingDown = false\nlocal runService = game:GetService(\"RunService\")\n\nlocal CollectionService = game:GetService(\"CollectionService\")\nlocal HttpService = game:GetService(\"HttpService\")\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal datastoreInterface\nlocal network\nlocal utilities\nlocal physics\nlocal levels\nlocal mapping\nlocal configuration\nlocal placeSetup\nlocal events\nlocal detection\nlocal projectile\n\nlocal entityManifestCollectionFolder\nlocal temporaryEquipmentFolder\n\nlocal itemLookup = require(ReplicatedStorage.itemData)\nlocal itemAttributes = require(ReplicatedStorage.itemAttributes)\nlocal perkLookup = require(ReplicatedStorage.perkLookup)\n\nlocal assetsFolder = ReplicatedStorage.assets\n\n-- has to load here due to requirements on stuff above\nlocal PLAYER_LEVEL_CAP = 49\n\nlocal function getPlayerData(player)\n\treturn playerDataContainer[player]\nend\n\nlocal function getPlayerData_remote(callingPlayer, ...)\n\treturn getPlayerData(...)\nend\n\n-- GLOBAL DATA HOOKUP\nlocal function getPlayerGlobalData(player)\n\tlocal success, data, status = datastoreInterface:getPlayerGlobalSaveFileData(player)\n\n\tif not success then\n\t\tlocal reportingSuccess, reportingError = pcall(function()\n\t\t\tnetwork:invoke(\"reportError\", player, \"error\", \"getPlayerGlobalData failed: \"..status)\n\t\t\tnetwork:invoke(\"reportAnalyticsEvent\",player,\"data:fail:save\")\n\t\tend)\n\n\t\tif not reportingSuccess then\n\t\t\twarn(\"Failed to report data error: \"..reportingError)\n\t\tend\n\tend\n\n\treturn success, data, status\nend\n\nlocal function setPlayerGlobalData(player, GlobalData)\n\tlocal playerId = player.userId\n\n\tlocal success, status, version = datastoreInterface:updatePlayerGlobalSaveFileData(playerId, GlobalData)\n\n\tif not success then\n\t\tlocal reportingSuccess, reportingError = pcall(function()\n\t\t\tnetwork:invoke(\"reportError\", player, \"error\", \"setPlayerGlobalData failed: \"..status)\n\t\t\tnetwork:invoke(\"reportAnalyticsEvent\",player,\"data:fail:save\")\n\t\tend)\n\n\t\tif not reportingSuccess then\n\t\t\twarn(\"Failed to report data error: \"..reportingError)\n\t\tend\n\tend\n\n\treturn success, status, version\nend\n\nlocal function isPlayerOfClass(player, class)\n\tclass = class:lower()\n\n\tif class == \"adventurer\" then\n\t\treturn true\n\tend\n\n\tlocal playerData = playerDataContainer[player]\n\tif not playerData then\n\t\treturn false\n\tend\n\n\tif playerData.class:lower() == class then\n\t\treturn true\n\tend\n\n\t-- conflating the name of an ability book with a class seems spookarook\n\t-- but here we go anyways, Davidii taking responsibility for this\n\treturn playerData.abilityBooks[class] ~= nil\nend\n\nlocal function getPlayerDefaultHomePlaceId(player)\n\tif isPlayerOfClass(player, \"warrior\") then\n\t\treturn 2470481225 -- warrior stronghold\n\n\telseif isPlayerOfClass(player, \"mage\") then\n\t\treturn 3112029149 -- tree of life\n\n\telseif isPlayerOfClass(player, \"hunter\") then\n\t\treturn 2546689567\n\n\telse\n\n\t\treturn 2064647391\n\tend\nend\n\nlocal function onDeathGuiAccepted(player)\n\tlocal playerData = playerDataContainer[player]\n\tif not playerData then return end\n\n\tlocal tagName = \"acceptedDeathConsequences\"\n\tlocal tag = player:FindFirstChild(tagName)\n\tif tag then return end\n\n\tlocal source = \"game:death\"\n\n\tplayerData.nonSerializeData.incrementPlayerData(\"gold\", -(playerData.gold*0.1), source)\n\tlocal expForNextLevel = levels.getEXPToNextLevel(playerData.level)\n\tlocal newExp = math.clamp(playerData.exp - expForNextLevel * 0.2, 0, expForNextLevel)\n\tplayerData.nonSerializeData.setPlayerData(\"exp\", newExp)\n\tlocal nearestCity = game.ReplicatedStorage:FindFirstChild(\"nearestCityId\")\n\tnearestCity = nearestCity and nearestCity.Value\n\tlocal returnDestination = nearestCity or playerData.homePlaceId or getPlayerDefaultHomePlaceId(player)\n\treturnDestination = utilities.placeIdForGame(returnDestination)\n\tplayerData.lastLocationDeathOverride = returnDestination\n\n\ttag = Instance.new(\"BoolValue\")\n\ttag.Name = tagName\n\ttag.Value = true\n\ttag.Parent = player\n\n\t-- set health and mana to 1\n\tlocal character = player.Character\n\tif character then\n\t\tlocal manifest = character.PrimaryPart\n\t\tif manifest then\n\t\t\tlocal health = manifest:FindFirstChild(\"health\")\n\t\t\tlocal mana = manifest:FindFirstChild(\"mana\")\n\t\t\tif health and mana then\n\t\t\t\thealth.Value = 1\n\t\t\t\tmana.Value = 1\n\t\t\tend\n\t\tend\n\tend\n\n\tif game.PlaceId == 2103419922 then\n\t--\tgame:GetService(\"TeleportService\"):Teleport(2103419922, player)\n\t\tplayer:Kick(\"You are dead.\")\n\t\treturn\n\tend\n\n\tnetwork:invoke(\"teleportPlayer\", player, playerData.lastLocationDeathOverride, \"default\", nil, \"death\")\nend\n\n\nlocal function onPlayerRemoving(player)\n\n\tlocal playerId = player.userId\n\tif not player:FindFirstChild(\"teleporting\") then\n\t\tif player:FindFirstChild(\"awaitingDeathGuiResponse\") or player:FindFirstChild(\"acceptedDeathConsequences\") or\n\t\t\t(player:FindFirstChild(\"isPlayerSpawning\") and player.isPlayerSpawning.Value) then\n\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\tText = player.Name .. \" rage quit.\";\n\t\t\t\tFont = Enum.Font.SourceSansBold;\n\t\t\t\tColor = Color3.fromRGB(45, 87, 255)\n\t\t\t})\n\t\telse\n\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\tText = player.Name .. \" disconnected.\";\n\t\t\t\tFont = Enum.Font.SourceSansBold;\n\t\t\t\tColor = Color3.fromRGB(45, 87, 255)\n\t\t\t})\n\t\tend\n\n\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\ttag.Name = \"disconnected\"\n\t\ttag.Parent = player\n\tend\n\n\tplayerPositionDataContainer[player] = nil\n\n\tif playerDataContainer[player] then\n\t\t-- onUnequipped this player's equipment to avoid memory leaks\n\t\tfor _, slotData in pairs(playerDataContainer[player].equipment) do\n\t\t\tlocal itemData = itemLookup[slotData.id]\n\t\t\tif itemData.perks then\n\t\t\t\tfor perkName, _ in pairs(itemData.perks) do\n\t\t\t\t\tlocal perkData = perkLookup[perkName]\n\t\t\t\t\tif perkData and perkData.onUnequipped then\n\t\t\t\t\t\tlocal success, err = pcall(function()\n\t\t\t\t\t\t\tperkData.onUnequipped(player, itemData, tostring(slotData.position))\n\t\t\t\t\t\tend)\n\t\t\t\t\t\tif not success then\n\t\t\t\t\t\t\twarn(string.format(\"item %s unequip failed because: %s\", itemData.name, err))\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tlocal deathTag = player:FindFirstChild(\"awaitingDeathGuiResponse\")\n\t\tif deathTag ~= nil then\n\t\t\tdeathTag:Destroy()\n\t\t\tonDeathGuiAccepted(player)\n\t\tend\n\n\t\tif player:FindFirstChild(\"entityGUID\") then\n\t\t\tlocal statusEffects = network:invoke(\"playerRemovingPackageStatusEffects\", player)\n\n\t\t\tif statusEffects then\n\t\t\t\tplayerDataContainer[player].packagedStatusEffects = statusEffects\n\t\t\tend\n\t\tend\n\n\t\tlocal char = player.Character\n\t\tif char then\n\t\t\tlocal manifest = char.PrimaryPart\n\t\t\tif manifest then\n\t\t\t\tlocal health = manifest:FindFirstChild(\"health\")\n\t\t\t\tlocal mana = manifest:FindFirstChild(\"mana\")\n\t\t\t\tif health and mana then\n\t\t\t\t\tplayerDataContainer[player][\"condition\"] = {\n\t\t\t\t\t\thealth = health.Value,\n\t\t\t\t\t\tmana = mana.Value\n\t\t\t\t\t}\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tlocal playerDataBackup = playerDataContainer[player]\n\t\tplayerDataContainer[player] = nil\n\n\t\t-- attempt to retry up to 5 times on failure\n\t\tfor _ = 1, 5 do\n\t\t\tlocal Success, Error, TimeStamp = datastoreInterface:updatePlayerSaveFileData(playerId, playerDataBackup)\n\t\t\tif not Success then\n\t\t\t\twarn(player.Name,\"'s data failed to save.\",Error)\n\n\t\t\t\tlocal reportingSuccess, reportingError = pcall(function()\n\t\t\t\t\tnetwork:invoke(\"reportError\", player, \"error\", \"Failed to save player data (on exit!): \"..Error)\n\t\t\t\t\tnetwork:invoke(\"reportAnalyticsEvent\",player,\"data:fail:save\")\n\t\t\t\tend)\n\t\t\t\tif not reportingSuccess then\n\t\t\t\t\twarn(\"Failed to report data error: \"..reportingError)\n\t\t\t\tend\n\n\t\t\t\tlocal messagingSuccess, messagingError = pcall(function()\n\t\t\t\t\tgame:GetService(\"MessagingService\"):PublishAsync(\"datawarning\", {userId = playerId})\n\t\t\t\tend)\n\t\t\t\tif not messagingSuccess then\n\t\t\t\t\twarn(\"Failed to send warning: \"..messagingError)\n\t\t\t\tend\n\n\t\t\telse\n\t\t\t\tif player:FindFirstChild(\"DataSaveFailed\") then\n\t\t\t\t\tplayer.DataSaveFailed:Destroy()\n\t\t\t\tend\n\t\t\t\treturn TimeStamp\n\t\t\tend\n\t\tend\n\tend\n\n\t-- can't believe i have to do this.\n\tif player.Character then\n\t\tplayer.Character.Parent = nil\n\t\tplayer.Character \t\t= nil\n\tend\nend\n\n-- TODO: tie into teleport manager\nlocal function saveDataForTeleport(player)\n\tif not player:FindFirstChild(\"teleporting\") then\n\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\ttag.Name = \"teleporting\"\n\t\ttag.Parent = player\n\n\t\tnetwork:invoke(\"reportAnalyticsEvent\",player,\"teleport:attempt\")\n\t\tlocal TimeStamp = onPlayerRemoving(player)\n\t\tif TimeStamp then\n\t\t\treturn TimeStamp\n\t\tend\n\tend\nend\n\n--[[\n\tPLAYER APPEARANCE CHANGE\n--]]\n\nlocal function playerRequest_changeAccessories(player, desiredAccessories, dialogueSource)\n\tlocal playerData = playerDataContainer[player]\n\tif playerData then\n\t\tif dialogueSource:IsA(\"ModuleScript\") then\n\t\t\tlocal dialogueData = require(dialogueSource)\n\t\t\tif dialogueData and dialogueData.characterCustomization then\n\t\t\t\tlocal details = dialogueData.characterCustomization\n\t\t\t\tlocal cost = details.cost or 50000\n\n\t\t\t\tif #desiredAccessories > 0 and playerData.gold >= cost then\n\t\t\t\t\tlocal playerAccessoryData = utilities.copyTable(playerData.accessories)\n\n\t\t\t\t\tfor _, desiredAccessory in pairs(desiredAccessories) do\n\t\t\t\t\t\tlocal category = game.ReplicatedStorage.accessoryLookup:FindFirstChild(desiredAccessory.accessory) or\n\t\t\t\t\t\t\tgame.ReplicatedStorage.accessoryLookup:FindFirstChild(string.gsub(desiredAccessory.accessory, \"Id\", \"\"))\n\t\t\t\t\t\tif category and category:FindFirstChild(tostring(desiredAccessory.value)) then\n\t\t\t\t\t\t\tplayerAccessoryData[desiredAccessory.accessory] = desiredAccessory.value\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\treturn false, \"Invalid accessory request\"\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tplayerData.nonSerializeData.setPlayerData(\"accessories\", playerAccessoryData)\n\t\t\t\t\tplayerData.nonSerializeData.incrementPlayerData(\"gold\", -50000)\n\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\t\treturn false, \"Not enough money or no accessory choice\"\n\t\t\tend\n\t\t\treturn false, \"Invalid source\"\n\t\tend\n\t\treturn false, \"No source provided\"\n\tend\n\treturn false, \"Player data not found\"\nend\n\n\n--[[\n\tINVENTORY\n--]]\n\nlocal CATEGORIES = {\"equipment\"; \"consumable\"; \"miscellaneous\"}\nlocal MAX_NUMBER_SLOTS_PER_CATEGORY = 20\nlocal MAX_COUNT_PER_STACK = 99\nlocal MAX_STORAGE_COUNT = 20\n\nlocal function onGetPlayerEquipment(client, player)\n\tif player and typeof(player) == \"Instance\" and player:IsA(\"Player\") then\n\t\t-- this is important, yield for it if the data isn't there.\n\t\tif not playerDataContainer[player] then\n\t\t\twhile not playerDataContainer[player] do\n\t\t\t\twait(0.1)\n\t\t\tend\n\t\tend\n\n\t\treturn playerDataContainer[player].equipment\n\tend\nend\n\n\nlocal function performDeathToRenderCharacter(player)\n\tlocal previousCharacter = player.Character\n\tlocal serverHitbox = previousCharacter.PrimaryPart\n\n\tif serverHitbox.state.Value ~= \"dead\" then\n\t\tplayer.isPlayerSpawning.Value = true\n\t\tplayer.playerSpawnTime.Value = os.time()\n\n\t\tserverHitbox.state.Value = \"dead\"\n\n\t\tlocal killer\n\t\tif serverHitbox:FindFirstChild(\"killingBlow\") and serverHitbox.killingBlow:FindFirstChild(\"source\") then\n\t\t\tkiller = serverHitbox.killingBlow.source.Value\n\t\tend\n\n\t\tnetwork:fire(\"playerCharacterDied\", player, previousCharacter, killer)\n\t\tevents:fireEventLocal(\"entityManifestDied\", serverHitbox)\n\n\t\tif serverHitbox.health.Value <= serverHitbox.maxHealth.Value * -3 then\n\t\t\tutilities.playSound(\"DEATH\", serverHitbox)\n\t\telse\n\t\t\tutilities.playSound(\"kill\", serverHitbox)\n\t\tend\n\n\t\tlocal respawnTime = 7\n\n\t\tif game.ReplicatedStorage:FindFirstChild(\"respawnTime\") then\n\t\t\trespawnTime = game.ReplicatedStorage.respawnTime.Value\n\t\tend\n\n\t\tlocal respawnType do\n\t\t\tif ReplicatedStorage:FindFirstChild(\"safeZone\") or (game.PlaceId == 2061558182) then\n\t\t\t\trespawnType = \"normal\"\n\t\t\telseif ReplicatedStorage:FindFirstChild(\"overrideDeathBehavior\") then\n\t\t\t\trespawnType = \"custom\"\n\t\t\telse\n\t\t\t\trespawnType = \"dangerous\"\n\t\t\tend\n\t\tend\n\n\t\tlocal tombstoneDuration = 300\n\n\t\tif not ReplicatedStorage:FindFirstChild(\"safeZone\") then\n\t\t\tdelay(3, function()\n\t\t\t\tlocal tombstoneTag = player:FindFirstChild(\"tombstone\")\n\t\t\t\tif tombstoneTag == nil then\n\t\t\t\t\ttombstoneTag = Instance.new(\"ObjectValue\")\n\t\t\t\t\ttombstoneTag.Name = \"tombstone\"\n\t\t\t\t\ttombstoneTag.Parent = player\n\t\t\t\tend\n\t\t\t\tif tombstoneTag.Value then\n\t\t\t\t\ttombstoneTag.Value:Destroy()\n\t\t\t\tend\n\n\t\t\t\tlocal tombstone = assetsFolder.entities.tombstone:Clone()\n\t\t\t\ttombstone.CanCollide = false\n\n\t\t\t\tlocal offset = Vector3.new(0, previousCharacter.PrimaryPart.Size.Y / 2, 0)\n\t\t\t\tlocal targetPosition = (previousCharacter.PrimaryPart.CFrame - offset).Position\n\n\t\t\t\tlocal rayDown = Ray.new(previousCharacter.PrimaryPart.Position, Vector3.new(0,-50,0))\n\t\t\t\tlocal hitPart, hitPosition = workspace:FindPartOnRayWithIgnoreList(rayDown, {\n\t\t\t\t\tworkspace.placeFolders, workspace.CurrentCamera\n\t\t\t\t}, false, true)\n\t\t\t\tif hitPart and hitPosition then\n\t\t\t\t\ttargetPosition = hitPosition + Vector3.new(0, tombstone.Size.Y / 2.1, 0)\n\t\t\t\tend\n\n\t\t\t\ttombstone.CFrame = previousCharacter.PrimaryPart.CFrame - previousCharacter.PrimaryPart.Position + targetPosition\n\t\t\t\tgame.Debris:AddItem(tombstone, tombstoneDuration)\n\t\t\tend)\n\t\tend\n\n\t\tif respawnType == \"dangerous\" then\n\t\t\tnetwork:fireClient(\"deathGuiRequested\", player)\n\n\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\ttag.Name = \"awaitingDeathGuiResponse\"\n\t\t\ttag.Value = true\n\t\t\ttag.Parent = player\n\n\t\t\tlocal playerStillDead = true\n\t\t\tlocal timer = 70\n\t\t\twhile timer > 0 do\n\t\t\t\ttimer = timer - wait(1)\n\n\t\t\t\t-- if player is alive, stop this killswitch\n\t\t\t\tlocal character = player.Character\n\t\t\t\tif character then\n\t\t\t\t\tlocal manifest = character.PrimaryPart\n\t\t\t\t\tif manifest then\n\t\t\t\t\t\tlocal state = manifest:FindFirstChild(\"state\")\n\t\t\t\t\t\tif state and state.Value ~= \"dead\" then\n\t\t\t\t\t\t\tplayerStillDead = false\n\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif player and player.Parent then\n\t\t\t\ttag:Destroy()\n\n\t\t\t\tif playerStillDead then\n\t\t\t\t\tonDeathGuiAccepted(player)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif respawnType == \"normal\" then\n\t\t\tdelay(respawnTime, function()\n\t\t\t\tif player then\n\t\t\t\t\tlocal character = player.Character\n\t\t\t\t\tif character then\n\t\t\t\t\t\t-- skip out on respawning if they're not dead\n\t\t\t\t\t\tlocal manifest = character.PrimaryPart\n\t\t\t\t\t\tif manifest then\n\t\t\t\t\t\t\tlocal state = manifest:FindFirstChild(\"state\")\n\t\t\t\t\t\t\tif state and state.Value ~= \"dead\" then\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\t-- otherwise we should go on\n\t\t\t\t\t\tplayer.Character:Destroy()\n\t\t\t\t\tend\n\n\t\t\t\t\tplayer:LoadCharacter()\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\tend\nend\n\nlocal function onCharacterHealthChanged(player, healthValue)\n\tif healthValue <= 0 then\n\t\tperformDeathToRenderCharacter(player)\n\tend\nend\n\nlocal function applyMaxHealth(player, dontHeal)\n\tlocal playerData = playerDataContainer[player]\n\n\tif playerData and player.Character and player.Character.PrimaryPart then\n\t\tlocal stats = playerData.nonSerializeData.statistics_final\n\n\t\tlocal oldMaxHealth = player.Character.PrimaryPart.maxHealth.Value\n\t\tlocal oldMaxMana = player.Character.PrimaryPart.maxMana.Value\n\n\t\t--[[\n\t\tplayer.Character.PrimaryPart.maxMana.Value \t= 15 + (3 * playerData.level) + (5 * stats.int)\n\t\tplayer.Character.PrimaryPart.maxHealth.Value = 75 + (25 * playerData.level) + (10 * stats.vit)\n\t\t]]\n\n\t\tplayer.Character.PrimaryPart.maxMana.Value = stats.maxMana\n\t\tplayer.Character.PrimaryPart.maxHealth.Value = stats.maxHealth\n\n\t\tlocal newHealth = player.Character.PrimaryPart.maxHealth.Value\n\t\tlocal newMana = player.Character.PrimaryPart.maxMana.Value\n\n\t\tif dontHeal then\n\t\t\tnewHealth = player.Character.PrimaryPart.health.Value + (newHealth - oldMaxHealth)\n\t\t\tnewMana = player.Character.PrimaryPart.mana.Value + (newMana - oldMaxMana)\n\t\tend\n\n\t\tplayer.Character.PrimaryPart.maxStamina.Value = stats.stamina or 0\n\n\t\tplayer.Character.PrimaryPart.health.Value = math.clamp(newHealth, 0, player.Character.PrimaryPart.maxHealth.Value)\n\t\tplayer.Character.PrimaryPart.mana.Value = math.clamp(newMana, 0, player.Character.PrimaryPart.maxMana.Value)\n\tend\nend\n\nlocal function onPlayerRequest_returnToMainMenu(player)\n\tgame:GetService(\"TeleportService\"):Teleport(2376885433, player, {\n\t\tteleportReason = \"Heading back to the main menu...\"\n\t}, game.ReplicatedStorage.returnToLobby)\nend\n\nlocal function onPlayerRequest_respawnMyCharacter(player)\n\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart:FindFirstChild(\"health\") then\n\t\tif player.Character.PrimaryPart.health.Value > 0 and player.Character.PrimaryPart.state.Value ~= \"dead\" then\n\t\t\tif not game.ReplicatedStorage:FindFirstChild(\"safeZone\") then\n\n\t\t\t\tlocal isNight = game.Lighting.ClockTime < 5.9 or game.Lighting.ClockTime > 18.6\n\t\t\t\tlocal messages = {\n\t\t\t\t\t\"got themselves into a sticky situation\";\n\t\t\t\t\t\"is not having a good time right now\";\n\t\t\t\t\t\"pondered the meaning of life a little too hard\";\n\t\t\t\t\t\"wants to go home right now\";\n\t\t\t\t\t\"tripped over a pebble\";\n\t\t\t\t\t\"forgot how to breathe\";\n\t\t\t\t\t\"is just taking a long nap... right guys?\";\n\t\t\t\t\t\"ceased to exist\";\n\t\t\t\t\t\"ignored the warning\";\n\t\t\t\t\t\"used the stones to destroy... uh... themselves.\";\n\t\t\t\t\t\"didn't wash their hands for 20 seconds\";\n\t\t\t\t\t\"evaporated into thin air\";\n\t\t\t\t\t\"suddenly stopped being alive\";\n\t\t\t\t\t\"pressed the wrong button\";\n\t\t\t\t\tisNight and \"stared directly at the moon\" or \"stared directly into the sun\"\n\t\t\t\t}\n\n\t\t\t\tlocal message = messages[math.random(1,#messages)]\n\t\t\t\tlocal text = \"☠ \" .. player.Name .. \" \" .. message .. \" ☠\"\n\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\t\tText = text,\n\t\t\t\t\tFont = Enum.Font.SourceSansBold,\n\t\t\t\t\tColor = Color3.fromRGB(255, 130, 100),\n\t\t\t\t})\n\t\t\tend\n\t\t\tplayer.Character.PrimaryPart.health.Value = 0\n\t\tend\n\telse\n\t\tif not game.ReplicatedStorage:FindFirstChild(\"noRespawns\") then\n\t\t\tplayer:LoadCharacter()\n\t\tend\n\tend\nend\n\nlocal statsToProcessWithFormula = {\"dex\"; \"int\"; \"str\"; \"vit\"}\nlocal function generatecompletePlayerStats(player, isInitializing, playerData)\n\tif not isInitializing and (not player or not playerDataContainer[player]) then return nil end\n\n\tplayerData = playerData or playerDataContainer[player]\n\n\tlocal baseStats = {}\n\tlocal baseStatsAdditive = {}\n\tlocal baseStatsMultiplicative = {}\n\tlocal totalStatsMultiplicative = {}\n\n\tlocal equipmentDefense = 0\n\tlocal equipmentDamage = 0\n\n\tlocal function incorporateModifierData(modifierData)\n\t\tfor _, modifier in pairs(modifierData) do\n\t\t\tfor stat, statValue in pairs(modifier) do\n\t\t\t\tif stat == \"defense\" then\n\t\t\t\t\tequipmentDefense = equipmentDefense + statValue\n\t\t\t\telseif stat == \"damage\" or stat == \"baseDamage\" then\n\t\t\t\t\tequipmentDamage = equipmentDamage + statValue\n\t\t\t\telse\n\t\t\t\t\tbaseStats[stat] = (baseStats[stat] or 0) + statValue\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t--[[\n\t\tfor ii, stat in pairs(statsToProcessWithFormula) do\n\t\t\tif modifierData[stat] then\n\t\t\t\tbaseStats[stat] = baseStats[stat] + modifierData[stat]\n\t\t\telseif modifierData[stat .. \"_baseAdditive\"] then\n\t\t\t\tbaseStatsAdditive[stat] = baseStatsAdditive[stat] + modifierData[stat .. \"_baseAdditive\"]\n\t\t\telseif modifierData[stat .. \"_baseMultiplicative\"] then\n\t\t\t\tbaseStatsMultiplicative[stat] = baseStatsMultiplicative[stat] + modifierData[stat .. \"_baseMultiplicative\"]\n\t\t\telseif modifierData[stat .. \"_totalAdditive\"] then\n\t\t\t\ttotalStatsAdditive[stat] = totalStatsAdditive[stat] + modifierData[stat .. \"_totalAdditive\"]\n\t\t\telseif modifierData[stat .. \"_totalMultiplicative\"] then\n\t\t\t\ttotalStatsMultiplicative[stat] = totalStatsMultiplicative[stat] + modifierData[stat .. \"_totalMultiplicative\"]\n\t\t\tend\n\t\tend\n\n\t\tif modifierData.defense then\n\t\t\tequipmentDefense = equipmentDefense + modifierData.defense\n\t\tend\n\n\t\tif modifierData.baseDamage or modifierData.damage then\n\t\t\tequipmentDamage = equipmentDamage + (modifierData.baseDamage or modifierData.damage)\n\t\tend\n\t\t]]\n\tend\n\n\t-- set defaults for each stat\n\tfor _, stat in pairs(statsToProcessWithFormula) do\n\t\tbaseStats[stat] = 0\n\t\tbaseStatsAdditive[stat] = 0\n\t\tbaseStatsMultiplicative[stat] = 1\n\t\ttotalStatsMultiplicative[stat] = 1\n\tend\n\n\t-- apply the stats\n\tlocal completePlayerStats = {}\n\tfor i, stat in pairs(statsToProcessWithFormula) do\n\t\t-- fetch the base stat in the player data container file\n\t\tbaseStats[stat] \t\t\t\t= playerData.statistics[stat] or 0\n\t\tbaseStatsAdditive[stat] \t\t= baseStatsAdditive[stat] or 0\n\t\tbaseStatsMultiplicative[stat] \t= baseStatsMultiplicative[stat] or 1\n\t\ttotalStatsMultiplicative[stat] \t= totalStatsMultiplicative[stat] or 1\n\tend\n\n\t-- statusEffectsV2\n\tlocal activeStatusEffects = network:invoke(\"getStatusEffectsOnEntityManifestByEntityGUID\", player.entityGUID.Value)\n\tfor _, activeStatusEffectData in pairs(activeStatusEffects) do\n\t\tif activeStatusEffectData.statusEffectModifier and activeStatusEffectData.statusEffectModifier.modifierData then\n\t\t\tlocal modifierData = activeStatusEffectData.statusEffectModifier.modifierData\n\n\t\t\tincorporateModifierData({modifierData})\n\t\tend\n\tend\n\n\tlocal equipAttackSpeed = 3\n\n\t-- calculate from what the player is currently wearing\n\tlocal mainhandWeaponData, offhandWeaponData\n\n\tfor _, equipmentSlotData in pairs(playerData.equipment) do\n\t\tlocal itemBaseData = itemLookup[equipmentSlotData.id]\n\n\t\t-- add in base data\n\t\tif itemBaseData then\n\t\t\tif itemBaseData.modifierData then\n\t\t\t\tincorporateModifierData(itemBaseData.modifierData)\n\t\t\tend\n\n\t\t\tlocal slot = equipmentSlotData.position\n\t\t\tlocal isWeapon = false\n\t\t\tif slot == 1 then\n\t\t\t\tmainhandWeaponData = itemBaseData\n\t\t\t\tisWeapon = true\n\t\t\t\tif itemBaseData.attackSpeed then\n\t\t\t\t\tequipAttackSpeed = itemBaseData.attackSpeed\n\t\t\t\tend\n\t\t\t-- only swords in the offhand count as weapons\n\t\t\telseif slot == 11 and itemBaseData.equipmentType == \"sword\" then\n\t\t\t\toffhandWeaponData = itemBaseData\n\t\t\t\tisWeapon = true\n\n\t\t\t-- ignore bows and daggers in the offhand too\n\t\t\t-- but don't save their data because we aren't\n\t\t\t-- averaging the bows and daggers together\n\t\t\telseif slot == 11 and (itemBaseData.equipmentType == \"bow\" or itemBaseData.equipmentType == \"dagger\") then\n\t\t\t\tisWeapon = true\n\n\t\t\t-- ignore fishing rods? why are these considered weapons\n\t\t\telseif slot == 11 and itemBaseData.equipmentType == \"fishing-rod\" then\n\t\t\t\tisWeapon = true\n\t\t\tend\n\n\t\t\t-- we'll calculate weapon damage later because dual wielding is wacky\n\t\t\tif (not isWeapon) and (itemBaseData.baseDamage or itemBaseData.damage) then\n\t\t\t\tequipmentDamage = equipmentDamage + (itemBaseData.baseDamage or itemBaseData.damage)\n\t\t\tend\n\n\t\t\t-- enchantments! :P\n\t\t\tlocal statUpgrade = 0\n\t\t\tif equipmentSlotData.enchantments then\n\t\t\t\tfor _, enchantmentData in pairs(equipmentSlotData.enchantments) do\n\t\t\t\t\tlocal enchantmentBaseData = itemLookup[enchantmentData.id]\n\t\t\t\t\tif enchantmentBaseData.enchantments then\n\t\t\t\t\t\tlocal enchantmentState = enchantmentBaseData.enchantments[enchantmentData.state]\n\t\t\t\t\t\tif enchantmentState then\n\t\t\t\t\t\t\tif enchantmentState.modifierData then\n\t\t\t\t\t\t\t\tincorporateModifierData({enchantmentBaseData.enchantments[enchantmentData.state].modifierData})\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif enchantmentState.statUpgrade then\n\t\t\t\t\t\t\t\tstatUpgrade = statUpgrade + enchantmentState.statUpgrade\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- variable stat-enhancing enchants (mainly for hats!)\n\t\t\tif statUpgrade > 0 and itemBaseData.statUpgrade then\n\t\t\t\tlocal statUpgradeModifierData = {}\n\t\t\t\tfor stat, value in pairs(itemBaseData.statUpgrade) do\n\t\t\t\t\tif value ~= 0 then\n\t\t\t\t\t\tstatUpgradeModifierData[stat] = value * statUpgrade\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tincorporateModifierData({statUpgradeModifierData})\n\t\t\tend\n\n\t\t\t-- attributes! :D\n\t\t\tif equipmentSlotData.attribute then\n\t\t\t\tlocal attributeData = itemAttributes[equipmentSlotData.attribute]\n\t\t\t\tif attributeData and attributeData.modifier then\n\t\t\t\t\tlocal attributeModifierData = attributeData.modifier(itemBaseData, equipmentSlotData)\n\t\t\t\t\tif attributeModifierData then\n\t\t\t\t\t\tincorporateModifierData({attributeModifierData})\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif equipmentSlotData.modifierData then\n\t\t\tincorporateModifierData(equipmentSlotData.modifierData)\n\t\tend\n\tend\n\n\t-- calculate the damage of weapons because dual wielding is wacky\n\tif mainhandWeaponData and offhandWeaponData then\n\t\tlocal damage = (\n\t\t\t(mainhandWeaponData.baseDamage or 0) + (mainhandWeaponData.damage or 0) +\n\t\t\t(offhandWeaponData.baseDamage or 0) + (offhandWeaponData.damage or 0)\n\t\t) / 2\n\t\tequipmentDamage = equipmentDamage + damage\n\telseif mainhandWeaponData and (not offhandWeaponData) then\n\t\tlocal damage = (mainhandWeaponData.baseDamage or 0) + (mainhandWeaponData.damage or 0)\n\t\tequipmentDamage = equipmentDamage + damage\n\tend\n\n\t-- compile the stats\n\tfor stat, value in pairs(baseStats) do\n\t\tcompletePlayerStats[stat] = value\n\tend\n\n\t-- post def-negation buffs\n\tcompletePlayerStats.damageTakenMulti = 1 + (baseStats.damageTakenMulti or 0)\n\tcompletePlayerStats.damageGivenMulti = 1 + (baseStats.damageGivenMulti or 0)\n\n\t-- unique stats are calculated differently\n\tcompletePlayerStats.equipmentDefense \t= equipmentDefense --+ playerData.level\n\tcompletePlayerStats.defense \t\t\t= equipmentDefense --+ playerData.level\n\n\tcompletePlayerStats.equipmentDamage \t= (equipmentDamage + (baseStats.equipmentDamage or 0)) --+ playerData.level\n\tcompletePlayerStats.damage \t\t\t\t= completePlayerStats.equipmentDamage --+ playerData.level\n\n\tcompletePlayerStats.physicalDamage \t\t= completePlayerStats.equipmentDamage --+ playerData.level\n\tcompletePlayerStats.magicalDamage \t\t= completePlayerStats.equipmentDamage --+ playerData.level\n\n\tcompletePlayerStats.physicalDefense \t= completePlayerStats.equipmentDefense --+ playerData.level\n\tcompletePlayerStats.magicalDefense \t\t= completePlayerStats.equipmentDefense --+ playerData.level\n\n\tcompletePlayerStats.stamina = 3 + (baseStats.stamina or 0)\n\tcompletePlayerStats.staminaRecovery = 1 + (baseStats.staminaRecovery or 0)\n\n\t-- previous:\t\t75 + 25*lvl * 1/100*VIT\n\t-- new:\t\t\t\t50 + 25*lvl\n\tlocal level = playerData.level\n\tlocal vit = completePlayerStats.vit\n\tlocal int = completePlayerStats.int\n\n\t-- health\n\tlocal HP = 15 + (5 * level)\n\tHP = HP + (5 * vit)\n\tif baseStats.maxHealth then\n\t\tHP = HP + baseStats.maxHealth\n\tend\n\tcompletePlayerStats.maxHealth = math.ceil(HP)\n\n\t-- mana\n\tlocal MP = 3 + (2 * level)\n\tMP = MP + 1 * int\n\tif baseStats.maxMana then\n\t\tMP = MP + baseStats.maxMana\n\tend\n\tcompletePlayerStats.maxMana = math.ceil(MP)\n\n\tlocal manaRegenMulti = 1\n\tif baseStats.manaRegen then\n\t\tmanaRegenMulti = manaRegenMulti + baseStats.manaRegen/100\n\tend\n\n\tcompletePlayerStats.manaRegen \t\t\t= (1.0 + 0.035 * playerData.level + 0.050 * completePlayerStats.int) * manaRegenMulti\n\n\tlocal healthRegenMulti = 1\n\tif baseStats.healthRegen then\n\t\thealthRegenMulti = healthRegenMulti + baseStats.healthRegen/100\n\tend\n\n\tcompletePlayerStats.healthRegen \t\t= (0.5 + 0.240 * playerData.level + 0.100 * completePlayerStats.vit) * healthRegenMulti\n\tcompletePlayerStats.jump \t\t\t\t= 75 + (baseStats.jump or 0)\n\tcompletePlayerStats.consumeTimeReduction = 0 + (baseStats.consumeTimeReduction  or 0)\n\tcompletePlayerStats.attackSpeed = 0 + (equipAttackSpeed - 3)/3.5\n\tcompletePlayerStats.woodcutting = math.max(baseStats.woodcutting or 1, 1)\n\tcompletePlayerStats.mining = math.max(baseStats.mining or 1, 1)\n\tcompletePlayerStats.criticalStrikeChance \t= ((0.5 / 100) / 3) * completePlayerStats.dex + (baseStats.criticalStrikeChance or 0)\n\tcompletePlayerStats.blockChance \t\t\t= math.clamp(0.20 * (completePlayerStats.dex / (3 * playerData.level)), 0, 1) + (baseStats.blockChance or 0)\n\t-- how much damage critical strikes do (decimal multiplier)\n\t--  2 means 200%, not just \"2\".\n\tcompletePlayerStats.criticalStrikeDamage = 2 + (baseStats.criticalStrikeDamage or 0)\n\t-- how much more gold you get (this is a multipler, so 1 is just 100% of the\n\t-- regular value you'd get, anything higher is considered an increase)\n\tcompletePlayerStats.greed = 1 + (baseStats.greed or 0)\n\t-- how much more exp you get (this is a multipler, so 1 is just 100% of the\n\t-- regular value you'd get, anything higher is considered an increase)\n\tcompletePlayerStats.wisdom = 1 + (baseStats.wisdom or 0)\n\t-- increases chance of soulbound items being given to you\n\tcompletePlayerStats.luck = 1 + (baseStats.luck or 0)\n\t-- flat movement speed of player\n\tcompletePlayerStats.walkspeed = 18 + (baseStats.walkspeed or 0)\n\t-- merchantCostReduction (1 = 100% reduction (1 gold minimum))\n\tcompletePlayerStats.merchantCostReduction = 0\n\t-- ability cool down reduction (1 = 100% reduction)\n\tcompletePlayerStats.abilityCDR = 0\n\t-- additive bonus (1 + attackRangeIncrease, therefore 1 = 200% increase)\n\tcompletePlayerStats.attackRangeIncrease = 0\n\t-- additive bonus (1 + consumableHealthIncrease, therefore 1 = 200% increase)\n\tcompletePlayerStats.consumableHealthIncrease \t= 0\n\tcompletePlayerStats.consumableManaIncrease \t\t= 0\n\t-- apply multiplicative bonuses?\n\tfor stat, value in pairs(completePlayerStats) do\n\t\tlocal multiplicative = baseStats[stat..\"_totalMultiplicative\"]\n\t\tif multiplicative then\n\t\t\tcompletePlayerStats[stat] = value * (1 + multiplicative)\n\t\tend\n\tend\n\n\t-- apply perks here\n\tlocal activePerks = {}\n\n\t-- add perks from equipment\n\tfor i, equipmentSlotData in pairs(playerData.equipment) do\n\t\tlocal itemBaseData = itemLookup[equipmentSlotData.id]\n\t\t-- add in base data\n\t\tif itemBaseData.perks then\n\t\t\tfor perkName, perkData in pairs(itemBaseData.perks) do\n\t\t\t\tactivePerks[perkName] = true\n\t\t\tend\n\t\tend\n\t\tif equipmentSlotData.perks then\n\t\t\tfor perkName, perkData in pairs(equipmentSlotData.perks) do\n\t\t\t\tactivePerks[perkName] = true\n\t\t\tend\n\t\tend\n\tend\n\n\t-- run conditions and apply perks as needed\n\tfor perkName, perk in pairs(perkLookup) do\n\t\tlocal condition = perk.condition and perk.condition(completePlayerStats)\n\t\t-- figured this would be easier to do at the manager level\n\t\tif perk.class and isPlayerOfClass(player, perk.class) then\n\t\t\tcondition = true\n\t\tend\n\t\tif condition or activePerks[perkName] then\n\t\t\tactivePerks[perkName] = true\n\t\t\tif perk.apply then\n\t\t\t\tperk.apply(completePlayerStats)\n\t\t\tend\n\t\telse\n\t\t\tactivePerks[perkName] = false\n\t\tend\n\tend\n\n\tcompletePlayerStats.activePerks = activePerks\n\n\tplayerData.nonSerializeData.statistics_final = completePlayerStats\n\n\tapplyMaxHealth(player, true)\n\n\tif player then\n\t\tfor i, stat in pairs(statsToProcessWithFormula) do\n\t\t\tlocal valueObject = player:FindFirstChild(stat)\n\t\t\tif valueObject == nil then\n\t\t\t\tvalueObject = Instance.new(\"IntValue\")\n\t\t\t\tvalueObject.Name = stat\n\t\t\t\tvalueObject.Parent = player\n\t\t\tend\n\n\t\t\tvalueObject.Value = completePlayerStats[stat]\n\t\tend\n\n\t\tif playerDataContainer[player] then\n\t\t\tnetwork:fireClient(\"playerStatisticsChanged\", player, playerData.statistics, completePlayerStats)\n\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"nonSerializeData\")\n\t\tend\n\tend\n\n\treturn completePlayerStats\nend\n\n\n\nlocal function isStartingValueRemoved(primaryPart, child)\n\tif primaryPart.Parent == nil then\n\t\treturn false\n\tend\n\n\treturn not not game.StarterPlayer.StarterCharacter.PrimaryPart:FindFirstChild(child.Name)\nend\n\nlocal RESPAWN_POINT_RADIUS = 32\nlocal RESPAWN_POINT_RADIUS_SQ = RESPAWN_POINT_RADIUS ^ 2\n\nlocal function checkForRespawnPoint(player)\n\tlocal char = player.Character\n\tif not char then return end\n\n\tlocal root = char.PrimaryPart\n\tif not root then return end\n\n\tlocal respawnPoint = player:FindFirstChild(\"respawnPoint\")\n\tif not respawnPoint then return end\n\n\tlocal spawnPoints = game.ReplicatedStorage.spawnPoints:GetChildren()\n\n\tlocal bestSpawnPoint = nil\n\tlocal bestDistanceSq = RESPAWN_POINT_RADIUS_SQ\n\n\tlocal function checkSpawnPoint(spawnPoint)\n\t\tif not spawnPoint:FindFirstChild(\"description\") then return end\n\t\tif spawnPoint:FindFirstChild(\"ignore\") then return end\n\n\t\tlocal delta = spawnPoint.Value.Position - root.Position\n\t\tlocal distanceSq = delta.X * delta.X + delta.Y * delta.Y + delta.Z * delta.Z\n\n\t\tif distanceSq <= bestDistanceSq then\n\t\t\tbestSpawnPoint = spawnPoint\n\t\t\tbestDistanceSq = distanceSq\n\t\tend\n\tend\n\n\tfor _, spawnPoint in pairs(spawnPoints) do\n\t\tcheckSpawnPoint(spawnPoint)\n\tend\n\n\tif bestSpawnPoint and (respawnPoint.Value ~= bestSpawnPoint) then\n\t\trespawnPoint.Value = bestSpawnPoint\n\t\tlocal spawnText = bestSpawnPoint:FindFirstChild(\"description\") and bestSpawnPoint.description.Value\n\t\tif spawnText then\n\t\t\tlocal playerData = playerDataContainer[player]\n\t\t\tlocal placeData = playerData.locations[tostring(game.PlaceId)]\n\t\t\tplaceData.spawns[bestSpawnPoint.Name] = {text = spawnText}\n\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"locations\")\n\t\tend\n\t\tevents:fireEventPlayer(\"playerRespawnPointChanged\", player, respawnPoint.Value)\n\tend\nend\n\nlocal function onCharacterAdded(player, character)\n\twhile character.Parent ~= entityManifestCollectionFolder do\n\t\twait(0.1)\n\t\tcharacter.Parent = entityManifestCollectionFolder\n\tend\n\n\tgeneratecompletePlayerStats(player, false, playerDataContainer[player])\n\n\tif character:WaitForChild(\"hitbox\", 10) then\n\t\tcharacter.PrimaryPart = character.hitbox\n\n\t\tlocal VALUE_OBJECT_EXPLOITER_DELETION_CHECK do\n\t\t\tlocal primaryPartForCheck = character.PrimaryPart\n\n\t\t\tfor i, obj in pairs(game.StarterPlayer.StarterCharacter.PrimaryPart:GetChildren()) do\n\t\t\t\tif not character.PrimaryPart:FindFirstChild(obj.Name) then\n\t\t\t\t\tplayer:Kick(\"Anti-exploit\")\n\t\t--\t\t\tgame:GetService(\"TeleportService\"):Teleport(2376885433, player, nil, game.ReplicatedStorage.returnToLobby)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal function onChildRemoved(child)\n\t\t\t\tif isStartingValueRemoved(primaryPartForCheck, child) then\n\t\t\t\t\tplayer:Kick(\"Anti-exploit\")\n\t\t--\t\t\tgame:GetService(\"TeleportService\"):Teleport(2376885433, player, nil, game.ReplicatedStorage.returnToLobby)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tcharacter.PrimaryPart.ChildRemoved:connect(onChildRemoved)\n\t\tend\n\n\t\tnetwork:fire(\"playerCharacterLoaded\", player, character)\n\n\t\tlocal success = pcall(function() character.PrimaryPart:SetNetworkOwner(player) end) do\n\t\t\twhile not success do\n\t\t\t\twait(0.5)\n\t\t\t\tsuccess = pcall(function() character.PrimaryPart:SetNetworkOwner(player) end)\n\t\t\tend\n\t\tend\n\n\t\tapplyMaxHealth(player)\n\n\t\tlocal playerData = playerDataContainer[player]\n\t\tif playerData and playerData.condition then\n\t\t\tlocal manifest = character.PrimaryPart\n\t\t\tif not manifest then return end\n\n\t\t\tlocal health = manifest.health\n\t\t\tlocal mana = manifest.mana\n\t\t\tif not (health and mana) then return end\n\n\t\t\thealth.Value = math.max(1, playerData.condition.health)\n\t\t\tmana.Value = math.max(1, playerData.condition.mana)\n\n\t\t\tplayerData.condition = nil\n\t\tend\n\n\t\tonCharacterHealthChanged(player, player.Character.PrimaryPart.health.Value)\n\n\t\tlocal alive = true\n\n\t\tif character.PrimaryPart and player.Character.PrimaryPart.health.Value > 0 then\n\t\t\tplayer.Character.PrimaryPart.health.Changed:connect(function(newHealth)\n\t\t\t\tonCharacterHealthChanged(player, newHealth)\n\n\t\t\t\tif newHealth <= 0 then\n\t\t\t\t\talive = false\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\n\t\tlocal health_regenPool \t= 0\n\t\tlocal mana_regenPool \t= 0\n\n\t\tlocal playerData = playerDataContainer[player]\n\n\t\tspawn(function()\n\t\t\twhile true do\n\t\t\t\tlocal delta = wait(1)\n\n\t\t\t\tif configuration.getConfigurationValue(\"server_doRedirectMaxHealthDeletions\") and character.PrimaryPart and not character.PrimaryPart:FindFirstChild(\"maxHealth\") then\n\t\t\t\t\tplayer:Kick(\"Anti-exploit\")\n\t--\t\t\t\tgame:GetService(\"TeleportService\"):Teleport(2376885433, player, nil, game.ReplicatedStorage.returnToLobby)\n\t\t\t\tend\n\n\t\t\t\tcheckForRespawnPoint(player)\n\n\t\t\t\tif alive and character and character.PrimaryPart and player.Character == character and playerData and playerData.nonSerializeData then\n\t\t\t\t\tlocal isInCombat = (tick() - (playerData.nonSerializeData.lastTimeInCombat or 0)) <= 4\n\t\t\t\t\tlocal stats = playerData.nonSerializeData.statistics_final\n\n\t\t\t\t\tlocal multiplier_mana \t= 1\n\t\t\t\t\tlocal multiplier_health = 1\n\n\t\t\t\t\tlocal state = player.Character.PrimaryPart:FindFirstChild(\"state\")\n\n\t\t\t\t\tif state and state.Value == \"sitting\" and player.Character.PrimaryPart.Anchored then\n\t\t\t\t\t\tmultiplier_mana \t= multiplier_mana * 2\n\t\t\t\t\t\tmultiplier_health \t= multiplier_health * 2\n\t\t\t\t\tend\n\n\t\t\t\t\tif isInCombat then\n\t\t\t\t\t\tmultiplier_health \t= multiplier_health * 0\n\t\t\t\t\t\tmultiplier_mana \t= multiplier_mana \t* 0.5\n\t\t\t\t\tend\n\n\t\t\t\t\tif state and state.Value == \"dead\" then\n\t\t\t\t\t\tmultiplier_health = 0\n\t\t\t\t\t\tmultiplier_mana = 0\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal healthGainFromThisTick \t= stats.healthRegen * delta * multiplier_health * 0.5\n\t\t\t\t\tlocal healthGain \t\t\t\t= health_regenPool + healthGainFromThisTick\n\t\t\t\t\tlocal healthToGivePlayer \t\t= math.floor(healthGain)\n\n\t\t\t\t\tif healthToGivePlayer > 0 then\n\t\t\t\t\t\tcharacter.PrimaryPart.health.Value = math.clamp(character.PrimaryPart.health.Value + healthToGivePlayer, 0, character.PrimaryPart.maxHealth.Value)\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal manaGainFromThisTick \t= stats.manaRegen * delta * multiplier_mana * 0.5\n\t\t\t\t\tlocal manaGain \t\t\t\t= mana_regenPool + manaGainFromThisTick\n\t\t\t\t\tlocal manaToGivePlayer \t\t= math.floor(manaGain)\n\n\t\t\t\t\tif manaToGivePlayer > 0 then\n\t\t\t\t\t\tcharacter.PrimaryPart.mana.Value = math.clamp(character.PrimaryPart.mana.Value + manaToGivePlayer, 0, character.PrimaryPart.maxMana.Value)\n\t\t\t\t\tend\n\n\t\t\t\t\thealth_regenPool \t= healthGain - healthToGivePlayer\n\t\t\t\t\tmana_regenPool \t\t= manaGain - manaToGivePlayer\n\t\t\t\telse\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\n\t\t-- when we stand on death traps, be... uh, death trapped.\n\t\tlocal manifest = character.PrimaryPart\n\t\tlocal deathTrapNext = 0\n\t\tlocal deathTrapTime = 1\n\t\tlocal collisionWiggle = Vector3.new(1, 1, 1) * 0.2\n\t\tlocal conn = nil\n\t\tlocal lastDeathTrap\n\t\tlocal function getDeathTrapKillMessage(part)\n\t\t\tlocal text = \"☠ \" .. player.Name .. \" was killed by \"..part.Name..\" ☠\"\n\n\t\t\tlocal message = part:FindFirstChild(\"message\")\n\t\t\tif message then\n\t\t\t\ttext = message.Value\n\t\t\t\ttext = string.gsub(text, \"%[playerName]\", player.Name)\n\t\t\t\ttext = string.gsub(text, \"%[trapName]\", part.Name)\n\t\t\t\ttext = \"☠ \"..text..\" ☠\"\n\t\t\tend\n\n\t\t\treturn text\n\t\tend\n\t\tlocal function applyDeathTrap(part, now)\n\t\t\tdeathTrapNext = now + deathTrapTime\n\n\t\t\tif part:FindFirstChild(\"cooldown\") then\n\t\t\t\tdeathTrapNext = now + part.cooldown.Value\n\t\t\tend\n\n\t\t\t-- is this a status effect trap?\n\t\t\tif part:FindFirstChild(\"statusEffect\") then\n\t\t\t\tlocal statusName = part.statusEffect.Value\n\n\t\t\t\tlocal function getArgs(object)\n\t\t\t\t\tlocal args = {}\n\t\t\t\t\tfor _, child in pairs(object:GetChildren()) do\n\t\t\t\t\t\tif child:IsA(\"Folder\") then\n\t\t\t\t\t\t\targs[child.Name] = getArgs(child)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\targs[child.Name] = child.Value\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\treturn args\n\t\t\t\tend\n\t\t\t\tlocal args = getArgs(part.statusEffect)\n\n\t\t\t\tnetwork:invoke(\"applyStatusEffectToEntityManifest\",\n\t\t\t\t\tmanifest,\n\t\t\t\t\tstatusName,\n\t\t\t\t\targs,\n\t\t\t\t\tmanifest,\n\t\t\t\t\t\"trap\",\n\t\t\t\t\t0\n\t\t\t\t)\n\n\t\t\t\t-- drop a special tag so that the kill notification system knows what to say\n\t\t\t\t-- in the case where we \"kill ourselves\" and it was actually due to a trap\n\t\t\t\tlocal tag = Instance.new(\"StringValue\")\n\t\t\t\ttag.Name = \"deathTrapKillMessage\"\n\t\t\t\ttag.Value = getDeathTrapKillMessage(part)\n\t\t\t\ttag.Parent = player\n\t\t\t\tgame:GetService(\"Debris\"):AddItem(tag, args.duration or 1)\n\t\t\tend\n\n\t\t\t-- how much damage?\n\t\t\tlocal damage\n\t\t\tif part:FindFirstChild(\"damage\") then\n\t\t\t\tdamage = part.damage.Value\n\n\t\t\telseif part:FindFirstChild(\"percentDamage\") then\n\t\t\t\tlocal maxHealth = manifest:FindFirstChild(\"maxHealth\")\n\t\t\t\tif maxHealth then\n\t\t\t\t\tdamage = maxHealth.Value * part.percentDamage.Value\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif damage then\n\t\t\t\tlocal health = manifest:FindFirstChild(\"health\")\n\t\t\t\tif not health then return end\n\t\t\t\tif health.Value <= 0 then return end\n\n\t\t\t\thealth.Value = math.max(0, health.Value - damage)\n\n\t\t\t\t-- play a sound\n\t\t\t\tlocal injurySound = Instance.new(\"Sound\")\n\t\t\t\tinjurySound.SoundId = \"rbxassetid://2065833626\"\n\t\t\t\tinjurySound.MaxDistance = 1000\n\t\t\t\tinjurySound.Volume = 1.5\n\t\t\t\tinjurySound.EmitterSize = 5\n\t\t\t\tinjurySound.Parent = manifest\n\t\t\t\tinjurySound:Play()\n\t\t\t\tgame:GetService(\"Debris\"):AddItem(injurySound, injurySound.TimeLength)\n\n\t\t\t\t-- potentially show a message\n\t\t\t\tif health.Value <= 0 then\n\t\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\t\t\tText = getDeathTrapKillMessage(part),\n\t\t\t\t\t\tFont = Enum.Font.SourceSansBold,\n\t\t\t\t\t\tColor = Color3.fromRGB(255, 130, 100),\n\t\t\t\t\t})\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- any knockback?\n\t\t\tif part:FindFirstChild(\"knockback\") then\n\t\t\t\t--\"deathTrapKnockback\"\n\t\t\t\tlocal nearestPoint = detection.projection_Box(part.CFrame, part.Size, manifest.Position)\n\t\t\t\tlocal delta = manifest.Position - nearestPoint\n\t\t\t\tnetwork:fireClient(\"deathTrapKnockback\", player, delta.Unit * part.knockback.Value)\n\t\t\tend\n\t\tend\n\t\tlocal function getIsTouchingDeathTrap(part)\n\t\t\t-- fast distance check\n\t\t\tlocal delta = manifest.Position - part.Position\n\t\t\tlocal manhattan = math.max(math.abs(delta.X), math.abs(delta.Y), math.abs(delta.Z))\n\t\t\tlocal range = math.max(part.Size.X, part.Size.Y, part.Size.Z)\n\t\t\tif manhattan > range then return false end\n\n\t\t\t-- slower collision check\n\t\t\t-- we need to add a bit to the top and bottom of our character because the\n\t\t\t-- hit box as it exists now doesn't fully cover the head and feet\n\t\t\tlocal charCFrame = manifest.CFrame\n\t\t\tlocal charSize = manifest.Size + collisionWiggle + Vector3.new(0, 2, 0)\n\t\t\tlocal castPosition = detection.projection_Box(charCFrame, charSize, part.Position)\n\t\t\tif not detection.boxcast_singleTarget(part.CFrame, part.Size, castPosition) then return false end\n\n\t\t\treturn true\n\t\tend\n\t\tlocal function checkDeathTrap(part, now)\n\t\t\tif not getIsTouchingDeathTrap(part) then return end\n\n\t\t\tif part:FindFirstChild(\"reference\") then\n\t\t\t\tpart = part.reference.Value\n\t\t\t\tif not part then return end\n\t\t\tend\n\n\t\t\tlastDeathTrap = part\n\n\t\t\tif part:FindFirstChild(\"onTouched\") then\n\t\t\t\tpart = part.onTouched\n\t\t\tend\n\n\t\t\tapplyDeathTrap(part, now)\n\t\tend\n\t\tlocal function checkForOnTouchEnded(part)\n\t\t\tif getIsTouchingDeathTrap(part) then return end\n\n\t\t\tlocal part = lastDeathTrap\n\t\t\tlastDeathTrap = nil\n\n\t\t\tif part:FindFirstChild(\"onTouchEnded\") then\n\t\t\t\tapplyDeathTrap(part.onTouchEnded, tick())\n\t\t\tend\n\t\tend\n\t\tlocal function checkForDeathTraps()\n\t\t\tif (not manifest.Parent) or (manifest.Parent ~= player.Character) then\n\t\t\t\tconn:Disconnect()\n\t\t\tend\n\n\t\t\tif lastDeathTrap then\n\t\t\t\tcheckForOnTouchEnded(lastDeathTrap)\n\t\t\tend\n\n\t\t\tlocal now = tick()\n\t\t\tif now < deathTrapNext then return end\n\n\t\t\tlocal origin = manifest.Position - Vector3.new(0, manifest.Size.Y / 2, 0)\n\t\t\tlocal direction = Vector3.new(0, -2, 0)\n\t\t\tlocal parts = CollectionService:GetTagged(\"deathTrap\")\n\n\t\t\tfor _, part in pairs(parts) do\n\t\t\t\tlocal hitATrap = checkDeathTrap(part, now)\n\n\t\t\t\tif hitATrap then\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\tconn = runService.Heartbeat:Connect(checkForDeathTraps)\n\n\t\tphysics:setWholeCollisionGroup(character, \"characters\")\n\n\t\tlocal entity = character.PrimaryPart\n\t\t--[[\n\t\tentity:WaitForChild(\"state\")\n\n\t\twhile character and character.Parent do\n\t\t\tif entity.state.Value == \"sprinting\" then\n\t\t\t\tentity.stamina.Value = math.max(entity.stamina.Value - 0.2, 0)\n\t\t\telseif entity.stamina.Value < entity.maxStamina.Value then\n\t\t\t\tentity.stamina.Value = math.min(entity.stamina.Value + 0.2, entity.maxStamina.Value)\n\t\t\tend\n\t\t\twait(0.2)\n\t\tend\n\n\t\t]]\n\n\tend\nend\n\nlocal function forceCharacterPosition(player, cf)\n\tif player.Character and player.Character.PrimaryPart then\n\t\tplayer.Character:SetPrimaryPartCFrame(cf)\n\tend\nend\n\nlocal function onClientRequestFlushPropogationCache(client)\n\tif not playerDataContainer[client] then\n\t\twhile not playerDataContainer[client] do\n\t\t\twait()\n\t\tend\n\tend\n\n\tif playerDataContainer[client] then\n\t\tlocal propogationCacheLookupTable = {}\n\t\tfor propogationNameTag, propogationValue in pairs(playerDataContainer[client]) do\n\t\t\t--if propogationNameTag ~= \"nonSerializeData\" then\n\t\t\t\tpropogationCacheLookupTable[propogationNameTag] = propogationValue\n\t\t\t--end\n\t\tend\n\n\t\tnetwork:fireClient(\"clientFlushPropogationCache\", client, propogationCacheLookupTable)\n\tend\nend\n\nlocal function replicatePlayerCharacterAppearance(player, playerData)\n\tplayerData = playerData or playerDataContainer[player]\n\n\tif playerData then\n\t\tlocal characterAppearanceData = {}\n\t\t\tcharacterAppearanceData.equipment \t\t\t= playerData.equipment\n\t\t\tcharacterAppearanceData.accessories \t\t= playerData.accessories\n\t\t\tcharacterAppearanceData.temporaryEquipment \t= playerData.nonSerializeData.temporaryEquipment\n\n\t\tif player.Character and player.Character.PrimaryPart then\n\t\t\tplayer.Character.PrimaryPart.appearance.Value = HttpService:JSONEncode(characterAppearanceData)\n\t\tend\n\tend\nend\n\nlocal function onPlayerRequest_equipTemporaryEquipment(player, temporaryEquipmentModel)\n\tlocal playerData = playerDataContainer[player]\n\n\tif playerData then\n\t\tif temporaryEquipmentModel and temporaryEquipmentModel:IsDescendantOf(temporaryEquipmentFolder) then\n\t\t\tif not playerData.nonSerializeData.temporaryEquipment[temporaryEquipmentModel.Name] then\n\t\t\t\tplayerData.nonSerializeData.temporaryEquipment[temporaryEquipmentModel.Name] = true\n\n\t\t\t\t-- force update player appearance\n\t\t\t\treplicatePlayerCharacterAppearance(player, playerData)\n\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal function replicatePlayerCharacterActiveStatusEffects(player, playerData)\n\tif player:FindFirstChild(\"entityGUID\") then\n\t\tnetwork:invoke(\"updateStatusEffectsForEntityManifestByEntityGUID\", player.entityGUID.Value)\n\tend\nend\n\nlocal function getPropogationCacheLookupTable(client)\n\tif game.PlaceId == 2376885433 or game.PlaceId == 3323943158 or game.PlaceId == 2015602902 then\n\t\treturn {}\n\tend\n\n\tif not playerDataContainer[client] then\n\t\twhile not playerDataContainer[client] do\n\t\t\twait()\n\t\tend\n\tend\n\n\tlocal propogationCacheLookupTable = {}\n\tfor propogationNameTag, propogationValue in pairs(playerDataContainer[client]) do\n\t\t--if propogationNameTag ~= \"nonSerializeData\" then\n\t\t\tpropogationCacheLookupTable[propogationNameTag] = propogationValue\n\t\t--end\n\tend\n\n\treturn propogationCacheLookupTable\nend\n\nlocal function onClientRequestPropogateCacheData(client, propogationNameTag)\n\tif not playerDataContainer[client] then\n\t\twhile not playerDataContainer[client] do\n\t\t\twait(0.1)\n\t\tend\n\tend\n\n\tif playerDataContainer[client] then\n\t\tnetwork:fireClient(\"propogateCacheDataRequest\", client, propogationNameTag, playerDataContainer[client][propogationNameTag])\n\tend\nend\n\nlocal function getAvailableSlot(slots)\n\tfor slotId, isSlotOpen in pairs(slots) do\n\t\tif isSlotOpen then\n\t\t\treturn slotId\n\t\tend\n\tend\n\n\treturn nil\nend\n\nlocal function validateEquipmentSlots(playerEquipment)\n\t-- iterate through playerEquipment and make sure its all good\n\twhile true do\n\t\tlocal hasMadeChange = false\n\n\t\tfor i, equipmentSlotData in pairs(playerEquipment) do\n\t\t\tlocal itemBaseData = itemLookup[equipmentSlotData.id]\n\n\t\t\tif itemBaseData.equipmentSlot ~= equipmentSlotData.position then\n\t\t\t\thasMadeChange = true\n\n\t\t\t\ttable.remove(playerEquipment, i)\n\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\tif not hasMadeChange then\n\t\t\tbreak\n\t\tend\n\tend\nend\n\n-- validates and builds intermediate transfer data\n-- second return is true IF AND ONLY IF the player is missing the\n-- items stipulated\nlocal function int__getInventoryTransferData_intermediateCollectionFromInventoryTransferDataCollection(player, inventoryTransferDataCollection)\n\tlocal playerData = playerDataContainer[player]\n\n\tif playerData then\n\t\tlocal inventoryTransferData_intermediateCollection \t= {}\n\t\tlocal wasInventoryTransferDataModified \t\t\t\t= false\n\t\tlocal isDataMalformed \t\t\t\t\t\t\t\t= false\n\t\tlocal successful_conversions \t\t\t\t\t\t= 0\n\n\t\tlocal inventoryTransferDataCollection_list = utilities.copyTable(inventoryTransferDataCollection)\n\n\t\tfor i, inventoryTransferData in pairs(inventoryTransferDataCollection_list) do\n\t\t\tlocal itemBaseData = itemLookup[inventoryTransferData.id]\n\n\t\t\tif itemBaseData and (inventoryTransferData.stacks or 1) > 0 then\n\t\t\t\tfor trueInventorySlotPosition, inventorySlotData in pairs(playerData.inventory) do\n\t\t\t\t\tif inventorySlotData.id == inventoryTransferData.id then\n\t\t\t\t\t\tif itemBaseData.canStack then\n\t\t\t\t\t\t\tif inventoryTransferData.stacks > 0 then\n\t\t\t\t\t\t\t\tlocal stacksToRemove = math.clamp(inventoryTransferData.stacks, 1, inventorySlotData.stacks or 1)\n\n\t\t\t\t\t\t\t\tlocal inventoryTransferData_intermediate = {}\n\t\t\t\t\t\t\t\t\tinventoryTransferData_intermediate.id \t\t= inventoryTransferData.id\n\t\t\t\t\t\t\t\t\tinventoryTransferData_intermediate.position = inventorySlotData.position\n\n\t\t\t\t\t\t\t\t-- copy over the item metadata\n\t\t\t\t\t\t\t\tfor metadataName, metadataValue in pairs(inventorySlotData) do\n\t\t\t\t\t\t\t\t\tif not inventoryTransferData_intermediate[metadataName] then\n\t\t\t\t\t\t\t\t\t\tif type(metadataValue) == \"table\" then\n\t\t\t\t\t\t\t\t\t\t\tinventoryTransferData_intermediate[metadataName] = utilities.copyTable(metadataValue)\n\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\tinventoryTransferData_intermediate[metadataName] = metadataValue\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tinventoryTransferData_intermediate.stacks = stacksToRemove\n\n\t\t\t\t\t\t\t\t-- flag this as taken!\n\t\t\t\t\t\t\t\tinventoryTransferData.stacks = inventoryTransferData.stacks - stacksToRemove\n\n\t\t\t\t\t\t\t\ttable.insert(inventoryTransferData_intermediateCollection, inventoryTransferData_intermediate)\n\n\t\t\t\t\t\t\t\tif inventoryTransferData.stacks == 0 then\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tif inventorySlotData.position == inventoryTransferData.position then\n\t\t\t\t\t\t\t\tlocal inventoryTransferData_intermediate = {}\n\t\t\t\t\t\t\t\t\tinventoryTransferData_intermediate.id \t\t= inventoryTransferData.id\n\t\t\t\t\t\t\t\t\tinventoryTransferData_intermediate.position = inventoryTransferData.position\n\n\t\t\t\t\t\t\t\t-- copy over the item metadata\n\t\t\t\t\t\t\t\tfor metadataName, metadataValue in pairs(inventorySlotData) do\n\t\t\t\t\t\t\t\t\tif not inventoryTransferData_intermediate[metadataName] then\n\t\t\t\t\t\t\t\t\t\tif type(metadataValue) == \"table\" then\n\t\t\t\t\t\t\t\t\t\t\tinventoryTransferData_intermediate[metadataName] = utilities.copyTable(metadataValue)\n\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\tinventoryTransferData_intermediate[metadataName] = metadataValue\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\ttable.insert(inventoryTransferData_intermediateCollection, inventoryTransferData_intermediate)\n\n\t\t\t\t\t\t\t\t-- flag this as taken!\n\t\t\t\t\t\t\t\tinventoryTransferData.stacks = 0\n\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\t--  [{\"position\":2,\"stacks\":1,\"id\":3}, {\"position\":1,\"stacks\":1,\"id\":8}, {\"position\":1,\"stacks\":1,\"id\":3}]\n\t\t\t\tif inventoryTransferData.stacks == 0 then\n\t\t\t\t\t-- pop it from the table, its all good\n\t\t\t\t\t-- table.remove(inventoryTransferDataCollection_list, i)\n\t\t\t\telse\n\t\t\t\t\t-- didn't get all the stacks for this item! deny it.\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\telse\n\t\t\t\t-- invalid item, either stacks <= 0 or id is invalid\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\tlocal isGood = true\n\t\tfor i, v in pairs(inventoryTransferDataCollection_list) do\n\t\t\tif (v.stacks or 1) > 0 then\n\t\t\t\tisGood = false\n\t\t\tend\n\t\tend\n\n\t\t-- if everything went well, #inventoryTransferDataCollection_list == 0\n\t\t-- this function returns true in arg#2 if there was an error\n\t\treturn inventoryTransferData_intermediateCollection, not isGood--#inventoryTransferDataCollection_list > 0 --successful_conversions ~= #inventoryTransferDataCollection or isDataMalformed or wasInventoryTransferDataModified\n\tend\n\n\treturn nil, true\nend\n\n-- CONTINUE NPC TRADING HERE!!!!!!\nlocal function int__doesPlayerHaveInventorySpaceForTrade(player, inventoryTransferData_intermediateCollection_losing, inventoryTransferData_intermediateCollection_gaining)\n\t-- reminder: respect category\n\tlocal availableSlots = {}\n\tlocal necessarySlots = {}\n\n\tfor i_category = 1, #CATEGORIES do\n\t\tavailableSlots[CATEGORIES[i_category]] = MAX_NUMBER_SLOTS_PER_CATEGORY\n\tend\n\n\tlocal playerData = playerDataContainer[player]\n\tif playerData then\n\t\tfor trueInventorySlotDataPosition, inventorySlotData in pairs(playerData.inventory) do\n\t\t\tlocal itemBaseData = itemLookup[inventorySlotData.id]\n\n\t\t\tif itemBaseData then\n\t\t\t\tavailableSlots[itemBaseData.category] = availableSlots[itemBaseData.category] - 1\n\t\t\telse\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\tend\n\n\tfor i, inventoryTransferData_intermediate in pairs(inventoryTransferData_intermediateCollection_losing) do\n\t\tlocal itemBaseData = itemLookup[inventoryTransferData_intermediate.id]\n\n\t\tif itemBaseData then\n\t\t\tavailableSlots[itemBaseData.category] = availableSlots[itemBaseData.category] + 1\n\t\telse\n\t\t\treturn false\n\t\tend\n\tend\n\n\tlocal representation_inventoryTransferData_intermediateCollection_gaining = utilities.copyTable(inventoryTransferData_intermediateCollection_gaining)\n\tlocal playerInventoryClone = utilities.copyTable(playerData.inventory)\n\n\twhile true do\n\t\tlocal madeChange = false\n\n\t\tfor i, inventoryTransferData_intermediate in pairs(representation_inventoryTransferData_intermediateCollection_gaining) do\n\t\t\tlocal itemBaseData = itemLookup[inventoryTransferData_intermediate.id]\n\n\t\t\tif not inventoryTransferData_intermediate.stacks then\n\t\t\t\tinventoryTransferData_intermediate.stacks = 1\n\t\t\tend\n\n\t\t\tif itemBaseData then\n\t\t\t\tif itemBaseData.canStack then\n\t\t\t\t\tfor _, playerInventorySlotData in pairs(playerInventoryClone) do\n\t\t\t\t\t\tif playerInventorySlotData.id == inventoryTransferData_intermediate.id and playerInventorySlotData.stacks <= (itemBaseData.stackSize or MAX_COUNT_PER_STACK) then\n\t\t\t\t\t\t\tlocal stacksToAdd = math.clamp(inventoryTransferData_intermediate.stacks, 0, (itemBaseData.stackSize or MAX_COUNT_PER_STACK) - playerInventorySlotData.stacks)\n\n\t\t\t\t\t\t\tinventoryTransferData_intermediate.stacks \t= inventoryTransferData_intermediate.stacks - stacksToAdd\n\t\t\t\t\t\t\tplayerInventorySlotData.stacks \t\t\t\t= playerInventorySlotData.stacks + stacksToAdd\n\n\t\t\t\t\t\t\tif inventoryTransferData_intermediate.stacks == 0 then\n\t\t\t\t\t\t\t\ttable.remove(representation_inventoryTransferData_intermediateCollection_gaining, i)\n\n\t\t\t\t\t\t\t\tmadeChange = true\n\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif madeChange then\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\tif not madeChange then\n\t\t\tbreak\n\t\tend\n\tend\n\n\tfor i, inventoryTransferData_intermediate in pairs(representation_inventoryTransferData_intermediateCollection_gaining) do\n\t\tlocal itemBaseData = itemLookup[inventoryTransferData_intermediate.id]\n\n\t\tif itemBaseData then\n\t\t\tif itemBaseData.canStack then\n\t\t\t\tavailableSlots[itemBaseData.category] = availableSlots[itemBaseData.category] - 1\n\t\t\telse\n\t\t\t\tavailableSlots[itemBaseData.category] = availableSlots[itemBaseData.category] - inventoryTransferData_intermediate.stacks\n\t\t\tend\n\n\t\t\tnecessarySlots[itemBaseData.category] = true\n\t\telse\n\t\t\treturn false\n\t\tend\n\tend\n\n\t-- check if player can do the trade based on their slots!\n\tfor category, _ in pairs(necessarySlots) do\n\t\t-- NOTE: we are not doing <= 0 because if we do, it doesn't\n\t\t-- acount for the scenario where we are at 24 slots available\n\t\t-- and gaining 1 item. if we have all items full, and only gain 1\n\t\t-- itll be -1 so it wont pass through.\n\t\tif availableSlots[category] < 0 then\n\t\t\treturn false\n\t\tend\n\tend\n\n\treturn true\nend\n\n-- todo: merge into already present stacks!\nlocal function grantPlayerItemsByInventoryTranferData_intermediateCollection(player, inventoryTransferDataCollection_g)\n\tlocal playerData = playerDataContainer[player]\n\tlocal inventoryTransferDataCollection = utilities.copyTable(inventoryTransferDataCollection_g)\n\n\tif playerData then\n\t\tlocal hasInventoryChanged = false\n\n\t\tfor i, inventoryTransferData in pairs(inventoryTransferDataCollection) do\n\t\t\tlocal itemBaseData = itemLookup[inventoryTransferData.id]\n\n\t\t\t-- set this for the lazy\n\t\t\tif not inventoryTransferData.stacks then\n\t\t\t\tinventoryTransferData.stacks = 1\n\t\t\tend\n\n\t\t\tif itemBaseData and itemBaseData.canStack then\n\t\t\t\tfor trueInventorySlotDataPosition, inventorySlotData in pairs(playerData.inventory) do\n\t\t\t\t\tif inventorySlotData.id == inventoryTransferData.id then\n\t\t\t\t\t\tif (inventorySlotData.stacks + inventoryTransferData.stacks) <= (itemBaseData.stackSize or MAX_COUNT_PER_STACK) then\n\t\t\t\t\t\t\tinventorySlotData.stacks = inventorySlotData.stacks + inventoryTransferData.stacks\n\n\t\t\t\t\t\t\tnetwork:fire(\"questTriggerOccurred\", player, \"item-collected\", {id = itemBaseData.id; amount = 1}) -- amount is irrelevant to the check\n\t\t\t\t\t\t\t-- flag as finished item\n\t\t\t\t\t\t\tinventoryTransferData.stacks = 0\n\n\t\t\t\t\t\t\t-- flag as changed!\n\t\t\t\t\t\t\thasInventoryChanged = true\n\n\t\t\t\t\t\t\t-- move to next item!\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\telseif inventorySlotData.stacks < (itemBaseData.stackSize or MAX_COUNT_PER_STACK) then\n\t\t\t\t\t\t\tlocal diff = (itemBaseData.stackSize or MAX_COUNT_PER_STACK) - inventorySlotData.stacks\n\n\t\t\t\t\t\t\tif inventoryTransferData.stacks >= diff then\n\t\t\t\t\t\t\t\tinventorySlotData.stacks \t\t= inventorySlotData.stacks + diff\n\t\t\t\t\t\t\t\tinventoryTransferData.stacks \t= inventoryTransferData.stacks - diff\n\n\n\t\t\t\t\t\t\t\thasInventoryChanged = true\n\n\t\t\t\t\t\t\t\tif inventoryTransferData.stacks == 0 then\n\t\t\t\t\t\t\t\t\tnetwork:fire(\"questTriggerOccurred\", player, \"item-collected\", {id = itemBaseData.id; amount = 1})\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tfor _, inventoryTransferData in pairs(inventoryTransferDataCollection) do\n\t\t\tif inventoryTransferData.stacks > 0 then\n\t\t\t\t-- grant the item\n\t\t\t\tlocal itemBaseData = itemLookup[inventoryTransferData.id]\n\n\t\t\t\tif itemBaseData then\n\t\t\t\t\t-- make sure the id is the integer id, not the string id\n\t\t\t\t\tinventoryTransferData.id = itemBaseData.id\n\n\n\t\t\t\t\tif itemBaseData.canStack then\n\t\t\t\t\t\tlocal itemStackSize = itemBaseData.stackSize or 99\n\t\t\t\t\t\tlocal stacksNeeded \t= inventoryTransferData.stacks\n\n\t\t\t\t\t\twhile stacksNeeded > 0 do\n\t\t\t\t\t\t\tif stacksNeeded >= itemStackSize then\n\t\t\t\t\t\t\t\tlocal itemToAdd \t= utilities.copyTable(inventoryTransferData)\n\t\t\t\t\t\t\t\titemToAdd.stacks \t= itemStackSize\n\t\t\t\t\t\t\t\tstacksNeeded \t\t= stacksNeeded - itemStackSize\n\n\t\t\t\t\t\t\t\ttable.insert(playerData.inventory, itemToAdd)\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tlocal itemToAdd \t= utilities.copyTable(inventoryTransferData)\n\t\t\t\t\t\t\t\titemToAdd.stacks \t= stacksNeeded\n\t\t\t\t\t\t\t\tstacksNeeded \t\t= 0\n\t\t\t\t\t\t\t\ttable.insert(playerData.inventory, itemToAdd)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tinventoryTransferData.stacks = 0\n\n\t\t\t\t\t\tnetwork:fire(\"questTriggerOccurred\", player, \"item-collected\", {id = itemBaseData.id; amount = 1})\n\t\t\t\t\t\t-- flip flag\n\t\t\t\t\t\thasInventoryChanged = true\n\t\t\t\t\telse\n\t\t\t\t\t\t-- todo look to remove this for loop!\n\t\t\t\t\t\tfor i = 1, inventoryTransferData.stacks or 1 do\n\t\t\t\t\t\t\tlocal newInventorySlotData = {\n\t\t\t\t\t\t\t\tid \t\t\t\t= inventoryTransferData.id;\n\t\t\t\t\t\t\t\tstacks \t\t\t= 1;\n\t\t\t\t\t\t\t\tmodifierData \t= inventoryTransferData.modifierData;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tfor i, v in pairs(inventoryTransferData) do\n\t\t\t\t\t\t\t\tif not newInventorySlotData[i] then\n\t\t\t\t\t\t\t\t\tif type(v) == \"table\" then\n\t\t\t\t\t\t\t\t\t\tnewInventorySlotData[i] = utilities.copyTable(v)\n\t\t\t\t\t\t\t\t\telseif i ~= \"stacks\" then\n\t\t\t\t\t\t\t\t\t\tnewInventorySlotData[i] = v\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\ttable.insert(playerData.inventory, newInventorySlotData)\n\t\t\t\t\t\t\tnetwork:fire(\"questTriggerOccurred\", player, \"item-collected\", {id = itemBaseData.id; amount = 1})\n\t\t\t\t\t\t\t-- flip flag\n\t\t\t\t\t\t\thasInventoryChanged = true\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif hasInventoryChanged then\n\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"inventory\")\n\t\tend\n\n\t\treturn true\n\tend\n\n\treturn false\nend\n\n-- reminder: inventoryTransferData and inventoryTransferData_intermediate can both be used. Since we don't care about modifierData\n-- they're pretty much identical.\nlocal function revokePlayerItemsByInventoryTransferDataCollection(player, inventoryTransferDataCollection_r)\n\tlocal playerData = playerDataContainer[player]\n\tlocal inventoryTransferDataCollection = utilities.copyTable(inventoryTransferDataCollection_r)\n\n\tif playerData then\n\t\tlocal hasInventoryChanged = false\n\n\t\tfor _, inventoryTransferData in pairs(inventoryTransferDataCollection) do\n\t\t\tlocal itemBaseData = itemLookup[inventoryTransferData.id]\n\n\t\t\t-- set this for the lazy\n\t\t\tif not inventoryTransferData.stacks then\n\t\t\t\tinventoryTransferData.stacks = 1\n\t\t\tend\n\n\t\t\tif itemBaseData and itemBaseData.canStack then\n\t\t\t\twhile inventoryTransferData.stacks > 0 do\n\t\t\t\t\tlocal tookStacksThisCycle = false\n\n\t\t\t\t\tfor trueInventorySlotPosition, inventorySlotData in pairs(playerData.inventory) do\n\t\t\t\t\t\tif inventorySlotData.id == inventoryTransferData.id then\n\t\t\t\t\t\t\t-- drop the item\n\t\t\t\t\t\t\tif inventorySlotData.stacks > inventoryTransferData.stacks then\n\t\t\t\t\t\t\t\tinventorySlotData.stacks \t\t= inventorySlotData.stacks - inventoryTransferData.stacks\n\t\t\t\t\t\t\t\tinventoryTransferData.stacks \t= 0\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tinventoryTransferData.stacks = inventoryTransferData.stacks - inventorySlotData.stacks\n\n\t\t\t\t\t\t\t\ttable.remove(playerData.inventory, trueInventorySlotPosition)\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t-- flip flag\n\t\t\t\t\t\t\thasInventoryChanged = true\n\t\t\t\t\t\t\ttookStacksThisCycle = true\n\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif not tookStacksThisCycle then\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telse\n\t\t\t\t-- non-stackable item revoke, only one stack (entire item)\n\t\t\t\tinventoryTransferData.stacks = 1\n\n\t\t\t\tfor trueInventorySlotPosition, inventorySlotData in pairs(playerData.inventory) do\n\t\t\t\t\tif inventorySlotData.position == inventoryTransferData.position and inventorySlotData.id == inventoryTransferData.id then\n\t\t\t\t\t\t-- revoke item\n\t\t\t\t\t\ttable.remove(playerData.inventory, trueInventorySlotPosition)\n\n\t\t\t\t\t\t-- flag is taken\n\t\t\t\t\t\tinventoryTransferData.stacks = 0\n\n\t\t\t\t\t\t-- mark as changed\n\t\t\t\t\t\thasInventoryChanged = true\n\n\t\t\t\t\t\t-- move to next item\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif hasInventoryChanged then\n\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"inventory\")\n\t\tend\n\n\t\treturn true\n\tend\n\n\treturn false\nend\n\nlocal function int__transferInventoryToStorage(player, inventorySlotData)\n\tlocal playerData = playerDataContainer[player]\n\n\tif playerData then\n\t\tlocal inventoryTransferData_intermediateCollection_player, wasInventoryTransferDataModified_player \t= int__getInventoryTransferData_intermediateCollectionFromInventoryTransferDataCollection(player, {inventorySlotData})\n\n\t\tif not wasInventoryTransferDataModified_player then\n\t\t\tfor i, inventoryTransferData in pairs(inventoryTransferData_intermediateCollection_player) do\n\t\t\t\tlocal itemBaseData = itemLookup[inventoryTransferData.id]\n\t\t\t\tif inventoryTransferData.questBound or (itemBaseData and itemBaseData.questBound) then\n\t\t\t\t\treturn false, \"Item is quest bound\"\n\t\t\t\tend\n\t\t\tend\n\n\t\t\trevokePlayerItemsByInventoryTransferDataCollection(player, inventoryTransferData_intermediateCollection_player)\n\n\t\t\tlocal copy_inventoryTransferData_intermediateCollection_player = utilities.copyTable(inventoryTransferData_intermediateCollection_player)\n\n\t\t\t-- merge itemStorage stacks if possible\n\t\t\twhile true do\n\t\t\t\tlocal hasChanged = false\n\t\t\t\tfor i, inventoryTransferData in pairs(copy_inventoryTransferData_intermediateCollection_player) do\n\t\t\t\t\tlocal itemBaseData = itemLookup[inventoryTransferData.id]\n\n\t\t\t\t\tif itemBaseData.canStack then\n\t\t\t\t\t\tfor ii, itemStorageSlotData in pairs(playerData.globalData.itemStorage) do\n\t\t\t\t\t\t\tlocal maxStackSizeForItem = itemBaseData.stackSize or MAX_COUNT_PER_STACK\n\n\t\t\t\t\t\t\tif inventoryTransferData.id == itemStorageSlotData.id and itemStorageSlotData.stacks < maxStackSizeForItem then\n\t\t\t\t\t\t\t\tlocal amountFromMaxStacks = (itemStorageSlotData.stacks + inventoryTransferData.stacks) - maxStackSizeForItem\n\n\t\t\t\t\t\t\t\tif amountFromMaxStacks > 0 then\n\t\t\t\t\t\t\t\t\t-- this is all overflow\n\t\t\t\t\t\t\t\t\titemStorageSlotData.stacks \t\t= amountFromMaxStacks\n\t\t\t\t\t\t\t\t\tinventoryTransferData.stacks \t= maxStackSizeForItem\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t-- this is all underflow, so it merged completely.\n\t\t\t\t\t\t\t\t\titemStorageSlotData.stacks \t\t= itemStorageSlotData.stacks + inventoryTransferData.stacks\n\t\t\t\t\t\t\t\t\tinventoryTransferData.stacks \t= 0\n\n\t\t\t\t\t\t\t\t\ttable.remove(copy_inventoryTransferData_intermediateCollection_player, i)\n\n\t\t\t\t\t\t\t\t\thasChanged = true\n\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif hasChanged then\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif not hasChanged then\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tfor i, itemSlotData in pairs(copy_inventoryTransferData_intermediateCollection_player) do\n\t\t\t\ttable.insert(playerData.globalData.itemStorage, itemSlotData)\n\t\t\tend\n\n\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"inventory\")\n\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"globalData\")\n\n\t\t\treturn true, \"Successfully transfered to storage.\"\n\t\telse\n\t\t\treturn false, \"Failed find item.\"\n\t\tend\n\tend\n\n\treturn false, \"PlayerData not found.\"\nend\n\nlocal function int__transferStorageToInventory(player, storageSlotData)\n\tlocal playerData = playerDataContainer[player]\n\n\tif playerData then\n\t\tlocal player_hasInventorySpace = int__doesPlayerHaveInventorySpaceForTrade(player, {}, {storageSlotData})\n\n\t\tif player_hasInventorySpace then\n\t\t\tlocal wasStorageSlotDataRemoved, removeSlotData = false, nil do\n\t\t\t\tfor i, storageSlot in pairs(playerData.globalData.itemStorage) do\n\t\t\t\t\tif storageSlot.id == storageSlotData.id and i == storageSlotData.position then\n\t\t\t\t\t\tremoveSlotData \t\t\t\t= table.remove(playerData.globalData.itemStorage, i)\n\t\t\t\t\t\twasStorageSlotDataRemoved \t= true\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif wasStorageSlotDataRemoved then\n\t\t\t\ttable.insert(playerData.inventory, removeSlotData)\n\n\t\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"inventory\")\n\t\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"globalData\")\n\n\t\t\t\treturn true, \"Successfully transfered to inventory.\"\n\t\t\telse\n\t\t\t\treturn false, \"Failed to find in storage.\"\n\t\t\tend\n\t\telse\n\t\t\treturn false, \"Player does not have space in inventory.\"\n\t\tend\n\tend\n\n\treturn false, \"PlayerData not found.\"\nend\n\nlocal function updateInventorySlots(player)\n\tlocal playerData = playerDataContainer[player]\n\n\tif playerData then\n\t\tlocal availableSlots = {}\n\n\t\tfor i_category = 1, #CATEGORIES do\n\t\t\tavailableSlots[CATEGORIES[i_category]] = {}\n\t\t\tfor i_slot = 1, MAX_NUMBER_SLOTS_PER_CATEGORY do\n\t\t\t\tavailableSlots[CATEGORIES[i_category]][i_slot] = true\n\t\t\tend\n\t\tend\n\n\t\tfor i, inventorySlotData in pairs(playerData.inventory) do\n\t\t\tlocal itemBaseData = itemLookup[inventorySlotData.id]\n\t\t\tif itemBaseData then\n\t\t\t\t-- make sure the slots have stacks value\n\t\t\t\tif not inventorySlotData.stacks then\n\t\t\t\t\tinventorySlotData.stacks = 1\n\t\t\t\tend\n\n\t\t\t\t-- TEMP DISABLED WHILE CONVERTING OLD STACK SIZES TO NEW ONES\n\t\t\t\t-- make sure the slots are at their valid stack value and reset if not\n\t\t\t\t--if inventorySlotData.stacks >= (itemBaseData.stackSize or MAX_COUNT_PER_STACK) then\n\t\t\t\t--\tinventorySlotData.stacks = itemBaseData.stackSize or MAX_COUNT_PER_STACK\n\t\t\t\t--end\n\n\t\t\t\tif availableSlots[itemBaseData.category] then\n\t\t\t\t\tif inventorySlotData.position and availableSlots[itemBaseData.category][inventorySlotData.position] then\n\t\t\t\t\t\tavailableSlots[itemBaseData.category][inventorySlotData.position] = false\n\t\t\t\t\telseif inventorySlotData.position then\n\t\t\t\t\t\t-- item trying to take up same category+position as another item\n\t\t\t\t\t\t-- reset its position\n\t\t\t\t\t\tinventorySlotData.position = nil\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\t-- item with no category, kick it to the curb\n\t\t\t\t\ttable.remove(playerData.inventory, i)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\t-- no itemBaseData, kick it to the curb\n\t\t\t\ttable.remove(playerData.inventory, i)\n\t\t\tend\n\t\tend\n\n\t\tlocal storageTransferItems = {}\n\n\t\t-- so gross we have to do this cus tables dont autoscale\n\t\twhile true do\n\t\t\tlocal madeChange = false\n\t\t\tfor i, inventorySlotData in pairs(playerData.inventory) do\n\t\t\t\tif not inventorySlotData.position then\n\t\t\t\t\t-- find a slot for this\n\t\t\t\t\tlocal itemBaseData \t= itemLookup[inventorySlotData.id]\n\t\t\t\t\tlocal slot \t\t\t= getAvailableSlot(availableSlots[itemBaseData.category])\n\n\t\t\t\t\tif slot then\n\t\t\t\t\t\tinventorySlotData.position = slot\n\t\t\t\t\t\tavailableSlots[itemBaseData.category][slot] = false\n\t\t\t\t\telse\n\t\t\t\t\t\t-- no open slot, kick it to the curb\n\t\t\t\t\t\twarn(\"moving item to storage due to lack of inv space\")\n\n\t\t\t\t\t\tlocal success, reason = int__transferInventoryToStorage(player, inventorySlotData)\n\t\t\t\t\t\twarn(\"INV->STOR\", success, reason)\n\n\t\t\t\t\t\tif not success then\n\t\t\t\t\t\t\ttable.remove(playerData.inventory, i)\n\t\t\t\t\t\t\twarn(\"taking out and wiping it\")\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tmadeChange = true\n\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif not madeChange then\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\n\t\t-- try to ensure that stuff is gotten to in order\n\t\t-- if a.position and b.position then\n\t\t\t-- ^ fix for https://forum.playvesteria.com/t/game-breaking-glitch/2991\n\t\ttable.sort(playerData.inventory, function(a, b)\n\t\t\tif a.position and b.position then\n\t\t\t\treturn a.position > b.position\n\t\t\tend\n\t\t\t-- default\n\t\t\treturn false\n\t\tend)\n\tend\nend\n\n-- true \t= its completed\n-- false \t= its being worked on\n-- nil \t\t= not assigned\n\nlocal function autoSavePlayerData(player)\n\tif player.Parent ~= game.Players or player:FindFirstChild(\"DataLoaded\") == nil or player:FindFirstChild(\"teleporting\") or playerDataContainer[player] == nil then\n\t\treturn false\n\tend\n\n\t-- autosave ongoing statusEffects\n\tif player:FindFirstChild(\"entityGUID\") then\n\t\tlocal statusEffects = network:invoke(\"playerRemovingPackageStatusEffects\", player)\n\n\t\tif statusEffects then\n\t\t\tplayerDataContainer[player].packagedStatusEffects = statusEffects\n\t\tend\n\tend\n\n\tlocal playerId = player.userId\n\n\tlocal Success, Error, TimeStamp = datastoreInterface:updatePlayerSaveFileData(playerId, playerDataContainer[player])\n\tif Success then\n\t\tif player:FindFirstChild(\"DataSaveFailed\") then\n\t\t\tplayer.DataSaveFailed:Destroy()\n\t\tend\n\telse\n\t\twarn(player.Name,\"'s data failed to save.\",Error)\n\t\tnetwork:invoke(\"reportError\", player, \"error\", \"Failed to save player data: \"..Error)\n\t\tnetwork:invoke(\"reportAnalyticsEvent\",player,\"data:fail:save\")\n\t\tif player:FindFirstChild(\"DataSaveFailed\") == nil then\n\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\ttag.Name = \"DataSaveFailed\"\n\t\t\ttag.Parent = player\n\t\tend\n\t\tnetwork:fireClient(\"alertPlayerNotification\", player, {\n\t\t\ttext = \"Failed to save data: \"..Error;\n\t\t\ttextColor3 = Color3.fromRGB(255, 57, 60)\n\t\t})\n\tend\n\n\t-- get rid of packagedstatuseffects\n\tif playerDataContainer[player] then\n\t\tplayerDataContainer[player].packagedStatusEffects = nil\n\tend\n\n\treturn Success, TimeStamp\nend\n\nlocal playerStatTypes = {\"dex\", \"int\", \"vit\", \"str\"}\n\nlocal function VALIDATE_SECTION_FOR_CHEAT_WEAPONS(section, holding)\n\tlocal enchantmentMapping ={\n\t\t[\"0.05\"] = 1.0;\n\t\t[\"0.075\"] = 0.7;\n\t\t[\"0.1\"] = 0.1;\n\t}\n\n\tfor i, inventorySlotData in pairs(section) do\n\t\tlocal weaponEnchantmentSuccess \t= 1\n\t\tlocal hasUnrealisticIncrease \t= false\n\t\tif inventorySlotData.successfulUpgrades and inventorySlotData.successfulUpgrades > 0 then\n\t\t\tlocal itemBaseData = itemLookup[inventorySlotData.id]\n\t\t\tif itemBaseData.baseDamage then\n\t\t\t\tfor ii, modifierData in pairs(inventorySlotData.modifierData) do\n\t\t\t\t\tif modifierData.baseDamage then\n\t\t\t\t\t\tlocal percentIncrease = tostring(math.floor(modifierData.baseDamage / itemBaseData.baseDamage / 0.025) * 0.025)\n\t\t\t\t\t\tif enchantmentMapping[percentIncrease] then\n\t\t\t\t\t\t\tweaponEnchantmentSuccess = weaponEnchantmentSuccess * enchantmentMapping[percentIncrease]\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\twarn(inventorySlotData.id, \"@\", inventorySlotData.position, \"has weird scaling (\", percentIncrease,\"% increase)\", modifierData.baseDamage, \"baseDamage increase\")\n\n\t\t\t\t\t\t\t--hasUnrealisticIncrease = true\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif weaponEnchantmentSuccess * 100 <= 0.5 or hasUnrealisticIncrease then\n\t\t\ttable.remove(section, i)\n\t\t\ttable.insert(holding, inventorySlotData)\n\n\t\t\twarn(\"id\", inventorySlotData.id, \"@\", inventorySlotData.position, \"was revoked\", \"(\" .. weaponEnchantmentSuccess * 100 .. \"%)\")\n\t\tend\n\tend\nend\n\nlocal function completelyNukeSaveFile(player, playerData)\n\tplayerData.level \t\t= 1\n\tplayerData.exp \t\t\t= 0\n\tplayerData.equipment \t= {}\n\tplayerData.inventory \t= {}\n\tplayerData.abilities \t= {}\n\tplayerData.abilityBooks = {}\n\tplayerData.statistics \t= {}\n\tplayerData.gold \t\t= 0\n\tplayerData.class \t\t= \"Adventurer\"\nend\n\nlocal function onLogPerkActivation_server(player, equipmentId)\n\tlocal playerData = playerDataContainer[player]\n\n\tif playerData then\n\t\tplayerData.nonSerializeData.perksActivated[tostring(equipmentId)] = tick()\n\tend\nend\n\n\nlocal function flagCheck(player, playerData)\n\n\tif not playerData.flags.arrowChangeXD then\n\t\tplayerData.flags.arrowChangeXD = true\n\n\t\tlocal trueEquip do\n\t\t\tfor i, equip in pairs(playerData.equipment) do\n\t\t\t\tif equip.position == mapping.equipmentPosition.arrow then\n\t\t\t\t\ttrueEquip = equip\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif trueEquip and trueEquip.id == nil then\n\t\t\ttrueEquip.id = 87\n\t\telseif not trueEquip then\n\t\t\ttable.insert(playerData.equipment, {\n\t\t\t\tid \t\t\t= 87;\n\t\t\t\tposition \t= mapping.equipmentPosition.arrow;\n\t\t\t\tstacks \t\t= 0;\n\t\t\t})\n\t\tend\n\tend\n\n\n\tif not playerData.flags.abilityReset then\n\t\tplayerData.flags.abilityReset = true\n\t\tif game.PlaceId ~= 2103419922 then\n\t\t\tplayerData.abilities = {}\n\n\t\t\tfor abilityBookName, abilityBookPlayerData in pairs(playerData.abilityBooks) do\n\t\t\t\tabilityBookPlayerData.pointsAssigned = 0\n\t\t\tend\n\n\t\t\twhile true do\n\t\t\t\tlocal madeChange = false\n\n\t\t\t\tfor i, hotbarAbilityData in pairs(playerData.hotbar) do\n\t\t\t\t\tif hotbarAbilityData.dataType == mapping.dataType.ability then\n\t\t\t\t\t\tmadeChange = true\n\n\t\t\t\t\t\ttable.remove(playerData.hotbar, i)\n\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif not madeChange then\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tplayerData.statistics.dex = 0\n\t\t\tplayerData.statistics.int = 0\n\t\t\tplayerData.statistics.str = 0\n\t\t\tplayerData.statistics.vit = 0\n\t\tend\n\n\tend\n\n\tif not playerData.flags.fixNightmareChickensEXPandInfinitePetPickup then\n\t\tplayerData.flags.fixNightmareChickensEXPandInfinitePetPickup = true\n\n\t\tif playerData.gold > 25000000 and not runService:IsStudio() then\n\t\t\tplayerData.gold = 0\n\t\tend\n\n\t\tif playerData.level == 30 or playerData.exp > levels.getEXPToNextLevel(playerData.level) then\n\t\t\tplayerData.exp = 0\n\t\tend\n\tend\n\n\tif not playerData.flags.statCheck then\n\t\tlocal needsReset = false\n\t\tlocal statSum = 0\n\n\t\t-- check individual stats\n\t\tfor i, statName in pairs(playerStatTypes) do\n\t\t\tlocal stat = playerData.statistics[statName] or 0\n\t\t\tstatSum = statSum + math.abs(stat)\n\t\tend\n\n\t\tif statSum > levels.getStatPointsForLevel(playerData.level or 1) then\n\t\t\tneedsReset = true\n\t\tend\n\n\t\tplayerData.flags.statCheck = true\n\n\t\t-- wipe stats if bad\n\t\tif needsReset then\n\t\t\tfor i, statName in pairs(playerStatTypes) do\n\t\t\t\tplayerData.statistics[statName] = 0\n\t\t\tend\n\t\tend\n\tend\n\n\tif not playerData.flags.resetQuests then\n\t\tplayerData.flags.resetQuests = true\n\n\t\tplayerData.quests = {}\n\t\t\tplayerData.quests.completed = {} --playerSaveFileData.quests.completed or {}\n\t\t\tplayerData.quests.active \t= {} --playerSaveFileData.quests.active or {}\n\tend\n\n\tif configuration.getConfigurationValue(\"doStartRevokingCheatWeapons\") then\n\t\tif not playerData.flags.revokeCheatWeapons then\n\t\t\tplayerData.flags.revokeCheatWeapons = true\n\t\t\tplayerData.holding \t\t\t\t\t= {}\n\n\t\t\tVALIDATE_SECTION_FOR_CHEAT_WEAPONS(playerData.inventory, playerData.holding)\n\t\t\tVALIDATE_SECTION_FOR_CHEAT_WEAPONS(playerData.equipment, playerData.holding)\n\t\tend\n\tend\n\n\n\n\tif not playerData.flags.resetStatPointsForV23 then\n\t\tplayerData.flags.resetStatPointsForV23 = true\n\n\t\tplayerData.statistics.dex = 0\n\t\tplayerData.statistics.int = 0\n\t\tplayerData.statistics.str = 0\n\t\tplayerData.statistics.vit = 0\n\tend\n\n\tif not playerData.flags.removeSpiderQueenCrown then\n\t\tplayerData.flags.removeSpiderQueenCrown = true\n\n\t\tfor i, inventorySlotData in pairs(playerData.inventory) do\n\t\t\tif inventorySlotData.id == 68 then\n\t\t\t\ttable.remove(playerData.inventory, i)\n\t\t\tend\n\t\tend\n\n\t\tfor i, equipmentSlotData in pairs(playerData.equipment) do\n\t\t\tif equipmentSlotData.id == 68 then\n\t\t\t\ttable.remove(playerData.equipment, i)\n\t\t\tend\n\t\tend\n\tend\nend\n\n\nlocal function onGetPlayerEquipmentDataByEquipmentPosition(player, equipmentPosition)\n\tif playerDataContainer[player] then\n\t\tfor i, equipmentData in pairs(playerDataContainer[player].equipment) do\n\t\t\tif equipmentData.position == equipmentPosition then\n\t\t\t\treturn equipmentData\n\t\t\tend\n\t\tend\n\tend\n\n\treturn nil\nend\n\n-- couldn't find a better place to put this\n-- spawn point logic\nlocal spawnPoints = Instance.new(\"Folder\")\nspawnPoints.Name = \"spawnPoints\"\n\nlocal function registerSpawnPoint(Child)\n\tif Child then\n\t\tlocal tag = Instance.new(\"CFrameValue\")\n\t\ttag.Name = ((Child.Name:lower() == \"spawnpoint\" or Child.Name:lower() == \"spawnpart\") and \"default\") or Child.Name\n\t\ttag.Value = Child.CFrame\n\t\tif Child:FindFirstChild(\"description\") then\n\t\t\tChild.description:Clone().Parent = tag\n\t\tend\n\t\ttag.Parent = spawnPoints\n\tend\nend\n\nfor i,Child in pairs(game.CollectionService:GetTagged(\"spawnPoint\")) do\n\tregisterSpawnPoint(Child)\nend\n\ngame.CollectionService:GetInstanceAddedSignal(\"spawnPoint\"):Connect(registerSpawnPoint)\n\nlocal function registerTeleportAsSpawn(Child)\n\tif Child and Child:FindFirstChild(\"teleportDestination\") then\n\n\t\tlocal tag = Instance.new(\"CFrameValue\")\n\t\tlocal destination = utilities.placeIdForGame(Child.teleportDestination.Value)\n\t\ttag.Name = tostring(destination)\n\t\ttag.Value = Child.CFrame\n\t\ttag.Parent = spawnPoints\n\n\t\tlocal isLoadBarrier = Instance.new(\"BoolValue\")\n\t\tisLoadBarrier.Name = \"isLoadBarrier\"\n\t\tisLoadBarrier.Value = true\n\t\tisLoadBarrier.Parent = tag\n\n\t\tif Child:FindFirstChild(\"ignore\") == nil then\n\t\t\tspawn(function()\n\t\t\t\tlocal description = Instance.new(\"StringValue\")\n\t\t\t\tdescription.Name = \"description\"\n\t\t\t\tdescription.Value = \"Path to \".. utilities.getPlaceName(destination)\n\t\t\t\tdescription.Parent = tag\n\t\t\tend)\n\t\tend\n\tend\nend\n\nfor i,Child in pairs(game.CollectionService:GetTagged(\"teleportPart\")) do\n\tregisterTeleportAsSpawn(Child)\nend\n\ngame.CollectionService:GetInstanceAddedSignal(\"teleportPart\"):Connect(registerTeleportAsSpawn)\n\nspawnPoints.Parent = game.ReplicatedStorage\n-- end spawn point logic\n\nlocal function signal_inputChanged(player, input)\n\tif input == \"xbox\" or input == \"mobile\" or input == \"pc\" then\n\t\tif player:FindFirstChild(\"input\") then\n\t\t\tplayer.input.Value = input\n\t\tend\n\tend\nend\n\nlocal function onPlayerAdded(player, desiredSlot, desiredTimeStamp, accessories)\n\tif player:FindFirstChild(\"DataLoaded\") or playerDataContainer[player] then\n\t\tspawn(function()\n\t\tend)\n\t\treturn false, nil, \"Data already loaded\"\n\tend\n\n\tlocal joinData = player:GetJoinData()\n\n\tlocal teleportData\n\tif joinData and joinData.TeleportData then\n\t\t-- support shared teleportData for parties\n\t\tteleportData = (joinData.TeleportData.members and joinData.TeleportData.members[player.Name]) or joinData.TeleportData\n\tend\n\n\tif not ((teleportData and teleportData.destination == game.PlaceId) or game.PlaceId == 2015602902 or game.PlaceId == 2015602902 or game.PlaceId == 2376885433 or runService:IsRunMode() or runService:IsStudio()) then\n\n\t\tif player:GetRankInGroup(4238824) < 250 then\n\t\t\t-- stress test place is always allowed\n\t\t\tif game.PlaceId ~= 2103419922 then\n\t\t\t\tplayer:Kick(\"Not authorized\")\n\t\t\t\tnetwork:invoke(\"reportError\", player, \"debug\", \"Player not authorized to join server\")\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\n\n\tend\n\n\n\n\n\n\n\n\tlocal existingAnalyticsSession\n\tlocal joinTime\n\tlocal wasReferred\n\n\tif teleportData then\n\t\tdesiredSlot = teleportData.dataSlot\n\t\tdesiredTimeStamp = teleportData.dataTimestamp\n\n\t\tif teleportData.analyticsSessionId and teleportData.joinTime then\n\t\t\texistingAnalyticsSession = teleportData.analyticsSessionId\n\t\t\tjoinTime = teleportData.joinTime\n\t\tend\n\n\t\tif teleportData.playerAccessories then\n\t\t\taccessories = teleportData.playerAccessories\n\t\tend\n\n\t\tif teleportData.partyData and teleportData.partyData.guid then\n\t\t\tnetwork:invoke(\"resumePartyAfterTeleport\", player, teleportData.partyData.guid, teleportData.partyData.partyLeaderUserId)\n\t\tend\n\n\t\tif teleportData.wasReferred then\n\t\t\twasReferred = true\n\t\tend\n\n\t\tlocal location = teleportData.arrivingFrom\n\t\tif location and location ~= 2376885433 and location ~= 2015602902 then\n\t\t\tspawn(function()\n\t\t\t\tlocal placeName = utilities.getPlaceName(location)\n\t\t\t\tif teleportData.teleportType and teleportData.teleportType == \"rune\" then\n\t\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\t\t\tText = player.Name .. \" arrived from \" .. placeName .. \" via a magical rune.\";\n\t\t\t\t\t\tFont = Enum.Font.SourceSansBold;\n\t\t\t\t\t\tColor = Color3.fromRGB(45, 87, 255)\n\t\t\t\t\t})\n\t\t\t\telseif teleportData.teleportType and teleportData.teleportType == \"death\" then\n\t\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\t\t\tText = player.Name .. \" escaped from \" .. placeName .. \".\";\n\t\t\t\t\t\tFont = Enum.Font.SourceSansBold;\n\t\t\t\t\t\tColor = Color3.fromRGB(45, 87, 255)\n\t\t\t\t\t})\n\t\t\t\telseif teleportData.teleportType and teleportData.teleportType == \"taxi\" then\n\t\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\t\t\tText = player.Name .. \" arrived from \" .. placeName .. \" via Taximan Dave.\";\n\t\t\t\t\t\tFont = Enum.Font.SourceSansBold;\n\t\t\t\t\t\tColor = Color3.fromRGB(45, 87, 255)\n\t\t\t\t\t})\n\t\t\t\telse\n\t\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\t\t\tText = player.Name .. \" arrived from \" .. placeName .. \".\";\n\t\t\t\t\t\tFont = Enum.Font.SourceSansBold;\n\t\t\t\t\t\tColor = Color3.fromRGB(45, 87, 255)\n\t\t\t\t\t})\n\t\t\t\tend\n\n\n\t\t\tend)\n\t\telse\n\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\tText = player.Name .. \" connected.\";\n\t\t\t\tFont = Enum.Font.SourceSansBold;\n\t\t\t\tColor = Color3.fromRGB(45, 87, 255)\n\t\t\t})\n\t\tend\n\tend\n\n\t-- defaults\n\tdesiredSlot = desiredSlot or 1\n\n\tlocal success, playerData, errorMsg = datastoreInterface:getPlayerSaveFileData(player, desiredSlot, desiredTimeStamp)\n\n\tif not success then\n\t\twarn(\"Failed to load \"..player.Name..\"'s data from slot \"..desiredSlot..\" (\"..errorMsg..\")\")\n\t\tnetwork:invoke(\"reportError\", player, \"error\", \"Failed to load player data: \"..errorMsg)\n\t\tnetwork:invoke(\"reportAnalyticsEvent\",player,\"data:fail:load\")\n\t\treturn false, nil, errorMsg\n\tend\n\n\tif playerData == nil then\n\t\twarn(\"Player data is nil???\")\n\tend\n\n\tif game.PlaceId == 2376885433 or game.PlaceId == 2015602902 or game.PlaceId == 4623219432 then\n\t\t-- lobby server, return data to player\n\t\treturn playerData\n\tend\n\n\tlocal professionTag = Instance.new(\"IntValue\")\n\tprofessionTag.Name = \"professions\"\n\n\tspawn(function()\n\t\tif game.MarketplaceService:UserOwnsGamePassAsync(player.userId, 7785243) or game:GetService(\"RunService\"):IsStudio() then\n\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\ttag.Name = \"bountyHunter\"\n\t\t\ttag.Parent = player\n\t\tend\n\tend)\n\n\tif game.ReplicatedStorage.professionLookup then\n\t\tfor i, profession in pairs(game.ReplicatedStorage.professionLookup:GetChildren()) do\n\t\t\tif profession:IsA(\"ModuleScript\") then\n\t\t\t\tplayerData.professions[profession.Name] = playerData.professions[profession.Name] or {level = 1, exp = 0}\n\t\t\t\tlocal tag = Instance.new(\"IntValue\")\n\t\t\t\ttag.Name = profession.Name\n\t\t\t\ttag.Value = playerData.professions[profession.Name].level\n\t\t\t\ttag.Parent = professionTag\n\t\t\tend\n\t\tend\n\tend\n\n\tprofessionTag.Parent = player\n\n\tlocal levelTag = Instance.new(\"IntValue\")\n\tlevelTag.Name = \"level\"\n\tlevelTag.Value = playerData.level\n\tlevelTag.Parent = player\n\n\tlocal moneyTag = Instance.new(\"NumberValue\")\n\tmoneyTag.Name = \"gold\"\n\tmoneyTag.Value = playerData.gold\n\tmoneyTag.Parent = player\n\n\tspawn(function()\n\t\tnetwork:invoke(\"reportAnalyticsEvent\",player,\"level:lvl\"..tostring(playerData.level),playerData.level)\n\tend)\n\tlocal inputTag = Instance.new(\"StringValue\")\n\tinputTag.Name = \"input\"\n\tinputTag.Parent = player\n\n\tlocal classTag = Instance.new(\"StringValue\")\n\tclassTag.Name = \"class\"\n\tclassTag.Value = playerData.class\n\tclassTag.Parent = player\n\n\tlocal pvpTag = Instance.new(\"BoolValue\")\n\tpvpTag.Name = \"isPVPEnabled\"\n\tpvpTag.Value = false\n\tpvpTag.Parent = player\n\n\tlocal pvpTag = Instance.new(\"BoolValue\")\n\tpvpTag.Name = \"isInPVP\"\n\tpvpTag.Value = false\n\tpvpTag.Parent = player\n\n\tlocal entityGUIDTag = Instance.new(\"StringValue\")\n\tentityGUIDTag.Name = \"entityGUID\"\n\tentityGUIDTag.Value = HttpService:GenerateGUID(false)\n\tentityGUIDTag.Parent = player\n\n\tlocal playerSpawnTimeTag = Instance.new(\"IntValue\")\n\tplayerSpawnTimeTag.Name = \"playerSpawnTime\"\n\tplayerSpawnTimeTag.Value = os.time()\n\tplayerSpawnTimeTag.Parent = player\n\n\tlocal isPlayerSpawningTag = Instance.new(\"BoolValue\")\n\tisPlayerSpawningTag.Name = \"isPlayerSpawning\"\n\tisPlayerSpawningTag.Value = true\n\tisPlayerSpawningTag.Parent = player\n\t\tlocal isFirstTimeSpawning = Instance.new(\"BoolValue\")\n\t\tisFirstTimeSpawning.Name = \"isFirstTimeSpawning\"\n\t\tisFirstTimeSpawning.Value = true\n\t\tisFirstTimeSpawning.Parent = isPlayerSpawningTag\n\n\tlocal respawnPointTag = Instance.new(\"ObjectValue\")\n\trespawnPointTag.Name = \"respawnPoint\"\n\trespawnPointTag.Value = nil\n\trespawnPointTag.Parent = player\n\n\tif not playerData.flags.ancientsRevert then\n\t\tplayerData.flags.ancientsRevert = true\n\n\t\tfor _, v in pairs(playerData.inventory) do\n\t\t\tif v.id == 63 or v.id == 62 or v.id == 64 or v.id == 17 then -- 63, 62, 17, 64 -> 200\n\t\t\t\tv.id = 200\n\t\t\tend\n\t\tend\n\n\t\tif playerData.globalData and playerData.globalData.itemStorage then\n\t\t\tif not playerData.globalData.ancientsRevertStore then\n\t\t\t\tplayerData.globalData.ancientsRevertStore = true\n\t\t\t\tfor _, v in pairs(playerData.globalData.itemStorage) do\n\t\t\t\t\tif v.id == 63 or v.id == 62 or v.id == 64 or v.id == 17 then -- 63, 62, 17, 64 -> 200\n\t\t\t\t\t\tv.id = 200\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tif not playerData.flags.enchantWipe3 then\n\t\tplayerData.flags.enchantWipe3 = true\n\n\t\tif playerData.gold > 50000 then\n\t\t\tplayerData.gold = 50000\n\t\tend\n\n\t\tlocal function process(v)\n\t\t\tif v.modifierData then\n\t\t\t\tv.modifierData = {}\n\t\t\tend\n\t\t\tif v.upgrades then\n\t\t\t\tv.upgrades = 0\n\t\t\tend\n\t\t\tif v.successfulUpgrades then\n\t\t\t\tv.successfulUpgrades = 0\n\t\t\tend\n\t\t\tif v.blessed then\n\t\t\t\tv.blessed = nil\n\t\t\tend\n\t\t\tif v.enchantments then\n\t\t\t\tv.enchantments = nil\n\t\t\tend\n\t\tend\n\n\t\tfor _, v in pairs(playerData.equipment) do\n\t\t\tprocess(v)\n\t\tend\n\n\t\tfor _, v in pairs(playerData.inventory) do\n\t\t\tprocess(v)\n\t\tend\n\n\t\t-- for damien: this isnt how storage works pls fix it thnx\n\t\t--playerData.globalData.itemStorage\n\t\tif playerData.globalData and playerData.globalData.itemStorage then\n\t\t\tif not playerData.globalData.enchantWipe3 then\n\t\t\t\tplayerData.globalData.enchantWipe3 = true\n\t\t\t\tfor _, v in pairs(playerData.globalData.itemStorage) do\n\t\t\t\t\tprocess(v)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tplayerData.treasureChests = nil\n\tend\n\n\t-- boom boom\n\tif not playerData.hasCustomizedCharacter then\n\t\tif accessories then\n\t\t\tplayerData.accessories = accessories\n\t\telse\n\t\t\tplayerData.accessories = require(ReplicatedStorage.defaultCharacterAppearance).accessories\n\t\tend\n\t\tplayerData.hasCustomizedCharacter = true\n\tend\n\n\tplayerData.accessories.skinColorId = playerData.accessories.skinColorId or 1\n\tplayerData.accessories.hairColorId = playerData.accessories.hairColorId or 1\n\tplayerData.accessories.shirtColorId = playerData.accessories.shirtColorId or 1\n\n\n\t-- location data\n\tplayerData.locations = playerData.locations or {}\n\tif game.PrivateServerId == \"\" and game.PrivateServerOwnerId == 0 then\n\t\tplayerData.locations[tostring(game.PlaceId)] = playerData.locations[tostring(game.PlaceId)] or {}\n\t\tlocal spawns = {} do\n\t\t\tfor i, spawnPart in pairs(CollectionService:GetTagged(\"spawnPoint\")) do\n\t\t\t\tspawns[spawnPart.Name] = spawnPart\n\t\t\tend\n\t\tend\n\t\tlocal placeData = playerData.locations[tostring(game.PlaceId)]\n\t\tplaceData.visited = os.time()\n\t\tplaceData.spawns = placeData.spawns or {}\n\t\tfor spawnName, spawnDescription in pairs(placeData.spawns) do\n\t\t\tif not spawns[spawnName] then\n\t\t\t\tplaceData.spawns[spawnName] = nil\n\t\t\tend\n\t\tend\n\tend\n\n\t-- treasure data\n\tplayerData.treasure = playerData.treasure or {}\n\tplayerData.treasure[\"place-\"..game.PlaceId] = playerData.treasure[\"place-\"..game.PlaceId] or {}\n\tplayerData.treasure[\"place-\"..game.PlaceId].chests = playerData.treasure[\"place-\"..game.PlaceId].chests or {}\n\tfor i, treasureChest in pairs(game.CollectionService:GetTagged(\"treasureChest\")) do\n\t\tplayerData.treasure[\"place-\"..game.PlaceId].chests[treasureChest.Name] = playerData.treasure[\"place-\"..game.PlaceId].chests[treasureChest.Name] or {\n\t\t\topen = false\n\t\t}\n\tend\n\n\tif wasReferred then\n\t\tif not playerData.globalData.recievedReferralGift then\n\t\t\tplayerData.globalData.recievedReferralGift  = true\n\t\t\tplayerData.globalData.ethyr = (playerData.globalData.ethyr or 0) + 200\n\t\t\tnetwork:fireClient(\"signal_alertChatMessage\", player, {Text = \"Recieved 200 Ethyr referral bonus.\"; Font = Enum.Font.SourceSansBold; Color = Color3.fromRGB(196, 209, 216)} )\n\t\t\tnetwork:invoke(\"reportCurrency\", player, \"ethyr\", 200, \"gift:referral\")\n\t\tend\n\tend\n\n\tlocal lastPosition = playerData.lastPhysicalPosition\n\tif lastPosition and (playerData.lastLocation == nil or game.PlaceId == playerData.lastLocation) then\n\t\tlocal tag = Instance.new(\"Vector3Value\")\n\t\ttag.Name = \"lastPhysicalPosition\"\n\t\ttag.Value = Vector3.new(lastPosition[1],lastPosition[2],lastPosition[3])\n\t\ttag.Parent = player\n\tend\n\n\t-- signal for when data is changed (`_previousValue` and `_newValue` are optional,\n\t-- do not depend on them being there unless you know they will be)\n\tlocal function signal__playerDataChanged(index, _previousValue, _newValue)\n\t\tif index == \"class\" then\n\t\t\tplayer.class.Value = _newValue\n\t\t\tgeneratecompletePlayerStats(player)\n\t\telseif index == \"gold\" then\n\t\t\tplayer.gold.Value = _newValue\n\t\telseif index == \"exp\" then -- EXP CHANGED\n\t\t\twhile playerData.level < PLAYER_LEVEL_CAP do\n\t\t\t\tlocal expForNextLevel = levels.getEXPToNextLevel(playerData.level)\n\t\t\t\tif playerData.exp >= expForNextLevel then\n\t\t\t\t\t-- level the player up and silently remove extra exp\n\n\t\t\t\t\t-- NOTE: we are doing this silently because the propogation at the end will get the **CURRENT** exp value\n\t\t\t\t\t-- which will include this silent reduction.\n\t\t\t\t\tplayerData.exp = playerData.exp - expForNextLevel\n\n\t\t\t\t\t-- increase player level and propogate this change\n\t\t\t\t\tplayerData.nonSerializeData.incrementPlayerData(\"level\", 1)\n\n\t\t\t\t\tplayer.level.Value = player.level.Value + 1\n\n\t\t\t\t\tspawn(function()\n\t\t\t\t\t\tnetwork:invoke(\"reportAnalyticsEvent\",player,\"levelup:lvl\"..tostring(player.level.Value))\n\t\t\t\t\tend)\n\n\t\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\t\t\tText = player.Name .. \" has reached Lvl.\" .. player.level.Value .. \"!\";\n\t\t\t\t\t\tFont = Enum.Font.SourceSansBold;\n\t\t\t\t\t\tColor = Color3.fromRGB(255, 219, 12)\n\t\t\t\t\t})\n\n\t\t\t\t\tif player.Character and player.Character.PrimaryPart then\n\t\t\t\t\t\tfor i,oPlayer in pairs(game.Players:GetPlayers()) do\n\t\t\t\t\t\t\tlocal ui = script.levelUp:Clone()\n\t\t\t\t\t\t\tui.Adornee = player.Character.PrimaryPart\n\t\t\t\t\t\t\tui.Parent = oPlayer.PlayerGui\n\t\t\t\t\t\tend\n\t\t\t\t\t\tlocal Sound = Instance.new(\"Sound\")\n\t\t\t\t\t\tSound.Volume = 0.7\n\t\t\t\t\t\tSound.MaxDistance = 500\n\t\t\t\t\t\tSound.SoundId = \"rbxassetid://2066645345\"\n\t\t\t\t\t\tSound.Parent = player.Character.PrimaryPart\n\t\t\t\t\t\tSound:Play()\n\t\t\t\t\t\tgame.Debris:AddItem(Sound,10)\n\n\t\t\t\t\t\tlocal Attach = Instance.new(\"Attachment\")\n\t\t\t\t\t\tAttach.Parent = player.Character.PrimaryPart\n\t\t\t\t\t\tAttach.Orientation = Vector3.new(0,0,0)\n\t\t\t\t\t\tAttach.Axis = Vector3.new(1,0,0)\n\t\t\t\t\t\tAttach.SecondaryAxis = Vector3.new(0,1,0)\n\n\t\t\t\t\t\tfor i,particle in pairs(script.particles:GetChildren()) do\n\t\t\t\t\t\t\tlocal newParticle = particle:Clone()\n\t\t\t\t\t\t\tnewParticle.Enabled = false\n\t\t\t\t\t\t\tnewParticle.Parent = Attach\n\t\t\t\t\t\t\tif newParticle.Name == \"Dust\" then\n\t\t\t\t\t\t\t\tnewParticle:Emit(40)\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tnewParticle:Emit(1)\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tgame.Debris:AddItem(Attach, 10)\n\n\t\t\t\t\t\tapplyMaxHealth(player)\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tif playerData.level >= PLAYER_LEVEL_CAP then\n\t\t\t\t\t\tplayerData.exp = 0\n\t\t\t\t\tend\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\telseif index == \"equipment\" then -- equipment was changed!\n\t\t\tgeneratecompletePlayerStats(player)\n\t\t\treplicatePlayerCharacterAppearance(player)\n\n\t\t\tnetwork:fire(\"playerEquipmentChanged_server\", player, playerData.equipment)\n\t\t\t--network:fireAllClients(\"playerEquipmentChanged\", player, playerData.equipment)\n\t\telseif index == \"inventory\" then\n\t\t\t-- update the inventory\n\t\t\tupdateInventorySlots(player)\n\n\t\t\tnetwork:fire(\"playerInventoryChanged_server\", player)\n\n\t\t\t-- try to ensure that stuff is gotten to in order\n\t\t\ttable.sort(playerData[index], function(a, b)\n\t\t\t\treturn a.position > b.position\n\t\t\tend)\n\t\telseif index == \"level\" then\n\n\t\telseif index == \"statistics\" then\n\t\t\tgeneratecompletePlayerStats(player)\n\t\telseif index == \"accessories\" then\n\t\t\treplicatePlayerCharacterAppearance(player)\n\t\telseif index == \"nonSerializeData\" then\n\t\t\tplayer.isPVPEnabled.Value \t= playerData.nonSerializeData.isPVPEnabled\n\t\t\tplayer.isInPVP.Value \t\t= playerData.nonSerializeData.isGlobalPVPEnabled or #playerData.nonSerializeData.whitelistPVPEnabled > 0\n\n\t\t\tif player.isInPVP.Value and player.Character then\n\t\t\t\tphysics:setWholeCollisionGroup(player.Character, \"pvpCharacters\")\n\t\t\telseif player.Character then\n\t\t\t\tphysics:setWholeCollisionGroup(player.Character, \"characters\")\n\t\t\tend\n\t\telseif index == \"statusEffects\" then\n\t\t\tgeneratecompletePlayerStats(player)\n\t\t\t--replicatePlayerCharacterActiveStatusEffects(player)\n\t\telseif index == \"internalData\" then\n\t\t\twarn(\"attempt to signal internalData changed.\")\n\t\tend\n\n\t\tonClientRequestPropogateCacheData(player, index)\n\tend\n\n\t-- set-up playerDataChanged event to propogate playerData changes\n\tplayerData.nonSerializeData.playerDataChanged = Instance.new(\"BindableEvent\")\n\tplayerData.nonSerializeData.playerDataChanged.Event:connect(signal__playerDataChanged)\n\n\t-- set condition properly if this was a death teleport\n\tif teleportData and teleportData.teleportType == \"death\" then\n\t\tplayerData.condition = {\n\t\t\thealth = 1,\n\t\t\tmana = 1,\n\t\t}\n\tend\n\n\t-- continue statusEffects that were packaged\n\tif playerData.packagedStatusEffects then\n\t\tnetwork:invoke(\"playerAddedContinuePackageStatusEffects\", player, playerData.packagedStatusEffects)\n\n\t\tplayerData.packagedStatusEffects = nil\n\tend\n\n\t-- calculate stats table\n\tgeneratecompletePlayerStats(player, true, playerData)\n\treplicatePlayerCharacterActiveStatusEffects(player)\n\n\t-- set up character appearance\n\treplicatePlayerCharacterAppearance(player, playerData)\n\n\tlocal function onCharacterSpawn(character)\n\t\tisPlayerSpawningTag.Value = true\n\t\tplayerSpawnTimeTag.Value = os.time()\n\n\t\tif character.Parent == nil then\n\t\t\tcharacter.Parent = entityManifestCollectionFolder\n\t\tend\n\n\t\tif character.PrimaryPart == nil then\n\t\t\trepeat wait(0.1) until character.PrimaryPart or character:FindFirstChild(\"hitbox\") or character.Parent == nil\n\t\tend\n\n\t\tif character:FindFirstChild(\"hitbox\") and character.PrimaryPart ~= character.hitbox then\n\t\t\tcharacter.PrimaryPart = character.hitbox\n\t\tend\n\n\t\treplicatePlayerCharacterAppearance(player, playerData)\n\t\treplicatePlayerCharacterActiveStatusEffects(player, playerData)\n\n\t\tlocal characterPrimaryPart = character.PrimaryPart\n\n\t\tpcall(function()\n\t\t\tcharacterPrimaryPart:SetNetworkOwner(player)\n\t\tend)\n\n\t\tlocal lastCharacterPostion do\n\t\t\tif player:FindFirstChild(\"lastPhysicalPosition\") then\n\t\t\t\tlastCharacterPostion  = player.lastPhysicalPosition.Value\n\t\t\t\tplayer.lastPhysicalPosition:Destroy()\n\t\t\tend\n\t\tend\n\n\t\tlocal targetCharacterSpawnPosition do\n\t\t\tif lastCharacterPostion then\n\t\t\t\ttargetCharacterSpawnPosition = CFrame.new(lastCharacterPostion) + Vector3.new(0, 4, 0)\n\t\t\telseif ReplicatedStorage:FindFirstChild(\"spawnPoints\") then\n\t\t\t\tlocal spawnPoint = teleportData and ((teleportData.spawnLocation and ReplicatedStorage.spawnPoints:FindFirstChild(teleportData.spawnLocation)) or teleportData.arrivingFrom and ReplicatedStorage.spawnPoints:FindFirstChild(teleportData.arrivingFrom)) or ReplicatedStorage.spawnPoints:FindFirstChild(\"default\")\n\n\t\t\t\t-- if the player has a set respawn point, use that\n\t\t\t\tif player:FindFirstChild(\"respawnPoint\") and player.respawnPoint.Value then\n\t\t\t\t\tspawnPoint = player.respawnPoint.Value\n\t\t\t\tend\n\n\t\t\t\tif spawnPoint then\n\t\t\t\t\tlocal cf = spawnPoint.Value\n\t\t\t\t\tlocal goalCFrame = characterPrimaryPart.CFrame - characterPrimaryPart.CFrame.p + cf.p + cf.lookVector * 30 + Vector3.new(math.random() * 3, 4, math.random() * 3)\n\n\t\t\t\t\t-- shift randomly if this isn't a load barrier\n\t\t\t\t\t-- local isLoadBarrier = (teleportData and (teleportData.spawnLocation == nil)) and spawnPoint.Name ~= \"default\"\n\t\t\t\t\tlocal isLoadBarrier = (spawnPoint:FindFirstChild(\"isLoadBarrier\") ~= nil)\n\t\t\t\t\tif not isLoadBarrier then\n\t\t\t\t\t\tgoalCFrame = cf + Vector3.new(math.random(-3, 3), math.random(3, 5), math.random(-3, 3))\n\t\t\t\t\tend\n\n\t\t\t\t\ttargetCharacterSpawnPosition = goalCFrame\n\t\t\t\telseif #ReplicatedStorage.spawnPoints:GetChildren() > 0 then\n\t\t\t\t\twarn(\">> spawn point missing!\",\tteleportData and teleportData.arrivingFrom or \"no default found\")\n\t\t\t\t\twarn(\">> using first spawnPoint\")\n\n\t\t\t\t\tlocal cf = ReplicatedStorage.spawnPoints:GetChildren()[1].Value\n\t\t\t\t\tlocal goalCFrame = characterPrimaryPart.CFrame - characterPrimaryPart.CFrame.p + cf.p + cf.lookVector * 30 + Vector3.new(math.random() * 3, 4, math.random() * 3)\n\n\t\t\t\t\ttargetCharacterSpawnPosition = goalCFrame\n\t\t\t\telse\n\t\t\t\t\twarn(\">> no spawn points at all.. ?\")\n\t\t\t\tend\n\t\t\telse\n\t\t\t\twarn(\">> no spawnPoints ?\")\n\t\t\tend\n\t\tend\n\n\t\tlocal resurrectTag = player:FindFirstChild(\"resurrecting\")\n\t\tif resurrectTag ~= nil then\n\t\t\tresurrectTag:Destroy()\n\t\t\ttargetCharacterSpawnPosition = nil\n\t\tend\n\n\t\tif targetCharacterSpawnPosition then\n\t\t\tlocal ray = Ray.new(targetCharacterSpawnPosition.p + Vector3.new(0, 2, 0), Vector3.new(0, -999, 0))\n\t\t\tlocal hitPart = projectile.raycast(ray, {character; entityManifestCollectionFolder})\n\n\t\t\tif hitPart then\n\t\t\t\twait(0.1)\n\n\t\t\t\tfor _ = 1, 8 do\n\t\t\t\t\tnetwork:invoke(\"teleportPlayerCFrame_server\", player, targetCharacterSpawnPosition)\n\n\t\t\t\t\twait(0.2)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif player.Character == character and characterPrimaryPart.state.Value ~= \"dead\" then\n\t\t\tisPlayerSpawningTag.Value = false\n\t\tend\n\tend\n\n\tif player.Character then\n\t\tspawn(function()\n\t\t\tonCharacterSpawn(player.Character)\n\t\tend)\n\tend\n\n\tplayer.CharacterAdded:connect(onCharacterSpawn)\n\n\t-- register playerData internally\n\tplayerDataContainer[player] = playerData\n\n\tflagCheck(player, playerData)\n\n\t-- assign inventory slots that aren't assigned positions\n\tupdateInventorySlots(player)\n\n\tlocal success, rank = pcall(function()\n\t\treturn player:GetRankInGroup(4238824)\n\tend)\n\n\t-- push player data to client\n\tonClientRequestFlushPropogationCache(player)\n\n\t-- some functions to change value easily while also flushing the change to the client\n\tfunction playerData.nonSerializeData.setPlayerData(index, value)\n\t\tlocal previousValue = playerData[index]\n\t\tplayerData[index] \t= value\n\n\t\t-- signal to script something changed\n\t\tplayerData.nonSerializeData.playerDataChanged:Fire(index, previousValue, playerData[index])\n\tend\n\n\tfunction playerData.nonSerializeData.incrementPlayerData(index, increment, source)\n\t\tif index == \"exp\" and playerData.level >= PLAYER_LEVEL_CAP then\n\t\t\treturn\n\t\tend\n\n\t\tlocal previousValue = playerData[index]\n\t\tplayerData[index] = previousValue + increment\n\n\t\t-- signal to script something changed\n\t\tplayerData.nonSerializeData.playerDataChanged:Fire(index, previousValue, playerData[index])\n\n\t\t-- analytics for gold\n\t\tif index == \"gold\" then\n\t\t\tlocal currency = index\n\t\t\tlocal amount = increment\n\n\t\t\tif amount ~= 0 and source then\n\t\t\t\tnetwork:invoke(\"reportCurrency\", player, currency, amount, source)\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction playerData.nonSerializeData.setNonSerializeDataValue(index, value)\n\t\tplayerData.nonSerializeData[index] = value\n\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"nonSerializeData\")\n\tend\n\t-- end easy functions --\n\n\tif player.Character and player.Character.PrimaryPart then\n\t\tspawn(function()\n\t\t\tonCharacterAdded(player, player.Character)\n\t\tend)\n\tend\n\n\tplayer.CharacterAdded:connect(function(character)\n\t\twhile not character.PrimaryPart and player.Parent == game.Players do\n\t\t\twait(0.1)\n\t\tend\n\n\t\tif character.PrimaryPart then\n\t\t\tonCharacterAdded(player, character)\n\t\tend\n\tend)\n\n\tlocal tag = Instance.new(\"BoolValue\")\n\ttag.Name = \"DataLoaded\"\n\ttag.Parent = player\n\n\tlocal tag = Instance.new(\"IntValue\")\n\ttag.Name = \"dataLoaded\"\n\ttag.Value = os.time()\n\ttag.Parent = player\n\n\tplayer:LoadCharacter()\n\tnetwork:fire(\"playerDataLoaded\", player, playerDataContainer[player])\n\n\tlocal posTag = Instance.new(\"Vector3Value\")\n\tposTag.Name = \"playerCharacterPosition\"\n\tposTag.Parent = player\n\tspawn(function()\n\t\twhile player and player.Parent == game.Players do\n\t\t\tif player.Character and player.Character.PrimaryPart then\n\t\t\t\tposTag.Value = player.Character.PrimaryPart.Position\n\t\t\tend\n\t\t\twait(3)\n\t\tend\n\tend)\n\n\t-- more aggresively save lastPhysicalPosition\n\tspawn(function()\n\t\tlocal players = game:GetService(\"Players\")\n\t\twhile player and player.Parent == players do\n\t\t\tlocal playerData = playerDataContainer[player]\n\t\t\tif playerData and player.Character and player.Character.PrimaryPart then\n\t\t\t\tlocal position = player.Character.PrimaryPart.Position\n\t\t\t\tplayerData[\"lastPhysicalPosition\"] = {\n\t\t\t\t\tmath.floor(position.X),\n\t\t\t\t\tmath.floor(position.Y),\n\t\t\t\t\tmath.floor(position.Z)\n\t\t\t\t}\n\t\t\tend\n\t\t\twait(3)\n\t\tend\n\tend)\n\n\tspawn(function()\n\t\twait(60)\n\t\tlocal errorcount = 0\n\n\t\twhile player and player.Parent == game.Players and player:FindFirstChild(\"DataLoaded\") do\n\t\t\tlocal waittime = 180\n\t\t\tif player:FindFirstChild(\"teleporting\") == nil and playerDataContainer[player] then\n\t\t\t\tlocal success, timestamp = autoSavePlayerData(player)\n\t\t\t\tif not success then\n\t\t\t\t\tif errorcount == 0 then\n\t\t\t\t\t\twaittime = 10\n\t\t\t\t\telseif errorcount <= 3 then\n\t\t\t\t\t\twaittime = 20\n\t\t\t\t\telse\n\t\t\t\t\t\twaittime = 60\n\t\t\t\t\tend\n\t\t\t\t\terrorcount = errorcount + 1\n\t\t\t\tend\n\t\t\tend\n\t\t\twait(waittime)\n\t\tend\n\tend)\n\n\t-- Admin hunter\n\tspawn(function()\n\t\tif player:IsInGroup(1200769) then\n\t\t\tlocal Error = \"An admin (\"..player.Name..\") joined the game.\"\n\t\t\tnetwork:invoke(\"reportError\", player, \"info\", Error)\n\t\telseif player:IsInGroup(4199740) then\n\t\t\tlocal Error = \"A Star Creator (\"..player.Name..\") joined the game.\"\n\t\t\tnetwork:invoke(\"reportError\", player, \"info\", Error)\n\t\tend\n\t\tif player:IsInGroup(4484634) then\n\t\t\tlocal Error = \"A Red Manta (\"..player.Name..\") joined the game.\"\n\t\t\tnetwork:invoke(\"reportError\", player, \"info\", Error)\n\t\tend\n\tend)\n\n\tif existingAnalyticsSession then\n\t\tnetwork:invoke(\"continueSession\", player, existingAnalyticsSession, joinTime)\n\telse\n\t\tnetwork:invoke(\"newSession\", player)\n\tend\n\n\t-- tp exploit stuff --\n\tlocal playerPositionData = {}\n\t\tplayerPositionData.positions \t\t\t= {}\n\t\tplayerPositionData.sketchyMovements \t= {}\n\n\tplayerPositionDataContainer[player] = playerPositionData\n\t----------------------\n\n\t--items currently equipped need to have their onEquipped fired\n\tfor _, slotData in pairs(playerData.equipment) do\n\t\tlocal itemData = itemLookup[slotData.id]\n\t\tif itemData.perks then\n\t\t\tfor perkName, _ in pairs(itemData.perks) do\n\t\t\t\tlocal perkData = perkLookup[perkName]\n\t\t\t\tif perkData and perkData.onEquipped then\n\t\t\t\t\tlocal success, err = pcall(function()\n\t\t\t\t\t\tperkData.onEquipped(player, itemData, tostring(slotData.position))\n\t\t\t\t\tend)\n\t\t\t\t\tif not success then\n\t\t\t\t\t\twarn(string.format(\"item %s equip failed because: %s\", itemData.name, err))\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\t-- data recovery flags processed here, we don't need to care if this is studio\n\tspawn(function()\n\t\t-- data recovery disabled by this return\n\t\t-- guess we're not using it for a while\n\t\tif game.PlaceId ~= 4409709778 then return end\n\n\n\n\t\tlocal lastSaveTimestamp = 0\n\t\tif playerData.globalData then\n\t\t\tlastSaveTimestamp = playerData.globalData.lastSaveTimestamp or 0\n\t\tend\n\n\t\tif not playerData.flags.dataRecovery11_14 then\n\t\t\tif lastSaveTimestamp < 1573760698 then\n\t\t\t\tplayerData.flags.dataRecovery11_14 = true\n\t\t\telse\n\t\t\t\tperformDataRecovery(player, desiredSlot, \"dataRecovery11_14\")\n\t\t\tend\n\t\tend\n\tend)\n\n\treturn true, playerData, errorMsg\nend\n\nfunction performDataRecovery(player, desiredSlot, flagName)\n\tlocal playerData = playerDataContainer[player]\n\n\tif game.PlaceId == 4409709778 then\n\t\twhile true do\n\t\t\twait(1)\n\t\t\tnetwork:fireClient(\"dataRecoveryRequested\", player, playerData, desiredSlot, flagName)\n\t\tend\n\telse\n\t\tplayerData.dataRecoveryReturnPlaceId = game.PlaceId\n\t\trepeat\n\t\t\twait(1)\n\t\tuntil network:invoke(\"teleportPlayer\", player, 4409709778)\n\tend\nend\n\nlocal DATA_RECOVERY_FLAG_NAMES = {\n\t\"dataRecovery11_14\",\n}\n\nfunction onDataRecoveryRejected(player, flagName)\n\t-- is this a legal flag?\n\tlocal legalFlag = false\n\tfor _, acceptedFlagName in pairs(DATA_RECOVERY_FLAG_NAMES) do\n\t\tif acceptedFlagName == flagName then\n\t\t\tlegalFlag = true\n\t\tend\n\tend\n\tif not legalFlag then return end\n\n\tlocal playerData = playerDataContainer[player]\n\tif not playerData then return end\n\tif playerData.flags[flagName] then return end\n\n\tplayerData.flags[flagName] = true\n\n\trepeat\n\t\twait(1)\n\tuntil network:invoke(\"teleportPlayer\", player, playerData.dataRecoveryReturnPlaceId)\nend\n\nfunction onDataRecoveryRequested(player, slot, version, flagName)\n\t-- only in data recovery place!\n\tif game.PlaceId ~= 4409709778 then return end\n\n\t-- is this a legal flag?\n\tlocal legalFlag = false\n\tfor _, acceptedFlagName in pairs(DATA_RECOVERY_FLAG_NAMES) do\n\t\tif acceptedFlagName == flagName then\n\t\t\tlegalFlag = true\n\t\tend\n\tend\n\tif not legalFlag then return end\n\n\t-- do we have this flag already?\n\tlocal playerData = playerDataContainer[player]\n\tif playerData.flags[flagName] then return end\n\n\tlocal latestVersion = datastoreInterface:getLatestSaveVersion(player)\n\tlocal success, rollbackData, message = datastoreInterface:getPlayerSaveFileData(player, slot, version)\n\n\tif not success then return end\n\n\trollbackData.timestamp = playerData.timestamp\n\trollbackData.globalData.version = playerData.globalData.version\n\n\trollbackData.flags[flagName] = true\n\tplayerDataContainer[player] = rollbackData\n\n\tdatastoreInterface:updatePlayerSaveFileData(player.UserId, rollbackData)\n\n\trepeat\n\t\twait(1)\n\tuntil network:invoke(\"teleportPlayer\", player, playerData.dataRecoveryReturnPlaceId)\nend\n\ngame:BindToClose(function()\n\tif shuttingDown then\n\t\treturn false\n\tend\n\tshuttingDown = true\n\n\tlocal msg = Instance.new(\"Message\")\n\tmsg.Text = \"Servers are shutting down for a Vesteria or Roblox client update. Your data is now saving...\"\n\tmsg.Parent = workspace\n\n\tif game:GetService(\"RunService\"):IsStudio() then return end\n\tlocal playersToSave = {}\n\tlocal playerCount = 0\n\tfor i,player in pairs(game.Players:GetPlayers()) do\n\t\tlocal playerName = player.Name\n\t\tplayersToSave[playerName] = player\n\t\tplayerCount = playerCount + 1\n\t\tspawn(function()\n\t\t\tlocal success, err = pcall(onPlayerRemoving,player)\n\t\t\tif success then\n\t\t\t\tplayersToSave[playerName] = nil\n\t\t\t\tplayerCount = playerCount - 1\n\t\t\t\tlocal destination = 2376885433\n\t\t\t\tif game.GameId == 712031239 then\n\t\t\t\t\tdestination = 2015602902\n\t\t\t\tend\n\n\t\t\t\tgame:GetService(\"TeleportService\"):Teleport(destination, player, {teleportReason = \"You were teleported back to the lobby because Vesteria your server was shutdown. This probably means we released an update for new content or bug fixes.\"}, game.ReplicatedStorage.returnToLobby)\n\t\t\telse\n\t\t\t\twarn(\"Failed to save\",playerName,\"data\")\n\t\t\t\twarn(err)\n\t\t\tend\n\t\tend)\n\tend\n\n\trepeat wait(0.1) until playerCount <= 0\nend)\n\n-- note: category can be factored out because items of same itemId share same category\nlocal function getInventorySlotByItemId(player, itemId, ignoreSlotsThatAreStacked)\n\tlocal lowestInventorySlot\n\tif playerDataContainer[player] then\n\t\tfor i, inventorySlot in pairs(playerDataContainer[player].inventory) do\n\t\t\tlocal itemBaseData = itemLookup[inventorySlot.id]\n\t\t\tif inventorySlot.id == itemId and (not lowestInventorySlot or inventorySlot.position < lowestInventorySlot.position) and (not ignoreSlotsThatAreStacked or (inventorySlot.stacks and inventorySlot.stacks < (itemBaseData.stackSize or MAX_COUNT_PER_STACK))) then\n\t\t\t\tlowestInventorySlot = inventorySlot\n\t\t\tend\n\t\tend\n\tend\n\n\treturn lowestInventorySlot\nend\n\nlocal function onGetPlayerInventorySlotDataByInventorySlotPosition(player, category, inventorySlotPosition)\n\tif not player or not playerDataContainer[player] then return nil end\n\n\tfor trueInventorySlotPosition, inventorySlotData in pairs(playerDataContainer[player].inventory) do\n\t\tlocal itemBaseData = itemLookup[inventorySlotData.id]\n\t\tif itemBaseData and itemBaseData.category == category then\n\t\t\tif inventorySlotData.position == inventorySlotPosition then\n\t\t\t\treturn inventorySlotData, trueInventorySlotPosition\n\t\t\tend\n\t\tend\n\tend\n\n\treturn nil\nend\n\n-- gold_player is how much the player loses, gold_NPC is how much the player gains\n-- inventoryTransferDataCollection_player is what the player loses\n-- inventoryTransferData_intermediateCollection_NPC is what the player gains\nlocal function int__tradeItemsBetweenPlayerAndNPC(player, inventoryTransferDataCollection_player, gold_player, inventoryTransferData_intermediateCollection_NPC, gold_NPC, source, additionalValues)\n\tlocal playerData = playerDataContainer[player]\n\n\tif player and playerData and inventoryTransferDataCollection_player and inventoryTransferData_intermediateCollection_NPC then\n\t\tlocal inventoryTransferData_intermediateCollection_player, wasInventoryTransferDataModified_player = int__getInventoryTransferData_intermediateCollectionFromInventoryTransferDataCollection(player, inventoryTransferDataCollection_player)\n\t\tlocal player_hasInventorySpace = int__doesPlayerHaveInventorySpaceForTrade(player, inventoryTransferData_intermediateCollection_player, inventoryTransferData_intermediateCollection_NPC)\n\n\t\tif not wasInventoryTransferDataModified_player and player_hasInventorySpace then\n\t\t\tif not gold_player or playerData.gold >= gold_player then\n\t\t\t\t-- remove items from playerFrom\n\t\t\t\trevokePlayerItemsByInventoryTransferDataCollection(player, inventoryTransferData_intermediateCollection_player)\n\t\t\t\tif gold_player and gold_player ~= 0 then\n\t\t\t\t\tplayerData.nonSerializeData.incrementPlayerData(\"gold\", -gold_player, source)\n\t\t\t\tend\n\n\t\t\t\t-- grant items from NPC\n\t\t\t\tgrantPlayerItemsByInventoryTranferData_intermediateCollection(player, inventoryTransferData_intermediateCollection_NPC)\n\t\t\t\tif gold_NPC and gold_NPC ~= 0 then\n\t\t\t\t\tplayerData.nonSerializeData.incrementPlayerData(\"gold\", gold_NPC, source)\n\t\t\t\tend\n\n\n\t\t\t\tif not (additionalValues and additionalValues.overrideItemsRecieved) then\n\t\t\t\t\tnetwork:fireClient(\"itemsRecieved\", player, inventoryTransferData_intermediateCollection_NPC, gold_NPC)\n\t\t\t\tend\n\n\t\t\t\treturn true\n\t\t\telse\n\t\t\t\treturn false, player.Name .. \" does not have enough gold\"\n\t\t\tend\n\t\telse\n\t\t\treturn false, wasInventoryTransferDataModified_player and \"Player inventory was modified\" or \"Not enough space in inventory!\"\n\t\tend\n\tend\n\n\treturn false, \"denied straight-up\"\nend\n\nlocal function int__tradeItemsBetweenPlayers(playerFrom, inventoryTransferData_intermediateCollection_playerFrom, gold_playerFrom, playerTo, inventoryTransferData_intermediateCollection_playerTo, gold_playerTo)\n\tlocal playerFromData \t= playerDataContainer[playerFrom]\n\tlocal playerToData \t\t= playerDataContainer[playerTo]\n\n\tif playerFrom:FindFirstChild(\"DataSaveFailed\") or playerTo:FindFirstChild(\"DataSaveFailed\") then\n\t\treturn false, \"player is experiencing a DataStore outage\"\n\tend\n\n\tif playerFromData and playerToData then\n\t\t-- check if playerFrom has inventory space\n\t\tlocal playerFrom_hasInventorySpace = int__doesPlayerHaveInventorySpaceForTrade(playerFrom, inventoryTransferData_intermediateCollection_playerFrom, inventoryTransferData_intermediateCollection_playerTo)\n\t\tif playerFrom_hasInventorySpace then\n\t\t\t-- check if playerTo has inventory space\n\t\t\tlocal playerTo_hasInventorySpace = int__doesPlayerHaveInventorySpaceForTrade(playerTo, inventoryTransferData_intermediateCollection_playerTo, inventoryTransferData_intermediateCollection_playerFrom)\n\t\t\tif playerTo_hasInventorySpace then\n\t\t\t\t-- remove items from playerFrom\n\t\t\t\trevokePlayerItemsByInventoryTransferDataCollection(playerFrom, inventoryTransferData_intermediateCollection_playerFrom)\n\n\t\t\t\t-- remove items from playerTo\n\t\t\t\trevokePlayerItemsByInventoryTransferDataCollection(playerTo, inventoryTransferData_intermediateCollection_playerTo)\n\n\t\t\t\t-- grant items to playerFrom\n\t\t\t\tgrantPlayerItemsByInventoryTranferData_intermediateCollection(playerFrom, inventoryTransferData_intermediateCollection_playerTo)\n\n\t\t\t\t-- grant items to playerTo\n\t\t\t\tgrantPlayerItemsByInventoryTranferData_intermediateCollection(playerTo, inventoryTransferData_intermediateCollection_playerFrom)\n\n\t\t\t\tlocal source = \"player:trade\"\n\n\t\t\t\tplayerFromData.nonSerializeData.incrementPlayerData(\"gold\", -gold_playerFrom, source)\n\t\t\t\tplayerFromData.nonSerializeData.incrementPlayerData(\"gold\", gold_playerTo * 0.7, source)\n\n\t\t\t\tplayerToData.nonSerializeData.incrementPlayerData(\"gold\", -gold_playerTo, source)\n\t\t\t\tplayerToData.nonSerializeData.incrementPlayerData(\"gold\", gold_playerFrom * 0.7, source)\n\n\t\t\t\t-- replicate these changes\n\t\t\t\tplayerFromData.nonSerializeData.playerDataChanged:Fire(\"inventory\")\n\t\t\t\tplayerToData.nonSerializeData.playerDataChanged:Fire(\"inventory\")\n\n\t\t\t\treturn true, \"ALL GOOD!!!\"\n\t\t\telse\n\t\t\t\treturn false, playerTo.Name .. \" does not have inventory space.\"\n\t\t\tend\n\t\telse\n\t\t\treturn false, playerFrom.Name .. \" does not have inventory space.\"\n\t\tend\n\tend\n\n\treturn false, \"ERROR!\"\nend\n\n-- inventoryTransferDataCollection_player1 refers to the data player1 is going to transfer to player2 and vice versa\nlocal function onTradeRequestReceived(player1, inventoryTransferDataCollection_player1, gold_player1, player2, inventoryTransferDataCollection_player2, gold_player2)\n\tif not configuration.getConfigurationValue(\"isTradingEnabled\", player1) or not configuration.getConfigurationValue(\"isTradingEnabled\", player2) then\n\t\treturn false, \"This feature has been disabled\"\n\tend\n\n\tif player1:FindFirstChild(\"DataSaveFailed\") then\n\t\tnetwork:fireClient(\"alertPlayerNotification\", player1, {text = \"Cannot trade during DataStore outage.\"; textColor3 = Color3.fromRGB(255, 57, 60)})\n\t\treturn false, \"This feature is temporarily disabled\"\n\tend\n\n\tif player1 and player2 and inventoryTransferDataCollection_player1 and inventoryTransferDataCollection_player2 then\n\t\tdo -- todo: fix this ugly hack\n\t\t\tlocal p1in = {}\n\t\t\tfor i,v in pairs(inventoryTransferDataCollection_player1) do\n\t\t\t\ttable.insert(p1in, v)\n\t\t\tend\n\n\t\t\tlocal p2in = {}\n\t\t\tfor i,v in pairs(inventoryTransferDataCollection_player2) do\n\t\t\t\ttable.insert(p2in, v)\n\t\t\tend\n\n\t\t\tinventoryTransferDataCollection_player1 = p1in\n\t\t\tinventoryTransferDataCollection_player2 = p2in\n\t\tend\n\n\t\tif player1:FindFirstChild(\"DataSaveFailed\") then\n\t\t\tnetwork:fireClient(\"alertPlayerNotification\", player1, {text = \"Cannot trade during DataStore outage. Trade canceled.\"; textColor3 = Color3.fromRGB(255, 57, 60)})\n\t\t\tnetwork:fireClient(\"alertPlayerNotification\", player2, {text = \"Other player is experiencing a DataStore outage. Trade canceled.\"; textColor3 = Color3.fromRGB(255, 57, 60)})\n\t\t\treturn false, \"This feature is temporarily disabled\"\n\t\tend\n\n\t\tif player2:FindFirstChild(\"DataSaveFailed\") then\n\t\t\tnetwork:fireClient(\"alertPlayerNotification\", player2, {text = \"Cannot trade during DataStore outage. Trade canceled.\"; textColor3 = Color3.fromRGB(255, 57, 60)})\n\t\t\tnetwork:fireClient(\"alertPlayerNotification\", player1, {text = \"Other player is experiencing a DataStore outage. Trade canceled.\"; textColor3 = Color3.fromRGB(255, 57, 60)})\n\t\t\treturn false, \"This feature is temporarily disabled\"\n\t\tend\n\n\t\tlocal inventoryTransferData_intermediateCollection_player1, wasInventoryTransferDataModified_player1 = int__getInventoryTransferData_intermediateCollectionFromInventoryTransferDataCollection(player1, inventoryTransferDataCollection_player1)\n\t\tlocal inventoryTransferData_intermediateCollection_player2, wasInventoryTransferDataModified_player2 = int__getInventoryTransferData_intermediateCollectionFromInventoryTransferDataCollection(player2, inventoryTransferDataCollection_player2)\n\n\t\tif not wasInventoryTransferDataModified_player1 then\n\t\t\tif not wasInventoryTransferDataModified_player2 then\n\t\t\t\tlocal success, reason = int__tradeItemsBetweenPlayers(player1, inventoryTransferData_intermediateCollection_player1, gold_player1, player2, inventoryTransferData_intermediateCollection_player2, gold_player2)\n\t\t\t\treturn success, reason\n\t\t\telse\n\t\t\t\treturn false, player2.Name .. \" has invalid data\"\n\t\t\tend\n\t\telse\n\t\t\treturn false, player1.Name .. \" has invalid data\"\n\t\tend\n\tend\n\n\treturn false, \"invalid data\"\nend\n\nlocal function incrementPlayerStatPointsByStatName(player, statName)\n\tlocal playerData = playerDataContainer[player]\n\n\tif playerData and (statName == \"dex\" or statName == \"int\" or statName == \"str\" or statName == \"vit\") then\n\t\tlocal usedPoints = playerData.statistics.dex + playerData.statistics.int + playerData.statistics.str + playerData.statistics.vit\n\n\t\tif levels.getStatPointsForLevel(playerData.level) - usedPoints >= 1 then\n\t\t\tplayerData.statistics[statName] \t\t= playerData.statistics[statName] + 1\n\n\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"statistics\")\n\n\t\t\tapplyMaxHealth(player, true)\n\n\t\t\treturn true, \"All good\"\n\t\telse\n\t\t\treturn false, \"not enough\"\n\t\tend\n\tend\n\n\treturn false, \"nope\"\nend\n\n-- todo: handle 'swapping' and 'stack breaking/making' as one function based on the items involved\n-- or something like that. it'll be a big function though, but they cross over too much to keep it separate\n\nlocal function onRequestSplitInventorySlotDataStack(player, category, inventorySlotPosition, targetInventorySlotPosition, splitAmount)\n\tsplitAmount = splitAmount or 1\n\tsplitAmount = math.floor(splitAmount)\n\n\tif splitAmount > 0 then\n\n\t\tlocal inventorySlotData, trueInventorySlotPosition = onGetPlayerInventorySlotDataByInventorySlotPosition(player, category, inventorySlotPosition)\n\t\tif inventorySlotData then\n\t\t\tlocal itemBaseData = itemLookup[inventorySlotData.id]\n\t\t\tif itemBaseData and itemBaseData.canStack then\n\t\t\t\tlocal targetInventorySlotData \t= onGetPlayerInventorySlotDataByInventorySlotPosition(player, category, targetInventorySlotPosition)\n\t\t\t\tif not targetInventorySlotData then\n\t\t\t\t\tlocal cap \t\t\t\t\t\t= math.min((itemBaseData.stackSize or MAX_COUNT_PER_STACK), inventorySlotData.stacks)\n\t\t\t\t\tlocal real_split_amount \t\t= math.clamp(splitAmount, 1, cap)\n\t\t\t\t\tlocal diff \t\t\t\t\t\t= cap - real_split_amount\n\n\t\t\t\t\tlocal itemDataForInv = {id = inventorySlotData.id; count = real_split_amount}\n\t\t\t\t\ttable.insert(playerDataContainer[player].inventory, itemDataForInv)\n\n\t\t\t\t\t-- empty stack now :CC\n\t\t\t\t\tif diff == 0 then\n\t\t\t\t\t\ttable.remove(playerDataContainer[player].inventory, trueInventorySlotPosition)\n\t\t\t\t\telse\n\t\t\t\t\t\tinventorySlotData.stacks = inventorySlotData.stacks - real_split_amount\n\t\t\t\t\tend\n\n\t\t\t\t\t-- update the inventory slots to assign this new slot a position value\n\t\t\t\t\tupdateInventorySlots(player)\n\n\t\t\t\t\t-- force an inventory refresh\n\t\t\t\t\tonClientRequestPropogateCacheData(player, \"inventory\")\n\n\t\t\t\t\treturn true\n\t\t\t\telse\n\t\t\t\t\t-- targetInventorySlotData is there\n\t\t\t\t\tif targetInventorySlotData.id == inventorySlotData.id and targetInventorySlotData.stacks and targetInventorySlotData.stacks < (itemBaseData.stackSize or MAX_COUNT_PER_STACK) then\n\t\t\t\t\t\tlocal splitDifference = math.clamp(splitAmount, 1, math.min(inventorySlotData.stacks, (itemBaseData.stackSize or MAX_COUNT_PER_STACK)))\n\n\t\t\t\t\t\tif inventorySlotData.stacks >= splitDifference then\n\t\t\t\t\t\t\t-- clamp this to not overfill the target slot\n\t\t\t\t\t\t\tsplitDifference = math.clamp(splitDifference, 1, (itemBaseData.stackSize or MAX_COUNT_PER_STACK) - targetInventorySlotData.stacks)\n\n\t\t\t\t\t\t\t-- remove from old stack\n\t\t\t\t\t\t\tif inventorySlotData.stacks - splitDifference == 0 then\n\t\t\t\t\t\t\t\t-- diff is zero, remove old stack!!!!!!\n\t\t\t\t\t\t\t\ttable.remove(playerDataContainer[player].inventory, trueInventorySlotPosition)\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tinventorySlotData.stacks = inventorySlotData.stacks - splitDifference\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t-- add to new stack\n\t\t\t\t\t\t\ttargetInventorySlotData.stacks = targetInventorySlotData.stacks + splitDifference\n\n\t\t\t\t\t\t\t-- update the inventory slots to assign this new slot a position value\n\t\t\t\t\t\t\tupdateInventorySlots(player)\n\n\t\t\t\t\t\t\t-- force an inventory refresh\n\t\t\t\t\t\t\tonClientRequestPropogateCacheData(player, \"inventory\")\n\n\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\n-- todo: further validation, rn it just gives it to them\nlocal function onRequestAddItemToInventoryReceived(player, itemId, stacks, metadata)\n\t-- default to one stack!\n\tstacks = stacks or 1\n\n\tif playerDataContainer[player] and itemLookup[itemId] then\n\t\tlocal itemBaseData = itemLookup[itemId]\n\t\tif itemBaseData then\n\t\t\t-- todo, probably use a copy of inventory to prevent tampering?\n\t\t\tif itemBaseData.canStack then\n\t\t\t\twhile stacks > 0 do\n\t\t\t\t\tlocal currentInventorySlotData = getInventorySlotByItemId(player, itemId, true)\n\n\t\t\t\t\tif currentInventorySlotData then\n\t\t\t\t\t\tlocal amountTaken = math.clamp(stacks, 1, (itemBaseData.stackSize or MAX_COUNT_PER_STACK) - currentInventorySlotData.stacks)\n\n\t\t\t\t\t\tcurrentInventorySlotData.stacks = currentInventorySlotData.stacks + amountTaken\n\n\t\t\t\t\t\tstacks = stacks - amountTaken\n\t\t\t\t\telse\n\t\t\t\t\t\tlocal amountTaken = math.clamp(stacks, 1, (itemBaseData.stackSize or MAX_COUNT_PER_STACK))\n\n\t\t\t\t\t\ttable.insert(playerDataContainer[player].inventory, {id = itemId; stacks = amountTaken})\n\n\t\t\t\t\t\tstacks = stacks - amountTaken\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\t-- update the inventory slots to assign this new slot a position value\n\t\t\t\tupdateInventorySlots(player)\n\t\t\t\t-- force an inventory refresh\n\t\t\t\tonClientRequestPropogateCacheData(player, \"inventory\")\n\n\t\t\t\treturn true\n\t\t\telse\n\t\t\t\tstacks = stacks or 1\n\n\t\t\t\tlocal inventoryTransferDataCollection = {} do\n\t\t\t\t\tfor i = 1, stacks do\n\t\t\t\t\t\tlocal itemDataForInv = {id = itemId}\n\n\t\t\t\t\t\tfor i, v in pairs(metadata or {}) do\n\t\t\t\t\t\t\titemDataForInv[i] = v\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\ttable.insert(inventoryTransferDataCollection, itemDataForInv)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tlocal hasInventorySpace = int__doesPlayerHaveInventorySpaceForTrade(player, {}, inventoryTransferDataCollection)\n\n\t\t\t\tif hasInventorySpace then\n\t\t\t\t\tgrantPlayerItemsByInventoryTranferData_intermediateCollection(player, inventoryTransferDataCollection)\n\n\t\t\t\t\t-- update the inventory slots to assign this new slot a position value\n\t\t\t\t\tupdateInventorySlots(player)\n\n\t\t\t\t\t-- force an inventory refresh\n\t\t\t\t\tonClientRequestPropogateCacheData(player, \"inventory\")\n\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\n-- todo: check for category\nlocal function onSwitchInventorySlotDataRequestReceived(player, category, inventorySlotNumber1, inventorySlotNumber2)\n\tif playerDataContainer[player] and inventorySlotNumber1 and inventorySlotNumber2 then\n\t\tif inventorySlotNumber1 > 0 and inventorySlotNumber2 > 0 and inventorySlotNumber1 ~= inventorySlotNumber2 and category then\n\t\t\t-- lock the state of the player's Inventory to prevent duplications\n\t\t\tlocal playerInventoryCopy = utilities.copyTable(playerDataContainer[player].inventory)\n\n\t\t\tlocal inventorySlotNumber1_Index\n\t\t\tlocal inventorySlotNumber2_Index\n\t\t\tfor i, inventorySlotData in pairs(playerInventoryCopy) do\n\t\t\t\tlocal itemBaseData = itemLookup[inventorySlotData.id]\n\t\t\t\tif itemBaseData and itemBaseData.category == category then\n\t\t\t\t\tif inventorySlotData.position == inventorySlotNumber1 then\n\t\t\t\t\t\tinventorySlotNumber1_Index = i\n\t\t\t\t\telseif inventorySlotData.position == inventorySlotNumber2 then\n\t\t\t\t\t\tinventorySlotNumber2_Index = i\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif inventorySlotNumber1_Index and inventorySlotNumber2_Index then\n\t\t\t\tplayerInventoryCopy[inventorySlotNumber1_Index].position = inventorySlotNumber2\n\t\t\t\tplayerInventoryCopy[inventorySlotNumber2_Index].position = inventorySlotNumber1\n\t\t\telseif inventorySlotNumber1_Index and not inventorySlotNumber2_Index then\n\t\t\t\tplayerInventoryCopy[inventorySlotNumber1_Index].position = inventorySlotNumber2\n\t\t\telseif not inventorySlotNumber1_Index and inventorySlotNumber2_Index then\n\t\t\t\tplayerInventoryCopy[inventorySlotNumber2_Index].position = inventorySlotNumber1\n\t\t\telse\n\n\t\t\t\t-- request was denied, force a refresh\n\t\t\t\tonClientRequestPropogateCacheData(player, \"inventory\")\n\t\t\t\treturn false\n\t\t\tend\n\n\t\t\tplayerDataContainer[player].nonSerializeData.setPlayerData(\"inventory\", playerInventoryCopy)\n\t\t\treturn true\n\t\tend\n\tend\n\n\t-- request was denied, force a refresh\n\tonClientRequestPropogateCacheData(player, \"inventory\")\n\treturn false\nend\n\nlocal function getEquipmentDataByPosition(equipment, position)\n\tfor index, data in pairs(equipment) do\n\t\tif data.position == position then\n\t\t\treturn data, index\n\t\tend\n\tend\n\treturn nil, nil\nend\n\n-- todo make sure equipmentSlotPosition is valid for the weapon being moved, deny if not\nlocal function onTransferInventoryToEquipment(player, category, inventorySlotPosition, equipmentSlotPosition)\n\tif playerDataContainer[player] and inventorySlotPosition and equipmentSlotPosition then\n\t\tif inventorySlotPosition > 0 and equipmentSlotPosition > 0 then\n\n\t\t\tlocal playerData = playerDataContainer[player]\n\n\t\t\t-- we make copies to prevent duplications or to prevent the player from doing something\n\t\t\t-- fishy when the client script is hung waiting for this to return (ie due to ping)\n\t\t\tlocal playerInventoryCopy = utilities.copyTable(playerData.inventory)\n\t\t\tlocal playerEquipmentCopy = utilities.copyTable(playerData.equipment)\n\n\t\t\tlocal trueInventorySlotPosition\n\t\t\tfor i, inventorySlotData in pairs(playerInventoryCopy) do\n\t\t\t\tlocal itemBaseData = itemLookup[inventorySlotData.id]\n\t\t\t\tif itemBaseData and itemBaseData.category == category then\n\t\t\t\t\tif inventorySlotData.position == inventorySlotPosition and category then\n\t\t\t\t\t\tif not itemBaseData.minLevel or playerData.level >= itemBaseData.minLevel then\n\t\t\t\t\t\t\tif not itemBaseData.minimumClass or isPlayerOfClass(player, itemBaseData.minimumClass) then\n\t\t\t\t\t\t\t\ttrueInventorySlotPosition = i\n\n\t\t\t\t\t\t\telse\n\n\t\t\t\t\t\t\t\treturn false, \"incorrect class\"\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\n\t\t\t\t\t\t\treturn false, \"not high enough level\"\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal trueEquipmentSlotPosition\n\t\t\tfor i, equipmentSlotData in pairs(playerEquipmentCopy) do\n\t\t\t\tif equipmentSlotData.position == equipmentSlotPosition then\n\t\t\t\t\ttrueEquipmentSlotPosition = i\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif trueInventorySlotPosition then\n\t\t\t\tlocal baseData = itemLookup[playerInventoryCopy[trueInventorySlotPosition].id]\n\n\t\t\t\tif baseData.equipmentPosition == mapping.equipmentPosition.arrow then\n\t\t\t\t\tprint(\"in arrow exception\")\n\t\t\t\t\tif not trueEquipmentSlotPosition then\n\t\t\t\t\t\ttable.insert(playerEquipmentCopy, {\n\t\t\t\t\t\t\tposition \t= mapping.equipmentPosition.arrow;\n\t\t\t\t\t\t\tid \t\t\t= baseData.id;\n\t\t\t\t\t\t\tstacks \t\t= 0;\n\t\t\t\t\t\t})\n\t\t\t\t\telse\n\t\t\t\t\t\tplayerEquipmentCopy[trueEquipmentSlotPosition].id = baseData.id;\n\t\t\t\t\tend\n\n\t\t\t\t\tplayerDataContainer[player].nonSerializeData.setPlayerData(\"equipment\", playerEquipmentCopy)\n\n\t\t\t\t\tevents:fireEventLocal(\"playerEquipmentChanged\", player)\n\n\t\t\t\t\treturn\n\t\t\t\tend\n\n\t\t\t\t-- validate that the equip is ok\n\t\t\t\tlocal isWeapon = baseData.equipmentSlot == 1\n\t\t\t\tlocal isWeaponSlot = (equipmentSlotPosition == 1) or (equipmentSlotPosition == 11)\n\t\t\t\tlocal equippingWeaponToWeaponSlot = isWeapon and isWeaponSlot\n\t\t\t\tlocal equippingToExactSlot = baseData.equipmentSlot == equipmentSlotPosition\n\t\t\t\tlocal equipLegal = equippingWeaponToWeaponSlot or equippingToExactSlot\n\n\t\t\t\t-- only allow berzerkers to equip weapons to offhand\n\t\t\t\tif baseData.equipmentType == \"sword\" and equipmentSlotPosition == 11 then\n\t\t\t\t\tequipLegal = equipLegal and isPlayerOfClass(player, \"berserker\")\n\t\t\t\tend\n\n\t\t\t\t-- only allow knights to equip shields to offhand\n\t\t\t\tif baseData.equipmentType == \"shield\" and equipmentSlotPosition == 11 then\n\t\t\t\t\tequipLegal = equipLegal and isPlayerOfClass(player, \"knight\")\n\t\t\t\tend\n\n\t\t\t\t-- hunters can equip a dagger and a bow, but not two of each\n\t\t\t\tif baseData.equipmentType == \"dagger\" or baseData.equipmentType == \"bow\" then\n\t\t\t\t\tif equipmentSlotPosition == 1 then\n\t\t\t\t\t\tlocal offhand = getEquipmentDataByPosition(playerEquipmentCopy, 11)\n\t\t\t\t\t\tif offhand then\n\t\t\t\t\t\t\toffhand = itemLookup[offhand.id]\n\t\t\t\t\t\t\tif offhand then\n\t\t\t\t\t\t\t\tequipLegal = equipLegal and (offhand.equipmentType ~= baseData.equipmentType)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\telseif equipmentSlotPosition == 11 then\n\t\t\t\t\t\tlocal mainHand = getEquipmentDataByPosition(playerEquipmentCopy, 1)\n\t\t\t\t\t\tif mainHand then\n\t\t\t\t\t\t\tmainHand = itemLookup[mainHand.id]\n\t\t\t\t\t\t\tif mainHand then\n\t\t\t\t\t\t\t\tequipLegal = equipLegal and (mainHand.equipmentType ~= baseData.equipmentType)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\t-- make some offhands illegal\n\t\t\t\tif equipmentSlotPosition == 11 then\n\t\t\t\t\tlocal equipmentType = baseData.equipmentType\n\t\t\t\t\tif\n\t\t\t\t\t\tequipmentType == \"staff\" or\n\t\t\t\t\t\tequipmentType == \"greatsword\" or\n\t\t\t\t\t\tequipmentType == \"fishing-rod\"\n\t\t\t\t\tthen\n\t\t\t\t\t\tequipLegal = false\n\t\t\t\t\tend\n\n\t\t\t\tend\n\n\t\t\t\t-- no offhands if you're holding a greatsword (unless it's an amulet)\n\t\t\t\tif equipmentSlotPosition == 11 then\n\t\t\t\t\tlocal mainHand = getEquipmentDataByPosition(playerEquipmentCopy, 1)\n\t\t\t\t\tif mainHand then\n\t\t\t\t\t\tmainHand = itemLookup[mainHand.id]\n\t\t\t\t\t\tif mainHand then\n\t\t\t\t\t\t\tif mainHand.equipmentType == \"greatsword\" and baseData.equipmentType ~= \"amulet\" then\n\t\t\t\t\t\t\t\tequipLegal = false\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\t-- no greatswords if you're holding an offhand (unless it's an amulet)\n\t\t\t\tif equipmentSlotPosition == 1 and baseData.equipmentType == \"greatsword\" then\n\t\t\t\t\tlocal offhand = getEquipmentDataByPosition(playerEquipmentCopy, 11)\n\t\t\t\t\tif offhand then\n\t\t\t\t\t\toffhand = itemLookup[offhand.id]\n\t\t\t\t\t\tif offhand and (offhand.equipmentType ~= \"amulet\") then\n\t\t\t\t\t\t\tequipLegal = false\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\t-- now we can use it\n\t\t\t\tif not equipLegal then\n\t\t\t\t\treturn false, \"attempt to equip illegal item\"\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal function onEquipped(itemId, equipmentSlot)\n\t\t\t\tlocal itemData = itemLookup[itemId]\n\t\t\t\tif itemData.perks then\n\t\t\t\t\tfor perkName, _ in pairs(itemData.perks) do\n\t\t\t\t\t\tlocal perkData = perkLookup[perkName]\n\t\t\t\t\t\tif perkData and perkData.onEquipped then\n\t\t\t\t\t\t\tlocal success, err = pcall(function()\n\t\t\t\t\t\t\t\tperkData.onEquipped(player, itemData, tostring(equipmentSlot))\n\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\tif not success then\n\t\t\t\t\t\t\t\twarn(string.format(\"item %s equip failed because: %s\", itemData.name, err))\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal function onUnequipped(itemId, equipmentSlot)\n\t\t\t\tlocal itemData = itemLookup[itemId]\n\t\t\t\tif itemData.perks then\n\t\t\t\t\tfor perkName, _ in pairs(itemData.perks) do\n\t\t\t\t\t\tlocal perkData = perkLookup[perkName]\n\t\t\t\t\t\tif perkData and perkData.onUnequipped then\n\t\t\t\t\t\t\tlocal success, err = pcall(function()\n\t\t\t\t\t\t\t\tperkData.onUnequipped(player, itemData, tostring(equipmentSlot))\n\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\tif not success then\n\t\t\t\t\t\t\t\twarn(string.format(\"item %s unequip failed because: %s\", itemData.name, err))\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- never allow real swapping with arrows\n\t\t\tif equipmentSlotPosition == mapping.equipmentPosition.arrow then\n\t\t\t\treturn false\n\t\t\tend\n\n\t\t\tif not trueInventorySlotPosition and trueEquipmentSlotPosition then\n\t\t\t\t-- inventory is empty, equipment is not empty.\n\t\t\t\t-- move equipment to that slot\n\t\t\t\tif not int__doesPlayerHaveInventorySpaceForTrade(player, {}, {playerEquipmentCopy[trueEquipmentSlotPosition]}) then\n\t\t\t\t\t-- denied, force a refresh\n\t\t\t\t\tonClientRequestPropogateCacheData(player, \"inventory\")\n\t\t\t\t\tonClientRequestPropogateCacheData(player, \"equipment\")\n\n\t\t\t\t\twarn(\"inventory is full, no go\")\n\n\t\t\t\t\treturn false\n\t\t\t\tend\n\n\t\t\t\tlocal equipmentSlotData \t= table.remove(playerEquipmentCopy, trueEquipmentSlotPosition)\n\t\t\t\tequipmentSlotData.position \t= inventorySlotPosition\n\n\t\t\t\ttable.insert(playerInventoryCopy, equipmentSlotData)\n\n\t\t\t\tplayerDataContainer[player].nonSerializeData.setPlayerData(\"inventory\", playerInventoryCopy)\n\t\t\t\tplayerDataContainer[player].nonSerializeData.setPlayerData(\"equipment\", playerEquipmentCopy)\n\n\t\t\t\tevents:fireEventLocal(\"playerEquipmentChanged\", player)\n\t\t\t\tonUnequipped(equipmentSlotData.id, equipmentSlotPosition)\n\n\t\t\t\treturn true\n\t\t\telseif trueInventorySlotPosition and trueEquipmentSlotPosition then\n\t\t\t\t-- inventory is not empty, equipment is not empty.\n\t\t\t\tlocal inventorySlotData \t= table.remove(playerInventoryCopy, trueInventorySlotPosition)\n\t\t\t\tinventorySlotData.position \t= equipmentSlotPosition\n\n\t\t\t\tlocal equipmentSlotData \t= table.remove(playerEquipmentCopy, trueEquipmentSlotPosition)\n\t\t\t\tequipmentSlotData.position \t= inventorySlotPosition\n\n\t\t\t\ttable.insert(playerInventoryCopy, equipmentSlotData)\n\t\t\t\ttable.insert(playerEquipmentCopy, inventorySlotData)\n\n\t\t\t\tplayerDataContainer[player].nonSerializeData.setPlayerData(\"inventory\", playerInventoryCopy)\n\t\t\t\tplayerDataContainer[player].nonSerializeData.setPlayerData(\"equipment\", playerEquipmentCopy)\n\n\t\t\t\tevents:fireEventLocal(\"playerEquipmentChanged\", player)\n\t\t\t\tonUnequipped(equipmentSlotData.id, equipmentSlotPosition)\n\t\t\t\tonEquipped(inventorySlotData.id, equipmentSlotPosition)\n\n\t\t\t\treturn true\n\t\t\telseif trueInventorySlotPosition and not trueEquipmentSlotPosition then\n\t\t\t\t-- inventory is not empty, equipment is empty.\n\t\t\t\tlocal inventorySlotData \t= table.remove(playerInventoryCopy, trueInventorySlotPosition)\n\t\t\t\tinventorySlotData.position \t= equipmentSlotPosition\n\n\t\t\t\ttable.insert(playerEquipmentCopy, inventorySlotData)\n\n\t\t\t\tplayerDataContainer[player].nonSerializeData.setPlayerData(\"inventory\", playerInventoryCopy)\n\t\t\t\tplayerDataContainer[player].nonSerializeData.setPlayerData(\"equipment\", playerEquipmentCopy)\n\n\t\t\t\tevents:fireEventLocal(\"playerEquipmentChanged\", player)\n\t\t\t\tonEquipped(inventorySlotData.id, equipmentSlotPosition)\n\n\t\t\t\treturn true\n\t\t\telse\n\t\t\t\t-- wtf?\n\n\t\t\t\t--warn(\"wtf?!?!\")\n\n\t\t\t\tonClientRequestPropogateCacheData(player, \"inventory\")\n\t\t\t\tonClientRequestPropogateCacheData(player, \"equipment\")\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\tend\n\n\twarn(\"straight up denial\")\n\n\t-- denied, force a refresh\n\tonClientRequestPropogateCacheData(player, \"inventory\")\n\tonClientRequestPropogateCacheData(player, \"equipment\")\n\treturn false\nend\n\nlocal function swapPlayerWeapons(player)\n\tlocal playerData = playerDataContainer[player]\n\tif not playerData then return end\n\n\tlocal equipment = playerData.equipment\n\tif not equipment then return end\n\n\tlocal equipmentCopy = utilities.copyTable(equipment)\n\tlocal mainHand, mainHandIndex, offhand, offhandIndex\n\n\tfor index, slotData in pairs(equipmentCopy) do\n\t\tif slotData.position == 1 then\n\t\t\tmainHand = itemLookup[slotData.id]\n\t\t\tmainHandIndex = index\n\n\t\telseif slotData.position == 11 then\n\t\t\toffhand = itemLookup[slotData.id]\n\t\t\toffhandIndex = index\n\t\tend\n\tend\n\n\t-- only swap if we have two weapons\n\tif (not mainHand) or (not offhand) then return end\n\n\tlocal shouldSwap = false\n\n\t-- cases where swap is valid, for now only dagger/bow\n\tif mainHand.equipmentType == \"dagger\" then\n\t\tshouldSwap = offhand.equipmentType == \"bow\"\n\n\telseif mainHand.equipmentType == \"bow\" then\n\t\tshouldSwap = offhand.equipmentType == \"dagger\"\n\tend\n\n\t-- perform the swap\n\tif not shouldSwap then return end\n\n\tequipmentCopy[mainHandIndex].position = 11\n\tequipmentCopy[offhandIndex].position = 1\n\n\tplayerData.nonSerializeData.setPlayerData(\"equipment\", equipmentCopy)\n\n\tlocal function firePerkEvents(itemData, oldSlot, newSlot)\n\t\tif itemData.perks then\n\t\t\tfor perkName, _ in pairs(itemData.perks) do\n\t\t\t\tlocal perkData = perkLookup[perkName]\n\t\t\t\tif perkData and perkData.onUnequipped then\n\t\t\t\t\tperkData.onUnequipped(player, itemData, oldSlot)\n\t\t\t\tend\n\t\t\t\tif perkData and perkData.onEquipped then\n\t\t\t\t\tperkData.onEquipped(player, itemData, newSlot)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tfirePerkEvents(mainHand, \"1\", \"11\")\n\tfirePerkEvents(offhand, \"11\", \"1\")\n\n\tevents:fireEventLocal(\"playerEquipmentChanged\", player)\nend\n\n-- Give clients a section of their data file that they can freely modify/read (maybe with some sanity checks)\n\nlocal function changePlayerSetting(player, setting, value)\n\tlocal playerData = getPlayerData(player)\n\tif playerData and playerData.userSettings then\n\n\t\t-- todo: maybe do some basic sanity checks?\n\t\tif type(setting) == \"string\" and #setting < 40 then\n\n\t\t\tplayerData.userSettings[setting] = value\n\t\t\tonClientRequestPropogateCacheData(player, \"userSettings\")\n\n\t\t\treturn true\n\t\tend\n\n\tend\nend\n\n\n-- keybind stuff\n\nlocal function playerRequestSetKeyAction(player,key,action)\n\t-- validation\n\tif type(action) == \"string\"\tand #action < 40 then\n\t\t-- store the key preference\n\t\tlocal playerData = getPlayerData(player)\n\n\t\tlocal preferences = playerData.userSettings.keybinds or {}\n\n\t\t-- remove old keybinds\n\t\tfor key, existingAction in pairs(preferences) do\n\t\t\tif existingAction == action then\n\t\t\t\tpreferences[key] = nil\n\t\t\tend\n\t\tend\n\n\t\tpreferences[key] = action\n\n\t\tplayerData.userSettings.keybinds = preferences\n\t\tonClientRequestPropogateCacheData(player, \"userSettings\")\n\t\treturn true\n\tend\nend\n\n\nlocal function onDamagePlayer(player, damage)\n\tplayer.Character.PrimaryPart.health.Value = player.Character.PrimaryPart.health.Value - damage\nend\n\nlocal function getTrueInventorySlotDataByInventorySlotDataFromPlayer(player, inventorySlotDataFromPlayer)\n\tlocal playerData = playerDataContainer[player]\n\tif playerData then\n\t\tfor trueInventorySlot, inventorySlotData in pairs(playerData.inventory) do\n\t\t\tif inventorySlotData.position == inventorySlotDataFromPlayer.position and inventorySlotData.id == inventorySlotDataFromPlayer.id then\n\t\t\t\treturn trueInventorySlot, inventorySlotData\n\t\t\tend\n\t\tend\n\tend\n\n\treturn nil, nil\nend\n\nlocal function getTrueEquipmentSlotDataByEquipmentSlotDataFromPlayer(player, equipmentSlotDataFromPlayer)\n\tlocal playerData = playerDataContainer[player]\n\tif playerData then\n\t\tlocal itemBaseDataFromPlayer = itemLookup[equipmentSlotDataFromPlayer.id]\n\t\tfor trueEquipmentSlot, equipmentSlotData in pairs(playerData.equipment) do\n\t\t\tlocal itemBaseData = itemLookup[equipmentSlotData.id]\n\t\t\tif equipmentSlotData.position == equipmentSlotDataFromPlayer.position and itemBaseData.category == itemBaseDataFromPlayer.category then\n\t\t\t\treturn trueEquipmentSlot, equipmentSlotData\n\t\t\tend\n\t\tend\n\tend\n\n\treturn nil, nil\nend\n\n\n-- dont mind me just inserting this here\n\nlocal megaphoneConnection\n\nlocal isMessagingEnabled, messagingError = pcall(function()\n\tmegaphoneConnection = game:GetService(\"MessagingService\"):SubscribeAsync(\"megaphone\", function(message)\n\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {Text = message.Data; Font = Enum.Font.SourceSansBold; Color = Color3.fromRGB(196, 209, 216)} )\n\tend)\nend)\n\nif not isMessagingEnabled then\n\twarn(\"Failed to enable MessagingService\", messagingError)\nend\n\nlocal function int__decrementStackSizeForInventorySlotData(player, trueInventorySlotDataPosition, stacksToRemove)\n\tif not player or not playerDataContainer[player] then return nil end\n\tstacksToRemove = stacksToRemove or 1\n\n\tlocal inventorySlotData = playerDataContainer[player].inventory[trueInventorySlotDataPosition]\n\tif inventorySlotData then\n\n\t\tlocal stacksRemoved = math.clamp(stacksToRemove, 0, inventorySlotData.stacks)\n\t\tif inventorySlotData.stacks and inventorySlotData.stacks >= stacksToRemove then\n\t\t\tinventorySlotData.stacks \t= inventorySlotData.stacks - stacksToRemove\n\n\t\t\tif inventorySlotData.stacks <= 0 then\n\t\t\t\ttable.remove(playerDataContainer[player].inventory, trueInventorySlotDataPosition)\n\t\t\tend\n\t\telse\n\t\t\tstacksRemoved = inventorySlotData.stacks\n\t\t\ttable.remove(playerDataContainer[player].inventory, trueInventorySlotDataPosition)\n\t\tend\n\n\t\t-- update the inventory\n\t\tonClientRequestPropogateCacheData(player, \"inventory\")\n\n\t\treturn stacksRemoved\n\tend\n\n\treturn 0\nend\n\n-- removes _stacks[=1] stacks from inventorySlotData at position `inventorySlotPosition`\nlocal function onRemovePlayerInventorySlotData(player, inventorySlotData, stacksToRemove)\n\tif not player or not playerDataContainer[player] or not inventorySlotData then return nil end\n\tlocal itemBaseData = itemLookup[inventorySlotData.id]\n\tstacksToRemove = math.clamp(stacksToRemove or 1, 1, (itemBaseData.stackSize or MAX_COUNT_PER_STACK))\n\n\tlocal playerData = playerDataContainer[player]\n\n\tlocal itemBaseData = itemLookup[inventorySlotData.id]\n\tif itemBaseData then\n\t\tif itemBaseData.canStack then\n\t\t\tfor inventorySlotPosition, trueInventorySlotData in pairs(playerData.inventory) do\n\t\t\t\tif trueInventorySlotData.position == inventorySlotData.position and trueInventorySlotData.id == inventorySlotData.id then\n\t\t\t\t\tlocal canRemoveStacksRequested = ((trueInventorySlotData.stacks or 1) - stacksToRemove) >= 0\n\n\t\t\t\t\tif canRemoveStacksRequested then\n\t\t\t\t\t\tif trueInventorySlotData.stacks then\n\t\t\t\t\t\t\ttrueInventorySlotData.stacks = trueInventorySlotData.stacks\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\treturn false\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\t-- item doesn't stack, can only remove one stack from this item (so entire item)\n\t\t\tif stacksToRemove <= 1 then\n\t\t\t\tfor inventorySlotPosition, trueInventorySlotData in pairs(playerData.inventory) do\n\t\t\t\t\tif trueInventorySlotData.position == inventorySlotData.position and trueInventorySlotData.id == inventorySlotData.id then\n\t\t\t\t\t\ttable.remove(playerData.inventory, inventorySlotPosition)\n\n\t\t\t\t\t\t-- update the inventory\n\t\t\t\t\t\tonClientRequestPropogateCacheData(player, \"inventory\")\n\n\t\t\t\t\t\treturn true\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telse\n\t\t\t\treturn false, \"attempt to remove more than 1 stack from non-stackable item\"\n\t\t\tend\n\t\tend\n\tend\n\n\n\n\twarn(\"just nothing found\")\n\treturn false, 0\nend\n\n-- let the player be authoritative in this regard, it's their personal data anyway.\nlocal function onRegisterHotbarSlotData(player, dataType, id, position)\n\tif not playerDataContainer[player] then return end\n\n\tif not id or not dataType then\n\t\tif position then\n\t\t\tlocal alteration = false\n\t\t\t-- clear out the position for this one\n\t\t\tfor i, hotbarSlotData in pairs(playerDataContainer[player].hotbar) do\n\t\t\t\tif hotbarSlotData.position == position then\n\t\t\t\t\ttable.remove(playerDataContainer[player].hotbar, i)\n\n\t\t\t\t\talteration = true\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif alteration then\n\t\t\t\t-- update the inventory\n\t\t\t\tonClientRequestPropogateCacheData(player, \"hotbar\")\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\telseif position then\n\t\t-- clear out the position for this one\n\t\tfor i, hotbarSlotData in pairs(playerDataContainer[player].hotbar) do\n\t\t\tif hotbarSlotData.position == position then\n\t\t\t\ttable.remove(playerDataContainer[player].hotbar, i)\n\t\t\tend\n\t\tend\n\n\t\ttable.insert(playerDataContainer[player].hotbar, {dataType = dataType; id = id; position = position})\n\n\t\t-- update the inventory\n\t\tonClientRequestPropogateCacheData(player, \"hotbar\")\n\t\treturn true\n\tend\nend\n\nlocal seatsTaken = {}\n\nlocal function isSeatTaken(seat)\n\tfor i, otherSeat in pairs(seatsTaken) do\n\t\tif otherSeat == seat then\n\t\t\treturn true\n\t\tend\n\tend\nend\n\nlocal function onReplicateClientStateChanged(player, state, stateVariant, otherData)\n\t-- never replicate the dead state, client is not incharge of this.\n\tif state == \"dead\" then\n\t\treturn false\n\tend\n\n\tstateVariant = stateVariant or \"\"\n\n\tif player.Character and player.Character.PrimaryPart and (player.Character.PrimaryPart.state.Value ~= state or player.Character.PrimaryPart.state.variant.Value ~= stateVariant) and player.Character.PrimaryPart.state.Value ~= \"dead\" then\n\t\tlocal previousState = player.Character.PrimaryPart.state.Value\n\n\t\tplayer.Character.PrimaryPart.state.variant.Value = stateVariant or \"\"\n\t\tplayer.Character.PrimaryPart.state.Value = state\n\n\t\t-- handle sitting/unsitting\n\t\tif state == \"sitting\" and otherData then\n\t\t\t-- new state is sitting\n\t\t\tlocal existingSeat = seatsTaken[player.Name]\n\t\t\tif existingSeat then\n\t\t\t\tgame.CollectionService:AddTag(existingSeat,\"interact\")\n\t\t\t\tseatsTaken[player.Name] = nil\n\t\t\tend\n\n\t\t\tif isSeatTaken(otherData) then\n\t\t\t\t-- tell the client to go away\n\t\t\t\tplayer.Character.PrimaryPart.Anchored = false\n\t\t\t\tplayer.Character.PrimaryPart:SetNetworkOwner(player)\n\n\t\t\t\treturn false\n\t\t\telse\n\t\t\t\tplayer.Character.PrimaryPart:SetNetworkOwner(nil)\n\t\t\t\tplayer.Character.PrimaryPart.Anchored = true\n\t\t\t\tseatsTaken[player.Name] = otherData\n\t\t\t\tgame.CollectionService:RemoveTag(otherData, \"interact\")\n\t\t\tend\n\n\t\t\tif seatsTaken[player.Name] == otherData then\n\t\t\t\tplayer.Character.PrimaryPart.grounder.Position = otherData.CFrame.p + Vector3.new(0, 0.5, 0)\n\t\t\t\tplayer.Character.PrimaryPart.hitboxVelocity.Velocity = Vector3.new()\n\t\t\t\tplayer.Character.PrimaryPart.hitboxGyro.CFrame = CFrame.new(otherData.CFrame.p + Vector3.new(0, 0.5, 0), otherData.CFrame.p + Vector3.new(0, 0.5, 0) + otherData.CFrame.lookVector)\n\t\t\t\tplayer.Character.PrimaryPart.CFrame = otherData.CFrame + Vector3.new(0, 0.5, 0)\n\t\t\tend\n\t\telseif previousState == \"sitting\" then\n\t\t\tif otherData and otherData == \"override\" then\n\t\t\t\treturn false\n\t\t\tend\n\n\t\t\t-- old state was sitting\n\t\t\tplayer.Character.PrimaryPart.Anchored = false\n\t\t\tplayer.Character.PrimaryPart:SetNetworkOwner(player)\n\n\t\t\tfor userName,seat in pairs(seatsTaken) do\n\t\t\t\tif userName == player.Name or game.Players:FindFirstChild(userName) == nil then\n\t\t\t\t\tgame.CollectionService:AddTag(seat,\"interact\")\n\t\t\t\t\tseatsTaken[player.Name] = nil\n\t\t\t\tend\n\t\t\tend\n\t\telseif state == \"gettingUp\" and otherData then\n\n\t\t\tlocal MINIMUM_DAMAGE = 30\n\t\t\tlocal MAXIMUM_DAMAGE = 120\n\t\t\tlocal MINIMUM_DAMAGE_FALL_DISTANCE = 100\n\t\t\tlocal MAXIMUM_DAMAGE_FALL_DISTANCE = 400\n\n\t\t\tlocal playerData = playerDataContainer[player]\n\n\t\t\tlocal fallingDistance \t= math.clamp(math.abs(otherData), MINIMUM_DAMAGE_FALL_DISTANCE, MAXIMUM_DAMAGE_FALL_DISTANCE)\n\t\t\tlocal damageTaken \t\t= math.floor(MINIMUM_DAMAGE + (MAXIMUM_DAMAGE - MINIMUM_DAMAGE) * ((fallingDistance - MINIMUM_DAMAGE_FALL_DISTANCE) / (MAXIMUM_DAMAGE_FALL_DISTANCE - MINIMUM_DAMAGE_FALL_DISTANCE))) * math.clamp(1 - playerData.nonSerializeData.statistics_final.featherFalling, 0, 1)\n\n\t\t\tplayer.Character.PrimaryPart.health.Value = math.clamp(player.Character.PrimaryPart.health.Value - damageTaken, 0, player.Character.PrimaryPart.maxHealth.Value)\n\t\tend\n\tend\nend\n\nlocal function onReplicateClientWeaponStateChanged(player, weaponState)\n\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.weaponState.Value ~= weaponState and player.Character.PrimaryPart.state.Value ~= \"dead\" then\n\t\tlocal previousState \t= player.Character.PrimaryPart.weaponState.Value\n\t\tlocal playerData \t\t= playerDataContainer[player]\n\t\tlocal equipmentData \t= onGetPlayerEquipmentDataByEquipmentPosition(player, 1)\n\t\tlocal weaponBaseData \t= equipmentData and itemLookup[equipmentData.id] or nil\n\n\t\t-- block bad requests\n\t\tif weaponState ~= nil and weaponState ~= \"\" then\n\t\t\tif not weaponBaseData then\n\t\t\t\treturn false\n\t\t\telseif weaponBaseData.equipmentType == \"bow\" then\n\t\t\t\tif weaponState ~= \"stretched\" and weaponState ~= \"firing\" then\n\t\t\t\t\treturn false\n\t\t\t\tend\n\t\t\telse\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\n\t\tplayer.Character.PrimaryPart.weaponState.Value = weaponState or \"\"\n\tend\nend\n\n\n-- NOTE: This is not the same route that state animations take (ie, walking running etc!)\nlocal function onReplicatePlayerAnimationSequence(player, animationCollection, animationName, extraData)\n\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.state.Value ~= \"dead\" then\n\t\tif playerDataContainer[player] and (animationCollection == \"swordAnimations\" or animationCollection == \"staffAnimations\" or animationCollection == \"daggerAnimations\" or animationCollection == \"bowAnimations\") then\n\t\t\textraData = extraData or {}\n\t\t\t\textraData.attackSpeed = playerDataContainer[player].nonSerializeData.statistics_final.attackSpeed or 0\n\t\tend\n\n\t\tif animationCollection == \"staffAnimations\" and extraData then\n\t\t\tif player.Character.PrimaryPart.mana.Value < configuration.getConfigurationValue(\"mageManaDrainFromBasicAttack\") then\n\t\t\t\textraData.noRangeManaAttack = true\n\t\t\tend\n\t\tend\n\n\t\t-- let the server know we are replicating this\n\t\tnetwork:fire(\"playerAnimationReplicated\", player, animationCollection, animationName, extraData)\n\n\t\t-- replicate this to other clients\n\t\tnetwork:fireAllClientsExcludingPlayer(\"replicatePlayerAnimationSequence\", player, player, animationCollection, animationName, extraData)\n\tend\nend\n\nlocal function isPlayerNearResetCharacter(player)\n\tlocal char = player.Character\n\tif not char then return false end\n\tlocal manifest = char.PrimaryPart\n\tif not manifest then return false end\n\n\tlocal resetCharacters = game:GetService(\"CollectionService\"):GetTagged(\"resetCharacter\")\n\tif #resetCharacters == 0 then return false end\n\n\tfor _, resetCharacter in pairs(resetCharacters) do\n\t\tlocal root = resetCharacter.PrimaryPart\n\t\tif root then\n\t\t\tlocal distance = (manifest.Position - root.Position).Magnitude\n\t\t\tif distance <= 8 then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal function playerRequest_transferInventoryToStorage(player, inventorySlotData)\n\tif not configuration.getConfigurationValue(\"isTradingEnabled\", player) then return false, \"Storage has been disabled.\" end\n\tif math.floor(inventorySlotData.stacks) ~= inventorySlotData.stacks then return false, \"MagicRebirthed... BAD!\" end\n\t--if not player:FindFirstChild(\"QA\") then return false, \"Only testers can use Storage right now.\" end\n\tif not configuration.getConfigurationValue(\"isStorageEnabled\", player) then return false, \"Storage is currently disabled\" end\n\n\tlocal playerData = playerDataContainer[player]\n\n\tif playerData and #playerData.globalData.itemStorage < MAX_STORAGE_COUNT then\n\t\treturn int__transferInventoryToStorage(player, inventorySlotData)\n\telseif playerData then\n\t\treturn false, \"Inventory full.\"\n\tend\n\n\treturn false, \"PlayerData not found.\"\nend\n\nlocal function playerRequest_transferStorageToInventory(player, storageSlotData)\n\tif not configuration.getConfigurationValue(\"isTradingEnabled\", player) then return false, \"Storage has been disabled.\" end\n\tif math.floor(storageSlotData.stacks) ~= storageSlotData.stacks then return false, \"MagicRebirthed... BAD!\" end\n\t--if not player:FindFirstChild(\"QA\") then return false, \"Only testers can use Storage right now.\" end\n\tif not configuration.getConfigurationValue(\"isStorageEnabled\", player) then return false, \"Storage is currently disabled\" end\n\n\tlocal playerData = playerDataContainer[player]\n\n\tif playerData then\n\t\treturn int__transferStorageToInventory(player, storageSlotData)\n\tend\n\n\treturn false, \"PlayerData not found.\"\nend\n\nfunction module.init(Modules)\n\tdatastoreInterface = Modules.datastoreInterface\n\tnetwork = Modules.network\n\tutilities = Modules.utilities\n\tphysics = Modules.physics\n\tlevels = Modules.levels\n\tmapping = Modules.mapping\n\tconfiguration = Modules.configuration\n\tplaceSetup = Modules.placeSetup\n\tevents = Modules.events\n\tdetection = Modules.detection\n\tprojectile = Modules.projectile\n\n\tentityManifestCollectionFolder = placeSetup.getPlaceFolder(\"entityManifestCollection\")\n\ttemporaryEquipmentFolder = placeSetup.getPlaceFolder(\"temporaryEquipment\")\n\n\t-- data manipulation\n\tnetwork:create(\"switchInventorySlotData\", \"RemoteFunction\", \"OnServerInvoke\", onSwitchInventorySlotDataRequestReceived)\n\tnetwork:create(\"playerRequest_switchInventorySlotData\", \"RemoteFunction\", \"OnServerInvoke\", onSwitchInventorySlotDataRequestReceived)\n\tnetwork:create(\"transferInventoryToEquipment\", \"RemoteFunction\", \"OnServerInvoke\", onTransferInventoryToEquipment)\n\tnetwork:create(\"playerRequest_transferInventoryToEquipment\", \"RemoteFunction\", \"OnServerInvoke\", onTransferInventoryToEquipment)\n\tnetwork:create(\"playerRequest_transferInventoryToStorage\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_transferInventoryToStorage)\n\tnetwork:create(\"playerRequest_transferStorageToInventory\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_transferStorageToInventory)\n\tnetwork:create(\"requestAddItemToInventory\", \"BindableFunction\", \"OnInvoke\", onRequestAddItemToInventoryReceived)\n\tnetwork:create(\"onPlayerRemoving\", \"BindableFunction\", \"OnInvoke\", onPlayerRemoving)\n\tnetwork:create(\"teleportPlayerCFrame_server\", \"BindableFunction\", \"OnInvoke\", function(player, targetCFrame)\n\t\tif playerPositionDataContainer[player] and player.Character and player.Character.PrimaryPart then\n\t\t\tplayerPositionDataContainer[player].positions = {{position = targetCFrame.p; velocity = Vector3.new()}}\n\t\t\tplayer.Character.PrimaryPart.CFrame = targetCFrame\n\t\tend\n\tend)\n\tnetwork:create(\"playerRequestAlphaGift\", \"RemoteFunction\", \"OnServerInvoke\")\n\tnetwork:create(\"playerRequestSetKeyAction\",\"RemoteFunction\",\"OnServerInvoke\",playerRequestSetKeyAction)\n\tnetwork:create(\"playerRequest_changeAccessories\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_changeAccessories)\n\tnetwork:create(\"playerRequest_swapWeapons\", \"RemoteEvent\", \"OnServerEvent\", swapPlayerWeapons)\n\tnetwork:create(\"playerRequest_swapWeapons_yielding\", \"RemoteFunction\", \"OnServerInvoke\", swapPlayerWeapons)\n\tnetwork:create(\"requestChangePlayerSetting\",\"RemoteFunction\",\"OnServerInvoke\",changePlayerSetting)\n\n\n\t-- data interfacing with client\n\tnetwork:create(\"getPropogationCacheLookupTable\", \"RemoteFunction\", \"OnServerInvoke\", getPropogationCacheLookupTable)\n\tnetwork:create(\"propogateCacheDataRequest\", \"RemoteEvent\", \"OnServerEvent\", onClientRequestPropogateCacheData)\n\tnetwork:create(\"clientFlushPropogationCache\", \"RemoteEvent\", \"OnServerEvent\", onClientRequestFlushPropogationCache)\n\tnetwork:create(\"getPlayerEquipment\", \"RemoteFunction\", \"OnServerInvoke\", onGetPlayerEquipment)\n\tnetwork:create(\"playerRequest_getPlayerEquipmentData\", \"RemoteFunction\", \"OnServerInvoke\", onGetPlayerEquipment)\n\tnetwork:create(\"dataRecoveryRequested\", \"RemoteEvent\", \"OnServerEvent\", onDataRecoveryRequested)\n\tnetwork:create(\"playerEquipmentChanged\", \"RemoteEvent\")\n\tnetwork:create(\"loadPlayerData\", \"RemoteFunction\", \"OnServerInvoke\", onPlayerAdded)\n\tnetwork:create(\"playerRequest_setupPlayerData\", \"RemoteFunction\", \"OnServerInvoke\", onPlayerAdded)\n\n\tnetwork:create(\"requestSplitInventorySlotDataStack\", \"RemoteFunction\", \"OnServerInvoke\", onRequestSplitInventorySlotDataStack)\n\tnetwork:create(\"playerRequest_splitInventorySlotDataStack\", \"RemoteFunction\", \"OnServerInvoke\", onRequestSplitInventorySlotDataStack)\n\tnetwork:create(\"registerHotbarSlotData\", \"RemoteFunction\", \"OnServerInvoke\", onRegisterHotbarSlotData)\n\tnetwork:create(\"playerRequest_getHotbarSlotData\", \"RemoteFunction\", \"OnServerInvoke\", onRegisterHotbarSlotData)\n\tnetwork:create(\"replicateClientStateChanged\", \"RemoteEvent\", \"OnServerEvent\", onReplicateClientStateChanged)\n\tnetwork:create(\"replicateClientWeaponStateChanged\", \"RemoteEvent\", \"OnServerEvent\", onReplicateClientWeaponStateChanged)\n\tnetwork:create(\"playerRequest_equipTemporaryEquipment\", \"RemoteFunction\", \"OnServerInvoke\", onPlayerRequest_equipTemporaryEquipment)\n\tnetwork:create(\"getIsPlayerOfClass\", \"RemoteFunction\", \"OnServerInvoke\", isPlayerOfClass)\n\tnetwork:create(\"serverVerifyCharacterPosition\", \"RemoteEvent\", \"OnServerEvent\", forceCharacterPosition)\n\n\t-- data interfacing with server\n\tnetwork:create(\"getPlayerData\", \"BindableFunction\", \"OnInvoke\", getPlayerData)\n\tnetwork:create(\"getPlayerData_remote\", \"RemoteFunction\", \"OnServerInvoke\", getPlayerData_remote)\n\tnetwork:create(\"getPlayerEquipmentDataByEquipmentPosition\", \"BindableFunction\", \"OnInvoke\", onGetPlayerEquipmentDataByEquipmentPosition)\n\tnetwork:create(\"getPlayerInventorySlotDataByInventorySlotPosition\", \"BindableFunction\", \"OnInvoke\", onGetPlayerInventorySlotDataByInventorySlotPosition)\n\tnetwork:create(\"removePlayerInventorySlotData\", \"BindableFunction\", \"OnInvoke\", onRemovePlayerInventorySlotData)\n\tnetwork:create(\"playerEquipmentChanged_server\", \"BindableEvent\")\n\tnetwork:create(\"doesPlayerHaveInventorySpaceForTrade\", \"BindableFunction\", \"OnInvoke\", int__doesPlayerHaveInventorySpaceForTrade)\n\tnetwork:create(\"getPlayerGlobalData\", \"BindableFunction\", \"OnInvoke\", getPlayerGlobalData)\n\tnetwork:create(\"setPlayerGlobalData\", \"BindableFunction\", \"OnInvoke\", setPlayerGlobalData)\n\tnetwork:create(\"serverChangePlayerSetting\",\"BindableFunction\",\"OnInvoke\",changePlayerSetting)\n\tnetwork:create(\"getIsPlayerOfClass_server\", \"BindableFunction\", \"OnInvoke\", isPlayerOfClass)\n\n\t-- data routing\n\tnetwork:create(\"replicatePlayerAnimationSequence\", \"RemoteEvent\", \"OnServerEvent\", onReplicatePlayerAnimationSequence)\n\n\t-- events\n\tnetwork:create(\"playerCharacterDied\", \"BindableEvent\")\n\tnetwork:create(\"alertPlayerNotification\",\"RemoteEvent\")\n\tnetwork:create(\"playerRequest_incrementPlayerStatPointsByStatName\", \"RemoteFunction\", \"OnServerInvoke\", incrementPlayerStatPointsByStatName)\n\tnetwork:create(\"playerStatisticsChanged\", \"RemoteEvent\")\n\tnetwork:create(\"playerRequest_respawnMyCharacter\", \"RemoteFunction\", \"OnServerInvoke\", onPlayerRequest_respawnMyCharacter)\n\tnetwork:create(\"playerRequest_returnToMainMenu\", \"RemoteFunction\", \"OnServerInvoke\", onPlayerRequest_returnToMainMenu)\n\tnetwork:create(\"requestTradeBetweenPlayers\", \"BindableFunction\", \"OnInvoke\", onTradeRequestReceived)\n\tnetwork:create(\"deathGuiAccepted\", \"RemoteEvent\", \"OnServerEvent\", onDeathGuiAccepted)\n\tnetwork:create(\"deathGuiRequested\", \"RemoteEvent\")\n\tnetwork:create(\"promptPlayerDeathScreen\",\"RemoteEvent\")\n\tnetwork:create(\"playerCharacterLoaded\", \"BindableEvent\")\n\tnetwork:create(\"deathTrapKnockback\", \"RemoteEvent\")\n\tnetwork:create(\"logPerkActivation_server\", \"BindableEvent\", \"Event\", onLogPerkActivation_server)\n\tnetwork:create(\"playerDataLoaded\", \"BindableEvent\")\n\tnetwork:create(\"signal_inputChanged\", \"RemoteEvent\", \"OnServerEvent\", signal_inputChanged)\n\tnetwork:create(\"playerAppliedScroll\",\"RemoteEvent\")\n\n\t-- todo: probably tighten this\n\tnetwork:create(\"tradeItemsBetweenPlayerAndNPC\", \"BindableFunction\", \"OnInvoke\", int__tradeItemsBetweenPlayerAndNPC)\n\tnetwork:create(\"playerAnimationReplicated\", \"BindableEvent\")\n\tnetwork:create(\"setStamina\", \"RemoteEvent\")\n\tnetwork:create(\"signal_exp\", \"RemoteEvent\")\n\n\t-- random teleport crap\n\tnetwork:create(\"saveDataForTeleportation\", \"RemoteFunction\", \"OnServerInvoke\", saveDataForTeleport)\n\tnetwork:create(\"playerRequest_savePlayerDataForTeleportation\", \"RemoteFunction\", \"OnServerInvoke\", saveDataForTeleport)\n\tnetwork:create(\"saveDataForTeleport\", \"BindableFunction\", \"OnInvoke\", saveDataForTeleport)\n\tnetwork:create(\"externalTeleport\", \"RemoteEvent\")\n\n\t-- other trash\n\tnetwork:create(\"signal_alertChatMessage\", \"RemoteEvent\")\n\tnetwork:create(\"openLoreBookFromServer\", \"RemoteEvent\")\n\tnetwork:create(\"playerInventoryChanged_server\", \"BindableEvent\")\n\tnetwork:create(\"playerWasExhausted\", \"RemoteEvent\", \"OnServerEvent\", function()\n\n\tend)\n\n\tgame.Players.PlayerRemoving:connect(onPlayerRemoving)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_product.lua",
    "content": "local module = {}\n\nlocal paymentDataCache = {}\nlocal ProductCache = {d = \"1\"}\n\nlocal network\n\nlocal function processPayment(player, receiptInfo, playerGlobalData)\n\tif playerGlobalData == nil then\n\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\tif playerData and playerData.globalData then\n\t\t\tplayerGlobalData = playerData.globalData\n\t\telse\n\t\t\treturn false\n\t\tend\n\tend\n\n\tlocal additionalInfo = {}\n\n\tlocal doFinishTransaction = false\n\n\t-- 300 ethyr\n\tif receiptInfo.ProductId == 509935760 then\n\t\tplayerGlobalData.ethyr = (playerGlobalData.ethyr or 0) + 300\n\t\tdoFinishTransaction = true\n\t\tspawn(function()\n\t\t\tnetwork:invoke(\"reportCurrency\", player, \"ethyr\", 300, \"product:ethyr300\")\n\t\tend)\n\t-- 120 ethyr\n\telseif receiptInfo.ProductId == 509934399 then\n\t\tplayerGlobalData.ethyr = (playerGlobalData.ethyr or 0) + 120\n\t\tdoFinishTransaction = true\n\t\tspawn(function()\n\t\t\tnetwork:invoke(\"reportCurrency\", player, \"ethyr\", 120, \"product:ethyr120\")\n\t\tend)\n\t-- 750 ethyr\n\telseif receiptInfo.ProductId == 509935018 then\n\t\tplayerGlobalData.ethyr = (playerGlobalData.ethyr or 0) + 750\n\t\tdoFinishTransaction = true\n\t\tspawn(function()\n\t\t\tnetwork:invoke(\"reportCurrency\", player, \"ethyr\", 750, \"product:ethyr750\")\n\t\tend)\n\telseif receiptInfo.ProductId == 539152241 then\n\t-- 3500 ethyr (3000 + 500 bonus)\n\t\tplayerGlobalData.ethyr = (playerGlobalData.ethyr or 0) + 3500\n\t\tdoFinishTransaction = true\n\t\tspawn(function()\n\t\t\tnetwork:invoke(\"reportCurrency\", player, \"ethyr\", 3500, \"product:ethyr3500\")\n\t\tend)\n\tend\n\n\treturn doFinishTransaction, playerGlobalData, additionalInfo\nend\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\n\tnetwork:create(\"processPayment\", \"BindableFunction\", \"OnInvoke\", processPayment)\n\n\tnetwork:create(\"signal_productPurchaseConfirmed\", \"RemoteEvent\")\n\n\tif game.PlaceId ~= 2376885433 and game.PlaceId ~= 2015602902 then\n\t\tgame:GetService(\"MarketplaceService\").ProcessReceipt = function(receiptInfo)\n\n\n\n\t\t\tlocal player = game.Players:GetPlayerByUserId(receiptInfo.PlayerId)\n\t\t\tif not player then\n\t\t\t\t-- no player, abort.\n\t\t\t\twarn(\"aborted purchase due to missing player\")\n\t\t\t\treturn Enum.ProductPurchaseDecision.NotProcessedYet\n\t\t\tend\n\n\t\t\tif player:FindFirstChild(\"DataSaveFailed\") then\n\t\t\t\t-- player data has failed to save, abort.\n\t\t\t\twarn(\"aborted purchase due to save failure\")\n\t\t\t\treturn Enum.ProductPurchaseDecision.NotProcessedYet\n\t\t\tend\n\n\t\t\tlocal processingStartTime = tick()\n\n\t\t\trepeat wait(0.1) until player == nil or player.Parent ~= game.Players or network:invoke(\"getPlayerData\", player) or tick() - processingStartTime > 15\n\n\t\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\t\tif playerData == nil then\n\t\t\t\twarn(\"aborted purchase due to player missing\")\n\t\t\t\treturn Enum.ProductPurchaseDecision.NotProcessedYet\n\t\t\tend\n\n\t\t\tlocal playerGlobalData = playerData.globalData\n\t\t\tif playerGlobalData == nil then\n\t\t\t\twarn(\"aborted purchase due to player global data missing\")\n\t\t\t\treturn Enum.ProductPurchaseDecision.NotProcessedYet\n\t\t\tend\n\n\t\t\tpaymentDataCache[player] = paymentDataCache[player] or {}\n\t\t\tlocal playerPaymentDataCache = paymentDataCache[player]\n\t\t\tplayerPaymentDataCache.payments = playerPaymentDataCache.payments or {}\n\n\n\n\t\t\tlocal doFinishTransaction\n\t\t\tlocal modifiedGlobalPlayerData\n\t\t\tlocal additionalInfo\n\n\t\t\t-- Do not modify data if payment has already been cached this session\n\t\t\tif playerPaymentDataCache.payments[receiptInfo.PurchaseId] then\n\t\t\t\tdoFinishTransaction = true\n\t\t\telse\n\t\t\t\tdoFinishTransaction, modifiedGlobalPlayerData, additionalInfo = network:invoke(\"processPayment\", player, receiptInfo, playerGlobalData)\n\t\t\tend\n\n\t\t\tif doFinishTransaction and modifiedGlobalPlayerData then\n\n\t\t\t\tplayerData.nonSerializeData.setPlayerData(\"globalData\", modifiedGlobalPlayerData)\n\t\t\t\tplayerPaymentDataCache.payments[receiptInfo.PurchaseId] = true\n\n\n\t\t\t\tspawn(function()\n\n\n\t\t\t\t\tif additionalInfo.broadcast then\n\t\t\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", additionalInfo.broadcast)\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal ProductName = ProductCache[receiptInfo.ProductId]\n\t\t\t\t\tif ProductName == nil then\n\t\t\t\t\t\tlocal Product = game.MarketplaceService:GetProductInfo(receiptInfo.ProductId,Enum.InfoType.Product)\n\t\t\t\t\t\tif Product then\n\t\t\t\t\t\t\tProductName = Product.Name\n\t\t\t\t\t\t\tProductCache[receiptInfo.ProductId] = ProductName\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tProductName = \"unknown\"\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\tnetwork:invoke(\"purchaseMade\", player, \"Product\", ProductName, receiptInfo.CurrencySpent)\n\t\t\t\tend)\n\n\n\t\t\t\treturn Enum.ProductPurchaseDecision.PurchaseGranted\n\t\t\tend\n\n\n\t\tend\n\tend\nend\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/contents/manager_profession.lua",
    "content": "local module = {}\n\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal network\nlocal levels\n\nlocal professionLookup = require(ReplicatedStorage:WaitForChild(\"professionLookup\"))\n\nlocal function getProfessionLevel(player, profession)\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\tif playerData then\n\t\tif professionLookup[profession] then\n\t\t\tplayerData.professions[profession] = playerData.professions[profession] or {level = 1, exp = 0}\n\t\t\treturn playerData.professions[profession].level\n\t\tend\n\tend\nend\n\nlocal function grantProfessionExp(player, profession, exp)\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\tif playerData then\n\t\t-- professions are not hard-coded, can be added at any time.\n\t\tplayerData.professions[profession] = playerData.professions[profession] or {level = 1, exp = 0}\n\t\tlocal professionData = playerData.professions[profession]\n\n\t\tlocal expForNextLevel = levels.getEXPToNextLevel(professionData.level)\n\t\tif professionData.exp >= expForNextLevel then\n\t\t\t-- profession level up!\n\n\t\t\tprofessionData.exp = professionData.exp - expForNextLevel\n\t\t\tprofessionData.level = professionData.level + 1\n\n\t\t\tlocal professionTag = player.professions:FindFirstChild(profession)\n\t\t\tif professionTag then\n\t\t\t\tprofessionTag.Value = professionData.level\n\t\t\tend\n\n\t\t\tif player.Character and player.Character.PrimaryPart then\n\t\t\t\t--[[\n\t\t\t\tlocal Sound = Instance.new(\"Sound\")\n\t\t\t\tSound.Volume = 0.7\n\t\t\t\tSound.MaxDistance = 500\n\t\t\t\tSound.SoundId = \"rbxassetid://2066645345\"\n\t\t\t\tSound.Parent = player.Character.PrimaryPart\n\t\t\t\tSound:Play()\n\t\t\t\tgame.Debris:AddItem(Sound,10)\n\t\t\t\t]]\n\t\t\t\tlocal Attach = Instance.new(\"Attachment\")\n\t\t\t\tAttach.Parent = player.Character.PrimaryPart\n\t\t\t\tAttach.Orientation = Vector3.new(0,0,0)\n\t\t\t\tAttach.Axis = Vector3.new(1,0,0)\n\t\t\t\tAttach.SecondaryAxis = Vector3.new(0,1,0)\n\n\t\t\t\tlocal particle = script.particles.Wave:Clone()\n\t\t\t\tparticle.Parent = Attach\n\t\t\t\tparticle.Color = ColorSequence.new(professionData.color)\n\t\t\t\tparticle:Emit(1)\n\n\t\t\t\tgame.Debris:AddItem(Attach, 5)\n\t\t\tend\n\t\tend\n\n\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"professions\")\n\tend\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tlevels = Modules.levels\n\n\tnetwork:create(\"getProfessionLevel\", \"BindableFunction\", \"OnInvoke\", getProfessionLevel)\n\tnetwork:create(\"grantProfessionExp\", \"BindableFunction\", \"OnInvoke\", grantProfessionExp)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_pvp.lua",
    "content": "local module = {}\n\n-- from Player Manager, needs to be set up\n\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal network\n\nlocal shuttingDown = false\n\n-- TODO: give this a value LOL\nlocal pvpZoneCollectionFolder = workspace.placeFolders.pvpZoneCollection\n\nlocal function isPlayerInPVPZone(pvpZone, player)\n\tif not player or not player.Character or not player.Character.PrimaryPart then return false end\n\n\tlocal isInPVPZone\n\n\tlocal points = pvpZone:GetChildren()\n\tfor i = 1, #points do\n\t\tlocal point1 \t\t= pvpZone[tostring(i)]\n\t\tlocal point2 \t\t= pvpZone[tostring(i == #points and 1 or i + 1)]\n\t\tlocal isInsideFace \t= (point2.Position - point1.Position):Cross(player.Character.PrimaryPart.Position - point1.Position).Y < 0\n\n\t\tif isInPVPZone ~= nil and isInsideFace ~= isInPVPZone then\n\t\t\treturn false\n\t\tend\n\n\t\tisInPVPZone = isInsideFace\n\tend\n\n\tif isInPVPZone then\n\t\tlocal characterY \t= player.Character.PrimaryPart.Position.Y\n\t\tlocal upperYBound \t= points[1].Position.Y + points[1].Size.Y / 2\n\t\tlocal lowerYBound \t= points[1].Position.Y - points[1].Size.Y / 2\n\n\t\treturn characterY >= lowerYBound and characterY <= upperYBound\n\tend\n\n\treturn isInPVPZone\nend\n\nlocal function int__tickForPVP()\n\tif #pvpZoneCollectionFolder:GetChildren() == 0 and not ReplicatedStorage:FindFirstChild(\"isPVPGloballyEnabled\") then return end\n\n\twhile not shuttingDown do\n\t\tfor _, player in pairs(game.Players:GetPlayers()) do\n\t\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\t\tif not playerData then return end\n\t\t\tlocal currentPVPValue = playerData.nonSerializeData.isGlobalPVPEnabled\n\n\t\t\tif ReplicatedStorage:FindFirstChild(\"isPVPGloballyEnabled\") and ReplicatedStorage.isPVPGloballyEnabled.Value then\n\t\t\t\tif currentPVPValue == false then\n\t\t\t\t\tplayerData.nonSerializeData.isGlobalPVPEnabled = true\n\t\t\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"nonSerializeData\")\n\t\t\t\t\tnetwork:fireClient(\"alertPlayerNotification\", player,\n\t\t\t\t\t\t{text = \"Entered global pvp map.\"; id = \"pvp\"},\n\t\t\t\t\t\tnil,\n\t\t\t\t\t\t\"pvp\"\n\t\t\t\t\t)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tlocal isInPVPZone = false\n\t\t\t\tlocal isPVPZoneUnsafe = false\n\n\t\t\t\tfor i, pvpZone in pairs(pvpZoneCollectionFolder:GetChildren()) do\n\t\t\t\t\tif isPlayerInPVPZone(pvpZone, player) then\n\t\t\t\t\t\tisInPVPZone = true\n\n\t\t\t\t\t\tif pvpZone.Name == \"unsafe\" then\n\t\t\t\t\t\t\tisPVPZoneUnsafe = true\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif currentPVPValue ~= isInPVPZone then\n\t\t\t\t\tplayerData.nonSerializeData.isGlobalPVPEnabled \t= isInPVPZone\n\t\t\t\t\tplayerData.nonSerializeData.isPVPZoneUnsafe \t= isPVPZoneUnsafe\n\t\t\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"nonSerializeData\")\n\t\t\t\t\tif isInPVPZone then\n\t\t\t\t\t\tif isPVPZoneUnsafe then\n\t\t\t\t\t\t\tnetwork:fireClient(\"alertPlayerNotification\", player,\n\t\t\t\t\t\t\t{text = \"Entered unsafe global PVP zone.\"; id=\"pvp\"},nil,\"pvp\")\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tnetwork:fireClient(\"alertPlayerNotification\", player,\n\t\t\t\t\t\t\t{text = \"Entered safe global PVP zone.\"; id=\"pvp\"},nil,\"pvp\")\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\twait(1 / 3)\n\tend\nend\n\nlocal function isPlayerPVPWhitelisted(player, playerToCheck)\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\tif playerData then\n\t\tfor i, whitelistPlayer in pairs(playerData.nonSerializeData.whitelistPVPEnabled) do\n\t\t\tif whitelistPlayer == playerToCheck then\n\t\t\t\treturn true, i\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function requestPVPWhitelistPlayer_server(player, playerToWhitelist)\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\tif playerData then\n\t\tlocal isPVPWhitelisted, index = isPlayerPVPWhitelisted(player, playerToWhitelist)\n\t\tif not isPVPWhitelisted then\n\t\t\ttable.insert(playerData.nonSerializeData.whitelistPVPEnabled, playerToWhitelist)\n\n\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"nonSerializeData\")\n\t\tend\n\tend\nend\n\nlocal function revokePVPWhitelistPlayer_server(player, playerToRevokeWhitelist)\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\tif playerData then\n\t\tlocal isPVPWhitelisted, index = isPlayerPVPWhitelisted(player, playerToRevokeWhitelist)\n\t\tif isPVPWhitelisted then\n\t\t\ttable.remove(playerData.nonSerializeData.whitelistPVPEnabled, index)\n\n\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"nonSerializeData\")\n\t\tend\n\tend\nend\n\n\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\n\tnetwork:create(\"requestPVPWhitelistPlayer_server\", \"BindableFunction\", \"OnInvoke\", requestPVPWhitelistPlayer_server)\n\tnetwork:create(\"revokePVPWhitelistPlayer_server\", \"BindableFunction\", \"OnInvoke\", revokePVPWhitelistPlayer_server)\n\tspawn(int__tickForPVP)\nend\n\n\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_quest.lua",
    "content": "local module = {}\n\nlocal network\n\n\nlocal cachedPlayerQuestData = {}\n\nfunction module.completeQuest(player, questId)\n\nend\n\nfunction module.returnPlayerQuestData(player)\n\tlocal playerQuestData = cachedPlayerQuestData[player]\n\n\tif playerQuestData then\n\t\treturn playerQuestData\n\telseif not playerQuestData and game.Players:FindFirstChild(player.Name) then\n\t\tmodule.loadQuestData(player)\n\t\treturn module.returnPlayerQuestData(player)\n\tend\nend\n\nfunction module.loadQuestData(player)\n\nend\n\nfunction module.saveQuestData(player)\n\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tnetwork:create(\"questTriggerOccurred\", \"BindableEvent\", \"Event\", function(player, trigger, data)\n\t\t-- TODO: implement (ctrl+shift+F for occurances of questTriggerOccurred)\n\tend)\nend\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/contents/manager_resources.lua",
    "content": "-- Resource Manager\n-- Rocky28447 (for Vesteria) nah bro i thought it was for World Zero\n-- May 28, 2020\n\nlocal module = {}\n\nlocal CollectionService = game:GetService(\"CollectionService\")\n\nlocal thread\nlocal tableUtil\nlocal placeSetup\nlocal network\n\n--[[\n\n\tManages resource harvesting server-side.\n\n\tInternally tracks how many harvests each player\n\thas left on each resource node.\tThe structure\n\tlooks like this:\n\n\tplayerHarvestData = {\n\t\t[player] = {\n\t\t\t[exampleNodeInstance] = {\n\t\t\t\tharvestsLeft = 6\n\t\t\t}\n\t\t}\n\t}\n\n]]--\n\nlocal nodesFolder\n\nlocal globalResourceNodeData = {}\nlocal localResourceNodeData = {}\n\nlocal function getNodeTypeMetadataFromNode(node)\n\tlocal containingFolder = node:FindFirstAncestorWhichIsA(\"Folder\")\n\tlocal isNodeGroup = CollectionService:HasTag(containingFolder, \"resourceNodeGroupFolder\")\n\tlocal nodeTypeMetadata = isNodeGroup and containingFolder.Parent.Metadata or containingFolder.Metadata\n\n\treturn nodeTypeMetadata\nend\n\nlocal function setupBaseNodeDataForPlayer(player)\n\tlocal p = {}\n\tlocalResourceNodeData[player] = p\n\treturn p\nend\n\nlocal function newNodeData(node)\n\tlocal n = {}\n\tlocal nodeTypeMetadata = require(getNodeTypeMetadataFromNode(node))\n\tlocal dropPoints = node:FindFirstChild(\"DropPoints\")\n\n\tn.owners = {}\n\tn.harvestsLeft = nodeTypeMetadata.Harvests\n\t-- n.durability = 0\n\tn.durability = nodeTypeMetadata.Durability\n\tn.DropPoints = dropPoints and dropPoints:GetChildren() or {}\n\treturn n\nend\n\nlocal function getNodeDataForPlayer(node, player)\n\tif localResourceNodeData[player] and localResourceNodeData[player][node] then\n\t\treturn localResourceNodeData[player][node]\n\tend\n\n\tlocal n = newNodeData(node)\n\tif not localResourceNodeData[player] then\n\t\tsetupBaseNodeDataForPlayer(player)[node] = n\n\telse\n\t\tlocalResourceNodeData[player][node] = n\n\tend\n\n\treturn n\nend\n\nlocal function getGlobalDataForNode(node)\n\tif globalResourceNodeData[node] then\n\t\treturn globalResourceNodeData[node]\n\tend\n\n\tlocal n = newNodeData(node)\n\tglobalResourceNodeData[node] = n\n\treturn n\nend\n\nlocal function sumLootTableWeights(lootTable)\n\tlocal totalWeight = 0\n\tfor _, item in pairs (lootTable) do\n\t\ttotalWeight = totalWeight + item.Chance\n\tend\n\treturn totalWeight\nend\n\nlocal function rollLootTable(lootTable)\n\tlocal rng = Random.new()\n\tlocal roll = rng:NextNumber(0, 1)\n\tlocal remainingDistance = sumLootTableWeights(lootTable) * roll\n\n\t-- https://blog.bruce-hill.com/a-faster-weighted-random-choice\n\t-- \"Linear Scan\" (since we probably aren't gonna be sorting through too many items)\n\n\t-- This implementation means that in a drop table with 1 item, the chance can be any\n\t-- positive non-zero number and it will still be picked 100% of the time.\n\tfor _, item in pairs (lootTable) do\n\t\tremainingDistance = remainingDistance - item.Chance\n\t\tif remainingDistance < 0 then\n\t\t\treturn item\n\t\tend\n\tend\nend\n\nlocal function calcDamageForNode(node, player)\n\tlocal nodeTypeMetadata = require(getNodeTypeMetadataFromNode(node))\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\tif nodeTypeMetadata.NodeCategory then\n\t\treturn playerData.nonSerializeData.statistics_final[nodeTypeMetadata.NodeCategory]\n\tend\n\n\treturn 1\nend\n\nlocal function getDepletedResourceNodes(player)\n\tlocal depleted = {}\n\tfor node, data in pairs (globalResourceNodeData) do\n\t\tif data.Depleted then\n\t\t\tdepleted[#depleted + 1] = node\n\t\tend\n\tend\n\treturn depleted\nend\n\nlocal function calcNumHarvests(damage, node, nodeData)\n\tlocal nodeTypeMetadata = require(getNodeTypeMetadataFromNode(node))\n\tlocal maxdurability = nodeTypeMetadata.Durability\n\tlocal durability = nodeData.durability\n\tlocal harvestsLeft = nodeData.harvestsLeft\n\tlocal toHarvest = math.floor( (damage + durability) / maxdurability )\n\n\tif damage < durability then\n\t\treturn 0, durability - damage\n\telseif damage == durability then\n\t\treturn 1, 0\n\telse\n\t\treturn math.min(toHarvest, harvestsLeft), (damage + durability) % maxdurability\n\tend\n\n\t-- while damage >= maxdurability do\n\t-- \tdamage -= maxdurability\n\t-- end\n\n\t-- 8 dmg, 3 max durability, 1 durability\n\t-- 5 dmg, 3 max dirability, 1 durability\n\t-- 2 dmg, 3 max dirability, 1 durability\n\n\t-- -7 durability\n\t-- math.abs( math.floor( (durability - damage) / maxdurability ) )\n\t-- i think that math is right???? maybe???\n\nend\n\n-- this should really be a function of item manager\nlocal function applyVelocityToItem(item, velocity)\n\tlocal attachmentTarget\n\n\tif item:IsA(\"BasePart\") then\n\t\tattachmentTarget = item\n\telseif item:IsA(\"Model\") and (item.PrimaryPart or item:FindFirstChild(\"HumanoidRootPart\")) then\n\t\tlocal primaryPart = item.PrimaryPart or item:FindFirstChild(\"HumanoidRootPart\")\n\t\tif primaryPart then\n\t\t\tattachmentTarget = primaryPart\n\t\tend\n\tend\n\n\tattachmentTarget.Velocity = velocity\nend\n\nlocal function resourceNodeReplenished(node, player)\n\tlocal nodeTypeMetadata = require(getNodeTypeMetadataFromNode(node))\n\tlocal isNodeGlobal = nodeTypeMetadata.IsGlobal\n\tlocal nodeData = isNodeGlobal and getGlobalDataForNode(node) or getNodeDataForPlayer(node, player)\n\tlocal dropPoints = node:FindFirstChild(\"DropPoints\")\n\n\tnodeData.harvestsLeft = nodeTypeMetadata.Harvests\n\t-- nodeData.durability = 0\n\tnodeData.durability = nodeTypeMetadata.Durability\n\tnodeData.DropPoints = dropPoints and dropPoints:GetChildren() or {}\n\tnodeData.Depleted = false\n\n\tif isNodeGlobal then\n\t\tif nodeTypeMetadata.DestroyOnDeplete then\n\t\t\tfor _, c in pairs (node:GetDescendants()) do\n\t\t\t\tif c:IsA(\"BasePart\") then\n\t\t\t\t\tc.CanCollide = true\n\t\t\t\t\tc.Transparency = 0\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\tCollectionService:AddTag(node.PrimaryPart, \"attackable\")\n\t\tnetwork:fireAllClients(\"resourceReplenished\", node)\n\telse\n\t\tnetwork:fireClient(\"resourceReplenished\", player, node)\n\tend\nend\n\nlocal function resourceNodeDepleted(node, player)\n\tlocal nodeTypeMetadata = require(getNodeTypeMetadataFromNode(node))\n\tlocal isGlobal = nodeTypeMetadata.IsGlobal\n\tlocal nodeData = isGlobal and getGlobalDataForNode(node) or getNodeDataForPlayer(node, player)\n\n\tif nodeTypeMetadata.Replenish ~= 0 then\n\t\tthread.Delay(nodeTypeMetadata.Replenish, resourceNodeReplenished, node, player)\n\tend\n\n\tif isGlobal then\n\t\t-- Disable node collision on server BEFORE spawning items otherwise items\n\t\t-- will get stuck and will not \"fling\" away from the node. Nodes that\n\t\t-- have exterior drop points WILL NOT have this problem.\n\t\tif nodeTypeMetadata.DestroyOnDeplete then\n\t\t\tfor _, c in pairs (node:GetDescendants()) do\n\t\t\t\tif c:IsA(\"BasePart\") then\n\t\t\t\t\tc.CanCollide = false\n\t\t\t\t\tc.Transparency = 1\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\tCollectionService:RemoveTag(node.PrimaryPart, \"attackable\")\n\t\tnetwork:fireAllClients(\"resourceDepleted\", node)\n\telse\n\t\tnetwork:fireClient(\"resourceDepleted\", player, node)\n\tend\n\n\tnodeData.Depleted = true\n\n\tif nodeTypeMetadata.Animations.OnDeplete then\n\t\tnodeTypeMetadata.Animations.OnDeplete()\n\tend\nend\n\nlocal function harvestResource(player, node)\n\tlocal playerManifest = player.Character and player.Character.PrimaryPart\n\tassert(playerManifest, \"No character\")\n\n\tif node:IsA(\"Model\") and node:IsDescendantOf(nodesFolder) and node.Name ~= \"Nodes\" and node.Name ~= \"Props\" then\n\t\tlocal nodePosition = node:GetBoundingBox().Position\n\t\tlocal charPosition = playerManifest.Position\n\t\tlocal distance = (nodePosition - charPosition).Magnitude\n\n\t\tif distance < node:GetExtentsSize().Magnitude * 1.5 then\n\t\t\tlocal nodeTypeMetadata = require(getNodeTypeMetadataFromNode(node))\n\t\t\tlocal isNodeGlobal = nodeTypeMetadata.IsGlobal\n\t\t\tlocal numDrops = nodeTypeMetadata.LootTable.Drops\n\n\t\t\tlocal damage = calcDamageForNode(node, player)\n\t\t\tlocal nodeData = isNodeGlobal and getGlobalDataForNode(node) or getNodeDataForPlayer(node, player)\n\t\t\tlocal harvestsLeft = nodeData.harvestsLeft\n\t\t\tlocal durability = nodeData.durability\n\n\t\t\tdurability = math.max(durability - damage, 0)\n\t\t\tnodeData.durability = durability\n\n\t\t\tif isNodeGlobal and not table.find(nodeData.owners, player) then\n\t\t\t\tnodeData.owners[#nodeData.owners + 1] = player\n\t\t\tend\n\n\t\t\tif harvestsLeft > 0 then\n\t\t\t\tif durability == 0 then\n\t\t\t\t\tlocal rng = Random.new()\n\n\t\t\t\t\t-- Contingency: If harvest > num drop points we will run out of drop points\n\t\t\t\t\t-- If this happens, item will drop on node's primary part position\n\t\t\t\t\tlocal dropPointNum = rng:NextInteger(1, #nodeData.DropPoints)\n\t\t\t\t\tlocal dropPoint = nodeData.DropPoints[dropPointNum]\n\t\t\t\t\tlocal dropPosition = dropPoint and dropPoint.Value.DropAttachment.WorldPosition or\n\t\t\t\t\t\t\t\t\t\t\tnode.PrimaryPart.Position + Vector3.new(0, node.PrimaryPart.Size.Y / 2 + 0.5, 0)\n\n\t\t\t\t\ttableUtil.FastRemove(nodeData.DropPoints, dropPointNum)\n\t\t\t\t\tharvestsLeft = harvestsLeft - 1\n\t\t\t\t\tnodeData.harvestsLeft = harvestsLeft\n\t\t\t\t\tnodeData.durability = nodeTypeMetadata.Durability\n\n\t\t\t\t\tif isNodeGlobal then\n\t\t\t\t\t\tif dropPoint then\n\t\t\t\t\t\t\tdropPoint.Value.Transparency = 1\n\t\t\t\t\t\t\tdropPoint.Value.CanCollide = false\n\t\t\t\t\t\tend\n\t\t\t\t\t\tnetwork:fireAllClients(\"resourceHarvested\", node, dropPoint and dropPoint.Value or nil)\n\t\t\t\t\telse\n\t\t\t\t\t\tnetwork:fireClient(\"resourceHarvested\", player, node, dropPoint and dropPoint.Value or nil)\n\t\t\t\t\tend\n\n\t\t\t\t\tif harvestsLeft == 0 then\n\t\t\t\t\t\tresourceNodeDepleted(node, player)\n\t\t\t\t\tend\n\n\t\t\t\t\tfor _ = 1, numDrops do\n\t\t\t\t\t\tlocal itemDrop = rollLootTable(nodeTypeMetadata.LootTable.Items)\n\t\t\t\t\t\tlocal numToDrop = itemDrop:Amount()\n\n\t\t\t\t\t\tfor _ = 1, numToDrop do\n\t\t\t\t\t\t\tlocal itemModifiers = itemDrop:Modifiers()\n\t\t\t\t\t\t\tlocal velocity = Vector3.new((rng:NextNumber() - 0.5) * 10, (2 + rng:NextNumber()) * 25, (rng:NextNumber() - 0.5) * 10)\n\n\t\t\t\t\t\t\titemModifiers.id = itemDrop.ID\n\t\t\t\t\t\t\tlocal item = network:invoke(\"spawnItemOnGround\",\n\t\t\t\t\t\t\t\titemModifiers,\n\t\t\t\t\t\t\t\tdropPosition,\n\t\t\t\t\t\t\t\tisNodeGlobal and nodeData.owners or {player}\n\t\t\t\t\t\t\t)\n\n\t\t\t\t\t\t\tapplyVelocityToItem(item, velocity)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\treturn dropPoint and dropPoint.Value or nil\n\t\t\t\telse\n\t\t\t\t\tif isNodeGlobal then\n\t\t\t\t\t\tnetwork:fireAllClients(\"resourceHarvested\", node)\n\t\t\t\t\telse\n\t\t\t\t\t\tnetwork:fireClient(\"resourceHarvested\", player, node)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\nend\n\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\tplaceSetup = Modules.placeSetup\n\tthread = Modules.thread\n\ttableUtil = Modules.tableUtil\n\n\tnodesFolder = placeSetup.getPlaceFolder(\"resourceNodes\")\n\n\tnetwork:create(\"harvestResource\", \"RemoteFunction\", \"OnServerInvoke\", harvestResource)\n\n\tnetwork:create(\"getDepletedResourceNodes\", \"RemoteFunction\", \"OnServerInvoke\", getDepletedResourceNodes)\n\n\tnetwork:create(\"resourceHarvested\", \"RemoteEvent\")\n\tnetwork:create(\"resourceDepleted\", \"RemoteEvent\")\n\tnetwork:create(\"resourceReplenished\", \"RemoteEvent\")\nend\n\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_security.lua",
    "content": "local module = {}\nmodule.priority = 3\n\nlocal CollectionService = game:GetService(\"CollectionService\")\nlocal RunService = game:GetService(\"RunService\")\n\nlocal network\nlocal utilities\nlocal configuration\nlocal abilityLookup\n\n--[[\n\tBANS & SUSPICION\n--]]\n-- unban\n--[[\nlocal banRecord = game:GetService(\"DataStoreService\"):GetDataStore(\"banRecord\")\nbanRecord:UpdateAsync(userId, function(history)\n\thistory.unbanTime = 0\n\treturn history\nend)\n\n]]\n\nlocal banRecord = game:GetService(\"DataStoreService\"):GetDataStore(\"banRecord\")\nlocal playerPositionDataContainer = {}\n\nlocal function handleBanHistory(player, playerBanHistory)\n\tif playerBanHistory and playerBanHistory.unbanTime > os.time() then\n\t\tif player:FindFirstChild(\"DataLoaded\") then\n\t\t\tnetwork:invoke(\"onPlayerRemoving\", player)\n\t\tend\n\t\tlocal banDuration = playerBanHistory.unbanTime - os.time()\n\t\tif playerBanHistory.reason then\n\t\t\tplayer:Kick(\"You have been banned for: \"..playerBanHistory.reason..\" (unbanned in \"..utilities.timeToString(banDuration)..\")\")\n\t\telse\n\t\t\tplayer:Kick(\"You have been banned (unbanned in \"..utilities.timeToString(banDuration)..\")\")\n\t\tend\n\tend\nend\n\nlocal function onPlayerAdded(player)\n\tif not RunService:IsStudio() then\n\t\tlocal playerBanHistory = banRecord:GetAsync(player.userId)\n\t\tif playerBanHistory then\n\t\t\thandleBanHistory(player, playerBanHistory)\n\t\tend\n\tend\nend\n\n\n\nlocal function banPlayer(player, duration, reason, source)\n\tsource = source or \"system\"\n\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\tlocal playerBanHistory\n\n\tfor _=1, 3 do\n\t\tlocal success = pcall(function()\n\t\t\tbanRecord:UpdateAsync(player.userId, function(banHistory)\n\t\t\t\tbanHistory = banHistory or {}\n\t\t\t\tplayerBanHistory = banHistory\n\t\t\t\tplayerBanHistory.reason = reason\n\t\t\t\tplayerBanHistory.source = source\n\n\t\t\t\tplayerBanHistory.previousRecords = playerBanHistory.previousRecords or {}\n\t\t\t\ttable.insert(playerBanHistory.previousRecords, {\n\t\t\t\t\treason = reason,\n\t\t\t\t\tsource = source,\n\t\t\t\t\tduration = duration,\n\t\t\t\t\ttimestamp = os.time()\n\t\t\t\t})\n\n\t\t\t\tif playerData then\n\t\t\t\t\tplayerBanHistory.offendingData = playerData\n\t\t\t\tend\n\n\t\t\t\tplayerBanHistory.unbanTime = playerBanHistory.unbanTime or 0\n\t\t\t\tif playerBanHistory.unbanTime >= os.time() then\n\t\t\t\t\tplayerBanHistory.unbanTime = playerBanHistory.unbanTime + duration\n\t\t\t\telse\n\t\t\t\t\tplayerBanHistory.unbanTime = os.time() + duration\n\t\t\t\tend\n\t\t\t\treturn playerBanHistory\n\t\t\tend)\n\t\tend)\n\n\t\tif success then\n\t\t\tbreak\n\t\tend\n\tend\n\n\thandleBanHistory(player, playerBanHistory)\nend\n\nlocal function addSuspicion(player, amount)\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\tplayerData.internalData.suspicion = playerData.internalData.suspicion + amount\n\n\tif playerData.internalData.suspicion > 100 then\n\t\tlocal playerBanHistory\n\t\tfor _ = 1, 3 do\n\t\t\tlocal success = pcall(function()\n\t\t\t\tbanRecord:UpdateAsync(player.userId, function(banHistory)\n\n\t\t\t\t\tbanHistory = banHistory or {}\n\t\t\t\t\tplayerBanHistory = banHistory\n\n\t\t\t\t\tlocal reason = \"cheating suspicion\"\n\t\t\t\t\tlocal source = \"system\"\n\t\t\t\t\tlocal duration = 36000\n\n\t\t\t\t\tplayerBanHistory.cheatingBans = (playerBanHistory.cheatingBans or 0) + 1\n\n\t\t\t\t\tlocal banCount = playerBanHistory.cheatingBans\n\t\t\t\t\tif banCount >= 5 then\n\t\t\t\t\t\tduration = 14 * 86400\n\t\t\t\t\telseif banCount == 4 then\n\t\t\t\t\t\tduration = 7 * 86400\n\t\t\t\t\telseif banCount == 3 then\n\t\t\t\t\t\tduration = 3 * 86400\n\t\t\t\t\telseif banCount == 2 then\n\t\t\t\t\t\tduration = 86400\n\t\t\t\t\tend\n\n\t\t\t\t\tplayerBanHistory.previousRecords = playerBanHistory.previousRecords or {}\n\t\t\t\t\ttable.insert(playerBanHistory.previousRecords, {\n\t\t\t\t\t\treason = reason,\n\t\t\t\t\t\tsource = source,\n\t\t\t\t\t\tduration = duration,\n\t\t\t\t\t\ttimestamp = os.time()\n\t\t\t\t\t})\n\n\t\t\t\t\tplayerBanHistory.reason = reason\n\t\t\t\t\tplayerBanHistory.source = source\n\n\t\t\t\t\tif playerData then\n\t\t\t\t\t\tplayerBanHistory.offendingData = playerData\n\t\t\t\t\tend\n\n\t\t\t\t\tplayerBanHistory.unbanTime = playerBanHistory.unbanTime or 0\n\t\t\t\t\tif playerBanHistory.unbanTime >= os.time() then\n\t\t\t\t\t\tplayerBanHistory.unbanTime = playerBanHistory.unbanTime + duration\n\t\t\t\t\telse\n\t\t\t\t\t\tplayerBanHistory.unbanTime = os.time() + duration\n\t\t\t\t\tend\n\n\t\t\t\t\treturn playerBanHistory\n\t\t\t\tend)\n\t\t\tend)\n\t\t\tif success then\n\t\t\t\tplayerData.internalData.suspicion = 0\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\tend\nend\n\n-- tp exploit stuff\nlocal function init__exploitBlock()\n\tlocal positionCheckHeartbeatTick = 1 / 3\n\n\tlocal doors \t\t= CollectionService:GetTagged(\"door\")\n\tlocal cannons \t\t= CollectionService:GetTagged(\"cannon\")\n\tlocal escapeRopes \t= CollectionService:GetTagged(\"escapeRope\")\n\n\tlocal ACCEPTABLE_THRESHOLD_FOR_NEARBY = 50\n\tlocal function isNearbyAcceptableObject(pos, objTable, label)\n\t\tfor _, obj in pairs(objTable) do\n\t\t\tlocal objPos = obj:IsA(\"Model\") and (obj.PrimaryPart and obj.PrimaryPart.Position) or obj.Position\n\n\t\t\tif objPos and (pos - objPos).magnitude <= ACCEPTABLE_THRESHOLD_FOR_NEARBY then\n\t\t\t\treturn true, obj\n\t\t\tend\n\t\tend\n\n\t\treturn false, nil\n\tend\n\n\tlocal function getOtherDoorFromDoor(door)\n\t\tfor _, v in pairs(doors) do\n\t\t\tif v.Parent.Name == door.Parent.Name and v.Parent ~= door.Parent then\n\t\t\t\treturn v\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function isPlayerPerformingUnacceptableSketchyMovements(player)\n\t\tlocal timeWindow \t= configuration.getConfigurationValue(\"server_TPExploitTimeWindow\")\n\t\tlocal scoreToFail \t= configuration.getConfigurationValue(\"server_TPExploitScoreToFail\")\n\n\t\tlocal score = 0\n\n\t\tif playerPositionDataContainer[player] then\n\t\t\tfor _, scoreData in pairs(playerPositionDataContainer[player].sketchyMovements) do\n\t\t\t\tif tick() - scoreData.timestamp <= timeWindow then\n\t\t\t\t\tscore = score + scoreData.movementRatio\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\treturn score >= scoreToFail\n\tend\n\n\tnetwork:create(\"reportPlayerAttemptDamageEntity\", \"BindableEvent\", \"Event\", function(player, weaponType, serverHitbox)\n\t\tif playerPositionDataContainer[player] then\n\t\t\tif playerPositionDataContainer[player].positions and #playerPositionDataContainer[player].positions > 0 then\n\t\t\t\tlocal lastPosition = playerPositionDataContainer[player].positions[#playerPositionDataContainer[player].positions].position\n\n\t\t\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\t\t\tlocal distanceToTravel = (serverHitbox.Position - lastPosition).magnitude\n\t\t\t\tlocal distanceMax = playerData.nonSerializeData.statistics_final.walkspeed * 4 * positionCheckHeartbeatTick + 5\n\n\t\t\t\tif distanceToTravel > distanceMax then\n\t\t\t\t\twarn(\"earlyTrigger exploiter!\", player)\n\t\t\t\t\tlocal response = configuration.getConfigurationValue(\"tpExploitPunishment\")\n\t\t\t\t\tif response == \"suspicion\" then\n\t\t\t\t\t\tlocal amount = configuration.getConfigurationValue(\"tpExploitPunishmentSuspicionAddAmount\")\n\t\t\t\t\t\taddSuspicion(player, amount or 25)\n\t\t\t\t\telseif response == \"kick\" then\n\t\t\t\t\t\tplayer:Kick(\"TP Exploiting\")\n\t\t\t\t\telseif response == \"redirect\" then\n\t\t\t\t\t\tplayer:Kick(\"Anti-exploit\")\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\n\n\tlocal function getRayClosestPoint(ray, point)\n\t    --  shift point to be relative to the origin of the ray\n\t    local a, b = ray.Origin - point, ray.Direction\n\t    --  calculate rejection of a from b\n\t    local v = a - ((a:Dot(b)) / (b:Dot(b))) * b\n\t    --  add rejection to point to get the closest point on the ray\n\t    return point + v\n\tend\n\n\tnetwork:create(\"playerRequest_activateEscapeRope\", \"RemoteEvent\", \"OnServerEvent\", function(player, cEscapeRope)\n\t\tif cEscapeRope and CollectionService:HasTag(cEscapeRope, \"escapeRope\") then\n\t\t\tlocal isNearbyEscapeRope, nearestEscapeRope = isNearbyAcceptableObject(player.Character.PrimaryPart.Position, escapeRopes)\n\n\t\t\tif isNearbyEscapeRope and nearestEscapeRope == cEscapeRope then\n\t\t\t\tif nearestEscapeRope.Parent and nearestEscapeRope.Parent:FindFirstChild(\"Target\") then\n\t\t\t\t\tnetwork:invoke(\"teleportPlayerCFrame_server\", player, nearestEscapeRope.Parent.Target.CFrame)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\n\n\tlocal function checkIfPlayerIsBad(player)\n\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\t\tif playerData then\n\t\t\tif isPlayerPerformingUnacceptableSketchyMovements(player) then\n\t\t\t\tif configuration.getConfigurationValue(\"doLogTPExploitersInPlayerDataFlags\") then\n\t\t\t\t\tplayerData.flags.isPlayerTPExploiter = true\n\t\t\t\tend\n\n\t\t\t\twarn(\"player exploiting!\", player)\n\n\t\t\t\tlocal response = configuration.getConfigurationValue(\"tpExploitPunishment\")\n\t\t\t\tif response == \"suspicion\" then\n\t\t\t\t\tlocal amount = configuration.getConfigurationValue(\"tpExploitPunishmentSuspicionAddAmount\")\n\t\t\t\t\taddSuspicion(player, amount or 25)\n\t\t\t\telseif response == \"kick\" then\n\t\t\t\t\tplayer:Kick(\"TP Exploiting\")\n\t\t\t\telseif response == \"redirect\" then\n\t\t\t\t\tplayer:Kick(\"Anti-exploit\")\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tnetwork:create(\"incrementPlayerArcadeScore\", \"BindableFunction\", \"OnInvoke\", function(player, scoreToAdd)\n\t\tlocal playerPositionData = playerPositionDataContainer[player]\n\n\t\tif playerPositionData then\n\t\t\ttable.insert(playerPositionData.sketchyMovements, {movementRatio = scoreToAdd; timestamp = tick()})\n\n\t\t\tcheckIfPlayerIsBad(player)\n\t\tend\n\tend)\n\n\twhile true do\n\t\tlocal step = wait(positionCheckHeartbeatTick)\n\n\t\tfor player, playerPositionData in pairs(playerPositionDataContainer) do\n\t\t\tif player:FindFirstChild(\"isPlayerSpawning\") and not player.isPlayerSpawning.Value and player:FindFirstChild(\"playerSpawnTime\") and (os.time() - player.playerSpawnTime.Value >= 5) then\n\t\t\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart:FindFirstChild(\"state\") and player.Character.PrimaryPart.state.Value ~= \"dead\" and configuration.getConfigurationValue(\"isTeleportingExploitFixEnabled\", player) then\n\t\t\t\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\t\t\t\t\tif #playerPositionData.positions > 0 then\n\t\t\t\t\t\tlocal currPosition = player.Character.PrimaryPart.Position\n\t\t\t\t\t\tlocal currVelocity = player.Character.PrimaryPart.Velocity\n\n\t\t\t\t\t\tlocal prevPosition = playerPositionData.positions[#playerPositionData.positions].position\n\t\t\t\t\t\tlocal prevVelocity = playerPositionData.positions[#playerPositionData.positions].velocity\n\n\t\t\t\t\t\tlocal playerVelocityForCalculations = currVelocity.magnitude > prevVelocity.magnitude and currVelocity or prevVelocity\n\n\t\t\t\t\t\tif playerVelocityForCalculations.magnitude < 0.1 then\n\t\t\t\t\t\t\tif (currPosition - prevPosition).magnitude >= 0.1 then\n\t\t\t\t\t\t\t\tif (currPosition - prevPosition).magnitude < playerData.nonSerializeData.statistics_final.walkspeed * step then\n\t\t\t\t\t\t\t\t\tplayerVelocityForCalculations = (currPosition - prevPosition).unit * playerData.nonSerializeData.statistics_final.walkspeed * step\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t-- actually nothing is going on\n\t\t\t\t\t\t\t\tplayerVelocityForCalculations = Vector3.new()\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telseif playerVelocityForCalculations.magnitude < playerData.nonSerializeData.statistics_final.walkspeed * step then\n\t\t\t\t\t\t\tplayerVelocityForCalculations = playerVelocityForCalculations.unit * playerData.nonSerializeData.statistics_final.walkspeed * step\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\t-- pretend double?\n\t\t\t\t\t\tplayerVelocityForCalculations = playerVelocityForCalculations * configuration.getConfigurationValue(\"server_TPExploitVelocityMultiplier\")\n\n\n\t\t\t\t\t\tlocal delta \t\t= ((currPosition - prevPosition) * Vector3.new(1, 0, 1)).magnitude\n\n\t\t\t\t\t\t-- stuns set your walkspeed to zero so let's not ban people just for getting stunned yeah?\n\t\t\t\t\t\tlocal walkspeed = math.max(playerData.nonSerializeData.statistics_final.walkspeed, 0.1)\n\n\t\t\t\t\t\tlocal movementRatio = delta / (math.max((playerVelocityForCalculations * Vector3.new(1, 0, 1)).magnitude, walkspeed) * step)\n\n\t\t\t\t\t\t-- we're not kicking anyone for going too slowly, just chill out\n\t\t\t\t\t\tif playerVelocityForCalculations.Magnitude < 16 then\n\t\t\t\t\t\t\tmovementRatio = 1\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\ttable.insert(playerPositionData.positions, {position = currPosition; velocity = player.Character.PrimaryPart.Velocity})\n\n\t\t\t\t\t\tif #playerPositionData.positions > 10 then\n\t\t\t\t\t\t\ttable.remove(playerPositionData.positions, 1)\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\t-- 2x is running\n\t\t\t\t\t\tif delta > (playerData.nonSerializeData.statistics_final.walkspeed * 3) * step then\n\t\t\t\t\t\t\t-- flag!\n\t\t\t\t\t\t\tlocal isPlayerGood = false\n\n\t\t\t\t\t\t\tif not isPlayerGood then\n\t\t\t\t\t\t\t\tlocal isCurrNearDoor, currDoor = isNearbyAcceptableObject(currPosition, doors, \"curr\")\n\t\t\t\t\t\t\t\tlocal isPrevNearDoor, prevDoor = isNearbyAcceptableObject(prevPosition, doors, \"prev\")\n\t\t\t\t\t\t\t\tif isCurrNearDoor and isPrevNearDoor then\n\t\t\t\t\t\t\t\t\tisPlayerGood = true\n\t\t\t\t\t\t\t\telseif isCurrNearDoor or isPrevNearDoor then\n\t\t\t\t\t\t\t\t\tlocal door1 \t= currDoor or prevDoor\n\t\t\t\t\t\t\t\t\tlocal otherDoor = getOtherDoorFromDoor(door1)\n\n\t\t\t\t\t\t\t\t\tif otherDoor then\n\t\t\t\t\t\t\t\t\t\tlocal ray = Ray.new(door1.Position, otherDoor.Position - door1.Position)\n\n\t\t\t\t\t\t\t\t\t\tif (currPosition - getRayClosestPoint(ray, currPosition)).magnitude <= ACCEPTABLE_THRESHOLD_FOR_NEARBY and (prevPosition - getRayClosestPoint(ray, prevPosition)).magnitude <= ACCEPTABLE_THRESHOLD_FOR_NEARBY then\n\t\t\t\t\t\t\t\t\t\t\tisPlayerGood = true\n\t\t\t\t\t\t\t\t\t\t\twarn(\"is good at intersection!\")\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif not isPlayerGood then\n\t\t\t\t\t\t\t\tlocal isNearbyEscapeRope, nearestEscapeRope = isNearbyAcceptableObject(prevPosition, escapeRopes)\n\t\t\t\t\t\t\t\tif isNearbyEscapeRope and (nearestEscapeRope.Parent.Target.Position - currPosition).magnitude <= ACCEPTABLE_THRESHOLD_FOR_NEARBY then\n\t\t\t\t\t\t\t\t\tisPlayerGood = true\n\t\t\t\t\t\t\t\telseif nearestEscapeRope then\n\t\t\t\t\t\t\t\t\tlocal ray = Ray.new(nearestEscapeRope.Position, nearestEscapeRope.Parent.Target.Position - nearestEscapeRope.Position)\n\n\t\t\t\t\t\t\t\t\tif (currPosition - getRayClosestPoint(ray, currPosition)).magnitude <= ACCEPTABLE_THRESHOLD_FOR_NEARBY and (prevPosition - getRayClosestPoint(ray, prevPosition)).magnitude <= ACCEPTABLE_THRESHOLD_FOR_NEARBY then\n\t\t\t\t\t\t\t\t\t\tisPlayerGood = true\n\t\t\t\t\t\t\t\t\t\twarn(\"is good at intersection for rope!\")\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif not isPlayerGood then\n\t\t\t\t\t\t\t\tlocal isNearbyCannon, cannon = isNearbyAcceptableObject(prevPosition, cannons)\n\t\t\t\t\t\t\t\tif isNearbyCannon then\n\t\t\t\t\t\t\t\t\tif math.acos((cannon.Parent.target.CFrame.lookVector * Vector3.new(1, 0, 1)).unit:Dot(((currPosition - prevPosition).unit))) <= math.pi / 2 then\n\t\t\t\t\t\t\t\t\t\tplayerPositionData.lastCannon \t\t\t= cannon\n\t\t\t\t\t\t\t\t\t\tplayerPositionData.lastTimeNearCannon \t= tick()\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif playerPositionData.lastTimeNearCannon and tick() - playerPositionData.lastTimeNearCannon <= 7 then\n\t\t\t\t\t\t\t\t\tif math.acos((playerPositionData.lastCannon.Parent.target.CFrame.lookVector * Vector3.new(1, 0, 1)).unit:Dot(((currPosition - prevPosition).unit))) <= math.pi / 2 then\n\t\t\t\t\t\t\t\t\t\tisPlayerGood = true\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tplayerPositionData.lastCannon \t\t\t= nil\n\t\t\t\t\t\t\t\t\tplayerPositionData.lastTimeNearCannon \t= nil\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif not isPlayerGood then\n\t\t\t\t\t\t\t\tlocal playerUnitVelocity = playerVelocityForCalculations.magnitude > 0.01 and playerVelocityForCalculations.unit or Vector3.new()\n\n\t\t\t\t\t\t\t\t-- if the directions at least 72 degrees align..\n\t\t\t\t\t\t\t\tif math.acos((playerUnitVelocity * Vector3.new(1, 0, 1)):Dot(((currPosition - prevPosition).unit * Vector3.new(1, 0, 1)))) <= math.pi / 2 then\n\t\t\t\t\t\t\t\t\t-- ok... figure out now what couldve caused this massive velocity..\n\t\t\t\t\t\t\t\t\tif movementRatio <= 1 then\n\t\t\t\t\t\t\t\t\t\tisPlayerGood = true\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t-- check abilities\n\t\t\t\t\t\t\tif not isPlayerGood then\n\t\t\t\t\t\t\t\tlocal activeAbilityIds = network:invoke(\"getCurrentlyActiveAbilityGUIDsForPlayer\", player)\n\n\t\t\t\t\t\t\t\tfor _, abilityId in pairs(activeAbilityIds) do\n\t\t\t\t\t\t\t\t\tlocal ability = abilityLookup[abilityId](playerData)\n\t\t\t\t\t\t\t\t\tif ability and ability.__serverValidateMovement then\n\t\t\t\t\t\t\t\t\t\tif ability.__serverValidateMovement(player, prevPosition, currPosition) then\n\t\t\t\t\t\t\t\t\t\t\tisPlayerGood = true\n\n\t\t\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif not isPlayerGood then\n\t\t\t\t\t\t\t\ttable.insert(playerPositionData.sketchyMovements, {movementRatio = movementRatio; timestamp = tick()})\n\n\t\t\t\t\t\t\t\tcheckIfPlayerIsBad(player)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\ttable.insert(playerPositionData.positions, {position = player.Character.PrimaryPart.Position; velocity = player.Character.PrimaryPart.Velocity})\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tplayerPositionData.positions = {}\n\t\t\t\tend\n\t\t\telse\n\t\t\t\t-- wipe if respawning, just incase\n\t\t\t\tif playerPositionData.positions and #playerPositionData.positions > 0 then\n\t\t\t\t\tplayerPositionData.positions = {}\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tutilities = Modules.utilities\n\tconfiguration = Modules.configuration\n\tabilityLookup = Modules.abilityLookup\n\n\tnetwork:create(\"banPlayer\", \"BindableFunction\", \"OnInvoke\", banPlayer)\n\tnetwork:create(\"addSuspicion\", \"BindableFunction\", \"OnInvoke\", addSuspicion)\n\n\tgame.Players.PlayerAdded:connect(onPlayerAdded)\n\tfor _, player in pairs(game.Players:GetPlayers()) do\n\t\tonPlayerAdded(player)\n\tend\n\tspawn(init__exploitBlock)\nend\n\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_servers.lua",
    "content": "-- Communicates to other servers of the same place and allows players to teleport between them\n\nlocal module = {}\n\nlocal placeKey = \"pl-\"..tostring(game.PlaceId)\n\nlocal httpService = game:GetService(\"HttpService\")\nlocal messaging = game:GetService(\"MessagingService\")\n\n\nlocal network\n\n\nlocal success, err\nlocal messagingConnection\n\nlocal servers = {}\n\nlocal serversDataValue = Instance.new(\"StringValue\")\nserversDataValue.Name = \"serversData\"\nserversDataValue.Parent = game.ReplicatedStorage\n\nlocal function serversDataUpdated()\n\tserversDataValue.Value = httpService:JSONEncode(servers)\nend\n\nlocal function registerMessage(message)\n\tlocal data = message.Data\n\tlocal timestamp = message.Sent\n\tlocal jobId = data.jobId\n\tif jobId ~= game.JobId then\n\t\tif data.status == \"open\" then\n\t\t\tservers[tostring(jobId)] = {\n\t\t\t\tplayers = data.players;\n\t\t\t\tupdated = timestamp;\n\t\t\t}\n\t\telseif data.status == \"close\" then\n\t\t\tservers[tostring(jobId)] = nil\n\t\tend\n\t\tserversDataUpdated()\n\tend\nend\n\nlocal function playerRequest_teleportToJobId(player, jobId)\n\tif servers[jobId] then\n\t\tif player.Character and player.Character.PrimaryPart then\n\t\t\tif player.Character.PrimaryPart.state.Value ~= \"dead\" and player.Character.PrimaryPart.health.Value > 0 then\n\t\t\t\tnetwork:invoke(\"teleportPlayerToJobId\", player, game.PlaceId, jobId)\n\t\t\tend\n\t\tend\n\t\treturn true\n\tend\n\treturn false\nend\n\nlocal function playerRequest_returnToMainMenu(player)\n\tif player.Character and player.Character.PrimaryPart then\n\t\tif player.Character.PrimaryPart.state.Value ~= \"dead\" and player.Character.PrimaryPart.health.Value > 0 then\n\t\t\tnetwork:invoke(\"teleportPlayer\", player, 2376885433)\n\t\tend\n\tend\nend\n\n\nlocal serverClosing\n\nlocal function connect()\n\tif game.PrivateServerId == \"\" and game.PlaceId ~= 4561988219 and game.PlaceId ~= 4041427413 then\n\t\tlocal success, err\n\t\trepeat\n\t\t\twait(1)\n\t\t\tsuccess, err = pcall(function()\n\t\t\t\tmessagingConnection = messaging:SubscribeAsync(\n\t\t\t\t\tplaceKey,\n\t\t\t\t\tregisterMessage\n\t\t\t\t)\n\t\t\tend)\n\t\tuntil success\n\n\t\tgame:BindToClose(function()\n\t\t\tserverClosing = true\n\t\t\tif game:GetService(\"RunService\"):IsStudio() then return end\n\t\t\tlocal message = {\n\t\t\t\tjobId = game.JobId;\n\t\t\t\tstatus = \"close\";\n\t\t\t}\n\t\t\tlocal success, err\n\t\t\trepeat\n\t\t\t\tsuccess, err = pcall(function()\n\t\t\t\t\tmessaging:PublishAsync(placeKey, message)\n\t\t\t\tend)\n\t\t\t\twait(1)\n\t\t\tuntil success\n\n\t\tend)\n\n\t\twhile not serverClosing do\n\t\t\tlocal message = {\n\t\t\t\tjobId = game.JobId;\n\t\t\t\tstatus = \"open\";\n\t\t\t\tplayers = #game.Players:GetPlayers();\n\t\t\t}\n\t\t\tlocal messageSent, err = pcall(function()\n\t\t\t\tmessaging:PublishAsync(placeKey, message)\n\t\t\tend)\n\t\t\twait(messageSent and 60 or 20)\n\t\tend\n\tend\nend\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\n\tnetwork:create(\"playerRequest_teleportToJobId\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_teleportToJobId)\n\tnetwork:create(\"playerRequest_returnToMainMenu\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_returnToMainMenu)\n\n\tspawn(connect)\nend\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/contents/manager_statusEffect.lua",
    "content": "\n-- author: Polymorphic\n\nlocal module = {}\n\nlocal httpService \t\t= game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal network\nlocal utilities\nlocal configuration\nlocal terrainUtil\n\n-- >>> BIGGGG REALIZATION!!! Do not let the client tell you what status effect\n-- to apply. The server will decide to apply a status effect as it receives\n-- requests from the client to use an item or use an ability!\n-- ie, in manager_ability when activeAbilityId is set, determine if there\n-- is an associated status effect with the activation of the ability and\n-- if so, channel it through manager_statusEffect\n\nlocal statusEffectLookup = require(replicatedStorage.statusEffectLookup)\nlocal activeStatusEffectsCollectionContainer = {}\n\n--[[\n\tactiveStatusEffect {}\n\t\tint sourceType[\"ability\"; \"entity\"; \"consumable\"]\n\t\tint sourceId\n\n\t\tstring statusEffectType\n\t\tstatusEffectData_intermediate statusEffectData\n\t\t\tint\n\n\t\tstring sourceEntityGUID\n--]]\n\nlocal function updatePlayerStatusEffects(player)\nend\n\nlocal function onGetStatusEffectsOnEntityManifestByEntityGUID(entityGUID)\n\tlocal statusEffects = {}\n\n\tfor i, activeStatusEffectData in pairs(activeStatusEffectsCollectionContainer) do\n\t\tif activeStatusEffectData.affecteeEntityGUID == entityGUID then\n\t\t\ttable.insert(statusEffects, activeStatusEffectData)\n\t\tend\n\tend\n\n\treturn statusEffects\nend\n\nlocal function doesEntityManifestHaveStatusEffectBySourceType(entityManifest, sourceType)\n\tlocal entityGUID = utilities.getEntityGUIDByEntityManifest(entityManifest)\n\n\tif entityGUID then\n\t\tfor i, activeStatusEffectData in pairs(activeStatusEffectsCollectionContainer) do\n\t\t\tif activeStatusEffectData.affecteeEntityGUID == entityGUID and activeStatusEffectData.sourceType == sourceType then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal function removeStatusEffectByIndex(index)\n\tlocal activeStatusEffectData = activeStatusEffectsCollectionContainer[index]\n\ttable.remove(activeStatusEffectsCollectionContainer, index)\n\n\t-- potentially call an on-ended status effect thing\n\tlocal statusEffectBaseData = statusEffectLookup[activeStatusEffectData.statusEffectType]\n\tif statusEffectBaseData and statusEffectBaseData.onEnded_server then\n\t\tlocal entityManifest = utilities.getEntityManifestByEntityGUID(activeStatusEffectData.affecteeEntityGUID)\n\t\tif entityManifest then\n\t\t\tstatusEffectBaseData.onEnded_server(activeStatusEffectData, entityManifest)\n\t\tend\n\tend\nend\n\nlocal function updateStatusEffectsForEntityManifestByEntityGUID(entityGUID)\n\tlocal statusEffects \t= onGetStatusEffectsOnEntityManifestByEntityGUID(entityGUID)\n\tlocal entityManifest \t= utilities.getEntityManifestByEntityGUID(entityGUID)\n\n\tif entityManifest and entityManifest:FindFirstChild(\"statusEffectsV2\") then\n\t\tlocal success, returnValue = utilities.safeJSONEncode(statusEffects)\n\n\t\tif success then\n\t\t\tentityManifest.statusEffectsV2.Value = returnValue\n\n\t\t\tif entityManifest.Parent then\n\t\t\t\tlocal player = game.Players:GetPlayerFromCharacter(entityManifest.Parent)\n\n\t\t\t\tif player then\n\t\t\t\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\t\t\t\t\tif playerData then\n\t\t\t\t\t\tplayerData.nonSerializeData.playerDataChanged:Fire(\"statusEffects\")\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function removeStatusEffectFromEntityManifestBySourceType(entityManifest, sourceType)\n\tlocal entityGUID = utilities.getEntityGUIDByEntityManifest(entityManifest)\n\n\tif entityGUID then\n\t\tfor i = #activeStatusEffectsCollectionContainer, 1, -1 do\n\t\t\tlocal activeStatusEffectData = activeStatusEffectsCollectionContainer[i]\n\t\t\tif activeStatusEffectData.affecteeEntityGUID == entityGUID and activeStatusEffectData.sourceType == sourceType then\n\t\t\t\tlocal statusEffectBaseData = statusEffectLookup[activeStatusEffectData.statusEffectType]\n\n\t\t\t\tif statusEffectBaseData._serverCleanupFunction then\n\t\t\t\t\tstatusEffectBaseData._serverCleanupFunction(activeStatusEffectData, entityManifest)\n\t\t\t\tend\n\n\t\t\t\tremoveStatusEffectByIndex(i)\n\t\t\t\tupdateStatusEffectsForEntityManifestByEntityGUID(entityGUID)\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal function onPlayerRemovingPackageStatusEffects(player)\n\tif not player:FindFirstChild(\"entityGUID\") then warn(\"FAILED TO FIND PLAYER ENTITYGUID FOR STATUS EFFECTS PACKAGING\") return false end\n\n\tlocal playerEntityGUID \t= player.entityGUID.Value\n\tlocal statusEffects \t= {}\n\n\tfor i = #activeStatusEffectsCollectionContainer, 1, -1 do\n\t\tlocal activeStatusEffectData \t= activeStatusEffectsCollectionContainer[i]\n\n\t\tlocal statusBaseData = statusEffectLookup[activeStatusEffectData.statusEffectType]\n\t\tlocal doNotSave =\n\t\t\t(activeStatusEffectData.DO_NOT_SAVE) or\n\t\t\t(statusBaseData and statusBaseData.notSavedToPlayerData)\n\n\t\tif activeStatusEffectData.affecteeEntityGUID == playerEntityGUID and not doNotSave then\n\t\t\tremoveStatusEffectByIndex(i)\n\t\t\ttable.insert(statusEffects, activeStatusEffectData)\n\t\tend\n\tend\n\n\treturn statusEffects\nend\n\nlocal function onPlayerAddedContinuePackageStatusEffects(player, packageStatusEffects)\n\tif not player:FindFirstChild(\"entityGUID\") then warn(\"FAILED TO FIND PLAYER ENTITYGUID\") return false end\n\n\tfor i, activeStatusEffectData in pairs(packageStatusEffects) do\n\t\t-- update to current player\n\t\tactiveStatusEffectData.affecteeEntityGUID = player.entityGUID.Value\n\n\t\t-- pop to be updated\n\t\ttable.insert(activeStatusEffectsCollectionContainer, activeStatusEffectData)\n\tend\nend\n\nlocal function int__startTickingAbilities()\n\tlocal activeStatusEffectTickTimePerSecond = configuration.getConfigurationValue(\"activeStatusEffectTickTimePerSecond\")\n\n\tlocal function tickStatusEffects()\n\t\tlocal requiresUpdate \t\t\t\t\t\t= {}\n\t\tlocal indicesToRemove = {}\n\n\t\t-- index in reverse so we can table.remove just fine, remove finished statusEffects\n\t\tfor i = #activeStatusEffectsCollectionContainer, 1, -1 do\n\t\t\tlocal activeStatusEffectData \t= activeStatusEffectsCollectionContainer[i]\n\t\t\tlocal entityManifest, isInWorld = utilities.getEntityManifestByEntityGUID(activeStatusEffectData.affecteeEntityGUID)\n\n\t\t\tlocal shouldUpdateEntity = false\n\n\t\t\tif entityManifest then\n\t\t\t\tlocal statusEffectBaseData = statusEffectLookup[activeStatusEffectData.statusEffectType]\n\n\t\t\t\tif statusEffectBaseData and statusEffectBaseData.execute then\n\t\t\t\t\tstatusEffectBaseData.execute(activeStatusEffectData, entityManifest, activeStatusEffectTickTimePerSecond)\n\t\t\t\tend\n\n\t\t\t\tif activeStatusEffectData.ticksMade then\n\t\t\t\t\tactiveStatusEffectData.ticksMade = activeStatusEffectData.ticksMade + 1\n\n\t\t\t\t\tif activeStatusEffectData.ticksMade >= activeStatusEffectData.ticksNeeded then\n\t\t\t\t\t\tremoveStatusEffectByIndex(i)\n\t\t\t\t\tend\n\n\t\t\t\t\tshouldUpdateEntity = true\n\t\t\t\tend\n\t\t\telseif not isInWorld then\n\t\t\t\t-- pop it\n\n\t\t\t\tshouldUpdateEntity = true\n\t\t\t\tremoveStatusEffectByIndex(i)\n\t\t\tend\n\n\t\t\tif shouldUpdateEntity then\n\t\t\t\tlocal guid = activeStatusEffectData.affecteeEntityGUID\n\t\t\t\tif guid and not requiresUpdate[guid] then\n\t\t\t\t\trequiresUpdate[guid] = true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t-- update everything\n\t\tfor guid, _ in pairs(requiresUpdate) do\n\t\t\tupdateStatusEffectsForEntityManifestByEntityGUID(guid)\n\t\tend\n\tend\n\n\tlocal savedTime = 0\n\tlocal tickDuration = 1 / activeStatusEffectTickTimePerSecond\n\n\tlocal function onHeartbeat(dt)\n\t\tsavedTime = savedTime + dt\n\t\twhile savedTime > tickDuration do\n\t\t\tsavedTime = savedTime - tickDuration\n\t\t\ttickStatusEffects()\n\t\tend\n\tend\n\n\tgame:GetService(\"RunService\").Heartbeat:Connect(onHeartbeat)\nend\n\n-- \"applyStatusEffectToEntityManifest\", player.Character.PrimaryPart, \"regenerate\", {health = 25; duration = 5}, {sourceId = item}\n\nlocal function onApplyStatusEffectToEntityManifest(entityManifest, statusEffectType, statusEffectModifierData, sourceEntityManifest, sourceType, sourceId, variant)\n\tprint(\"APPLYING AN EFFECT\")\n\tif not statusEffectLookup[statusEffectType] then\n\t\treturn false, \"invalid status effect\"\n\tend\n\n\tlocal activeStatusEffectTickTimePerSecond \t= configuration.getConfigurationValue(\"activeStatusEffectTickTimePerSecond\")\n\tlocal sourceEntityGUID \t\t\t\t\t\t= utilities.getEntityGUIDByEntityManifest(sourceEntityManifest)\n\tlocal affecteeEntityGUID \t\t\t\t\t= utilities.getEntityGUIDByEntityManifest(entityManifest)\n\tlocal statusEffectBaseData \t\t\t\t\t= statusEffectLookup[statusEffectType]\n\tlocal activeStatusEffectData \t\t\t\t= {}\n\t\t-- help us decipher where this statusEffect came from\n\t\tactiveStatusEffectData.sourceType \t\t= sourceType\n\t\tactiveStatusEffectData.sourceId \t\t= sourceId\n\t\tif variant then\n\t\t\tactiveStatusEffectData.variant\t\t\t= variant\n\t\tend\n\t\tactiveStatusEffectData.sourceEntityGUID = sourceEntityGUID\n\n\t\t-- what kind of status effect is this?\n\t\tactiveStatusEffectData.statusEffectType \t= statusEffectType\n\t\tactiveStatusEffectData.statusEffectModifier = statusEffectModifierData\n\t\tactiveStatusEffectData.statusEffectGUID \t= httpService:GenerateGUID(false)\n\n\t\t-- who does this affect?\n\t\tactiveStatusEffectData.affecteeEntityGUID \t= affecteeEntityGUID\n\t\tactiveStatusEffectData.timestamp \t\t\t= tick()\n\n\t-- handle internal stuff here --\n\tif activeStatusEffectData.statusEffectModifier.duration then\n\t\tactiveStatusEffectData.ticksMade \t= 0\n\t\tactiveStatusEffectData.ticksNeeded \t= activeStatusEffectData.statusEffectModifier.duration * activeStatusEffectTickTimePerSecond\n\telse\n\t\tactiveStatusEffectData.isPermanent = true\n\tend\n\n\tif statusEffectBaseData.hideInStatusBar then\n\t\tactiveStatusEffectData.hideInStatusBar = true\n\tend\n\n\tif activeStatusEffectData.statusEffectModifier.DO_NOT_SAVE then\n\t\tactiveStatusEffectData.DO_NOT_SAVE = true\n\tend\n\n\tif activeStatusEffectData.statusEffectModifier.icon then\n\t\tactiveStatusEffectData.icon = activeStatusEffectData.statusEffectModifier.icon\n\telse\n\t\tif (sourceType ~= \"item\") and (sourceType ~= \"ability\") then\n\t\t\tactiveStatusEffectData.icon = statusEffectBaseData.image\n\t\tend\n\tend\n\n\t-- pop duplicate statusEffects out\n\tfor i = #activeStatusEffectsCollectionContainer, 1, -1 do\n\t\tlocal _statusEffectData = activeStatusEffectsCollectionContainer[i]\n\n\t\tlocal sameType = _statusEffectData.sourceType == sourceType\n\t\tlocal sameSource = _statusEffectData.sourceId == sourceId\n\t\tlocal sameAffectee = _statusEffectData.affecteeEntityGUID == affecteeEntityGUID\n\n\t\tif sameType and sameSource and sameAffectee then\n\t\t\tremoveStatusEffectByIndex(i)\n\t\tend\n\tend\n\n\tif statusEffectBaseData._serverExecutionFunction then\n\t\tstatusEffectBaseData._serverExecutionFunction(activeStatusEffectData, entityManifest)\n\tend\n\n\tif statusEffectBaseData.onStarted_server then\n\t\tstatusEffectBaseData.onStarted_server(activeStatusEffectData, entityManifest)\n\tend\n\n\ttable.insert(activeStatusEffectsCollectionContainer, activeStatusEffectData)\n\n\t-- update status effects\n\tupdateStatusEffectsForEntityManifestByEntityGUID(affecteeEntityGUID)\n\n\tif sourceId == \"item\" then\n\t\tutilities.playSound(\"item_buff\", entityManifest)\n\tend\n\n\treturn true, activeStatusEffectData.statusEffectGUID\nend\n\n\n\nlocal function revokeStatusEffectByStatusEffectGUID(statusEffectGUID)\n\tfor i = #activeStatusEffectsCollectionContainer, 1, -1 do\n\t\tlocal status = activeStatusEffectsCollectionContainer[i]\n\t\tif status.statusEffectGUID == statusEffectGUID then\n\t\t\tremoveStatusEffectByIndex(i)\n\t\t\tupdateStatusEffectsForEntityManifestByEntityGUID(status.affecteeEntityGUID)\n\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\n\n\n-- when a player enters water, certain status effects should be wiped\nlocal function onPlayerEnteredWater(player, clientPosition)\n\tlocal char = player.Character\n\tif not char then return end\n\tlocal root = char.PrimaryPart\n\tif not root then return end\n\tlocal guid = utilities.getEntityGUIDByEntityManifest(root)\n\tif not guid then return end\n\tif not terrainUtil.isPointUnderwater(clientPosition) then return end\n\tlocal sanityRangeSq = 6 ^ 2\n\tlocal delta = clientPosition - root.Position\n\tlocal distanceSq = delta.X ^ 2 + delta.Y ^ 2 + delta.Z ^ 2\n\tif distanceSq > sanityRangeSq then return end\n\n\tlocal updateRequired = false\n\n\tfor index = #activeStatusEffectsCollectionContainer, 1, -1 do\n\t\tlocal activeStatusEffectData = activeStatusEffectsCollectionContainer[index]\n\n\t\tif activeStatusEffectData.affecteeEntityGUID == guid then\n\t\t\t-- here is where we determine if a status must be removed\n\t\t\tif activeStatusEffectData.statusEffectType == \"ablaze\" then\n\t\t\t\tremoveStatusEffectByIndex(index)\n\t\t\t\tupdateRequired = true\n\t\t\tend\n\t\tend\n\tend\n\n\tif updateRequired then\n\t\tupdateStatusEffectsForEntityManifestByEntityGUID(guid)\n\tend\nend\n\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\tutilities = Modules.utilities\n\tconfiguration = Modules.configuration\n\tterrainUtil = Modules.terrainUtil\n\n\tnetwork:create(\"applyStatusEffectToEntityManifest\", \"BindableFunction\", \"OnInvoke\", onApplyStatusEffectToEntityManifest)\n\tnetwork:create(\"revokeStatusEffectByStatusEffectGUID\", \"BindableFunction\", \"OnInvoke\", revokeStatusEffectByStatusEffectGUID)\n\tnetwork:create(\"onPlayerEnteredWater\", \"RemoteEvent\", \"OnServerEvent\", onPlayerEnteredWater)\n\tnetwork:create(\"removeStatusEffectFromEntityManifestBySourceType\", \"BindableFunction\", \"OnInvoke\", removeStatusEffectFromEntityManifestBySourceType)\n\tnetwork:create(\"doesEntityManifestHaveStatusEffectBySourceType\", \"BindableFunction\", \"OnInvoke\", doesEntityManifestHaveStatusEffectBySourceType)\n\n\tnetwork:create(\"doesEntityHaveStatusEffect\", \"BindableFunction\", \"OnInvoke\", function(manifest, effectType)\n\t\tif not manifest then return false end\n\t\tlocal guid = utilities.getEntityGUIDByEntityManifest(manifest)\n\t\tif not guid then return false end\n\n\t\tlocal statuses = network:invoke(\"getStatusEffectsOnEntityManifestByEntityGUID\", guid)\n\n\t\tfor _, status in pairs(statuses) do\n\t\t\tif status.statusEffectType == effectType then\n\t\t\t\treturn true, status\n\t\t\tend\n\t\tend\n\n\t\treturn false\n\tend)\n\n\tnetwork:create(\"updateStatusEffectsForEntityManifestByEntityGUID\", \"BindableFunction\", \"OnInvoke\", updateStatusEffectsForEntityManifestByEntityGUID)\n\n\tnetwork:create(\"playerRemovingPackageStatusEffects\", \"BindableFunction\", \"OnInvoke\", onPlayerRemovingPackageStatusEffects)\n\tnetwork:create(\"playerAddedContinuePackageStatusEffects\", \"BindableFunction\", \"OnInvoke\", onPlayerAddedContinuePackageStatusEffects)\n\n\tnetwork:create(\"getStatusEffectsOnEntityManifestByEntityGUID\", \"BindableFunction\", \"OnInvoke\", onGetStatusEffectsOnEntityManifestByEntityGUID)\n\n\tnetwork:create(\"getIsManifestStunned\", \"BindableFunction\", \"OnInvoke\", function(manifest)\n\t\tif not manifest then return false end\n\t\tlocal guid = utilities.getEntityGUIDByEntityManifest(manifest)\n\t\tif not guid then return false end\n\n\t\tlocal statuses = network:invoke(\"getStatusEffectsOnEntityManifestByEntityGUID\", guid)\n\n\t\tfor _, status in pairs(statuses) do\n\t\t\tif status.statusEffectType == \"stunned\" then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\n\t\treturn false\n\tend)\n\n\tspawn(int__startTickingAbilities)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_taxi/dialogue.lua",
    "content": "local extraOptionsByPlaceId = {\n\t-- mushtown\n\t[2064647391] = {\n\t\t{\n\t\t\tresponse = \"Where's your horse?\",\n\t\t\tdialogue = {{text = \"Taximan Dave doesn't need a horse, silly!\"}},\n\t\t},\n\t},\n\t\n\t-- port fidelio\n\t[2546689567] = {\n\t\t{\n\t\t\tresponse = \"Where's your swimsuit?\",\n\t\t\tdialogue = {{text = \"Taximan Dave doesn't get to have fun, silly!\"}},\n\t\t}\n\t},\n\t\n\t-- warrior stronghold\n\t[2470481225] = {\n\t\t{\n\t\t\tresponse = \"Where's your coat?\",\n\t\t\tdialogue = {{text = \"Taximan Dave doesn't get cold, silly!\"}},\n\t\t}\n\t},\n\t\n\t-- guild hall\n\t[4653017449] = {\n\t\t{\n\t\t\tresponse = \"Where's your cart?\",\n\t\t\tdialogue = {{text = \"Taximan Dave doesn't need a cart, silly!\"}},\n\t\t}\n\t},\n}\n\nreturn {\n\tsound = \"npc_male_ahaa\",\n\tid = \"startTalkingTo\",\n\tcanExit = true,\n\t\n\tdialogue = {{text = \"Greetings Adventurer! You know me, the one and only Taximan Dave! I can take you to any location that you've visited before.\"}},\n\toptions = function(util)\n\t\tlocal utilities = util.utilities\n\t\tlocal network = util.network\n\t\tlocal options = {\n\t\t\t{\n\t\t\t\tcanExit = true,\n\t\t\t\tid = \"choose\";\n\t\t\t\tresponse = \"Let's go somewhere.\",\n\t\t\t\tresponseButtonColor = Color3.fromRGB(234, 174, 53),\n\t\t\t\tdialogue = {{text = \"Alright Adventurer! Where would you like me to take ya?\"}},\n\t\t\t\ttaxiMenu = true;\n\t\t\t\t\n\t\t\t\toptions = {\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid = \"undiscovered\";\n\t\t\t\t\t\t\tdialogue = {{text = \"There's a lot of places that I don't know how to get to yet. Maybe if you discover them you can tell me how to get there!\"}};\n\t\t\t\t\t\t\tmoveToId = \"choose\";\n\t\t\t\t\t\t};\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid = \"spawns\";\n\t\t\t\t\t\t\tdialogue = function(util, extraData)\n\t\t\t\t\t\t\t\treturn {{text = \"Alright! Where in\"},{text = extraData.taxiLocationName,font = Enum.Font.SourceSansBold},{text = \"should I take ya?\"}};\n\t\t\t\t\t\t\tend;\n\t\t\t\t\t\t\tcanExit = true;\n\t\t\t\t\t\t\toptions = function(util, extraData)\n\t\t\t\t\t\t\t\tlocal locations = network:invoke(\"getCacheValueByNameTag\", \"locations\")\n\t\t\t\t\t\t\t\tlocal placeData = locations[extraData.taxiLocation]\n\t\t\t\t\t\t\t\tlocal checkpointOptions = {}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tfor spawnName,spawnData in pairs(placeData.spawns) do\n\t\t\t\t\t\t\t\t\tif spawnData.text then\n\t\t\t\t\t\t\t\t\t\ttable.insert(checkpointOptions, {\n\t\t\t\t\t\t\t\t\t\t\tresponse = spawnData.text;\n\t\t\t\t\t\t\t\t\t\t\tdialogue = function(util)\n\t\t\t\t\t\t\t\t\t\t\t\tlocal success = network:invokeServer(\"playerRequest_taximanDave\", extraData.taxiLocation, spawnName)\n\t\t\t\t\t\t\t\t\t\t\t\tif success then\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn {{text = \"Giddy up, lets go!\"}}\n\t\t\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\t\t\treturn {{text = \"I can't help you right now. Sorry!\"}}\n\t\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\t\tend;\n\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\treturn checkpointOptions\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t};\n\t\t\t\t};\n\t\n\t\t\t\t--[[\n\t\t\t\toptions = function(util)\n\t\t\t\t\tlocal player = game:GetService(\"Players\").LocalPlayer\n\t\t\t\t\t\n\t\t\t\t\tlocal taxiInfo = util.network:invokeServer(\"getTaxiInfo\")\n\t\t\t\t\t\n\t\t\t\t\tlocal options = {}\n\t\t\t\t\t\n\t\t\t\t\tfor _, info in pairs(taxiInfo) do\n\t\t\t\t\t\tlocal color = Color3.fromRGB(234, 174, 53)\n\t\t\t\t\t\tif not info.enabled then\n\t\t\t\t\t\t\tcolor = Color3.new(0.4, 0.4, 0.4)\n\t\t\t\t\t\tend\n\t\t\t\t\t\t\n\t\t\t\t\t\tlocal option = {\n\t\t\t\t\t\t\tresponse = string.format(\"%s (%d Silver)\", info.name, info.price),\n\t\t\t\t\t\t\tresponseButtonColor = color,\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tdialogue = function(util)\n\t\t\t\t\t\t\t\tlocal success, reason, message = util.network:invokeServer(\"takeTaxiToDestination\", info.id)\n\t\t\t\t\t\t\t\tif success then\n\t\t\t\t\t\t\t\t\treturn {{text = \"All right, then! Giddy up, let's go!\"}}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tif reason == \"notEnoughMoney\" then\n\t\t\t\t\t\t\t\t\t\treturn {{text = \"Come back when you can afford the trip.\"}}\n\t\t\t\t\t\t\t\t\telseif reason == \"notValidDestination\" then\n\t\t\t\t\t\t\t\t\t\treturn {{text = \"Sorry, uh, I've never heard of that place.\"}}\n\t\t\t\t\t\t\t\t\telseif reason == \"conditionUnfulfilled\" then\n\t\t\t\t\t\t\t\t\t\treturn {{text = message}}\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\treturn {{text = reason}}\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend,\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttable.insert(options, option)\n\t\t\t\t\tend\n\t\t\t\t\t\n\t\t\t\t\treturn options\n\t\t\t\t\t\n\t\t\t\tend\n\t\t\t\t]]\n\t\t\t}\n\t\t}\n\t\t\n\t\tlocal extraOptions = extraOptionsByPlaceId[utilities.originPlaceId(game.PlaceId)]\n\t\tif extraOptions then\n\t\t\tfor _, option in pairs(extraOptions) do\n\t\t\t\ttable.insert(options, option)\n\t\t\tend\n\t\tend\n\t\t\n\t\treturn options\n\tend\n}"
  },
  {
    "path": "src/ServerScriptService/contents/manager_taxi/init.lua",
    "content": "local replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal modules = require(replicatedStorage:WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\nlocal utilities = modules.load(\"utilities\")\n\n\nlocal taximan\n\nlocal function setUpTaximan()\n\ttaximan = workspace:FindFirstChild(\"Taximan Dave\", true)\n\tif not taximan then return end\n\t\n\tlocal dialogue = script.dialogue:Clone()\n\tdialogue.Parent = taximan.UpperTorso\nend\nnetwork:create(\"setUpTaximan\", \"BindableFunction\", \"OnInvoke\", setUpTaximan)\n\nsetUpTaximan()\n\nlocal function playerRequest_taximanDave(player, destination, spawnLocation)\n\tif not taximan then return end\n\tif (not player.Character) or (not player.Character.PrimaryPart) then return end\n\tif (player.Character.PrimaryPart.Position - taximan.PrimaryPart.Position).magnitude >= 100 then return end\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\tif not playerData then return false end\n\tlocal placeData = playerData.locations[destination]\n\tif placeData and placeData.spawns and placeData.spawns[spawnLocation] then\n\t\tnetwork:invoke(\"teleportPlayer\", player, tonumber(destination), spawnLocation, nil, \"taxi\")\n\tend\nend\n\nnetwork:create(\"playerRequest_taximanDave\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_taximanDave)\n\n-- old taximan dave code by davidii\n-- some cool stuff in there like determining distance between locations\n-- maybe we'll use in the future\n\n\n-- constants that help do math\nlocal silverPerDistance = 5\n\n-- destinations are formatted like so\n-- string name = the name of the place as displayed in the dialogue\n-- int id = the place id of the destination\n-- bool, string condition(player) = a function that returns whether or not a player can travel to that location and a message as to why not\nlocal destinations = {\n\tportFidelio = {\n\t\tname = \"Port Fidelio\",\n\t\tid = 2546689567,\n\t},\n\t\n\twarriorStronghold = {\n\t\tname = \"Warrior Stronghold\",\n\t\tid = 2470481225,\n\t},\n\t\n\ttreeOfLife = {\n\t\tname = \"Tree of Life\",\n\t\tid = 3112029149,\n\t},\n\t\n\tnilgarf = {\n\t\tname = \"Nilgarf\",\n\t\tid = 2119298605,\n\t},\n\t\n\tmushtown = {\n\t\tname = \"Mushtown\",\n\t\tid = 2064647391,\n\t},\n\t\n\thog = {\n\t\tname = \"The Gauntlet\",\n\t\tid = 3360349837,\n\t\tcondition = function(player)\n\t\t\tlocal data = network:invoke(\"getPlayerData\", player)\n\t\t\tif data.flags.completedGauntlet then\n\t\t\t\treturn true\n\t\t\telse\n\t\t\t\treturn false, \"Sorry, haven't been able to get up that way since the bandits moved in.\"\n\t\t\tend\n\t\tend,\n\t},\n\t\n\tguildHall = {\n\t\tpriceOverride = 10,\n\t\t\n\t\tname = \"Guild Hall\",\n\t\tid = 4653017449,\n\t\tcondition = function(player)\n\t\t\tlocal guildId = player:FindFirstChild(\"guildId\")\n\t\t\tif (not guildId) or (guildId.Value == \"\") then\n\t\t\t\treturn false, \"You're not in a guild, silly!\"\n\t\t\tend\n\t\t\tguildId = guildId.Value\n\t\t\t\n\t\t\tlocal guildData = network:invoke(\"getGuildData\", player, guildId)\n\t\t\tif not guildData then\n\t\t\t\treturn false, \"Couldn't find your guild data, silly!\"\n\t\t\tend\n\t\t\t\n\t\t\tif guildData.level < 3 then\n\t\t\t\treturn false, \"Sorry, I don't do work for small-time guilds. Get a level 3 guild, silly!\"\n\t\t\tend\n\t\t\t\n\t\t\tif not guildData.hallLocation then\n\t\t\t\treturn false, \"Uh, you might want to have a guild hall first, silly!\"\n\t\t\tend\n\t\t\t\n\t\t\treturn true\n\t\tend,\n\t\ttravelFunction = function(player)\n\t\t\tlocal guildId = player:FindFirstChild(\"guildId\")\n\t\t\tif not guildId then return end\n\t\t\tguildId = guildId.Value\n\t\t\tif guildId == \"\" then return end\n\t\t\t\n\t\t\tnetwork:invoke(\"teleportPlayersToGuildHall\", {player}, guildId)\n\t\tend,\n\t},\n}\n\n-- connections\n-- the system thinks of the game like a network of roads\n-- it uses dijkstra's algorithm to find the shortest path\n-- and creates the price of the route automatically using\n-- the connections created here\nlocal function addConnection(placeA, placeB, distance)\n\tif not destinations[placeA] then\n\t\terror(\"Invalid connection: \"..placeA..\" is not a valid destination.\")\n\tend\n\tif not destinations[placeB] then\n\t\terror(\"Invalid connection: \"..placeB..\" is not a valid destination.\")\n\tend\n\t\n\tlocal infoA = destinations[placeA]\n\tif not infoA.connections then\n\t\tinfoA.connections = {}\n\tend\n\tinfoA.connections[placeB] = distance\n\t\n\tlocal infoB = destinations[placeB]\n\tif not infoB.connections then\n\t\tinfoB.connections = {}\n\tend\n\tinfoB.connections[placeA] = distance\nend\n\n-- here is where the connections get made, two names\n-- and a distance between them for price generation\n-- no need to do reversals, the system automatically links\n-- in both directions\naddConnection(\"nilgarf\", \"mushtown\", 2)\naddConnection(\"nilgarf\", \"portFidelio\", 3)\naddConnection(\"nilgarf\", \"warriorStronghold\", 4)\naddConnection(\"nilgarf\", \"treeOfLife\", 3)\naddConnection(\"nilgarf\", \"guildHall\", 1)\naddConnection(\"portFidelio\", \"hog\", 2)\n\nlocal function getDestinationById(placeId)\n\tfor id, info in pairs(destinations) do\n\t\tif info.id == placeId then\n\t\t\treturn id, info\n\t\tend\n\tend\n\treturn nil\nend\n\nlocal function getTaxiInfoForPlayer(player)\n\t\n\tlocal hereId, hereInfo = getDestinationById(utilities.originPlaceId(game.PlaceId))\n\tif not (hereId and hereInfo) then\n\t\terror(\"Attempted to get taxi data in a place without taxi data.\")\n\tend\n\t\n\tlocal visited = {}\n\tlocal taxiInfo = {}\n\t\n\tlocal function addDestination(id, info, distance)\n\t\tlocal enabled = true\n\t\tlocal reason = nil\n\t\tif info.condition then\n\t\t\tenabled, reason = info.condition(player)\n\t\tend\n\t\t\n\t\ttable.insert(taxiInfo, {\n\t\t\tid = info.id,\n\t\t\tname = info.name,\n\t\t\tprice = (hereInfo.priceOverride) or (info.priceOverride) or (distance * silverPerDistance),\n\t\t\tenabled = enabled,\n\t\t\treason = reason,\n\t\t\ttravelFunction = info.travelFunction,\n\t\t})\n\tend\n\t\n\tlocal queue = {\n\t\t{id = hereId, distance = 0},\n\t}\n\t\n\twhile #queue > 0 do\n\t\tlocal id = queue[1].id\n\t\tlocal info = destinations[id]\n\t\tlocal distance = queue[1].distance\n\t\t\n\t\ttable.remove(queue, 1)\n\t\t\n\t\tif not visited[id] then\n\t\t\tvisited[id] = true\n\t\t\t\n\t\t\tif id ~= hereId then\n\t\t\t\taddDestination(id, info, distance)\n\t\t\tend\n\t\t\t\n\t\t\tfor nextId, nextDistance in pairs(info.connections or {}) do\n\t\t\t\ttable.insert(queue, {id = nextId, distance = distance + nextDistance})\n\t\t\tend\n\t\tend\n\tend\n\t\n\treturn taxiInfo\nend\nnetwork:create(\"getTaxiInfo\", \"RemoteFunction\", \"OnServerInvoke\", getTaxiInfoForPlayer)\n--[[\nlocal function takeTaxiToDestination(player, placeId)\n\tlocal taxiInfo = getTaxiInfoForPlayer(player)\n\tlocal destinationInfo\n\tfor _, info in pairs(taxiInfo) do\n\t\tif info.id == placeId then\n\t\t\tdestinationInfo = info\n\t\t\tbreak\n\t\tend\n\tend\n\t\n\tif not destinationInfo.enabled then\n\t\treturn false, \"conditionUnfulfilled\", destinationInfo.reason\n\tend\n\t\n\tlocal success = network:invoke(\"tradeItemsBetweenPlayerAndNPC\", player, {}, destinationInfo.price * 1000, {}, 0, \"etc:taxi\")\n\tif success then\n\t\tdelay(0.2, function()\n\t\t\tif destinationInfo.travelFunction then\n\t\t\t\tdestinationInfo.travelFunction(player)\n\t\t\telse\n\t\t\t\tnetwork:invoke(\"teleportPlayer\", player, destinationInfo.id, \"taxi\")\n\t\t\tend\n\t\tend)\n\t\t\n\t\treturn true, \"\"\n\telse\n\t\treturn false, \"notEnoughMoney\"\n\tend\nend\nnetwork:create(\"takeTaxiToDestination\", \"RemoteFunction\", \"OnServerInvoke\", takeTaxiToDestination)\n]]\n\n\n\n\n\n\nreturn {}"
  },
  {
    "path": "src/ServerScriptService/contents/manager_teleport.lua",
    "content": "local module = {}\nmodule.priority = 3\n\nlocal TeleportService = game:GetService(\"TeleportService\")\n\nlocal network\nlocal utilities\nlocal configuration\n\nlocal function preparePlayerToTeleport(player, destination)\n\tif player:FindFirstChild(\"DataSaveFailed\") then\n\t\tnetwork:fireClient(\"alertPlayerNotification\", player, {\n\t\t\ttext = \"Cannot teleport during a DataStore outage.\";\n\t\t\ttextColor3 = Color3.fromRGB(255, 57, 60)\n\t\t})\n\t\treturn false\n\tend\n\tif player:FindFirstChild(\"Teleporting\") or player:FindFirstChild(\"teleporting\") then\n\t\treturn false\n\tend\n\tif player:FindFirstChild(\"DataLoaded\") == nil then\n\t\treturn false\n\tend\n\tdestination = utilities.placeIdForGame(destination)\n\tlocal timestamp = network:invoke(\"saveDataForTeleport\", player)\n\tif timestamp then\n\t\tlocal teleportData = {}\n\t\tteleportData.arrivingFrom = game.PlaceId\n\t\tteleportData.destination = destination\n\t\tteleportData.dataTimestamp = timestamp\n\t\tteleportData.dataSlot = player.dataSlot.Value\n\t\tteleportData.analyticsSessionId = player.AnalyticsSessionId.Value\n\t\tteleportData.joinTime = player.JoinTime.Value\n\t\tteleportData.partyData = network:invoke(\"getPartyDataByPlayer\", player)\n\t\treturn teleportData\n\telse\n\t\tnetwork:fireClient(\"alertPlayerNotification\", player, {\n            text = \"Failed to save your data. Teleportation canceled.\";\n            textColor3 = Color3.fromRGB(255, 57, 60)\n        })\n\t\treturn false\n\tend\nend\n\nlocal function createPartyTeleportData(playersToTeleport, destination, partyLeaderUserId, spawnLocation)\n\n\tfor _, player in pairs(playersToTeleport) do\n\t\tnetwork:fireClient(\"alertPlayerNotification\", player, {text = \"Preparing to teleport party...\";})\n\tend\n\tlocal groupTeleportData = {}\n\tgroupTeleportData.members = {}\n\n\tfor _, player in pairs(playersToTeleport) do\n\t\tif player:FindFirstChild(\"teleporting\") then\n\t\t\tnetwork:fireClient(\"alertPlayerNotification\", player, {\n                text = \"Party teleport failed: \"..player.Name..\" is already teleporting.\";\n                textColor3 = Color3.fromRGB(255, 57, 60)\n            })\n\t\t\treturn false\n\t\tend\n\tend\n\n\tlocal playersSuccessfullySaved = {}\n\tfor _, player in pairs(playersToTeleport) do\n\t\tlocal playerTeleportData = preparePlayerToTeleport(player, destination)\n\t\twarn(player.Name, \"teleport data\", game.HttpService:JSONEncode(playerTeleportData))\n\t\tif playerTeleportData and playerTeleportData.partyData then\n\t\t\tplayerTeleportData.partyData.partyLeaderUserId = partyLeaderUserId\n\t\t\tif spawnLocation then\n\t\t\t\tplayerTeleportData.spawnLocation = spawnLocation\n\t\t\tend\n\n\t\t\ttable.insert(playersSuccessfullySaved, player)\n\t\t\tgroupTeleportData.members[player.Name] = playerTeleportData\n\t\telse\n\t\t\tfor _, oPlayer in pairs(playersToTeleport) do\n\t\t\t\tnetwork:fireClient(\"alertPlayerNotification\", oPlayer, {\n                    text = player.Name..\" failed to teleport with the party.\";\n                    textColor3 = Color3.fromRGB(234, 129, 59)\n                })\n\t\t\tend\n\t\tend\n\tend\n\n\tif #playersSuccessfullySaved > 0 then\n\t\twarn(\"group data\", game.HttpService:JSONEncode(groupTeleportData))\n\n\t\treturn playersSuccessfullySaved, groupTeleportData\n\telse\n\n\t\treturn false\n\tend\nend\n\nlocal function getReserveServerKeyForMirrorDestination(destination)\n\tdestination = utilities.placeIdForGame(destination)\n\tlocal reserveServerKey\n\tlocal success, err = pcall(function()\n\t\tlocal mwv = configuration.getConfigurationValue(\"mirrorWorldVersion\")\n\t\tlocal mirrorWorldStore = game:GetService(\"DataStoreService\"):GetDataStore(\"mirrorWorld\"..mwv)\n\t\treserveServerKey = mirrorWorldStore:GetAsync(tostring(destination))\n\t\tif reserveServerKey == nil then\n\t\t\treserveServerKey = TeleportService:ReserveServer(destination)\n\t\t\tmirrorWorldStore:SetAsync(tostring(destination), reserveServerKey)\n\t\tend\n\tend)\n\treturn reserveServerKey, success, err\nend\n\nlocal function teleportPlayersToReserveServer(players, destination, spawnLocation, realm, teleportType, reserveServerKey)\n\tdestination = utilities.placeIdForGame(destination)\n\tteleportType = teleportType or \"default\"\n\n\treserveServerKey = reserveServerKey or TeleportService:ReserveServer(destination)\n\n\tfor _, player in pairs(players) do\n\t\tspawn(function()\n\t\t\tif player:FindFirstChild(\"teleporting\") then\n\t\t\t\treturn false\n\t\t\tend\n\n\t\t\tif player:FindFirstChild(\"dataLoaded\") == nil or os.time() - player.dataLoaded.Value <= 5 then\n\t\t\t\treturn false, \"Please wait before teleporting\"\n\t\t\tend\n\n\t\t\tnetwork:fireClient(\"signal_teleport\", player, destination)\n\n\t\t\tlocal teleportData = preparePlayerToTeleport(player, destination)\n\n\t\t\tif teleportData then\n\t\t\t\tif spawnLocation then\n\t\t\t\t\tteleportData.spawnLocation = spawnLocation\n\t\t\t\tend\n\t\t\t\tteleportData.teleportType = teleportType\n\n\t\t\t\tif game.ReplicatedStorage:FindFirstChild(\"mirrorWorld\") or realm == \"mirror\" then\n\t\t\t\t\tlocal reserveServerKey = getReserveServerKeyForMirrorDestination(destination)\n\t\t\t\t\tif reserveServerKey then\n\t\t\t\t\t\tTeleportService:TeleportToPrivateServer(destination, reserveServerKey, {player}, nil, teleportData)\n\t\t\t\t\telse\n\t\t\t\t\t\treturn false, \"Failed to find mirror world reserve server key\"\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tTeleportService:TeleportToPrivateServer(destination, reserveServerKey, {player}, nil, teleportData)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\treturn false, \"Failed to prepare teleportData\"\n\t\tend)\n\tend\nend\n\nlocal function teleportParty(playersToTeleport, destination, partyLeaderUserId, spawnLocation)\n\tdestination = utilities.placeIdForGame(destination)\n\tlocal playersSuccessfullySaved, groupTeleportData = createPartyTeleportData(playersToTeleport, destination, partyLeaderUserId, spawnLocation)\n\tif playersSuccessfullySaved and groupTeleportData then\n\n\t\tlocal playerstring = \"\"\n\t\tlocal n = #playersToTeleport\n\t\tfor i,player in pairs(playersToTeleport) do\n\t\t\tplayerstring = playerstring .. player.Name\n\t\t\tif n > 1 and i == n-1 then\n\t\t\t\tplayerstring = playerstring .. \" & \"\n\t\t\telseif i < n then\n\t\t\t\tplayerstring = playerstring .. \",\"\n\t\t\tend\n\t\tend\n\n\t\tif game.ReplicatedStorage:FindFirstChild(\"mirrorWorld\") then\n\t\t\tlocal reserveServerKey = getReserveServerKeyForMirrorDestination(destination)\n\t\t\tif reserveServerKey then\n\t\t\t\tTeleportService:TeleportToPrivateServer(destination, reserveServerKey, playersSuccessfullySaved, nil, groupTeleportData)\n\t\t\telse\n\t\t\t\treturn false, \"Failed to find key for mirror world teleport\"\n\t\t\tend\n\t\telse\n\t\t\tTeleportService:TeleportPartyAsync(destination, playersSuccessfullySaved, groupTeleportData --[[, replicatedStorage.teleportUI]])\n\t\tend\n\n\t\tspawn(function()\n\n\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\tText = playerstring .. \" departed towards \" .. utilities.getPlaceName(destination) .. \".\";\n\t\t\t\tFont = Enum.Font.SourceSansBold;\n\t\t\t\tColor = Color3.fromRGB(45, 87, 255)\n\t\t\t})\n\n\t\tend)\n\t\treturn true, \"Teleporting\"\n\tend\n\treturn false, \"Failed to teleport\"\nend\n\nlocal function teleportPlayerToJobId(player, destination, jobId, spawnLocation)\n\tdestination = utilities.placeIdForGame(destination)\n\tlocal teleportType = \"serverBrowser\"\n\tif player:FindFirstChild(\"teleporting\") then\n\t\treturn false\n\tend\n\n\tif player:FindFirstChild(\"dataLoaded\") == nil or os.time() - player.dataLoaded.Value <= 5 then\n\t\treturn false, \"Please wait before teleporting\"\n\tend\n\n\tnetwork:fireClient(\"signal_teleport\", player, destination)\n\n\tlocal teleportData = preparePlayerToTeleport(player, destination)\n\n\tif teleportData then\n\t\tif spawnLocation then\n\t\t\tteleportData.spawnLocation = spawnLocation\n\t\tend\n\t\tteleportData.teleportType = teleportType\n\n\n\t\tspawn(function()\n\t\t\tTeleportService:TeleportToPlaceInstance(destination, jobId, player, nil, teleportData)\n\t\tend)\n\t\treturn true, \"teleporting\"\n\n\tend\n\n\treturn false, \"failed to prepare teleportdata\"\nend\n\nlocal function teleportPlayer(player, destination, spawnLocation, realm, teleportType)\n\tdestination = utilities.placeIdForGame(destination)\n\n\tlocal playerName = player.Name\n\tteleportType = teleportType or \"default\"\n\n\tif player:FindFirstChild(\"teleporting\") then\n\t\treturn false\n\tend\n\n\tif player:FindFirstChild(\"dataLoaded\") == nil or os.time() - player.dataLoaded.Value <= 5 then\n\t\treturn false, \"Please wait before teleporting\"\n\tend\n\n\tnetwork:fireClient(\"signal_teleport\", player, destination, teleportType)\n\n\tlocal teleportData = preparePlayerToTeleport(player, destination)\n\n\tif teleportData then\n\t\tif spawnLocation then\n\t\t\tteleportData.spawnLocation = spawnLocation\n\t\tend\n\t\tteleportData.teleportType = teleportType\n\n\t\tif game.ReplicatedStorage:FindFirstChild(\"mirrorWorld\") or realm == \"mirror\" then\n\t\t\tlocal reserveServerKey = getReserveServerKeyForMirrorDestination(destination)\n\t\t\tif reserveServerKey then\n\t\t\t\tTeleportService:TeleportToPrivateServer(destination, reserveServerKey, {player}, nil, teleportData)\n\t\t\t\treturn true, \"teleporting\"\n\t\t\telse\n\t\t\t\treturn false, \"Failed to find key for mirror world teleport\"\n\t\t\tend\n\t\telse\n\t\t\tspawn(function()\n\t\t\t\tTeleportService:Teleport(destination, player, teleportData)\n\t\t\tend)\n\t\t\tif teleportType == \"death\" and not player:FindFirstChild(\"disconnected\") then\n\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\t\tText = playerName .. \" escaped to \" .. utilities.getPlaceName(destination) .. \".\";\n\t\t\t\t\tFont = Enum.Font.SourceSansBold;\n\t\t\t\t\tColor = Color3.fromRGB(45, 87, 255)\n\t\t\t\t})\n\t\t\tend\n\t\t\treturn true, \"teleporting\"\n\t\tend\n\n\n\tend\n\n\treturn false, \"failed to prepare teleportdata\"\nend\n\nlocal function teleportPlayer_rune(player, destination)\n\tdestination = utilities.placeIdForGame(destination)\n\tlocal playerName = player.Name\n\tlocal success, reason = teleportPlayer(player, destination, nil, nil, \"rune\")\n\tif success then\n\t\tspawn(function()\n\n\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\tText = playerName .. \" departed towards \" .. utilities.getPlaceName(destination) .. \" using a magical rune.\";\n\t\t\t\tFont = Enum.Font.SourceSansBold;\n\t\t\t\tColor = Color3.fromRGB(45, 87, 255)\n\t\t\t})\n\n\t\tend)\n\tend\n\treturn success, reason\n\nend\n\nlocal function playerRequest_useTeleporter(player, teleporter)\n\tif teleporter and teleporter:IsA(\"BasePart\") and game.CollectionService:HasTag(teleporter, \"teleportPart\") and teleporter:FindFirstChild(\"teleportDestination\") then\n\t\tif player.Character and player.Character.PrimaryPart and player:DistanceFromCharacter(teleporter.Position) <= 50 then\n\t\t\tlocal playerName = player.Name\n\t\t\tlocal success, reason = teleportPlayer(player, teleporter.teleportDestination.Value)\n\t\t\tif success then\n\t\t\t\tspawn(function()\n\n\t\t\t\t\tnetwork:fireAllClients(\"signal_alertChatMessage\", {\n\t\t\t\t\t\tText = playerName .. \" departed towards \" .. utilities.getPlaceName(teleporter.teleportDestination.Value) .. \".\";\n\t\t\t\t\t\tFont = Enum.Font.SourceSansBold;\n\t\t\t\t\t\tColor = Color3.fromRGB(45, 87, 255)\n\t\t\t\t\t})\n\n\t\t\t\tend)\n\t\t\tend\n\t\t\treturn success, reason\n\t\tend\n\tend\n\treturn false\nend\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\tutilities = Modules.utilities\n\tconfiguration = Modules.configuration\n\n\tnetwork:create(\"getPlayerTeleportData\", \"BindableFunction\", \"OnInvoke\", preparePlayerToTeleport)\n\tnetwork:create(\"teleportPlayersToReserveServer\", \"BindableFunction\", \"OnInvoke\", teleportPlayersToReserveServer)\n\tnetwork:create(\"createPartyTeleportData\", \"BindableFunction\", \"OnInvoke\", createPartyTeleportData)\n\tnetwork:create(\"teleportParty\", \"BindableFunction\", \"OnInvoke\", teleportParty)\n\tnetwork:create(\"teleportPlayerToJobId\", \"BindableFunction\", \"OnInvoke\", teleportPlayerToJobId)\n\tnetwork:create(\"teleportPlayer\", \"BindableFunction\", \"OnInvoke\", teleportPlayer)\n\tnetwork:create(\"teleportPlayer_rune\", \"BindableFunction\", \"OnInvoke\", teleportPlayer_rune)\n\tnetwork:create(\"playerRequest_useTeleporter\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_useTeleporter)\n\tnetwork:create(\"signal_teleport\", \"RemoteEvent\")\nend\n\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_trade.lua",
    "content": "-- manages trades\n-- author: Polymorphic\n\nlocal module = {}\n\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal HttpService = game:GetService(\"HttpService\")\n\nlocal network\nlocal configuration\n\nlocal itemLookup = require(ReplicatedStorage.itemData)\n\n--[[\n\tplayerTradeSessionData {}\n\t\tinstance player\n\t\tstring state\n\n\t\tint gold\n\t\t{inventoryTransferData} inventoryTransferDataCollection\n\n\ttradeSessionData {}\n\t\tstring guid\n\t\tstring state\n\n\t\tplayerTradeSessionData playerTradeSessionData_player1\n\t\tplayerTradeSessionData playerTradeSessionData_player2\n--]]\n\n-- contains tradeSessionData\nlocal tradeSessionDataCollection = {}\nlocal pendingTrade_guids = {}\n\n-- if IFFIsActiveTrade is set, the trade must be ongoing to return the sessionData\nlocal function getTradeSessionDataByPlayer(player)\n\tfor i, tradeSessionData in pairs(tradeSessionDataCollection) do\n\t\tif tradeSessionData.playerTradeSessionData_player1.player == player or tradeSessionData.playerTradeSessionData_player2.player == player then\n\t\t\treturn tradeSessionData\n\t\tend\n\tend\n\n\treturn nil\nend\n\nlocal function propogateTradeDataUpdate(tradeSessionData)\n\tnetwork:fireClient(\"signal_tradeSessionChanged\", tradeSessionData.playerTradeSessionData_player1.player, tradeSessionData)\n\tnetwork:fireClient(\"signal_tradeSessionChanged\", tradeSessionData.playerTradeSessionData_player2.player, tradeSessionData)\nend\n\nlocal function invalidatePendingTradesForPlayer(player)\n\tfor i, v in pairs(pendingTrade_guids) do\n\t\tif v.playerToTradeWith == player or v.tradeRequester == player then\n\t\t\tpendingTrade_guids[i] = nil\n\t\tend\n\tend\nend\n\nlocal invitationCD = {}\nlocal function playerRequest_requestTrade(tradeRequester, playerToTradeWith)\n\tif not configuration.getConfigurationValue(\"isTradingEnabled\", tradeRequester) or not configuration.getConfigurationValue(\"isTradingEnabled\", playerToTradeWith) then\n\t\treturn false, \"Trading is disabled right now.\"\n\tend\n\n\tif tradeRequester:FindFirstChild(\"DataSaveFailed\") then\n\t\tnetwork:fireClient(\"alertPlayerNotification\", tradeRequester, {text = \"Cannot trade during DataStore outage.\"; textColor3 = Color3.fromRGB(255, 57, 60)})\n\t\treturn false, \"This feature is temporarily disabled\"\n\tend\n\n\tif tradeRequester and playerToTradeWith and tradeRequester ~= playerToTradeWith then\n\t\tif not getTradeSessionDataByPlayer(tradeRequester) and not getTradeSessionDataByPlayer(playerToTradeWith) then\n\t\t\tif not invitationCD[tradeRequester] or (tick() - invitationCD[tradeRequester] > 3) then\n\t\t\t\tinvitationCD[tradeRequester] = tick()\n\n\t\t\t\tlocal guid = HttpService:GenerateGUID(false)\n\n\t\t\t\tpendingTrade_guids[guid] = {\n\t\t\t\t\ttradeRequester \t\t= tradeRequester;\n\t\t\t\t\tplayerToTradeWith \t= playerToTradeWith;\n\t\t\t\t}\n\n\t\t\t\tnetwork:fireClient(\"signal_playerTradeRequest\", playerToTradeWith, tradeRequester, guid)\n\n\t\t\t\treturn true\n\t\t\telse\n\t\t\t\treturn false, \"stop sending trades so fast.\"\n\t\t\tend\n\t\tend\n\telseif tradeRequester == playerToTradeWith then\n\t\treturn false, \"you can't trade with yourself, idiot.\"\n\tend\n\n\treturn false, \"player is already trading\"\nend\n-- playerInventoryChanged_server\nlocal function isPlayerInventoryTransferDataValid(player, inventoryTransferDataCollection)\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\tlocal count = 0 do\n\t\tfor i, inventoryTransferData in pairs(inventoryTransferDataCollection) do\n\t\t\tlocal itemBaseData = itemLookup[inventoryTransferData.id]\n\t\t\tif itemBaseData.soulbound then\n\t\t\t\treturn false\n\t\t\tend\n\n\t\t\tif not inventoryTransferData.stacks then\n\t\t\t\tinventoryTransferData.stacks = 1\n\t\t\tend\n\n\t\t\tif inventoryTransferData.stacks <= 0 then\n\t\t\t\treturn false\n\t\t\telseif math.floor(inventoryTransferData.stacks) ~= inventoryTransferData.stacks then\n\t\t\t\treturn false\n\t\t\tend\n\n\t\t\tcount = count + 1\n\t\tend\n\tend\n\n\tif playerData then\n\t\tlocal matches = 0\n\t\tlocal trueInventoryTransferDataCollection = {}\n\n\t\tfor i, inventorySlotData in pairs(playerData.inventory) do\n\t\t\tfor ii, inventoryTransferData in pairs(inventoryTransferDataCollection) do\n\t\t\t\tif inventorySlotData.position == inventoryTransferData.position and inventorySlotData.id == inventoryTransferData.id and (inventoryTransferData.stacks or 1) <= (inventorySlotData.stacks or 1) then\n\t\t\t\t\tif inventorySlotData.soulbound then\n\t\t\t\t\t\treturn false\n\t\t\t\t\tend\n\n\t\t\t\t\ttrueInventoryTransferDataCollection[ii] = inventorySlotData\n\n\t\t\t\t\tmatches = matches + 1\n\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\treturn matches == count, trueInventoryTransferDataCollection\n\tend\nend\n\nlocal function getTradeSessionDataById(tradeSessionId)\n\tfor i, tradeSessionData in pairs(tradeSessionDataCollection) do\n\t\tif tradeSessionData.id == tradeSessionId then\n\t\t\treturn tradeSessionData\n\t\tend\n\tend\n\n\treturn nil\nend\n\nlocal function playerRequest_cancelTrade(player)\n\tlocal tradeSessionData = getTradeSessionDataByPlayer(player)\n\n\tif tradeSessionData then\n\t\ttradeSessionData.state = \"canceled\"\n\n\t\ttradeSessionData.playerTradeSessionData_player1.state = \"denied\"\n\t\ttradeSessionData.playerTradeSessionData_player2.state = \"denied\"\n\t\tpropogateTradeDataUpdate(tradeSessionData)\n\n\t\tfor i, _tradeSessionData in pairs(tradeSessionDataCollection) do\n\t\t\tif _tradeSessionData == tradeSessionData then\n\t\t\t\ttable.remove(tradeSessionDataCollection, i)\n\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal processTicketDuplicationValidationQueue = {}\nlocal function playerRequest_updatePlayerTradeSessionData(player, guid, key, value)\n\tlocal tradeSessionData = getTradeSessionDataByPlayer(player)\n\tif not tradeSessionData then return false, \"no tradeSessionData found for player\" end\n\tif tradeSessionData.guid ~= guid then return false, \"invalid guid for tradeSessionData\" end\n\n\tlocal p1 = tradeSessionData.playerTradeSessionData_player1.player\n\tlocal p2 = tradeSessionData.playerTradeSessionData_player2.player\n\n\t-- stop teleporting while trading\n\tif p1.Parent == nil or p1:FindFirstChild(\"teleporting\") or p1:FindFirstChild(\"DataLoaded\") == nil then\n\t\treturn false, \"invalid players\"\n\tend\n\n\t-- stop teleporting while trading\n\tif p2.Parent == nil or p2:FindFirstChild(\"teleporting\") or p2:FindFirstChild(\"DataLoaded\") == nil then\n\t\treturn false, \"invalid players\"\n\tend\n\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\tif playerData and tradeSessionData and tradeSessionData.state ~= \"completed\" and tradeSessionData.state ~= \"canceled\" then\n\t\tlocal playerTradeSessionData_player = (tradeSessionData.playerTradeSessionData_player1.player == player and tradeSessionData.playerTradeSessionData_player1) or (tradeSessionData.playerTradeSessionData_player2.player == player and tradeSessionData.playerTradeSessionData_player2)\n\t\tif key == \"state\" and (value == \"approved\" or value == \"denied\" or value == \"none\") then\n\n\t\t\tif value ~= playerTradeSessionData_player.state then\n\t\t\t\tplayerTradeSessionData_player.state = value\n\n\t\t\t\t-- prevent players from spamming approve!\n\t\t\t\tlocal processTicket \t\t\t\t\t\t\t= HttpService:GenerateGUID(false)\n\t\t\t\tprocessTicketDuplicationValidationQueue[guid] \t= processTicket\n\n\t\t\t\tif key == \"state\" and value == \"denied\" and tradeSessionData.state == \"countdown\" then\n\t\t\t\t\ttradeSessionData.state = \"active\"\n\t\t\t\t\tpropogateTradeDataUpdate(tradeSessionData)\n\t\t\t\t\treturn true\n\t\t\t\tend\n\n\t\t\t\tif tradeSessionData.state ~= \"countdown\" and tradeSessionData.playerTradeSessionData_player1.state == \"approved\" and tradeSessionData.playerTradeSessionData_player2.state == \"approved\" then\n\t\t\t\t\ttradeSessionData.state = \"countdown\"\n\n\t\t\t\t\tpropogateTradeDataUpdate(tradeSessionData)\n\n\t\t\t\t\tlocal startTime = tick()\n\n\t\t\t\t\twhile (tick() - startTime) < 6 and (tradeSessionData.playerTradeSessionData_player1.state == \"approved\" and tradeSessionData.playerTradeSessionData_player2.state == \"approved\") do\n\t\t\t\t\t\twait(1 / 4)\n\t\t\t\t\tend\n\n\t\t\t\t\t-- stop teleporting while trading\n\t\t\t\t\tif p1.Parent ~= game.Players or p1:FindFirstChild(\"teleporting\") or p1:FindFirstChild(\"DataLoaded\") == nil or not network:invoke(\"getPlayerData\", p1) then\n\t\t\t\t\t\treturn false, \"invalid players call 2\"\n\t\t\t\t\tend\n\n\t\t\t\t\t-- stop teleporting while trading\n\t\t\t\t\tif p2.Parent ~= game.Players or p2:FindFirstChild(\"teleporting\") or p2:FindFirstChild(\"DataLoaded\") == nil or not network:invoke(\"getPlayerData\", p2) then\n\t\t\t\t\t\treturn false, \"invalid players call 2\"\n\t\t\t\t\tend\n\n\t\t\t\t\tif not isPlayerInventoryTransferDataValid(p1, tradeSessionData.playerTradeSessionData_player1.inventoryTransferDataCollection) then\n\t\t\t\t\t\treturn false, tostring(p1) .. \" has invalid trade data\"\n\t\t\t\t\tend\n\n\t\t\t\t\tif not isPlayerInventoryTransferDataValid(p2, tradeSessionData.playerTradeSessionData_player2.inventoryTransferDataCollection) then\n\t\t\t\t\t\treturn false, tostring(p2) .. \" has invalid trade data\"\n\t\t\t\t\tend\n\n\t\t\t\t\tif processTicketDuplicationValidationQueue[guid] == processTicket and (tradeSessionData.playerTradeSessionData_player1.state == \"approved\" and tradeSessionData.playerTradeSessionData_player2.state == \"approved\") then\n\t\t\t\t\t\tlocal success, errMsg = network:invoke(\"requestTradeBetweenPlayers\", tradeSessionData.playerTradeSessionData_player1.player, tradeSessionData.playerTradeSessionData_player1.inventoryTransferDataCollection, tradeSessionData.playerTradeSessionData_player1.gold, tradeSessionData.playerTradeSessionData_player2.player, tradeSessionData.playerTradeSessionData_player2.inventoryTransferDataCollection, tradeSessionData.playerTradeSessionData_player2.gold)\n\t\t\t\t\t\tif success then\n\t\t\t\t\t\t\ttradeSessionData.state = \"completed\"\n\n\t\t\t\t\t\t\tpropogateTradeDataUpdate(tradeSessionData)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tplayerRequest_cancelTrade(player)\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\t-- empty the guid\n\t\t\t\t\t\tprocessTicketDuplicationValidationQueue[guid] = nil\n\n\t\t\t\t\t\tfor i, v in pairs(tradeSessionDataCollection) do\n\t\t\t\t\t\t\tif v.guid == guid then\n\t\t\t\t\t\t\t\ttable.remove(tradeSessionDataCollection, i)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\treturn success, errMsg\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\ttradeSessionData.state = \"active\"\n\n\t\t\t\t\tpropogateTradeDataUpdate(tradeSessionData)\n\t\t\t\tend\n\n\t\t\t\treturn true\n\t\t\tend\n\t\telseif key == \"gold\" and (type(value) == \"number\" and value >= 0) then\n\t\t\tif playerData.gold >= value then\n\t\t\t\tplayerTradeSessionData_player.gold = value\n\n\t\t\t\ttradeSessionData.playerTradeSessionData_player1.state = \"none\"\n\t\t\t\ttradeSessionData.playerTradeSessionData_player2.state = \"none\"\n\t\t\t\ttradeSessionData.state = \"active\"\n\n\t\t\t\tpropogateTradeDataUpdate(tradeSessionData)\n\t\t\t\treturn true\n\t\t\telse\n\t\t\t\treturn false, \"invalid gold\"\n\t\t\tend\n\t\telseif key == \"inventoryTransferDataCollection\" and (typeof(value) == \"table\") then\n\t\t\tlocal playerInventoryTransferDataCollectionIsValid, trueInventoryTransferDataCollection = isPlayerInventoryTransferDataValid(player, value)\n\n\t\t\tif playerInventoryTransferDataCollectionIsValid then\n\t\t\t\ttradeSessionData.playerTradeSessionData_player1.state = \"none\"\n\t\t\t\ttradeSessionData.playerTradeSessionData_player2.state = \"none\"\n\t\t\t\ttradeSessionData.state = \"active\"\n\n\t\t\t\tplayerTradeSessionData_player.inventoryTransferDataCollection = trueInventoryTransferDataCollection\n\n\t\t\t\tpropogateTradeDataUpdate(tradeSessionData)\n\n\t\t\t\treturn true\n\t\t\telse\n\t\t\t\treturn false, \"invalid trade transfer data\"\n\t\t\tend\n\t\telse\n\t\t\treturn false, \"invalid key\"\n\t\tend\n\tend\n\n\treturn false, \"invalid\"\nend\n\nlocal function playerRequest_acceptTradeRequest(player, guid)\n\n\tif player:FindFirstChild(\"DataSaveFailed\") then\n\t\tnetwork:fireClient(\"alertPlayerNotification\", player, {text = \"Cannot trade during DataStore outage.\"; textColor3 = Color3.fromRGB(255, 57, 60)})\n\t\treturn false, \"This feature is temporarily disabled\"\n\tend\n\n\tif pendingTrade_guids[guid] then\n\t\t-- cancel all current trades involving player except for this trade.\n\t\tlocal pendingTradeData = pendingTrade_guids[guid]\n\n\t\tinvalidatePendingTradesForPlayer(pendingTradeData.tradeRequester)\n\t\tinvalidatePendingTradesForPlayer(pendingTradeData.playerToTradeWith)\n\n\n\t\tif not pendingTradeData.tradeRequester.Parent or not pendingTradeData.playerToTradeWith.Parent then return false end\n\n\t--[[\n\tplayerTradeSessionData {}\n\t\tinstance player\n\t\tstring state\n\n\t\tint gold\n\t\t{inventoryTransferData} inventoryTransferDataCollection\n\n\ttradeSessionData {}\n\t\tstring guid\n\t\tstring state\n\n\t\tplayerTradeSessionData playerTradeSessionData_player1\n\t\tplayerTradeSessionData playerTradeSessionData_player2\n\t--]]\n\n\t\tlocal tradeSessionData = {}\n\t\t\ttradeSessionData.guid \t= guid\n\t\t\ttradeSessionData.state \t= \"active\"\n\n\t\t\ttradeSessionData.playerTradeSessionData_player1 = {}\n\t\t\t\ttradeSessionData.playerTradeSessionData_player1.player \t\t\t\t\t\t\t= pendingTradeData.tradeRequester\n\t\t\t\ttradeSessionData.playerTradeSessionData_player1.state \t\t\t\t\t\t\t= \"pending\"\n\t\t\t\ttradeSessionData.playerTradeSessionData_player1.gold \t\t\t\t\t\t\t= 0\n\t\t\t\ttradeSessionData.playerTradeSessionData_player1.inventoryTransferDataCollection = {}\n\n\t\t\ttradeSessionData.playerTradeSessionData_player2 = {}\n\t\t\t\ttradeSessionData.playerTradeSessionData_player2.player \t\t\t\t\t\t\t= pendingTradeData.playerToTradeWith\n\t\t\t\ttradeSessionData.playerTradeSessionData_player2.state \t\t\t\t\t\t\t= \"pending\"\n\t\t\t\ttradeSessionData.playerTradeSessionData_player2.gold \t\t\t\t\t\t\t= 0\n\t\t\t\ttradeSessionData.playerTradeSessionData_player2.inventoryTransferDataCollection = {}\n\n\t\t-- register this trade :]\n\t\ttable.insert(tradeSessionDataCollection, tradeSessionData)\n\n\t\tpropogateTradeDataUpdate(tradeSessionData)\n\n\t\treturn true\n\tend\n\n\treturn false, \"invalid or inactive guid\"\nend\n\nlocal function onPlayerInventoryChanged_server(player)\n\tlocal tradeSessionData = getTradeSessionDataByPlayer(player)\n\n\tif tradeSessionData then\n\t\ttradeSessionData.playerTradeSessionData_player1.state \t= \"none\"\n\t\ttradeSessionData.playerTradeSessionData_player2.state \t= \"none\"\n\t\ttradeSessionData.state \t\t\t\t\t\t\t\t\t= \"active\"\n\tend\nend\n\nlocal function onPlayerRemoving(player)\n\tinvitationCD[player] = nil\nend\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\tconfiguration = Modules.configuration\n\n\tgame.Players.PlayerRemoving:connect(onPlayerRemoving)\n\n\tnetwork:create(\"playerRequest_cancelTrade\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_cancelTrade)\n\tnetwork:create(\"playerRequest_requestTrade\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_requestTrade)\n\tnetwork:create(\"playerRequest_acceptTradeRequest\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_acceptTradeRequest)\n\tnetwork:create(\"playerRequest_updatePlayerTradeSessionData\", \"RemoteFunction\", \"OnServerInvoke\", playerRequest_updatePlayerTradeSessionData)\n\n\tnetwork:create(\"signal_playerTradeRequest\", \"RemoteEvent\")\n\tnetwork:create(\"signal_tradeSessionChanged\", \"RemoteEvent\")\n\n\tnetwork:connect(\"playerInventoryChanged_server\", \"Event\", onPlayerInventoryChanged_server)\nend\n\nreturn module"
  },
  {
    "path": "src/ServerScriptService/contents/manager_treasure.lua",
    "content": "-- NEW and IMPROVED Treasure chest manager\nlocal module = {}\n\nlocal INTERVAL = 30 * 60 * 24\n\n--\tlocal current = math.floor(getTime() / INTERVAL)\n\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal RunService = game:GetService(\"RunService\")\n\nlocal itemLookup = require(ReplicatedStorage.itemData)\n\nlocal network\nlocal utilities\nlocal configuration\nlocal levels\n\nlocal rand = Random.new()\nlocal currentDay = 0\nlocal dailyChestCount = 7\nlocal currentChests = {}\n\nlocal function getTime()\n\t-- 7AM/PM PT, 10AM/PM ET\n\treturn os.time() - INTERVAL / 12\nend\n\nlocal lootTable = {\n\n\t{id = \"health potion\"; stacks = 10; chance = 30; minLevel = 0; maxLevel = 5;};\n\t{id = \"mana potion\"; stacks = 10; chance = 30; minLevel = 0; maxLevel = 5;};\n\t{id = \"health potion\"; stacks = 20; chance = 5; minLevel = 0; maxLevel = 5;};\n\t{id = \"mana potion\"; stacks = 20; chance = 5; minLevel = 0; maxLevel = 5;};\n\n\t{id = \"100% armor defense scroll\"; stacks = 1; chance = 15; minLevel = 0; maxLevel = 100;};\n\t{id = \"100% weapon attack scroll\"; stacks = 1; chance = 15; minLevel = 0; maxLevel = 100;};\n\n\t{id = \"megaphone\"; stacks = 1; chance = 1; minLevel = 10; maxLevel = 100;};\n}\n\n-- map-specific potions!!!!!!\n\nlocal mapSpecificLoot = {\n\n\t-- Whispering Dunes\n\t[\"3303140173\"] = {\n\t\t{id = \"crystal beetle\"; stacks = 3; chance = 10;};\n\t\t{id = \"broken crystal beetle\"; stacks = 6; chance = 30;};\n\t\t{id = \"broken crystal beetle\"; stacks = 7; chance = 30;};\n\t\t{id = \"broken crystal beetle\"; stacks = 8; chance = 30;};\n\t\t{id = \"cactus fruit\"; stacks = 15; chance = 25;};\n\t\t{id = \"item dye yellow\"; stacks = 1; chance = 1;};\n\t};\n\n\t-- Port Fidelio\n\t[\"2546689567\"] = {\n\t\t{id = \"rune hunter\"; stacks = 3; chance = 25;};\n\t\t{id = \"health potion chalice\"; stacks = 10; chance = 80;};\n\t\t{id = \"dexterity potion\"; stacks = 1; chance = 10;};\n\t\t{id = \"arrow\"; stacks = 50; chance = 20;};\n\t\t{id = \"banana\"; stacks = 15; chance = 25;};\n\t\t{id = \"mighty sub\"; stacks = 16; chance = 3;};\n\t\t{id = \"wayfarer ticket\"; stacks = 1; chance = 2;};\n\t\t{id = \"item dye green\"; stacks = 1; chance = 1;};\n\t\t{id = \"wooden bow\"; attribute = \"pristine\"; stacks = 1; chance = 5;};\n\t\t{id = \"hunter bandit vest\"; attribute = \"pristine\"; stacks = 1; chance = 5;};\n\t\t{id = \"hunter bandit mask\"; attribute = \"pristine\"; stacks = 1; chance = 5;};\n\t};\n\n\t-- spider abyss\n\t[\"3207211233\"] = {\n\t\t{id = \"spider fang\"; stacks = 30; chance = 80;};\n\t\t{id = \"royal spider egg\"; stacks = 1; chance = 20;};\n\t\t{id = \"spider potion\"; stacks = 1; chance = 10;};\n\t\t{id = \"spider fang dagger\"; stacks = 1; chance = 3;};\n\t\t{id = \"spider bow\"; stacks = 1; chance = 3;};\n\t\t{id = \"spider staff\"; stacks = 1; chance = 3;};\n\t\t{id = \"spider sword\"; stacks = 1; chance = 3;};\n\t\t{id = \"item dye purple\"; stacks = 1; chance = 1;};\n\t};\n\n\t-- Scallop Shores\n\t[\"2471035818\"] = {\n\t\t{id = \"rune hunter\"; stacks = 3; chance = 25;};\n\t\t{id = \"dexterity potion\"; stacks = 1; chance = 10;};\n\t\t{id = \"tomahawk\"; attribute = \"pristine\"; stacks = 1; chance = 5;};\n\t\t{id = \"arrow\"; stacks = 50; chance = 20;};\n\t\t{id = \"item dye green\"; stacks = 1; chance = 1;};\n\t};\n\t-- Shiprock Bottom\n\t[\"3232913902\"] = {\n\t\t{id = \"snel snel shell\"; stacks = 1; chance = 1;};\n\t\t{id = \"snelleth shell\"; stacks = 1; chance = 1;};\n\t\t{id = \"snelly shell\"; stacks = 1; chance = 1;};\n\t\t{id = \"snelvin shell\"; stacks = 1; chance = 1;};\n\t};\n\t-- Seaside Path\n\t[\"2093766642\"] = {\n\t\t{id = \"rune hunter\"; stacks = 3; chance = 25;};\n\t\t{id = \"fish\"; stacks = 10; chance = 80;};\n\t\t{id = \"tall blue fish\"; stacks = 10; chance = 20;};\n\t\t{id = \"yellow puffer fish\"; stacks = 3; chance = 10;};\n\t};\n\n\t-- The Colosseum\n\t[\"2496503573\"] = {\n\t\t{id = \"rune colosseum\"; stacks = 3; chance = 25;};\n\t\t{id = \"health potion horn\"; stacks = 10; chance = 80;};\n\t};\n\n\t-- Warrior Stronghold\n\t[\"2470481225\"] = {\n\t\t{id = \"rune warrior\"; stacks = 3; chance = 25;};\n\t\t{id = \"health potion flagon\"; stacks = 10; chance = 80;};\n\t\t{id = \"strength potion\"; stacks = 1; chance = 10;};\n\t\t{id = \"item dye red\"; stacks = 1; chance = 1;};\n\t\t{id = \"bronze helmet\"; attribute = \"pristine\"; stacks = 1; chance = 5;};\n\t\t{id = \"bronze armor\"; attribute = \"pristine\"; stacks = 1; chance = 5;};\n\t\t{id = \"bronze mace\"; attribute = \"pristine\"; stacks = 1; chance = 5;};\n\t};\n\t-- Redwood Pass\n\t[\"2376890690\"] = {\n\t\t{id = \"rune warrior\"; stacks = 3; chance = 25;};\n\t\t{id = \"strength potion\"; stacks = 1; chance = 10;};\n\t};\n\t-- Enchanted forest\n\t[\"2260598172\"] = {\n\t\t{id = \"rune mage\"; stacks = 3; chance = 25;};\n\t\t{id = \"health potion silver\"; stacks = 10; chance = 80;};\n\t};\n\t-- Tree of life\n\t[\"3112029149\"] = {\n\t\t{id = \"rune mage\"; stacks = 3; chance = 25;};\n\t\t{id = \"health potion silver\"; stacks = 10; chance = 80;};\n\t\t{id = \"item dye blue\"; stacks = 1; chance = 1;};\n\t\t{id = \"mage hat 2\"; attribute = \"pristine\"; stacks = 1; chance = 5;};\n\t\t{id = \"mage robes 2\"; attribute = \"pristine\"; stacks = 1; chance = 5;};\n\t\t{id = \"willow staff\"; attribute = \"pristine\"; stacks = 1; chance = 5;};\n\t};\n\t-- The Clearing\n\t[\"2060556572\"] = {\n\t\t{id = \"oak axe\"; attribute = \"pristine\"; stacks = 1; chance = 10;};\n\t};\n\t-- Nilgarf\n\t[\"2119298605\"] = {\n\t\t{id = \"rune nilgarf\"; stacks = 3; chance = 25;};\n\t\t{id = \"pear\"; stacks = 15; chance = 25;};\n\t\t{id = \"item renamer\"; stacks = 1; chance = 1;};\n\t\t{id = \"item lore\"; stacks = 1; chance = 1;};\n\t\t{id = \"item dye grey\"; stacks = 1; chance = 1;};\n\t};\n\t-- Mushtropolis\n\t[\"3273679677\"] = {\n\t\t{id = \"mushroom soup\"; stacks = 1; chance = 5;};\n\t\t{id = \"rune mushtown\"; stacks = 3; chance = 25;};\n\t\t{id = \"golden mushroom\"; stacks = 5; chance = 35;};\n\t};\n\t-- Mushroom Grotto\n\t[\"2060360203\"] = {\n\t\t{id = \"mushroom beard\"; stacks = 30; chance = 20;};\n\t\t{id = \"mushroom mini\"; stacks = 30; chance = 40;};\n\t\t{id = \"mushroom soup\"; stacks = 1; chance = 1;};\n\t\t{id = \"rune mushtown\"; stacks = 3; chance = 25;};\n\t};\n\t-- Mushtown\n\t[\"2064647391\"] = {\n\t\t{id = \"rune mushtown\"; stacks = 3; chance = 25;};\n\t\t{id = \"mushroom mini\"; stacks = 30; chance = 25;};\n\t\t{id = \"apple\"; stacks = 15; chance = 25;};\n\t\t{id = \"mushroom soup\"; stacks = 1; chance = 1;};\n\t};\n\t-- Mushroom Forest\n\t[\"2035250551\"] = {\n\t\t{id = \"rune mushtown\"; stacks = 3; chance = 25;};\n\t\t{id = \"mushroom mini\"; stacks = 30; chance = 40;};\n\t\t{id = \"mushroom soup\"; stacks = 1; chance = 1;};\n\t};\n\t-- Internal build (testing)\n--\t[\"2061558182\"] = {\n--\t\t{id = \"bronze helmet\"; attribute = \"pristine\"; stacks = 1; chance = 500;};\n--\t};\n}\n\nlocal chestStorage = Instance.new(\"Folder\")\nchestStorage.Name = \"chestStorage\"\nchestStorage.Parent = game.ServerStorage\n\nlocal dailyChests = {}\nlocal n = 1\nfor _, chest in pairs(game.CollectionService:GetTagged(\"treasureChest\")) do\n\tlocal specialContents = chest:FindFirstChild(\"inventory\") or chest:FindFirstChild(\"ironChest\") or chest:FindFirstChild(\"goldChest\")\n\tif not specialContents and chest.Name == \"Chest\" then\n\t\tchest.Name = \"Chest\" .. n\n\t\tn = n + 1\n\t\ttable.insert(dailyChests, chest)\n\t\tchest.Parent = chestStorage\n\tend\nend\n\n-- Shuffle random chests\nlocal function newDay()\n\tlocal today = math.floor(getTime() / INTERVAL)\n\tlocal rand = Random.new(today)\n\tlocal chance = dailyChestCount / #dailyChests\n\t-- remove existing chests\n\tfor _, chest in pairs(currentChests) do\n\t\tchest:Destroy()\n\tend\n\tcurrentChests = {}\n\t-- add in new chests (clone)\n\tfor i, chest in pairs(dailyChests) do\n\t\tif rand:NextNumber() <= chance then\n\t\t\tlocal newChest = chest:Clone()\n\t\t\tnewChest.Parent = workspace\n\t\t\ttable.insert(currentChests, newChest)\n\t\tend\n\tend\nend\n\n-- Day loop\n\n\n\nlocal function getTreasureForChest(player, chest)\n\tlocal placeId = utilities.originPlaceId(game.PlaceId)\n\tassert(chest, \"Chest cannot be nil!\")\n\n\tlocal level = chest.chestLevel.Value\n\tlocal goldReward = 0\n\n\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\n\tlocal mapLoot = mapSpecificLoot[tostring(placeId)]\n\tlocal rewards = {}\n\n\n\tlocal function selectReward()\n\n\t\t-- ethyr reward\n\t\tlocal ethyrReward\n\n\t\tlocal globalData = playerData.globalData\n\n\t\tif globalData then\n\n\t\t\tlocal today = math.floor(getTime() / INTERVAL)\n\n\t\t\tglobalData.ethyrRewards = globalData.ethyrRewards or 0\n\t\t\tif globalData.lastEthyrReward == nil or globalData.lastEthyrReward < today then\n\t\t\t\tglobalData.ethyrRewards = 0\n\t\t\t\tglobalData.lastEthyrReward = today\n\t\t\tend\n\n\t\t\tif globalData.ethyrRewards < 5 then\n\t\t\t\tlocal chance = 0.025\n\t\t\t\tif level >= 40 then\n\t\t\t\t\tchance = 0.15\n\t\t\t\telseif level >= 25 then\n\t\t\t\t\tchance = 0.1\n\t\t\t\telseif level >= 18 then\n\t\t\t\t\tchance = 0.075\n\t\t\t\telseif level >= 7 then\n\t\t\t\t\tchance = 0.04\n\t\t\t\tend\n\t\t\t\tif chance and rand:NextNumber() <= chance then\n\t\t\t\t\tglobalData.ethyrRewards = globalData.ethyrRewards + 1\n\t\t\t\t\tethyrReward = true\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tplayerData.nonSerializeData.setPlayerData(\"globalData\", globalData)\n\n\t\tend\n\n\t\tif ethyrReward then\n\t\t\tlocal reward = {id = \"ethyr pile\"; stacks = 1;}\n\t\t\ttable.insert(rewards, {id = reward.id; stacks = reward.stacks})\n\t\telseif rand:NextInteger(1,5) == 4 then\n\t\t\t-- 1/5 chance to recieve money\n\t\t\tgoldReward = (goldReward or 0) + levels.getQuestGoldFromLevel(level or 1) * 0.5\n\t\telse\n\t\t\t-- standard loot table\n\t\t\tlocal lottery = {}\n\n\t\t\tfor i, lootInfo in pairs(lootTable) do\n\t\t\t\tif (level >= (lootInfo.minLevel or 0)) and (level <= (lootInfo.maxLevel or 999999)) then\n\t\t\t\t\tfor i=1, (lootInfo.chance or 0) do\n\t\t\t\t\t\ttable.insert(lottery, lootInfo)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif mapLoot then\n\t\t\t\tfor i, lootInfo in pairs(mapLoot) do\n\t\t\t\t\tif (level >= (lootInfo.minLevel or 0)) and (level <= (lootInfo.maxLevel or 999999)) then\n\t\t\t\t\t\tfor i=1, (lootInfo.chance or 0) do\n\t\t\t\t\t\t\ttable.insert(lottery, lootInfo)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif #lottery > 0 then\n\t\t\t\tlocal reward = lottery[rand:NextInteger(1, #lottery)]\n\t\t\t\tlocal rewardItemData = {}\n\t\t\t\tfor key, value in pairs(reward) do\n\t\t\t\t\tif key ~= \"minLevel\" and key ~= \"maxLevel\" and key ~= \"chance\" then\n\t\t\t\t\t\trewardItemData[key] = value\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\ttable.insert(rewards, rewardItemData)\n\t\t\tend\n\t\tend\n\tend\n\n\tselectReward()\n\n\t-- double trouble(rare)\n\tif rand:NextNumber() >= 0.85 then\n\t\tselectReward()\n\tend\n\n\t-- treasure hunt quest\n\tfor i, quest in pairs(playerData.quests.active) do\n\t\tif quest.id == 10 then\n\t\t\tlocal hadRelic = false\n\t\t\tfor ii, item in pairs(playerData.inventory) do\n\t\t\t\tif item.id == 138 then\n\t\t\t\t\thadRelic = true\n\t\t\t\tend\n\t\t\tend\n\t\t\tif not hadRelic then\n\t\t\t\tlocal chance = rand:NextInteger(1,3)\n\t\t\t\tif chance == 1 then\n\t\t\t\t\ttable.insert(rewards, {id = 138; stacks = 1})\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\treturn {\n\t\trewards = rewards,\n\t\tgold = goldReward,\n\t}\nend\n\nlocal function playerRequest_openTreasureChest(player, chest)\n\tif player.Character and player.Character.PrimaryPart then\n\t\tif chest and chest.PrimaryPart then\n\t\t\tlocal dist = (chest.PrimaryPart.Position - player.Character.PrimaryPart.Position).magnitude\n\t\t\tif dist >= 40 then\n\t\t\t\tlocal factor = math.floor(dist/40)\n\t\t\t\tnetwork:invoke(\"incrementPlayerArcadeScore\", player, configuration.getConfigurationValue(\"server_TPExploitScoreToFail\") * (factor / 5))\n\t\t\t\t-- (previously just a flat increment of 1/5... i decided to make it fun)\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\tend\n\tif game.CollectionService:HasTag(chest, \"treasureChest\") then\n\t\tlocal playerData = network:invoke(\"getPlayerData\", player)\n\t\tif playerData then\n\t\t\tlocal treasureData = playerData.treasure\n\t\t\tlocal chestData = treasureData[\"place-\"..game.PlaceId].chests[chest.Name]\n\t\t\tif chestData == nil or chestData.blocked then\n\t\t\t\treturn false, \"Invalid chest\"\n\t\t\tend\n\t\t\tlocal chestInfo = getTreasureForChest(player, chest) or {}\n\t\t\t-- check for inventory\n\t\t\tlocal specialContents = chest:FindFirstChild(\"inventory\") or chest:FindFirstChild(\"ironChest\") or chest:FindFirstChild(\"goldChest\")\n\n\t\t\tlocal today = math.floor(getTime() / INTERVAL)\n\n\t\t\tif specialContents then\n\t\t\t\tif chestData.open then\n\t\t\t\t\treturn false, \"Already opened\"\n\t\t\t\tend\n\t\t\t\tchestInfo = require(specialContents)\n\t\t\t\t-- Allow custom prereqs for chests\n\t\t\t\tif chestInfo.prereq then\n\t\t\t\t\tlocal success, status = chestInfo.prereq(player, {network = network; utilities = utilities})\n\t\t\t\t\tif not success then\n\t\t\t\t\t\treturn false, status\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tif specialContents.Name == \"goldChest\" then\n\t\t\t\t\tspawn(function()\n\t\t\t\t\t\tgame.BadgeService:AwardBadge(player.userId, 2124445630)\n\t\t\t\t\tend)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tif chestData.open and chestData.open >= today then\n\t\t\t\t\treturn false, \"Already opened\"\n\t\t\t\tend\n\t\t\t\t--\n\t\t\tend\n\n\t\t\tlocal rewards = chestInfo.rewards or {}\n\t\t\tlocal goldReward = chestInfo.gold or 0\n\n\n\t\t\tif chest:FindFirstChild(\"minLevel\") then\n\t\t\t\tif player.level.Value < chest.minLevel.Value then\n\t\t\t\t\treturn false, \"This chest is too sturdy to for you to break right now!\"\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tlocal chestLevel = chest:FindFirstChild(\"chestLevel\")\n\t\t\t\tif chestLevel then\n\t\t\t\t\tif player.level.Value < chestLevel.Value - 10 then\n\t\t\t\t\t\treturn false, \"This chest is too sturdy to for you to break right now!\"\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\treturn false, \"This chest is broken! Let a dev know!\"\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal rewardInfo = utilities.copyTable(rewards)\n\n\t\t\tif goldReward > 0 then\n\t\t\t\ttable.insert(rewardInfo, {id = 1; value = goldReward})\n\t\t\tend\n\n\t\t\tchestData.open = today\n\n\n\t\t\tplayerData.nonSerializeData.setPlayerData(\"treasure\", treasureData)\n\n\n\t\t\tspawn(function()\n\t\t\t\twait(0.784)\n\t\t\t\tfor _, itemInfo in pairs(rewardInfo) do\n\t\t\t\t\tlocal dropInformation = {\n\t\t\t\t\t\tlootDropData = itemInfo,\n\t\t\t\t\t\tdropPosition = (chest.PrimaryPart.CFrame * CFrame.new(0,chest.PrimaryPart.Size.Y*2, 0)).Position,\n\t\t\t\t\t\titemOwners = {player},\n\t\t\t\t\t}\n\n\t\t\t\t\tlocal item = network:invoke(\"spawnItemOnGround\",dropInformation.lootDropData, dropInformation.dropPosition, dropInformation.itemOwners)\n\t\t\t\t\tif item == nil then break end\n\n\t\t\t\t\tlocal attachmentTarget\n\t\t\t\t\tlocal rand = Random.new()\n\n\t\t\t\t\tlocal cf = chest.PrimaryPart.CFrame * CFrame.Angles(0, -math.pi/2, 0)\n\t\t\t\t\tlocal velo = Vector3.new((rand:NextNumber() - 0.5) * 10, (2 + rand:NextNumber()) * 25, (rand:NextNumber() - 0.5) * 10) + cf.lookVector * 10\n\n\t\t\t\t\tif item:IsA(\"BasePart\") then\n\t\t\t\t\t\titem.Velocity = velo\n\t\t\t\t\telseif item:IsA(\"Model\") and (item.PrimaryPart or item:FindFirstChild(\"HumanoidRootPart\")) then\n\t\t\t\t\t\tlocal primaryPart = item.PrimaryPart or item:FindFirstChild(\"HumanoidRootPart\")\n\t\t\t\t\t\tif primaryPart then\n\t\t\t\t\t\t\tprimaryPart.Velocity = velo\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\twait(2/5)\n\t\t\t\tend\n\t\t\tend)\n\t\t\treturn true\n\n\t\tend\n\t\treturn false, \"Not a chest\"\n\tend\nend\n\nlocal function dayLoop()\n\twhile wait(1) do\n\t\tlocal today = math.floor(getTime() / INTERVAL)\n\t\tif today > currentDay then\n\t\t\tnewDay()\n\t\t\tcurrentDay = today\n\t\tend\n\tend\nend\n\nlocal function conversion()\n\tfor placeId, contents in pairs(mapSpecificLoot) do\n\t\tif tonumber(placeId) == utilities.originPlaceId(game.PlaceId) then\n\t\t\tfor _, lootEntry in pairs(contents) do\n\t\t\t\ttable.insert(lootTable, lootEntry)\n\t\t\tend\n\t\tend\n\tend\n\n\tfor _, lootDrop in pairs(lootTable) do\n\t\tif type(lootDrop.id) == \"string\" then\n\t\t\tlootDrop.id = itemLookup[lootDrop.id].id\n\t\tend\n\tend\n\n\tfor placeId, placeLootTable in pairs(mapSpecificLoot) do\n\t\tfor _, lootDrop in pairs(placeLootTable) do\n\t\t\tif type(lootDrop.id) == \"string\" then\n\t\t\t\tlocal lookupLootDrop = itemLookup[lootDrop.id]\n\t\t\t\tif (lookupLootDrop) then\n\t\t\t\t\tlootDrop.id = itemLookup[lootDrop.id].id\n\t\t\t\telse\n\t\t\t\t\tif RunService:IsStudio() and game.PlaceId == placeId then\n\t\t\t\t\t\twarn(\"Treasure manager: Item \\\"\" .. lootDrop.id .. \" does not exist in itemLookup!\")\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\tutilities = Modules.utilities\n\tconfiguration = Modules.configuration\n\tlevels = Modules.levels\n\n\tspawn(dayLoop)\n\tconversion()\n\n\tnetwork:create(\"playerRequest_openTreasureChest\",\"RemoteFunction\",\"OnServerInvoke\", playerRequest_openTreasureChest)\nend\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/contents/secretCodes/init.lua",
    "content": "local httpService \t\t= game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules \t\t= require(replicatedStorage.modules)\n\t\tlocal network \t\t= modules.load(\"network\")\n\nlocal Module = require(script.verify)\nscript.verify:Destroy()\t\t\n\nlocal function redeemcode(player, code)\n\tif string.sub(code,1,1) == \"$\" and string.len(code) > 6 and string.len(code) < 10 then\n\t\tif game.MarketplaceService:PlayerOwnsAsset(player, 2376885433) then\n\t\t\tlocal success = Module.Verify(player, code)\n\t\t\tif success then\n\t\t\t\t--[[\n\t\t\t\tspawn(function()\n\t\t\t\t\tgame.BadgeService:AwardBadge(player.userId,1742681250)\n\t\t\t\tend)\t\t\t\n\t\t\t\t]]\n\t\t\t\treturn success\t\t\t\n\t\t\tend\n\t\tend\n\tend\t\nend\n\nnetwork:create(\"playerRequest_redeemcode\", \"RemoteFunction\", \"OnServerInvoke\", redeemcode)\n\nreturn {}"
  },
  {
    "path": "src/ServerScriptService/contents/secretCodes/verify/init.lua",
    "content": "local module = {}\n\nlocal httpService = game:GetService(\"HttpService\")\n\nlocal key = require(script.key)\n\nfunction module.Verify(player, code)\n\tif player:FindFirstChild(\"VerifyCooldown\") == nil then\n\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\ttag.Name = \"VerifyCooldown\"\n\t\ttag.Parent = player\n\t\tspawn(function()\n\t\t\twait(10)\n\t\t\tif tag then\n\t\t\t\ttag:Destroy()\n\t\t\tend\n\t\tend)\n\n\t\tlocal playerRank = player:GetRankInGroup(1137635)\n\t\n\t\tif type(code)==\"string\" and code ~= \"\" then\n\t\n\t\n\t\t\tlocal rolesTable = {}\n\t\t\trolesTable[\"Verified\"] = true\n\t\t\t\n\t\t\trolesTable[\"Vesterian\"] = true\n\n\t\t\t\n\t\t\n\t\t\tlocal resp = httpService:GetAsync(\"http://104.236.92.94:8005/process?userId=\" .. player.userId .. \"&code=\" .. code .. \"&doPromote=\" .. tostring(playerRank == 1) .. \"&roles=\" .. httpService:JSONEncode(rolesTable)..\"&key=\"..key)\n\t\t\tif string.lower(resp) == \"true\" or string.lower(resp) == \"success\" then\n\t\t\t\t\n\t\t\t\treturn true\n\t\t\telse\n\t\t\t\tgame.ReplicatedStorage.Error:FireClient(player, \"Server denied code: Response: \"..resp)\n\t\t\t\treturn false\n\t\t\tend\n\t\t\t\n\t\tend\n\telse\n\t\treturn false \n\tend\nend\n\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/contents/secretCodes/verify/key.lua",
    "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\treturn \"Dfdkj26flkdsJzSDFkkDSF29lDjxkp9sos\""
  },
  {
    "path": "src/ServerScriptService/contents/teleport.lua",
    "content": "local module = {}\n\n\n\nreturn module\n"
  },
  {
    "path": "src/ServerScriptService/modules/cannonScript.lua",
    "content": "return function()\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal network = modules.load(\"network\")\n\tlocal utilities = modules.load(\"utilities\")\n\tlocal tween = modules.load(\"tween\")\n\t\nlocal debounceTable = {}\t\n\nlocal animationController = script.Parent.Parent.AnimationController\n\n\tlocal track = animationController:LoadAnimation(script.Parent.Parent.Animation)\n\ttrack.Looped = false\n\t\n\tlocal idle = animationController:LoadAnimation(script.Parent.Parent.idle)\n\tidle:Play()\n\nlocal lastPlayer \n\nnetwork:create(\"signal_playerReadyToBeBOOMEDByTheCannon\", \"RemoteEvent\")\n\t\nfunction BOOM(player, cannon)\n\t\n\tif cannon ~= script.Parent.Parent then\n\t\treturn false\n\tend\n\t\n\t-- play cannon sounds\n\tlastPlayer = player\n\t\n\t-- animation\n\t\n\t\n\ttrack.Looped = false\n\ttrack:Play()\n\t\n\t-- hack\n\tlocal connection\n\tconnection = track.KeyframeReached:connect(function(keyframe)\n\t\tif keyframe == \"finish\" then\n\t\t\ttrack:Stop()\n\t\t\tconnection:disconnect()\n\t\t\tconnection = nil\t\t\t\n\t\tend\n\tend)\n\t--[[\n\tconnection = track.DidLoop:connect(function()\n\t\ttrack:Stop()\n\t\tconnection:disconnect()\n\t\tconnection = nil\n\tend)\n\t]]\n\t\n\tscript.Parent.Parent.target.CannonFire:Play()\n\t\n\tnetwork:fireClient(\"signal_playerReadyToBeBOOMEDByTheCannon\", player, script.Parent.Parent)\n\t\n\tscript.Parent.Parent.target.smoke.Enabled = true\n\tscript.Parent.Parent.target.light.Enabled = true\n\tscript.Parent.Parent.target.explode:Emit(100) \n\tscript.Parent.Parent.target.light.Range = 15 \n\tfor i=5,1,-1 do \n\t\twait(0.1) \n\t\tscript.Parent.Parent.target.explode:Emit(i) \n\t\tif lastPlayer == player then\n\t\t\tscript.Parent.Parent.target.light.Range = i * 3\n\t\tend\n\tend \n\twait(0.5)\n\tif lastPlayer == player then\n\t\tscript.Parent.Parent.target.smoke.Enabled = false\n\t\tscript.Parent.Parent.target.light.Enabled = false\n\tend\n\ttrack.Looped = false\n\tspawn(function()\n\t\twait(5)\n\t\tif connection then\n\t\t\tconnection:disconnect()\n\t\tend\n\tend)\nend\n\n\nnetwork:create(\"signal_playerHasDecidedThatTheyWantToUseTheCannon\", \"RemoteEvent\", \"OnServerEvent\", BOOM)\n\n\n--network:create(\"playerRequest_cannonGoBOOM\", \"RemoteFunction\", \"OnServerInvoke\", BOOM)\nend"
  },
  {
    "path": "src/ServerScriptService/modules/enterGame.lua",
    "content": "-- main menu control script\n\nreturn function()\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules \t\t\t= require(replicatedStorage:WaitForChild(\"modules\"))\n\t\tlocal network \t\t\t= modules.load(\"network\")\n\t\tlocal configuration \t= modules.load(\"configuration\")\n\n\t\t\n\t\t\nlocal banList = {\"chrisinator66\";\"Guest40231\";\"pup115888\";\"inearthly\";\"ReferalKing\";\"Raqnaar\";\"ivorfy\";\n\"Axdreid\";\"Avalian\";\"Silent_Karasu\";\"QuakeBeard\";\"Harder_Dude\";\"howdoisnarfsnarf\";\n\"GusFront\";\"longrod247\";\"JokeChef\";\"A4lt\";\"FEIMZIGRON\";\"cmack4243\";\n\"buttmanthecool\";\"in2dream\";\"Linqed\";\"Ascusis\";\"RandomUsername64x\";\"decaul\";\"Fluxxxx_222\";\n\"SpiritedNoFace\";\"Yoclash\";\"WrongStarZ\";\"JosephCorozzo\";\"YouKindaCutee\";\"Triseria\";\"Chronicxz\";\n\"winter_soldat\";\"ctjepkes\";\"Gmasterjkh\";\"aleccc\";\"caine_felix\";\"EchikaKurosaki\";\"goithefat1\";\"Kyene\";\"AddictBloxy\";\"Thyemium\";\"RandoOof\";\"Xena_SR\";\"ASecretNote\";\n\"MGsha3883\";\"FlamingExpert\";\"Alex_itk\";\"Imperfectjosh\";\"firestoney\";\"certifiedkidfiddler\";\n\"thebowmaster2345\";\"willow\";\"Dvcember\";\"Cozmicc\";\"insaisissable\";\"GodzLightning\";\"WhiiteBeard\";\"swamp\";\"TehChickenBoy\";\"Gamer1OderSoHD\";\"boobuu444\";\"zxZTHeDeMonZxz\";\"kerso\";\"Wen3698\";\"iRunBoYi\";\n\"TrollLexBR2\";\"VesteriaFreak\";\"Storage_Magical6\";\"link2uu\";\n\"TheTruSucc\";\"Dony164\";\"sourtings\";\"DevilsSoul\";\"TheDark_Mage\";\n\"LittlePeep\";\"K4Kiva\";\"Alekx1235x\";\"Kain87\";\"RONKLEY\";\"I7906\";\"chino146\";\"lightdarksun\";\n\"LokiCard\";\"Thistlerow\";\"4legitly\";\"MikeGarciaAGM\";\"teuwaiseuu\";\"x0iAlan\";\"Achillez\";\"Christmas_Awaits\";\"YoRHa_9S\";\"shadowrider101\";\"Zaharielekk\";\"Maserq\";\n\"Impossibilities\";\"25nite\";\"SaxoMaxyy\";\"rayverberm24\";\"Athynasius\";\"Avalinity\";\"Chro11oLucifer\";\"Ansonabc\";\"bf_g50\";\"SpeedWolfBG\";\n\"cghcgh25801\";\"superbaconplayer1020\";\"Gidein\";\"m4nil410\";\n\"SquishyRockets\";\"kxlebs\";\"VesteriaReferrals\";\"YezzSirrr\";\"Ezmaster01\";\"Vesteriakills\";\"Zinszo\";\"0dd_1\";\"Xorias\";\"ViaDarkNessTH\";\"AeonixTheFiend\";\n\"nex_s2\";\"Kain2212\";\"Jank_z\";\"ScroogeMcDuck3679\";\"HieiSan\";\"aleister90\";\"BoneChills\";\n\"Ros_ilia\";\"darkghost110\";\"DisastrousTemptation\";\"overpath41\";\"LesRocket\";\"wraithies\";\"JameO1818\";\"Decryptional\";\"F_oop\";\"AltVaziec\";\"alphabeto2444\";\"decryptedMIRAGE\";\"ThatCriminal\";\"TurkiSD\";\"VesteriaStoranger\";\n\"CaterBois\";\"GeeTeeArgh\";\"decipheredENIGMA\";\"theRGBKing\";\n\"IIIIIlllllllIIllIIIl\";\"MemezzMachine\";\"BIGLIVERTHEMAX1212\"; \"xxxwomenbeater62\"; \"vsk_0\"; \"vsk_god\"; \"dad_killer69\"; \"EpicMetatableMoment\";\n\"children_sniffer72\"; \"vsk_1\"; \"Legoracer\", \"X6M\"; \"Draco7898\"}\t\t\n\t\t\nlocal banUserIdList = {}\n\nspawn(function()\n\tfor i, playerName in pairs(banList) do\n\t\tpcall(function()\n\t\t\tlocal userId = game.Players:GetUserIdFromNameAsync(playerName)\n\t\t\tif userId then\n\t\t\t\ttable.insert(banUserIdList, userId)\n\t\t\tend\n\t\tend)\n\tend\nend)\t\t\n\t\t\nlocal function checkBanList(player)\n\tfor i, playerName in pairs(banList) do\n\t\tif player.Name == playerName then\n\t\t\tplayer:Kick(\"Banned\")\n\t\t\treturn false\n\t\tend\n\tend\n\tfor i, userId in pairs(banUserIdList) do\n\t\tif player.userId == userId then\n\t\t\tplayer:Kick(\"Banned\")\n\t\t\treturn false\n\t\tend\n\tend\n\treturn true\nend\n\ngame.Players.PlayerAdded:Connect(checkBanList)\n\t\nlocal playerGlobalDataContainer = {}\n\nlocal paymentDataCache = {}\nlocal playerAnalyticsId = {}\n\nlocal transactionPendingHold = {}\n\nlocal ProductCache = {d = \"1\"}\n\nnetwork:create(\"globalDataUpdated\", \"RemoteEvent\")\n\ngame:GetService(\"MarketplaceService\").ProcessReceipt = function(receiptInfo)\n\t\n\tlocal player = game.Players:GetPlayerByUserId(receiptInfo.PlayerId)\n\tif not player then\n\t\t-- no player, abort.\n\t\twarn(\"aborted purchase due to missing player\")\n\t\treturn Enum.ProductPurchaseDecision.NotProcessedYet\n\tend\n\t\n\tif player:FindFirstChild(\"DataSaveFailed\") then\n\t\t-- player data has failed to save, abort.\n\t\twarn(\"aborted purchase due to save failure\")\n\t\treturn Enum.ProductPurchaseDecision.NotProcessedYet\n\tend\n\t\n\n\tlocal processingStartTime = tick()\n\t\n\t-- ensure the player data is loaded in\n\trepeat wait(0.1) until player == nil or player.Parent ~= game.Players or playerGlobalDataContainer[player] or tick() - processingStartTime > 15\n\t\n\tif player and player.Parent == game.Players and playerGlobalDataContainer[player] then\n\t\n\t\tlocal playerGlobalData = playerGlobalDataContainer[player]\n\t\n\t\tpaymentDataCache[player] = paymentDataCache[player] or {}\n\t\tlocal playerPaymentDataCache = paymentDataCache[player]\n\t\tplayerPaymentDataCache.payments = playerPaymentDataCache.payments or {}\t\n\t\t\n\t\tlocal doFinishTransaction\n\t\tlocal modifiedGlobalPlayerData\n\t\t\n\t\t-- Do not modify data if payment has already been cached this session\n\t\tif playerPaymentDataCache.payments[receiptInfo.PurchaseId] then\n\t\t\tdoFinishTransaction = true\n\t\telse\n\t\t\tdoFinishTransaction, modifiedGlobalPlayerData = network:invoke(\"processPayment\", player, receiptInfo, playerGlobalData)\n\t\tend\n\t\t\n\t\t-- Cache previously-accepted payments in case data doesn't save\n\t\tif doFinishTransaction and modifiedGlobalPlayerData then\n\t\t\tplayerPaymentDataCache.payments[receiptInfo.PurchaseId] = true\n\t\t\tplayerGlobalData = modifiedGlobalPlayerData\n\t\t\tplayerGlobalDataContainer[player] = modifiedGlobalPlayerData\n\t\tend\n\n\t\tif doFinishTransaction then\n\t\t\t\n\t\t\tlocal transactionHoldTime = os.time()\n\t\t\ttransactionPendingHold[player] = transactionHoldTime\n\t\t\t\n\t\t\tplayerGlobalData.version = playerGlobalData.version + 1\n\t\t\t\n\t\t\tlocal success, status, version = network:invoke(\"setPlayerGlobalData\", player, playerGlobalData)\n\t\t\t\n\t\t\tif transactionPendingHold[player] == transactionHoldTime then\n\t\t\t\ttransactionPendingHold[player] = nil\n\t\t\tend\n\t\t\t\n\t\t\tif success then\n\t\t\t\tplayerPaymentDataCache.globalDataVersion = version\n\t\t\t\tspawn(function()\t\t\n\t\t\t\t\tlocal ProductName = ProductCache[receiptInfo.ProductId]\n\t\t\t\t\tif ProductName == nil then\n\t\t\t\t\t\tlocal Product = game.MarketplaceService:GetProductInfo(receiptInfo.ProductId,Enum.InfoType.Product)\n\t\t\t\t\t\tif Product then\n\t\t\t\t\t\t\tProductName = Product.Name\n\t\t\t\t\t\t\tProductCache[receiptInfo.ProductId] = ProductName\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tProductName = \"unknown\"\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\tprint(\"transaction recorded:\", ProductName)\n\t\t\t\t\tnetwork:invoke(\"purchaseMade\", player, \"Product\", ProductName, receiptInfo.CurrencySpent)\n\t\t\t\tend)\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\tnetwork:fireClient(\"globalDataUpdated\", player, playerGlobalData)\n\t\t\t\tprint(\"Transaction success\")\n\t\t\t\treturn Enum.ProductPurchaseDecision.PurchaseGranted\n\t\t\t\t\n\t\t\telse\n\t\t\t\twarn(\"Transaction failed\")\n\t\t\t\treturn Enum.ProductPurchaseDecision.NotProcessedYet\n\t\t\tend\t\t\t\n\t\tend\n\t\n\t\n\tend\n\twarn(\"no player global data found\")\n\t\n\treturn Enum.ProductPurchaseDecision.NotProcessedYet\nend\n\n\nnetwork:create(\"loadGame\", \"RemoteFunction\", \"OnServerInvoke\", function(player)\n\t\n\tif not checkBanList(player) then\n\t\treturn false\n\tend\n\t\n\tprint(\"starting new session\")\n\tplayerAnalyticsId[player] = network:invoke(\"newSession\", player)\t\n\tprint(playerAnalyticsId[player])\t\n\t\n\t\n\tlocal success, data, status = network:invoke(\"getPlayerGlobalData\", player)\n\tif success then\n\t\t\n\t\tif data == nil then\n\t\t\tlocal newtag = Instance.new(\"BoolValue\")\n\t\t\tnewtag.Name = \"newPlayerTag\"\n\t\t\tnewtag.Parent = player\t\t\n\t\tend\n\t\t\n\t\t\n\t\tplayerGlobalDataContainer[player] = data or {}\n\t\t\n\n\t\t\n\t\treturn success, data, status\n\tend\n\n\n\treturn false, nil, status\nend)\t\n\nlocal referralStore = game:GetService(\"DataStoreService\"):GetDataStore(\"Referrals2\")\n\nlocal referralConnection\n\nlocal isMessagingEnabled, messagingError\nspawn(function()\n\trepeat \n\t\tisMessagingEnabled, messagingError = pcall(function()\n\t\t\t\n\t\t\tif referralConnection then\n\t\t\t\treferralConnection:Disconnect()\n\t\t\t\treferralConnection = nil\n\t\t\tend\n\t\t\t\n\t\t\treferralConnection = game:GetService(\"MessagingService\"):SubscribeAsync(\"acceptedReferrals\", function(message)\n\t\t\n\t\t\t\tlocal userId = message.Data\n\t\t\n\t\t\t\tlocal hasReferred \n\t\t\t\t\t\n\t\t\t\tlocal success, fail = pcall(function()\n\t\t\t\t\t\n\t\t\t\t\treferralStore:UpdateAsync(tostring(userId), function(previouslyReferred)\n\t\t\t\t\t\thasReferred = previouslyReferred\n\t\t\t\t\t\treturn true\n\t\t\t\t\tend)\n\t\t\t\t\n\t\t\t\tend)\t\t\n\t\t\t\t\n\t\t\t\tif not success then\n\t\t\t\t\twarn(\"oh no!\", fail)\n\t\t\t\tend\n\n\t\t\t\t\n\t\t\t\tlocal player = game.Players:GetPlayerByUserId(userId)\n\t\t\t\tif player then\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\n\n\t\t\t\t\t\n\t\t\t\t\tif not hasReferred then\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\tlocal acceptedTag = Instance.new(\"BoolValue\")\n\t\t\t\t\t\tacceptedTag.Name = \"acceptedReferral\"\n\t\t\t\t\t\tacceptedTag.Parent = player\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend)\n\t\tend)\n\t\twait(10)\n\tuntil isMessagingEnabled\nend)\n\n\nnetwork:create(\"sendReferral\", \"RemoteFunction\", \"OnServerInvoke\", function(player, username)\n\n\t\n\tif not checkBanList(player) then\n\t\treturn false\n\tend\n\t\n\tif not (isMessagingEnabled and referralConnection ~= nil)  then\n\t\treturn false, \"Roblox's MessagingService is offline. Please wait or rejoin to attempt a referral\"\n\tend\n\t\n\tif player:FindFirstChild(\"acceptedReferral\") then\n\t\treturn false, \"Already referred\"\n\tend\n\t\n\tif not player:FindFirstChild(\"newPlayerTag\") then\n\t\treturn false, \"Invalid - not a new player\"\n\tend\n\tif player:FindFirstChild(\"messagePending\") then\n\t\treturn false, \"Invalid - message is already sending\"\n\tend\t\n\tlocal hasReferred\n\tlocal success, err = pcall(function()\n\t\thasReferred = referralStore:GetAsync(tostring(player.userId))\n\tend)\n\tif not success then\n\t\treturn false, \"DataStore error - \"..(err or \"unknown\")\n\tend\n\tif hasReferred then\n\t\treturn false, \"You've already referred\"\n\tend\n\tif username == player.Name then\n\t\treturn false, \"You're not funny\"\n\tend\n\n\tlocal userId\n\n\tlocal idSuccess = pcall(function()\n\t\tuserId = game.Players:GetUserIdFromNameAsync(username)\n\tend)\n\tif not idSuccess then\n\t\treturn false, \"Failed to find player userId, try again.\"\n\tend\n\n\tlocal tag = Instance.new(\"StringValue\")\n\ttag.Name = \"messagePending\"\n\ttag.Value = username\n\ttag.Parent = player\n\n\tgame.Debris:AddItem(tag, 10)\n\n\tlocal messageSuccess, errMsg = pcall(function()\n\t\tgame:GetService(\"MessagingService\"):PublishAsync(\"user-\"..tostring(userId), {messageType = \"referral\"; referredUserId = player.userId; referredUsername = player.Name})\n\tend)\n\t\n\tif messageSuccess then\n\n\t\treturn true, \"Awaiting a response, please do not leave this page...\"\n\telse\n\t\ttag:Destroy()\n\t\treturn false, \"Message error - \"..(errMsg or \"unknown\")\n\tend\t\nend)\n\n\n\n\n\t\ngame.Players.PlayerRemoving:Connect(function(player)\n\tplayerGlobalDataContainer[player] = nil\n\tpaymentDataCache[player] = nil\n\tplayerAnalyticsId[player] = nil\nend)\t\t\n\n--[[\n  {\n  \t\"lastSave\": {\n  \t\t\"-slot1\": 1549216535,\n  \t\t\"-slot7\": 11,\n  \t\t\"-slot5\": 13,\n  \t\t\"-slot3\": 1549242980,\n  \t\t\"-slot9\": 15,\n  \t\t\"-slot8\": 4,\n  \t\t\"-slot6\": 3,\n  \t\t\"-slot2\": 1549139589,\n  \t\t\"-slot10\": 2,\n  \t\t\"-slot4\": 74\n  \t},\n  \t\"ethyr\": 12100,\n  \t\"itemStorage\": [],\n  \t\"saveSlotData\": {\n  \t\t\"-slot4\": {\n  \t\t\t\"customized\": true,\n  \t\t\t\"level\": 8,\n  \t\t\t\"lastLocation\": 2093766642,\n  \t\t\t\"class\": \"Adventurer\",\n  \t\t\t\"accessories\": {\n  \t\t\t\t\"hair\": 12,\n  \t\t\t\t\"face\": 2,\n  \t\t\t\t\"shirtColorId\": 4,\n  \t\t\t\t\"undershirt\": 2,\n  \t\t\t\t\"skinColorId\": 9,\n  \t\t\t\t\"underwear\": 4,\n  \t\t\t\t\"hairColorId\": 1\n  \t\t\t},\n  \t\t\t\"equipment\": [{\n  \t\t\t\t\"upgrades\": 2,\n  \t\t\t\t\"successfulUpgrades\": 2,\n  \t\t\t\t\"position\": 1,\n  \t\t\t\t\"id\": 3,\n  \t\t\t\t\"modifierData\": [{\n  \t\t\t\t\t\"baseDamage\": 5,\n  \t\t\t\t\t\"dex\": 2\n  \t\t\t\t}, {\n  \t\t\t\t\t\"baseDamage\": 1\n  \t\t\t\t}],\n  \t\t\t\t\"stacks\": 1\n  \t\t\t}]\n  \t\t}\n  \t},\n  \t\"version\": 1091\n  }\t\n--]]\n\nlocal teleportService = game:GetService(\"TeleportService\")\n\n\nlocal function getReserveServerKeyForMirrorDestination(destination)\n\tlocal reserveServerKey\n\tlocal success, err = pcall(function()\n\t\tlocal mirrorWorldStore = game:GetService(\"DataStoreService\"):GetDataStore(\"mirrorWorld\"..configuration.getConfigurationValue(\"mirrorWorldVersion\"))\n\t\treserveServerKey = mirrorWorldStore:GetAsync(tostring(destination))\n\t\tif reserveServerKey == nil then\n\t\t\treserveServerKey = teleportService:ReserveServer(destination) \n\t\t\tmirrorWorldStore:SetAsync(tostring(destination), reserveServerKey)\n\t\tend\n\tend)\n\treturn reserveServerKey, success, err\nend\n\t\t\n\t\t\nnetwork:create(\"enterGame\", \"RemoteFunction\", \"OnServerInvoke\", function(player, _, timestamp, slot, customize, realm)\n\t\n\tif not checkBanList(player) then\n\t\treturn false\n\tend\n\t\n\tlocal rank = player:GetRankInGroup(4238824)\n\tlocal isAdmin = true -- rank >=20\n\tlocal isLegend = rank >= 2\n\tlocal allowedSlots = (isAdmin and 20) or (isLegend and 10) or 4\n\t\n\t\t-- todo remove this lmao\n\t\tif not isAdmin then\n\t\t\tplayer:Kick(\"Not authorized.\")\n\t\t\treturn false\n\t\tend\t\n\n\tif slot > allowedSlots then\n\t\tplayer:Kick(\"Not authorized.\")\n\t\treturn false\n\tend\n\t\n\tif realm == \"mirror\" then\n\t\t-- teleport to the mirror world (admin only)\n\t\tif not isAdmin then\n\t\t\tplayer:Kick(\"Not authorized.\")\n\t\t\treturn false\n\t\tend\t\t\n\t\t\n\t\tlocal destination = 3372071669\n\t\tprint(\"Going to the mirror world\")\n\t\t\n\t\tlocal teleportData = {\n\t\t\tdestination \t\t\t\t= destination;\n\t\t\tdataTimestamp \t\t\t\t= timestamp;\n\t\t\tdataSlot\t\t\t\t\t= slot;\n\t\t\tplayerAccessories \t\t\t= customize;\n\t\t\tarrivingFrom\t\t\t\t= game.PlaceId;\t\n\t\t\tanalyticsSessionId\t\t\t= player:FindFirstChild(\"AnalyticsSessionId\") and player.AnalyticsSessionId.Value;\n\t\t\tjoinTime\t\t\t\t\t= player:FindFirstChild(\"JoinTime\") and player.JoinTime.Value;\n\t\t}\t\t\n\t\t\n\t\tlocal reserveServerKey = getReserveServerKeyForMirrorDestination(destination)\n\t\tif reserveServerKey then\n\t\t\tteleportService:TeleportToPrivateServer(destination, reserveServerKey, {player}, nil, teleportData)\n\t\telse\n\t\t\tprint(\"Mirror TP failed\")\n\t\t\treturn false, \"Failed to find key for mirror world teleport\"\n\t\tend\n\t\t\t\t\t\t\t\n\telse\n\t\t-- teleport to the normal world (everyone else)\n\t\tlocal realDestination\n\t\t\n\t\tlocal playerGlobalData =  playerGlobalDataContainer[player]\n\t\tif playerGlobalData and playerGlobalData.saveSlotData then\n\t\t\tlocal slotData = playerGlobalData.saveSlotData[\"-slot\"..tostring(slot)]\n\t\t\tif slotData and slotData.lastLocation then\n\t\t\t\trealDestination = slotData.lastLocation\n\t\t\telse\n\t\t\t\tif game.GameId == 712031239 then\n\t\t\t\t\trealDestination = 4041449372\n\t\t\t\telse\n\t\t\t\t\trealDestination = 2064647391\n\t\t\t\tend\n\t\t\t\t\n\t\t\tend\n\t\tend\n\t\t\n\t\tif transactionPendingHold[player] then\n\t\t\tlocal startTime = os.time()\n\t\t\trepeat wait() until transactionPendingHold[player] == nil or player == nil or player.Parent ~= game.Players\n\t\t\t\n\t\t\tif player == nil or player.Parent ~= game.Players then\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\n\t\t\n\t\tlocal destination = realDestination or (game.GameId == 712031239 and 4041449372) or 2064647391\n\t\t\n\t\tlocal playerPaymentDataCache = paymentDataCache[player]\n\t\tif playerPaymentDataCache and playerPaymentDataCache.globalDataVersion then\n\t\t\ttimestamp = playerPaymentDataCache.globalDataVersion\n\t\tend\n\t\t\n\t\tlocal wasReferred = player:FindFirstChild(\"acceptedReferral\") and true\n\t\tlocal spawnLocation \n\t\tif not realDestination then \n\t\t\tspawnLocation = \"newb\" \n\t\tend\n\t\t\n\t\tlocal teleportData = {\n\t\t\tdestination = destination;\n\t\t\tdataTimestamp = timestamp;\n\t\t\tdataSlot = slot;\n\t\t\tplayerAccessories = customize;\n\t\t\tarrivingFrom = game.PlaceId;\t\n\t\t\tanalyticsSessionId = player:FindFirstChild(\"AnalyticsSessionId\") and player.AnalyticsSessionId.Value;\n\t\t\tjoinTime = player:FindFirstChild(\"JoinTime\") and player.JoinTime.Value;\n\t\t\twasReferred\t= wasReferred;\n\t\t\tspawnLocation = spawnLocation\n\t\t}\n\t\tnetwork:fireClient(\"signal_teleport\", player, destination)\n\t\n\t\tspawn(function()\n\t\t\twait(0.5)\n\t\t\tgame:GetService(\"TeleportService\"):Teleport(destination, player, teleportData--[[, replicatedStorage:FindFirstChild(\"teleportUI\")]])\n\t\tend)\t\t\n\t\t\n\tend\n\t\n\nend)\t\t\t\nlocal TeleportService = game:GetService(\"TeleportService\")\n\nlocal function isUserInSameGame(userId)\n\tlocal success, errorMessage, placeId, jobId\n\n\tfor i=1,2 do\n\t\tlocal psuccess, perror = pcall(function()\n\t\t\tsuccess, errorMessage, placeId, jobId = TeleportService:GetPlayerPlaceInstanceAsync(userId)\n\t\tend)\n\t\tif psuccess or success or jobId then\n\t\t\tbreak\n\t\tend\n\tend\n\n\n\treturn success, errorMessage, placeId, jobId\nend\n\nlocal function fetchPlayerFriendsInfo(Player, friendInfo)\n\tprint(\"!!start fetching\")\n\n\tlocal onlineFriends = {}\n\n\tfor i,friend in pairs(friendInfo) do\n\t\tif friend.IsOnline then\n\t\t\tif friend.PlaceId and friend.PlaceId > 0 then\n\t\t\t\tlocal success, err, placeId, jobId = isUserInSameGame(friend.VisitorId)\n\t\t\t\tlocal location = friend.LastLocation\n\t\t\t\t--[[\n\t\t\t\tlocation = string.gsub(location, \"playing \", \"In \")\n\t\t\t\tlocation = string.gsub(location, \"Playing \", \"In \")\n\t\t\t\tif string.find(location:lower(), \"creating\") then\n\t\t\t\t\tlocation = \"Roblox Studio\"\n\t\t\t\tend\n\t\t\t\tif string.find(location, \"Vesteria\") then\n\t\t\t\t\tlocation = \"Main Menu\"\n\t\t\t\tend\n\t\t\t\t]]\n\t\t\t\t\n\n\t\t\t\tif placeId or jobId then\n\t\t\t\t\t\n\t\t\t\t\tif placeId == 2376885433 then\n\t\t\t\t\t\tlocation = \"Main Menu\"\n\t\t\t\t\telse\n\t\t\t\t\t\tpcall(function()\n\t\t\t\t\t\t\tlocal info = game.MarketplaceService:GetProductInfo(placeId)\n\t\t\t\t\t\t\tif info then\n\t\t\t\t\t\t\t\tlocation = \"In \" .. info.Name\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend)\n\t\t\t\t\tend\n\t\t\t\t\t\n\t\t\t\t\tfriend.LastLocation = location\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\ttable.insert(onlineFriends, friend)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\t\n\tprint(\"!!done fetching\")\n\t\n\treturn onlineFriends\n\n\t\nend\n\nnetwork:create(\"fetchPlayerFriendsInfo\",\"RemoteFunction\",\"OnServerInvoke\",fetchPlayerFriendsInfo)\t\n\t\nend"
  },
  {
    "path": "src/ServerScriptService/modules/firepitScript.lua",
    "content": "return function()\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal modules = require(replicatedStorage:WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nlocal firetime = 0\nlocal DURATION = 300\nlocal fireLoop\nlocal fireIgnite\n\n--[[\nlocal fireLoop = Instance.new(\"Sound\")\nfireLoop.SoundId = \"rbxassetid://2564068359\"\nfireLoop.Looped = true\nfireL\n\nlocal fireIgnite = Instance.new(\"Sound\")\nfireIgnite.SoundId = \"rbxassetid://2564068060\"\n]]\n\ngame.CollectionService:AddTag(script.Parent, \"firepit\")\n\nif script.Parent:IsA(\"BasePart\") then\n\tscript.Parent.Anchored = true\nend\n\nlocal sounds = game.ReplicatedStorage.assets:WaitForChild(\"sounds\")\n\nif sounds:FindFirstChild(\"fireLoop\") then\n\tfireLoop = sounds.fireLoop:Clone()\n\tfireLoop.Parent = script.Parent\nend\n\nif sounds:FindFirstChild(\"fireIgnite\") then\n\tfireIgnite = sounds.fireIgnite:Clone()\n\tfireIgnite.Parent = script.Parent\nend\n\nlocal function ignite(player, firepit)\n\tif firepit == script.Parent then\n\t\tif fireIgnite then\n\t\t\tfireIgnite:Play()\n\t\tend\n\t\tfor _, v in pairs(script.Parent:GetChildren()) do\n\t\t\tpcall(function()\n\t\t\t\tv.Enabled = true\n\t\t\tend)\n\t\tend\n\t\tif fireLoop then\n\t\t\tfireLoop:Play()\n\t\tend\n\t\tgame.CollectionService:RemoveTag(script.Parent,\"interact\")\n\t\tfiretime = os.time()\n\tend\nend\n\nlocal function dampen()\n\tfor _, v in pairs(script.Parent:GetChildren()) do\n\t\tpcall(function()\n\t\t\tv.Enabled = false\n\t\tend)\n\tend\n\tif fireLoop then\n\t\tfireLoop:Stop()\n\tend\n\tgame.CollectionService:AddTag(script.Parent,\"interact\")\nend\n\nnetwork:create(\"igniteFirePit\",\"RemoteEvent\",\"OnServerEvent\",ignite)\n\nwhile wait(1) do\n\tif script.Parent.Fire.Enabled and os.time() - firetime > DURATION then\n\t\tdampen()\n\tend\nend\n--\n\nend"
  },
  {
    "path": "src/ServerScriptService/server.server.lua",
    "content": "-- Master server script\nlocal modules = {}\n\nlocal ServerScriptService = game:GetService(\"ServerScriptService\")\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal directories = {ReplicatedStorage.modules, ServerScriptService.contents}\n\nlocal beginInit = false\n\nlocal function setup(directory)\n    for _, moduleScript in pairs(directory:GetDescendants()) do\n        if moduleScript:IsA(\"ModuleScript\") then\n            print(\"$ server\", \"require module\", moduleScript.Name)\n            modules[moduleScript.Name] = require(moduleScript)\n        end\n    end\nend\n\nlocal function initialize()\n    for moduleName, module in pairs(modules) do\n        if typeof(module) == \"table\" and (module.init and not module.__initialized) then\n            print(\"$ server\", \"initialize module\", moduleName)\n            module.init(modules)\n            module.__initialized = true\n        end\n    end\nend\n\nfor _, directory in pairs(directories) do\n    -- Get all static modules\n    setup(directory)\n    -- Ongoing support\n    directory.DescendantAdded:connect(function(moduleScript)\n        if moduleScript:IsA(\"ModuleScript\") then\n            print(\"$ server\", \"require module\", moduleScript.Name)\n            local module = require(moduleScript)\n            modules[moduleScript.Name] = module\n            if typeof(module) == \"table\" and module.init and beginInit then\n                print(\"$ server\", \"initialize module\", moduleScript.Name)\n                module.init(modules)\n            end\n        end\n    end)\nend\n\ntable.sort(modules, function(module1, module2)\n    return (module1.priority or 10) < (module2.priority or 10)\nend)\n\nbeginInit = true\ninitialize()\nprint(\"$ server\", \"all modules in queue initialized\")"
  },
  {
    "path": "src/StarterGui/abilities.lua",
    "content": "local module = {}\n\nlocal menu = script.Parent.gameUI.menu_abilities\n\nfunction module.show()\n\tmenu.Visible = not menu.Visible\nend\nfunction module.hide()\n\tmenu.Visible = false\nend\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal abilityLookup = require(replicatedStorage:WaitForChild(\"abilityLookup\"))\n\nfunction module.init(Modules)\n\tlocal network = Modules.network\n\tlocal uiCreator = Modules.uiCreator\n\n\tlocal abilityDataPairing = {}\n\n\tlocal function update(abilities)\n\t\t-- playerData.abilities\n\t\tabilities = abilities or network:invoke(\"getCacheValueByNameTag\", \"abilities\")\n\t\tlocal unlockedAbilities = {}\n\n\t\tprint(abilities)\n\n\t\tfor _, abilityData in pairs(abilities) do\n\t\t\t-- TODO: check if REALLY unlocked\n\t\t\ttable.insert(unlockedAbilities, abilityData)\n\t\t\t--print(abilityData)\n\t\tend\n\n\t\t-- clear existing buttons\n\t\tfor _, button in pairs(menu.content:GetChildren()) do\n\t\t\tif button:IsA(\"GuiObject\") then\n\t\t\t\tbutton:Destroy()\n\t\t\tend\n\t\t\tabilityDataPairing = {}\n\t\tend\n\n\t\tfor i = 1, 9 do\n\t\t\tlocal template = menu.sampleAbility:clone()\n\t\t\ttemplate.Name = i\n\n\t\t\tlocal abilityData = unlockedAbilities[i]\n\t\t\tif abilityData then\n\t\t\t\tlocal abilityInfo = abilityLookup[abilityData.id]\n\n\t\t\t\ttemplate.item.Image = abilityInfo.image\n\t\t\t\tabilityDataPairing[template] = abilityData\n\n\t\t\t\tuiCreator.drag.setIsDragDropFrame(template.item)\n\t\t\t\tuiCreator.setIsDoubleClickFrame(template.item, 0.2, function()\n\t\t\t\t\tlocal thing, error = network:invoke(\"abilityUseRequest\", abilityData.id)\n\t\t\t\tend)\n\t\t\tend\n\n\t\t\ttemplate.Parent = menu.content\n\t\t\ttemplate.Visible = true\n\t\tend\n\tend\n\n\tupdate()\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", function(key, value)\n\t\tif key == \"abilities\" then\n\t\t\tupdate(value)\n\t\tend\n\tend)\n\tnetwork:create(\"getAbilitySlotDataByAbilitySlotUI\", \"BindableFunction\", \"OnInvoke\", function(button)\n\t\treturn abilityDataPairing[button.Parent]\n\tend)\nend\n\n\n\n\n\nmenu.close.Activated:connect(module.hide)\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/bossHealth.lua",
    "content": "local module = {}\n\nlocal frame = script.Parent.gameUI.bossHealth\nfunction module.init(Modules)\n\tlocal network = Modules.network\n\tframe.Visible = false\n\n\tnetwork:create(\"prepareBossHealthUIForMonster\", \"BindableFunction\", \"OnInvoke\", function(monsterData)\n\t\tif monsterData.portrait then\n\t\t\tframe.thumbnail.Image = monsterData.portrait\n\t\tend\n\t\tframe.Visible = true\n\t\treturn frame\n\tend)\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/chat.lua",
    "content": "local module = {}\n\nfunction module.init()\n\t\n\tlocal function main()\n\t\tgame.StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.All, false)\n\t\tspawn(function()\n\t\t\twait()\n\t\t\t\n\t\t\t\n\n--\t\t\tgame.StarterGui:SetCore(\"ChatWindowPosition\", UDim2.new(0,0,0,0))\n\t\t\t\n\t\t\tgame.StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Chat, true)\n\t\t\tgame.StarterGui:SetCore(\"ChatBarDisabled\", false)\n\t\t\tgame.StarterGui:SetCore(\"ChatActive\", true)\n\t\t\t\n\n\t\tend)\n\tend\n\t\n\tmain()\t\n\t\nend\n\nreturn module"
  },
  {
    "path": "src/StarterGui/checkpoint.lua",
    "content": "local module = {}\n\nfunction module.init(Modules)\n\tlocal tween = Modules.tween\n\tlocal events = Modules.events\n\t\n\tlocal currentSpawnPoint\n\t\n\tlocal textLabel = script.Parent.gameUI.checkpoint\n\t\n\tevents:registerForEvent(\"playerRespawnPointChanged\", function(spawnPoint)\n\t\tif spawnPoint:FindFirstChild(\"description\") then\n\t\t\tcurrentSpawnPoint = spawnPoint\n\t\t\ttextLabel.Text = spawnPoint.description.Value\n\t\t\ttextLabel.TextStrokeTransparency = 1\n\t\t\ttextLabel.TextTransparency = 1\n\t\t\ttextLabel.Visible = true\n\t\t\ttween(textLabel, {\"TextTransparency\", \"TextStrokeTransparency\"}, 0, 1)\n\t\t\tdelay(3, function()\n\t\t\t\tif currentSpawnPoint == spawnPoint then\n\t\t\t\t\ttween(textLabel, {\"TextTransparency\", \"TextStrokeTransparency\"}, 1, 1)\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\tend)\n\t\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/customization.lua",
    "content": "-- character customization module\n-- berezaa\n\nlocal ui = script.Parent.customize\nlocal accessories = game.ReplicatedStorage.assets.accessories\n\nlocal module = {}\n\nlocal connection\nlocal targetTableTop\nlocal currentDesiredAppearance\n\nfunction module.display()\n\nend\n\nfunction module.hide()\n\nend\n\nlocal runService = game:GetService(\"RunService\")\n\nfunction module.init(Modules)\n\tlocal network = Modules.network\n\tlocal tween = Modules.tween\n\tlocal input = Modules.input\n\tlocal utilities = Modules.utilities\n\n\tlocal lookup = accessories\n\tlocal renderedItems = Instance.new(\"Folder\")\n\trenderedItems.Name = \"renderedOptions\"\n\trenderedItems.Parent = workspace\n\n\tlocal characterRender\n\tlocal rand = Random.new(os.time())\n\n\tlocal characterTable = {}\n\n\trenderedItems.DescendantAdded:connect(function(object)\n\t\tif object.Name == \"bodyPart\" then\n\t\t\tlocal part = object.Parent\n\t\t\tpart.Color = lookup:FindFirstChild(\"skinColor\"):FindFirstChild(tostring(characterTable.accessories.skinColorId or 1)).Value\n\t\telseif object.Name == \"hair_Head\" and object:IsA(\"BasePart\") then\n\t\t\tobject.Color = lookup:FindFirstChild(\"hairColor\"):FindFirstChild(tostring(characterTable.accessories.hairColorId or 1)).Value\n\t\telseif object.Name == \"shirt\" or object.Name == \"shirtTag\" then\n\t\t\tif object.Name == \"shirtTag\" then\n\t\t\t\tobject = object.Parent\n\t\t\tend\n\t\t\tif object:IsA(\"BasePart\") then\n\t\t\t\tobject.Color = lookup:FindFirstChild(\"shirtColor\"):FindFirstChild(tostring(characterTable.accessories.shirtColorId or 1)).Value\n\t\t\tend\n\t\tend\n\tend)\n\n\tlocal pastLanding = true\n\n\tlocal function getItemFromHitpart(hitpart)\n\t\tfor _, renderItem in pairs(renderedItems:GetChildren()) do\n\t\t\tif hitpart:IsDescendantOf(renderItem) then\n\t\t\t\treturn renderItem\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal selectedItem\n\n\tlocal function selectItem(item)\n\t\tif selectedItem and selectedItem.Parent then\n\t\t\tselectedItem.PrimaryPart.Transparency = 1\n\t\t\tselectedItem = nil\n\t\tend\n\t\tif item then\n\t\t\tselectedItem = item\n\t\t\titem.PrimaryPart.Transparency = 0.3\n\t\tend\n\tend\n\n\tlocal inputModule = input\n\tlocal mouseDownTime = 0\n\n\tgame:GetService(\"UserInputService\").InputBegan:connect(function(input, absorbed)\n\t\tif not absorbed and input.UserInputType == Enum.UserInputType.MouseButton1 then\n\t\t\tmouseDownTime = tick()\n\t\tend\n\tend)\n\n\tlocal currentCategory\n\n\tgame:GetService(\"UserInputService\").InputEnded:connect(function(input, absorbed)\n\t\tif not absorbed and input.UserInputType == Enum.UserInputType.MouseButton1 and tick() - mouseDownTime <= 2 then\n\t\t\tif selectedItem then\n\t\t\t\tlocal currentDisplayCategory = currentCategory\n\t\t\t\tif currentDisplayCategory == \"skinColor\" then\n\t\t\t\t\tcurrentDisplayCategory = \"skinColorId\"\n\t\t\t\tend\n\n\t\t\t\tcharacterTable.accessories[currentDisplayCategory] = tonumber(selectedItem.Name)\n\t\t\t\tnetwork:invoke(\"applyCharacterAppearanceToRenderCharacter\", characterRender.entity, characterTable)\n\t\t\tend\n\t\tend\n\tend)\n\n\tlocal function reset()\n\t\tfor _, oslot in pairs(script.Parent.Frame.DataSlots:GetChildren()) do\n\t\t\tif oslot:IsA(\"ImageButton\") then\n\t\t\t\toslot.ImageColor3 = Color3.fromRGB(126, 126, 126)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal debounce = false\n\n\tlocal function getModelAveragePosition(model)\n\t\tlocal preavg = Vector3.new()\n\t\tlocal totalcount = 0\n\t\tfor _, part in pairs(model:GetChildren()) do\n\t\t\tif part:IsA(\"BasePart\") then\n\t\t\t\tpreavg = preavg + part.Position\n\t\t\t\ttotalcount = totalcount + 1\n\t\t\tend\n\t\tend\n\t\treturn preavg / totalcount\n\tend\n\n\tlocal function updateColors()\n\t\tfor _, object in pairs(renderedItems:GetDescendants()) do\n\t\t\tif object.Name == \"bodyPart\" then\n\t\t\t\tlocal part = object.Parent\n\t\t\t\tpart.Color = lookup:FindFirstChild(\"skinColor\"):FindFirstChild(tostring(characterTable.accessories.skinColorId or 1)).Value\n\t\t\telseif object.Name == \"hair_Head\" and object:IsA(\"BasePart\") then\n\t\t\t\tobject.Color = lookup:FindFirstChild(\"hairColor\"):FindFirstChild(tostring(characterTable.accessories.hairColorId or 1)).Value\n\t\t\telseif object.Name == \"shirt\" or object:FindFirstChild(\"shirtTag\") then\n\t\t\t\tobject.Color = lookup:FindFirstChild(\"shirtColor\"):FindFirstChild(tostring(characterTable.accessories.shirtColorId or 1)).Value\n\t\t\tend\n\t\tend\n\t\tnetwork:invoke(\"applyCharacterAppearanceToRenderCharacter\", characterRender.entity, characterTable)\n\tend\n\n\tlocal function createRepresentationOfItem(item)\n\t\tlocal repre\n\n\t\tif item:IsA(\"Color3Value\") then\n\t\t\trepre = script.colorRepre:Clone()\n\t\t\trepre.value.Color = item.Value\n\t\t\trepre.Name = item.Name\n\t\telse\n\t\t\trepre = item:Clone()\n\t\tend\n\n\t\tlocal avgpos = getModelAveragePosition(repre)\n\t\t-- reparent everything to root\n\t\tfor _, part in pairs(repre:GetDescendants()) do\n\t\t\tif part:IsA(\"BasePart\") then\n\t\t\t\tif part.Parent == repre and part:FindFirstChild(\"colorOverride\") == nil then\n\t\t\t\t\tlocal bodyPartTag = Instance.new(\"BoolValue\")\n\t\t\t\t\tbodyPartTag.Name = \"bodyPart\"\n\t\t\t\t\tbodyPartTag.Parent = part\n\t\t\t\telse\n\t\t\t\t\tpart.Parent = repre\n\t\t\t\tend\n\t\t\t\tpart.Anchored = true\n\t\t\tend\n\t\tend\n\t\t-- create a PrimaryPart using the avgPos\n\t\tlocal primaryPart = Instance.new(\"Part\")\n\t\tprimaryPart.Size = Vector3.new(2,0.5,2)\n\t\tprimaryPart.CFrame = CFrame.new(avgpos - Vector3.new(0,2,0))\n\t\tprimaryPart.Parent = repre\n\t\tprimaryPart.Anchored = true\n\t\tprimaryPart.TopSurface = Enum.SurfaceType.Smooth\n\t\tprimaryPart.Material = Enum.Material.Neon\n\t\tprimaryPart.Transparency = 1\n\n\t\tlocal immuneTag = Instance.new(\"BoolValue\")\n\t\timmuneTag.Name = \"colorOverride\"\n\t\timmuneTag.Parent = primaryPart\n\n\t\trepre.PrimaryPart = primaryPart\n\t\treturn repre\n\tend\n\n\tlocal displayConnection\n\tlocal lastXboxSelected\n\n\tlocal function generateCoolButtons()\n\t\tui.xboxButtons:ClearAllChildren()\n\t\tfor _, item in pairs(renderedItems:GetChildren()) do\n\t\t\tif item and item.PrimaryPart then\n\t\t\t\tlocal vector, onScreen = workspace.CurrentCamera:WorldToScreenPoint(item.PrimaryPart.Position)\n\t\t\t\tif onScreen then\n\t\t\t\t\tlocal button = ui.sampleXboxButton:Clone()\n\t\t\t\t\tbutton.Name = item.Name\n\t\t\t\t\tbutton.Parent = ui.xboxButtons\n\t\t\t\t\tbutton.Visible = input.mode.Value == \"xbox\"\n\t\t\t\t\tbutton.Position = UDim2.new(0, vector.X, 0, vector.Y + game.GuiService:GetGuiInset().Y)\n\t\t\t\t\tbutton.Activated:connect(function()\n\t\t\t\t\t\tif input.mode.Value == \"xbox\" then\n\t\t\t\t\t\t\tcharacterTable.accessories[currentCategory] = tonumber(button.Name)\n\t\t\t\t\t\t\tnetwork:invoke(\"applyCharacterAppearanceToRenderCharacter\", characterRender.entity, characterTable)\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\t\t\t\t\tbutton.SelectionGained:connect(function()\n\t\t\t\t\t\tlastXboxSelected = item\n\t\t\t\t\t\tselectItem(item)\n\t\t\t\t\tend)\n\t\t\t\t\tbutton.SelectionLost:connect(function()\n\t\t\t\t\t\tif lastXboxSelected == item then\n\t\t\t\t\t\t\tselectItem(nil)\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tinput.mode.Changed:connect(function()\n\n\t\tfor _, button in pairs(ui.xboxButtons:GetChildren()) do\n\t\t\tif button:IsA(\"GuiObject\") then\n\t\t\t\tbutton.Visible = input.mode.Value == \"xbox\"\n\t\t\tend\n\t\tend\n\tend)\n\n\tlocal function displayCategory(categoryName)\n\t\trenderedItems:ClearAllChildren()\n\t\tlookup = accessories\n\t\tlocal items = lookup:FindFirstChild(categoryName)\n\t\tif items then\n\t\t\tlocal x = 0\n\t\t\tlocal z = 0\n\n\t\t\tlocal target = targetTableTop\n\n\t\t\tfor _, item in pairs(items:GetChildren()) do\n\t\t\t\tlocal repre = createRepresentationOfItem(item)\n\t\t\t\tlocal cf = target.CFrame * CFrame.new((z * 6) - target.Size.X/2 + 1.1, target.Size.Y/2 + (z * 3.2), (x * 2.5) - target.Size.Z/2 + 1.1)\n\t\t\t\trepre:SetPrimaryPartCFrame(cf)\n\t\t\t\trepre.Parent = renderedItems\n\n\t\t\t\tx = x + 1\n\t\t\t\tif x > 4 then\n\t\t\t\t\tx = 0\n\t\t\t\t\tz = z + 1\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tfor _, button in pairs(ui.buttons:GetChildren()) do\n\t\t\tif button:IsA(\"ImageButton\") then\n\t\t\t\tbutton.ImageColor3 = Color3.fromRGB(103, 255, 212)\n\t\t\tend\n\t\tend\n\n\t\tlocal newbutton = ui.buttons:FindFirstChild(categoryName)\n\t\tif newbutton then\n\t\t\tnewbutton.ImageColor3 = Color3.fromRGB(255, 255, 255)\n\t\tend\n\t\tcurrentCategory = categoryName\n\n\t\tui.hairColor.Visible = false\n\t\tui.shirtColor.Visible = false\n\t\tif categoryName == \"hair\" then\n\t\t\tui.hairColor.Visible = true\n\t\telseif categoryName == \"undershirt\" then\n\t\t\tui.shirtColor.Visible = true\n\t\tend\n\n\t\tgenerateCoolButtons()\n\tend\n\n\tfor _, button in pairs(ui.buttons:GetChildren()) do\n\t\tif button:IsA(\"ImageButton\") then\n\t\t\tbutton.Activated:connect(function()\n\t\t\t\tdisplayCategory(button.Name)\n\t\t\tend)\n\t\tend\n\tend\n\n\tfor _, color in pairs(lookup:WaitForChild(\"hairColor\"):GetChildren()) do\n\t\tif color:IsA(\"Color3Value\") then\n\t\t\tlocal buttonRepre = ui.colorButtonSample:Clone()\n\t\t\tbuttonRepre.ImageColor3 = color.value\n\t\t\tbuttonRepre.Name = color.Name\n\t\t\tbuttonRepre.Parent = ui.hairColor\n\t\t\tbuttonRepre.Visible = true\n\t\t\t-- change hairColor\n\t\t\tbuttonRepre.Activated:connect(function()\n\t\t\t\tcharacterTable.accessories[\"hairColorId\"] = tonumber(buttonRepre.Name)\n--\t\t\t\t\tcharacterTable.accessories[\"hairColor\"] = tonumber(buttonRepre.Name)\n\t\t\t\tupdateColors()\n\n\t\t\tend)\n\t\tend\n\tend\n\n\tfor _, color in pairs(lookup:WaitForChild(\"shirtColor\"):GetChildren()) do\n\t\tif color:IsA(\"Color3Value\") then\n\t\t\tlocal buttonRepre = ui.colorButtonSample:Clone()\n\t\t\tbuttonRepre.ImageColor3 = color.value\n\t\t\tbuttonRepre.Name = color.Name\n\t\t\tbuttonRepre.Parent = ui.shirtColor\n\t\t\tbuttonRepre.Visible = true\n\t\t\t-- change shirtColor\n\t\t\tbuttonRepre.Activated:connect(function()\n\t\t\t\tcharacterTable.accessories[\"shirtColorId\"] = tonumber(buttonRepre.Name)\n--\t\t\t\t\tcharacterTable.accessories[\"shirtColor\"] = tonumber(buttonRepre.Name)\n\t\t\t\tupdateColors()\n\n\t\t\tend)\n\t\tend\n\tend\n\n\tfunction module.hide()\n\t\tnetwork:invoke(\"lockCameraPosition\", false)\n\t\tui.Enabled = false\n\t\tui.Parent.gameUI.Enabled = true\n\t\tif connection then\n\t\t\tconnection:disconnect()\n\t\t\tconnection = nil\n\t\tend\n\n\t\tif characterRender then\n\t\t\tcharacterRender:Destroy()\n\t\tend\n\tend\n\n\tui.options.cancel.Activated:connect(module.hide)\n\tui.options.done.Activated:connect(function()\n\t\tcurrentDesiredAppearance = characterTable\n\t\tmodule.hide()\n\tend)\n\n\n\tfunction module.display(tableTop)\n\t\tcurrentDesiredAppearance = nil\n\t\tui.Enabled = true\n\t\tui.Parent.gameUI.Enabled = false\n\t\ttargetTableTop = tableTop\n\n\t\tif input.mode.Value == \"xbox\" then\n\t\t\tgame.GuiService.GuiNavigationEnabled = true\n\t\t\tgame.GuiService.SelectedObject = input.getBestButton(ui)\n\t\tend\n\n\t\tcharacterTable.accessories = {}\n\n\t\t-- default values\n\t\tfor i,category in pairs(lookup:GetChildren()) do\n\t\t\tlocal children = category:GetChildren()\n\t\t\tif #children > 0 then\n\t\t\t\tcharacterTable.accessories[category.Name] = 1;\n\t\t\tend\n\t\tend\n\n\t\t-- real from player appearance\n\t\tlocal player = game.Players.LocalPlayer\n\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart:FindFirstChild(\"appearance\") then\n\t\t\tlocal success, playerAppearance = utilities.safeJSONDecode(player.Character.PrimaryPart.appearance.Value)\n\t\t\tif success and playerAppearance.accessories then\n\t\t\t\tfor accessory, value in pairs(playerAppearance.accessories) do\n\t\t\t\t\tcharacterTable.accessories[accessory] = value\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tdisplayCategory(\"hair\")\n\n\t\tif characterRender then\n\t\t\tcharacterRender:Destroy()\n\t\tend\n\n\t\tcharacterRender = network:invoke(\"createRenderCharacterContainerFromCharacterAppearanceData\", workspace:WaitForChild(\"characterMask\"), characterTable)\n\t\tcharacterRender.Parent = workspace\n\t\tlocal animationController = characterRender.entity:WaitForChild(\"AnimationController\")\n\t\tlocal track = animationController:LoadAnimation(workspace.characterMask:WaitForChild(\"idle\"))\n\t\ttrack.Looped = true\n\t\ttrack.Priority = Enum.AnimationPriority.Idle\n\t\ttrack:Play()\n\n\t\tlocal cameraTarget = CFrame.new(targetTableTop.Position + Vector3.new(0,4.4,0) + targetTableTop.CFrame.rightVector * -28, targetTableTop.Position + Vector3.new(0,2.9,0)) * CFrame.new(-4,0,0)\n\n\t\tnetwork:invoke(\"lockCameraPosition\", cameraTarget, 0.7)\n\t\ttween(workspace.CurrentCamera,{\"FieldOfView\"},30,0.5)\n\n\t\tutilities.playSound(\"swoosh\")\n\n\t\twait(0.7)\n\n\t\tgenerateCoolButtons()\n\t\tif connection then\n\t\t\tconnection:disconnect()\n\t\tend\n\n\t\tlocal connection = game:GetService(\"UserInputService\").InputChanged:connect(function(input, processed)\n\t\t\tif input.UserInputType == Enum.UserInputType.MouseMovement then\n\t\t\t\tlocal camSway = 0\n\t\t\t\tlocal hitpart, hitpos\n\t\t\t\tif pastLanding and renderedItems and ui.Enabled then\n\t\t\t\t\tcamSway = 70\n\n\t\t\t\t\tlocal ray = workspace.CurrentCamera:ScreenPointToRay(input.Position.X,input.Position.Y,1)\n\t\t\t\t\tlocal renderedChildren = renderedItems:GetChildren()\n\t\t\t\t\tif #renderedChildren > 0 and inputModule.mode.Value == \"pc\" then\n\t\t\t\t\t\tlocal hitpart, hitpos = workspace:FindPartOnRayWithWhitelist(Ray.new(ray.Origin, ray.Direction * 100), renderedChildren, true)\n\t\t\t\t\t\tif hitpart then\n\t\t\t\t\t\t\tlocal item = getItemFromHitpart(hitpart)\n\t\t\t\t\t\t\tselectItem(item)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tselectItem()\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\t-- add camera sway effect, to a lessor extent\n\t\t\t\telseif not pastLanding then\n\t\t\t\t\tcamSway = 100\n\t\t\t\tend\n\n\t\t\t\tif camSway > 0 then\n\t\t\t\t\tlocal ray = workspace.CurrentCamera:ScreenPointToRay(input.Position.X,input.Position.Y,camSway)\n\t\t\t\t\thitpart, hitpos = workspace:FindPartOnRay(ray)\n\t\t\t\t\tif hitpos then\n\t\t\t\t\t\tlocal lookat = cameraTarget.Position + cameraTarget.lookVector * 50\n\t\t\t\t\t\tlookat = Vector3.new(hitpos.x + lookat.x * 25, hitpos.y + lookat.y * 25, hitpos.z + lookat.z * 25)/26\n\t\t\t\t\t\t--workspace.CurrentCamera.CFrame = CFrame.new(cameraTarget.Position, lookat)\n\t\t\t\t\t\tnetwork:invoke(\"lockCameraPosition\",CFrame.new(cameraTarget.Position, lookat),0.2)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\tend\n\n\tnetwork:create(\"displayCharacterCustomizationScreen\", \"BindableFunction\", \"OnInvoke\", module.display)\n\tnetwork:create(\"hideCharacterCustomizationScreen\", \"BindableFunction\", \"OnInvoke\", module.hide)\n\n\t-- yield until the player makes a selection\n\tfunction module.yieldDesiredAppearance(tableTop)\n\t\tif ui.Enabled then\n\t\t\treturn false, \"Appearance picker is already active\"\n\t\tend\n\n\t\tmodule.display(tableTop)\n\n\t\trepeat runService.Heartbeat:wait() until not ui.Enabled\n\n\t\treturn currentDesiredAppearance\n\tend\n\n\tnetwork:create(\"yieldCharacterCustomizationScreen\", \"BindableFunction\", \"OnInvoke\", module.yieldDesiredAppearance)\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/dataFail.lua",
    "content": "local module = {}\n\nlocal player = game.Players.LocalPlayer\n\nfunction module.init(Modules)\n\t\n\tlocal tween = Modules.tween\n\n\tlocal frame = script.Parent.gameUI.leftBar.dataFail\n\t\n\tframe.Visible = false\n\t\n\tlocal function display(n)\n\t\t\n\t\t\n\t\tframe.success.Visible = false\n\t\tframe.fail.Visible = false\n\t\tframe.queue.Visible = false\n\t\tframe.Visible = true\n\t\t\n\t\tlocal frame = frame.fail\n\t\t\n\t\tif n == 1 then\n\t\t\tframe = frame.queue\n\t\tend\n\t\t\n\t\tframe.Visible = true\n\t\t\n\t\tfor i=1,4 do\n\t\t\tlocal flare = frame.flare:Clone()\n\t\t\tflare.Name = \"flareCopy\"\n\t\t\tflare.ImageColor3 = frame.spinner.ImageColor3\n\t\t\tflare.Parent = frame\n\t\t\tflare.Visible = true\n\t\t\tflare.Size = UDim2.new(1,4,1,4)\n\t\t\tflare.Position = UDim2.new(0,-2,0.5,0)\n\t\t\tflare.AnchorPoint = Vector2.new(0,0.5)\n\t\t\tlocal x = (180 - 40*i)\n\t\t\tlocal y = (14 - 2*i)\n\t\t\tlocal EndPosition = UDim2.new(0,-y/2,0.5,0)\n\t\t\tlocal EndSize = UDim2.new(1,x,1,y)\n\t\t\ttween(flare,{\"Position\",\"Size\",\"ImageTransparency\"},{EndPosition, EndSize, 1},0.5*i)\n\t\tend\t\t\t\t\n\tend\n\t\n\tlocal function hide()\n\t\tif frame.Visible then\n\t\t\tframe.fail.Visible = false\n\t\t\tframe.success.Position = UDim2.new(0,0,0,0)\n\t\t\tframe.success.Visible = true\n\t\t\tspawn(function()\n\t\t\t\twait(10)\n\t\t\t\tif frame.success.Visible then\n\t\t\t\t\ttween(frame.success, {\"Position\"}, {UDim2.new(-1,-20,0,0)}, 0.5)\n\t\t\t\t\twait(0.5)\n\t\t\t\t\t\n\t\t\t\t\tframe.Visible = false\t\t\t\t\t\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\tend\n\n\tif player:FindFirstChild(\"DataSaveFailed\") then\n\t\tdisplay(player.DataSaveFailed.Value)\n\tend\n\t\n\tplayer.ChildAdded:Connect(function(Child)\n\t\tif Child.Name == \"DataSaveFailed\" then\n\t\t\tdisplay(Child.Value)\n\t\tend\n\tend)\n\t\n\tplayer.ChildRemoved:Connect(function(Child)\n\t\tif frame.Visible and frame.fail.Visible and player:FindFirstChild(\"DataSaveFailed\") == nil then\n\t\t\thide()\n\t\tend\n\tend)\n\t\nend\n\nreturn module"
  },
  {
    "path": "src/StarterGui/deathScreen.lua",
    "content": "local module = {}\n\n\nfunction module.show()\n\t\nend\n\nlocal COUNTDOWN_TIME = 60\n\nfunction module.init(Modules)\n\n\tlocal frame = script.Parent.gameUI.deathScreen\n\t\n\tlocal network = Modules.network\n\tlocal tween = Modules.tween\n\tlocal focus = Modules.focus\n\tlocal utilities = Modules.utilities\n\tlocal money = Modules.money\n\tlocal levels = Modules.levels\n\t\n\tif game.Lighting:FindFirstChild(\"deathEffect\") then\n\t\tgame.Lighting.deathEffect:Destroy()\n\tend\n\tif game.Lighting:FindFirstChild(\"deathBlur\") then\n\t\tgame.Lighting.deathBlur:Destroy()\n\tend\t\n\t\n\tlocal function updateLabel()\n\t\tlocal gold = network:invoke(\"getCacheValueByNameTag\", \"gold\")\n\t\tlocal exp = network:invoke(\"getCacheValueByNameTag\", \"exp\")\t\t\n\t\tlocal level = network:invoke(\"getCacheValueByNameTag\", \"level\")\n\t\tlocal expForNextLevel = levels.getEXPToNextLevel(level)\n\t\tlocal expLost = math.min(exp, expForNextLevel*0.2)\n\t\tframe.curve.exp.Text = \"-\" .. utilities.formatNumber(expLost) .. \" XP\"\n\t\tmoney.setLabelAmount(frame.curve.cost, -gold*0.1)\n\tend\n\t\n\tspawn(function()\n\t\tupdateLabel()\n\tend)\n\t\n\tlocal pendingDeath = false\n\tnetwork:invoke(\"ambienceSetIsDead\", false)\n\t\n\tfunction module.accept()\n\t\tif pendingDeath then\n\t\t\tnetwork:fireServer(\"deathGuiAccepted\")\n\t\t\tpendingDeath = false\n\t\tend\n\tend\t\n\t\n\tfunction module.show()\n\t\t\n\t\tpendingDeath = true\n\t\t\n\t\tfocus.close()\n\t\t\n\t\tlocal color = Instance.new(\"ColorCorrectionEffect\")\n\t\tcolor.Saturation = 0\n\t\tcolor.Name = \"deathEffect\"\n\t\tcolor.Parent = game.Lighting\n\t\ttween(color, {\"Saturation\", \"Contrast\"}, {-1, 0.1}, 0.1)\n\t\t\n\t\tlocal blur = Instance.new(\"BlurEffect\")\n\t\tblur.Size = 0\n\t\tblur.Enabled = true\n\t\tblur.Name = \"deathBlur\"\n\t\tblur.Parent = game.Lighting\n\t\ttween(blur, {\"Size\"}, 10, 4)\n\t\t\n\t\tnetwork:invoke(\"ambienceSetIsDead\", true)\n\t\tdelay(4, function()\n\t\t\tif frame and script:FindFirstChild(\"chorus\") then\n\t\t\t\tscript.chorus:Play()\n\t\t\tend\n\t\t\tframe.gradient.ImageTransparency = 1\n\t\t\tframe.gradient.BackgroundTransparency = 1\n\t\t\ttween(frame.gradient, {\"ImageTransparency\"}, 0, 1)\n--\t\t\ttween(frame.gradient, {\"BackgroundTransparency\"}, 0.4, 2)\t\n\t\t\tframe.Visible = true\n\t\t\t--[[\n\t\t\tfor i=COUNTDOWN_TIME,0,-1 do\n\t\t\t\tframe.timer.Text = tostring(i)\n\t\t\t\twait(1)\n\t\t\tend\n\t\t\t]]\n\t\t\tframe.curve.timer.progress.Size = UDim2.new(0,0,1,0)\n\t\t\ttween(frame.curve.timer.progress, {\"Size\"}, UDim2.new(1,0,1,0), COUNTDOWN_TIME, Enum.EasingStyle.Linear)\n\t\t\twait(COUNTDOWN_TIME)\n\t\t\tmodule.accept()\t\t\t\t\t\n\t\tend)\n\t\t\t\n\t\t\n\t\t\n\t\t\n\n\tend\n\t\n\n\tframe.curve.respawn.Activated:connect(module.accept)\n\t\n\tnetwork:connect(\"deathGuiRequested\", \"OnClientEvent\", module.show)\n\t\n\tlocal function onDataChange(key, value)\n\t\tif key == \"gold\" or key == \"level\" or key == \"exp\" then\n\t\t\tupdateLabel()\n\t\tend\n\tend\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onDataChange)\nend\n\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/dialogue.lua",
    "content": "local module = {}\n\nlocal superRootDialogueData\n--[[\n\tdialogueData {}\n\t\tstring id\n\t\tstring dialogue\n\n\t\ttable responses\n\t\t\t[string playerResponse] = <dialogData>\n\n\n\tdialogueHandler {}\n\t\tinstance dialogUI\n\t\tinstance currentSpeaker\n\n\t\tdialogueData rootDialogueData\n\t\tdialogueData currentDialogueData\n\t\tdialogueData previousDialogueData\n\n\t\tevent::onPlayerDialogueProceed(string currentId)\n\t\tevent::onDialogueFinishShowing(string currentId)\n\t\tevent::onPlayerSelectResponse(string currentId, string selectionId)\n\n\t\tmethod::showDialogue([string startingId])\n\t\tmethod::setSpeaker(instance speakerModel, bool tweenCameraToSpeaker = false[, cframe cameraOffset])\n\n\tlocal shopDialogue = dialogue:createDialog({\n\t\tid \t\t\t= \"startTalkingToShopkeeper\"\n\t\tdialogue \t= \"Welcome to my shop, how may I help you?\";\n\t\tresponses \t= {\n\t\t\t[\"What are you selling?\"] = {\n\t\t\t\tdialogue = \"Come take a look!\";\n\t\t\t};\n\n\t\t\t[\"Goodbye\"] = {};\n\t\t};\n\t})\n\t--]]\n\nlocal dialogueFrameUI = script.Parent.gameUI.dialogueFrame\nlocal uiCreator\nlocal textService \t\t= game:GetService(\"TextService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal itemLookup = require(replicatedStorage.itemData)\nlocal modules = require(replicatedStorage.modules)\nlocal network = modules.load(\"network\")\nlocal mapping = modules.load(\"mapping\")\nlocal levels = modules.load(\"levels\")\nlocal util = modules.load(\"utilities\")\nlocal questUtil = modules.load(\"client_quest_util\")\nlocal localization = modules.load(\"localization\")\n\nlocal acceptButtonConnection\n\nlocal globalOnClose\n\nfunction module.init(Modules)\n\n\tlocal utilities = {}\n\tutilities.network = network\n\tutilities.utilities = util\n\tutilities.levels = levels\n\tutilities.quest_util = questUtil\n\tutilities.mapping = mapping\n\n\t-- ensure dialogue handler is added to modules table\n\tuiCreator = Modules.uiCreator\n\n\tlocal function inputUpdate()\n\t\tdialogueFrameUI.UIScale.Scale = Modules.input.menuScale or 1\n\t\tif Modules.input.mode.Value == \"mobile\" then\n\t\t\tdialogueFrameUI.Position = UDim2.new(0.5, 0,1, -20)\n\t\telse\n\t\t\tdialogueFrameUI.Position = UDim2.new(0.5, 0,1, -140)\n\t\tend\n\tend\n\n\tlocal responseOptionTemplate = dialogueFrameUI:WaitForChild(\"responseOption\")\n\n\tlocal dialogueHandler = {}\n\t\tdialogueHandler.events = {}\n\n\tlocal function getDialogueDataById(dialogData, dialogDataId, extraData)\n\n\t\tif dialogData.id == dialogDataId then\n\t\t\treturn dialogData\n\t\tend\n\n\t\tprint(\"$\",dialogData.id,dialogDataId,extraData)\n\n\t\tif dialogData.options then\n\n\t\t\tlocal dialogOptions = dialogData.options\n\t\t\tif type(dialogOptions) == \"function\" then\n\t\t\t\tdialogOptions = dialogOptions(utilities, extraData)\n\t\t\tend\n\n\t\t\tif type(dialogOptions) == \"table\" and #dialogOptions > 0 then\n\t\t\t\tfor _, v in pairs(dialogData.options) do\n\t\t\t\t\tlocal dialog = getDialogueDataById(v, dialogDataId, extraData)\n\t\t\t\t\tif dialog then\n\t\t\t\t\t\treturn dialog\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\treturn nil\n\tend\n\n\n\tfunction dialogueHandler:moveToId(dialogDataId, extraData)\n\n\n\t\tlocal newDialogueData = getDialogueDataById(superRootDialogueData, dialogDataId, extraData)\n\n\t\tif newDialogueData then\n\n\t\t\t-- stop any ongoing dialogue\n\t\t\tdialogueHandler:stopDialogue()\n\n\t\t\t-- set dialogueData to new dialogueData\n\t\t\tdialogueHandler:setDialogue(newDialogueData, extraData)\n\n\t\t\t-- show new dialogueData root\n\t\t\tdialogueHandler:startDialogue()\n\t\tend\n\n\tend\n\n\tnetwork:create(\"dialogueMoveToId\", \"BindableFunction\", \"OnInvoke\", function(id, extraData)\n\t\tdialogueHandler:moveToId(id, extraData)\n\tend)\n\n\n\t-- update me\n\tlocal function getPlayerQuestStateByQuestId(questId)\n\t\tlocal quests = network:invoke(\"getCacheValueByNameTag\", \"quests\")\n\n\t\tfor _, playerQuestData in pairs(quests.active) do\n\t\t\tif playerQuestData.id == questId then\n\n\t\t\t\tlocal objectiveSteps = 0\n\t\t\t\tlocal objectiveStepsDone = 0\n\n\t\t\t\t-- hacky fix for now but at least it wont break\n\t\t\t\tif playerQuestData.currentObjective > #playerQuestData.objectives then\n\t\t\t\t\treturn mapping.questState.completed\n\t\t\t\tend\n\n\t\t\t\tfor _, playerStepData in pairs(playerQuestData.objectives[playerQuestData.currentObjective].steps) do\n\t\t\t\t\tobjectiveSteps = objectiveSteps + 1\n\t\t\t\t\tif playerStepData.requirement.amount <= playerStepData.completion.amount then\n\t\t\t\t\t\tobjectiveStepsDone = objectiveStepsDone + 1\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif objectiveStepsDone > 0 and objectiveStepsDone == objectiveSteps and playerQuestData.objectives[playerQuestData.currentObjective].started then\n\t\t\t\t\treturn mapping.questState.objectiveDone\n\t\t\t\telse\n\n\t\t\t\t\tif playerQuestData.objectives[playerQuestData.currentObjective].started then\n\t\t\t\t\t\treturn mapping.questState.active\n\t\t\t\t\telse\n\t\t\t\t\t\treturn mapping.questState.unassigned\n\t\t\t\t\tend\n\n\t\t\t--\t\treturn mapping.questState.active\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tfor _, completePlayerQuestData in pairs(quests.completed) do\n\t\t\tif completePlayerQuestData.id == questId then\n\t\t\t\treturn mapping.questState.completed\n\t\t\tend\n\t\tend\n\n\t\treturn mapping.questState.unassigned\n\tend\n\n\tlocal function getPlayerQuestObjectiveByQuestId(questId)\n\t\tlocal quests = network:invoke(\"getCacheValueByNameTag\", \"quests\")\n\n\t\tfor _, playerQuestData in pairs(quests.active) do\n\t\t\tif playerQuestData.id == questId then\n\t\t\t\treturn playerQuestData.currentObjective\n\t\t\tend\n\t\tend\n\t\treturn 1\n\tend\n\n\tlocal function checkIfObjectiveStartedByQuestId(questId)\n\t\tlocal quests = network:invoke(\"getCacheValueByNameTag\", \"quests\")\n\n\t\tfor _, playerQuestData in pairs(quests.active) do\n\t\t\tif playerQuestData.id == questId then\n\n\t\t\t\treturn playerQuestData.objectives[playerQuestData.currentObjective].started\n\n\t\t\t\t--return playerQuestData.objectives[playerQuestData.currentObjective].started\n\t\t\tend\n\t\tend\n\t\treturn true\n\tend\n\n\tfunction dialogueHandler:clearEvents()\n\t\tfor _, v in pairs(dialogueHandler.events) do\n\t\t\tv:disconnect()\n\t\tend\n\n\t\tdialogueHandler.events = {}\n\tend\n\n\tfunction dialogueHandler:navigateCurrentDialogueData(response)\n\t\tlocal dialogue = self.CurrentDialogueData\n\t\tassert(dialogue and dialogue.options and dialogue.options[response], \"invalid dialogue option\")\n\t\tself.isPlayingDialogue = false\n\t\tself:clearEvents()\n\t\tself:setDialogue(dialogue.options[response])\n\t\t-- update the new dialogue to match current data\n\t\tself:startDialogue()\n\tend\n\n\tfunction dialogueHandler:stopDialogue()\n\t\tself.isPlayingDialogue = false\n\t\tself:clearEvents()\n\n\n\t\tdialogueFrameUI.Visible = true\n\t\tModules.focus.toggle(dialogueFrameUI)\n\n\t\t-- hide the UI\n--\t\tdialogueFrameUI.Visible = false\n\tend\n\n\tfunction dialogueHandler:setDialogue(dialogueData, extraData)\n\t\tself.rootDialogueData \t\t= dialogueData\n\t\tself.currentDialogueData \t= dialogueData\n\t\tself.extraData\t\t\t\t= extraData\n\t\tself.previousDialogueData \t= nil\n\tend\n\n\n\tfunction dialogueHandler:acceptQuestRewardsButtonActivated(npcName)\n\t\tlocal success = network:invokeServer(\"playerRequest_submitQuest\", dialogueHandler.questData.id, self.questData.objectives[self.currentQuestObjective].handerNpcName)\n\t\tif success then\n\t\t\tif dialogueHandler.questData.objectives[self.currentQuestObjective].localOnFinish then\n\t\t\t\tdialogueHandler.questData.objectives[self.currentQuestObjective].localOnFinish(utilities, self.extraData)\n\t\t\tend\n\t\t\tutilities.utilities.playSound(\"questTurnedIn\")\n\t\tend\n\n\t\tmodule.endDialogue()\n\t\tModules.interaction.stopInteract()\n\tend\n\n\tdialogueFrameUI.cancel.Activated:Connect(function()\n\t\tmodule.endDialogue()\n\t\tModules.interaction.stopInteract()\n\tend)\n\n\tfunction dialogueHandler:startDialogue(dialogueNumber, questResponseType)\n\t\tif not self.currentDialogueData then return end\n\n\t\tdialogueFrameUI.UIScale.Scale = 1\n\n\t\tif self.currentDialogueData.onClose then\n\t\t\tglobalOnClose = self.currentDialogueData.onClose\n\t\tend\n\n\t\tlocal body = self.currentDialogueData.Parent and self.currentDialogueData.Parent.Parent\n\t\tif body and body:FindFirstChild(\"AnimationController\") then\n\t\t\tnetwork:invoke(\"lockCameraTarget\", body)\n\t\tend\n\n\t\tif self.currentDialogueData.sound and self.speaker and self.speaker.PrimaryPart and (dialogueNumber == nil or dialogueNumber <= 1) then\n\t\t\tutilities.utilities.playSound(self.currentDialogueData.sound, self.speaker.PrimaryPart)\n\t\tend\n\n\t\t-- show the UI\n\n\t\tlocal yOffset_dialogue \t\t= 30\n\t\tdialogueNumber \t\t\t\t= (dialogueNumber and dialogueNumber > 1) and tostring(dialogueNumber) or \"\"\n\t\tlocal trueDialogueNumber \t= dialogueNumber == \"\" and 1 or tonumber(dialogueNumber)\n\n\n\t\t-- update speaker title\n\t\tif self.currentDialogueData.speakerName or self.speaker then\n\t\t\tlocal speakerText \t\t= (self.speaker and self.speaker.Name or self.currentDialogueData.speakerName) or \"Someone messed up.\"\n\t\t\tlocal speakerTextSize \t= textService:GetTextSize(speakerText, dialogueFrameUI.titleFrame.title.TextSize, dialogueFrameUI.titleFrame.title.Font, Vector2.new())\n\n\t\t\tdialogueFrameUI.titleFrame.title.Text \t= speakerText\n\t\t\tdialogueFrameUI.titleFrame.Size \t\t= UDim2.new(0, speakerTextSize.X + 20, 0, 32)\n\t\tend\n\n\t\t-- clear text\n\t\tdialogueFrameUI.contents.dialogue:ClearAllChildren()\n\n\t\t--for i, v in pairs(self.currentDialogueData) do\n\t\t--end\n\n\t\t-- update text\n\t\tlocal isOnLastDialogue = true\n\n\t\tlocal objective\n\t\tif self.currentQuestObjective then\n\t\t\tobjective = self.currentQuestObjective\n\t\tend\n\n\t\t-- \"dialogue\" .. dialogueNumber .. (self.questState and \"_\" .. mapping.getMappingByValue(\"questState\", self.questState) .. (questResponseType and \"_\" .. questResponseType or \"\") or \"\")\n\t\t--\"dialogue\" .. dialogueNumber .. (self.questState and \"_\" .. mapping.getMappingByValue(\"questState\", self.questState) .. ( (questResponseType and \"_\" .. questResponseType and (objective and \"_\" .. objective or \"\")) or (objective and \"_\" .. objective) or \"\") or \"\")\n\t\tlocal target \t\t\t= \"dialogue\" .. dialogueNumber .. (self.questState and \"_\" .. mapping.getMappingByValue(\"questState\", self.questState) .. (questResponseType and \"_\" .. questResponseType or \"\") .. (objective and \"_\"..objective or \"\") or \"\")\n\t\tlocal targetDialogue \t= self.currentDialogueData[target]\n\n\t\t-- CONVERT FUNCTION TO TEXT\n\t\tif type(targetDialogue) == \"function\" then\n\t\t\ttargetDialogue = targetDialogue(utilities, self.extraData)\n\t\tend\n\n\t\tif typeof(targetDialogue) == \"string\" then\n\t\t\ttargetDialogue = localization.translate(targetDialogue, dialogueFrameUI.contents.dialogue)\n\t\t\ttargetDialogue = localization.convertToVesteriaDialogueTable(targetDialogue)\n\t\tend\n\n\t\tif targetDialogue then\n\t\t\tlocal dialogueData = targetDialogue\n\t\t\t-- dialogueText, yOffset\n\t\t\tlocal _, yOffset = uiCreator.createTextFragmentLabels(dialogueFrameUI.contents.dialogue, dialogueData)\n\n\t\t\tdialogueFrameUI.contents.dialogue.Size = UDim2.new(1, 0, 0, yOffset + 18)\n\t\t\tyOffset_dialogue = yOffset_dialogue + yOffset + 18\n\n\t\t\tif self.currentDialogueData[\"dialogue\" .. trueDialogueNumber + 1] then\n\t\t\t\tisOnLastDialogue = false\n\t\t\tend\n\t\tend\n\n\t\t-- clear responses\n\t\tdialogueFrameUI.contents.options:ClearAllChildren()\n\n\t\tyOffset_dialogue \t\t\t\t\t\t\t= yOffset_dialogue + 10\n\t\tdialogueFrameUI.contents.options.Position \t= UDim2.new(0, 0, 0, yOffset_dialogue)\n\n\t\tlocal responseButtonCount = 0\n\t\tlocal xOffset_options, yOffset_options = 0, 0\n\t\t-- easy function to create responseButtons\n\t\tlocal function createResponseButton(textToDisplay, dialogueData, questState, questData, buttonColor3, callback, curQuestObjective, isQuestAcceptB)\n\t\t\tlocal responseOptionSize = textService:GetTextSize(textToDisplay, responseOptionTemplate.inner.TextSize, responseOptionTemplate.inner.Font, Vector2.new())\n\n\t\t\tlocal responseOption = responseOptionTemplate:Clone()\n\n\t\t\tif textToDisplay == \"X\" then\n\t\t\t\tresponseOptionSize = Vector2.new(10,0)\n\t\t\t\tresponseOption.inner.Text = \"X\"\n\t\t\t\tresponseOption.inner.Font = Enum.Font.SourceSansBold\n\t\t\tend\n\n\t\t\tif typeof(textToDisplay) == \"string\" then\n\t\t\t\ttextToDisplay = localization.translate(textToDisplay, dialogueFrameUI.contents.options)\n\t\t\tend\n\n\t\t\tif xOffset_options + (responseOptionSize.X + 20) > dialogueFrameUI.contents.options.AbsoluteSize.X then\n\t\t\t\txOffset_options = 0\n\t\t\t\tyOffset_options = yOffset_options + 42\n\t\t\tend\n\n\t\t\tresponseOption.inner.Text = textToDisplay\n\t\t\tresponseOption.Size = UDim2.new(0, responseOptionSize.X + 30, 0, 42)\n\t\t\tresponseOption.Position = UDim2.new(0, xOffset_options, 0, yOffset_options)\n\t\t\tresponseOption.Visible = true\n\t\t\tresponseOption.Parent = dialogueFrameUI.contents.options\n\n\t\t\tlocal questResponseType\n\t\t\tif dialogueData then\n\t\t\t\tlocal objective = \"\"\n\t\t\t\tif self.currentQuestObjective then\n\t\t\t\t\tobjective = \"_\"..self.currentQuestObjective\n\t\t\t\tend\n\n\t\t\t\tif dialogueData.responseButtonColor then\n\t\t\t\t\tresponseOption.ImageColor3 = dialogueData.responseButtonColor\n\t\t\t\telseif dialogueData.onSelected then\n\t\t\t\t\tresponseOption.ImageColor3 = Color3.fromRGB(255, 210, 29)\n\t\t\t\telseif textToDisplay == dialogueData[\"response_unassigned_accept\"..objective] then\n\t\t\t\t\tresponseOption.ImageColor3 = Color3.fromRGB(150, 255, 150)\n\t\t\t\t\tquestResponseType = \"accept\"\n\t\t\t\telseif textToDisplay == dialogueData[\"response_unassigned_decline\"..objective] then\n\t\t\t\t\tresponseOption.ImageColor3 = Color3.fromRGB(255, 150, 150)\n\t\t\t\t\tquestResponseType = \"decline\"\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif buttonColor3 then\n\t\t\t\tresponseOption.ImageColor3 = buttonColor3\n\t\t\tend\n\n\t\t\ttable.insert(self.events, callback and responseOption.Activated:connect(callback) or responseOption.Activated:connect(function()\n\t\t\t\tif dialogueData then\n\t\t\t\t\tif questState and questData and curQuestObjective then\n\t\t\t\t\t\tself.questState = questState\n\t\t\t\t\t\tself.questData \t= questData\n\t\t\t\t\t\tself.currentQuestObjective = curQuestObjective\n\t\t\t\t\tend\n\n\t\t\t\t\tif dialogueData.onSelected then\n\t\t\t\t\t\tdialogueData.onSelected()\n\t\t\t\t\tend\n\n\t\t\t\t\tif questResponseType == \"accept\" then\n\t\t\t\t\t\t-- request server to add quest!\n\t\t\t\t\t\tlocal success = network:invokeServer(\"playerRequest_grantQuestToPlayer\", self.questData.id, self.questData.objectives[self.currentQuestObjective].giverNpcName)\n\n\t\t\t\t\t\tif success then\n\t\t\t\t\t\t\tnetwork:fire(\"displayQuestInQuestLog\", self.questData.id)\n\t\t\t\t\t\t\tif self.questData.objectives[self.currentQuestObjective].clientOnAcceptQuest then\n\t\t\t\t\t\t\t\tself.questData.objectives[self.currentQuestObjective].clientOnAcceptQuest(utilities, self.extraData)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\n\n\t\t\t\t\t-- stop any ongoing dialogue\n\t\t\t\t\tdialogueHandler:stopDialogue()\n\n\t\t\t\t\t-- set dialogueData to new dialogueData\n\t\t\t\t\tdialogueHandler:setDialogue(dialogueData)\n\n\t\t\t\t\t-- show new dialogueData root\n\t\t\t\t\tdialogueHandler:startDialogue(nil, questResponseType)\n\t\t\t\telse\n\t\t\t\t\tif isQuestAcceptB then -- moved in here\n\t\t\t\t\t\t\tlocal success = network:invokeServer(\"playerRequest_submitQuest\", dialogueHandler.questData.id, self.questData.objectives[self.currentQuestObjective].handerNpcName)\n\t\t\t\t\t\t\tif success then\n\t\t\t\t\t\t\t\tif dialogueHandler.questData.objectives[self.currentQuestObjective].localOnFinish then\n\t\t\t\t\t\t\t\t\tdialogueHandler.questData.objectives[self.currentQuestObjective].localOnFinish(utilities, self.extraData)\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tutilities.utilities.playSound(\"questTurnedIn\")\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tmodule.endDialogue()\n\t\t\t\t\t\t\tModules.interaction.stopInteract()\n\n\t\t\t\t\tend\n\t\t\t\t\tmodule.endDialogue()\n\t\t\t\t\tModules.interaction.stopInteract()\n\t\t\t\tend\n\t\t\tend))\n\n\t\t\tresponseButtonCount = responseButtonCount + 1\n\n\t\t\txOffset_options = xOffset_options + responseOptionSize.X + 20 + 5\n\t\tend\n\n\t\tlocal dialogOptions = self.currentDialogueData.options\n\n\t\t-- prevent the function from having cache behavior\n\t\tif self.currentDialogueData.optionsFunction then\n\t\t\tdialogOptions = self.currentDialogueData.optionsFunction(utilities, self.extraData)\n\t\tend\n\n\t\tif type(dialogOptions) == \"function\" then\n\t\t\tself.currentDialogueData.optionsFunction = dialogOptions\n\t\t\tdialogOptions = dialogOptions(utilities, self.extraData)\n\t\tend\n\n\t\tself.currentDialogueData.options = dialogOptions\n\t\tdialogueFrameUI.bottom.Visible = false\n\n\t\t-- update responses\n\n\t\t-- accept rewards\n\t\tif self.questState == mapping.questState.objectiveDone or self.questState == mapping.questState.handing then\n\t\t\t-- show di!\n\t\t\tif self.questData then\n\t\t\t\tfor _, obj in pairs(dialogueFrameUI.contents.rewards.contents:GetChildren()) do\n\t\t\t\t\tif not obj:IsA(\"UIListLayout\") then\n\t\t\t\t\t\tobj:Destroy()\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tlocal curObj = self.currentQuestObjective\n\t\t\t\tlocal rewards   = self.questData.objectives[curObj].rewards\n\t\t\t\tlocal goldMulti = self.questData.objectives[curObj].goldMulti\n\t\t\t\tlocal expMulti = self.questData.objectives[curObj].expMulti\n\t\t\t\tlocal level = self.questData.objectives[curObj].level\n\n\t\t\t\tfor i, inventoryTransferData_intermediate in pairs(rewards) do\n\t\t\t\t\tlocal itemBaseData = itemLookup[inventoryTransferData_intermediate.id]\n\t\t\t\t\tlocal amount = inventoryTransferData_intermediate.stacks\n\n\t\t\t\t\tif itemBaseData then\n\t\t\t\t\t\tlocal itemLine_quest \t= dialogueFrameUI.itemLine_quest:Clone()\n\t\t\t\t\t\titemLine_quest.AutoLocalize = false\n\n\t\t\t\t\t\tlocal itemName = localization.translate(itemBaseData.name, dialogueFrameUI.contents.rewards)\n\t\t\t\t\t\titemLine_quest.Text \t= itemName .. (amount and \" x\"..amount or \"\")\n\n\t\t\t\t\t\titemLine_quest.preview.Image = itemBaseData.image\n\n\t\t\t\t\t\titemLine_quest.Parent = dialogueFrameUI.contents.rewards.contents\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tlocal expText = dialogueFrameUI.contents.rewards.chest.exp\n\t\t\t\texpText.Visible = false\n\t\t\t\tif (expMulti or 1) > 0 then\n--\t\t\t\t\tlocal itemLine_quest \t= dialogueFrameUI.itemLine_quest:Clone()\n\t\t\t\t\texpText.Visible = true\n\t\t\t\t\texpText.Text = \"+ \"..math.floor(levels.getQuestEXPFromLevel(level or 1) * (expMulti or 1)) .. \" EXP\"\n\n--\t\t\t\t\titemLine_quest.preview.Image = \"\"\n\n--\t\t\t\t\titemLine_quest.Parent = dialogueFrameUI.contents.rewards.contents\n\t\t\t\tend\n\n\t\t\t\tif (goldMulti or 1) > 0 then\n\t\t\t\t\tlocal reward = levels.getQuestGoldFromLevel(level or 1) * (goldMulti or 1)\n\n\t\t\t\t\t--[[\n\t\t\t\t\tlocal itemLine_quest \t= dialogueFrameUI.itemLine_quest:Clone()\n\t\t\t\t\titemLine_quest.Text \t= levels.getQuestGoldFromLevel(self.questData.level or 1) * (self.questData.goldMulti or 1) .. \" Gold\"\n\n\t\t\t\t\titemLine_quest.preview.Image = \"\"\n\n\t\t\t\t\titemLine_quest.Parent = dialogueFrameUI.contents.rewards.contents\n\t\t\t\t\t]]\n\t\t\t\t\tlocal itemLine_quest = dialogueFrameUI.itemLine_money:clone()\n\t\t\t\t\tModules.money.setLabelAmount(itemLine_quest, reward)\n\t\t\t\t\titemLine_quest.Parent = dialogueFrameUI.contents.rewards.contents\n\t\t\t\tend\n\n\t\t\t\tlocal tYSize_rewards = 0\n\t\t\t\tfor i, obj in pairs(dialogueFrameUI.contents.rewards.contents:GetChildren()) do\n\t\t\t\t\tif obj:IsA(\"GuiObject\") and obj.Visible then\n\t\t\t\t\t\ttYSize_rewards = tYSize_rewards + 29\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tdialogueFrameUI.contents.rewards.contents.Size \t= UDim2.new(1, -15, 0, tYSize_rewards)\n\t\t\t\tdialogueFrameUI.contents.rewards.Size \t\t\t= UDim2.new(1, 0, 0, tYSize_rewards + 18 + 24)\n\t\t\tend\n\t\t\tdialogueFrameUI.contents.rewards.Visible = true\n\t\t\tdialogueFrameUI.contents.taxi.Visible = false\n\n\t\t\t--(textToDisplay, dialogueData, questState, questData)\n\t\t\tcreateResponseButton(\"Accept Rewards\", nil, nil, nil, Color3.fromRGB(85, 255, 76), nil, nil, true)\n\t\t\tcreateResponseButton(\"X\")\n\n\t\t\tdialogueFrameUI.accept.Visible = true\n\t\t\tif acceptButtonConnection then\n\t\t\t\tacceptButtonConnection:Disconnect()\n\t\t\t\tacceptButtonConnection = nil\n\t\t\tend\n\t\t\t-- ugly ugly hack -ber\n\t\t\tacceptButtonConnection = dialogueFrameUI.accept.Activated:connect(function()\n\t\t\t\tself:acceptQuestRewardsButtonActivated(self.questData.objectives[self.currentQuestObjective].handerNpcName)\n\t\t\tend)\n\t\t\t--dialogueFrameUI.cancel.Visible\t\t\t\t= true\n\t\telseif self.currentDialogueData.taxiMenu then\n\t\t\tdialogueFrameUI.accept.Visible \t\t\t\t\t= false\n\t\t\tdialogueFrameUI.cancel.Visible\t\t\t\t= false\n--\t\t\tcreateResponseButton(\"X\")\n\n\t\t\tdialogueFrameUI.contents.rewards.Visible = false\n\t\t\tdialogueFrameUI.contents.taxi.Visible = true\n\t\telse\n\t\t\tdialogueFrameUI.accept.Visible \t\t\t\t\t= false\n\t\t\tdialogueFrameUI.cancel.Visible\t\t\t\t= false\n\n\t\t\tdialogueFrameUI.contents.rewards.Visible = false\n\t\t\tdialogueFrameUI.contents.taxi.Visible = false\n\t\t\tdialogueFrameUI.contents.rewards.contents.Size \t= UDim2.new(0, 0, 0, 0)\n\t\t\tdialogueFrameUI.contents.rewards.Size \t\t\t= UDim2.new(0, 0, 0, 0)\n\n\t\t\tif self.currentDialogueData.options then\n\t\t\t\t-- iterate through all responses and create buttons\n\t\t\t\tfor i, dialogueData in pairs(self.currentDialogueData.options) do\n\t\t\t\t\tlocal doSkipShowing \t\t= false\n\t\t\t\t\tlocal isQuestDecisionPrompt = false\n\t\t\t\t\tlocal textToDisplay\n\t\t\t\t\tlocal questState\n\t\t\t\t\tlocal questData\n\t\t\t\t\tlocal currentQuestObjective\n\n\t\t\t\t\tif dialogueData.questId then\n\t\t\t\t\t\tquestData = questLookup[dialogueData.questId]\n\n\t\t\t\t\t\tif questData and questData.dialogueData then\n\t\t\t\t\t\t\tdialogueData \t= questData.dialogueData\n\t\t\t\t\t\t\tquestState \t\t= getPlayerQuestStateByQuestId(questData.id)\n\t\t\t\t\t\t\tcurrentQuestObjective = getPlayerQuestObjectiveByQuestId(questData.id)\n\n\t\t\t\t\t\t\tlocal objectiveStarted = checkIfObjectiveStartedByQuestId(questData.id)\n\n\t\t\t\t\t\t\tlocal objectiveName = questData.objectives[currentQuestObjective].objectiveName\n\n\n\n\t\t\t\t\t\t\t-- for multiple npc handling\n\n\t\t\t\t\t\t\tlocal flagForQuest = self.currentDialogueData.flagForQuest\n\t\t\t\t\t\t\tif type(flagForQuest) == \"function\" then\n\t\t\t\t\t\t\t\tflagForQuest = flagForQuest(utilities, self.extraData)\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tlocal currentObjectiveAndStarted = questUtil.getQuestObjectiveAndStarted(self.currentDialogueData.flagForQuest)\n\n\t\t\t\t\t\t\tlocal canContinue = true\n\n\n\t\t\t\t\t\t\tlocal objectiveChoiceTable = self.currentDialogueData.getObjectiveOptionsTable(utilities, self.extraData)\n\t\t\t\t\t\t\tif currentObjectiveAndStarted < 0 then\n\t\t\t\t\t\t\t\tif objectiveChoiceTable[currentObjectiveAndStarted *-1] == nil then\n\t\t\t\t\t\t\t\t\tcanContinue = false\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tif objectiveChoiceTable[currentObjectiveAndStarted] == nil then\n\t\t\t\t\t\t\t\t\tcanContinue = false\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\n\t\t\t\t\t\t\t-- objective unassigned logic\n\t\t\t\t\t\t\tif currentObjectiveAndStarted < 0 and canContinue then\n\n\t\t\t\t\t\t\t\tlocal actualObjective = currentObjectiveAndStarted * -1\n\n\t\t\t\t\t\t\t\tobjectiveChoiceTable = objectiveChoiceTable[actualObjective]\n\t\t\t\t\t\t\t\tfor i, choice in pairs(objectiveChoiceTable) do\n\t\t\t\t\t\t\t\t\tif choice.isStarterNPC ~= nil and not choice.isStarterNPC then\n\t\t\t\t\t\t\t\t\t\tcanContinue = false\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\telseif canContinue then\n\t\t\t\t\t\t\t\t-- objective handing logic\n\t\t\t\t\t\t\t\tobjectiveChoiceTable = objectiveChoiceTable[currentObjectiveAndStarted]\n\t\t\t\t\t\t\t\tfor i, choice in pairs(objectiveChoiceTable) do\n\t\t\t\t\t\t\t\t\tif choice.isHanderNPC ~= nil and not choice.isHanderNPC then\n\t\t\t\t\t\t\t\t\t\tcanContinue = false\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tend\n\n\n\n\n\t\t\t\t\t\t\tif canContinue then\n\n\n\n\n\n\t\t\t\t\t\t\t\tif questState == mapping.questState.completed then\n\t\t\t\t\t\t\t\t\tdoSkipShowing = true\n\t\t\t\t\t\t\t\telseif questState == mapping.questState.unassigned or not objectiveStarted then\n\t\t\t\t\t\t\t\t\ttextToDisplay = \"[Quest] \" .. objectiveName\n\t\t\t\t\t\t\t\telseif questState == mapping.questState.active then\n\t\t\t\t\t\t\t\t\ttextToDisplay = \"[In-progress] \" .. objectiveName\n\t\t\t\t\t\t\t\telseif questState == mapping.questState.handing or questState == mapping.questState.objectiveDone then\n\t\t\t\t\t\t\t\t\ttextToDisplay = \"[Complete] \" .. objectiveName\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\n\t\t\t\t\t\t-- check if the nextObjective has been started\n\n\t\t\t\t\t\tlocal forceDisplayQuest = false\n\t\t\t\t\t\tlocal currentObjective\n\t\t\t\t\t\tlocal fixed_response_unassinged = \"response_unassigned\"\n\t\t\t\t\t\tlocal fixed_response_unassinged_accept = \"response_unassigned_accept\"\n\t\t\t\t\t\tlocal fixed_response_unassinged_decline = \"response_unassigned_decline\"\n\t\t\t\t\t\tlocal fixed_response_active = \"response_active\"\n\t\t\t\t\t\tif self.questData and self.currentQuestObjective then\n\t\t\t\t\t\t\tforceDisplayQuest = not checkIfObjectiveStartedByQuestId(self.questData.id)\n\t\t\t\t\t\t\tcurrentObjective = self.currentQuestObjective\n\t\t\t\t\t\t\tfixed_response_unassinged = fixed_response_unassinged..\"_\"..currentObjective\n\t\t\t\t\t\t\tfixed_response_unassinged_accept = fixed_response_unassinged_accept..\"_\"..currentObjective\n\t\t\t\t\t\t\tfixed_response_unassinged_decline = fixed_response_unassinged_decline..\"_\"..currentObjective\n\t\t\t\t\t\t\tfixed_response_active = fixed_response_active..\"_\"..currentObjective\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif self.questState then\n\t\t\t\t\t\t\tif forceDisplayQuest then\n\t\t\t\t\t\t\t\tif dialogueData[fixed_response_unassinged_accept] and dialogueData[fixed_response_unassinged_decline] then\n\n\t\t\t\t\t\t\t\t\tisQuestDecisionPrompt = true\n\t\t\t\t\t\t\t\telseif dialogueData[fixed_response_unassinged] then\n\t\t\t\t\t\t\t\t\ttextToDisplay = dialogueData[fixed_response_unassinged]\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tdoSkipShowing = true\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\telseif self.questState == mapping.questState.handing then -- obsolete\n\t\t\t\t\t\t\t\ttextToDisplay = dialogueData.response_handing\n\t\t\t\t\t\t\telseif self.questState == mapping.questState.active then\n\t\t\t\t\t\t\t\ttextToDisplay = dialogueData[fixed_response_active]\n\t\t\t\t\t\t\telseif self.questState == mapping.questState.unassigned then\n\n\t\t\t\t\t\t\t\tif dialogueData[fixed_response_unassinged_accept] and dialogueData[fixed_response_unassinged_decline] then\n\n\t\t\t\t\t\t\t\t\tisQuestDecisionPrompt = true\n\t\t\t\t\t\t\t\telseif dialogueData[fixed_response_unassinged] then\n\t\t\t\t\t\t\t\t\ttextToDisplay = dialogueData[fixed_response_unassinged]\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tdoSkipShowing = true\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\telseif self.questState == mapping.questState.completed then\n\t\t\t\t\t\t\t\tdoSkipShowing = true\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\n\t\t\t\t\t\t\ttextToDisplay = dialogueData.response\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif not doSkipShowing and (textToDisplay or isQuestDecisionPrompt) then\n\t\t\t\t\t\tif isQuestDecisionPrompt then\n\n\t\t\t\t\t\t\tlocal acceptText = \"response_unassigned_accept\"\n\t\t\t\t\t\t\tlocal declineText = \"response_unassigned_decline\"\n\t\t\t\t\t\t\tif self.currentQuestObjective then\n\t\t\t\t\t\t\t\tacceptText = \"response_unassigned_accept\"..\"_\"..self.currentQuestObjective\n\t\t\t\t\t\t\t\tdeclineText = \"response_unassigned_decline\"..\"_\"..self.currentQuestObjective\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tcreateResponseButton(dialogueData[acceptText], dialogueData, questState, questData, nil, nil, currentQuestObjective)\n\t\t\t\t\t\t\tcreateResponseButton(dialogueData[declineText], dialogueData, questState, questData, nil, nil, currentQuestObjective)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tcreateResponseButton(textToDisplay, dialogueData, questState, questData, nil, nil, currentQuestObjective)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tdialogueFrameUI.contents.next.Visible = false\n\n\t\tif not isOnLastDialogue then\n\t\t\tdialogueFrameUI.contents.options.Visible = false\n\t\t\tdialogueFrameUI.contents.next.Visible = true\n\t\t\tdialogueFrameUI.contents.next.inner.Text = \"→\"\n\t\t\tdialogueFrameUI.contents.next.tooltip.Value = \"Next\"\n\t\t\ttable.insert(self.events, dialogueFrameUI.contents.next.Activated:connect(function()\n\t\t\t\tself:startDialogue(trueDialogueNumber + 1)\n\t\t\tend))\n\t\telseif responseButtonCount > 0 then\n\n\t\t\tdialogueFrameUI.contents.options.Visible = true\n\t\t\tdialogueFrameUI.contents.next.Visible = false\n\n\t\t\tif self.currentDialogueData.canExit and (not self.questState or (self.questState ~= mapping.questState.handing and self.questState ~= mapping.questState.objectiveDone)) then\n\t\t\t\tcreateResponseButton(\"X\")\n\t\t\tend\n\n\t\telseif self.currentDialogueData.moveToId then\n\n\t\t\tlocal newDialogueData = getDialogueDataById(superRootDialogueData, self.currentDialogueData.moveToId )\n\n\t\t\tif newDialogueData then\n\n\t\t\t\tdialogueFrameUI.contents.options.Visible = false\n\t\t\t\tdialogueFrameUI.contents.next.Visible = true\n\t\t\t\tdialogueFrameUI.contents.next.inner.Text = \"→\"\n\t\t\t\tdialogueFrameUI.contents.next.tooltip.Value = \"Next\"\n\t\t\t\t-- oh boy\n\t\t\t\ttable.insert(self.events, dialogueFrameUI.contents.next.Activated:connect(function()\n\n\t\t\t\t\t-- stop any ongoing dialogue\n\t\t\t\t\tdialogueHandler:stopDialogue()\n\n\t\t\t\t\t-- set dialogueData to new dialogueData\n\t\t\t\t\tdialogueHandler:setDialogue(newDialogueData)\n\n\t\t\t\t\t-- show new dialogueData root\n\t\t\t\t\tdialogueHandler:startDialogue(nil, questResponseType)\n\t\t\t\tend))\n\n\t\t\telse\n\t\t\t\tdialogueFrameUI.contents.options.Visible = false\n\t\t\t\tdialogueFrameUI.contents.next.Visible = true\n\t\t\t\tdialogueFrameUI.contents.next.inner.Text = \"X\"\n\t\t\t\tdialogueFrameUI.contents.next.tooltip.Value = \"Exit\"\n\t\t\t\ttable.insert(self.events, dialogueFrameUI.contents.next.Activated:connect(function()\n\t\t\t\t\tmodule.endDialogue()\n\t\t\t\t\tModules.interaction.stopInteract()\n\t\t\t\tend))\n\t\t\tend\n\t\telseif not self.questState or self.questState ~= mapping.questState.handing then\n\t\t\tdialogueFrameUI.contents.options.Visible = false\n\t\t\tdialogueFrameUI.contents.next.Visible = true\n\t\t\tdialogueFrameUI.contents.next.inner.Text = \"X\"\n\t\t\tdialogueFrameUI.contents.next.tooltip.Value = \"Exit\"\n\t\t\ttable.insert(self.events, dialogueFrameUI.contents.next.Activated:connect(function()\n\t\t\t\tmodule.endDialogue()\n\t\t\t\tModules.interaction.stopInteract()\n\t\t\tend))\n\t\tend\n\n\t\tyOffset_dialogue = yOffset_dialogue + yOffset_options\n\n\t\t-- update size!\n\t\tdialogueFrameUI.contents.options.Size = UDim2.new(1, 0, 0, yOffset_options + 42)\n\t\tdialogueFrameUI.bottom.Size = UDim2.new(1,0,0,yOffset_options + 52)\n\t\tdialogueFrameUI.bottom.Visible = true\n\n\t\tlocal tYSize = 0\n\t\tfor i, obj in pairs(dialogueFrameUI.contents:GetChildren()) do\n\t\t\tif obj:IsA(\"GuiObject\") and obj.Visible then\n\t\t\t\tlocal size = obj.Size.Y.Offset\n\t\t\t\tif size > 0 then\n\t\t\t\t\ttYSize = tYSize + size + dialogueFrameUI.contents.UIListLayout.Padding.Offset\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tdialogueFrameUI.Size = UDim2.new(0, 400, 0, tYSize + 36)\n--\t\tdialogueFrameUI.Position = UDim2.new(0.5, 0, 1, - (150 + dialogueFrameUI.Size.Y.Scale/2))\n\n\t\tdialogueFrameUI.Visible = false\n\n\t\tinputUpdate()\n\t\tModules.focus.toggle(dialogueFrameUI)\n\n\t\tif Modules.input.mode.Value == \"xbox\" and (game.GuiService.SelectedObject == nil or not game.GuiService.SelectedObject:IsDescendantOf(dialogueFrameUI)) then\n\t\t\tgame.GuiService.SelectedObject = Modules.focus.getBestButton(dialogueFrameUI.contents)\n\t\tend\n\n\tend\n\n\tfunction dialogueHandler:setSpeaker(speaker)\n\t\t-- body\n\n\t\tself.speaker = speaker\n\tend\n\n\n\n\n\tfunction module.beginDialogue(triggerPart, dialogueData)\n\t\tdialogueHandler.questState \t= nil\n\t\tdialogueHandler.questData \t= nil\n\n\t\t-- stop any ongoing dialogue\n\t\tdialogueHandler:stopDialogue()\n\n\t\t-- set dialogueData to new dialogueData\n\t\tdialogueHandler:setDialogue(dialogueData)\n\n\t\t-- set speaker if there\n\t\tif triggerPart then\n\t\t\tlocal rootPart = triggerPart.Parent:FindFirstChild(\"HumanoidRootPart\")\n\t\t\tif rootPart and rootPart.Parent:IsA(\"Model\") then\n--\t\t\t\tnetwork:invoke(\"lockCameraTarget\", triggerPart.Parent.HumanoidRootPart)\n\t\t\t\tlocal pos = rootPart.Position + rootPart.CFrame.lookVector * 3.5 + Vector3.new(0,1.75,0)\n\t\t\t\tlocal lookat = rootPart.Position + Vector3.new(0,1.25,0)\n\t\t\t\tlocal camCf = CFrame.new(pos, lookat)\n\t\t\t\tnetwork:invoke(\"lockCameraPosition\",camCf--[[,0.5]])\n\t\t\tend\n\n\t\t\tdialogueHandler:setSpeaker(triggerPart.Parent)\n\t\tend\n\n\t\tsuperRootDialogueData = dialogueData\n\n\n\n\t\t-- show new dialogueData root\n\t\tnetwork:invoke(\"setCharacterArrested\", true)\n\t\tdialogueHandler:startDialogue()\n\n\n\t\tfor i,child in pairs(dialogueFrameUI:GetDescendants()) do\n\t\t\tif child:IsA(\"TextLabel\") or child:IsA(\"TextButton\") then\n\t\t\t\tchild.TextScaled = false\n\t\t\t\tchild.TextWrapped = false\n\t\t\tend\n\t\tend\n\t\t--[[\n\t\tdialogueFrameUI.UIScale.Scale = 0.8\n\t\tModules.tween(dialogueFrameUI.UIScale, {\"Scale\"}, 1, 0.5, Enum.EasingStyle.Bounce)\n\t\t]]\n\tend\n\n\tnetwork:create(\"beginDialogue\",\"BindableFunction\",\"OnInvoke\",module.beginDialogue)\n\n\tfunction module.endDialogue()\n\n\t\tdialogueFrameUI.Visible = false\n\n\t\tif globalOnClose then\n\t\t\tglobalOnClose(utilities, dialogueHandler.extraData)\n\t\t\tglobalOnClose = nil\n\t\tend\n\n\t\tnetwork:invoke(\"lockCameraTarget\", nil)\n\n\t\tdialogueHandler:stopDialogue()\n\t\tsuperRootDialogueData = nil\n\t\tnetwork:invoke(\"setCharacterArrested\", false)\n\tend\nend\n\n\n\nreturn module"
  },
  {
    "path": "src/StarterGui/dyePreview.lua",
    "content": "-- Input prompt by Locard, modified by berezaa\n-- Imported from Miner's Haven\nlocal module = {}\n\nlocal runService = game:GetService(\"RunService\")\n\nlocal promptOut\nlocal promptFrame = script.Parent.gameUI.dyePreview\nlocal buttonCons = {}\nlocal currentDecision\n\nfunction module.forceClose()\n\tif promptOut then\n\t\tcurrentDecision = false\n\tend\nend\n\nfunction module.isPrompting()\n\treturn promptOut\nend\n\n-- moved under module.init\nfunction module.prompt(headerText)\n\t\nend\n\nmodule.PROMPT_EXPIRE_TIME = 30\n\nfunction module.init(Modules)\n\n\tlocal tween = Modules.tween\n\n\tpromptFrame.Visible = false\n\t\n\tlocal promptQueue = {}\n\t\n\tlocal currentPrompt\n\n\n\tlocal function getCenterOfMassOfModel(model)\n\t\t\n\t\tlocal validParts = {}\n\t\t\n\t\tif model:FindFirstChild(\"manifest\") then\n\t\t\ttable.insert(validParts, model.manifest)\n\t\tend\n\t\tfor i,child in pairs(model:GetChildren()) do\n\t\t\tif child:IsA(\"BasePart\") then\n\t\t\t\tchild.Color = Color3.new(1,1,1)\n\t\t\t\tchild.Transparency = 0.5\n\t\t\tend\n\t\t\tfor e,childchild in pairs(child:GetChildren()) do\n\t\t\t\t\n\t\t\t\tif childchild:IsA(\"BasePart\") then\n\t\t\t\t\ttable.insert(validParts, childchild)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\t\n\t\tlocal totalVotedPosition = Vector3.new()\n\t\tlocal totalVotes = 0\n\t\tfor i,part in pairs(validParts) do\n\t\t\tif part:IsA(\"BasePart\") then\n\t\t\t\tlocal center = part.Position\n\t\t\t\tlocal mass = part:GetMass()\n\t\t\t\ttotalVotedPosition = totalVotedPosition + (center * mass)\n\t\t\t\ttotalVotes = totalVotes + mass\n\t\t\tend\n\t\tend\n\t\treturn totalVotedPosition / totalVotes\n\tend\t\n\t\n\tlocal function displayPrompt(prompt)\n\t\tcurrentPrompt = prompt\n\n\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(30, 30, 30)\n\t\tpromptFrame.Visible = true\n\t\t\n\t\tpromptFrame.curve.yes.Visible = true\n\t\tpromptFrame.curve.no.Visible = true\t\t\n\t\t\n\t\tpromptFrame.curve.Visible = true\n\t\t\n\n\t\t\n\t\tpromptFrame.curve.title.Text = prompt.text\n\t\t\n--\t\tpromptFrame.curve.Position = UDim2.new(-1,0,0,0)\t\t\n--\t\ttween(promptFrame.curve, {\"Position\"}, UDim2.new(0,0,0,0), 0.6)\t\n\n\n\t\t\n\t\tlocal itemBaseData = prompt.itemBaseData\n\t\tlocal dyeItemData = prompt.dyeItemData\n\t\t\n\t\t\n\t\tpromptFrame.curve.before:ClearAllChildren()\n\t\tpromptFrame.curve.after:ClearAllChildren()\n\t\t\n\t\tlocal module = itemBaseData.module\n\t\t\n\t\t\n\t\tlocal container\n\t\tif module:FindFirstChild(\"manifest\") then\n\t\t\tcontainer = Instance.new(\"Model\")\n\t\t\tlocal primaryPart = module.manifest:Clone() \n\t\t\tprimaryPart.Parent = container\n\t\t\tcontainer.PrimaryPart = primaryPart\n\t\telseif module:FindFirstChild(\"container\") then\n\t\t\tcontainer = module.container:Clone()\t\n\t\tend\n\t\tcontainer.Parent = promptFrame.curve.before\n\t\t\n\t\t\n\t\tlocal modelExtents = container:GetExtentsSize()\n\t\t\n\t\tlocal centerOfMass = getCenterOfMassOfModel(container)\n\t\t\n\t\tlocal camera = Instance.new(\"Camera\")\n\t\tcamera.CFrame = CFrame.new( centerOfMass + modelExtents * Vector3.new(0.7,0,-0.7), centerOfMass)\n\t\tcamera.Parent = promptFrame.curve.before\n\t\tpromptFrame.curve.before.CurrentCamera = camera\n\t\t\n\t\t\n\t\tlocal afterContainer = container:Clone()\n\t\tafterContainer.Parent = promptFrame.curve.after\n\t\t\n\t\t\n\t\tlocal itemInventorySlotData = {id = itemBaseData.id}\n\t\tif dyeItemData.applyScroll(game.Players.LocalPlayer, itemInventorySlotData, true) then\n\t\t\tlocal dyeColor = itemInventorySlotData.dye\n\t\t\tif dyeColor then\n\t\t\t\tfor i,child in pairs(afterContainer:GetDescendants()) do\n\t\t\t\t\tif child:IsA(\"BasePart\") then\n\t\t\t\t\t\t-- Color3.new(v.Color.r * dye.r/255, v.Color.g * dye.g/255, v.Color.b * dye.b/255)\n\t\t\t\t\t\tchild.Color = Color3.new(child.Color.r * dyeColor.r/255, child.Color.g * dyeColor.g/255, child.Color.b * dyeColor.b/255)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\t\n\t\tlocal camera = Instance.new(\"Camera\")\n\t\tcamera.CFrame = CFrame.new( centerOfMass + modelExtents * Vector3.new(0.7,0,-0.7), centerOfMass)\n\t\tcamera.Parent = promptFrame.curve.after\n\t\tpromptFrame.curve.after.CurrentCamera = camera\t\t\n\t\t\n\t\t\n--[[\n\n\tapplyScroll = function(player, itemInventorySlotData, successfullyScrolled)\n\t\tlocal itemLookup = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"itemData\"))\n\t\tlocal itemBaseData = itemLookup[itemInventorySlotData.id]\n\t\t\n\t\tif itemBaseData.category == \"equipment\" then\n\t\t\tif successfullyScrolled then\n\t\t\t\t--85, 85, 85\n\t\t\t\titemInventorySlotData.dye = {r=85, g=85, b=85}\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\t\t\n\t\treturn false, \"Only equipment can be dyed\"\n\tend;\t\n\t\n--]]\t\t\n\t\t\n\t\n\tend\n\t\n\tlocal function addPrompt(prompt)\n\t\ttable.insert(promptQueue, prompt)\n\t\t\n\t\tif #promptQueue == 1 or promptQueue[1] == prompt then\n\t\t\tdisplayPrompt(prompt)\n\t\tend\n\t\t--[[\n\t\tfor i=1,3 do\n\t\t\tlocal flare = promptFrame.flare:Clone()\n\t\t\tflare.Name = \"flareCopy\"\n\t\t\tflare.Parent = promptFrame\n\t\t\tflare.Visible = true\n\t\t\tflare.Size = UDim2.new(1,4,1,4)\n\t\t\tflare.Position = UDim2.new(0,-2,0.5,0)\n\t\t\tflare.AnchorPoint = Vector2.new(0,0.5)\n\t\t\tlocal x = (180 - 40*i)\n\t\t\tlocal y = (14 - 2*i)\n\t\t\tlocal EndPosition = UDim2.new(0,-y/2,0.5,0)\n\t\t\tlocal EndSize = UDim2.new(1,x,1,y)\n\t\t\ttween(flare,{\"Position\",\"Size\",\"ImageTransparency\"},{EndPosition, EndSize, 1},0.5*i)\n\t\tend\t\t\n\t\t]]\t\n\tend\n\t\n\tlocal function makeUserResponse(userResponse)\n\t\tif currentPrompt then\n\t\t\t-- register the user's response\n\t\t\tcurrentPrompt.userResponse = userResponse\n\t\t\t-- remove the response from queue\n\t\t\tfor i,prompt in pairs(promptQueue) do\n\t\t\t\tif prompt == currentPrompt then\n\t\t\t\t\ttable.remove(promptQueue, i)\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\t\t\n\t\t\tcurrentPrompt = nil\t\n\n\t\t\t-- fancy transition out\t\t\t\n\t\t\tlocal transitionCurve = promptFrame.curve:Clone()\n\t\t\ttransitionCurve.Name = \"transition\"\n\t\t\ttransitionCurve.ZIndex = transitionCurve.ZIndex + 1\n\t\t\ttransitionCurve.Parent = promptFrame\n\t\t\t\n\t\t\ttween(transitionCurve, {\"Position\"}, UDim2.new(-1,-10,0,0), 0.6) \n\t\t\t\n\t\t\tgame.Debris:AddItem(transitionCurve, 0.6)\n\t\t\tspawn(function()\n\t\t\t\twait(0.6)\n\t\t\t\tif not currentPrompt then\n\t\t\t\t\tpromptFrame.Visible = false\n\t\t\t\tend\n\t\t\tend)\n\t\t\t\n\t\t\tpromptFrame.curve.Visible = false\n\t\t\t\n\t\t\t-- show the next response if applicable\n\t\t\tlocal nextPrompt = promptQueue[1]\n\t\t\tif nextPrompt then\n\t\t\t\tdisplayPrompt(nextPrompt)\n\t\t\tend\t\t\t\n\t\t\t\t\t\n\t\tend\n\tend\t\n\t\n\tfunction module.prompt(dyeItemData, itemBaseData)\n\t\t\n\t\tlocal promptStartTime = tick()\n\t\tlocal prompt = {text = \"Are you sure you want to apply \"..dyeItemData.name..\"?\"; timestamp = promptStartTime;dyeItemData = dyeItemData; itemBaseData = itemBaseData;}\n\t\taddPrompt(prompt)\n\t\t\n\t\t\n\t\trepeat wait() until prompt.userResponse ~= nil or tick() - promptStartTime >= module.PROMPT_EXPIRE_TIME\n\t\t\n\t\t-- remove the response from queue (if expired)\n\t\tif tick() - promptStartTime >= module.PROMPT_EXPIRE_TIME then\n\t\t\tif prompt == currentPrompt then\n\t\t\t\tmakeUserResponse(false)\n\t\t\telse\t\t\t\n\t\t\t\tfor i,prompt in pairs(promptQueue) do\n\t\t\t\t\tif prompt == currentPrompt then\n\t\t\t\t\t\ttable.remove(promptQueue, i)\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\t\t\t\t\t\n\t\t\tend\n\t\tend\n\t\t\n\t\treturn prompt.userResponse or false\n\t\t\n\tend\n\t\n\n\n\tpromptFrame.curve.no.MouseButton1Click:Connect(function()\n\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(30, 7, 8)\n\t\tpromptFrame.curve.yes.Visible = false\n\t\tpromptFrame.curve.no.Visible = false\n\t\tmakeUserResponse(false)\n\tend)\n\tpromptFrame.curve.yes.MouseButton1Click:Connect(function()\n\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(8, 30, 10)\n\t\tpromptFrame.curve.yes.Visible = false\n\t\tpromptFrame.curve.no.Visible = false\t\t\t\t\n\t\tmakeUserResponse(true)\t\n\tend)\t\n\t\n\t\n\t\n\t\n\t-- old stuff no one cares about:\n\t\n\tfunction module.prompt_old(headerText)\n\t\t\t\n\t\t\n\t\tif promptOut then\n\t\t\treturn false\n\t\tend\n\n\n\t\tpromptFrame.Position = UDim2.new(0.5,0,0.5,0)\n\t\t\n\t\t\n\t\t-- temp measure\n\t\t\n\t\tlocal function selfIsSelected()\n\t\t\tlocal obj = game:GetService(\"GuiService\").SelectedObject\n\t\t\t\n\t\t\tif obj then\n\t\t\t\treturn obj:IsDescendantOf(promptFrame)\n\t\t\telse\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\t\n\t\t\n\t\tpromptOut = true\n\t\t\n\t\tlocal transitionOut \n\t\t\n\t\t--First we initiate the prompt\n\n\t\tpromptFrame.curve.title.Text = headerText\n\t\t\n\t\tlocal con0 = promptFrame.curve.no.MouseButton1Click:Connect(function()\n\t\t\t--Modules.Menu.sounds.Click:Play()\n\t\t\tif promptOut then\n\t\t\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(30, 7, 8)\n\t\t\t\tpromptFrame.curve.yes.Visible = false\n\t\t\t\tpromptFrame.curve.no.Visible = false\n\t\t\t\tcurrentDecision = false\n\t\t\tend\n\t\tend)\n\t\tlocal con1 = promptFrame.curve.yes.MouseButton1Click:Connect(function()\n\t\t\t--Modules.Menu.sounds.Click:Play()\n\t\t\tif promptOut then\n\t\t\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(8, 30, 10)\n\t\t\t\tpromptFrame.curve.yes.Visible = false\n\t\t\t\tpromptFrame.curve.no.Visible = false\t\t\t\t\n\t\t\t\tcurrentDecision = true\n\t\t\tend\n\t\tend)\n\t\t\n\t\ttween(promptFrame.curve, {\"Position\"}, UDim2.new(0,0,0,0), 0)\n\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(30, 30, 30)\n\t\tpromptFrame.Visible = true\n\t\tpromptFrame.curve.yes.Visible = true\n\t\tpromptFrame.curve.no.Visible = true\t\t\n\t\tfor i=1,3 do\n\t\t\tlocal flare = promptFrame.flare:Clone()\n\t\t\tflare.Name = \"flareCopy\"\n\t\t\tflare.Parent = promptFrame\n\t\t\tflare.Visible = true\n\t\t\tflare.Size = UDim2.new(1,4,1,4)\n\t\t\tflare.Position = UDim2.new(0,-2,0.5,0)\n\t\t\tflare.AnchorPoint = Vector2.new(0,0.5)\n\t\t\tlocal x = (180 - 40*i)\n\t\t\tlocal y = (14 - 2*i)\n\t\t\tlocal EndPosition = UDim2.new(0,-y/2,0.5,0)\n\t\t\tlocal EndSize = UDim2.new(1,x,1,y)\n\t\t\ttween(flare,{\"Position\",\"Size\",\"ImageTransparency\"},{EndPosition, EndSize, 1},0.5*i)\n\t\tend\t\t\t\n\n\t\t--Transition the stuff into the screen\n\t\t--spawn(function()\n\t\t\t-- use my tween function nerd\n\t\t\t\n\n\n\t\t--promptFrame.Absorb.Visible = true\n\t\t--Modules.Menu.tween(promptFrame,{\"BackgroundTransparency\"}, .5, 0.7, Enum.EasingStyle.Quint)\n\t\t--Modules.Menu.tween(promptFrame.InputPrompt, {\"Position\"}, UDim2.new(0.5,0,0.5,0), 0.7, Enum.EasingStyle.Quint)\n\t\t\t\n\t\t\t--[[\n\t\t\t\n\t\t\tlocal startT = tick()\n\t\t\tfor i = 1,60*deltaT do\n\t\t\t\tif transitionOut then\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\t\tlocal now = tick() - startT\n\t\t\t\tlocal a = now/deltaT\n\t\t\t\tlocal inputFrameY = quint(-.5,.5,a)\n\t\t\t\tlocal bgTransparency = quint(1,.6,a)\n\t\t\t\tpromptFrame.BackgroundTransparency = bgTransparency\n\t\t\t\tpromptFrame.InputPrompt.Position = UDim2.new(0,0,inputFrameY,0)\n\t\t\t\trunService.Heartbeat:Wait()\n\t\t\tend\n\t\t\t]]\n\n\t\t--end)\n\t\t\n\t\t\n\t\t--Yield the thread until an answer pops up\t\n\t\trepeat\n\t\t\t--[[\n\t\t\tlocal Xbox = Modules.Input.mode.Value == \"Xbox\"\n\t\t\tif Xbox and not selfIsSelected() then\n\t\t\t\tModules.Focus.stealFocus(promptFrame.InputPrompt)\n\t\t\tend\n\t\t\t]]\n\t\t\trunService.Heartbeat:Wait()\n\t\tuntil currentDecision ~= nil\n\t\t\n\t\tlocal thisDecision = currentDecision\n\t\tcurrentDecision = nil\t\n\t\t\t\n\t\t--Disconnect the buttons\n\t\tcon0:Disconnect()\n\t\tcon1:Disconnect()\n\t\t\n\t\t--make prompt ready\n\t\tpromptOut = false\n\t\ttween(promptFrame.curve, {\"Position\"}, UDim2.new(-1,-10,0,0), 0.6)\n\t\tspawn(function()\n\t\t\twait(0.6)\n\t\t\tif not promptOut then\n\t\t\t\tpromptFrame.Visible = false\t\n\t\t\tend\n\t\tend)\n\t--\tspawn(function()\n\t\t\n\t--\tpromptFrame.Absorb.Visible = false\n\t--\tModules.Menu.tween(promptFrame,{\"BackgroundTransparency\"}, 1, 0.7, Enum.EasingStyle.Quint)\n\t--\tModules.Menu.tween(promptFrame.InputPrompt, {\"Position\"}, UDim2.new(0.5,0,0,-250), 0.7, Enum.EasingStyle.Quint)\t\t\t\n\t\t--[[\n\t\tlocal startT = tick()\n\t\tfor i = 1,60*deltaT do\n\t\t\tif promptOut then\n\t\t\t\tbreak\n\t\t\tend\n\t\t\tlocal now = tick() - startT\n\t\t\tlocal a = now/deltaT\n\t\t\tlocal inputFrameY = quint(.5,-.5,a)\n\t\t\tlocal bgTransparency = quint(.6,1,a)\n\t\t\tpromptFrame.BackgroundTransparency = bgTransparency\n\t\t\tpromptFrame.InputPrompt.Position = UDim2.new(0,0,inputFrameY,0)\n\t\t\trunService.Heartbeat:Wait()\n\t\tend\n\t\t]]\n\t--\tend)\n\t\n\t\t\n\n\t\treturn thisDecision\n\tend\t\n\t\n\t\n\nend\n\n\nreturn module"
  },
  {
    "path": "src/StarterGui/enchant.lua",
    "content": "--Enchantment ui by berezaa\n\nlocal module = {}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal modules = require(replicatedStorage.modules)\nlocal network = modules.load(\"network\")\nlocal utilities = modules.load(\"utilities\")\nlocal mapping = modules.load(\"mapping\")\nlocal enchantment = modules.load(\"enchantment\")\nlocal abilityLookup = require(replicatedStorage.abilityLookup)\n\n\nfunction module.dragItem(inventorySlotData)\n\nend\n\nlocal currentBall\n\n\nfunction module.init(Modules)\n\n\tlocal frame = script.Parent.gameUI.menu_enchant\n\n\tlocal tween = Modules.tween\n\tlocal localization = Modules.localization\n\n\tfunction module.close()\n\t\tframe.Visible = false\n\t\tModules.playerMenu.closeSelected()\n\t\t-- disgusting\n\t\tif frame.Parent.Parent.Visible then\n\t\t\tModules.focus.toggle(frame.Parent.Parent)\n\t\tend\n\tend\n\n\tlocal function emit(n, p, s)\n\n\t\tn = n or 1\n\t\tp = p or 1\n\t\tfor _ = 1, n do\n\n\t\t\tlocal s = s or math.random(1,3) == 2 and math.random(2,6)/2 or 1\n\n\t\t\tlocal sprite = frame.sprite:Clone()\n\t\t\tlocal start = UDim2.new(math.random(),0,1,0)\n\t\t\tsprite.Position = start\n\n\t\t\tlocal lifetime = math.random(40,150)/100\n\n\t\t\tsprite.Parent = frame.curve.sprites\n\n\n\n\t\t\tsprite.ImageTransparency = 1\n\t\t\tsprite.Size = UDim2.new(0,10,0,10)\n\t\t\tsprite.Visible = true\n\n\t\t\tlocal endPosition = UDim2.new(start.X.Scale, math.random(-50,50) * lifetime * p, start.Y.Scale, -math.random(20,200) * lifetime * p)\n\n\t\t\tlifetime = lifetime * s\n\n\t\t\tgame.Debris:AddItem(sprite, lifetime + 0.1)\n\n\t\t\ttween(sprite, {\"Position\"}, endPosition, lifetime)\n\n\n\n\t\t\ttween(sprite, {\"Size\"}, UDim2.new(0,20 * s,0,20 * s), lifetime/2)\n\t\t\tspawn(function()\n\t\t\t\twait(lifetime/2)\n\t\t\t\ttween(sprite, {\"Size\"}, UDim2.new(0,10,0,10), lifetime/2)\n\t\t\tend)\n\n\t\t\tlocal transparency = math.random(30,40)/100\n\n\t\t\ttween(sprite, {\"ImageTransparency\"}, { 1 - (1-transparency) / s^2 }, lifetime/4)\n\t\t\tspawn(function()\n\t\t\t\twait(lifetime * 3/4)\n\t\t\t\ttween(sprite, {\"ImageTransparency\"}, {1}, lifetime/4)\n\t\t\tend)\n\t\tend\n\tend\n\n\tspawn(function()\n\t\twhile wait(1) do\n\t\t\tfor i=1,15 do\n\t\t\t\tdelay(math.random(), function() emit() end)\n\t\t\tend\n\t\tend\n\tend)\n\n\tlocal enchantFrame = frame\n\n\tlocal debounce = false\n\n\tlocal currentAbilitySlotData\n\tlocal enchantItemSlotData\n\tlocal locationView\n\n\n\n\n\tfunction module.reset()\n\n\t\tfor i, enchantButton in pairs(enchantFrame.enchantments.contents:GetChildren()) do\n\t\t\tif enchantButton:IsA(\"GuiObject\") then\n\t\t\t\tenchantButton:Destroy()\n\t\t\tend\n\t\tend\n\n\t\ttween(enchantFrame.button, {\"ImageColor3\"}, {Color3.fromRGB(113, 113, 113)}, 0.5)\n\n\t\tenchantFrame.cost.Visible = false\n\n\t\tenchantFrame.input.equipItemButton.Image = \"rbxassetid://2528902611\"\n\t\tenchantFrame.input.equipItemButton.ImageTransparency = 0.6\n\t\tenchantFrame.input.equipItemButton.ImageColor3 = Color3.new(1,1,1)\n\t\tenchantFrame.input.shine.Visible = false\n\t\tenchantFrame.input.frame.Visible = false\n--\t\tenchantFrame.input.ImageTransparency = 0.4\n\n\t\tenchantFrame.output.equipItemButton.ImageTransparency = 1\n\t\tenchantFrame.output.frame.Visible = false\n\t\tenchantFrame.output.shine.Visible = false\n\t\tenchantFrame.output.ImageTransparency = 0.4\n\n\t\tcurrentAbilitySlotData = nil\n\t\tenchantItemSlotData = nil\n\t\tlocationView = nil\n\tend\n\n\tlocal currentButton\n\n\tlocal playerData\n\n\tfunction module.open(ball)\n\t\tcurrentBall = ball\n\t\tmodule.reset()\n\t\tModules.playerMenu.selectMenu(frame, Modules[script.Name])\n\t\tnetwork:fire(\"localSignal_enchantOpened\")\n\tend\n\n\n\tlocal enchantmentPairing = {}\n\tlocal selectedEnchantment\n\n\tlocal function fill(itemButton, abilitySlotData)\n\n\t\tcurrentButton = itemButton\n\n\t\tenchantmentPairing = {}\n\t\tselectedEnchantment = nil\n\n\t\tplayerData = network:invoke(\"getLocalPlayerDataCache\")\n\t\tlocal abilityBaseData = abilityLookup[abilitySlotData.id](playerData)\n\n\t\t-- OPTIONS ! :D\n\t\tfor i, enchantButton in pairs(enchantFrame.enchantments.contents:GetChildren()) do\n\t\t\tif enchantButton:IsA(\"GuiObject\") then\n\t\t\t\tenchantButton:Destroy()\n\t\t\tend\n\t\tend\n\n\t\titemButton.Image = abilityBaseData.image\n\n\t\titemButton.ImageColor3 = Color3.new(1,1,1)\n\n\t\titemButton.Parent.frame.Visible = true\n\t\titemButton.Parent.shine.Visible = true\n\t\titemButton.Parent.ImageTransparency = 0\n\t\titemButton.ImageTransparency = 0\n\n\t\tlocal metadata = abilityBaseData.metadata\n\t\tif metadata then\n\t\t\t-- your standard run of the mill upgrade\n\t\t\tif metadata.upgradeCost and metadata.maxRank then\n\t\t\t\tif abilitySlotData.rank < metadata.maxRank then\n\n\t\t\t\t\tlocal enchantButton = enchantFrame.enchantments.sampleItem:Clone()\n\t\t\t\t\tenchantButton.item.itemThumbnail.Image = abilityBaseData.image\n\t\t\t\t\tlocal abilityName = abilityBaseData.name and localization.translate(abilityBaseData.name) or \"???\"\n\t\t\t\t\tenchantButton.itemName.Text = abilityName .. \" +\" .. abilitySlotData.rank\n\t\t\t\t\tenchantButton.locked.Visible = false\n\t\t\t\t\tenchantButton.LayoutOrder = 1\n\t\t\t\t\tenchantButton.Parent = enchantFrame.enchantments.contents\n\t\t\t\t\tenchantButton.abilityPoints.amount.Text = metadata.upgradeCost .. \" AP\"\n\t\t\t\t\tenchantButton.Visible = true\n\t\t\t\t\tenchantmentPairing[enchantButton] = {id = abilitySlotData.id; request = \"upgrade\"}\n\n\t\t\t\t\tlocal function selected()\n\t\t\t\t\t\tif not enchantButton.locked.Visible then\n\t\t\t\t\t\t\tnetwork:invoke(\"populateItemHoverFrameWithAbility\",\n\t\t\t\t\t\t\t\tabilityBaseData, abilitySlotData.rank,\n\t\t\t\t\t\t\t\tabilityBaseData, abilitySlotData.rank + 1\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\tenchantButton.MouseEnter:connect(selected)\n\t\t\t\t\tenchantButton.SelectionGained:connect(selected)\n\t\t\t\t\tenchantButton.item.itemThumbnail.Active = false\n\n\n\t\t\t\t\tlocal function unselected()\n\t\t\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\t\t\t\t\tend\n\t\t\t\t\tenchantButton.MouseLeave:connect(unselected)\n\t\t\t\t\tenchantButton.SelectionLost:connect(unselected)\n\n\t\t\t\tend\n\t\t\tend\n\t\t\t-- variants\n\t\t\tif metadata.variants then\n\t\t\t\tif abilitySlotData.variant == nil or metadata.variants[abilitySlotData.variant].default then\n\t\t\t\t\tfor variantName, variant in pairs(metadata.variants) do\n\t\t\t\t\t\tif variant.cost and not variant.default then\n\t\t\t\t\t\t\tlocal abilityExecutionData = {variant = variantName}\n\t\t\t\t\t\t\tlocal variantBaseData = abilityLookup[abilitySlotData.id](nil, abilityExecutionData)\n\t\t\t\t\t\t\tif variantBaseData then\n\t\t\t\t\t\t\t\tlocal enchantButton = enchantFrame.enchantments.sampleItem:Clone()\n\t\t\t\t\t\t\t\tenchantButton.item.itemThumbnail.Image = variantBaseData.image\n\t\t\t\t\t\t\t\tlocal abilityName = variantBaseData.name and localization.translate(variantBaseData.name) or \"???\"\n\t\t\t\t\t\t\t\tenchantButton.itemName.Text = abilityName .. (abilitySlotData.rank > 1 and (\" +\" .. (abilitySlotData.rank - 1)) or \"\")\n\t\t\t\t\t\t\t\tenchantButton.abilityPoints.amount.Text = variant.cost .. \" AP\"\n\t\t\t\t\t\t\t\tif variant.requirement and variant.requirement(playerData) then\n\t\t\t\t\t\t\t\t\tenchantButton.locked.Visible = false\n\t\t\t\t\t\t\t\t\tenchantButton.LayoutOrder = 3\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tenchantButton.locked.Visible = true\n\t\t\t\t\t\t\t\t\tenchantButton.LayoutOrder = 5\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tenchantButton.Parent = enchantFrame.enchantments.contents\n\t\t\t\t\t\t\t\tenchantButton.Visible = true\n\t\t\t\t\t\t\t\tenchantmentPairing[enchantButton] = {id = abilitySlotData.id; request = \"variant\"; variant = variantName}\n\t\t\t\t\t\t\t\tenchantButton.item.itemThumbnail.Active = false\n\n\t\t\t\t\t\t\t\tlocal function selected()\n\t\t\t\t\t\t\t\t\tif not enchantButton.locked.Visible then\n\t\t\t\t\t\t\t\t\t\tnetwork:invoke(\"populateItemHoverFrameWithAbility\",\n\t\t\t\t\t\t\t\t\t\t\tabilityBaseData, abilitySlotData.rank,\n\t\t\t\t\t\t\t\t\t\t\tvariantBaseData, abilitySlotData.rank\n\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tenchantButton.MouseEnter:connect(selected)\n\t\t\t\t\t\t\t\tenchantButton.SelectionGained:connect(selected)\n\n\t\t\t\t\t\t\t\tlocal function unselected()\n\t\t\t\t\t\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tenchantButton.MouseLeave:connect(unselected)\n\t\t\t\t\t\t\t\tenchantButton.SelectionLost:connect(unselected)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tfor i, enchantButton in pairs(enchantFrame.enchantments.contents:GetChildren()) do\n\t\t\tif enchantButton:IsA(\"GuiObject\") then\n\t\t\t\tenchantButton.Activated:connect(function()\n\t\t\t\t\tselectedEnchantment = enchantButton\n\t\t\t\t\ttween(enchantFrame.button, {\"ImageColor3\"}, {Color3.fromRGB(87, 211, 217)}, 0.5)\n\t\t\t\tend)\n\t\t\tend\n\t\tend\n\n\t\tlocal titleColor\n\n\t\t--[[\n\t\tif abilitySlotData then\n\t\t\ttitleColor = Modules.itemAcquistion.getTitleColorForInventorySlotData(abilitySlotData)\n\t\tend\n\t\t]]\n\n\t\ttitleColor = titleColor or Color3.new(1,1,1)\n\n\t\titemButton.Parent.frame.ImageColor3 = titleColor or Color3.fromRGB(106, 105, 107)\n\t\titemButton.Parent.shine.ImageColor3 = titleColor or Color3.fromRGB(179, 178, 185)\n\t\titemButton.Parent.shine.ImageTransparency = titleColor and 0.47 or 0.63\n\tend\n\n\n\tenchantFrame.button.Activated:Connect(function()\n\n\t\tif not currentAbilitySlotData then\n\t\t\treturn false\n\t\tend\n\t\tif not selectedEnchantment then\n\t\t\treturn false\n\t\tend\n\t\tlocal request = enchantmentPairing[selectedEnchantment]\n\n\t\tif not debounce then\n\t\t\tdebounce = true\n\n\t\t\tlocal success, reason = network:invokeServer(\"playerRequest_enchantAbility\", currentBall, request)\n\t\t\tif success then\n\t\t\t\tselectedEnchantment = nil\n\t\t\t\tenchantFrame.curve.shine.ImageTransparency = 0\n\t\t\t\tenchantFrame.curve.shine.ImageColor3 = Color3.fromRGB(0, 230, 255)\n\t\t\t\temit(70,1.5)\n\t\t\t\ttween(enchantFrame.curve.shine, {\"ImageTransparency\", \"ImageColor3\"}, {0.5, Color3.fromRGB(14, 123, 125)}, 1)\n\t\t\t\tutilities.playSound(\"itemEnchanted\")\n--\t\t\t\tfill(currentButton, enchantItemSlotData)\n--\t\t\t\tmodule.dragItem(enchantItemSlotData, locationView)\n--\t\t\t\tmodule.reset()\n\t\t\t\ttween(enchantFrame.button, {\"ImageColor3\"}, {Color3.fromRGB(113, 113, 113)}, 0.5)\n\t\t\telse\n\t\t\t\twarn(\"No enchanting allowed\"..reason)\n\t\t\tend\n\t\t\tdebounce = false\n\t\tend\n\tend)\n\n\tfunction module.dragItem(inventorySlotData, view)\n\t\tmodule.reset()\n\t\tenchantFrame.input.equipItemButton.ImageTransparency = 0\n\t\tenchantFrame.input.equipItemButton.ImageColor3 = Color3.new(1,1,1)\n\n\t\tlocationView = view\n\n\t\tcurrentAbilitySlotData = inventorySlotData\n\n\n\t\tfill(enchantFrame.input.equipItemButton, currentAbilitySlotData)\n\t\tif enchantItemSlotData then\n\n\t\tend\n\n\n\tend\n\n\n\n\tlocal function itemHover()\n\t\tif currentAbilitySlotData then\n\t\t\tplayerData = network:invoke(\"getLocalPlayerDataCache\")\n\t\t\tlocal abilityBaseData = abilityLookup[currentAbilitySlotData.id](playerData)\n\t\t\tnetwork:invoke(\"populateItemHoverFrameWithAbility\", abilityBaseData, currentAbilitySlotData.rank)\n\t\tend\n\tend\n\n\n\tlocal function mouseLeave()\n\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\tend\n\n\n\tlocal function abilityDataUpdated(abilities)\n\t\tif currentAbilitySlotData then\n\t\t\tfor i, abilitySlotData in pairs(abilities) do\n\t\t\t\tif abilitySlotData.id == currentAbilitySlotData.id then\n\t\t\t\t\tcurrentAbilitySlotData = abilitySlotData\n\t\t\t\t\tfill(enchantFrame.input.equipItemButton, abilitySlotData)\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function onPropogationRequestToSelf(propogationNameTag, propogationData)\n\t\tif propogationNameTag == \"abilities\" then\n\t\t\tabilityDataUpdated(propogationData)\n\t\tend\n\tend\n\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\n\tenchantFrame.input.equipItemButton.MouseEnter:connect(itemHover)\n\tenchantFrame.input.equipItemButton.SelectionGained:connect(itemHover)\n\n\tenchantFrame.input.equipItemButton.MouseLeave:connect(mouseLeave)\n\n\n\tenchantFrame.output.equipItemButton.MouseLeave:connect(mouseLeave)\n\nend\n\n\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/equipment.lua",
    "content": "local module = {}\n\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal modules = require(replicatedStorage.modules)\nlocal network = modules.load(\"network\")\nlocal utilities = modules.load(\"utilities\")\nlocal mapping = modules.load(\"mapping\")\nlocal enchantment = modules.load(\"enchantment\")\nlocal itemData = require(replicatedStorage:WaitForChild(\"itemData\"))\nlocal itemAttributes = require(replicatedStorage:WaitForChild(\"itemAttributes\"))\n\nlocal menu = script.Parent.gameUI.menu_equipment\n\nlocal content = menu.content\nlocal viewport = menu.character.ViewportFrame\nlocal player = game.Players.LocalPlayer\n\nfunction module.show()\n\tmenu.Visible = not menu.Visible\nend\n\nfunction module.hide()\n\tmenu.Visible = false\nend\n\nmenu.close.Activated:connect(module.hide)\n\nlocal equipmentSlotPairing = {}\nlocal lastSelected\n\nfunction module.init(Modules)\n\tlocal function getInventoryCountLookupTableByItemId()\n\t\tlocal lookupTable = {}\n\t\tlocal inventoryLastGot = network:invoke(\"getCacheValueByNameTag\", \"inventory\")\n\n\t\tfor _, inventorySlotData in pairs(inventoryLastGot) do\n\t\t\tif lookupTable[inventorySlotData.id] then\n\t\t\t\tlookupTable[inventorySlotData.id] = lookupTable[inventorySlotData.id] + (inventorySlotData.stacks or 1)\n\t\t\telse\n\t\t\t\tlookupTable[inventorySlotData.id] = inventorySlotData.stacks or 1\n\t\t\tend\n\t\tend\n\n\t\treturn lookupTable\n\tend\n\n\tlocal function onInventoryItemMouseEnter(inventoryItem)\n\t\tlastSelected = inventoryItem\n\t\tlocal inventorySlotData = equipmentSlotPairing[inventoryItem]\n\t\tif inventorySlotData then\n\t\t\tlocal itemBaseData = itemData[inventorySlotData.id]\n\t\t\tif itemBaseData then\n\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\", itemBaseData, \"equipment\", inventorySlotData)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function onInventoryItemMouseLeave(inventoryItem)\n\t\tif lastSelected == inventoryItem then\n\t\t\t-- clears last selected\n\t\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\t\tend\n\tend\n\n\tlocal function getEquipmentDataByEquipmentPosition(equipment, equipmentPosition)\n\t\tfor _, equipmentData in pairs(equipment) do\n\t\t\tif equipmentData.position == equipmentPosition then\n\t\t\t\treturn equipmentData\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function int__updateEquipmentFrame(equipmentDataCollection)\n\t\tequipmentDataCollection = equipmentDataCollection or network:invoke(\"getCacheValueByNameTag\", \"equipment\")\n\t\t-- reset the previous slot pairing\n\t\tequipmentSlotPairing = {}\n\n\t\t-- update current equipment slots\n\t\tfor _, equipmentSlotData in pairs(equipmentDataCollection) do\n\t\t\tlocal positionName = mapping.getMappingByValue(\"equipmentPosition\", equipmentSlotData.position)\n\t\t\tif positionName and equipmentSlotData.id and content:FindFirstChild(positionName) then\n\t\t\t\tlocal itemBaseData = itemData[equipmentSlotData.id]\n\t\t\t\tlocal itemButton = content[positionName].equipItemButton\n\n\t\t\t\tif itemBaseData then\n\t\t\t\t\titemButton.Image = itemBaseData.image\n\t\t\t\t\titemButton.ImageColor3 = Color3.new(1,1,1)\n\t\t\t\t\tif equipmentSlotData.dye then\n\t\t\t\t\t\titemButton.ImageColor3 = Color3.fromRGB(equipmentSlotData.dye.r, equipmentSlotData.dye.g, equipmentSlotData.dye.b)\n\t\t\t\t\tend\n\n\t\t\t\t\tequipmentSlotPairing[itemButton] = equipmentSlotData\n\t\t\t\t\titemButton.Parent.frame.Visible = true\n\n\t\t\t\t\tif itemButton.Parent:FindFirstChild(\"icon\") then\n\t\t\t\t\t\titemButton.Parent.icon.Visible = false\n\t\t\t\t\tend\n\n\t\t\t\t\titemButton.Parent.ImageTransparency = 0\n\t\t\t\t--\titemButton.Parent.shadow.ImageTransparency = 0\n\t\t\t\t\titemButton.Parent.title.TextTransparency = 0\n\t\t\t\t\titemButton.Parent.title.TextStrokeTransparency = 0.2\n\n\t\t\t\t\tlocal titleColor, itemTier\n\t\t\t\t\tif equipmentSlotData then\n\t\t\t\t\t\ttitleColor, itemTier = Modules.itemAcquistion.getTitleColorForInventorySlotData(equipmentSlotData)\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal inventoryItem = itemButton.Parent\n\t\t\t\t\tinventoryItem.attribute.Visible = false\n\n\t\t\t\t\tif equipmentSlotData.attribute then\n\t\t\t\t\t\tlocal attributeData = itemAttributes[equipmentSlotData.attribute]\n\t\t\t\t\t\tif attributeData and attributeData.color then\n\t\t\t\t\t\t\tinventoryItem.attribute.ImageColor3 = attributeData.color\n\t\t\t\t\t\t\tinventoryItem.attribute.Visible = true\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tinventoryItem.stars.Visible = false\n\t\t\t\t\tlocal upgrades = equipmentSlotData.successfulUpgrades\n\t\t\t\t\tif upgrades then\n\t\t\t\t\t\tfor i, child in pairs(inventoryItem.stars:GetChildren()) do\n\t\t\t\t\t\t\tif child:IsA(\"ImageLabel\") then\n\t\t\t\t\t\t\t\tchild.ImageColor3 = titleColor or Color3.new(1,1,1)\n\t\t\t\t\t\t\t\tchild.Visible = false\n\t\t\t\t\t\t\telseif child:IsA(\"TextLabel\") then\n\t\t\t\t\t\t\t\tchild.TextColor3 = titleColor or Color3.new(1,1,1)\n\t\t\t\t\t\t\t\tchild.Visible = false\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\t\tinventoryItem.stars.Visible = true\n\t\t\t\t\t\tif upgrades <= 3 then\n\t\t\t\t\t\t\tfor i,star in pairs(inventoryItem.stars:GetChildren()) do\n\t\t\t\t\t\t\t\tlocal score = tonumber(star.Name)\n\t\t\t\t\t\t\t\tif score then\n\t\t\t\t\t\t\t\t\tstar.Visible = score <= upgrades\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tinventoryItem.stars.exact.Visible = false\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tinventoryItem.stars[\"1\"].Visible = true\n\t\t\t\t\t\t\tinventoryItem.stars.exact.Visible = true\n\t\t\t\t\t\t\tinventoryItem.stars.exact.Text = upgrades\n\t\t\t\t\t\tend\n\t\t\t\t\t\tinventoryItem.stars.Visible = true\n\t\t\t\t\tend\n\n\t\t\t\t\titemButton.Parent.shine.Visible = titleColor ~= nil and itemTier and itemTier > 1\n\t\t\t\t\tModules.fx.setFlash(itemButton.Parent.frame, itemButton.Parent.shine.Visible)\n\t\t\t\t\titemButton.Parent.frame.ImageColor3 = (itemTier and itemTier > 1 and titleColor) or Color3.fromRGB(106, 105, 107)\n\t\t\t\t\titemButton.Parent.shine.ImageColor3 = titleColor or Color3.fromRGB(179, 178, 185)\n\n\t\t\t\t\tif equipmentSlotData.position == mapping.equipmentPosition.weapon then\n\t\t\t\t\t\tlocal arrowEqpData \t= getEquipmentDataByEquipmentPosition(equipmentDataCollection, mapping.equipmentPosition.arrow)\n\t\t\t\t\t\tlocal weaponEqpData = getEquipmentDataByEquipmentPosition(equipmentDataCollection, mapping.equipmentPosition.weapon)\n\n\t\t\t\t\t\tif weaponEqpData and itemData[weaponEqpData.id].equipmentType == \"bow\" then\n\t\t\t\t\t\t\tif arrowEqpData then\n\t\t\t\t\t\t\t\tlocal lut = getInventoryCountLookupTableByItemId()\n\n\t\t\t\t\t\t\t\tcontent.weapon.ammo.amount.Text = tostring(lut[arrowEqpData.id] or 0)\n\t\t\t\t\t\t\t\tcontent.weapon.ammo.icon.Image = itemData[arrowEqpData.id].image\n\t\t\t\t\t\t\t\tcontent.weapon.ammo.Visible = true\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tcontent.weapon.ammo.Visible = false\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tcontent.weapon.ammo.Visible = false\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t-- reset untouched slots to blank image\n\t\tfor _, equipmentSlotUI in pairs(content:GetChildren()) do\n\t\t\tif equipmentSlotUI:FindFirstChild(\"frame\") and not equipmentSlotPairing[equipmentSlotUI.equipItemButton] then\n\t\t\t\tequipmentSlotUI.equipItemButton.Image = \"\"\n\t\t\t\tlocal itemButton = equipmentSlotUI\n\t\t\t\t\titemButton.frame.Visible = false\n\t\t\t\t\titemButton.shine.Visible = false\n\n\t\t\t\t\tif itemButton:FindFirstChild(\"icon\") then\n\t\t\t\t\t\titemButton.icon.Visible = true\n\t\t\t\t\tend\n\t\t\t\t\titemButton.stars.Visible = false\n\t\t\t\t\titemButton.attribute.Visible = false\n\t\t\t\t\titemButton.ImageTransparency = 0\n\t\t\t--\t\titemButton.shadow.ImageTransparency = 0.4\n\t\t\t\t\titemButton.title.TextTransparency = 0.6\n\t\t\t\t\titemButton.title.TextStrokeTransparency = 0.6\n\t\t\tend\n\t\tend\n\n\t\tif viewport:FindFirstChild(\"entity\") then\n\t\t\tviewport.entity:Destroy()\n\t\tend\n\n\t\tif viewport:FindFirstChild(\"entity2\") then\n\t\t\tviewport.entity2:Destroy()\n\t\tend\n\n\t\tlocal camera = viewport.CurrentCamera\n\t\tif camera == nil then\n\t\t\tcamera = Instance.new(\"Camera\")\n\t\t\tcamera.Parent = viewport\n\t\t\tviewport.CurrentCamera = camera\n\t\tend\n\n\t\tlocal client = game.Players.LocalPlayer\n\t\tlocal character = client.Character\n\t\tlocal mask = viewport.characterMask\n\n\t\tlocal characterAppearanceData = {}\n\t\tcharacterAppearanceData.equipment \t= network:invoke(\"getCacheValueByNameTag\", \"equipment\")\n\t\tcharacterAppearanceData.accessories = network:invoke(\"getCacheValueByNameTag\", \"accessories\")\n\n\t\tlocal characterRender = network:invoke(\"createRenderCharacterContainerFromCharacterAppearanceData\",mask, characterAppearanceData or {}, client)\n\t\tcharacterRender.Parent = workspace.CurrentCamera\n\n\t\tlocal animationController = characterRender.entity:WaitForChild(\"AnimationController\")\n\t\tlocal currentEquipment = network:invoke(\"getCurrentlyEquippedForRenderCharacter\", characterRender.entity)\n\n\t\tlocal weaponType do\n\t\t\tif currentEquipment[\"1\"] then\n\t\t\t\tweaponType = currentEquipment[\"1\"].baseData.equipmentType\n\t\t\tend\n\t\tend\n\n\t\tlocal track = network:invoke(\"getMovementAnimationForCharacter\", animationController, \"idling\", weaponType, nil)\n\n\t\tif track then\n\t\t\tspawn(function()\n\t\t\t\tif characterRender then\n\t\t\t\t\tlocal entity \t= characterRender.entity\n--\t\t\t\t\tentity.Parent \t= viewport\n--\t\t\t\t\tcharacterRender:Destroy()\n\t\t\t\t\tlocal focus \t= CFrame.new(entity.PrimaryPart.Position + entity.PrimaryPart.CFrame.lookVector * 6.5, entity.PrimaryPart.Position) * CFrame.new(-1,0,0)\n\t\t\t\t\tcamera.CFrame \t= CFrame.new(focus.p + Vector3.new(0,1.5,0), entity.PrimaryPart.Position + Vector3.new(0,0.5,0))\n\t\t\t\tend\n\n\t\t\t\tif typeof(track) == \"Instance\" then\n\t\t\t\t\ttrack:Play()\n\t\t\t\telseif typeof(track) == \"table\" then\n\t\t\t\t\tfor _, obj in pairs(track) do\n\t\t\t\t\t\tobj:Play()\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\twhile true do\n\t\t\t\t\twait(0.1)\n\n\t\t\t\t\tif typeof(track) == \"Instance\" then\n\t\t\t\t\t\tif track.Length > 0 then\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tend\n\t\t\t\t\telseif typeof(track) == \"table\" then\n\t\t\t\t\t\tlocal isGood = true\n\t\t\t\t\t\tfor ii, obj in pairs(track) do\n\t\t\t\t\t\t\tif track.Length == 0 then\n\t\t\t\t\t\t\t\tisGood = false\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif isGood then\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif characterRender then\n\t\t\t\t\tlocal entity \t= characterRender.entity\n\t\t\t\t\tentity.Parent \t= viewport\n\n\t\t\t\t\tcharacterRender:Destroy()\n\t\t\t\t\t--[[\n\t\t\t\t\tlocal focus \t= CFrame.new(entity.PrimaryPart.Position + entity.PrimaryPart.CFrame.lookVector * 6.3, entity.PrimaryPart.Position) * CFrame.new(-3,0,0)\n\t\t\t\t\tcamera.CFrame \t= CFrame.new(focus.p + Vector3.new(0,1.5,0), entity.PrimaryPart.Position + Vector3.new(0,0.5,0))\n\t\t\t\t\t]]\n\t\t\t\tend\n\t\t\tend)\n\t\telse\n\t\t\tlocal track = animationController:LoadAnimation(mask.idle)\n\t\t\ttrack.Looped = true\n\t\t\ttrack.Priority = Enum.AnimationPriority.Idle\n\t\t\ttrack:Play()\n\t\tend\n--\t\tlocal track = animationController:LoadAnimation(mask.idle)\n--\t\ttrack.Looped = true\n--\t\ttrack.Priority = Enum.AnimationPriority.Idle\n--\t\ttrack:Play()\n\tend\n\n\tlocal function onGetEquipmentSlotDataByEquipmentSlotUI(equipmentSlotUI)\n\t\treturn equipmentSlotPairing[equipmentSlotUI]\n\tend\n\n\tlocal levels = Modules.levels\n\tlocal tween = Modules.tween\n\n\tlocal function onPropogationRequestToSelf(propogationNameTag, propogationValue)\n\t\tif propogationNameTag == \"equipment\" then\n\t\t\tint__updateEquipmentFrame(propogationValue)\n\t\tend\n\tend\n\n\tmodule.update = function(...)\n\t\tint__updateEquipmentFrame(...)\n\tend\n\n\tlocal function main()\n\n\t\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\t\tnetwork:create(\"getEquipmentSlotDataByEquipmentSlotUI\", \"BindableFunction\", \"OnInvoke\", onGetEquipmentSlotDataByEquipmentSlotUI)\n\n\t\tfor _, item in pairs(menu.content:GetChildren()) do\n\t\t\tif item:FindFirstChild(\"equipItemButton\") then\n\t\t\t\titem.equipItemButton.MouseEnter:connect(function() onInventoryItemMouseEnter(item.equipItemButton) end)\n\t\t\t\titem.equipItemButton.MouseLeave:connect(function() onInventoryItemMouseLeave(item.equipItemButton) end)\n\n\t\t\t\titem.equipItemButton.SelectionGained:connect(function() onInventoryItemMouseEnter(item.equipItemButton) end)\n\t\t\t\titem.equipItemButton.SelectionLost:connect(function() onInventoryItemMouseLeave(item.equipItemButton) end)\n\n\t\t\t\titem.equipItemButton.Activated:connect(function()\n\t\t\t\t\tif Modules.inventory.isEnchantingEquipment then\n\t\t\t\t\t\tModules.inventory.enchantItem(item.equipItemButton)\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\tend\n\t\tend\n\tend\n\tmain()\n\t-- postInit\n\tspawn(function()\n\t\tmodule.update()\n\t\tprint(1)\n\t\tnetwork:connect(\"signal_isEnchantingEquipmentSet\", \"Event\", function(value, inventorySlotData_enchantment)\n\t\t\tfor _, equipmentButton in pairs(menu.content:GetChildren()) do\n\t\t\t\tif equipmentButton:FindFirstChild(\"equipItemButton\") then\n\t\t\t\t\tif value and inventorySlotData_enchantment then\n\t\t\t\t\t\tlocal equipmentSlotData = equipmentSlotPairing[equipmentButton.equipItemButton]\n\t\t\t\t\t\tif equipmentSlotData then\n\t\t\t\t\t\t\tequipmentButton.blocked.Visible = false\n\t\t\t\t\t\t\tlocal equipmentBaseData = itemData[equipmentSlotData.id]\n\n\t\t\t\t\t\t\tlocal itemBaseData_enchantment = itemData[inventorySlotData_enchantment.id]\n\t\t\t\t\t\t\tlocal cost = itemBaseData_enchantment.upgradeCost or 1\n\t\t\t\t\t\t\tlocal max = equipmentBaseData.maxUpgrades\n\n\t\t\t\t\t\t\tlocal canEnchant, indexToRemove = enchantment.enchantmentCanBeAppliedToItem(inventorySlotData_enchantment, equipmentSlotData)\n\t\t\t\t\t\t\tlocal blocked = not canEnchant\n\n\t\t\t\t\t\t\tequipmentButton.blocked.Visible = blocked\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\tequipmentButton.blocked.Visible = false\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\tend)\nend\n\n\n\nreturn module"
  },
  {
    "path": "src/StarterGui/focus.lua",
    "content": "-- Focus handler by berezaa honestly kinda of an artifact but important for xbox selection\n\nlocal module = {}\n\nmodule.focused = nil\n\nfunction module.toggle()\n\twarn(\"focus.toggle not ready\")\nend\n\nlocal function getBestButton(gui)\n\tlocal hiv = Vector2.new(9999,9999)\n\tlocal top\n\t--[[\n\tfor i, child in pairs (gui:GetDescendants()) do\n\t\tif child:IsA(\"GuiButton\") and child.Visible then\n\t\t\tlocal vec = child.AbsolutePosition - gui.AbsolutePosition\n\t\t\tif vec.magnitude < hiv.magnitude then\n\t\t\t\ttop = child\n\t\t\t\thiv = vec\n\t\t\tend\n\t\tend\n\tend\n\t]]\n\t\n\tlocal function checkChildren(object)\n\t\tif (not object:IsA(\"GuiObject\")) or object.Visible then\n\t\t\tif object:IsA(\"GuiButton\") then\n\t\t\t\tlocal vec = object.AbsolutePosition - gui.AbsolutePosition\n\t\t\t\tif vec.magnitude < hiv.magnitude then\n\t\t\t\t\ttop = object\n\t\t\t\t\thiv = vec\n\t\t\t\tend\t\t\t\t\n\t\t\tend\n\t\t\tfor i,child in pairs(object:GetChildren()) do\n\t\t\t\tcheckChildren(child)\n\t\t\tend\n\t\tend\n\tend\n\t\n\tcheckChildren(gui)\n\t\n\treturn top\nend\n\nmodule.getBestButton = getBestButton\n\n\nfunction module.init(Modules) \n\n\tlocal network = Modules.network\n\tlocal tween = Modules.tween\n\t\n\tlocal depth = game.Lighting:FindFirstChild(\"DepthOfField\")\n\t\n--\tlocal gradient = script.Parent.gradient\n\n\tlocal function update()\n\t\tlocal isMenuInFocus = not (module.focused == nil or module.focused.Visible == false)\n\t\tnetwork:fire(\"signal_menuFocusChanged\", isMenuInFocus)\n--\t\tscript.Parent.xboxMenuPrompt.Visible = isMenuInFocus\n-- ??????????\n\tend\n\n\tfunction module.close()\n\t\tif module.focused then\n\t\t\tmodule.focused.Visible = false\n\t\t\tmodule.focused = nil\n--\t\t\ttween(gradient, {\"ImageTransparency\"}, 1, 0.5)\n\t\t\ttween(depth, {\"FarIntensity\", \"FocusDistance\", \"InFocusRadius\"}, {0.25, 200, 0}, 0)\n\t\tend\t\t\n\t\tif Modules.input.mode.Value == \"xbox\" then\n\n\t\t\tgame:GetService(\"GuiService\").GuiNavigationEnabled = false\n\t\t\tgame:GetService(\"GuiService\").SelectedObject = nil\t\t\n\t\t\tpcall(function()\n\t\t\t\tgame:GetService(\"GuiService\"):RemoveSelectionGroup(\"focus\")\n\t\t\tend)\t\t\t\n\t\tend\n\t\tupdate()\n\tend\n\t\n\tfunction module.cleanup()\n\t\tif Modules.input.mode.Value == \"xbox\" then\n\n\t\t\tgame:GetService(\"GuiService\").GuiNavigationEnabled = false\n\t\t\tgame:GetService(\"GuiService\").SelectedObject = nil\t\t\n\t\t\tpcall(function()\n\t\t\t\tgame:GetService(\"GuiService\"):RemoveSelectionGroup(\"focus\")\n\t\t\tend)\t\t\t\n\t\tend\t\t\n\tend\n\t\n\tfunction module.change(object)\n\t\tif Modules.input.mode.Value == \"xbox\" then\n\t\t\tgame:GetService(\"GuiService\").GuiNavigationEnabled = false\n\t\t\tgame:GetService(\"GuiService\").SelectedObject = nil\t\t\n\t\t\tpcall(function()\n\t\t\t\tgame:GetService(\"GuiService\"):RemoveSelectionGroup(\"focus\")\n\t\t\tend)\n\t\t\tobject.Visible = true\n\t\t\tgame.GuiService.GuiNavigationEnabled = true\n\t\t\tgame.GuiService:AddSelectionParent(\"focus\", object)\n\t\t\tgame.GuiService.SelectedObject = getBestButton(object)\t\n--\t\t\ttween(gradient, {\"ImageTransparency\"}, 0, 0.5)\t\n\t\t\ttween(depth, {\"FarIntensity\", \"FocusDistance\", \"InFocusRadius\"}, {1, 4, 3}, 0)\t\t\t\t\t\n\t\tend\n\tend\n\t\n\tfunction module.toggle(object)\n\t\t\n\t\tModules.input.setCurrentFocusFrame(nil)\n\t\t\n\t\tif module.focused then\n\t\t\tif Modules.input.mode.Value == \"xbox\" then\n\t\t\t\tgame:GetService(\"GuiService\").GuiNavigationEnabled = false\n\t\t\t\tgame:GetService(\"GuiService\").SelectedObject = nil\t\t\n\t\t\t\tpcall(function()\n\t\t\t\t\tgame:GetService(\"GuiService\"):RemoveSelectionGroup(\"focus\")\n\t\t\t\tend)\t\n\t\t\t\tif object:IsDescendantOf(module.focused) then\n\t\t\t\t\tmodule.focused.Visible = false\n--\t\t\t\t\ttween(gradient, {\"ImageTransparency\"}, 1, 0.5)\n\t\t\t\t\ttween(depth, {\"FarIntensity\", \"FocusDistance\", \"InFocusRadius\"}, {0.25, 200, 0}, 0)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\t\n\t\tif object.Visible then\n\t\t\tobject.Visible = false\n\t\t\tmodule.focused = nil\n\t\t\tobject.ZIndex = 2\n--\t\t\ttween(gradient, {\"ImageTransparency\"}, 1, 0.5)\n\t\t\ttween(depth, {\"FarIntensity\", \"FocusDistance\", \"InFocusRadius\"}, {0.25, 200, 0}, 0)\n\t\telse\n\t\t\tobject.Visible = true\n\t\t\tif module.focused then\n\t\t\t\tmodule.focused.ZIndex = 2\n\t\t\tend\n\t\t\tobject.ZIndex = 3\n\t\t\tmodule.focused = object\n--\t\t\ttween(gradient, {\"ImageTransparency\"}, 0, 0.5)\n\t\t\ttween(depth, {\"FarIntensity\", \"FocusDistance\", \"InFocusRadius\"}, {1, 4, 3}, 0)\t\n\t\tend\n\t\t\n\t\tif module.focused then\n\t\t\tif Modules.input.mode.Value == \"xbox\" then\n\t\t\t\tgame.GuiService:AddSelectionParent(\"focus\", module.focused)\n\t\t\t\tgame.GuiService.SelectedObject = getBestButton(module.focused)\n\t\t\t\twarn(\"$\",game.GuiService.SelectedObject)\n\t\t\t\tgame.GuiService.GuiNavigationEnabled = true\n\t\t\tend\n\t\tend\n\t\tupdate()\n\tend\n\t\n\t\t\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/fx.lua",
    "content": "-- module for displaying cool effects xD\n-- author: the right, honorable just lord andrew alexander bereza\n\n-- was going to make a cool \"bought!\" and \"sold!\" and \"error!\" display for shop\n-- then i figured that might apply to more frames\n-- now here we are xD\n\n-- goal: eventually do cool stuff like fireworks here\n\nlocal module = {}\n\nfunction module.displayAbilityCooldown()\n\nend\n\nfunction module.statusRibbon()\n\nend\n\nfunction module.setFlash()\n\nend\n\nlocal effects = script.Parent.effects\n\nmodule.colorTypes = {\n\tsuccess = Color3.fromRGB(97, 180, 79);\n\tfail = Color3.fromRGB(180, 58, 60);\n\tdefault = Color3.fromRGB(180,180,0);\n\tgold = Color3.fromRGB(255, 188, 52);\n}\nlocal colorTypes = module.colorTypes\n\nfunction module.init(Modules)\n\n\tlocal tween = Modules.tween\n\t-- oh tween module, no one gets me quite like you do\n\tlocal network = Modules.network\n\n\tfunction module.setFlash(frame, value)\n\t\tif frame:FindFirstChild(\"flash\") then\n\t\t\tframe.flash:Destroy()\n\t\tend\n\t\tif value then\n\t\t\tlocal flash = effects.flash:Clone()\n\t\t\tflash.Parent = frame\n\t\t\tflash.Visible = true\n\n\t\t\tlocal duration = 0.6\n--\t\t\tduration = duration * (frame.AbsoluteSize.X/60)\n\t\t\tlocal max = math.max(frame.AbsoluteSize.X, frame.AbsoluteSize.Y)\n\t\t\tflash.ImageLabel.Size = UDim2.new(1, 16, 1, 16) --UDim2.new(0, 100 * max/60, 0, 100 * max/60)\n\n\t\t\tspawn(function()\n\n\t\t\t\twhile frame.Parent and flash.Parent do\n\n\t\t\t\t\tflash.ImageLabel.Position = UDim2.new(0,0,0,0)\n\t\t\t\t\tflash.ImageLabel.ImageTransparency = 1\n\n\t\t\t\t\ttween(flash.ImageLabel,{\"Position\"},{UDim2.new(1,0,1,0)},duration, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut)\n\t\t\t\t\ttween(flash.ImageLabel,{\"ImageTransparency\"},{0}, duration/2, nil, Enum.EasingDirection.Out )\n\n\t\t\t\t\twait(duration/2)\n\t\t\t\t\tif frame.Parent and flash.Parent then\n\t\t\t\t\t\ttween(flash.ImageLabel,{\"ImageTransparency\"},{1},duration/2, nil, Enum.EasingDirection.In )\n\t\t\t\t\tend\n\n\t\t\t\t\twait(4-duration)\n\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\n\n\tend\n\n\tfunction module.displayAbilityCooldown(abilityFrame, cooldown)\n\t\tif abilityFrame then\n\t\t\tif abilityFrame:FindFirstChild(\"cooldown\") then\n\t\t\t\tabilityFrame.cooldown:Destroy()\n\t\t\tend\n\t\t\tlocal indicator = effects.cooldown:Clone()\n\t\t\tindicator.Parent = abilityFrame\n\t\t\tindicator.bar.value.Size = UDim2.new(1,0,1,0)\n\t\t\ttween(indicator.bar.value,{\"Size\"}, UDim2.new(1,0,0,0), cooldown, Enum.EasingStyle.Linear)\n\t\t\tgame.Debris:AddItem(indicator, cooldown)\n\t\tend\n\tend\n\n\tfunction module.ring(ringInfo, absolutePosition)\n\t\tlocal ring = ringInfo or {}\n\t\tlocal color = ring.color or Color3.new(1,1,1)\n\t\tlocal duration = ring.duration or 0.5\n\t\tlocal scale = ring.scale or 5\n\n\t\tlocal ringObject = effects.ring:Clone()\n\t\tringObject.Parent = script.Parent.gameUI\n\t\tringObject.ImageTransparency = 0\n\t\tringObject.AnchorPoint = Vector2.new(0.5,0.5)\n\t\tringObject.Position = UDim2.new(0,absolutePosition.X,0,absolutePosition.Y - game.GuiService:GetGuiInset().Y)\n\t\tringObject.Visible = true\n\n\t\tringObject.ImageColor3 = color\n\n\t\tlocal sizeGoal = ringObject.AbsoluteSize * scale\n\t\ttween(ringObject,{\"Size\",\"ImageTransparency\"},{UDim2.new(0,sizeGoal.X,0,sizeGoal.Y),1},duration)\n\t\tgame.Debris:AddItem(ringObject,duration)\n\tend\n\n\tnetwork:create(\"displayAbilityCooldown\",\"BindableFunction\",\"OnInvoke\",module.displayAbilityCooldown)\n\n\tlocal endtime\n\n\tfunction module.consumableCooldown(duration)\n\t\tlocal durationEndTime = tick() + duration\n\t\tendtime = durationEndTime\n\tend\n\n\tfunction module.statusRibbon(parent, text, colorType, duration, target)\n\t\tcolorType = colorType or \"default\"\n\t\tlocal color = colorTypes[colorType]\n\t\tduration = duration or 3\n\n\t\t-- someone told me instancing is better than cloning\n\t\tlocal ribbon = Instance.new(\"Frame\")\n\t\tribbon.BorderSizePixel = 0\n\t\tribbon.BackgroundTransparency = 1\n\n\t\tribbon.ZIndex = 7\n\n\n\n\t\tlocal ysize = parent.AbsoluteSize.Y\n\t\tysize = ysize > 30 and 30 or ysize\n\n\t\tribbon.Size = UDim2.new(1,0,0,ysize)\n\t\tribbon.Name = \"fxStatusRibbon\"\n\t\tribbon.ClipsDescendants = true\n\t\t-- took me two tries to spell this property right\n\n\t\t-- todo maybe make this better\n\t\ttarget = target or UDim2.new(0,0,0,28)\n\t\tribbon.Position = target\n\n\t\tif target.Y.Scale >= 0.49 and target.Y.Scale <= 0.51 then\n\t\t\tribbon.AnchorPoint = Vector2.new(0,0.5)\n\t\tend\n\n\t\tlocal textLabel = Instance.new(\"TextLabel\")\n\t\ttextLabel.BackgroundTransparency = 1\n\t\ttextLabel.Size = UDim2.new(1,0,0.8,0)\n\t\ttextLabel.AnchorPoint = Vector2.new(0,0.5)\n\t\ttextLabel.Position = UDim2.new(1,0,0.5,0)\n\t\ttextLabel.TextColor3 = Color3.new(1,1,1)\n\t\ttextLabel.TextScaled = true\n\t\ttextLabel.Font = Enum.Font.SourceSans\n\t\ttextLabel.Parent = ribbon\n\n\t\ttextLabel.Text = text\n\t\tribbon.BackgroundColor3 = color\n\n\t\tribbon.Parent = parent\n\t\tgame.Debris:AddItem(ribbon, duration + 1)\n\n\t\t-- dont ask about the fractions. I like it this way\n\t\tspawn(function()\n\t\t\ttween(ribbon,{\"BackgroundTransparency\"},0.1,duration * 1/8)\n\t\t\ttween(textLabel,{\"Position\"},UDim2.new(0,0,0.5,0),duration * 2/8)\n\t\t\twait(duration * 6/8)\n\t\t\ttween(textLabel,{\"Position\"},UDim2.new(-1,0,0.5,0),duration * 2/8)\n\t\t\twait(duration * 1/8)\n\t\t\ttween(ribbon,{\"BackgroundTransparency\"},1,duration * 1/8)\n\t\t\twait(duration * 1/8)\n\t\t\tif ribbon then\n\t\t\t\tribbon.Visible = false\n\t\t\tend\n\t\tend)\n\n\t\treturn ribbon\n\tend\n\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/guild.lua",
    "content": "local module = {}\n\n-- Guild menu and local guild logic for inviting/other actions\n-- Written by berezaa\n\nlocal menu = script.Parent.gameUI.guild\n\nfunction module.open()\n\tmenu.Visible = true\nend\n\nlocal httpService = game:GetService(\"HttpService\")\n\nlocal guildRankValues = {\n    member = 1;\n    officer = 2;\n    general = 3;\n    leader = 4;\n}\n\n\nfunction module.init(Modules)\n\tlocal network = Modules.network\n\n\tfunction module.open()\n\t\tif not menu.Visible then\n\t\t\tmenu.UIScale.Scale = (Modules.input.menuScale or 1) * 0.75\n\t\t\tModules.tween(menu.UIScale, {\"Scale\"}, (Modules.input.menuScale or 1), 0.5, Enum.EasingStyle.Bounce)\n\t\tend\n\t\tModules.focus.toggle(menu)\n\tend\n\n\tmenu.close.Activated:connect(function()\n\t\tmodule.open()\n\tend)\n\n\tmodule.guildData = {}\n\n\tlocal function getGuildData(guid)\n\t\tif guid == \"\" then\n\t\t\treturn nil\n\t\tend\n\t\tlocal guildDataFolder = game.ReplicatedStorage:FindFirstChild(\"guildDataFolder\")\n\t\tif guildDataFolder then\n\n\t\t\tlocal guildDataValue = guildDataFolder:WaitForChild(guid, 3)\n\t\t\tif guildDataValue then\n\t\t\t\tlocal guildData = httpService:JSONDecode(guildDataValue.Value)\n\t\t\t\treturn guildData\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function getGuildPlayerData(player, guid)\n\t\tlocal guildData = getGuildData(guid)\n\t\tif guildData == nil then\n\t\t\treturn false, \"Guild data not found.\"\n\t\tend\n\t\tlocal members = guildData.members\n\t\tlocal guildPlayerData = members[tostring(player.userId)]\n\t\tif guildPlayerData == nil then\n\t\t\treturn false, \"Not a member of guild.\"\n\t\tend\n\t\treturn guildPlayerData\n\tend\n\n\tlocal function updatePlayerGuildData()\n\t\tlocal guid = game.Players.LocalPlayer.guildId.Value\n\t\tlocal guildData = getGuildData(guid)\n\t\tmodule.guildData = guildData\n\t\tlocal guildPlayerData = getGuildPlayerData(game.Players.LocalPlayer, guid)\n\t\tmenu.Parent.inspectPlayer.content.buttons.guild.Visible = false\n\t\tif guildData and guildPlayerData then\n\t\t\tlocal canInvite = guildPlayerData.rank == \"leader\" or guildPlayerData.rank == \"officer\" or guildPlayerData.rank == \"general\"\n\t\t\tmenu.Parent.inspectPlayer.content.buttons.guild.Visible = canInvite\n\t\t\tmenu.curve.Visible = true\n\t\t\tmenu.Parent.right.buttons.openGuild.Visible = true\n\t\t\tmenu.curve.intro.title.Text = guildData.name\n\t\t\tmenu.curve.intro.notice.value.Text = guildData.notice or \"There is no notice at this time.\"\n\n\t\t\tlocal guildLeader = \"no one!\"\n\t\t\tfor userIdString, playerInfo in pairs(guildData.members) do\n\t\t\t\tif playerInfo.rank == \"leader\" then\n\t\t\t\t\tguildLeader = playerInfo.name\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tmenu.curve.intro.leader.Text = \"led by \"..guildLeader\n\n\t\t\tfor i,guildMemberButton in pairs(menu.curve.side.members.list:GetChildren()) do\n\t\t\t\tif guildMemberButton:IsA(\"GuiObject\") then\n\t\t\t\t\tguildMemberButton:Destroy()\n\t\t\t\tend\n\t\t\tend\n\t\t\tlocal memberCount = 0\n\t\t\tfor memberString, memberData in pairs(guildData.members) do\n\t\t\t\tlocal guildMemberButton = menu.curve.side.members.example:Clone()\n\t\t\t\tguildMemberButton.content.username.Text = memberData.name\n\t\t\t\tlocal level = guildMemberButton.content.level.value\n\t\t\t\tlevel.Text = \"Lvl. \"..memberData.level\n\n\t\t\t\tlocal xSize = game.TextService:GetTextSize(level.Text, level.TextSize, level.Font, Vector2.new()).X + 16\n\t\t\t\tlevel.Parent.Size = UDim2.new(0, xSize, 0, 20)\n\n\t\t\t\tlocal class = memberData.class\n\t\t\t\tif class:lower() == \"adventurer\" then\n\t\t\t\t\tguildMemberButton.content.level.emblem.Visible = false\n\t\t\t\telse\n\t\t\t\t\tguildMemberButton.content.level.emblem.Visible = true\n\t\t\t\t\tguildMemberButton.content.level.emblem.Image = \"rbxgameasset://Images/emblem_\"..class:lower()\n\t\t\t\tend\n\n\n\t\t\t\tlocal guildMemberRankValue = guildRankValues[memberData.rank] or 0\n\t\t\t\tguildMemberButton.LayoutOrder = 5-guildMemberRankValue\n\n\t\t\t\tguildMemberButton.content.rank.Image = \"rbxgameasset://Images/rank_\"..memberData.rank:lower()\n\t\t\t\tguildMemberButton.Parent = menu.curve.side.members.list\n\t\t\t\tguildMemberButton.Visible = true\n\t\t\t\tguildMemberButton.Name = memberString\n\t\t\t\tmemberCount = memberCount + 1\n\n\t\t\t\tguildMemberButton.Activated:connect(function()\n\t\t\t\t\tif guildMemberButton.actions.Visible then\n\t\t\t\t\t\tguildMemberButton.actions.Visible = false\n\t\t\t\t\telse\n\t\t\t\t\t\tfor i,otherButton in pairs(menu.curve.side.members.list:GetChildren()) do\n\t\t\t\t\t\t\tif otherButton:IsA(\"GuiObject\") then\n\t\t\t\t\t\t\t\totherButton.actions.Visible = false\n\t\t\t\t\t\t\t\totherButton.ZIndex = 1\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\t\tguildMemberButton.actions.Visible = true\n\t\t\t\t\t\tguildMemberButton.ZIndex = 2\n\t\t\t\t\tend\n\n\t\t\t\tend)\n\n\t\t\t\tlocal exile = guildMemberButton.actions.exile\n\n\t\t\t\tlocal clientRankValue = guildRankValues[getGuildPlayerData(game.Players.LocalPlayer, guid).rank]\n\t\t\t\tif clientRankValue <= guildMemberRankValue then\n\t\t\t\t\texile.ImageColor3 = Color3.new(0.3,0.3,0.3)\n\t\t\t\t\texile.Active = false\n\t\t\t\telse\n\t\t\t\t\texile.Activated:connect(function()\n\t\t\t\t\t\tif exile.Active then\n\t\t\t\t\t\t\texile.Active = false\n\t\t\t\t\t\t\tif Modules.prompting_Fullscreen.prompt(\"Are you sure you wish to EXILE \"..memberData.name..\" from your guild?\") then\n\t\t\t\t\t\t\t\texile.icon.Visible = false\n\t\t\t\t\t\t\t\texile.fail.Visible = false\n\t\t\t\t\t\t\t\texile.success.Visible = false\n\t\t\t\t\t\t\t\tlocal success, reason = network:invokeServer(\"playerRequest_exileUserIdFromGuild\", tonumber(guildMemberButton.Name))\n\t\t\t\t\t\t\t\tif not success then\n\t\t\t\t\t\t\t\t\tnetwork:fire(\"alert\", {text = reason; textColor3 = Color3.fromRGB(255, 100, 100);})\n\t\t\t\t\t\t\t\t\texile.fail.Visible = true\n\t\t\t\t\t\t\t\t\twait(0.3)\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif exile and exile.Parent then\n\t\t\t\t\t\t\t\texile.Active = true\n\t\t\t\t\t\t\t\texile.success.Visible = false\n\t\t\t\t\t\t\t\texile.fail.Visible = false\n\t\t\t\t\t\t\t\texile.icon.Visible = true\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\t\t\t\tend\n\n\t\t\t\tfor rankName, rankValue in pairs(guildRankValues) do\n\t\t\t\t\tlocal button = guildMemberButton.actions:FindFirstChild(rankName)\n\t\t\t\t\tif button then\n\t\t\t\t\t\tif clientRankValue <= guildMemberRankValue or clientRankValue <= rankValue or rankValue == guildMemberRankValue then\n\t\t\t\t\t\t\tbutton.ImageColor3 = Color3.new(0.3, 0.3, 0.3)\n\t\t\t\t\t\t\tbutton.Active = false\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tlocal userId = tonumber(guildMemberButton.Name)\n\t\t\t\t\t\t\tbutton.Activated:connect(function()\n\t\t\t\t\t\t\t\tif button.Active then\n\t\t\t\t\t\t\t\t\tbutton.Active = false\n\t\t\t\t\t\t\t\t\tif Modules.prompting_Fullscreen.prompt(\"Are you sure you wish to rank \"..memberData.name..\" to \"..rankName:upper()..\"?\") then\n\t\t\t\t\t\t\t\t\t\tbutton.icon.Visible = false\n\t\t\t\t\t\t\t\t\t\tbutton.fail.Visible = false\n\t\t\t\t\t\t\t\t\t\tbutton.success.Visible = false\n\t\t\t\t\t\t\t\t\t\tlocal success, reason = network:invokeServer(\"playerRequest_changeUserIdRankValue\", userId, rankValue)\n\t\t\t\t\t\t\t\t\t\tif not success then\n\t\t\t\t\t\t\t\t\t\t\tnetwork:fire(\"alert\", {text = reason; textColor3 = Color3.fromRGB(255, 100, 100);})\n\t\t\t\t\t\t\t\t\t\t\tbutton.fail.Visible = true\n\t\t\t\t\t\t\t\t\t\t\twait(0.3)\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\tif button and button.Parent then\n\t\t\t\t\t\t\t\t\t\tbutton.Active = true\n\t\t\t\t\t\t\t\t\t\tbutton.success.Visible = false\n\t\t\t\t\t\t\t\t\t\tbutton.fail.Visible = false\n\t\t\t\t\t\t\t\t\t\tbutton.icon.Visible = true\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\n\n\t\t\tend\n\t\t\tmenu.curve.side.members.title.Text = memberCount .. \" \" .. (memberCount == 1 and \"member\" or \"members\")\n\n\n\t\telse\n\t\t\tmenu.curve.Visible = false\n\t\t\tmenu.Parent.right.buttons.openGuild.Visible = false\n\t\tend\n\tend\n\tspawn(updatePlayerGuildData)\n\tgame.Players.LocalPlayer.guildId.Changed:connect(updatePlayerGuildData)\n\tnetwork:connect(\"signal_guildDataUpdated\", \"OnClientEvent\", updatePlayerGuildData)\n\n\t-- Invited to join a guild.\n\tnetwork:connect(\"serverPrompt_playerInvitedToServer\", \"OnClientInvoke\", function(invitingPlayer, guid)\n\t\tlocal guildDataFolder = game.ReplicatedStorage:FindFirstChild(\"guildDataFolder\")\n\t\tif guildDataFolder then\n\t\t\tlocal guildDataValue = guildDataFolder:FindFirstChild(guid)\n\t\t\tif guildDataValue then\n\t\t\t\tlocal guildData = httpService:JSONDecode(guildDataValue.Value)\n\t\t\t\tif guildData.name then\n\t\t\t\t\treturn Modules.prompting.prompt(invitingPlayer.Name..\" has invited you to join their Guild, \" .. guildData.name .. \".\")\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\treturn false\n\tend)\n\n\tmenu.curve.intro.leave.Activated:connect(function()\n\t\tif Modules.prompting_Fullscreen.prompt(\"Are you sure you wish to LEAVE the guild?\") then\n\t\t\tlocal success, reason = network:invokeServer(\"playerRequest_leaveMyGuild\", false)\n\t\t\tif not success then\n\t\t\t\tif reason == \"confirmAbandon\" then\n\t\t\t\t\tif Modules.prompting_Fullscreen.prompt(\"Will you ABANDON your guild? It will be dissolved PERMANENTLY, and you will get NO REFUND.\") then\n\t\t\t\t\t\tsuccess, reason = network:invokeServer(\"playerRequest_leaveMyGuild\", true)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif not success then\n\t\t\t\t\tnetwork:fire(\"alert\", {text = reason; textColor3 = Color3.fromRGB(255, 100, 100);})\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\n\nend\n\n\nreturn module"
  },
  {
    "path": "src/StarterGui/hotbarHandler.lua",
    "content": "local HttpService = game:GetService(\"HttpService\")\n\nlocal module = {}\nmodule.priority = 12\n\nlocal hotbarFrame = script.Parent.gameUI.bottomRight.hotbarFrame\n\nmodule.xboxHotbarKeys = {\n\t{keyCode = Enum.KeyCode.ButtonX; image = \"rbxassetid://2528905407\"};\n\t{keyCode = Enum.KeyCode.ButtonY; image = \"rbxassetid://2528905431\"};\n\t{keyCode = Enum.KeyCode.ButtonB; image = \"rbxassetid://2528905016\"};\n\t{keyCode = Enum.KeyCode.ButtonL1; image = \"rbxassetid://2528905196\"};\n\t{keyCode = Enum.KeyCode.ButtonR1; image = \"rbxassetid://2528905316\"};\n\t{keyCode = Enum.KeyCode.DPadUp; image = \"rbxassetid://2528905167\"};\n\t{keyCode = Enum.KeyCode.DPadRight; image = \"rbxassetid://2528905134\"};\n\t{keyCode = Enum.KeyCode.DPadDown; image = \"rbxassetid://2528905076\"};\n\t{keyCode = Enum.KeyCode.DPadLeft; image = \"rbxassetid://2528905102\"};\n\t{keyCode = Enum.KeyCode.ButtonSelect; image = \"rbxassetid://2528905387\"};\n}\n\nlocal selected = false\n\nmodule.captureFocus = function()\n\twarn(\"hotbarHandler.captureFocus not ready!\")\nend\n\nmodule.releaseFocus = function()\n\twarn(\"hotbarHandler.releaseFocus not ready!\")\nend\n\nfunction module.init(Modules)\n\n\tlocal network = Modules.network\n\tlocal utilities = Modules.utilities\n\tlocal mapping = Modules.mapping\n\tlocal uiCreator = Modules.uiCreator\n\tlocal tween = Modules.tween\n\tlocal projectile = Modules.projectile\n\n\tlocal placeSetup = Modules.placeSetup\n\tlocal entitiesFolder = placeSetup.awaitPlaceFolder(\"entities\")\n\n\n\t-- the whild ber, in a 3 am frenzy, decides this is the best course of action to fix that one goddamn bug\n\t--spawn(function()\n\n\tlocal replicatedStorage = game.ReplicatedStorage\n\n\tlocal itemData = require(replicatedStorage.itemData)\n\tlocal abilityLookup = require(replicatedStorage.abilityLookup)\n\t-- equipment, consumable, miscellaneous\n\tlocal lastHotbarDataReceived = nil\n\tlocal lastInventoryDataReceived = nil\n\tlocal hotbarSlotPairing = {}\n\n\tfor _, child in pairs(hotbarFrame.content:GetChildren()) do\n\t\tif child:IsA(\"ImageButton\") then\n\t\t\tchild:Destroy()\n\t\tend\n\tend\n\n\tlocal function getInventoryCountLookupTableByItemId()\n\t\tlocal lookupTable = {}\n\n\t\tfor _, inventorySlotData in pairs(lastInventoryDataReceived) do\n\t\t\tif lookupTable[inventorySlotData.id] then\n\t\t\t\tlookupTable[inventorySlotData.id] = lookupTable[inventorySlotData.id] + (inventorySlotData.stacks or 1)\n\t\t\telse\n\t\t\t\tlookupTable[inventorySlotData.id] = inventorySlotData.stacks or 1\n\t\t\tend\n\t\tend\n\n\t\treturn lookupTable\n\tend\n\n\tlocal function abilityLevelFromAbilityData(abilityData, abilityId)\n\t\tfor _, ability in pairs(abilityData) do\n\t\t\tif ability.id == abilityId then\n\t\t\t\treturn ability.rank\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal hotbarButtonClicked = Instance.new(\"BindableEvent\")\n\n\t-- none of this should be in hotbarHandler\n\t-- but here it is anyways, this is the price of victory\n\t-- Davidii\n\tlocal function doAbilityTargetingImpl(abilityId, initialKeyCode)\n\t\t-- we only do this if we're on a touch screen\n\n\t\t-- todo: local setting\n\t\tif not game:GetService(\"UserInputService\").TouchEnabled then return true end\n\t\tif not abilityId then return false end\n\n\t\tlocal playerData = network:invoke(\"getLocalPlayerDataCache\")\n\t\tlocal abilityData = abilityLookup[abilityId](playerData)\n\t\tif not abilityData then return false end\n\n\t\tlocal player = game.Players.LocalPlayer\n\t\tif not player then return false end\n\n\t\tlocal char = player.Character\n\t\tif not char then return false end\n\n\t\tlocal manifest = char.PrimaryPart\n\t\tif not manifest then return false end\n\n\t\tlocal entityContainer = network:invoke(\"getMyClientCharacterContainer\")\n\t\tif not entityContainer then return false end\n\n\t\tlocal abilityRank = abilityLevelFromAbilityData(network:invoke(\"getCacheValueByNameTag\", \"abilities\"), abilityId)\n\t\tlocal statistics = Modules.ability_utilities.getAbilityStatisticsForRank(abilityData, abilityRank)\n\n\t\t-- targeting data tells us what to do\n\t\t-- if we don't have any, just say we targeted successfully\n\t\tlocal targetingData = abilityData.targetingData\n\t\tif not targetingData then\n\t\t\treturn true\n\t\tend\n\n\t\t-- some stuff can cancel targeting, this bool helps us handle that\n\t\tlocal canceled = false\n\n\t\tlocal function getTargetingValue(key)\n\t\t\tlocal value = targetingData[key]\n\n\t\t\tif typeof(value) == \"string\" then\n\t\t\t\tif statistics[value] then\n\t\t\t\t\tvalue = statistics[value]\n\t\t\t\telseif abilityData[value] then\n\t\t\t\t\tvalue = abilityData[value]\n\t\t\t\tend\n\n\t\t\telseif typeof(value) == \"function\" then\n\t\t\t\tvalue = value(network:invoke(\"getAbilityExecutionData\", abilityId))\n\t\t\tend\n\n\t\t\treturn value\n\t\tend\n\n\t\tlocal function waitForTargetingConfirmation(updateFunction)\n\t\t\tlocal cas = game:GetService(\"ContextActionService\")\n\t\t\tlocal heartbeat = game:GetService(\"RunService\").Heartbeat\n\t\t\tlocal waiting = true\n\n\t\t\tlocal hotbarKeyCodes = {} do\n\t\t\t\tfor _, keyCode in pairs(network:invoke(\"getHotbarKeyCodes\")) do\n\t\t\t\t\tif keyCode ~= initialKeyCode then\n\t\t\t\t\t\ttable.insert(hotbarKeyCodes, keyCode)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- sink all hotbar keys, mouse, and touch\n\t\t\tcas:BindAction(\n\t\t\t\t\"confirmTargeting\",\n\n\t\t\t\tfunction(name, state, input)\n\t\t\t\t\tif state ~= Enum.UserInputState.Begin then return end\n\n\t\t\t\t\twaiting = false\n\t\t\t\tend,\n\n\t\t\t\tfalse,\n\n\t\t\t\tEnum.UserInputType.MouseButton1,\n\t\t\t\tEnum.UserInputType.Touch,\n\t\t\t\tunpack(hotbarKeyCodes)\n\t\t\t)\n\n\t\t\tcas:BindAction(\n\t\t\t\t\"cancelTargeting\",\n\n\t\t\t\tfunction(name, state, input)\n\t\t\t\t\tif state ~= Enum.UserInputState.Begin then return end\n\n\t\t\t\t\tcanceled = true\n\t\t\t\t\twaiting = false\n\t\t\t\tend,\n\n\t\t\t\tfalse,\n\n\t\t\t\t-- default to a non-existent keycode if we don't have one\n\t\t\t\tinitialKeyCode or Enum.KeyCode.Unknown\n\t\t\t)\n\n\t\t\tlocal hotbarButtonClickedConn\n\t\t\thotbarButtonClickedConn = hotbarButtonClicked.Event:Connect(function()\n\t\t\t\tcanceled = true\n\t\t\t\twaiting = false\n\t\t\t\thotbarButtonClickedConn:Disconnect()\n\t\t\tend)\n\t\t\tprint(\"WEAREATTHEWAITINGLOOP\")\n\t\t\twhile waiting do\n\t\t\t\tupdateFunction(network:invoke(\"getAbilityExecutionData\", abilityId))\n\t\t\t\theartbeat:Wait()\n\t\t\tend\n\n\t\t\tcas:UnbindAction(\"confirmTargeting\")\n\t\t\tcas:UnbindAction(\"cancelTargeting\")\n\t\t\thotbarButtonClickedConn:Disconnect()\n\t\tend\n\n\t\tlocal function createTargetingPart()\n\t\t\tlocal part = Instance.new(\"Part\")\n\t\t\tpart.Anchored = true\n\t\t\tpart.CanCollide = false\n\t\t\tpart.TopSurface = Enum.SurfaceType.Smooth\n\t\t\tpart.BottomSurface = Enum.SurfaceType.Smooth\n\t\t\tpart.Material = Enum.Material.ForceField\n\t\t\tpart.Color = Color3.new(0.75, 0.75, 0.75)\n\t\t\tpart.Transparency = 0.5\n\t\t\tpart.CastShadow = false\n\t\t\treturn part\n\t\tend\n\n\t\tlocal customFunctionData, customFunctionGuid\n\t\tif targetingData.onStarted then\n\t\t\tcustomFunctionGuid = HttpService:GenerateGUID()\n\t\t\tlocal abilityExecutionData = network:invoke(\"getAbilityExecutionData\", abilityId)\n\n\t\t\tcustomFunctionData = targetingData.onStarted(\n\t\t\t\tentityContainer,\n\t\t\t\tabilityExecutionData\n\t\t\t)\n\n\t\t\tnetwork:fireServer(\"abilityTargetingSequenceStarted\", abilityId, customFunctionGuid, manifest, abilityExecutionData)\n\t\tend\n\n\t\t----\n\t\t-- direct sphere\n\t\t-- a sphere of radius radius at maximum range range\n\t\t-- no projectile involved\n\t\t----\n\t\tif targetingData.targetingType == \"directSphere\" then\n\t\t\tlocal range = getTargetingValue(\"range\")\n\n\t\t\tlocal sphere = createTargetingPart()\n\t\t\tsphere.Shape = Enum.PartType.Ball\n\t\t\tsphere.Size = Vector3.new(2, 2, 2) * getTargetingValue(\"radius\")\n\t\t\tsphere.Parent = entitiesFolder\n\n\t\t\twaitForTargetingConfirmation(function(executionData)\n\t\t\t\tlocal there = executionData[\"mouse-target-position\"]\n\t\t\t\tlocal here = manifest.Position\n\t\t\t\tlocal delta = there - here\n\t\t\t\tlocal distance = delta.Magnitude\n\t\t\t\tif distance > range then\n\t\t\t\t\tthere = here + (delta / distance) * range\n\t\t\t\tend\n\t\t\t\tsphere.Position = there\n\t\t\tend)\n\n\t\t\tsphere:Destroy()\n\n\t\t----\n\t\t-- direct cylinder\n\t\t-- like sphere except we show a cylinder instead\n\t\t-- revolutionary i know\n\t\t----\n\t\telseif targetingData.targetingType == \"directCylinder\" then\n\t\t\tlocal range = getTargetingValue(\"range\")\n\n\t\t\tlocal cylinder = createTargetingPart()\n\t\t\tcylinder.Shape = Enum.PartType.Cylinder\n\t\t\tcylinder.Size = Vector3.new(2, 2, 2) * getTargetingValue(\"radius\")\n\t\t\tcylinder.Parent = entitiesFolder\n\n\t\t\twaitForTargetingConfirmation(function(executionData)\n\t\t\t\tlocal there = executionData[\"mouse-target-position\"]\n\t\t\t\tlocal here = manifest.Position\n\t\t\t\tlocal delta = there - here\n\t\t\t\tlocal distance = delta.Magnitude\n\t\t\t\tif distance > range then\n\t\t\t\t\tthere = here + (delta / distance) * range\n\t\t\t\tend\n\t\t\t\tcylinder.CFrame = CFrame.new(there) * CFrame.Angles(0, 0, math.pi / 2)\n\t\t\tend)\n\n\t\t\tcylinder:Destroy()\n\n\t\t----\n\t\t-- line\n\t\t-- an area of a certain length and width\n\t\t-- issues forth from the character\n\t\t-- kinda specific? but hey\n\t\t----\n\t\telseif targetingData.targetingType == \"line\" then\n\t\t\tlocal width = getTargetingValue(\"width\")\n\t\t\tlocal length = getTargetingValue(\"length\")\n\n\t\t\tlocal box = createTargetingPart()\n\t\t\tbox.Size = Vector3.new(width, width, length)\n\t\t\tbox.Parent = entitiesFolder\n\n\t\t\twaitForTargetingConfirmation(function(executionData)\n\t\t\t\tlocal here = manifest.Position\n\t\t\t\tlocal there = executionData[\"mouse-target-position\"]\n\t\t\t\tlocal delta = (there - here) * Vector3.new(1, 0, 1)\n\t\t\t\tbox.CFrame = CFrame.new(here, here + delta) * CFrame.new(0, 0, -length / 2)\n\t\t\tend)\n\n\n\n\t\t\tbox:Destroy()\n\n\t\t----\n\t\t-- projectile\n\t\t-- a projectile with a certain gravity and speed\n\t\t-- single target, no area of effect\n\t\t----\n\t\telseif targetingData.targetingType == \"projectile\" then\n\t\t\tlocal entity = entityContainer:FindFirstChild(\"entity\")\n\t\t\tif not entity then return false end\n\n\t\t\tlocal projectionPart =\n\t\t\t\t(customFunctionData and customFunctionData.projectionPart) or\n\t\t\t\tentity:FindFirstChild(\"RightHand\")\n\t\t\tif not projectionPart then return false end\n\n\t\t\tlocal speed = getTargetingValue(\"projectileSpeed\")\n\t\t\tlocal gravity = getTargetingValue(\"projectileGravity\")\n\n\t\t\tlocal function getPosition()\n\t\t\t\tif projectionPart:IsA(\"Attachment\") then\n\t\t\t\t\treturn projectionPart.WorldPosition\n\t\t\t\telse\n\t\t\t\t\treturn projectionPart.Position\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal function getDirection(targetPosition)\n\t\t\t\treturn projectile.getUnitVelocityToImpact_predictive(getPosition(), speed, targetPosition, Vector3.new(), gravity)\n\t\t\tend\n\n\t\t\tlocal beam, attach0, attach1 = projectile.showProjectilePath(getPosition(), Vector3.new(), 3, gravity)\n\n\t\t\twaitForTargetingConfirmation(function(executionData)\n\t\t\t\tlocal direction = getDirection(executionData[\"mouse-target-position\"])\n\t\t\t\tif direction then\n\t\t\t\t\tprojectile.updateProjectilePath(beam, attach0, attach1, getPosition(), direction * speed, 3, gravity)\n\t\t\t\tend\n\t\t\tend)\n\n\t\t\tbeam:Destroy()\n\t\t\tattach0:Destroy()\n\t\t\tattach1:Destroy()\n\t\tend\n\n\t\tif targetingData.onEnded then\n\t\t\tlocal abilityExecutionData = network:invoke(\"getAbilityExecutionData\", abilityId)\n\n\t\t\ttargetingData.onEnded(\n\t\t\t\tentityContainer,\n\t\t\t\tabilityExecutionData,\n\t\t\t\tcustomFunctionData\n\t\t\t)\n\n\t\t\tnetwork:fireServer(\"abilityTargetingSequenceEnded\", abilityId, customFunctionGuid, manifest, abilityExecutionData)\n\t\tend\n\n\t\tif canceled then\n\t\t\treturn false\n\t\tend\n\n\t\treturn true\n\tend\n\n\tlocal abilityTargetingActive = false\n\tlocal function doAbilityTargeting(...)\n\t\tif abilityTargetingActive then return false end\n\t\t-- swap the above line for this for behavior that\n\t\t-- simply ensures thread safety instead of cancelling\n\t\t-- superfluous requests:\n\t\t-- while abilityTargetingActive do wait() end\n\t\tabilityTargetingActive = true\n\t\tlocal retVals = {doAbilityTargetingImpl(...)}\n\t\tabilityTargetingActive = false\n\t\treturn unpack(retVals)\n\tend\n\n\tlocal hotbarButtonDebounce = true\n\tlocal function onHotbarButtonDoubleClicked(hotbarButtonItem, ...)\n\t\tprint(\"hotbar double clicked\")\n\t\tprint(hotbarButtonDebounce)\n\n\t\tif not hotbarButtonDebounce then return end\n\t\thotbarButtonDebounce = false\n\t\tdelay(0.05, function()\n\t\t\thotbarButtonDebounce = true\n\t\tend)\n\n\t\t-- let other parts of the code know that another button's been clicked\n\t\thotbarButtonClicked:Fire()\n\n\t\tlocal hotbarSlotData = hotbarSlotPairing[hotbarButtonItem]\n\n\t\tif hotbarSlotData then\n\n\t\t\tlocal correspondingBG = hotbarFrame.decor:FindFirstChild(hotbarButtonItem.Name:gsub(\"hotbarButton\",\"\"))\n\t\t\tif correspondingBG then\n\t\t\t\tcorrespondingBG.ImageColor3 = Color3.fromRGB(0, 255, 255)\n\t\t\t\ttween(correspondingBG,{\"ImageColor3\"},Color3.fromRGB(72, 72, 76),0.5)\n\t\t\tend\n\n\t\t\tif hotbarSlotData.dataType == mapping.dataType.item then\n\t\t\t\tlocal inventorySlotData = network:invoke(\"getInventorySlotDataWithItemId\", hotbarSlotData.id)\n\t\t\t\tif inventorySlotData then\n\t\t\t\t\tnetwork:invoke(\"activateItemRequestLocal\", inventorySlotData)\n\t\t\t\tend\n\n\t\t\telseif hotbarSlotData.dataType == mapping.dataType.ability then\n\t\t\t\tnetwork:invoke(\"abilityUseRequest\", hotbarSlotData.id)\n\t\t\tend\n\t\tend\n\tend\n\n\n\tlocal cooldownEndTime\n\tlocal function showConsumableCooldown(duration)\n\t\tlocal durationEndTime = tick() + duration\n\t\tcooldownEndTime = durationEndTime\n\t\tfor hotbarButtonItem, hotbarSlotData in pairs(hotbarSlotPairing) do\n\t\t\tif hotbarSlotData and hotbarSlotData.dataType == mapping.dataType.item then\n\t\t\t\tif hotbarButtonItem:FindFirstChild(\"cooldown\") then\n\t\t\t\t\thotbarButtonItem.cooldown.Visible = true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\tspawn(function()\n\t\t\twait(duration)\n\t\t\tif cooldownEndTime == durationEndTime then\n\t\t\t\tfor hotbarButtonItem, hotbarSlotData in pairs(hotbarSlotPairing) do\n\t\t\t\t\tif hotbarSlotData and hotbarSlotData.dataType == mapping.dataType.item then\n\t\t\t\t\t\tif hotbarButtonItem:FindFirstChild(\"cooldown\") then\n\t\t\t\t\t\t\thotbarButtonItem.cooldown.Visible = false\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\tend\n\n\tnetwork:create(\"showConsumableCooldown\", \"BindableFunction\", \"OnInvoke\", showConsumableCooldown)\n\n\tlocal buttons = {}\n\n\tlocal function onGetHotbarSlotDataByHotbarSlotUI(hotbarSlotUI)\n\t\tif hotbarSlotUI then\n\t\t\treturn hotbarSlotPairing[hotbarSlotUI]\n\t\tend\n\n\t\treturn nil\n\tend\n\n\thotbarFrame.xboxBinding.Visible = false\n\thotbarFrame.xboxBindPrompt.Visible = false\n\n\tlocal function getHotbarSlotDataByHotbarSlotPosition(hotbarSlotPosition)\n\t\tif not lastHotbarDataReceived then return end\n\n\t\tfor _, hotbarSlotData in pairs(lastHotbarDataReceived) do\n\t\t\tif hotbarSlotData.position == hotbarSlotPosition then\n\t\t\t\treturn hotbarSlotData\n\t\t\tend\n\t\tend\n\n\t\treturn nil\n\tend\n\n\tlocal valueCache = {}\n\n\t-- function to set a property, remembering the original value of the property\n\t-- if called with nil value, restores to default value\n\tlocal function setValue(object, property, value)\n\t\tvalueCache[object] = valueCache[object] or {}\n\t\tif valueCache[object][property] == nil then\n\t\t\tvalueCache[object][property] = object[property]\n\t\tend\n\t\tif value then\n\t\t\tobject[property] = value\n\t\telse\n\t\t\tobject[property] = valueCache[object][property]\n\t\tend\n\tend\n\tlocal function setValues(valueTable)\n\t\tfor _, entry in pairs(valueTable) do\n\t\t\tsetValue(entry[1], entry[2], entry[3])\n\t\tend\n\tend\n\n\tlocal function inputUpdate()\n\n\t\thotbarFrame.selectedDecor.Visible = Modules.input.mode.Value == \"xbox\"\n\n\t\tif Modules.input.mode.Value == \"mobile\" then\n\t\t\tsetValues({\n\t\t\t\t{hotbarFrame.Parent.statusBars, \"Position\", UDim2.new(0.25,0,1,-70)};\n\t\t\t\t{hotbarFrame.Parent.Parent.statusEffects, \"Position\", UDim2.new(0,5,1,-110)};\n\t\t\t\t{hotbarFrame.content, \"Position\", UDim2.new(1,-220,1,-216)};\n\t\t\t\t{hotbarFrame.content, \"Size\", UDim2.new(0,200,0,190)};\n\t\t\t\t{hotbarFrame.decor, \"Position\", UDim2.new(1,-220,1,-220)};\n\t\t\t\t{hotbarFrame.decor, \"Size\", UDim2.new(0,200,0,200)};\n\t\t\t\t{hotbarFrame, \"Size\", UDim2.new(1.2,0,0,60)};\n\t\t\t\t{hotbarFrame.decor.UIGridLayout, \"StartCorner\", \"BottomRight\"};\n\t\t\t\t{hotbarFrame.content.UIGridLayout, \"StartCorner\", \"BottomRight\"};\n\t\t\t})\n\t\telse\n\t\t\tsetValues({\n\t\t\t\t{hotbarFrame.Parent.statusBars, \"Position\"};\n\t\t\t\t{hotbarFrame.Parent.Parent.statusEffects, \"Position\"};\n\t\t\t\t{hotbarFrame.content, \"Position\"};\n\t\t\t\t{hotbarFrame.content, \"Size\"};\n\t\t\t\t{hotbarFrame.decor, \"Position\"};\n\t\t\t\t{hotbarFrame.decor, \"Size\"};\n\t\t\t\t{hotbarFrame, \"Size\"};\n\t\t\t\t{hotbarFrame.decor.UIGridLayout, \"StartCorner\"};\n\t\t\t\t{hotbarFrame.content.UIGridLayout, \"StartCorner\"};\n\t\t\t})\n\t\tend\n\n\tend\n\n\tModules.input.mode.changed:connect(inputUpdate)\n\tinputUpdate()\n\n\thotbarFrame.selectedDecor.ImageTransparency = 1\n\tfor _, decor in pairs(hotbarFrame.selectedDecor:GetChildren()) do\n\t\tdecor.ImageTransparency = 1\n\tend\n\n\tmodule.focused = false\n\n\tlocal lastButtonFrom\n\n\tlocal promptButtonFrom\n\n\tfunction module.showSelectionPrompt(buttonFrom)\n\t\tpromptButtonFrom = buttonFrom\n\t\thotbarFrame.xboxBindPrompt.contents.itemIcon.Image = buttonFrom.Image\n\t\thotbarFrame.xboxBindPrompt.Visible = true\n\tend\n\n\tfunction module.hideSelectionPrompt(buttonFrom)\n\t\tif promptButtonFrom == buttonFrom then\n\t\t\thotbarFrame.xboxBindPrompt.Visible = false\n\t\tend\n\tend\n\n\tlocal lastFocus\n\n\tmodule.captureFocus = function()\n\t\tif not module.focused then\n\t\t\tmodule.focused = true\n\n\t\t\thotbarFrame.xboxBinding.Visible = false\n\n\t\t\tfor _, hotbarButton in pairs(hotbarFrame.content:GetChildren()) do\n\t\t\t\tif hotbarButton:FindFirstChild(\"xboxPrompt\") then\n\t\t\t\t\thotbarButton.xboxPrompt.Visible = true\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal selectedObject = game.GuiService.SelectedObject\n\t\t\tif selectedObject and selectedObject:FindFirstChild(\"bindable\") then\n\t\t\t\tlastButtonFrom = selectedObject\n\t\t\t\thotbarFrame.xboxBinding.contents.curveBG.itemIcon.Image = selectedObject.Image\n\t\t\t\thotbarFrame.xboxBinding.Visible = true\n\n\t\t\t\tlastFocus = Modules.focus.focused\n\t\t\t\tgame.GuiService.GuiNavigationEnabled = false\n\n\t\t\t\tfor _, hotbarButton in pairs(hotbarFrame.content:GetChildren()) do\n\t\t\t\t\tif hotbarButton:FindFirstChild(\"xboxPrompt\") then\n\t\t\t\t\t\thotbarButton.xboxPrompt.Visible = true\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tlastButtonFrom = nil\n\t\t\t\ttween(hotbarFrame.selectedDecor,{\"ImageTransparency\"},0,0.3)\n\t\t\t\tfor _, decor in pairs(hotbarFrame.selectedDecor:GetChildren()) do\n\t\t\t\t\tlocal val = tonumber(decor.Name)\n\t\t\t\t\tif val then\n\t\t\t\t\t\ttween(decor,{\"ImageTransparency\"},val,1-val)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t--hotbarFrame.gamepadPrompt.close.Visible = true\n\t\t\t--hotbarFrame.gamepadPrompt.open.Visible = false\n\t\tend\n\tend\n\n\t-- modules.uiCreator.processSwap(buttonFrom, buttonTo, isRightClickTrigger)\n\n\n\t--[[\n\tmodule.promptNewBinding = function(buttonFrom)\n\t\tif not module.focused then\n\t\t\tmodule.focused = true\n\t\t\tlastButtonFrom = buttonFrom\n\t\t\thotbarFrame.xboxBinding.Visible = true\n\n\t\t\tfor i,hotbarButton in pairs(hotbarFrame.content:GetChildren()) do\n\t\t\t\tif hotbarButton:FindFirstChild(\"xboxPrompt\") then\n\t\t\t\t\thotbarButton.xboxPrompt.Visible = true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\t]]\n\n\tmodule.releaseFocus = function(input)\n\t\tlocal endFocus = input == nil or input.KeyCode == Enum.KeyCode.ButtonL2\n\n\t\tif input then\n\t\t\tfor _, hotbarButton in pairs(hotbarFrame.content:GetChildren()) do\n\t\t\t\tif hotbarButton:FindFirstChild(\"xboxKeyCode\") then\n\t\t\t\t\tif input.KeyCode.Name == hotbarButton.xboxKeyCode.Value then\n\t\t\t\t\t\tif hotbarFrame.xboxBinding.Visible and lastButtonFrom then\n\t\t\t\t\t\t\tModules.uiCreator.processSwap(lastButtonFrom, hotbarButton, false)\n\t\t\t\t\t\t\tendFocus = true\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tonHotbarButtonDoubleClicked(hotbarButton)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif endFocus then\n\t\t\thotbarFrame.xboxBinding.Visible = false\n\n\t\t\tif lastFocus == Modules.focus.focused or game.GuiService.GuiNavigationEnabled == lastButtonFrom then\n\t\t\t\tgame.GuiService.GuiNavigationEnabled = true\n\t\t\tend\n\n\t\t\tfor _, hotbarButton in pairs(hotbarFrame.content:GetChildren()) do\n\t\t\t\tif hotbarButton:FindFirstChild(\"xboxPrompt\") then\n\t\t\t\t\thotbarButton.xboxPrompt.Visible = false\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif module.focused then\n\t\t\t\ttween(hotbarFrame.selectedDecor,{\"ImageTransparency\"},1,0.3)\n\t\t\t\tfor _, decor in pairs(hotbarFrame.selectedDecor:GetChildren()) do\n\t\t\t\t\tlocal val = tonumber(decor.Name)\n\t\t\t\t\tif val then\n\t\t\t\t\t\ttween(decor,{\"ImageTransparency\"},1,1-val)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\t\t--hotbarFrame.gamepadPrompt.close.Visible = false\n\t\t\t--hotbarFrame.gamepadPrompt.open.Visible = true\n\n\t\t\tmodule.focused = false\n\t\tend\n\tend\n\n\tnetwork:create(\"getHotbarSlotPairing\", \"BindableFunction\", \"OnInvoke\", function()\n\t\tlocal pairing = {}\n\n\t\tfor i=1,10 do\n\t\t\tlocal button = hotbarFrame.content:FindFirstChild(\"hotbarButton\"..i)\n\t\t\tif button then\n\t\t\t\ttable.insert(pairing, {button = button; data = hotbarSlotPairing[button]})\n\t\t\tend\n\t\tend\n\n\t\treturn pairing\n\tend)\n\n\tlocal hotbarButtonConnections = {}\n\n\tlocal function updateButtonItem(hotbarButtonItem, i, flareEffect)\n\t\tlocal trans_i = i % 10\n\n\t\tif hotbarButtonConnections[i] then\n\t\t\t-- name, connection\n\t\t\tfor _, connection in pairs(hotbarButtonConnections[i]) do\n\t\t\t\tconnection:Disconnect()\n\t\t\tend\n\t\telse\n\t\t\thotbarButtonConnections[i] = {}\n\t\tend\n\n\t\tlocal buttonConnections = hotbarButtonConnections[i]\n\n\t\tlocal hotbarSlotData = getHotbarSlotDataByHotbarSlotPosition(trans_i)\n\t\thotbarButtonItem.LayoutOrder = i\n\t\thotbarButtonItem.Name = \"hotbarButton\"..i\n\t\thotbarButtonItem.ImageTransparency = 1\n\t\thotbarButtonItem.Image = \"\"\n\n\t\t-- xbox juiciness\n\t\tlocal xboxInput = module.xboxHotbarKeys[i]\n\t\thotbarButtonItem.xboxPrompt.key.Image = xboxInput.image\n\t\thotbarButtonItem.xboxKeyCode.Value = xboxInput.keyCode.Name\n\t\thotbarButtonItem.duplicateCount.Visible = false\n\t\thotbarButtonItem.level.Visible = false\n\t\tlocal inventoryCountLookupTable = getInventoryCountLookupTableByItemId()\n\n\t\tfor _, child in pairs(hotbarButtonItem:GetChildren()) do\n\t\t\tif child:FindFirstChild(\"keyCode\") then\n\t\t\t\tchild.ImageTransparency = 0\n\t\t\t\tchild.backdrop.ImageTransparency = 0\n\t\t\t\tchild.keyCode.TextTransparency = 0\n\t\t\t\tchild.keyCode.TextStrokeTransparency = 0\n\t--\t\t\tchild.shadow.ImageTransparency = 0\n\t\t\tend\n\t\tend\n\n\t\tif hotbarSlotData then\n\t\t\thotbarSlotPairing[hotbarButtonItem] = hotbarSlotData\n\n\t\t\tlocal correspondingBG = hotbarFrame.decor:FindFirstChild(hotbarButtonItem.Name:gsub(\"hotbarButton\",\"\"))\n\t\t\tif correspondingBG then\n\t\t\t\tcorrespondingBG.ImageTransparency = 0\n\t\t\tend\n\n\t\t\tif hotbarSlotData.dataType == mapping.dataType.item then\n\t\t\t\thotbarButtonItem.duplicateCount.Text = tostring(inventoryCountLookupTable[hotbarSlotData.id] or 0)\n\n\t\t\t\tlocal itemBaseData = itemData[hotbarSlotData.id]\n\t\t\t\tif itemBaseData then\n\t\t\t\t\thotbarButtonItem.Image = itemBaseData.image\n\t\t\t\t\thotbarButtonItem.inputKey.Text = \"[\" .. tostring(hotbarSlotData.keyCode or trans_i) .. \"]\"\n\t\t\t\t\thotbarButtonItem.keyCode.Value = tostring(hotbarSlotData.keyCode or trans_i)\n\n\t\t\t\t\tuiCreator.setIsDoubleClickFrame(hotbarButtonItem, 0.2, onHotbarButtonDoubleClicked)\n\t\t\t\t\t-- add item tooltip\n\n\t\t\t\t\tbuttonConnections.hovered = hotbarButtonItem.MouseEnter:connect(function()\n\t\t\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\", itemBaseData, \"equipment\", hotbarSlotData)\n\t\t\t\t\tend)\n\n\t\t\t\t\tbuttonConnections.unhovered = hotbarButtonItem.MouseLeave:connect(function()\n\t\t\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\t\t\t\t\tend)\n\n\t\t\t\t\ttable.insert(buttons, hotbarButtonItem)\n\t\t\t\tend\n\t\t\t\thotbarButtonItem.duplicateCount.Visible = true\n\t\t\t\thotbarButtonItem.ImageTransparency \t= 0\n\t\t\telseif hotbarSlotData.dataType == mapping.dataType.ability then\n\t\t\t\t--JAYY LOOK HERE ABILIT STUFF\n\t\t\t\tlocal abilityData = network:invoke(\"getCacheValueByNameTag\", \"abilities\")\n\t\t\t\tlocal level = abilityLevelFromAbilityData(abilityData, hotbarSlotData.id)\n\n\t\t\t\tlocal playerData = network:invoke(\"getLocalPlayerDataCache\")\n\t\t\t\tlocal abilityBaseData = abilityLookup[hotbarSlotData.id]\n\n\t\t\t\tif abilityBaseData then\n\t\t\t\t\tlocal textSize = game.TextService:GetTextSize(hotbarButtonItem.level.Text, hotbarButtonItem.level.TextSize, hotbarButtonItem.level.Font, Vector2.new())\n\t\t\t\t\thotbarButtonItem.level.Size = UDim2.new(0, textSize.X + 2, 0, textSize.Y - 2)\n--\t\t\t\t\thotbarButtonItem.level.Visible = true\n\n\t\t\t\t\t--hotbarButtonItem.level.Text = utilities.romanNumerals[level]\n\t\t\t\t\t--local tier = statistics.tier or 1\n\t\t\t\t\t--hotbarButtonItem.level.TextColor3 = Modules.itemAcquistion.tierColors[tier]\n\n\t\t\t\t\thotbarButtonItem.Image = abilityBaseData.image\n\t\t\t\t\thotbarButtonItem.inputKey.Text = \"[\" .. tostring(hotbarSlotData.keyCode or trans_i) .. \"]\"\n\t\t\t\t\thotbarButtonItem.keyCode.Value = tostring(hotbarSlotData.keyCode or trans_i)\n\n\t\t\t\t\tuiCreator.setIsDoubleClickFrame(hotbarButtonItem, 0.2, onHotbarButtonDoubleClicked)\n\n\t\t\t\t\ttable.insert(buttons, hotbarButtonItem)\n\t\t\t\tend\n\n\t\t\t\thotbarButtonItem.duplicateCount.Visible = false\n\t\t\t\thotbarButtonItem.ImageTransparency \t= 0\n\t\t\tend\n\t\telse\n\t\t\tfor _, child in pairs(hotbarButtonItem:GetChildren()) do\n\t\t\t\tif child:FindFirstChild(\"keyCode\") then\n\t\t\t\t\tchild.ImageTransparency = 1\n\t\t\t\t\tchild.backdrop.ImageTransparency = 1\n\t\t\t\t\tchild.keyCode.TextTransparency = 1\n\t\t\t\t\tchild.keyCode.TextStrokeTransparency = 1\n\t\t--\t\t\tchild.shadow.ImageTransparency = 1\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal correspondingBG = hotbarFrame.decor:FindFirstChild(hotbarButtonItem.Name:gsub(\"hotbarButton\",\"\"))\n\t\t\tif correspondingBG then\n\t\t\t\tcorrespondingBG.ImageTransparency = 0.5\n\t\t--\t\tif correspondingBG:FindFirstChild(\"UIGradient\") then\n\t\t--\t\t\tcorrespondingBG.UIGradient:Destroy()\n\t\t--\t\tend\n\t\t--\t\tcorrespondingBG.shadow.ImageTransparency = 0.5\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function updateHotbar(updateHotbarData)\n\t\tif updateHotbarData then\n\t\t\tlastHotbarDataReceived = updateHotbarData\n\t\tend\n\n\t\tif lastHotbarDataReceived then\n\t\t\thotbarSlotPairing = {}\n\n\t\t\tfor i = 1, 10 do\n\t\t\t\tlocal hotbarButtonItem = hotbarFrame.content:FindFirstChild(\"hotbarButton\"..i) or hotbarFrame.hotbarButton:Clone()\n\t\t\t\thotbarButtonItem.Name = \"hotbarButton\"..i\n\t\t\t\thotbarButtonItem.Parent = hotbarFrame.content\n\t\t\t\thotbarButtonItem.Visible = true\n\t\t\t\tupdateButtonItem(hotbarButtonItem, i)\n\t\t\t\tuiCreator.drag.setIsDragDropFrame(hotbarButtonItem)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal Rand = Random.new()\n\tlocal oldxp = 0\n\n\tlocal function setup()\n\t\tlocal value = network:invoke(\"getCacheValueByNameTag\", \"exp\")\n\t\tlocal level = network:invoke(\"getCacheValueByNameTag\", \"level\")\n\n\t\tlocal xp = value\n\t\tlocal needed = math.floor(Modules.levels.getEXPToNextLevel(level))\n\t\toldxp = value\n\t\thotbarFrame.Frame.xp.title.Text = \"XP: \" .. utilities.formatNumber(xp) .. \"/\" .. utilities.formatNumber(needed)\n\t\thotbarFrame.Frame.xp.value.Size = UDim2.new(xp/needed,0,1,0)\n\t\thotbarFrame.Frame.xp.instant.Size = hotbarFrame.Frame.xp.value.Size\n\t\t--[[\n\t\tlocal gold = network:invoke(\"getCacheValueByNameTag\", \"gold\")\n\t\thotbarFrame.header.gold.Text = \"$\"..gold\n\t\t]]\n\tend\n\tsetup()\n\n\thotbarFrame.Frame.xp.MouseEnter:connect(function()\n\t\thotbarFrame.Frame.xp.title.Visible = true\n\t\tselected = true\n\tend)\n\n\thotbarFrame.Frame.xp.MouseLeave:connect(function()\n\t\thotbarFrame.Frame.xp.title.Visible = false\n\t\tselected = false\n\tend)\n\n\tlocal lastStats\n\n\tlocal function onPropogationRequestToSelf(propogationNameTag, propogationData)\n\t\tif propogationNameTag == \"hotbar\" then\n\t\t\tupdateHotbar(propogationData)\n\t\telseif propogationNameTag == \"inventory\" then\n\t\t\tlastInventoryDataReceived = propogationData\n\t\t\tupdateHotbar()\n\t\telseif propogationNameTag == \"abilities\" then\n\t\t\tupdateHotbar()\n\t\telseif propogationNameTag == \"nonSerializeData\" then\n\t\t\tlocal stats = propogationData.statistics_final\n\t\t\tif lastStats == nil then\n\t\t\t\tlastStats = stats\n\t\t\t\tupdateHotbar()\n\t\t\telse\n\t\t\t\tfor stat, value in pairs(lastStats) do\n\t\t\t\t\tif stats[stat] ~= value then\n\t\t\t\t\t\tlastStats = stats\n\t\t\t\t\t\tupdateHotbar()\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\telseif propogationNameTag == \"exp\" then\n\t\t\tlocal key = propogationNameTag\n\t\t\tlocal value = propogationData\n\n\t\t\tlocal level = network:invoke(\"getCacheValueByNameTag\", \"level\")\n\n\t\t\tlocal xp = value\n\t\t\tlocal needed = math.floor(Modules.levels.getEXPToNextLevel(level))\n\t\t\thotbarFrame.Frame.xp.title.Text = \"XP: \" .. utilities.formatNumber(xp) .. \"/\" .. utilities.formatNumber(needed)\n\n\t\t\t-- ahh crying internally\n\n\t\t\tlocal change = xp - oldxp\n\n\t\t\tlocal notice = hotbarFrame.Frame.noticeTemplate:Clone()\n\t\t\tnotice.Name = \"Notice\"\n\t\t\tnotice.TextTransparency = 1\n\t\t\tnotice.TextStrokeTransparency = 1\n\t\t\tnotice.Parent = hotbarFrame.Frame\n\t\t\tnotice.Visible = true\n\t\t\tnotice.Text = \"+\"..utilities.formatNumber(change)..\" EXP\"\n\t\t\tnotice.Position = UDim2.new(0.5,Rand:NextInteger(-50,50),0,Rand:NextInteger(-30,40))\n\t\t\tModules.tween(notice,{\"Position\"},notice.Position + UDim2.new(0,0,0,-100),3)\n\t\t\tModules.tween(notice,{\"TextTransparency\",\"TextStrokeTransparency\"},{0,0.7},1.5)\n\t\t\tgame.Debris:AddItem(notice, 10)\n\t\t\tlocal sampleParticle = hotbarFrame.Frame.xp.value.value.tip.sample\n\n\t\t\tfor _ = 1, 6 do\n\t\t\t\tlocal particle = sampleParticle:Clone()\n\t\t\t\tparticle.Rotation = math.random(1,90)\n\t\t\t\tparticle.Parent = sampleParticle.Parent\n\t\t\t\tparticle.Visible = true\n\t\t\t\ttween(particle,{\"Rotation\", \"Position\", \"Size\", \"BackgroundTransparency\"},\n\t\t\t\t{particle.Rotation + math.random(100,200), UDim2.new(0, math.random(3,25),0.5,math.random(-20,20)), UDim2.new(0,16,0,16), 1},math.random(60,130)/100)\n\t\t\t\tgame.Debris:AddItem(particle,1.5)\n\t\t\tend\n\n\t\t\tif xp < oldxp then\n\t\t\t\tModules.tween(hotbarFrame.Frame.xp.value,{\"Size\"},{UDim2.new(1,0,1,0)},0.5)\n\t\t\t\thotbarFrame.Frame.xp.instant.Size = UDim2.new(1,0,1,0)\n\t\t\t\tspawn(function()\n\t\t\t\t\twait(0.25)\n\t\t\t\t\thotbarFrame.Frame.xp.value.Size = UDim2.new(1,0,0,0)\n\t\t\t\t\tlocal goal = UDim2.new(xp/needed,0,1,0)\n\t\t\t\t\tModules.tween(hotbarFrame.Frame.xp.value,{\"Size\"},{goal},0.5)\n\t\t\t\t\thotbarFrame.Frame.xp.instant.Size = goal\n\t\t\t\tend)\n\t\t\telse\n\t\t\t\tModules.tween(hotbarFrame.Frame.xp.value,{\"Size\"},{UDim2.new(xp/needed,0,1,0)},1)\n\t\t\t\thotbarFrame.Frame.xp.instant.Size = UDim2.new(xp/needed,0,1,0)\n\t\t\tend\n\n\t\t\thotbarFrame.Frame.xp.Visible = true\n\t\t\toldxp = value\n\n\t\t\tspawn(function()\n\t\t\t\twait(0.5)\n\t\t\t\tModules.tween(notice,{\"TextTransparency\",\"TextStrokeTransparency\"},{1,1},1.5)\n\t\t\t\twait(3)\n\t\t\t\tif oldxp == value and not selected then\n\t\t\t\t\thotbarFrame.Frame.xp.Visible = false\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\tend\n\n\tlocal function main()\n\t\tlastInventoryDataReceived = network:invoke(\"getCacheValueByNameTag\", \"inventory\")\n\n\t\tlocal hotbarBinds = {\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"0\"}\n\t\t-- setup input\n\t\tfor i=1,10 do\n\t\t\tlocal ei = i % 10\n\t\t\tnetwork:invoke(\"addInputAction\",\"hotbarButton\"..i,function(inputObject)\n\t\t\t\tlocal button = hotbarFrame.content:FindFirstChild(\"hotbarButton\"..i)\n\t\t\t\tif button then\n\t\t\t\t\tlocal hotbarSlotData = hotbarSlotPairing[button]\n\t\t\t\t\tif hotbarSlotData then\n\t\t\t\t\t\thotbarSlotData.keyCode = inputObject.KeyCode\n\t\t\t\t\tend\n\n\t\t\t\t\tonHotbarButtonDoubleClicked(button)\n\t\t\t\tend\n\t\t\tend, hotbarBinds[i],10)\n\t\tend\n\n\t\tupdateHotbar(network:invoke(\"getCacheValueByNameTag\", \"hotbar\"))\n\t\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\t\tnetwork:create(\"getHotbarSlotDataByHotbarSlotUI\", \"BindableFunction\", \"OnInvoke\", onGetHotbarSlotDataByHotbarSlotUI)\n\tend\n\n\tmain()\n\tmodule.releaseFocus()\n--\tend)\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/input.lua",
    "content": "-- centralized input dohikey\n-- berezaa\n\n-- TO ADD AN ACTION TO KEYBIND:\n\n-- network:invoke(\"addInputAction\", UNIQUE_ACTION_NAME, FUNCTION_TO_CALL, DEFAULT_KEYBIND)\n\n-- Note: for DEFAULT_KEYBIND, use a shortcut from module.shortcuts, not the Enum.KeyCode.Name\n\nlocal USI = game:GetService(\"UserInputService\")\nlocal RunService = game:GetService(\"RunService\")\n\nlocal module = {}\nlocal keybinds = {}\nlocal inputObjects = {}\nlocal actions = {}\n\nmodule.actions = actions\n\nlocal gameUi = script.Parent.gameUI\n\nlocal mode = script.Parent.mode\nmodule.mode = mode\n\n-- track all gui objects that belong to a specific (or multiple) platforms\nlocal platformSpecificGuiObjects = {}\n\nmodule.shortcuts = {\n\tUnknown = \"???\";\n\t-- Numbers\n\tOne = \"1\"; KeypadOne = \"1\"; Two = \"2\"; KeypadTwo = \"2\";\n\tThree = \"3\"; KeypadThree = \"3\"; Four = \"4\"; KeypadFour = \"4\";\n\tFive = \"5\"; KeypadFive = \"5\"; Six = \"6\"; KeypadSix = \"6\";\n\tSeven = \"7\"; KeypadSeven = \"7\"; Eight = \"8\"; KeypadEight = \"8\";\n\tNine = \"9\"; KeypadNine = \"9\"; Zero = \"0\"; KeypadZero = \"0\";\n\t-- Everything else\n\tBackspace = \"BkS\"; Clear = \"Clr\"; Return = \"Rtn\"; Hash = \"#\"; Dollar = \"$\"; Percent = \"%\";\n\tAmpersand = \"&\"; Quote = \"\\\"\"; LeftParenthesis = \"(\"; RightParenthesis = \")\"; Asterisk = \"*\"; Plus = \"+\";\n\tComma = \",\"; Minus = \"-\"; Period = \".\"; Slash = \"/\"; Colon = \":\"; Semicolon = \";\";\n\tLessThan = \"<\"; GreaterThan = \">\"; Question = \"?\"; At = \"@\"; LeftBracket = \"[\"; RightBracket = \"]\";\n\tCaret = \"^\"; Underscore = \"_\"; Backquote = \"`\"; LeftCurly = \"{\"; RightCurly = \"}\"; Pipe = \"|\";\n\tTilde = \"~\"; Delete = \"Del\"; Insert = \"Ins\"; Home = \"Hm\"; PageUp = \"PgUp\"; PageDown = \"PgDn\";\n\tNumLock = \"NmLk\"; CapsLock = \"CpLk\"; ScrollLock = \"ScLk\"; RightShift = \"Rshft\"; LeftShift = \"Lshft\"; RightControl = \"Rctrl\";\n\tLeftControl = \"Lctrl\"; RightAlt = \"Ralt\"; LeftAlt = \"Lalt\"; RightMeta = \"Rmta\"; LeftMeta = \"Lmta\"; RightSuper = \"Rspr\";\n\tLeftSuper = \"Lspr\"; Break = \"Brk\"; Power = \"Pwr\";\n}\n\nlocal shortcuts = module.shortcuts\n\nlocal setupComplete\nlocal settings\n\nlocal function preferencesUpdated()\n\tif setupComplete then\n\t\tfor _, inputObject in pairs(inputObjects) do\n\t\t\tlocal actionName = inputObject.Name\n\t\t\tlocal action = actions[actionName]\n\t\t\tif inputObject and inputObject:IsA(\"GuiObject\") and inputObject:FindFirstChild(\"keyCode\") then\n\t\t\t\tif action and action.bindedTo then\n\t\t\t\t\tinputObject.keyCode.Text = action.bindedTo\n\t\t\t\telse\n\t\t\t\t\tinputObject.keyCode.Text = \" \"\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\tif settings then\n\t\t\tsettings.refreshKeybinds()\n\t\tend\n\tend\nend\n\nfunction module.addAction(name, target, default, priority)\n\n\tpriority = priority or 5\n\tlocal action = {[\"target\"] = target; [\"default\"] = default; [\"priority\"] = priority;}\n\n\t-- check for an existing binding\n\tfor input,actionName in pairs(keybinds) do\n\t\tif name == actionName then\n\t\t\taction[\"bindedTo\"] = input\n\t\t\taction[\"priority\"] = priority\n\n\t\t\t-- check for an inputObject\n\t\t\tpreferencesUpdated()\n\n\t\t\tbreak\n\t\tend\n\tend\n\n\tactions[name] = action\nend\n\nlocal add = module.addAction\n\n\n\nlocal function addInputObject(object)\n\tif object:IsA(\"GuiObject\") and object:IsDescendantOf(game.Players.LocalPlayer) then\n\t\tlocal actionName = object.Parent.Name\n\t\tobject.Name = actionName\n\n\t\tif object:IsA(\"ImageLabel\") or object:IsA(\"ImageButton\") then\n\t\t\tlocal colorValue = Instance.new(\"Color3Value\")\n\t\t\tcolorValue.Name = \"originalColor\"\n\t\t\tcolorValue.Value = object.ImageColor3\n\t\t\tcolorValue.Parent = object\n\t\tend\n\n\t\ttable.insert(inputObjects,object)\n\t\t--inputObjects[actionName] = object\n\n\t\t-- check for an existing action\n\t\tlocal action = actions[actionName]\n\t\tif object:FindFirstChild(\"keyCode\") then\n\n\t\t\tif action and action.bindedTo then\n\t\t\t\tobject.keyCode.Text = action.bindedTo\n\t\t\telse\n\t\t\t\tobject.keyCode.Text = \" \"\n\t\t\tend\n\t\tend\n\tend\nend\n\nfor _, object in pairs(game.CollectionService:GetTagged(\"inputObject\")) do\n\taddInputObject(object)\nend\n\ngame.CollectionService:GetInstanceAddedSignal(\"inputObject\"):connect(addInputObject)\nlocal network\n\nspawn(function()\n\tlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tnetwork = modules.load(\"network\")\n\tnetwork:create(\"addInputAction\",\"BindableFunction\",\"OnInvoke\",add)\nend)\n\nmodule.menuButtons = {}\nfor _, button in pairs(gameUi.right.buttons:GetChildren()) do\n\tif button:IsA(\"GuiButton\") then\n\t\tmodule.menuButtons[button.Name] = button\n\tend\nend\n\n\n-- make all buttons play a cute click sound\nlocal function buttonSetup(button)\n\tif button:IsA(\"GuiButton\") then\n\t\tbutton.MouseButton1Click:connect(function()\n\t\t\tlocal clickSound = Instance.new(\"Sound\")\n\t\t\tclickSound.Name = \"clickSound\"\n\t\t\tclickSound.SoundId = \"rbxassetid://997701190\"\n\t\t\tclickSound.Parent = button\n\t\t\tclickSound.Volume = 0.1\n\t\t\tclickSound:Play()\n\t\t\tgame.Debris:AddItem(clickSound,3)\n\t\tend)\n\tend\nend\nfor _, guiButton in pairs(gameUi:GetDescendants()) do\n\tbuttonSetup(guiButton)\nend\ngameUi.DescendantAdded:connect(function(guiButton)\n\tbuttonSetup(guiButton)\nend)\n\nlocal currentFocusFrame\n\n-- do something cool\n\nfunction module.setCurrentFocusFrame(focusFrame)\n\tif currentFocusFrame and currentFocusFrame ~= focusFrame then\n\t\tcurrentFocusFrame.Visible = false\n\tend\n\tcurrentFocusFrame = focusFrame\nend\n\nlocal function updateModeDisplay()\n\nend\n\nmodule.menuScale = 1\n\nlocal buttonsFrame = gameUi.right.buttons\n\nfunction module.init(Modules)\n\tlocal tween = Modules.tween\n\tlocal control = Modules.control\n\tlocal network = Modules.network\n\tlocal currentlySelectedButtonTooltip\n\n\tlocal function processGuiObject(guiObject)\n\t\tif guiObject:IsA(\"GuiObject\") then\n\t\t\tif (guiObject:FindFirstChild(\"xbox\") or guiObject:FindFirstChild(\"pc\") or guiObject:FindFirstChild(\"mobile\")) then\n\t\t\t\ttable.insert(platformSpecificGuiObjects, guiObject)\n\t\t\tend\n\t\t\tif guiObject:FindFirstChild(\"bindable\") then\n\t\t\t\tguiObject.SelectionGained:connect(function()\n\t\t\t\t\tModules.hotbarHandler.showSelectionPrompt(guiObject)\n\t\t\t\tend)\n\t\t\t\tguiObject.SelectionLost:connect(function()\n\t\t\t\t\tModules.hotbarHandler.hideSelectionPrompt(guiObject)\n\t\t\t\tend)\n\t\t\tend\n\t\t -- \"populateItemHoverFrameWithTextData\"\n\n\t\t\tif guiObject:FindFirstChild(\"tooltip\") then\n\t\t\t\tguiObject.MouseEnter:connect(function()\n\t\t\t\t\tcurrentlySelectedButtonTooltip = guiObject\n--\t\t\t\t\tif guiObject.Active then\n\t\t\t\t\t\tnetwork:invoke(\"populateItemHoverFrameWithTextData\", {text = guiObject.tooltip.Value; source = guiObject})\n--\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\t\tguiObject.MouseLeave:connect(function()\n\t\t\t\t\tif currentlySelectedButtonTooltip == guiObject then\n\t\t\t\t\t\tcurrentlySelectedButtonTooltip = nil\n\t\t\t\t\tend\n\t\t\t\t\tnetwork:invoke(\"populateItemHoverFrameWithTextData\", {source = guiObject})\n\t\t\t\tend)\n\n\t\t\t\tguiObject.tooltip.Changed:connect(function()\n\t\t\t\t\tif currentlySelectedButtonTooltip == guiObject then\n\t\t\t\t\t\tnetwork:invoke(\"populateItemHoverFrameWithTextData\", {text = guiObject.tooltip.Value; source = guiObject})\n\t\t\t\t\tend\n\t\t\t\tend)\n\n\t\t\tend\n\n\t\t\tif guiObject:IsA(\"ImageButton\") and (guiObject.Parent == buttonsFrame or guiObject.Image == \"rbxassetid://29202694692\" or guiObject.Image == \"rbxassetid://2920343923\" or guiObject.Image == \"rbxassetid://3437374574shadow\" or guiObject.Image == \"rbxassetid://3437374574\" or guiObject.Image == \"rbxassetid://3437766345\" ) then\n\t\t\t\tlocal function selectionGained()\n\t\t\t\t\tif guiObject.Active then\n\n\t\t\t\t\t\tif guiObject.Parent == buttonsFrame then\n\t\t\t\t--\t\t\ttween(guiObject.icon.UIScale, {\"Scale\"}, 1.2, 0.3)\n\t\t\t\t--\t\t\ttween(guiObject.icon, {\"ImageTransparency\"}, 0, 0.3)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t--[[\n\t\t\t\t\t\t\tlocal selection = guiObject:FindFirstChild(\"selectionGlow\")\n\t\t\t\t\t\t\tif selection == nil then\n\t\t\t\t\t\t\t\tif guiObject.Image == \"rbxassetid://3437374574shadow\" or guiObject.Image == \"rbxassetid://3437766345\" then\n\t\t\t\t\t\t\t\t\tselection = script.selectionGlow_new:Clone()\n\t\t\t\t\t\t\t\t\tselection.Name = \"selectionGlow\"\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tselection = script.selectionGlow:Clone()\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tselection.Parent = guiObject\n\t\t\t\t\t\t\t\tif guiObject.Parent == buttonsFrame then\n\t\t\t\t\t\t\t\t\tselection.ImageColor3 = guiObject.icon.ImageColor3\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\ttween(selection, {\"ImageTransparency\"}, 0.45, 0.3)\n\t\t\t\t\t\t\t]]\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tlocal function selectionLost()\n\n\t\t\t\t\tif guiObject.Parent == buttonsFrame then\n\t\t\t\t--\t\ttween(guiObject.icon.UIScale, {\"Scale\"}, 1, 0.3)\n\t\t\t\t--\t\ttween(guiObject.icon, {\"ImageTransparency\"}, 1, 0.3)\n\t\t\t\t\telse\n\t\t\t\t\t\tlocal selection = guiObject:FindFirstChild(\"selectionGlow\")\n\t\t\t\t\t\tif selection then\n\t\t\t\t\t\t\tif guiObject.Image == \"rbxassetid://3437766345\" or \"rbxassetid://3445513431\" then\n\t\t\t\t\t\t\t\ttween(selection, {\"ImageTransparency\"}, 1, 0)\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\ttween(selection, {\"ImageTransparency\"}, 1, 0.2)\n\t\t\t\t\t\t\tend\n\n\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tlocal function activated(inputObject)\n\n\t\t\t\t\tif inputObject.UserInputType == Enum.UserInputType.MouseButton1 or inputObject.UserInputType == Enum.UserInputType.Gamepad1 or inputObject.UserInputType == Enum.UserInputType.Touch then\n\n\n\n\t\t\t\t\t\tif guiObject.Active then\n\t\t\t\t\t\t\tselectionLost()\n\t\t\t\t\t\t\tif guiObject.Image == \"rbxassetid://3437766345\" then\n\t\t\t\t\t\t\t\tguiObject.Image = \"rbxassetid://3445513431\"\n\t\t\t\t\t\t\t\tlocal padding\n\t\t\t\t\t\t\t\tlocal existingPadding = guiObject:FindFirstChild(\"UIPadding\")\n\t\t\t\t\t\t\t\tif existingPadding == nil then\n\t\t\t\t\t\t\t\t\tpadding = script.Parent.effects.clickPadding:Clone()\n\t\t\t\t\t\t\t\t\tpadding.Parent = guiObject\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\texistingPadding.PaddingTop = UDim.new(existingPadding.PaddingTop.Scale, existingPadding.PaddingTop.Offset + 4)\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\trepeat wait() until inputObject.UserInputState == Enum.UserInputState.Cancel or inputObject.UserInputState == Enum.UserInputState.End\n\t\t\t\t\t\t\t\twait(0.1)\n\t\t\t\t\t\t\t\tguiObject.Image = \"rbxassetid://3437766345\"\n\t\t\t\t\t\t\t\tif padding then\n\t\t\t\t\t\t\t\t\tpadding:Destroy()\n\t\t\t\t\t\t\t\telseif existingPadding then\n\t\t\t\t\t\t\t\t\texistingPadding.PaddingTop = UDim.new(existingPadding.PaddingTop.Scale, existingPadding.PaddingTop.Offset - 4)\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\telseif guiObject.Image ~= \"rbxassetid://3445513431\" then\n\t\t\t\t\t\t\t\t-- old buttons\n\t\t\t\t\t\t\t\tlocal dark = guiObject:FindFirstChild(\"activationDark\")\n\t\t\t\t\t\t\t\tif dark == nil then\n\t\t\t\t\t\t\t\t\tdark = script.Parent.effects.activationDark:Clone()\n\t\t\t\t\t\t\t\t\tdark.Parent = guiObject\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\ttween(dark, {\"ImageTransparency\"}, 0.5, 0.15)\n\t\t\t\t\t\t\t\tspawn(function()\n\t\t\t\t\t\t\t\t\twait(0.2)\n\t\t\t\t\t\t\t\t\ttween(dark, {\"ImageTransparency\"}, 1, 0.3)\n\t\t\t\t\t\t\t\tend)\n\n\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tguiObject.MouseEnter:connect(selectionGained)\n\t\t\t\tguiObject.SelectionGained:connect(selectionGained)\n\n\t\t\t\tguiObject.MouseLeave:connect(selectionLost)\n\t\t\t\tguiObject.SelectionLost:connect(selectionLost)\n\n\t\t\t\tguiObject.InputBegan:connect(activated)\n\t\t\tend\n\n\t\tend\n\tend\n\n\tfor _, guiObject in pairs(gameUi.Parent:GetDescendants()) do\n\t\tprocessGuiObject(guiObject)\n\tend\n\n\tscript.Parent.DescendantAdded:connect(processGuiObject)\n\n\tlocal function setInputObjectsVisible(visible)\n\t\tfor _, inputObject in pairs(inputObjects) do\n\t\t\tif inputObject then\n\t\t\t\tinputObject.Visible = visible\n\t\t\tend\n\t\tend\n\tend\n\n\n\t-- display relevant information for that input mode\n\tfunction updateModeDisplay()\n\t\tsetInputObjectsVisible(mode.Value == \"pc\")\n\t\tfor i,guiObject in pairs(platformSpecificGuiObjects) do\n\t\t\tguiObject.Visible = guiObject:FindFirstChild(mode.Value) ~= nil\n\t\tend\n\t\tif mode.Value == \"mobile\" then\n\t\t\tmodule.menuScale = 0.7\n\t\t\tgameUi.leftBar.UIScale.Scale = 0.65\n--\t\t\tgameUi.leftBar.Position = UDim2.new(0, 5,1, -50)\n\n\t\t\tgameUi.bottomRight.UIScale.Scale = 0.65\n\t\t\tgameUi.bottomRight.Size = UDim2.new(1, 0,1.625, 0)\n\n\t\t\tif gameUi.bottomRight.hotbarFrame.content:FindFirstChild(\"hotbarButton10\") then\n\t\t\t\tgameUi.bottomRight.hotbarFrame.content:FindFirstChild(\"hotbarButton10\").Visible = false\n\t\t\tend\n\t\t\tif gameUi.bottomRight.hotbarFrame.decor:FindFirstChild(\"10\") then\n\t\t\t\tgameUi.bottomRight.hotbarFrame.decor:FindFirstChild(\"10\").Visible = false\n\t\t\tend\n\t\t\tgameUi.bottomRight.AnchorPoint = Vector2.new(1,1)\n\t\t\tgameUi.bottomRight.Position = UDim2.new(1,0,1,0)\n\t\telse\n\t\t\tmodule.menuScale = 1\n\n\t\t\tgameUi.leftBar.UIScale.Scale = 1\n\t\t\tgameUi.leftBar.Size = UDim2.new(0, 100,1, -250)\n---\t\t\tgameUi.leftBar.Position = UDim2.new(0, 5,1, -110)\n--\t\t\tgameUi.leftBar.Position = UDim2.new(0, 5,1, -200)\n\n\t\t\tgameUi.bottomRight.UIScale.Scale = 1\n\t\t\tgameUi.bottomRight.Size = UDim2.new(1, 0, 1, 0)\n\t\t\tgameUi.bottomRight.AnchorPoint = Vector2.new(0.5,1)\n\t\t\tgameUi.bottomRight.Position = UDim2.new(0.5,0,1,0)\n\t\t\tgameUi.bottomRight.Size = UDim2.new(1, 0, 1, 0)\n\t\t\tif gameUi.bottomRight.hotbarFrame.content:FindFirstChild(\"hotbarButton10\") then\n\t\t\t\tgameUi.bottomRight.hotbarFrame.content:FindFirstChild(\"hotbarButton10\").Visible = true\n\t\t\tend\n\t\t\tif gameUi.bottomRight.hotbarFrame.decor:FindFirstChild(\"10\") then\n\t\t\t\tgameUi.bottomRight.hotbarFrame.decor:FindFirstChild(\"10\").Visible = true\n\t\t\tend\n\t\t\tif mode.Value == \"xbox\" or game.GuiService:IsTenFootInterface() then\n\t\t\t\tmodule.menuScale = 1.2\n\t\t\t\tgameUi.bottomRight.UIScale.Scale = 1.2\n\t\t\tend\n\t\tend\n\t\tnetwork:fireServer(\"signal_inputChanged\", mode.Value)\n\tend\n\n\tif USI.TouchEnabled and not USI.MouseEnabled then\n\t\tmode.Value = \"mobile\"\n\tend\n\tmode.Changed:connect(updateModeDisplay)\n\tupdateModeDisplay()\n\t-- old postInit\n\tspawn(function()\n\t\tlocal remapping = false\n\n\t\tgame.GuiService.AutoSelectGuiEnabled = false\n\t\tgame.GuiService.CoreGuiNavigationEnabled = false\n\n\t\tlocal function changeKeybindAction(keybind, actionName)\n\t\t\t-- Make sure the action is valid\n\n\n\t\t\tlocal action = actions[actionName]\n\t\t\tif action == nil then\n\t\t\t\treturn warn(\"Action\",actionName,\"not found\")\n\t\t\tend\n\n\t\t\tif not setupComplete then\n\t\t\t\treturn warn(\"Input module not set up yet\")\n\t\t\tend\n\n\t\t\tif remapping then\n\t\t\t\treturn false\n\t\t\tend\n\t\t\tremapping = true\n\n\t\t\t-- Ask before overriding an existing action\n\t\t\tlocal existingBindActionName = keybinds[keybind]\n\t\t\tlocal existingAction\n\t\t\tif existingBindActionName then\n\t\t\t\texistingAction = actions[existingBindActionName]\n\t\t\t\tif existingAction then\n\t\t\t\t\tif not Modules.prompting_Fullscreen.prompt(\"This will override an existing action (\"..existingBindActionName..\"). Are you sure?\") then\n\t\t\t\t\t\tremapping = false\n\t\t\t\t\t\treturn false\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- Execute order 66\n\t\t\tlocal success = network:invokeServer(\"playerRequestSetKeyAction\",keybind,actionName)\n\t\t\tif success then\n\n\t\t\t\tlocal oldkeybind = action.bindedTo\n\t\t\t\tif oldkeybind then\n\t\t\t\t\tkeybinds[oldkeybind] = nil\n\t\t\t\tend\n\n\t\t\t\tkeybinds[keybind] = actionName\n\t\t\t\taction.bindedTo = keybind\n\n\t\t\t\tif existingAction then\n\t\t\t\t\texistingAction.bindedTo = nil\n\t\t\t\tend\n\n\t\t\t\tpreferencesUpdated()\n\t\t\t\tremapping = false\n\t\t\t\treturn true\n\t\t\telse\n\t\t\t\tremapping = false\n\t\t\t\twarn(\"Server rejected keybind change\")\n\t\t\tend\n\t\tend\n\n\t\tlocal function setup()\n\n\t\t\tlocal userSettings = network:invoke(\"getCacheValueByNameTag\", \"userSettings\")\n\t\t\tkeybinds = {}\n\t\t\tif userSettings and userSettings.keybinds then\n\t\t\t\tkeybinds = userSettings.keybinds\n\t\t\tend\n\n\t\t\tfor key,actionName in pairs(keybinds) do\n\t\t\t\tlocal action = actions[actionName]\n\t\t\t\tif action then\n\t\t\t\t\taction.bindedTo = key\n\t\t\t\tend\n\t\t\tend\n\n\t\t\t-- apply default keys (but don't override!)\n\t\t\tfor actionName,action in pairs(actions) do\n\t\t\t\tlocal default = action.default\n\t\t\t\tif default and action[\"bindedTo\"] == nil and keybinds[default] == nil then\n\t\t\t\t\t-- apply keybind (no need to ping the server)\n\t\t\t\t\tkeybinds[default] = actionName\n\t\t\t\t\taction[\"bindedTo\"] = default\n\t\t\t\tend\n\t\t\tend\n\t\t\tsetupComplete = true\n\t\t\tpreferencesUpdated()\n\t\t\tupdateModeDisplay()\n\t\tend\n\n\t\t--local hotbarBinds = {\"1\", \"2\", \"Q\", \"E\", \"R\", \"G\", \"V\", \"3\", \"4\", \"5\"}\n\n\t\t-- add hard-coded actions\n\n\n\t\tadd(\"openEquipment\",Modules.equipment.show,\"Q\",3)\n\t\tadd(\"openInventory\",Modules.inventory.show,\"E\",3)\n\t\tadd(\"openAbilities\",Modules.abilities.show,\"R\",3)\n\t\tadd(\"openSettings\",Modules.settings.show,\"G\",3)\n\n\t--\tadd(\"openQuestLog\",Modules.questLog.open,\"L\",3)\n\t--\tadd(\"openMonsterBook\",Modules.monsterBook.open,\"B\",3)\n\t--\tadd(\"openGuild\",Modules.guild.open,\"P\",3)\n\t\tadd(\"interact\",Modules.interaction.interact,\"C\",4)\n\t--\tadd(\"cameraLock\",function() network:invoke(\"toggleCameraLock\") end, \"Tab\", 4)\n\t\tadd(\"emote1\",function() network:invoke(\"playerRequest_performEmote\", \"dance\") end, \"N\", 7)\n\t\tadd(\"emote2\",function() network:invoke(\"playerRequest_performEmote\", \"sit\") end, \"M\", 7)\n\n\t\tadd(\"swapWeapons\", function()\n\t\t\tif not network:invoke(\"getIsPlayerCastingAbility\") then\n\t\t\t\tnetwork:fireServer(\"playerRequest_swapWeapons\")\n\t\t\tend\n\t\tend, \"`\", 8)\n\t\t--add(\"defaultPoint\",function() network:invoke(\"playerRequest_performEmote\", \"point\") end, \"P\", 7)\n\n\n\n\t\tUSI.InputChanged:connect(function(input, absorbed)\n\n\t\t\tif input.UserInputType == Enum.UserInputType.Keyboard or input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.MouseButton2 then\n\t\t\t\tmode.Value = \"pc\"\n\t\t\telseif input.UserInputType == Enum.UserInputType.Gamepad1 then\n\t\t\t\tmode.Value = \"xbox\"\n\t\t\telseif input.UserInputType == Enum.UserInputType.Touch then\n\t\t\t\tmode.Value = \"mobile\"\n\t\t\tend\n\n\t\t\tif mode.Value ~= \"mobile\" then\n\t--\t\t\tnetwork:invoke(\"setMobileMovementDirection\", nil)\n\t\t\t\tnetwork:fire(\"mobileMovementDirectionChanged\", nil)\n\t\t\t\tnetwork:fire(\"mobileCameraRotationChanged\", nil)\n\t\t\tend\n\t\tend)\n\n\t\t--mobile stuff\n\t\tlocal touchJoystick = gameUi:WaitForChild(\"touchJoystick\")\n\n\t\ttouchJoystick.Visible = false\n\n\t\tlocal touchJoystickActive = false\n\t\tlocal touchJoystickDirection\n\n\t\tlocal cameraMovementActive = false\n\n\t\tUSI.TouchStarted:connect(function(touch, processed)\n\t\t\tif not processed then\n\t\t\t\tlocal startPos = touch.Position\n\t\t\t\tif startPos.x < 300 and startPos.y > workspace.CurrentCamera.ViewportSize.y * 0.6 then\n\t\t\t\t\tif not touchJoystickActive then\n\t\t\t\t\t\ttouchJoystick.Position = UDim2.new(0, startPos.x, 0, startPos.y)\n\t\t\t\t\t\ttouchJoystickActive = true\n\t\t\t\t\t\ttouchJoystick.Visible = true\n\t\t\t\t\t\t-- while the same finger is still down\n\t\t\t\t\t\twhile touch.UserInputState ~= Enum.UserInputState.End and touch.UserInputState ~= Enum.UserInputState.Cancel do\n\t\t\t\t\t\t\tlocal pos = touch.Position\n\t\t\t\t\t\t\tlocal difference = pos - startPos\n\n\t\t\t\t\t\t\tcontrol.doSprint(difference.magnitude > 80)\n\n\t\t\t\t\t\t\tif difference.magnitude > 35 then\n\t\t\t\t\t\t\t\tdifference = difference.unit * 35\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\ttouchJoystick.stick.Position = UDim2.new(0.5, difference.X, 0.5, difference.Y)\n\t--\t\t\t\t\t\tnetwork:invoke(\"setMobileMovementDirection\", difference.unit)\n\t\t\t\t\t\t\tnetwork:fire(\"mobileMovementDirectionChanged\", difference.unit)\n\t\t\t\t\t\t\tRunService.RenderStepped:wait()\n\t\t\t\t\t\tend\n\t--\t\t\t\t\tnetwork:invoke(\"setMobileMovementDirection\", Vector2.new())\n\t\t\t\t\t\tnetwork:fire(\"mobileMovementDirectionChanged\", nil)\n\t\t\t\t\t\tcontrol.doSprint(false)\n\t\t\t\t\t\ttouchJoystickActive = false\n\t\t\t\t\t\ttouchJoystick.Visible = false\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tif not cameraMovementActive then\n\t\t\t\t\t\tcameraMovementActive = true\n\t\t\t\t\t\twhile touch.UserInputState ~= Enum.UserInputState.End and touch.UserInputState ~= Enum.UserInputState.Cancel do\n\t\t\t\t\t\t\tlocal pos = touch.Position\n\t\t\t\t\t\t\tlocal difference = pos - startPos\n\t\t\t\t\t\t\tnetwork:fire(\"mobileCameraRotationChanged\", difference * 0.3)\n\t\t\t\t\t\t\tstartPos = pos\n\t\t\t\t\t\t\tRunService.RenderStepped:wait()\n\t\t\t\t\t\tend\n\t\t\t\t\t\tcameraMovementActive = false\n\t\t\t\t\t\tnetwork:fire(\"mobileCameraRotationChanged\", Vector2.new())\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\n\n\n\t\tUSI.InputEnded:connect(function(input, absorbed)\n\t\t\tif input.KeyCode == Enum.KeyCode.ButtonL2 and Modules.hotbarHandler.focused then\n\t\t\t\tModules.hotbarHandler.releaseFocus(input)\n\t\t\tend\n\t\tend)\n\n\n\t\tUSI.InputBegan:connect(function(input, absorbed)\n\n\t\t\tif input.UserInputType == Enum.UserInputType.Keyboard or input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.MouseButton2 then\n\t\t\t\tmode.Value = \"pc\"\n\t\t\telseif input.UserInputType == Enum.UserInputType.Gamepad1 then\n\t\t\t\tmode.Value = \"xbox\"\n\t\t\telseif input.UserInputType == Enum.UserInputType.Touch then\n\t\t\t\tmode.Value = \"mobile\"\n\t\t\tend\n\n\t\t\tif not absorbed then\n\t\t\t\tif mode.Value == \"xbox\" then\n\n\t\t\t\t\tif Modules.hotbarHandler.focused then\n\t\t\t\t\t\tModules.hotbarHandler.releaseFocus(input)\n\t\t\t\t\telse\n\t\t\t\t\t\tif input.KeyCode == Enum.KeyCode.ButtonB then\n\t\t\t\t\t\t\tif Modules.interaction.currentInteraction then\n\t\t\t\t\t\t\t\tModules.interaction.stopInteract()\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tModules.focus.close()\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tgame.GuiService.SelectedObject = nil\n\t\t\t\t\t\t\tprint(\"$ nil a\")\n\t\t\t\t\t\telseif input.KeyCode == Enum.KeyCode.ButtonX then\n\t\t\t\t\t\t\tif Modules.itemAcquistion.closestItem then\n\t\t\t\t\t\t\t\tModules.itemAcquistion.pickupInputGained(input)\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tModules.interaction.interact()\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\telseif input.KeyCode == Enum.KeyCode.ButtonY then\n\t\t\t\t\t\t\tif game.GuiService.SelectedObject and game.GuiService.SelectedObject:FindFirstChild(\"bindable\") then\n\t\t\t\t\t\t\telse\n\t--\t\t\t\t\t\t\tModules.playerMenu.open()\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\telseif input.KeyCode == Enum.KeyCode.ButtonSelect then\n\t\t\t\t\t\t\tModules.settings.open()\n\t--\t\t\t\t\telseif input.KeyCode == Enum.KeyCode.DPadRight then\n\t--\t\t\t\t\t\tModules.skillBooks.open()\n\t\t\t\t\t\telseif input.KeyCode == Enum.KeyCode.ButtonL2 then\n\t\t\t\t\t\t\tModules.hotbarHandler.captureFocus()\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\telseif mode.Value == \"pc\" then\n\t\t\t\t\tlocal key = shortcuts[input.KeyCode.Name] or input.KeyCode.Name\n\n\t\t\t\t\t-- remapping active\n\t\t\t\t\tlocal remapTarget = Modules.settings.remapTarget\n\t\t\t\t\tif remapTarget then\n\t\t\t\t\t\tlocal actionName = Modules.settings.remapTarget.Name\n\t\t\t\t\t\tlocal action = actions[actionName]\n\t\t\t\t\t\tif action then\n\t\t\t\t\t\t\tlocal success = changeKeybindAction(key, actionName)\n\t\t\t\t\t\t\tif success then\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal actionName = keybinds[key]\n\t\t\t\t\tif actionName then\n\t\t\t\t\t\tlocal action = actions[actionName]\n\t\t\t\t\t\tif action and (not action.active) and type(action.target) == \"function\" then\n\n\t\t\t\t\t\t\taction.active = true\n\t\t\t\t\t\t\t-- cool visual effect\n\t\t\t\t\t\t\tfor i,inputObject in pairs(inputObjects) do\n\t\t\t\t\t\t\t\tif inputObject.Name == actionName then\n\t\t\t\t\t\t\t\t\tif inputObject and inputObject:IsA(\"ImageLabel\") then\n\t\t\t\t\t\t\t\t\t\tlocal color = Color3.fromRGB(0, 255, 255)\n\t\t\t\t\t\t\t\t\t\tinputObject.ImageColor3 = color\n\t\t\t\t\t\t\t\t\t\tif inputObject:FindFirstChild(\"keyCode\") then\n\t\t\t\t\t\t\t\t\t\t\tinputObject.keyCode.TextColor3 = color\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\taction.target(input) -- pass the input object\n\t\t\t\t\t\t\twait()\n\t\t\t\t\t\t\taction.active = false\n\t\t\t\t\t\t\twait(0.15)\n\n\t\t\t\t\t\t\t-- reset visual effect\n\t\t\t\t\t\t\tif not action.active then\n\t\t\t\t\t\t\t\tfor i,inputObject in pairs(inputObjects) do\n\t\t\t\t\t\t\t\t\tif inputObject.Name == actionName then\n\t\t\t\t\t\t\t\t\t\tif inputObject and inputObject:IsA(\"ImageLabel\") then\n\t\t\t\t\t\t\t\t\t\t\tlocal color = inputObject:FindFirstChild(\"originalColor\") and inputObject.originalColor.Value or Color3.fromRGB(40, 40, 40)\n\t\t\t\t\t\t\t\t\t\t\tinputObject.ImageColor3 = color\n\t\t\t\t\t\t\t\t\t\t\tif inputObject:FindFirstChild(\"keyCode\") then\n\t\t\t\t\t\t\t\t\t\t\t\tinputObject.keyCode.TextColor3 = Color3.new(1,1,1)\n\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\n\t\tfor i,button in pairs(buttonsFrame:GetChildren()) do\n\t\t\tif button:IsA(\"GuiButton\") then\n\t\t\t\tbutton.MouseButton1Click:connect(function()\n\t\t\t\t\tlocal action = actions[button.Name]\n\t\t\t\t\tif action and action.target then\n\t\t\t\t\t\taction.target()\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\tend\n\t\tend\n\tend)\n\nend\n\n\n\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/inspectPlayer.lua",
    "content": "-- player interaction - inspect page\n-- berezaa\nlocal module = {}\n\nlocal activePlayer\nlocal ui = script.Parent.gameUI.inspectPlayer\n\nlocal slotData = {}\n\n\nfunction module.init(Modules)\n\tlocal tween = Modules.tween\n\tlocal network = Modules.network\n\tlocal configuration = Modules.configuration\n\n\tlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\t\tlocal itemLookup = require(replicatedStorage:WaitForChild(\"itemData\"))\n\t\tlocal itemAttributes = require(replicatedStorage:WaitForChild(\"itemAttributes\"))\n\n\tlocal slots = {}\n\n\tlocal currentPartyInfo\n\n\tlocal function updatePartyInfo(partyInfo)\n\t\tif partyInfo == nil then\n\t\t\tpartyInfo = network:invokeServer(\"playerRequest_getMyPartyData\")\n\t\tend\n\t\tif partyInfo then\n\t\t\tfor _, partyMemberInfo in pairs(partyInfo.members) do\n\n\t\t\t\tlocal player = partyMemberInfo.player\n\n\t\t\t\tif player == game.Players.LocalPlayer then\n\t\t\t\t\tpartyInfo.isClientPartyLeader = partyMemberInfo.isLeader\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\tcurrentPartyInfo = partyInfo\n\tend\n\n\tlocal function isPlayerInParty(player)\n\t\tif currentPartyInfo and currentPartyInfo.members then\n\t\t\tfor _, entry in pairs(currentPartyInfo.members) do\n\t\t\t\tif entry.player == player then\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tnetwork:connect(\"signal_myPartyDataChanged\", \"OnClientEvent\", function(partyInfo)\n\t\tupdatePartyInfo(partyInfo)\n\t\tif ui.Visible and activePlayer then\n\t\t\tmodule.open(activePlayer, true)\n\t\tend\n\tend)\n\tspawn(function()\n\t\tupdatePartyInfo()\n\tend)\n\n\tfor _, slot in pairs(ui.content.equipment:GetChildren()) do\n\t\tif slot:IsA(\"ImageButton\") or slot:IsA(\"ImageLabel\") then\n\t\t\tslot.item.Image = \"\"\n\t\t\tslot.frame.Visible = false\n\t\t\tslot.shine.Visible = false\n\t\t\tslot.ImageTransparency = 0.5\n\t\t\tslot.LayoutOrder = 99\n\t\t\tslotData[slot] = {}\n\t\t\ttable.insert(slots, slot)\n\n\t\t\tlocal function show()\n\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\", itemLookup[slotData[slot].id], \"inspect\", slotData[slot])\n\t\t\tend\n\t\t\tlocal function hide()\n\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\t\t\tend\n\n\t\t\tslot.item.MouseEnter:connect(show)\n\t\t\tslot.item.SelectionGained:connect(show)\n\n\t\t\tslot.item.MouseLeave:connect(hide)\n\t\t\tslot.item.SelectionLost:connect(hide)\n\t\tend\n\tend\n\n\tfunction module.close()\n\t\tif ui.Visible then\n\t\t\tModules.focus.toggle(ui)\n\t\tend\n\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\t\tui.Visible = false\n\t\tactivePlayer = nil\n\tend\n\n\tui.close.Activated:connect(module.close)\n\n\tfunction module.open(player, isUpdate)\n\n\t\tif ui.Visible and player == activePlayer and not isUpdate then\n\t\t\tmodule.close()\n\t\t\treturn\n\t\tend\n\n\t\tfor i,button in pairs(ui.content.buttons:GetChildren()) do\n\t\t\tif button:FindFirstChild(\"fail\") then\n\t\t\t\tbutton.fail.Visible = false\n\t\t\tend\n\t\t\tif button:FindFirstChild(\"success\") then\n\t\t\t\tbutton.success.Visible = false\n\t\t\tend\n\t\t\tif button:FindFirstChild(\"icon\") then\n\t\t\t\tbutton.icon.Visible = true\n\t\t\tend\n\t\tend\n\n\n\n\t\tlocal partyButton = ui.content.buttons:FindFirstChild(\"request party\")\n\t\tlocal isPartyMember = isPlayerInParty(player)\n\t\tif isPartyMember then\n\n\t\t\tif player == game.Players.LocalPlayer then\n\t\t\t\tif currentPartyInfo then\n\t\t\t\t\tpartyButton.ImageColor3 = Color3.fromRGB(247, 0, 4)\n\t\t\t\t\tpartyButton.tooltip.Value = \"Leave Party\"\n\t\t\t\t\tpartyButton.Active = true\n\t\t\t\telse\n\t\t\t\t\tpartyButton.ImageColor3 = Color3.fromRGB(95, 95, 95)\n\t\t\t\t\tpartyButton.tooltip.Value = \"Not in Party\"\n\t\t\t\t\tpartyButton.Active = false\n\t\t\t\tend\n\t\t\telseif currentPartyInfo and currentPartyInfo.isClientPartyLeader then\n\t\t\t\tpartyButton.ImageColor3 = Color3.fromRGB(247, 0, 4)\n\t\t\t\tpartyButton.tooltip.Value = \"Kick from Party\"\n\t\t\t\tpartyButton.Active = true\n\t\t\telse\n\t\t\t\tpartyButton.ImageColor3 = Color3.fromRGB(95, 95, 95)\n\t\t\t\tpartyButton.tooltip.Value = \"Already in Party\"\n\t\t\t\tpartyButton.Active = false\n\t\t\tend\n\t\telse\n\t\t\tpartyButton.ImageColor3 = Color3.fromRGB(80, 247, 222)\n\t\t\tpartyButton.tooltip.Value = \"Invite to Party\"\n\t\t\tpartyButton.Active = true\n\t\tend\n\n\t\tif not ui.Visible then\n\t\t\tModules.focus.toggle(ui)\n\t\tend\n\t\tui.UIScale.Scale = (Modules.input.menuScale or 1) * 0.75\n\t\tModules.tween(ui.UIScale, {\"Scale\"}, (Modules.input.menuScale or 1), 0.5, Enum.EasingStyle.Bounce)\n\n\n\t\tactivePlayer = player\n\t\tui.content.info.username.Text = player.Name\n\n\t\tlocal class = player:FindFirstChild(\"class\") and player.class.Value:lower() or \"unknown\"\n\t\tlocal emblemVisible\n\t\tif class:lower() ~= \"adventurer\" then\n\t\t\tui.content.info.username.emblem.Image = \"rbxgameasset://Images/emblem_\"..class:lower()\n\t\t\tui.content.info.username.emblem.Visible = true\n\t\t\temblemVisible = true\n\t\telse\n\t\t\tui.content.info.username.emblem.Visible = false\n\t\tend\n\n\t\tfor i,statText in pairs(ui.content.stats:GetChildren()) do\n\t\t\tif statText:IsA(\"TextLabel\") then\n\t\t\tlocal stat = player:FindFirstChild(statText.Name)\n\t\t\t\tif stat then\n\t\t\t\t\tstatText.Text = statText.Name:upper()..\": \"..tostring(stat.Value)\n\t\t\t\telse\n\t\t\t\t\tstatText.Text = statText.Name:upper()..\": ???\"\n\t\t\t\tend\n\t\t\t\tlocal textBounds = game:GetService(\"TextService\"):GetTextSize(statText.Text, statText.TextSize, statText.Font, Vector2.new()).X\n\t\t\t\tstatText.Size = UDim2.new(0, textBounds + 5, 1, 0)\n\t\t\tend\n\t\tend\n\n\t\tlocal level = player:FindFirstChild(\"level\") and player.level.Value or 0\n\t\tui.content.level.Text = \"Lvl. \".. level\n\n\t\tlocal label = ui.content.info.level.value\n\t\tlabel.Text = \"Lvl. \"..level\n\n\t\tlocal xSize = game.TextService:GetTextSize(label.Text, label.TextSize, label.Font, Vector2.new()).X + 16\n\t\tui.content.info.level.Size = UDim2.new(0, xSize, 0, 26)\n\n\t\tlocal referrals = player:FindFirstChild(\"referrals\") and player.referrals.Value or 0\n\t\tif referrals > 0 then\n\t\t\tlocal label = ui.content.info.referrals.value\n\t\t\tlabel.Text = tostring(referrals)\n\n\t\t\tlocal xSize = game.TextService:GetTextSize(label.Text, label.TextSize, label.Font, Vector2.new()).X + 41\n\t\t\tui.content.info.referrals.Size = UDim2.new(0, xSize, 0, 26)\n\n\n\t\t\tui.content.info.referrals.Visible = true\n\t\telse\n\t\t\tui.content.info.referrals.Visible = false\n\t\tend\n\n\n\t\tlocal extend = (emblemVisible and 22) or 0\n\n\t\tlocal textSize = game:GetService(\"TextService\"):GetTextSize(player.Name, ui.content.info.username.TextSize, ui.content.info.username.Font, Vector2.new()).X\n\t\tui.content.info.username.Size = UDim2.new(0, textSize + 5 + (extend), 0, 30)\n\n\t\tfor i,slot in pairs(slots) do\n\t\t\tslot.item.Image = \"\"\n\t\t\tslot.frame.Visible = false\n\t\t\tslot.shine.Visible = false\n\t\t\tslot.ImageTransparency = 0.5\n\t\t\tslot.LayoutOrder = 99\n\t\t\tslot.stars.Visible = false\n\t\t\tslot.attribute.Visible = false\n\t\t\tModules.fx.setFlash(slot.frame, false)\n\t\tend\n\n\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart:FindFirstChild(\"appearance\") then\n\t\t\tlocal data = game:GetService(\"HttpService\"):JSONDecode(player.Character.PrimaryPart.appearance.Value)\n\t\t\tif data then\n\t\t\t\tif data.equipment then\n\t\t\t\t\tfor i, equipment in pairs(data.equipment) do\n\t\t\t\t\t\tlocal slot = slots[i]\n\t\t\t\t\t\tlocal realItem = itemLookup[equipment.id]\n\t\t\t\t\t\tslot.stars.Visible = false\n\t\t\t\t\t\tslot.attribute.Visible = false\n\t\t\t\t\t\tif realItem then\n\t\t\t\t\t\t\tslot.item.Image = realItem.image\n\t\t\t\t\t\t\tslot.item.ImageColor3 = Color3.new(1,1,1)\n\n\t\t\t\t\t\t\tslot.frame.Visible = true\n\t\t\t\t\t\t\tslot.shine.Visible = true\n\t\t\t\t\t\t\tslot.ImageTransparency = 0\n\n\t\t\t\t\t\t\tif equipment.attribute then\n\t\t\t\t\t\t\t\tlocal attributeData = itemAttributes[equipment.attribute]\n\t\t\t\t\t\t\t\tif attributeData and attributeData.color then\n\t\t\t\t\t\t\t\t\tslot.attribute.ImageColor3 = attributeData.color\n\t\t\t\t\t\t\t\t\tslot.attribute.Visible = true\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif equipment.dye then\n\t\t\t\t\t\t\t\tslot.item.ImageColor3 = Color3.fromRGB(equipment.dye.r, equipment.dye.g, equipment.dye.b)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tlocal titleColor, itemTier = Modules.itemAcquistion.getTitleColorForInventorySlotData(equipment)\n\n\t\t\t\t\t\t\tslot.frame.ImageColor3 = (itemTier and itemTier > 1 and titleColor) or Color3.fromRGB(106, 105, 107)\n\t\t\t\t\t\t\tslot.shine.ImageColor3 = titleColor or Color3.fromRGB(179, 178, 185)\n\t\t\t\t\t\t\tslot.shine.Visible = titleColor ~= nil and itemTier > 1\n\n\t\t\t\t\t\t\tModules.fx.setFlash(slot.frame, slot.shine.Visible)\n\n\t\t\t\t\t\t\tslotData[slot] = equipment\n\t\t\t\t\t\t\tslot.LayoutOrder = equipment.position\n\n\t\t\t\t\t\t\tslot.stars.Visible = false\n\t\t\t\t\t\t\tlocal upgrades = equipment.successfulUpgrades\n\t\t\t\t\t\t\tif upgrades then\n\t\t\t\t\t\t\t\tfor i,child in pairs(slot.stars:GetChildren()) do\n\t\t\t\t\t\t\t\t\tif child:IsA(\"ImageLabel\") then\n\t\t\t\t\t\t\t\t\t\tchild.ImageColor3 = titleColor or Color3.new(1,1,1)\n\t\t\t\t\t\t\t\t\t\tchild.Visible = false\n\t\t\t\t\t\t\t\t\telseif child:IsA(\"TextLabel\") then\n\t\t\t\t\t\t\t\t\t\tchild.TextColor3 = titleColor or Color3.new(1,1,1)\n\t\t\t\t\t\t\t\t\t\tchild.Visible = false\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tif upgrades <= 3 then\n\t\t\t\t\t\t\t\t\tfor i,star in pairs(slot.stars:GetChildren()) do\n\t\t\t\t\t\t\t\t\t\tlocal score = tonumber(star.Name)\n\t\t\t\t\t\t\t\t\t\tif score then\n\t\t\t\t\t\t\t\t\t\t\tstar.Visible = score <= upgrades\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tslot.stars.exact.Visible = false\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tslot.stars[\"1\"].Visible = true\n\t\t\t\t\t\t\t\t\tslot.stars.exact.Visible = true\n\t\t\t\t\t\t\t\t\tslot.stars.exact.Text = upgrades\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tslot.stars.Visible = true\n\n\t\t\t\t\t\t\tend\n\n\n\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\t-- yeet right here\n\n\t\t\t\tif ui.character.ViewportFrame:FindFirstChild(\"entity\") then\n\t\t\t\t\tui.character.ViewportFrame.entity:Destroy()\n\t\t\t\tend\n\n\t\t\t\tif ui.character.ViewportFrame:FindFirstChild(\"entity2\") then\n\t\t\t\t\tui.character.ViewportFrame.entity2:Destroy()\n\t\t\t\tend\n\n\t\t\t\tlocal camera = ui.character.ViewportFrame.CurrentCamera\n\t\t\t\tif camera == nil then\n\t\t\t\t\tcamera = Instance.new(\"Camera\")\n\t\t\t\t\tcamera.Parent = ui.character.ViewportFrame\n\t\t\t\t\tui.character.ViewportFrame.CurrentCamera = camera\n\t\t\t\tend\n\n\t\t\t\tlocal client = player\n\t\t\t\tlocal character = player.Character\n\t\t\t\tlocal mask = ui.character.ViewportFrame.characterMask\n\n\t\t\t\tlocal characterAppearanceData = {}\n\t\t\t\tcharacterAppearanceData.equipment \t= data.equipment or {}\n\t\t\t\tcharacterAppearanceData.accessories = data.accessories or {}\n\n\t\t\t\tlocal characterRender = network:invoke(\"createRenderCharacterContainerFromCharacterAppearanceData\",mask, characterAppearanceData or {}, client)\n\t\t\t\tcharacterRender.Parent = workspace.CurrentCamera\n\n\t\t\t\tlocal animationController = characterRender.entity:WaitForChild(\"AnimationController\")\n\t\t\t\t--[[\n\t\t\t\tlocal track = animationController:LoadAnimation(mask.idle)\n\t\t\t\ttrack.Looped = true\n\t\t\t\ttrack.Priority = Enum.AnimationPriority.Idle\n\t\t\t\ttrack:Play()\n\t\t\t\t]]\n\n\n\t\t\t\tlocal currentEquipment = network:invoke(\"getCurrentlyEquippedForRenderCharacter\", characterRender.entity)\n\n\n\t\t\t\tlocal weaponType do\n\t\t\t\t\tif currentEquipment[\"1\"] then\n\t\t\t\t\t\tweaponType = currentEquipment[\"1\"].baseData.equipmentType\n\t\t\t\t\tend\n\t\t\t\tend\n\n\n\t\t\t\tlocal track = network:invoke(\"getMovementAnimationForCharacter\", animationController, \"idling\", weaponType, nil)\n\n\t\t\t\tif track then\n\t\t\t\t\tif typeof(track) == \"Instance\" then\n\t\t\t\t\t\ttrack:Play()\n\t\t\t\t\telseif typeof(track) == \"table\" then\n\t\t\t\t\t\tfor ii, obj in pairs(track) do\n\t\t\t\t\t\t\tobj:Play()\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tspawn(function()\n\t\t\t\t\t\twhile true do\n\t\t\t\t\t\t\twait()\n\n\t\t\t\t\t\t\tif typeof(track) == \"Instance\" then\n\t\t\t\t\t\t\t\tif track.Length > 0 then\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\telseif typeof(track) == \"table\" then\n\t\t\t\t\t\t\t\tlocal isGood = true\n\t\t\t\t\t\t\t\tfor ii, obj in pairs(track) do\n\t\t\t\t\t\t\t\t\tif track.Length == 0 then\n\t\t\t\t\t\t\t\t\t\tisGood = false\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif isGood then\n\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif characterRender then\n\n\t\t\t\t\t\t\tif ui.character.ViewportFrame:FindFirstChild(\"entity\") then\n\t\t\t\t\t\t\t\tui.character.ViewportFrame.entity:Destroy()\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tlocal entity \t= characterRender.entity\n\t\t\t\t\t\t\tentity.Parent \t= ui.character.ViewportFrame\n\n\t\t\t\t\t\t\tcharacterRender:Destroy()\n\t\t\t\t\t\t\tlocal focus \t= CFrame.new(entity.PrimaryPart.Position + entity.PrimaryPart.CFrame.lookVector * 6.3, entity.PrimaryPart.Position) * CFrame.new(3,0,0)\n\t\t\t\t\t\t\tcamera.CFrame \t= CFrame.new(focus.p + Vector3.new(0,1.5,0), entity.PrimaryPart.Position + Vector3.new(0,0.5,0))\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\t\t\t\telse\n\t\t\t\t\tlocal track = animationController:LoadAnimation(mask.idle)\n\t\t\t\t\ttrack.Looped = true\n\t\t\t\t\ttrack.Priority = Enum.AnimationPriority.Idle\n\t\t\t\t\ttrack:Play()\n\t\t\t\tend\n\n\t\t--[[\n\n\t\t\t\tspawn(function()\n\t\t\t\t\twait()\n\t\t\t\t\tif characterRender then\n\t\t\t\t\t\tlocal entity = characterRender.entity\n\t\t\t\t\t\tentity.Parent = ui.character.ViewportFrame\n\n\t\t\t\t\t\tcharacterRender:destroy()\n\t\t\t\t\t\tlocal focus = CFrame.new(entity.PrimaryPart.Position + entity.PrimaryPart.CFrame.lookVector * 6.3, entity.PrimaryPart.Position) * CFrame.new(3,0,0)\n\t\t\t\t\t\tcamera.CFrame = CFrame.new(focus.p + Vector3.new(0,1.5,0), entity.PrimaryPart.Position + Vector3.new(0,0.5,0))\n\n\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t]]\n\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction module.tradeRequest()\n\t\tif activePlayer and ui.Visible and ui.content.buttons[\"request trade\"].icon.Visible then\n\t\t\tif not configuration.getConfigurationValue(\"isTradingEnabled\") then\n\t\t\t\tModules.notifications.alert({text = \"Trading is temporarily disabled.\"}, 2)\n\t\t\tend\n\n\t\t\tlocal button = ui.content.buttons[\"request trade\"]\n\t\t\tbutton.icon.Visible = false\n\t\t\tlocal success = network:invokeServer(\"playerRequest_requestTrade\", activePlayer)\n\n\t\t\tif success then\n\t\t\t\tbutton.success.Visible = true\n\t\t\t\tModules.notifications.alert({text = \"Sent \"..activePlayer.Name..\" a trade request.\"}, 2)\n\t\t\telse\n\t\t\t\tbutton.fail.Visible = true\n\t\t\tend\n\n\t\t\tspawn(function()\n\t\t\t\twait(1)\n\t\t\t\tbutton.fail.Visible = false\n\t\t\t\tbutton.success.Visible = false\n\t\t\t\tbutton.icon.Visible = true\n\t\t\tend)\n\n\t\tend\n\tend\n\n\n\n\n\n\tui.content.buttons[\"request trade\"].Activated:Connect(module.tradeRequest)\n\tnetwork:invoke(\"addInputAction\", \"request trade\", module.tradeRequest, \"T\", 6)\n\n\n\tfunction module.guildRequest()\n\t\tlocal button = ui.content.buttons[\"guild\"]\n\t\tif activePlayer and ui.Visible and button.Visible and button.icon.Visible then\n\t\t\tlocal activePlayerName = activePlayer.Name\n\t\t\tModules.notifications.alert({text = \"Invited \"..activePlayerName..\" to your guild.\"}, 2)\n\n\t\t\tbutton.icon.Visible = false\n\n\t\t\tlocal waiting = true\n\n\t\t\tbutton.loading.Visible = true\n\t\t\tspawn(function()\n\t\t\t\twhile waiting do\n\t\t\t\t\tbutton.loading.Text = \".\"\n\t\t\t\t\twait(0.5)\n\t\t\t\t\tbutton.loading.Text = \"..\"\n\t\t\t\t\twait(0.5)\n\t\t\t\t\tbutton.loading.Text = \"...\"\n\t\t\t\t\twait(0.5)\n\t\t\t\tend\n\t\t\t\tbutton.loading.Visible = false\n\t\t\tend)\n\n\t\t\tlocal success, status = network:invokeServer(\"playerRequest_invitePlayerToGuild\", activePlayer)\n\t\t\tbutton.loading.Visible = false\n\t\t\twaiting = false\n\n\t\t\tif success then\n\t\t\t\tbutton.success.Visible = true\n\t\t\t\tModules.notifications.alert({text = activePlayerName .. \" accepted your guild invite!\"}, 2)\n\t\t\telse\n\t\t\t\tModules.notifications.alert({text = status or \"The guild invite failed.\"}, 2)\n\t\t\t\tbutton.fail.Visible = true\n\t\t\tend\n\n\t\t\tspawn(function()\n\t\t\t\twait(1)\n\t\t\t\tbutton.fail.Visible = false\n\t\t\t\tbutton.success.Visible = false\n\t\t\t\tbutton.icon.Visible = true\n\t\t\tend)\n\t\tend\n\tend\n\n\tui.content.buttons[\"guild\"].Activated:connect(module.guildRequest)\n\n\tfunction module.partyRequest()\n\t\tif activePlayer and ui.Visible and ui.content.buttons[\"request party\"].icon.Visible then\n\n\n\t\t\tlocal button = ui.content.buttons[\"request party\"]\n\t\t\tbutton.icon.Visible = false\n\n\t\t\tlocal target = activePlayer\n\t\t\tlocal success, reason\n\n\t\t\tif button.tooltip.Value == \"Kick from Party\" then\n\t\t\t\tpcall(function()\n\t\t\t\t\tsuccess, reason = network:invokeServer(\"playerRequest_leaveParty\", target)\n\t\t\t\tend)\n\t\t\telseif button.tooltip.Value == \"Leave Party\" then\n\t\t\t\tpcall(function()\n\t\t\t\t\tsuccess, reason = network:invokeServer(\"playerRequest_leaveParty\")\n\t\t\t\tend)\n\t\t\t\tif success then\n\t\t\t\t\tbutton.success.Visible = true\n\t\t\t\t\tModules.notifications.alert({text = \"Left the party.\"}, 2)\n\t\t\t\telseif reason then\n\t\t\t\t\tbutton.fail.Visible = true\n\t\t\t\t\tModules.notifications.alert({text = reason or \"Error occured\"}, 2)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tpcall(function()\n\t\t\t\t\tsuccess, reason = network:invokeServer(\"playerRequest_invitePlayerToMyParty\", target)\n\t\t\t\tend)\n\t\t\t\tif success then\n\t\t\t\t\tbutton.success.Visible = true\n\t\t\t\t\tModules.notifications.alert({text = \"Invited \"..target.Name..\" to the party.\"}, 2)\n\t\t\t\telseif reason then\n\t\t\t\t\tbutton.fail.Visible = true\n\t\t\t\t\tModules.notifications.alert({text = reason or \"Error occured\"}, 2)\n\t\t\t\tend\n\t\t\tend\n\n\n\n\t\t\tlocal waiting = true\n\n\n\n\n\t\t\twaiting = false\n\n\n\n\n\t\t\tspawn(function()\n\t\t\t\twait(1)\n\t\t\t\tbutton.fail.Visible = false\n\t\t\t\tbutton.success.Visible = false\n\t\t\t\tbutton.icon.Visible = true\n\t\t\tend)\n\n\t\tend\n\tend\n\n\tui.content.buttons[\"request party\"].Activated:Connect(module.partyRequest)\n\tnetwork:invoke(\"addInputAction\", \"request party\", module.partyRequest, \"P\", 6)\n\n\tfunction module.duelRequest()\n\t\tif activePlayer and ui.Visible and ui.content.buttons[\"request duel\"].icon.Visible then\n\n\t\t\tlocal button = ui.content.buttons[\"request duel\"]\n\t\t\tbutton.icon.Visible = false\n\n\t\t\tlocal target = activePlayer\n\t\t\tlocal success = network:invokeServer(\"playerRequest_requestChallenge\", target)\n\n\n\t\t\tif success then\n\t\t\t\tbutton.success.Visible = true\n\t\t\t\tModules.notifications.alert({text = \"Sent \"..target.Name..\" a duel challenge.\"}, 2)\n\t\t\telse\n\t\t\t\tbutton.fail.Visible = true\n\t\t\tend\n\t\t\tspawn(function()\n\t\t\t\twait(1)\n\t\t\t\tbutton.fail.Visible = false\n\t\t\t\tbutton.success.Visible = false\n\t\t\t\tbutton.icon.Visible = true\n\t\t\tend)\n\t\tend\n\tend\n\n\tui.content.buttons[\"request duel\"].Activated:Connect(module.duelRequest)\n\tnetwork:invoke(\"addInputAction\", \"request duel\", module.duelRequest, \"U\", 6)\nend\n\n\n\nreturn module\n\n\n"
  },
  {
    "path": "src/StarterGui/inspectPlayerPreview.lua",
    "content": "-- player interaction - inspect page\n-- berezaa\nlocal module = {}\n\nlocal activePlayer\n\nlocal ui = script.Parent.gameUI.inspectPlayerPreview\nlocal slotData = {}\n\n\nfunction module.init(Modules)\n\tlocal tween = Modules.tween\n\tlocal network = Modules.network\n\tlocal configuration = Modules.configuration\n\n\tlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\t\tlocal itemLookup = require(replicatedStorage:WaitForChild(\"itemData\"))\n\t\tlocal itemAttributes = require(replicatedStorage:WaitForChild(\"itemAttributes\"))\n\n\tlocal slots = {}\n\n\n\tfor i,slot in pairs(ui.content.equipment:GetChildren()) do\n\t\tif slot:IsA(\"ImageButton\") or slot:IsA(\"ImageLabel\") then\n\t\t\tslot.item.Image = \"\"\n\t\t\tslot.frame.Visible = false\n\t\t\tslot.shine.Visible = false\n\t\t\tslot.ImageTransparency = 0.5\n\t\t\tslot.LayoutOrder = 99\n\t\t\tslotData[slot] = {}\n\t\t\ttable.insert(slots, slot)\n\n\t\t\tlocal function show()\n\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\", itemLookup[slotData[slot].id], \"inspect\", slotData[slot])\n\t\t\tend\n\t\t\tlocal function hide()\n\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\t\t\tend\n\n\t\t\tslot.item.MouseEnter:connect(show)\n\t\t\tslot.item.SelectionGained:connect(show)\n\n\t\t\tslot.item.MouseLeave:connect(hide)\n\t\t\tslot.item.SelectionLost:connect(hide)\n\t\tend\n\tend\n\n\tlocal lastSelectedButton\n\n\tfunction module.close()\n\n\t\tui.Visible = false\n\t\tactivePlayer = nil\n\n\tend\n\n\n\tfunction module.open(player, selectedButton)\n\n\t\tlastSelectedButton = selectedButton\n\n\t\tactivePlayer = player\n\t\tui.content.info.username.Text = player.Name\n\n\t\tlocal class = player:FindFirstChild(\"class\") and player.class.Value:lower() or \"unknown\"\n\t\tlocal emblemVisible\n\t\tif class:lower() ~= \"adventurer\" then\n\t\t\tui.content.info.username.emblem.Image = \"rbxgameasset://Images/emblem_\"..class:lower()\n\t\t\tui.content.info.username.emblem.Visible = true\n\t\t\temblemVisible = true\n\t\telse\n\t\t\tui.content.info.username.emblem.Visible = false\n\t\tend\n\n\t\tlocal level = player:FindFirstChild(\"level\") and player.level.Value or 0\n\n\t\tlocal label = ui.content.info.level.value\n\t\tlabel.Text = \"Lvl. \"..level\n\n\t\tlocal xSize = game.TextService:GetTextSize(label.Text, label.TextSize, label.Font, Vector2.new()).X + 16\n\t\tui.content.info.level.Size = UDim2.new(0, xSize, 0, 26)\n\n\t\tlocal referrals = player:FindFirstChild(\"referrals\") and player.referrals.Value or 0\n\t\tif referrals > 0 then\n\t\t\tlocal label = ui.content.info.referrals.value\n\t\t\tlabel.Text = tostring(referrals)\n\n\t\t\tlocal xSize = game.TextService:GetTextSize(label.Text, label.TextSize, label.Font, Vector2.new()).X + 41\n\t\t\tui.content.info.referrals.Size = UDim2.new(0, xSize, 0, 26)\n\n\n\t\t\tui.content.info.referrals.Visible = true\n\t\telse\n\t\t\tui.content.info.referrals.Visible = false\n\t\tend\n\n\n\t\tlocal extend = (emblemVisible and 22) or 0\n\n\t\tlocal textSize = game:GetService(\"TextService\"):GetTextSize(player.Name, ui.content.info.username.TextSize, ui.content.info.username.Font, Vector2.new()).X\n\t\tui.content.info.username.Size = UDim2.new(0, textSize + 5 + (extend), 0, 30)\n\n\t\tfor i,slot in pairs(slots) do\n\t\t\tslot.item.Image = \"\"\n\t\t\tslot.frame.Visible = false\n\t\t\tslot.shine.Visible = false\n\t\t\tslot.ImageTransparency = 0.5\n\t\t\tslot.LayoutOrder = 99\n\t\t\tslot.stars.Visible = false\n\t\t\tslot.attribute.Visible = false\n\t\t\tModules.fx.setFlash(slot.frame, false)\n\t\tend\n\n\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart:FindFirstChild(\"appearance\") then\n\t\t\tlocal data = game:GetService(\"HttpService\"):JSONDecode(player.Character.PrimaryPart.appearance.Value)\n\t\t\tif data then\n\t\t\t\tif data.equipment then\n\t\t\t\t\tfor i, equipment in pairs(data.equipment) do\n\t\t\t\t\t\tlocal slot = slots[i]\n\t\t\t\t\t\tlocal realItem = itemLookup[equipment.id]\n\t\t\t\t\t\tslot.stars.Visible = false\n\t\t\t\t\t\tslot.attribute.Visible = false\n\t\t\t\t\t\tif realItem then\n\t\t\t\t\t\t\tslot.item.Image = realItem.image\n\t\t\t\t\t\t\tslot.item.ImageColor3 = Color3.new(1,1,1)\n\n\t\t\t\t\t\t\tslot.frame.Visible = true\n\t\t\t\t\t\t\tslot.shine.Visible = true\n\t\t\t\t\t\t\tslot.ImageTransparency = 0\n\n\t\t\t\t\t\t\tif equipment.attribute then\n\t\t\t\t\t\t\t\tlocal attributeData = itemAttributes[equipment.attribute]\n\t\t\t\t\t\t\t\tif attributeData and attributeData.color then\n\t\t\t\t\t\t\t\t\tslot.attribute.ImageColor3 = attributeData.color\n\t\t\t\t\t\t\t\t\tslot.attribute.Visible = true\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif equipment.dye then\n\t\t\t\t\t\t\t\tslot.item.ImageColor3 = Color3.fromRGB(equipment.dye.r, equipment.dye.g, equipment.dye.b)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tlocal titleColor, itemTier = Modules.itemAcquistion.getTitleColorForInventorySlotData(equipment)\n\n\t\t\t\t\t\t\tslot.frame.ImageColor3 = (itemTier and itemTier > 1 and titleColor) or Color3.fromRGB(106, 105, 107)\n\t\t\t\t\t\t\tslot.shine.ImageColor3 = titleColor or Color3.fromRGB(179, 178, 185)\n\t\t\t\t\t\t\tslot.shine.Visible = titleColor ~= nil and itemTier > 1\n\n\t\t\t\t\t\t\tModules.fx.setFlash(slot.frame, titleColor ~= nil and itemTier > 1\t)\n\n\t\t\t\t\t\t\tslotData[slot] = equipment\n\t\t\t\t\t\t\tslot.LayoutOrder = equipment.position\n\n\t\t\t\t\t\t\tslot.stars.Visible = false\n\t\t\t\t\t\t\tlocal upgrades = equipment.successfulUpgrades\n\t\t\t\t\t\t\tif upgrades then\n\t\t\t\t\t\t\t\tfor i,child in pairs(slot.stars:GetChildren()) do\n\t\t\t\t\t\t\t\t\tif child:IsA(\"ImageLabel\") then\n\t\t\t\t\t\t\t\t\t\tchild.ImageColor3 = titleColor or Color3.new(1,1,1)\n\t\t\t\t\t\t\t\t\t\tchild.Visible = false\n\t\t\t\t\t\t\t\t\telseif child:IsA(\"TextLabel\") then\n\t\t\t\t\t\t\t\t\t\tchild.TextColor3 = titleColor or Color3.new(1,1,1)\n\t\t\t\t\t\t\t\t\t\tchild.Visible = false\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tif upgrades <= 3 then\n\t\t\t\t\t\t\t\t\tfor i,star in pairs(slot.stars:GetChildren()) do\n\t\t\t\t\t\t\t\t\t\tlocal score = tonumber(star.Name)\n\t\t\t\t\t\t\t\t\t\tif score then\n\t\t\t\t\t\t\t\t\t\t\tstar.Visible = score <= upgrades\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tslot.stars.exact.Visible = false\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tslot.stars[\"1\"].Visible = true\n\t\t\t\t\t\t\t\t\tslot.stars.exact.Visible = true\n\t\t\t\t\t\t\t\t\tslot.stars.exact.Text = upgrades\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tslot.stars.Visible = true\n\n\t\t\t\t\t\t\tend\n\n\n\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\n\n\n\n\n\t\t\tend\n\t\tend\n\t\tui.Visible = true\n\tend\n\n\tlocal player = game.Players.LocalPlayer\n\n\tlocal Mouse = player:GetMouse()\n\n\tlocal function reposition()\n\n\t\tif ui.Visible then\n\t\t\tlocal screensize = workspace.CurrentCamera.ViewportSize\n\n\t\t\tlocal x, y\n\n\t\t\tif Modules.input.mode.Value == \"pc\" then\n\t\t\t\tlocal x = Mouse.X + 15\n\t\t\t\tlocal y = Mouse.Y - 5\n\n\n\n\t\t\t\tif x + ui.AbsoluteSize.X > screensize.X then\n\t\t\t\t\tx = Mouse.X - 15 - ui.AbsoluteSize.X\n\t\t\t\tend\n\n\t\t\t\tlocal yDisplacement = (y + ui.AbsoluteSize.Y) - (screensize.Y - 36)\n\n\t\t\t\tif yDisplacement > 0 then\n\t\t\t\t\ty = y - yDisplacement\n\t\t\t\tend\n\n\t\t\t\tlocal targetPosition = UDim2.new(0, x, 0, y)\n\t\t\t\tui.Position = targetPosition\n\t\t\telseif Modules.input.mode.Value == \"xbox\" then\n\t\t\t\tlocal frame = game.GuiService.SelectedObject\n\n\t\t\t\tlocal x = frame.AbsolutePosition.X + frame.AbsoluteSize.X + 10\n\t\t\t\tif x > screensize.X then\n\t\t\t\t\tx = frame.AbsolutePosition.X - ui.AbsoluteSize.X - 10\n\t\t\t\tend\n\n\t\t\t\tlocal y = frame.AbsolutePosition.Y + frame.AbsoluteSize.Y/2 - ui.AbsoluteSize.Y/2\n\t\t\t\tlocal targetPosition = UDim2.new(0, x, 0, y)\n\t\t\t\tui.Position = targetPosition\n\t\t\tend\n\t\tend\n\tend\n\n\n\tgame:GetService(\"RunService\").RenderStepped:connect(reposition)\n\nend\n\n\n\nreturn module\n\n\n"
  },
  {
    "path": "src/StarterGui/interactShell.lua",
    "content": "local module = {}\n\nlocal ui = game.Players.LocalPlayer.PlayerGui.gameUI.interactShell\n\nfunction module.show(prompt)\n\tprompt.Parent = ui.shell\n\tui.Visible = true\nend\n\nfunction module.hide()\n\tui.Visible = false\nend\n\nmodule.hide()\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/interaction.lua",
    "content": "-- ber's attempt to tackle interaction in one night\n-- making stuff up as i go instead of planning in advance\n\nlocal module = {}\n\nlocal interactions = {}\nmodule.interactions = interactions\n\nlocal player = game:GetService(\"Players\").LocalPlayer\nlocal gui = player.PlayerGui.gameUI.interactFrame\n\nlocal function isWorldPositionInFrame(worldPos)\n\tlocal screenPos = workspace.CurrentCamera:WorldToScreenPoint(worldPos)\n\tlocal max = workspace.CurrentCamera.ViewportSize\n\treturn screenPos.Z > 0 and screenPos.X > 0 and screenPos.Y > 0 and screenPos.X <= max.X and screenPos.Y <= max.Y\nend\n\nlocal effects = script.Parent.effects\n\nfunction module.interact()\nend\n\nlocal isPlayerTarget\n\n-- for talking animations\nlocal idlesAllowedToTalk = {\n\t[\"rbxassetid://2267538527\"] = {useBody = true};\n\t[\"rbxassetid://2345613400\"] = {useBody = false}; -- arms crossed\n\t[\"rbxassetid://2861532980\"] = {useBody = true};\n\t[\"rbxassetid://3539593106\"] = {useBody = true};\n\t[\"rbxassetid://3539592561\"] = {useBody = true};\n\t[\"rbxassetid://3539591914\"] = {useBody = true};\n\n\t[\"rbxassetid://3244862244\"] = {useBody = false}; -- warrior sword pose\n\t[\"rbxassetid://2510524077\"] = {useBody = false}; -- sweeping\n\t[\"rbxassetid://3165415763\"] = {useBody = false}; -- fishing\n}\n--    or target.Parent:FindFirstChild(\"idle\").AnimationId == \"rbxassetid://3539591914\" or target.Parent:FindFirstChild(\"idle\").AnimationId == \"rbxassetid://3244862244\" or target.Parent:FindFirstChild(\"idle\").AnimationId == \"rbxassetid://3244862244\"))\n\nfunction module.init(Modules)\n\n\tlocal network = Modules.network\n\tlocal tween = Modules.tween\n\n\tlocal collectionService = game:GetService(\"CollectionService\")\n\n\tfor _, interaction in pairs(collectionService:GetTagged(\"interact\")) do\n\t\ttable.insert(interactions,interaction)\n\tend\n\n\tcollectionService:GetInstanceAddedSignal(\"interact\"):connect(function(interaction)\n\t\ttable.insert(interactions,interaction)\n\tend)\n\n\tcollectionService:GetInstanceRemovedSignal(\"interact\"):connect(function(interaction)\n\t\tfor i,interact in pairs(interactions) do\n\t\t\tif interact == interaction then\n\t\t\t\ttable.remove(interactions,i)\n\t\t\tend\n\t\tend\n\tend)\n\n\tmodule.currentInteraction = nil\n\n\tlocal interactPrompt = gui.interact\n\n\tfunction module.stopInteract()\n\t\tinteractPrompt.Parent = gui\n\n\t\tlocal target = module.currentInteraction\n\t\tif target and target.Parent then\n\t\t\tif game.CollectionService:HasTag(target.Parent, \"treasureChest\") then\n\t\t\t\t-- nothing lol xD\n\t\t\telseif target.Parent:FindFirstChild(\"clientHitboxToServerHitboxReference\") then\n\t\t\t\tModules.playerInteract.hide()\n\t\t\telseif target:FindFirstChild(\"interactScript\") then\n\t\t\t\tlocal interactScript = require(target.interactScript)\n\t\t\t\tinteractScript.close(Modules)\n\t\t\telseif game.CollectionService:HasTag(target, \"teleportPart\") then\n\n\t\t\t\tlocal partyData = network:invoke(\"getCurrentPartyInfo\")\n\t\t\t\tif partyData then\n\t\t\t\t\tif partyData.isClientPartyLeader then\n\t\t\t\t\t\t-- teleport invoke server cancel\n\t\t\t\t\t\tnetwork:invokeServer(\"playerRequest_cancelGroupTeleport\")\n\t\t\t\t\tend\n\t\t\t\tend\n\n\n\t\t\telseif target.Parent.Name == \"Shopkeeper\" then\n\t\t\t\tModules.shop.close(true)\n\t\t\t\tnetwork:invoke(\"setCharacterArrested\", false)\n\t\t\telseif target:FindFirstChild(\"dialogue\") then\n\t\t\t\tModules.dialogue.endDialogue(target.dialogue)\n\t\t\t\tModules.shop.close(true)\n\t\t\tend\n\t\tend\n\n\t\tmodule.currentInteraction = nil\n\t\tnetwork:invoke(\"lockCameraPosition\",false)\n\t\tnetwork:invoke(\"setStopRenderingPlayers\", false)\n\tend\n\n\tnetwork:create(\"stopInteraction\", \"BindableFunction\", \"OnInvoke\", module.stopInteract)\n\n\tlocal lastNearest\n\n\tlocal function step()\n\t\tlocal character = game.Players.LocalPlayer.Character\n\t\tif character and character.PrimaryPart then\n\t\t\tlocal nearest\n\t\t\tlocal nearDist = 7\n\n\n\t\t\tlocal prompt = \"\"\n\n\t\t\tif module.currentInteraction and module.currentInteraction:IsA(\"BasePart\") then\n\t\t\t\tlocal dist = (character.PrimaryPart.Position - module.currentInteraction.Position).magnitude\n\n\t\t\t\tif game.CollectionService:HasTag(module.currentInteraction, \"teleportPart\") then\n\t\t\t\t\tnearDist = 20\n\t\t\t\tend\n\n\t\t\t\tif module.currentInteraction:FindFirstChild(\"range\") then\n\t\t\t\t\tnearDist = module.currentInteraction.range.Value\n\t\t\t\tend\n\n\t\t\t\tif dist <= nearDist + 1.5 then\n\t\t\t\t\tgui.Adornee = module.currentInteraction\n\n\t\t\t\t\tgui.Enabled = true\n\t\t\t\t\tprompt = \"Leave\"\n\n\t\t\t\t\tif module.currentInteraction:FindFirstChild(\"interactScript\") then\n\t\t\t\t\t\tlocal interactScript = require(module.currentInteraction.interactScript)\n\t\t\t\t\t\tprompt = interactScript.leavePrompt or prompt\n\n\t\t\t\t\telseif module.currentInteraction.Parent and module.currentInteraction.Parent:FindFirstChild(\"clientHitboxToServerHitboxReference\") then\n\t\t\t\t\t\tprompt = \"Cancel\"\n\t\t\t\t\t\t--\n\n\t\t\t\t\telseif collectionService:HasTag(module.currentInteraction, \"seat\") then\n\t\t\t\t\t\tprompt = \"Get up\"\n\t\t\t\t\t\t--\n\n\t\t\t\t\tend\n\n\t\t\t\t\tModules.interactShell.show(interactPrompt)\n\t\t\t\t\tgui.Enabled = false\n\t\t\t\telse\n\t\t\t\t\tmodule.stopInteract()\n\t\t\t\t\tgui.Adornee = nil\n\t\t\t\t\tgui.Enabled = false\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tlocal nearestDist = 999\n\t\t\t\tlocal nearestPriority = -1\n\t\t\t\tfor i, interaction in pairs(interactions) do\n\t\t\t\t\tif interaction:IsA(\"BasePart\") and interaction:IsDescendantOf(workspace) then\n\t\t\t\t\t\tlocal priority = interaction:FindFirstChild(\"priority\") and interaction.priority.Value or 1\n\t\t\t\t\t\t-- give players lower priority\n\t\t\t\t\t\tif interaction.Parent:FindFirstChild(\"clientHitboxToServerHitboxReference\") then\n\t\t\t\t\t\t\tpriority = 0\n\t\t\t\t\t\tend\n\t\t\t\t\t\tlocal dist = (character.PrimaryPart.Position - interaction.Position).magnitude\n\n\t\t\t\t\t\tif priority > nearestPriority then\n\t\t\t\t\t\t\tlocal range = interaction:FindFirstChild(\"range\") and interaction.range.Value or 8\n\t\t\t\t\t\t\tif dist <= range then\n\t\t\t\t\t\t\t\tnearest = interaction\n\t\t\t\t\t\t\t\tnearestDist = dist\n\t\t\t\t\t\t\t\tnearestPriority = priority\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif priority >= nearestPriority and dist < nearestDist and isWorldPositionInFrame(interaction.Position) then\n\t\t\t\t\t\t\tnearest = interaction\n\t\t\t\t\t\t\tnearestDist = dist\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\n\n\t\t\t\tif nearest then\n\n\t\t\t\t\tlocal range = nearest:FindFirstChild(\"range\") and nearest.range.Value or 8\n\n\t\t\t\t\tif (nearestDist <= range or game.CollectionService:HasTag(nearest, \"teleportPart\") ) then\n\n\t\t\t\t\t\tgui.Adornee = nearest\n\n\t\t\t\t\t\tprompt = \"Interact\"\n\t\t\t\t\t\tif game.CollectionService:HasTag(nearest.Parent, \"treasureChest\") then\n\t\t\t\t\t\t\tprompt = \"Open\"\n\t\t\t\t\t\telseif nearest:FindFirstChild(\"interactScript\") then\n\t\t\t\t\t\t\tlocal interactScript = require(nearest.interactScript)\n\t\t\t\t\t\t\tif interactScript.interactPrompt and type(interactScript.interactPrompt) == \"string\" then\n\t\t\t\t\t\t\t\tprompt = interactScript.interactPrompt\n\t\t\t\t\t\t\t\tprompt = prompt:sub(1,1):upper() .. prompt:sub(2):lower()\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t--[[\n\t\t\t\t\t\telseif nearest.Parent and nearest.Parent:FindFirstChild(\"clientHitboxToServerHitboxReference\") then\n\n\t\t\t\t\t\t\tlocal referenceValue = nearest.Parent:FindFirstChild(\"clientHitboxToServerHitboxReference\")\n\n\t\t\t\t\t\t\tif referenceValue.Value and referenceValue.Value.Parent then\n\t\t\t\t\t\t\t\tlocal player = game.Players:GetPlayerFromCharacter(referenceValue.Value.Parent)\n\n\t\t\t\t\t\t\t\tif not player and referenceValue.Value:FindFirstChild(\"mirrorValue\") and referenceValue.Value.mirrorValue.Value and referenceValue.Value.mirrorValue.Value.Parent then\n\t\t\t\t\t\t\t\t\tplayer = game.Players:GetPlayerFromCharacter(referenceValue.Value.mirrorValue.Value.Parent)\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif player then\n\t\t\t\t\t\t\t\t\tModules.playerInteract.show(player)\n\t\t\t\t\t\t\t\t\tisPlayerTarget = true\n\t\t\t\t\t\t\t\t\tprompt = nil\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t]]\n\t\t\t\t\t\telseif game.CollectionService:HasTag(nearest, \"teleportPart\") then\n\t\t\t\t\t\t\tprompt = \"Travel\"\n\t\t\t\t\t\telseif nearest.Parent.Name == \"Shopkeeper\" then\n\t\t\t\t\t\t\tprompt = \"Shop\"\n\t\t\t\t\t\telseif nearest:FindFirstChild(\"PromptOverride\") then\n\t\t\t\t\t\t\tprompt = nearest:FindFirstChild(\"PromptOverride\").Value\n\t\t\t\t\t\telseif nearest:FindFirstChild(\"dialogue\") then\n\t\t\t\t\t\t\tif nearest:FindFirstChild(\"OverridePrompt\") then\n\t\t\t\t\t\t\t\tprompt = nearest:FindFirstChild(\"OverridePrompt\").Value\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tprompt = \"Talk\"\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\telseif collectionService:HasTag(nearest, \"seat\") then\n\t\t\t\t\t\t\tprompt = \"Sit\"\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif prompt then\n\t\t\t\t\t\t\t-- waving animation\n\t\t\t\t\t\t\tif nearest ~= lastNearest then\n\t\t\t\t\t\t\t\tlocal target = nearest\n\t\t\t\t\t\t\t\tif target.Parent and target.Parent:FindFirstChild(\"AnimationController\") and target.Parent:FindFirstChild(\"greeting\") then\n\t\t\t\t\t\t\t\t\tlocal track = target.Parent.AnimationController:LoadAnimation(target.Parent.greeting)\n\t\t\t\t\t\t\t\t\tif track  then --and target.Parent:FindFirstChild(\"beingGreeted\") == nil\n\t\t\t\t\t\t\t\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\t\t\t\t\t\t\t\ttag.Name = \"beingGreeted\"\n\t\t\t\t\t\t\t\t\t\ttag.Parent = target.Parent\n\t\t\t\t\t\t\t\t\t\ttrack.Looped = false\n\t\t\t\t\t\t\t\t\t\ttrack.Priority = Enum.AnimationPriority.Action\n\t\t\t\t\t\t\t\t\t\ttrack:Play()\n\t\t\t\t\t\t\t\t\t\tlocal stopConnection\n\t\t\t\t\t\t\t\t\t\tstopConnection = track.Stopped:connect(function()\n\t\t\t\t\t\t\t\t\t\t\tstopConnection:disconnect()\n\t\t\t\t\t\t\t\t\t\t\tif tag then\n\t\t\t\t\t\t\t\t\t\t\t\ttag:Destroy()\n\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tlastNearest = nearest\n\t\t\t\t\t\t\tgui.Enabled = true\n\n\t\t\t\t\t\t\t-- I shouldn't have to do this, but something is inexplicably setting this to false\n\t\t\t\t\t\t\tinteractPrompt.Visible = true\n\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tgui.Enabled = false\n\t\t\t\t\t\t\tlastNearest = nil\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif isPlayerTarget and not (nearest.Parent and nearest.Parent:FindFirstChild(\"clientHitboxToServerHitboxReference\")) then\n\t\t\t\t\t\t\tisPlayerTarget = false\n\t\t\t\t\t\t\tModules.playerInteract.hide()\n\t\t\t\t\t\t\tlastNearest = nil\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\tgui.Enabled = false\n\t\t\t\t\t\tif isPlayerTarget then\n\t\t\t\t\t\t\tModules.playerInteract.hide()\n\t\t\t\t\t\tend\n\t\t\t\t\t\tlastNearest = nil\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tgui.Enabled = false\n\t\t\t\t\tif isPlayerTarget then\n\t\t\t\t\t\tModules.playerInteract.hide()\n\t\t\t\t\tend\n\t\t\t\t\tlastNearest = nil\n\n\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif prompt then\n\t\t\t\tinteractPrompt.value.Text = prompt\n\n\t\t\t\tlocal contents = game.TextService:GetTextSize(\n\t\t\t\t\tinteractPrompt.value.Text,\n\t\t\t\t\tinteractPrompt.value.TextSize,\n\t\t\t\t\tinteractPrompt.value.Font,\n\t\t\t\t\tVector2.new()\n\t\t\t\t)\n\n\t\t\t\tinteractPrompt.button.Size = UDim2.new(0, contents.X + 56, 0, 36)\n\n\t\t\tend\n\n\t\tend\n\tend\n\n\tgame:GetService(\"RunService\").Heartbeat:connect(step)\n\t--game:GetService(\"RunService\"):BindToRenderStep(\"interactionStep\", Enum.RenderPriority.Camera.Value - 1, step)\n\n\n\tfunction module.interact()\n\t\tlocal target = gui.Adornee\n\n\t\tif module.currentInteraction == target then\n\t\t\tmodule.stopInteract()\n\t\telse\n\t\t\tmodule.currentInteraction = target\n\t\t\tif (gui.Enabled or isPlayerTarget) and target then\n\t\t\t\tif target:FindFirstChild(\"helloSound\") then\n\t\t\t\t\tModules.utilities.playSound(target.helloSound.Value, target)\n\t\t\t\tend\n\t\t\t\tlocal track\n\t\t\t\tlocal controller = target.Parent:FindFirstChild(\"AnimationController\")\n\t\t\t\tif target.Parent and controller and target.Parent:FindFirstChild(\"talk\") then\n\t\t\t\t\ttrack = target.Parent.AnimationController:LoadAnimation(target.Parent.talk)\n\t\t\t\telseif controller then\n\t\t\t\t\ttrack = target.Parent.AnimationController:LoadAnimation(effects.defaulttalk)\n\t\t\t\tend\n\n\t\t\t\t\tif track  and target.Parent:FindFirstChild(\"idle\") and idlesAllowedToTalk[target.Parent:FindFirstChild(\"idle\").AnimationId]  then --and target.Parent:FindFirstChild(\"beingGreeted\") == nil\n\n\t\t\t\t\t\tif not  idlesAllowedToTalk[target.Parent:FindFirstChild(\"idle\").AnimationId].useBody then\n\t\t\t\t\t\t\ttrack = target.Parent.AnimationController:LoadAnimation(effects.defaulttalk_noarm)\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tlocal fadeTime = .5\n\n\t\t\t\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\t\t\t\ttag.Name = \"beingGreeted\"\n\t\t\t\t\t\ttag.Parent = target.Parent\n\t\t\t\t\t\ttrack.Looped = false\n\t\t\t\t\t\ttrack.Priority = Enum.AnimationPriority.Action\n\t\t\t\t\t\ttrack:Play(fadeTime)\n\n\t\t\t\t\t\tspawn(function()\n\t\t\t\t\t\t\twait(0.5)\n\n\t\t\t\t\t\t\tlocal tracks = controller:GetPlayingAnimationTracks()\n\t\t\t\t\t\t\tfor i, existingTrack in pairs(tracks) do\n\t\t\t\t\t\t\t\tif existingTrack.Animation and existingTrack.Animation.Name == \"greeting\" then\n\t\t\t\t\t\t\t\t\texistingTrack:Stop()\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend)\n\n\t\t\t\t\t\tgame.Debris:AddItem(tag, track.Length/track.Speed)\n\t\t\t\t\tend\n\n\n\t\t\t\tlocal dialogueObject = target:FindFirstChild(\"dialogue\")\n\n\t\t\t\tif game.CollectionService:HasTag(target.Parent, \"treasureChest\") then\n\t\t\t\t\tmodule.stopInteract()\n\t\t\t\t\tnetwork:invoke(\"openTreasureChest_client\", target.Parent)\n\t\t\t\telseif target:FindFirstChild(\"interactScript\") then\n\t\t\t\t\tlocal interactScript = require(target.interactScript)\n\t\t\t\t\t-- going through all lengths to avoid damien's network module\n\t\t\t\t\tinteractScript.init(Modules)\n\t\t\t\t\tif interactScript.instant then\n\t\t\t\t\t\tmodule.stopInteract()\n\t\t\t\t\tend\n\t\t\t\telseif target.Parent and target.Parent:FindFirstChild(\"clientHitboxToServerHitboxReference\") then\n\n\t\t\t\t\tlocal referenceValue = target.Parent:FindFirstChild(\"clientHitboxToServerHitboxReference\")\n\n\t\t\t\t\tif referenceValue.Value and referenceValue.Value.Parent then\n\t\t\t\t\t\tlocal player = game.Players:GetPlayerFromCharacter(referenceValue.Value.Parent)\n\n\t\t\t\t\t\tif not player and referenceValue.Value:FindFirstChild(\"mirrorValue\") and referenceValue.Value.mirrorValue.Value and referenceValue.Value.mirrorValue.Value.Parent then\n\t\t\t\t\t\t\tplayer = game.Players:GetPlayerFromCharacter(referenceValue.Value.mirrorValue.Value.Parent)\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif player then\n\t\t\t\t\t\t\tModules.playerInteract.activate(player)\n\t\t\t\t\t\t\tmodule.stopInteract()\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\n\t\t\t\telseif game.CollectionService:HasTag(target, \"teleportPart\") then\n\n\n\t\t\t\t\tlocal partyData = network:invoke(\"getCurrentPartyInfo\")\n\t\t\t\t\tif partyData then\n\t\t\t\t\t\tif partyData.isClientPartyLeader then\n\t\t\t\t\t\t\t-- teleport invoke server\n\t\t\t\t\t\t\tnetwork:invokeServer(\"playerRequest_startGroupTeleport\", target)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tModules.notifications.alert({\n\t\t\t\t\t\t\t\ttext = \"Only the party leader can teleport the party!\";\n\t\t\t\t\t\t\t\ttextColor = Color3.fromRGB(255,170,170)\n\t\t\t\t\t\t\t}, 2)\n\t\t\t\t\t\t\tmodule.stopInteract()\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\n\t\t\t\telseif target.Parent and target.Parent.Name == \"Shopkeeper\" then\n--\t\t\t\t\tlocal cf = target.CFrame:ToWorldSpace(camCf)\\\n\t\t\t\t\tModules.shop.open(target)\n\n\t\t\t\telseif dialogueObject then\n\t\t\t\t\t-- initiate dialogue\n\t\t\t\t\tModules.dialogue.beginDialogue(target, require(dialogueObject))\n\t\t\t\telseif collectionService:HasTag(target, \"seat\") then\n\t\t\t\t\tnetwork:invoke(\"seatPlayer\", target)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\tend\n\n\tinteractPrompt.button.Activated:connect(function()\n\t\ttween(interactPrompt.button, {\"ImageColor3\"}, {Color3.fromRGB(223, 223, 223)}, 0.5)\n\t\tmodule.interact()\n\tend)\n\tinteractPrompt.button.MouseEnter:connect(function()\n\t\ttween(interactPrompt.button, {\"ImageColor3\"}, {Color3.fromRGB(111, 236, 255)}, 0.5)\n\tend)\n\tinteractPrompt.button.MouseLeave:connect(function()\n\t\ttween(interactPrompt.button, {\"ImageColor3\"}, {Color3.fromRGB(223, 223, 223)}, 0.5)\n\tend)\n\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/inventory.lua",
    "content": "local module = {}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal itemData = require(replicatedStorage:WaitForChild(\"itemData\"))\nlocal itemAttributes = require(replicatedStorage:WaitForChild(\"itemAttributes\"))\nlocal abilityLookup = require(game.ReplicatedStorage:WaitForChild(\"abilityLookup\"))\n\nlocal player = game.Players.LocalPlayer\nlocal playerGui = player.PlayerGui\nlocal menuUI = playerGui.gameUI.menu_inventory\n\nlocal header = menuUI.header\nlocal sortCategoryButtonContainer = header.sorts\nlocal content = menuUI.content\nlocal ui = menuUI.Parent\n\nlocal scrollingMask = ui.scrollingMask\n\nlocal tweenService = game:GetService(\"TweenService\")\n\n-- equipment, consumable, miscellaneous\nlocal currentCategoryTab = \"equipment\"\nlocal lastInventoryDataReceived = nil\nlocal inventorySlotPairing = {}\nlocal abilityPairing = {}\nlocal tweenAnimations \t\t\t= {}\n\nlocal itemUseDebounce = false\nmodule.isEnchantingEquipment = false\nmenuUI.scrollPrompt.Visible = false\n\nlocal inventorySlotData_enchantment = nil\n\nlocal lastSelected\n\nlocal playerData\n\nfunction module.show()\n\tmenuUI.Visible = not menuUI.Visible\nend\nfunction module.hide()\n\tmenuUI.Visible = false\nend\n\nmenuUI.close.Activated:connect(module.hide)\n\n\nfunction module.init(Modules)\n\n\tlocal animationInterface = Modules.animationInterface\n\n\tlocal tradeFrame = ui.menu_trade\n\tlocal enchantFrame = ui.menu_enchant\n\tlocal shopFrame = ui.menu_shop\n\tlocal storageFrame = ui.menu_storage\n\tlocal equipFrame = ui.menu_equipment\n\n\tlocal network = Modules.network\n\tlocal utilities = Modules.utilities\n\tlocal uiCreator = Modules.uiCreator\n\tlocal tween = Modules.tween\n\tlocal enchantment = Modules.enchantment\n\tlocal localization = Modules.localization\n\tlocal mapping = Modules.mapping\n\n\n\tnetwork:create(\"signal_isEnchantingEquipmentSet\", \"BindableEvent\")\n\n\tfunction module.setIsEnchantingEquipment(value, inventorySlotData_enchantment)\n\t\tmodule.isEnchantingEquipment = value\n\t\tnetwork:fire(\"signal_isEnchantingEquipmentSet\", value, inventorySlotData_enchantment)\n\tend\n\n\tlocal function burst(n)\n\t\tlocal t = (n ^ (1/3)) / 3.5\n\t\tfor i=1,n do\n\t\t\tspawn(function()\n\t\t\t\twait(t * math.random())\n\t\t\t\tlocal icon = menuUI.currency.ethyr.icon:Clone()\n\t\t\t\ticon.Name = \"iconClone\"\n\t\t\t\ticon.Parent = menuUI.currency.ethyr\n\t\t\t\ticon.ImageTransparency = 0\n\n\t\t\t\tlocal initialX = math.random(-10,10)\n\t\t\t\tlocal initialY = math.random(-10,10)\n\t\t\t\ticon.Position = UDim2.new(0.5, initialX, 0.5, initialY)\n\t\t\t\ticon.Visible = true\n\n\t\t\t\tlocal s = math.random(60,110)/100\n\n\t\t\t\ttween(icon, {\"ImageTransparency\", \"Position\"}, {1, UDim2.new(0.5, initialX + math.random(-100,100) * t, 0.5, initialY + math.random(-100,100) * t)}, s)\n\t\t\t\tgame.Debris:AddItem(icon, s + 0.1)\n\t\t\tend)\n\t\tend\n\tend\n\n\n\tgame.MarketplaceService.PromptProductPurchaseFinished:Connect(function(playerId, assetId, isPurchased)\n\n\t\tif playerId == game.Players.LocalPlayer.userId and isPurchased then\n\n\t\t\tlocal burstEffect\n\t\t\tlocal soundEffect\n\n\t\t\tif assetId == 509934399 then\n\t\t\t\tsoundEffect = \"ethyr1\"\n\t\t\t\tburstEffect = 7\n\t\t\telseif assetId == 509935760 then\n\t\t\t\tsoundEffect = \"ethyr2\"\n\t\t\t\tburstEffect = 14\n\t\t\telseif assetId == 509935018 then\n\t\t\t\tsoundEffect = \"ethyr3\"\n\t\t\t\tburstEffect = 26\n\t\t\telseif assetId == 539152241 then\n\t\t\t\tsoundEffect = \"ethyr4\"\n\t\t\t\tburstEffect = 46\n\t\t\tend\n\n--\t\t\tif burstEffect then\n--\t\t\t\tmenuUI.currency.ethyr.Visible = true\n--\n--\t\t\t\tburst(burstEffect)\n--\t\t\tend\n\t\t\tif soundEffect then\n\t\t\t\tutilities.playSound(soundEffect)\n\t\t\tend\n\t\tend\n\tend)\n\n\tlocal ethyrCostInfo = {costType = \"ethyr\", icon = \"rbxassetid://31886504632\", textColor = Color3.fromRGB(115, 255, 251)}\n\n\tlocal globalData = network:invoke(\"getCacheValueByNameTag\", \"globalData\")\n\tmenuUI.currency.ethyr.Visible = false\n\tif globalData then\n\t\tModules.money.setLabelAmount(menuUI.currency.ethyr.amount, globalData.ethyr or 0, ethyrCostInfo)\n\t\tmenuUI.currency.ethyr.Size = UDim2.new(0, menuUI.currency.ethyr.amount.amount.AbsoluteSize.X + 95 + 10, 0, 36 + 10)\n\t\tmenuUI.currency.ethyr.Visible = (globalData.ethyr and globalData.ethyr > 0)\n\tend\n\n\n\n\tmenuUI.currency.ethyr.buy.Activated:connect(function()\n\t\tModules.products.open()\n\tend)\n\n\tlocal oldEthyr = globalData.ethyr or 0\n\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", function(key, value)\n\t\tif key == \"globalData\" then\n\t\t\tlocal newGlobalData = network:invoke(\"getCacheValueByNameTag\", \"globalData\")\n\t\t\tlocal newEthyr = newGlobalData.ethyr or 0\n\t\t\tif newEthyr > oldEthyr and newEthyr > 0 then\n\t\t\t\tlocal ethyrDiff = newEthyr - oldEthyr\n\t\t\t\tlocal burstAmt = math.clamp(ethyrDiff/10, 3, 50)\n\t\t\t\tburst(burstAmt)\n\t\t\t\tModules.money.setLabelAmount(menuUI.currency.ethyr.amount, value.ethyr or 0, ethyrCostInfo)\n\t\t\t\tmenuUI.currency.ethyr.Size = UDim2.new(0, menuUI.currency.ethyr.amount.amount.AbsoluteSize.X + 95, 0, 36 + 10)\n\t\t\t\tmenuUI.currency.ethyr.Visible = (newEthyr > 0)\n\t\t\tend\n\t\t\toldEthyr = newEthyr\n\t\tend\n\tend)\n\n\n\tlocal function onInventoryItemMouseEnter(inventoryItem)\n\t\tlastSelected = inventoryItem\n\t\tlocal abilitySlotData = abilityPairing[inventoryItem]\n\t\tif abilitySlotData then\n\t\t\tlocal abilityBaseData = abilityLookup[abilitySlotData.id](playerData)\n\t\t\tnetwork:invoke(\"populateItemHoverFrameWithAbility\", abilityBaseData, abilitySlotData.rank)\n\t\tend\n\t\tlocal inventorySlotData = inventorySlotPairing[inventoryItem]\n\t\tif inventorySlotData then\n\t\t\tlocal itemBaseData = itemData[inventorySlotData.id]\n\t\t\tif itemBaseData then\n\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\", itemBaseData, \"inventory\", inventorySlotData)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function onInventoryItemMouseLeave(inventoryItem)\n\t\tif lastSelected == inventoryItem then\n\t\t\t-- clears last selected\n\t\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\t\tend\n\tend\n\n\tModules.money.subscribeToPlayerMoney(menuUI.currency.money)\n\n\tlocal inventoryItemTemplate = ui.menu_inventory:WaitForChild(\"inventoryItemTemplate\")\n\n\tlocal function onInventoryItemDoubleClicked(inventoryItem)\n\t\tif abilityPairing[inventoryItem] then\n\t\t\tlocal abilitySlotData = abilityPairing[inventoryItem]\n\n\t\t\tif abilitySlotData then\n\t\t\t\tif enchantFrame.Visible then\n\t\t\t\t\tModules.enchant.dragItem(abilitySlotData)\n\t\t\t\telse\n\t\t\t\t\tnetwork:invoke(\"activateAbilityRequest\", abilitySlotData.id)\n\t\t\t\tend\n\t\t\tend\n\t\telseif inventorySlotPairing[inventoryItem] then\n\t\t\tlocal inventorySlotData = inventorySlotPairing[inventoryItem]\n\n\t\t\tif tradeFrame.Visible then\n\t\t\t\tModules.trading.processDoubleClickFromInventory(inventorySlotData)\n\t\t\t--elseif enchantFrame.Visible then\n\t\t\t--\tModules.enchant.dragItem(inventorySlotData)\n\t\t\telseif storageFrame.Visible then\n\t\t\t\tnetwork:invokeServer(\"playerRequest_transferInventoryToStorage\", inventorySlotData)\n\t\t\telseif shopFrame.Visible then\n\t\t\t\tnetwork:invoke(\"shop_setCurrentItem\", inventorySlotData, true)\n\t\t\telse\n\t\t\t\tlocal itemBaseData = itemData[inventorySlotData.id]\n\t\t\t\tprint(\"doubleclick\", itemBaseData and itemBaseData.name, itemBaseData and itemBaseData.category, itemBaseData and itemBaseData.equipmentPosition)\n\n\t\t\t\tprint(itemBaseData)\n\n\t\t\t\tif itemBaseData then\n\t\t\t\t\tif itemBaseData.category == \"equipment\" then\n\t\t\t\t\t\tif itemBaseData.equipmentPosition == mapping.equipmentPosition.arrow then\n\t\t\t\t\t\t\t-- this is an arrow.. we want to handle this uniquely\n\t\t\t\t\t\t\tprint(\"arrow hehe\")\n\t\t\t\t\t\t\tlocal success = network:invokeServer(\"transferInventoryToEquipment\", \"equipment\", inventorySlotData.position, mapping.equipmentPosition.arrow)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t--equipmentSlot\n\t\t\t\t\t\t\tlocal targetName = Modules.mapping.getMappingByValue(\"equipmentPosition\", itemBaseData.equipmentSlot)\n\n\t\t\t\t\t\t\tif targetName then\n\t\t\t\t\t\t\t\tlocal target = equipFrame.content:FindFirstChild(targetName)\n\n\t\t\t\t\t\t\t\tif target and target:FindFirstChild(\"equipItemButton\") then\n\t\t\t\t\t\t\t\t\tModules.uiCreator.processSwap(inventoryItem, target.equipItemButton)\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\telseif itemBaseData.category == \"consumable\" or itemBaseData.activationEffect ~= nil then\n\t\t\t\t\t\t-- so sad, we have to do this though.\n\t\t\t\t\t\t-- functions use each other :(\n\t\t\t\t\t\tlocal success, reason = network:invoke(\"activateItemRequestLocal\", inventorySlotData)\n\n\t\t\t\t\t\tprint(success, reason)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function onInventoryItemMouseButton1Click_isEnchantingEquipment(inventoryItem)\n\n\n\t\tif module.isEnchantingEquipment and inventorySlotData_enchantment then\n\t\t\tlocal target\n\t\t\tlocal mode\n\t\t\tif inventoryItem:IsDescendantOf(menuUI) and inventorySlotPairing[inventoryItem] then\n\t\t\t\tlocal inventorySlotData = inventorySlotPairing[inventoryItem]\n\t\t\t\tif inventorySlotData then\n\t\t\t\t\ttarget = inventorySlotData\n\t\t\t\tend\n\n\t\t\telseif inventoryItem:IsDescendantOf(equipFrame) then\n\t\t\t\tlocal equipmentSlotData = network:invoke(\"getEquipmentSlotDataByEquipmentSlotUI\", inventoryItem)\n\t\t\t\tif equipmentSlotData then\n\t\t\t\t\ttarget = equipmentSlotData\n\t\t\t\t\tmode = \"equipment\"\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif inventoryItem.Parent.blocked.Visible then\n\t\t\t\treturn false\n\t\t\tend\n\n\t\t\tlocal continue = true\n\t\t\tlocal definitiveEnchantmentData = inventorySlotData_enchantment\n\n\t\t\tif definitiveEnchantmentData then\n\t\t\t\tlocal dyeBaseData = itemData[definitiveEnchantmentData.id]\n\n\t\t\t\tif target then\n\t\t\t\t\tlocal equipmentBaseData = itemData[target.id]\n\n\t\t\t\t\tif dyeBaseData.dye and not Modules.dyePreview.prompt(dyeBaseData, equipmentBaseData) then\n\t\t\t\t\t\tcontinue = false\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif target and continue then\n\n\t\t\t\tlocal enchantmentData = definitiveEnchantmentData\n\n\t\t\t\tlocal playerInput = {}\n\t\t\t\tlocal itemBaseData_enchantment \t= itemData[enchantmentData.id]\n\t\t\t\tif itemBaseData_enchantment and itemBaseData_enchantment.playerInputFunction then\n\t\t\t\t\tplayerInput = itemBaseData_enchantment.playerInputFunction()\n\t\t\t\tend\n\n\t\t\t\tlocal success, scrollApplied, newInventorySlotData, status = network:invokeServer(\"playerRequest_enchantEquipment\", enchantmentData, target, mode, playerInput)\n\n\t\t\t\tif status then\n\t\t\t\t\tspawn(function()\n\t\t\t\t\t\twait(0.5)\n--\t\t\t\t\t\tgame.StarterGui:SetCore(\"ChatMakeSystemMessage\", status)\n\t\t\t\t\t\tnetwork:fire(\"alert\", status)\n\t\t\t\t\tend)\n\t\t\t\tend\n\n\t\t\t\titemUseDebounce = false\n--\t\t\t\tmodule.isEnchantingEquipment = false\n\t\t\t\tmodule.setIsEnchantingEquipment(false)\n\n\t\t\t\tmenuUI.scrollPrompt.Visible = false\n\n\t\t\t\tinventorySlotData_enchantment = nil\n\t\t\t\tscrollingMask.ImageTransparency = 1\n\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\n\t\t\t\tif success and scrollApplied and newInventorySlotData then\n\t\t\t\t\tspawn(function()\n\t\t\t\t\t\twait(0.5)\n\t\t\t\t\t\tlocal ringInfo = {\n\t\t\t\t\t\t\tcolor = Modules.itemAcquistion.getTitleColorForInventorySlotData(newInventorySlotData) or Color3.new(1,1,1);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tModules.fx.ring(ringInfo, inventoryItem.AbsolutePosition + inventoryItem.AbsoluteSize/2)\n\t\t\t\t\tend)\n\t\t\t\tend\n\t\t\t\t-- disgusting\n\t\t\t\tnetwork:invoke(\"updateInventoryUI\")\n\t\t\tend\n\t\tend\n\n\n\tend\n\n\tmodule.enchantItem = onInventoryItemMouseButton1Click_isEnchantingEquipment\n\n\tlocal abilityData\n\n\tlocal learnAbilitiesFrame = menuUI.learnAbilities\n\n\tlocal learnableAbilityCount = 0\n\n\tlocal function updateLearnableAbilities()\n\t\tabilityData = abilityData or network:invoke(\"getCacheValueByNameTag\", \"abilities\")\n\t\tfor _, button in pairs(learnAbilitiesFrame.contents:GetChildren()) do\n\t\t\tif button:IsA(\"GuiObject\") then\n\t\t\t\tbutton:Destroy()\n\t\t\tend\n\t\tend\n\t\t-- at this point shouldnt i just change how we store the data ;-;\n\t\tlocal organizedAbilityData = {}\n\n\t\tfor _, abilitySlotData in pairs(abilityData) do\n\t\t\torganizedAbilityData[abilitySlotData.id] = abilitySlotData\n\t\tend\n\t\tlocal learnableAbilities = {}\n\t\tfor abilityId, isAbility in pairs(abilityLookup:GetAbilityIds()) do\n\t\t\tif isAbility then\n\t\t\t\tlocal ability = abilityLookup[abilityId](playerData)\n\t\t\t\tif ability.metadata and ability.metadata.requirement and ability.metadata.requirement(playerData) then\n\t\t\t\t\tif organizedAbilityData[ability.id] == nil or organizedAbilityData[ability.id].rank == 0 then\n\t\t\t\t\t\ttable.insert(learnableAbilities, ability)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\tlearnableAbilityCount = #learnableAbilities\n\t\tfor _, ability in pairs(learnableAbilities) do\n\t\t\tlocal template = learnAbilitiesFrame.template:Clone()\n\t\t\tlocal cost = ability.metadata.cost or 0\n\t\t\ttemplate.item.Image = ability.image\n\t\t\ttemplate.abilityPoints.amount.Text = cost .. \" AP\"\n\t\t\ttemplate.LayoutOrder = cost\n\t\t\ttemplate.Parent = learnAbilitiesFrame.contents\n\t\t\ttemplate.Visible = true\n\t\t\tlocal function selected()\n\t\t\t\tnetwork:invoke(\"populateItemHoverFrameWithAbility\", ability, 0)\n\t\t\t\tlastSelected = template\n\t\t\tend\n\t\t\tlocal function unselected()\n\t\t\t\tif lastSelected == template then\n\t\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\t\t\t\tend\n\t\t\tend\n\t\t\ttemplate.item.MouseEnter:connect(selected)\n\t\t\ttemplate.item.SelectionGained:connect(selected)\n\t\t\ttemplate.item.MouseLeave:connect(unselected)\n\t\t\ttemplate.item.SelectionLost:connect(unselected)\n\t\t\ttemplate.item.Activated:connect(function()\n\t\t\t\tlocal str = localization.translate(\"Are you sure you want to spend %s to learn %s?\", learnAbilitiesFrame)\n\t\t\t\tlocal abilityName = ability.name and localization.translate(ability.name) or \"???\"\n\t\t\t\tlocal playerConfirmed = network:invoke(\"promptActionFullscreen\",string.format(str, cost .. \" AP\", abilityName))\n\t\t\t\tif playerConfirmed then\n\t\t\t\t\tlocal success, err = network:invokeServer(\"playerRequest_learnAbility\", ability.id)\n\t\t\t\t\t-- auto-bind new abilities\n\t\t\t\t\tif success then\n\t\t\t\t\t\tif not ability.passive then\n\t\t\t\t\t\t\tlocal hotbarSlotPairing = network:invoke(\"getHotbarSlotPairing\")\n\t\t\t\t\t\t\tfor i, hotbarSlot in pairs(hotbarSlotPairing) do\n\t\t\t\t\t\t\t\tlocal hotbarButton = hotbarSlot.button\n\t\t\t\t\t\t\t\tlocal hotbarData = hotbarSlot.data\n\t\t\t\t\t\t\t\tif (hotbarData == nil or hotbarData.id == nil or hotbarData.id <= 0) then\n\n\t\t\t\t\t\t\t\t\tlocal num = string.gsub(hotbarButton.Name,\"[^.0-9]+\",\"\")\n\t\t\t\t\t\t\t\t\tnum = tonumber(num)\n\n\t\t\t\t\t\t\t\t\tif num ~= 1 and num ~= 2 then -- 1 and 2 reserved for consumables\n\t\t\t\t\t\t\t\t\t\tif num == 10 then num = 0 end\n\t\t\t\t\t\t\t\t\t\tlocal bindsuccess = network:invokeServer(\"registerHotbarSlotData\", mapping.dataType.ability, ability.id, tonumber(num))\n\t\t\t\t\t\t\t\t\t\tif bindsuccess then\n\t\t\t\t\t\t\t\t\t\t\tfor i=1,4 do\n\t\t\t\t\t\t\t\t\t\t\t\tlocal flare = hotbarButton.flare:Clone()\n\t\t\t\t\t\t\t\t\t\t\t\tflare.Name = \"flareCopy\"\n\t\t\t\t\t\t\t\t\t\t\t\tflare.Parent = hotbarButton\n\t\t\t\t\t\t\t\t\t\t\t\tflare.Visible = true\n\t\t\t\t\t\t\t\t\t\t\t\tflare.Size = UDim2.new(1,4,1,4)\n\t\t\t\t\t\t\t\t\t\t\t\tflare.Position = UDim2.new(0.5,0,1,2)\n\t\t\t\t\t\t\t\t\t\t\t\tflare.AnchorPoint = Vector2.new(0.5,1)\n\t\t\t\t\t\t\t\t\t\t\t\tlocal y = (180 - 40*i)\n\t\t\t\t\t\t\t\t\t\t\t\tlocal x = (14 - 2*i)\n\t\t\t\t\t\t\t\t\t\t\t\tlocal EndPosition = UDim2.new(0.5,0,1,2)\n\t\t\t\t\t\t\t\t\t\t\t\tlocal EndSize = UDim2.new(1,x,1,y)\n\t\t\t\t\t\t\t\t\t\t\t\ttween(flare,{\"Position\",\"Size\",\"ImageTransparency\"},{EndPosition, EndSize, 1},0.5*i)\n\n\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\t\tif game.ReplicatedStorage.assets.sounds:FindFirstChild(\"idolPickup\") then\n\t\t\t\t\t\t\t--game.ReplicatedStorage.sounds.idolPickup:Play()\n\t\t\t\t\t\t\tutilities.playSound(\"idolPickup\")\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\n\t\tlocal rows = math.ceil(#learnableAbilities/4)\n\t\tlocal grid = learnAbilitiesFrame.contents.UIGridLayout\n\t\tlocal ySize = grid.CellPadding.Y.Offset + grid.CellSize.Y.Offset\n\t\tlocal xSize = grid.CellPadding.X.Offset + grid.CellSize.X.Offset\n\t\tlearnAbilitiesFrame.Size = UDim2.new(0, ((rows > 1 and xSize * 4 + 14) or xSize * #learnableAbilities), 0, math.min(rows * ySize, 170))\n\t\tlearnAbilitiesFrame.contents.CanvasSize = UDim2.new(0, 0, 0, rows * ySize)\n\t\tlearnAbilitiesFrame.contents.CanvasPosition = Vector2.new()\n\tend\n\n\tlocal function updateInventory(updateInventoryData)\n\t\tif updateInventoryData then\n\t\t\tlastInventoryDataReceived = updateInventoryData\n\t\tend\n\n\t\tif currentCategoryTab ~= \"ability\" then\n\t\t\tlearnAbilitiesFrame.Visible = false\n\t\tend\n\n\t\tlocal currentSelected = game.GuiService.SelectedObject\n\t\tlocal selectionName, selectionParent\n\t\tif currentSelected and currentSelected:IsDescendantOf(menuUI) then\n\t\t\tselectionName = currentSelected.Name\n\t\t\tselectionParent = currentSelected.Parent\n\t\tend\n\n\t\tif lastInventoryDataReceived then\n\t\t\tinventorySlotPairing = {}\n\t\t\tabilityPairing = {}\n\t\t\tfor i, inventoryItem in pairs(content:GetChildren()) do\n\t\t\t\tif inventoryItem:FindFirstChild(\"item\") then\n\t\t\t\t\tinventoryItem:Destroy()\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal currCells\t= 0\n\n\t\t\tplayerData = network:invoke(\"getLocalPlayerDataCache\")\n\t\t\tabilityData = abilityData or network:invoke(\"getCacheValueByNameTag\", \"abilities\")\n\n\t\t\tlocal level = network:invoke(\"getCacheValueByNameTag\", \"level\") or 1\n\n\t\t\tlocal contents = {}\n\t\t\tlocal n = 0\n\t\t\tif currentCategoryTab == \"ability\" then\n\t\t\t\tfor i, abilitySlotData in pairs(abilityData) do\n\t\t\t\t\tif abilitySlotData.rank > 0 then\n\t\t\t\t\t\tn = n + 1\n\t\t\t\t\t\tcontents[tostring(n)] = abilitySlotData\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tfor i, inventorySlotData in pairs(lastInventoryDataReceived) do\n\t\t\t\t\tlocal inventoryItemBaseData = itemData[inventorySlotData.id]\n\n\t\t\t\t\tif inventoryItemBaseData then\n\t\t\t\t\t\tif inventoryItemBaseData.category == currentCategoryTab then\n\t\t\t\t\t\t\tcontents[tostring(inventorySlotData.position)] = inventorySlotData\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tfor i = 1, 20 do\n\t\t\t\tlocal inventoryItem = ui.menu_inventory.inventoryItemTemplate:Clone()\n\t\t\t\tinventoryItem.stars.Visible = false\n\t\t\t\tinventoryItem.blocked.Visible = false\n\t\t\t\tinventoryItem.shine.Visible = false\n\t\t\t\tinventoryItem.locked.Visible = false\n\t\t\t\tinventoryItem.attribute.Visible = false\n\t\t\t\tinventoryItem.item.duplicateCount.Text \t= \"\"\n\t\t\t\tlocal item = contents[tostring(i)]\n\t\t\t\tif item then\n\t\t\t\t\tif currentCategoryTab == \"ability\" then\n\t\t\t\t\t\t-- ability\n\t\t\t\t\t\tlocal abilitySlotData = item\n\t\t\t\t\t\tlocal abilityBaseData = abilityLookup[abilitySlotData.id](playerData)\n\t\t\t\t\t\tif not abilityBaseData.passive then\n\t\t\t\t\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\t\t\t\t\ttag.Name = \"bindable\"\n\t\t\t\t\t\t\ttag.Parent = inventoryItem.item\n\t\t\t\t\t\tend\n\t\t\t\t\t\tinventoryItem.ImageTransparency = 0\n\t\t\t\t\t\tinventoryItem.item.Image = abilityBaseData.image\n\t\t\t\t\t\tinventoryItem.item.ImageColor3 = Color3.new(1,1,1)\n\t\t\t\t\t\tif abilityBaseData.passive then\n\t\t\t\t\t\t\tabilitySlotData.passive = true\n\t\t\t\t\t\tend\n\t\t\t\t\t\tabilityPairing[inventoryItem.item] = abilitySlotData\n\n\t\t\t\t\t\tlocal titleColor, itemTier\n\t\t\t\t\t\tif abilitySlotData then\n\t\t\t\t\t\t\tif abilitySlotData.rank > 1 then\n\t\t\t\t\t\t\t\titemTier = 2\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif abilityBaseData.statistics then\n\t\t\t\t\t\t\tlocal abilityStats = Modules.ability_utilities.getAbilityStatisticsForRank(abilityBaseData, abilitySlotData.rank)\n\t\t\t\t\t\t\tif abilityStats and abilityStats.tier and (itemTier == nil or abilityStats.tier > itemTier) then\n\t\t\t\t\t\t\t\titemTier = abilityStats.tier\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\n\t\t\t\t\t\tif itemTier and itemTier > 1 then\n\t\t\t\t\t\t\ttitleColor = enchantment.tierColors[itemTier]\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tlocal upgrades = abilitySlotData.rank > 1 and abilitySlotData.rank - 1\n\t\t\t\t\t\tif upgrades then\n\t\t\t\t\t\t\tfor i,child in pairs(inventoryItem.stars:GetChildren()) do\n\t\t\t\t\t\t\t\tif child:IsA(\"ImageLabel\") then\n\t\t\t\t\t\t\t\t\tchild.ImageColor3 = titleColor or Color3.new(1,1,1)\n\t\t\t\t\t\t\t\t\tchild.Visible = false\n\t\t\t\t\t\t\t\telseif child:IsA(\"TextLabel\") then\n\t\t\t\t\t\t\t\t\tchild.TextColor3 = titleColor or Color3.new(1,1,1)\n\t\t\t\t\t\t\t\t\tchild.Visible = false\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tinventoryItem.stars.Visible = true\n\t\t\t\t\t\t\tif upgrades <= 3 then\n\t\t\t\t\t\t\t\tfor i,star in pairs(inventoryItem.stars:GetChildren()) do\n\t\t\t\t\t\t\t\t\tlocal score = tonumber(star.Name)\n\t\t\t\t\t\t\t\t\tif score then\n\t\t\t\t\t\t\t\t\t\tstar.Visible = score <= upgrades\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tinventoryItem.stars.exact.Visible = false\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tinventoryItem.stars[\"1\"].Visible = true\n\t\t\t\t\t\t\t\tinventoryItem.stars.exact.Visible = true\n\t\t\t\t\t\t\t\tinventoryItem.stars.exact.Text = upgrades\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tinventoryItem.stars.Visible = true\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tinventoryItem.shine.Visible = titleColor ~= nil and itemTier > 1\n\t\t\t\t\t\tinventoryItem.shine.ImageColor3 = titleColor or Color3.fromRGB(179, 178, 185)\n\n\t\t\t\t\t--\tlocal gradient = script.abilityGradient:Clone()\n\t\t\t\t\t--\tgradient.Parent = inventoryItem.item\n\n\t\t\t\t\t\tModules.fx.setFlash(inventoryItem.frame, inventoryItem.shine.Visible)\n\n\t\t\t\t\t\tinventoryItem.frame.ImageColor3 = (itemTier and itemTier > 1 and titleColor) or Color3.fromRGB(106, 105, 107)\n\n\t\t\t\t\t\tuiCreator.drag.setIsDragDropFrame(inventoryItem.item)\n\t\t\t\t\t\tuiCreator.setIsDoubleClickFrame(inventoryItem.item, 0.2, onInventoryItemDoubleClicked)\n\t\t\t\t\telse\n\t\t\t\t\t\t-- item\n\t\t\t\t\t\tlocal inventorySlotData = item\n\t\t\t\t\t\tlocal inventoryItemBaseData = itemData[inventorySlotData.id]\n\t\t\t\t\t\tif inventoryItemBaseData.canBeBound then\n\t\t\t\t\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\t\t\t\t\ttag.Name = \"bindable\"\n\t\t\t\t\t\t\ttag.Parent = inventoryItem.item\n\t\t\t\t\t\tend\n\t\t\t\t\t\tinventoryItem.ImageTransparency = 0\n\t\t\t\t\t\tinventoryItem.item.Image = inventoryItemBaseData.image\n\t\t\t\t\t\tinventoryItem.item.ImageColor3 = Color3.new(1,1,1)\n\t\t\t\t\t\tif inventorySlotData.dye then\n\t\t\t\t\t\t\tinventoryItem.item.ImageColor3 = Color3.fromRGB(inventorySlotData.dye.r, inventorySlotData.dye.g, inventorySlotData.dye.b)\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif inventoryItemBaseData.minLevel then\n\t\t\t\t\t\t\tlocal level = network:invoke(\"getCacheValueByNameTag\", \"level\") or 1\n\t\t\t\t\t\t\tinventoryItem.locked.Visible = level <\tinventoryItemBaseData.minLevel\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tinventoryItem.item.duplicateCount.Text = (inventorySlotData.stacks and inventoryItemBaseData.canStack and inventorySlotData.stacks > 1) and tostring(inventorySlotData.stacks) or \"\"\n\n\t\t\t\t\t\tinventorySlotPairing[inventoryItem.item] = inventorySlotData\n\n\t\t\t\t\t\tlocal titleColor, itemTier\n\t\t\t\t\t\tif inventorySlotData then\n\t\t\t\t\t\t\ttitleColor, itemTier = Modules.itemAcquistion.getTitleColorForInventorySlotData(inventorySlotData)\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif inventorySlotData.attribute then\n\t\t\t\t\t\t\tlocal attributeData = itemAttributes[inventorySlotData.attribute]\n\t\t\t\t\t\t\tif attributeData and attributeData.color then\n\t\t\t\t\t\t\t\tinventoryItem.attribute.ImageColor3 = attributeData.color\n\t\t\t\t\t\t\t\tinventoryItem.attribute.Visible = true\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tlocal upgrades = inventorySlotData.successfulUpgrades\n\t\t\t\t\t\tif upgrades then\n\t\t\t\t\t\t\tfor i,child in pairs(inventoryItem.stars:GetChildren()) do\n\t\t\t\t\t\t\t\tif child:IsA(\"ImageLabel\") then\n\t\t\t\t\t\t\t\t\tchild.ImageColor3 = titleColor or Color3.new(1,1,1)\n\t\t\t\t\t\t\t\t\tchild.Visible = false\n\t\t\t\t\t\t\t\telseif child:IsA(\"TextLabel\") then\n\t\t\t\t\t\t\t\t\tchild.TextColor3 = titleColor or Color3.new(1,1,1)\n\t\t\t\t\t\t\t\t\tchild.Visible = false\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tinventoryItem.stars.Visible = true\n\t\t\t\t\t\t\tif upgrades <= 3 then\n\t\t\t\t\t\t\t\tfor i,star in pairs(inventoryItem.stars:GetChildren()) do\n\t\t\t\t\t\t\t\t\tlocal score = tonumber(star.Name)\n\t\t\t\t\t\t\t\t\tif score then\n\t\t\t\t\t\t\t\t\t\tstar.Visible = score <= upgrades\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tinventoryItem.stars.exact.Visible = false\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tinventoryItem.stars[\"1\"].Visible = true\n\t\t\t\t\t\t\t\tinventoryItem.stars.exact.Visible = true\n\t\t\t\t\t\t\t\tinventoryItem.stars.exact.Text = upgrades\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tinventoryItem.stars.Visible = true\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif module.isEnchantingEquipment and inventorySlotData_enchantment then\n\t\t\t\t\t\t\tlocal itemBaseData_enchantment = itemData[inventorySlotData_enchantment.id]\n\t\t\t\t\t\t\tlocal cost = itemBaseData_enchantment.upgradeCost or 1\n\t\t\t\t\t\t\tlocal max = inventoryItemBaseData.maxUpgrades\n\t\t\t\t\t\t\tlocal canEnchant, indexToRemove = enchantment.enchantmentCanBeAppliedToItem(inventorySlotData_enchantment, inventorySlotData)\n\t\t\t\t\t\t\tlocal blocked = not canEnchant\n\t\t\t\t\t\t\tinventoryItem.blocked.Visible = blocked\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tinventoryItem.shine.Visible = titleColor ~= nil and itemTier > 1\n\t\t\t\t\t\tinventoryItem.shine.ImageColor3 = titleColor or Color3.fromRGB(179, 178, 185)\n\n\t\t\t\t\t\tModules.fx.setFlash(inventoryItem.frame, inventoryItem.shine.Visible)\n\n\t\t\t\t\t\tinventoryItem.frame.ImageColor3 = (itemTier and itemTier > 1 and titleColor) or Color3.fromRGB(106, 105, 107)\n\n\t\t\t\t\t\tif not module.isEnchantingEquipment then\n\t\t\t\t\t\t\tuiCreator.drag.setIsDragDropFrame(inventoryItem.item)\n\t\t\t\t\t\t\tuiCreator.setIsDoubleClickFrame(inventoryItem.item, 0.2, onInventoryItemDoubleClicked)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tinventoryItem.item.MouseButton1Click:connect(function() onInventoryItemMouseButton1Click_isEnchantingEquipment(inventoryItem.item) end)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tinventoryItem.item.Image = \"\"\n\t\t\t\t\tinventoryItem.item.Visible = true\n\t\t\t\t\tinventoryItem.frame.Visible = false\n\t\t\t\t\tinventoryItem.shine.Visible = false\n\t\t\t\t\tinventoryItem.ImageTransparency = 0.5\n\t\t\t\t\tif currentCategoryTab == \"ability\" and i == n + 1 then\n\t\t\t\t\t\tlocal upgrade = ui.menu_inventory.upgrade:Clone()\n\t\t\t\t\t\tupgrade.Parent = inventoryItem\n\t\t\t\t\t\tupgrade.Visible = true\n\t\t\t\t\t\t-- todo: make sure there are actually abilities you can earn\n\t\t\t\t\t\tlocal function updateUpgradeButton()\n\t\t\t\t\t\t\tif learnAbilitiesFrame.Visible then\n\t\t\t\t\t\t\t\tupgrade.ImageColor3 = Color3.fromRGB(243, 37, 52)\n\t\t\t\t\t\t\t\tupgrade.text.Text = \"x\"\n\t\t\t\t\t\t\t\tupgrade.Active = true\n\t\t\t\t\t\t\t\tlocal position = inventoryItem.AbsolutePosition - menuUI.AbsolutePosition\n\t\t\t\t\t\t\t\tlearnAbilitiesFrame.Position = UDim2.new(0, position.X + inventoryItem.AbsoluteSize.X, 0, position.Y)\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tif unspentAbilityPoints > 0 then\n\t\t\t\t\t\t\t\t\tupgrade.ImageColor3 = Color3.fromRGB(255, 182, 12)\n\t\t\t\t\t\t\t\t\tupgrade.text.Text = \"+\"\n\t\t\t\t\t\t\t\t\tupgrade.Active = true\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tupgrade.ImageColor3 = Color3.fromRGB(185, 185, 185)\n\t\t\t\t\t\t\t\t\tupgrade.text.Text = \"+\"\n\t\t\t\t\t\t\t\t\tupgrade.Active = false\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tupdateLearnableAbilities()\n\t\t\t\t\t\t\tupgrade.Visible = learnableAbilityCount > 0\n\t\t\t\t\t\tend\n\t\t\t\t\t\tlearnAbilitiesFrame.Visible = false\n\t\t\t\t\t\tupdateUpgradeButton()\n\t\t\t\t\t\tupgrade.Activated:connect(function()\n\t\t\t\t\t\t\tif learnAbilitiesFrame.Visible then\n\t\t\t\t\t\t\t\tlearnAbilitiesFrame.Visible = false\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tupdateLearnableAbilities()\n\t\t\t\t\t\t\t\tlearnAbilitiesFrame.Visible = true\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tupdateUpgradeButton()\n\t\t\t\t\t\tend)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tinventoryItem.item.MouseEnter:connect(function() onInventoryItemMouseEnter(inventoryItem.item) end)\n\t\t\t\tinventoryItem.item.SelectionGained:connect(function() onInventoryItemMouseEnter(inventoryItem.item) end)\n\n\t\t\t\tinventoryItem.item.MouseLeave:connect(function() onInventoryItemMouseLeave(inventoryItem.item) end)\n\t\t\t\tinventoryItem.item.SelectionLost:connect(function() onInventoryItemMouseLeave(inventoryItem.item) end)\n\n\n\t\t\t\tinventoryItem.Name = tostring(i)\n\t\t\t\tinventoryItem.LayoutOrder = i\n\t\t\t\tinventoryItem.Parent = content\n\t\t\t\tinventoryItem.Visible = true\n\t\t\tend\n\n\n\t\t\tif selectionParent and selectionName then\n\t\t\t\tlocal newSelection = selectionParent:FindFirstChild(selectionName)\n\t\t\t\tif newSelection then\n\t\t\t\t\tgame.GuiService.SelectedObject = newSelection\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal canPlayerUseConsumable = true\n\tlocal function onSetCanPlayerUseConsumable(value)\n\t\tcanPlayerUseConsumable = value\n\tend\n\n\tlocal CONSUMABLE_COOLDOWN_TIME \t= 4\n\tlocal lastUseConsumable \t\t= 0\n\tlocal isCurrentlyConsuming \t\t= false\n\tlocal function activateItemRequest(inventorySlotData)\n\t\tif itemUseDebounce then\treturn false end\n\t\tprint(canPlayerUseConsumable)\n\t\tif not canPlayerUseConsumable then return false end\n\t\tif player.Character.PrimaryPart.health.Value <= 0 then return false end\n\t\tif network:invoke(\"getCharacterMovementStates\").isSprinting then return end\n\t\tif network:invoke(\"isCharacterStunned\") then return end\n\n\n\n\t\titemUseDebounce = true\n\n\t\tlocal itemBaseData = itemData[inventorySlotData.id]\n\t\tif itemBaseData then\n\t\t\tif itemBaseData.askForConfirmationBeforeConsume then\n\t\t\t\tlocal playerConfirmed = network:invoke(\"promptActionFullscreen\",\"Are you sure you want to use '\" .. itemBaseData.name .. \"'?\")\n\n\t\t\t\tif not playerConfirmed then\n\t\t\t\t\titemUseDebounce = false\n\n\t\t\t\t\treturn false, \"denied confirmation\"\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal consumeTimeReduction = math.clamp(1 - network:invoke(\"getCacheValueByNameTag\", \"nonSerializeData\").statistics_final.consumeTimeReduction, 0, 1)\n\n\t\t\tif itemBaseData.enchantsEquipment then\n\n\n\n--\t\t\t\tmodule.isEnchantingEquipment = true\n\t\t\t\tmodule.setIsEnchantingEquipment(true, inventorySlotData)\n\t\t\t\tinventorySlotData_enchantment \t= inventorySlotData\n\t\t\t\tscrollingMask.ImageTransparency = 0\n\t\t\t\tmenuUI.scrollPrompt.Visible = true\n\t\t\t\tmenuUI.scrollPrompt.contents.itemIcon.Image = itemBaseData.image\n\t\t\t\tscrollingMask.Image = itemBaseData.image\n\n\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\n\t\t\t\t-- show equipment tab\n\t\t\t\ttweenAnimations[currentCategoryTab].mouseLeaveTween:Play()\n\t\t\t\tcurrentCategoryTab = \"equipment\"\n\t\t\t\ttweenAnimations.equipment.mouseClickTween:Play()\n\t\t\t\tupdateInventory()\n\n\t\t\t\titemUseDebounce = false\n\t\t\t\treturn nil\n\t\t\telseif not isCurrentlyConsuming then\n\t\t\t\tlastUseConsumable = tick()\n\n\t\t\t\tlocal success, errorMessage\n\n\t\t\t\tlocal playerInput = itemBaseData.playerInputFunction and itemBaseData.playerInputFunction() or {}\n\n\t\t\t\tlocal itemConsumeTime = (itemBaseData.consumeTime or 1) * consumeTimeReduction\n\n\t\t\t\tnetwork:invoke(\"showConsumableCooldown\", itemConsumeTime)\n\n\t\t\t\tlocal function onAnimationStopped()\n\t\t\t\t\tisCurrentlyConsuming = false\n\n\t\t\t\t\tsuccess, errorMessage = network:invokeServer(\"activateItemRequest\", itemBaseData.category, inventorySlotData.position, nil, playerInput)\n\t\t\t\tend\n\n\t\t\t\tanimationInterface:replicateClientAnimationSequence(\"axeAnimations\", \"strike1\")\n\n\t\t\t\tdelay(itemConsumeTime + 1.5, function()\n\t\t\t\t\tonAnimationStopped()\n\t\t\t\tend)\n\n\t\t\t\titemUseDebounce = false\n\n\t\t\t\treturn success, errorMessage\n\t\t\tend\n\n\t\tend\n\n\t\titemUseDebounce = false\n\n\t\treturn false\n\tend\n\n\tnetwork:create(\"activateItemRequestLocal\", \"BindableFunction\", \"OnInvoke\", activateItemRequest)\n\tnetwork:create(\"getIsCurrentlyConsuming\", \"BindableFunction\", \"OnInvoke\", function()\n\t\treturn isCurrentlyConsuming\n\tend)\n\n\tlocal function onPropogationRequestToSelf(propogationNameTag, propogationData)\n\t\tif propogationNameTag == \"inventory\" then\n\t\t\tupdateInventory(propogationData)\n\t\telseif propogationNameTag == \"abilities\" then\n\t\t\tprint(\"abilities updated!\")\n\t\t\tabilityData = propogationData\n\t\t\tupdateInventory()\n\t\tend\n\tend\n\n\tlocal function onStopChannels_cancelConsuming(stateCancel)\n\t\tlocal myClientPlayerCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\t\tif myClientPlayerCharacterContainer then\n\t\t\tlocal animations = animationInterface:getAnimationsForAnimationController(myClientPlayerCharacterContainer.entity.AnimationController)\n\n\t\t\t-- wait to start playing\n\t\t\tif animations and animations.movementAnimations and animations.movementAnimations.consume_loop.IsPlaying then\n\t\t\t\tanimations.movementAnimations.consume_loop:Stop()\n\t\t\tend\n\t\tend\n\tend\n\n\t-- todo: yuck absorb this into input module\n\tgame:GetService(\"UserInputService\").InputBegan:connect(function(inputObject, absorbed)\n\t\tif menuUI.Visible then\n\t\t\tif inputObject.KeyCode == Enum.KeyCode.ButtonL1 then\n\t\t\t\tif currentCategoryTab == \"consumable\" then\n\t\t\t\t\tcurrentCategoryTab = \"equipment\"\n\t\t\t\telseif currentCategoryTab == \"miscellaneous\" then\n\t\t\t\t\tcurrentCategoryTab = \"consumable\"\n\t\t\t\telseif currentCategoryTab == \"equipment\" then\n\t\t\t\t\tcurrentCategoryTab = \"miscellaneous\"\n\t\t\t\tend\n\t\t\t\tgame.GuiService.SelectedObject = sortCategoryButtonContainer:FindFirstChild(currentCategoryTab)\n\t\t\t\tupdateInventory()\n\n\t\t\telseif inputObject.KeyCode == Enum.KeyCode.ButtonR1 then\n\t\t\t\tif currentCategoryTab == \"consumable\" then\n\t\t\t\t\tcurrentCategoryTab = \"miscellaneous\"\n\t\t\t\telseif currentCategoryTab == \"miscellaneous\" then\n\t\t\t\t\tcurrentCategoryTab = \"equipment\"\n\t\t\t\telseif currentCategoryTab == \"equipment\" then\n\t\t\t\t\tcurrentCategoryTab = \"consumable\"\n\t\t\t\tend\n\t\t\t\tgame.GuiService.SelectedObject = sortCategoryButtonContainer:FindFirstChild(currentCategoryTab)\n\t\t\t\tupdateInventory()\n\t\t\t\tfor i, button in pairs(sortCategoryButtonContainer:GetChildren()) do\n\t\t\t\t\tif button.Name == currentCategoryTab then\n\t\t\t\t\t\tbutton.ImageColor3 = Color3.fromRGB(84, 190, 255)\n\t\t\t\t\telseif button:IsA(\"GuiObject\") then\n\t\t\t\t\t\tbutton.ImageColor3 = Color3.fromRGB(202, 202, 202)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\n\n\tnetwork:create(\"localSignal_shopOpened\", \"BindableEvent\", \"Event\", function()\n\t\tif currentCategoryTab == \"ability\" then\n\t\t\tcurrentCategoryTab = \"miscellaneous\"\n\t\t\tupdateInventory()\n\t\t\tfor i, button in pairs(sortCategoryButtonContainer:GetChildren()) do\n\t\t\t\tif button.Name == currentCategoryTab then\n\t\t\t\t\tbutton.ImageColor3 = Color3.fromRGB(84, 190, 255)\n\t\t\t\telseif button:IsA(\"GuiObject\") then\n\t\t\t\t\tbutton.ImageColor3 = Color3.fromRGB(202, 202, 202)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\n\n\tnetwork:create(\"localSignal_enchantOpened\", \"BindableEvent\", \"Event\", function()\n\t\tif currentCategoryTab ~= \"ability\" then\n\t\t\tcurrentCategoryTab = \"ability\"\n\t\t\tupdateInventory()\n\t\t\tfor i, button in pairs(sortCategoryButtonContainer:GetChildren()) do\n\t\t\t\tif button.Name == currentCategoryTab then\n\t\t\t\t\tbutton.ImageColor3 = Color3.fromRGB(84, 190, 255)\n\t\t\t\telseif button:IsA(\"GuiObject\") then\n\t\t\t\t\tbutton.ImageColor3 = Color3.fromRGB(202, 202, 202)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\n\n\tlocal function setupSortCategoryButtonEffectsAndEvents(sortButton)\n\t\tlocal isHovered = false\n\n\t\tlocal tweenInformation \t= TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out, 0, false, 0)\n\n\t\ttweenAnimations[sortButton.Name] \t\t\t\t\t= {}\n\t\ttweenAnimations[sortButton.Name].mouseEnterTween \t= tweenService:Create(sortButton, tweenInformation, {ImageColor3 = Color3.fromRGB(120, 222, 222)})\n\t\ttweenAnimations[sortButton.Name].mouseLeaveTween \t= tweenService:Create(sortButton, tweenInformation, {ImageColor3 = Color3.fromRGB(202, 202, 202)})\n\t\ttweenAnimations[sortButton.Name].mouseClickTween \t= tweenService:Create(sortButton, tweenInformation, {ImageColor3 = Color3.fromRGB(84, 190, 255)})\n\n\t\tsortButton.Activated:connect(function()\n\t\t\tcurrentCategoryTab = sortButton.Name\n\n\t\t\t-- update the inventory ui to reflect the change\n\t\t\tupdateInventory()\n\t\tend)\n\n\t\tlocal function onInputBegan(inputObject)\n\t\t\tif inputObject.UserInputType == Enum.UserInputType.MouseMovement then\n\t\t\t\tisHovered = true\n\n\t\t\t\tif currentCategoryTab ~= sortButton.Name then\n\t\t\t\t\ttweenAnimations[sortButton.Name].mouseEnterTween:Play()\n\t\t\t\tend\n\t\t\telseif inputObject.UserInputType == Enum.UserInputType.MouseButton1 then\n\t\t\t\tif currentCategoryTab ~= sortButton.Name then\n\t\t\t\t\ttweenAnimations[sortButton.Name].mouseClickTween:Play()\n\n\t\t\t\t\tif tweenAnimations[currentCategoryTab] then\n\t\t\t\t\t\ttweenAnimations[currentCategoryTab].mouseLeaveTween:Play()\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\tend\n\t\tend\n\n\t\tlocal function onInputEnded(inputObject)\n\t\t\tif inputObject.UserInputType == Enum.UserInputType.MouseMovement then\n\t\t\t\tisHovered = false\n\n\t\t\t\tif currentCategoryTab ~= sortButton.Name then\n\t\t\t\t\ttweenAnimations[sortButton.Name].mouseLeaveTween:Play()\n\t\t\t\tend\n\n\t\t\tend\n\t\tend\n\n\t\tsortButton.InputBegan:connect(onInputBegan)\n\t\tsortButton.InputEnded:connect(onInputEnded)\n\tend\n\n\tlocal function onGetInventorySlotDataByInventorySlotUI(inventorySlotUI)\n\t\tif inventorySlotUI then\n\t\t\tif inventorySlotPairing[inventorySlotUI] then\n\t\t\t\treturn inventorySlotPairing[inventorySlotUI], \"item\"\n\t\t\tend\n\t\t\tif abilityPairing[inventorySlotUI] then\n\t\t\t\treturn abilityPairing[inventorySlotUI], \"ability\"\n\t\t\tend\n\t\tend\n\n\t\treturn nil\n\tend\n\n\tlocal function onGetCurrentInventoryCategory()\n\t\treturn currentCategoryTab\n\tend\n\n\tlocal function onGetInventorySlotDataWithItemId(itemId)\n\t\tif not lastInventoryDataReceived then return end\n\n\t\tfor i, inventorySlotData in pairs(lastInventoryDataReceived) do\n\t\t\tif inventorySlotData.id == itemId then\n\t\t\t\treturn inventorySlotData\n\t\t\tend\n\t\tend\n\n\t\treturn nil\n\tend\n\n\tlocal function main()\n\n\t\tplayerData = network:invoke(\"getLocalPlayerDataCache\")\n\n\t\tupdateInventory(network:invoke(\"getCacheValueByNameTag\", \"inventory\"))\n\n\n\t\tfor i, sortCategoryButton in pairs(sortCategoryButtonContainer:GetChildren()) do\n\t\t\tif sortCategoryButton:IsA(\"ImageButton\") then\n\t\t\t\tsetupSortCategoryButtonEffectsAndEvents(sortCategoryButton)\n\t\t\tend\n\t\tend\n\n\t\tnetwork:connect(\"stopChannels\", \"Event\", onStopChannels_cancelConsuming)\n\n\t\tnetwork:create(\"setCanPlayerUseConsumable\", \"BindableFunction\", \"OnInvoke\", onSetCanPlayerUseConsumable)\n\t\tnetwork:create(\"getInventorySlotDataByInventorySlotUI\", \"BindableFunction\", \"OnInvoke\", onGetInventorySlotDataByInventorySlotUI)\n\t\tnetwork:create(\"getCurrentInventoryCategory\", \"BindableFunction\", \"OnInvoke\", onGetCurrentInventoryCategory)\n\t\tnetwork:create(\"getInventorySlotDataWithItemId\", \"BindableFunction\", \"OnInvoke\", onGetInventorySlotDataWithItemId)\n\n\t\t--network:create(\"setOngoingPlayerTradeDataForInventoryException\")\n\t\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\n\t\t-- allow other scripts to update the inventoryUI\n\t\tnetwork:create(\"updateInventoryUI\", \"BindableFunction\", \"OnInvoke\", function()\n\t\t\tupdateInventory()\n\t\tend)\n\n\t\tlocal function onInputChanged(input)\n\t\t\tif module.isEnchantingEquipment then\n\t\t\t\tif scrollingMask.ImageTransparency ~= 0 then\n\t\t\t\t\tscrollingMask.ImageTransparency = 0\n\t\t\t\tend\n\n\n\t\t\t\tModules.tween(scrollingMask,{\"Position\"},UDim2.new(0, input.Position.X - 40, 0, input.Position.Y + 20),0.2)\n\t\t\t\t--scrollingMask.Position = UDim2.new(0, input.Position.X - 50, 0, input.Position.Y + 25)\n\t\t\tend\n\t\tend\n\n\t\tlocal userInputService = game:GetService(\"UserInputService\")\n\t\tuserInputService.InputChanged:connect(onInputChanged)\n\n\t\tuserInputService.InputBegan:connect(function(inputObject)\n\t\t\tif inputObject.UserInputType == Enum.UserInputType.MouseButton1 and module.isEnchantingEquipment then\n\t\t\t\twait(0.15)\n\t\t\t\titemUseDebounce = false\n--\t\t\t\tmodule.isEnchantingEquipment = false\n\t\t\t\tmodule.setIsEnchantingEquipment(false)\n\t\t\t\tmenuUI.scrollPrompt.Visible = false\n\t\t\t\tinventorySlotData_enchantment = nil\n\t\t\t\tscrollingMask.ImageTransparency = 1\n\n\t\t\t\t-- disgusting\n\t\t\t\tnetwork:invoke(\"updateInventoryUI\")\n\t\t\tend\n\t\tend)\n\tend\n\n\tdelay(1, main)\n\nend\n\nreturn module"
  },
  {
    "path": "src/StarterGui/itemAcquistion.lua",
    "content": "local module = {}\n\nlocal modules = require(game.ReplicatedStorage.modules)\nlocal network = modules.load(\"network\")\n\nlocal placeSetup = modules.load(\"placeSetup\")\nlocal utilities = modules.load(\"utilities\")\nlocal ability_utilities = modules.load(\"ability_utilities\")\nlocal enchantment = modules.load(\"enchantment\")\n\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal textService = game:GetService(\"TextService\")\n\nlocal itemsDataFolder = replicatedStorage:WaitForChild(\"itemData\")\nlocal itemLookup = require(itemsDataFolder)\nlocal perkLookup = require(replicatedStorage:WaitForChild(\"perkLookup\"))\nlocal itemAttributes = require(replicatedStorage:WaitForChild(\"itemAttributes\"))\n\nlocal player = game.Players.LocalPlayer\nlocal playerGui = player.PlayerGui\nlocal itemsFolder = placeSetup.awaitPlaceFolder(\"items\")\n\nlocal rootFrame = script.Parent.gameUI.itemHoverFrame\nlocal itemHoverFrame = rootFrame.contents\nlocal gameUI = rootFrame.Parent\n\nrootFrame.Visible = false\n\nlocal uiCreator = require(playerGui.uiCreator)\nlocal currentItemHoverData = nil\n--\tcurrentItemHoverData.item \t\t\t\t\t= nil\n--\tcurrentItemHoverData.pickupSizeAnimation \t= nil\n--\tcurrentItemHoverData.regularSizeAnimation \t= nil\nlocal ITEM_ACQUISITION_RANGE = 5\n\nlocal pickupInteractionPromptTable = {\n\tprompt = uiCreator.createInteractionPrompt({promptId = \"pickupInteractionPrompt\"},\n\t\t{text = \"Pick up\"}\n\t);\n\titem = nil;\n}\n\nmodule.getTitleColorForInventorySlotData = function()\n\nend\n\nlocal titleTextSize = 20\n\nmodule.tierColors = enchantment.tierColors\n\npickupInteractionPromptTable.prompt.manifest.LayoutOrder = 3\npickupInteractionPromptTable.prompt:hide(true)\n\nfunction module.init(Modules)\n\n\tlocal Mouse = player:GetMouse()\n\n\tlocal function reposition()\n\n\n\t\tlocal screensize = workspace.CurrentCamera.ViewportSize\n\n\t\tlocal x, y\n\n\t\tif Modules.input.mode.Value == \"pc\" then\n\t\t\tx = Mouse.X + 15\n\t\t\ty = Mouse.Y - 5\n\n\t\t\tif x + rootFrame.AbsoluteSize.X > screensize.X then\n\t\t\t\tx = Mouse.X - 15 - rootFrame.AbsoluteSize.X\n\t\t\tend\n\n\t\t\tlocal yDisplacement = (y + rootFrame.AbsoluteSize.Y) - (screensize.Y - 36)\n\n\t\t\tif yDisplacement > -115 then\n\t\t\t\ty = y - yDisplacement - 115\n\t\t\tend\n\n\t\t\tlocal targetPosition = UDim2.new(0, x, 0, y)\n\t\t\trootFrame.Position = targetPosition\n\t\telseif Modules.input.mode.Value == \"xbox\" then\n\t\t\tlocal frame = game.GuiService.SelectedObject\n\t\t\tif frame then\n\t\t\t\tx = frame.AbsolutePosition.X + frame.AbsoluteSize.X + 15\n\t\t\t\tif x + rootFrame.AbsoluteSize.X > screensize.X then\n\t\t\t\t\tx = frame.AbsolutePosition.X - rootFrame.AbsoluteSize.X - 15\n\t\t\t\tend\n\n\t\t\t\ty = frame.AbsolutePosition.Y + frame.AbsoluteSize.Y/2  - rootFrame.AbsoluteSize.Y/2\n\n\t\t\t\tlocal yDisplacement = (y + rootFrame.AbsoluteSize.Y) - (screensize.Y - 36)\n\n\t\t\t\tif yDisplacement > -115 then\n\t\t\t\t\ty = y - yDisplacement - 115\n\t\t\t\tend\n\n\t\t\t\tlocal targetPosition = UDim2.new(0, x, 0, y)\n\t\t\t\trootFrame.Position = targetPosition\n\t\t\tend\n\t\tend\n\n\tend\n\n\n\tgame:GetService(\"RunService\").Heartbeat:connect(reposition)\n\n\tlocal localization = Modules.localization\n\n\tlocal function raycastFromScreenPositionForItems(screenPositionX, screenPositionY)\n\t\tif player.Character and player.Character.PrimaryPart then\n\t\t\tlocal cameraRay = workspace.CurrentCamera:ScreenPointToRay(screenPositionX, screenPositionY)\n\n\t\t\tlocal ray = Ray.new(cameraRay.Origin, cameraRay.Direction.unit * 50)\n\t\t\tlocal hitPart, hitPosition = workspace:FindPartOnRayWithWhitelist(ray, {placeSetup.awaitPlaceFolder(\"items\")})\n\n\t\t\treturn hitPart, hitPosition, (hitPart and utilities.magnitude(hitPart.Position - player.Character.PrimaryPart.Position) or nil)\n\t\tend\n\tend\n\n\tmodule.source = \"none\"\n\n\tlocal function cleanup()\n\t\tfor i,child in pairs(itemHoverFrame:GetChildren()) do\n\t\t\tif child.Name == \"perk\" then\n\t\t\t\tchild:Destroy()\n\t\t\tend\n\t\tend\n\t\titemHoverFrame.main.thumbnailBG.ImageColor3 = Color3.new(1,1,1)\n\t\titemHoverFrame.soulbound.Visible = false\n\t\titemHoverFrame.enchantments.Visible = false\n\t\titemHoverFrame.notOwned.Visible = false\n\t\trootFrame.UIScale.Scale = 1\n\tend\n\n\tlocal function getItemInfo(itemBaseData, inventorySlotData)\n\t\tlocal statBonuses = {}\n\t\tlocal totalStats = {}\n\n\t\tif itemBaseData.baseDamage then\n\t\t\ttotalStats[\"baseDamage\"] = (totalStats[\"baseDamage\"] or 0) + itemBaseData.baseDamage\n\t\tend\n\t\tif itemBaseData.attackSpeed then\n\t\t\ttotalStats[\"attackSpeed\"] = (totalStats[\"attackSpeed\"] or 0) + itemBaseData.attackSpeed\n\t\tend\n\n\t\tif itemBaseData.modifierData then\n\t\t\tfor e,modifier in pairs(itemBaseData.modifierData) do\n\t\t\t\tfor statName, statValue in pairs(modifier) do\n\t\t\t\t\ttotalStats[statName] = (totalStats[statName] or 0) + statValue\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t-- cycle thru additional stats, increase totalDamage\n\n\t\tif inventorySlotData and inventorySlotData.modifierData then\n\t\t\tfor i, modifierData in pairs(inventorySlotData.modifierData) do\n\t\t\t\tfor statName,statValue in pairs(modifierData) do\n\t\t\t\t\tstatBonuses[statName] = (statBonuses[statName] or 0) + statValue\n\t\t\t\t\ttotalStats[statName] = (totalStats[statName] or 0) + statValue\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t-- attribute\n\t\t-- new: attributes treated as bonus stats\n\n\t\tif inventorySlotData and inventorySlotData.attribute then\n\t\t\tlocal attributeData = itemAttributes[inventorySlotData.attribute]\n\t\t\tif attributeData and attributeData.modifier then\n\t\t\t\tlocal attributeModifierData = attributeData.modifier(itemBaseData, inventorySlotData)\n\t\t\t\tif attributeModifierData then\n\t\t\t\t\tfor statName, statValue in pairs(attributeModifierData) do\n\t\t\t\t\t\tstatBonuses[statName] = (statBonuses[statName] or 0) + statValue\n\t\t\t\t\t\ttotalStats[statName] = (totalStats[statName] or 0) + statValue\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t-- cycle thru enchantments\n\t\tlocal statUpgrade = 0\n\t\tif inventorySlotData and inventorySlotData.enchantments then\n\t\t\tfor i, enchantment in pairs(inventorySlotData.enchantments) do\n\t\t\t\tlocal enchantmentBaseData = itemLookup[enchantment.id]\n\t\t\t\tlocal enchantState = enchantmentBaseData.enchantments[enchantment.state]\n\t\t\t\tif enchantState then\n\t\t\t\t\tif enchantState.modifierData then\n\t\t\t\t\t\tfor statName, statValue in pairs(enchantState.modifierData) do\n\t\t\t\t\t\t\tstatBonuses[statName] = (statBonuses[statName] or 0) + statValue\n\t\t\t\t\t\t\ttotalStats[statName] = (totalStats[statName] or 0) + statValue\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\tif enchantState.statUpgrade then\n\t\t\t\t\t\tstatUpgrade = statUpgrade + enchantState.statUpgrade\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t-- statUpgrade from items with variable enhancements (mainly hats!)\n\t\tif statUpgrade > 0 and itemBaseData.statUpgrade then\n\t\t\tfor statName, baseValue in pairs(itemBaseData.statUpgrade) do\n\t\t\t\tif baseValue ~= 0 then\n\t\t\t\t\tlocal statValue = baseValue * statUpgrade\n\t\t\t\t\tstatBonuses[statName] = (statBonuses[statName] or 0) + statValue\n\t\t\t\t\ttotalStats[statName] = (totalStats[statName] or 0) + statValue\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif itemBaseData.bonusStats then\n\t\t\t-- populate base stats\n\t\t\tfor statName, statValue in pairs(itemBaseData.bonusStats) do\n\t\t\t\tif type(statValue) == \"number\" then\n\t\t\t\t\ttotalStats[statName] = (totalStats[statName] or 0) + statValue\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\treturn totalStats, statBonuses\n\tend\n\n\tlocal function getTitleColorForInventorySlotData(inventorySlotData)\n\t\tlocal itemBaseData = itemLookup[inventorySlotData.id]\n\t\tlocal itemBaseTier = itemBaseData.tier\n\t\tlocal baseModifierData = itemBaseData.modifierData or {}\n\n\t\tlocal score = 0\n\n\t\tlocal totalStats, statBonuses = getItemInfo(itemBaseData, inventorySlotData)\n\n\t\tfor stat, value in pairs(statBonuses) do\n\t\t\tlocal adjustedValue = (value < 0 and value * 0.5) or value\n\t\t\tif stat == \"baseDamage\" or stat == \"defense\" then\n\t\t\t\tscore = score + adjustedValue\n\t\t\telseif stat == \"maxMana\" or stat == \"maxHealth\" then\n\t\t\t\tscore = score + adjustedValue * 0.05\n\t\t\telseif stat == \"str\" or stat == \"dex\" or stat == \"int\" or stat == \"vit\" then\n\t\t\t\tscore = score + adjustedValue * 0.7\n\t\t\telseif stat == \"stamina\" then\n\t\t\t\tscore = score + adjustedValue * 2\n\t\t\telseif stat == \"jump\" or stat == \"walkspeed\" then\n\t\t\t\tscore = score + adjustedValue\n\t\t\telseif stat == \"criticalStrikeChance\" or stat == \"blockChance\" then\n\t\t\t\tscore = score + adjustedValue * 50\n\t\t\tend\n\t\tend\n\n\t\tlocal baseMaxUpgrades = (itemBaseData.maxUpgrades or 0)\n\t\tlocal bench = baseMaxUpgrades / 7\n\n\t\tlocal enchantTier\n\t\tif baseMaxUpgrades > 0 and not itemBaseData.notUpgradable then\n\t\t\tif score >= 49*bench then\n\t\t\t\tenchantTier = 6\n\t\t\telseif score >= 32*bench then\n\t\t\t\tenchantTier = 5\n\t\t\telseif score >= 21*bench then\n\t\t\t\tenchantTier = 4\n\t\t\telseif score >= 10*bench then\n\t\t\t\tenchantTier = 3\n\t\t\telseif score < 0 then\n\t\t\t\tenchantTier = -1\n\t\t\telseif inventorySlotData and inventorySlotData.upgrades and inventorySlotData.upgrades > 0 then\n\t\t\t\tif inventorySlotData.successfulUpgrades and inventorySlotData.successfulUpgrades > 0 then\n\t\t\t\t\tenchantTier = 2\n\t\t\t\telse\n\t\t\t\t\tenchantTier = -1\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\n\t\tlocal trueTier = itemBaseTier\n\t\tif enchantTier then\n\t\t\tif itemBaseTier == nil or enchantTier > itemBaseTier then\n\t\t\t\ttrueTier = enchantTier\n\t\t\tend\n\t\tend\n\n\t\tlocal titleColor = trueTier and module.tierColors[trueTier]\n\n\t\treturn titleColor, trueTier\n\tend\n\n\tmodule.getTitleColorForInventorySlotData = getTitleColorForInventorySlotData\n\n\t-- used before reverting to 7 upgrade system\n\tlocal function getTitleColorForInventorySlotData_defunct(inventorySlotData)\n\n\t\tlocal itemBaseData = itemLookup[inventorySlotData.id]\n\t\tlocal itemBaseTier = itemBaseData.tier or 1\n\n\t\tlocal maxUpgrades = inventorySlotData.maxUpgrades or itemBaseData.maxUpgrades\n\n\t\tlocal averageEnchantmentTier = 0\n\n\t\tlocal enchantmentCount = inventorySlotData and inventorySlotData.upgrades or 0\n\t\t-- we want to include failed upgrades which are not included in slotData.enchantments\n\t\tlocal enchantments = inventorySlotData.enchantments or {}\n\t\tif enchantmentCount > 0 then\n\t\t\tlocal enchantmentTierTotal = 0\n\t\t\tfor i, enchantmentData in pairs(enchantments) do\n\t\t\t\tlocal enchantmentBaseData = itemLookup[enchantmentData.id].enchantments[enchantmentData.state]\n\t\t\t\tlocal enchantmentTier = enchantmentBaseData.tier\n\t\t\t\tif enchantmentTier then\n\t\t\t\t\tenchantmentTierTotal = enchantmentTierTotal + enchantmentTier\n\t\t\t\tend\n\t\t\tend\n\t\t\taverageEnchantmentTier = 1 + 0.01 + enchantmentTierTotal / maxUpgrades\n\t\tend\n\n\t\tlocal tier = ((averageEnchantmentTier > itemBaseTier or averageEnchantmentTier == -1) and math.floor(averageEnchantmentTier)) or itemBaseTier\n\n\t\t-- any upgrade will turn an item blue\n\t\tif averageEnchantmentTier > 1 and tier < 2 then\n\t\t\ttier = 2\n\t\tend\n\n\t\tif tier ~= 1 then\n\t\t\tlocal titleColor = module.tierColors[tier]\n\t\t\treturn titleColor\n\t\tend\n\tend\n\n\tlocal lastTextData\n\n\n\tlocal function populateItemHoverFrameWithTextData(textData)\n\n\t\t-- dont let text override important stuff\n\t\tif itemHoverFrame.Parent.Visible and itemHoverFrame.Visible and itemHoverFrame.main.Visible and itemHoverFrame.main.thumbnail.Visible then\n\t\t\treturn false\n\t\tend\n\n\t\titemHoverFrame.header.itemName.Position = UDim2.new(0, 0,0, 3)\n\t\titemHoverFrame.header.itemName.cuteDecor.Visible = false\n\n\t\tif textData == nil or textData.text == nil then\n\t\t\tif textData.source == nil or textData.source == lastTextData.source then\n\t\t\t\trootFrame.Visible = false\n\t\t\t\trootFrame.contents.Visible = false\n\t\t\tend\n\t\t\treturn false\n\t\tend\n\n\t\trootFrame.Size = UDim2.new(0, 320 + 4, 0, 100)\n\n\t\trootFrame.contents.Visible = false\n\n\t\tlastTextData = textData\n\n\n\t\tModules.input.setCurrentFocusFrame(rootFrame)\n\n--\t\trootFrame.itemType.Visible = false\n\t\tcleanup()\n\n\t\tmodule.source = \"text\"\n\n\t\titemHoverFrame.main.Visible = false\n\t\titemHoverFrame.stats.Visible = false\n\n\t\tlocal txtsrc = textData.text and localization.translate(textData.text, itemHoverFrame.header.itemName)\n\t\titemHoverFrame.header.itemName.Text = txtsrc or \"???\"\n\n\n\n\t\titemHoverFrame.header.itemName.TextColor3 = textData.textColor3 or Color3.new(1,1,1)\n\t\titemHoverFrame.header.itemName.Font = textData.font or Enum.Font.SourceSansBold\n\n\t\tlocal itemNameTextSize = textService:GetTextSize(itemHoverFrame.header.itemName.Text, titleTextSize, itemHoverFrame.header.itemName.Font, Vector2.new())\n\n\t\tlocal maxTextSize = itemNameTextSize\n\n\t\tlocal numStats = 0\n\n\t\t-- clear previous stats\n\t\tfor i, obj in pairs(itemHoverFrame.stats.container:GetChildren()) do\n\t\t\tif not obj:IsA(\"UIListLayout\") then\n\t\t\t\tobj:Destroy()\n\t\t\tend\n\t\tend\n\n\t\tif textData.additionalLines then\n\t\t\tfor i, line in pairs(textData.additionalLines) do\n\t\t\t\tuiCreator.createTextFragmentLabels(itemHoverFrame.stats.container, {line})\n\t\t\t\tlocal fragmentTextSize = textService:GetTextSize(line.text or \"???\", line.textSize or 20, line.font or Enum.Font.SourceSansBold, Vector2.new())\n\t\t\t\tif fragmentTextSize.X > maxTextSize.X then\n\t\t\t\t\tmaxTextSize = Vector2.new(math.min(320 + 10, fragmentTextSize.X), fragmentTextSize.Y)\n\t\t\t\tend\n\t\t\t\tlocal fragmentYSize = math.ceil(fragmentTextSize.X / (320 + 10))\n\t\t\t\tnumStats = numStats + fragmentYSize\n\t\t\tend\n\t\tend\n\n\t\titemHoverFrame.stats.container.Size = UDim2.new(1, 0, 0, numStats * 20)\n\t\titemHoverFrame.stats.Size \t\t\t= UDim2.new(1, 0, 0, numStats * 20)\n\n\t\titemHoverFrame.stats.Visible = numStats > 0\n\n\t\tlocal y = 0\n\t\tfor i,child in pairs(itemHoverFrame:GetChildren()) do\n\t\t\tif child:IsA(\"GuiObject\") and child.Visible then\n\t\t\t\ty = y + child.AbsoluteSize.Y + itemHoverFrame.UIListLayout.Padding.Offset\n\t\t\tend\n\t\tend\n\n\t\tlocal x = 20 + maxTextSize.X\n\n\t\tlocal openSize = UDim2.new(0, x + 4, 0, y + 4)\n\t\trootFrame.Size = openSize\n\n\n\t\titemHoverFrame.header.itemName.Size = UDim2.new(0, itemNameTextSize.X,0, 18)\n\t\titemHoverFrame.header.itemName.stars.Visible = false\n\t\trootFrame.UIScale.Scale = Modules.input.menuScale\n\t\treposition()\n\n\t\trootFrame.Visible = true\n\t\trootFrame.contents.Visible = true\n\tend\n\n\tnetwork:create(\"populateItemHoverFrameWithTextData\",\"BindableFunction\",\"OnInvoke\", populateItemHoverFrameWithTextData)\n\n\tlocal function populateItemHoverFrameWithAbility(abilityInfo, level, comparisonInfo, comparisonLevel)\n\t\t-- use comparison info for seeing dif between variants and upgrades\n\n\t\tlocal displayAbilityInfo = comparisonInfo or abilityInfo\n\t\tlocal displayLevel = comparisonLevel or level\n\n\t\titemHoverFrame.header.itemName.Position = UDim2.new(0, 18,0, 3)\n\t\titemHoverFrame.header.itemName.cuteDecor.Visible = true\n\t\titemHoverFrame.header.itemName.cuteDecor.Image = \"rbxassetid://2528902611\"\n\n\t\tif abilityInfo == nil then\n\t\t\trootFrame.Visible = false\n\t\t\trootFrame.contents.Visible = false\n\t\t\treturn false\n\t\tend\n\n\t\trootFrame.Size = UDim2.new(0, 320 + 4, 0, 40 + 4)\n\t\trootFrame.contents.Visible = false\n\n\t\tModules.input.setCurrentFocusFrame(rootFrame)\n\n\n\t\titemHoverFrame.main.thumbnailBG.Visible = false\n\n--\t\trootFrame.itemType.Visible = false\n\n\n\t\titemHoverFrame.header.itemName.AutoLocalize = false\n\n\t\titemHoverFrame.main.Visible = true\n\t\titemHoverFrame.main.thumbnail.Image = displayAbilityInfo.image\n\t\tlocal abilityName = displayAbilityInfo.name and localization.translate(displayAbilityInfo.name, itemHoverFrame.header.itemName) or \"???\"\n\t\tlocal unlearnedString = \"(\" .. localization.translate(\"Unlearned\", itemHoverFrame.header.itemName) .. \")\"\n\t\titemHoverFrame.header.itemName.Text = abilityName .. \" \" .. ((displayLevel > 1 and \"+\"..(displayLevel-1)) or (displayLevel == 0 and unlearnedString) or \"\")\n\n\t\tcleanup()\n\t\tmodule.source = \"ability\"\n\n\t\tlocal levelToUse = (level > 0 and level) or 1\n\n\t\tlocal abilityStats\n\t\tif abilityInfo.statistics then\n\t\t\tabilityStats = ability_utilities.getAbilityStatisticsForRank(abilityInfo, levelToUse)\n\t\tend\n\t\tlocal tier = -1\n\n\t\tif displayLevel > 0 then\n\t\t\ttier = 1\n\t\t\tif displayLevel > 1 then\n\t\t\t\ttier = 2\n\t\t\tend\n\t\tend\n\n\t\tlocal comparisonStats\n\t\tif comparisonInfo and comparisonLevel then\n\t\t\tcomparisonStats = ability_utilities.getAbilityStatisticsForRank(comparisonInfo, comparisonLevel)\n\t\tend\n\n\t\tif displayLevel ~= 0 and abilityStats and abilityStats.tier and abilityStats.tier > tier then\n\t\t\ttier = abilityStats.tier\n\t\tend\n\n\t\tif displayLevel ~= 0 and comparisonStats and comparisonStats.tier and comparisonStats.tier > tier then\n\t\t\ttier = comparisonStats.tier\n\t\tend\n\n\t\tlocal titleColor = module.tierColors[tier]\n\n\t\tlocal desc = displayAbilityInfo.description and localization.translate(displayAbilityInfo.description, itemHoverFrame.main.mainContents.itemDescription)\n\t\titemHoverFrame.main.mainContents.itemDescription.Text = desc or \"???\"\n\n\t\titemHoverFrame.main.mainContents.equippableClasses.Visible = false\n\t\titemHoverFrame.main.mainContents.abilityInfo.Visible = false\n\n\n\n\t\t-- clear previous stats\n\t\tfor i, obj in pairs(itemHoverFrame.stats.container:GetChildren()) do\n\t\t\tif not obj:IsA(\"UIListLayout\") then\n\t\t\t\tobj:Destroy()\n\t\t\tend\n\t\tend\n\n\t\tlocal numStats = 0\n\n\n\n\t\tif abilityInfo.statistics then\n\n\t\t\tif comparisonStats then\n\t\t\t\tfor stat,value in pairs(comparisonStats) do\n\t\t\t\t\tif not abilityStats[stat] then\n\t\t\t\t\t\tabilityStats[stat] = 0\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tfor stat,value in pairs(abilityStats) do\n\t\t\t\tif stat ~= \"tier\" and string.sub(stat, 1, 1) ~= \"_\" and not ((stat == \"manaCost\" or stat == \"cost\") and value == 0) then\n\n\t\t\t\t\tlocal prefix = stat\n\t\t\t\t\tlocal displayValueFunction\n\n\t\t\t\t\tlocal statTextColor = Color3.fromRGB(255,255,255)\n\t\t\t\t\tlocal statPriority = 5\n\t\t\t\t\tlocal operator = \":\"\n\n\t\t\t\t\tif stat == \"damageMultiplier\" then\n\t\t\t\t\t\tprefix = \"Power\"\n\t\t\t\t\t\tdisplayValueFunction = function(displayValue)\n\t\t\t\t\t\t\treturn displayValue * 100\n\t\t\t\t\t\tend\n\t\t\t\t\t\tstatPriority = 1\n\t\t\t\t\telseif stat == \"healing\" then\n\t\t\t\t\t\tstatPriority = 2\n\t\t\t\t\telseif stat == \"walkspeed\" then\n\t\t\t\t\t\tprefix = \"Movement Speed\"\n\t\t\t\t\t\toperator = \" +\"\n\t\t\t\t\telseif stat == \"staminaRecovery\" then\n\t\t\t\t\t\tprefix = \"Stamina Recovery\"\n\t\t\t\t\t\tdisplayValueFunction = function(displayValue)\n\t\t\t\t\t\t\treturn displayValue * 100 .. \"%\"\n\t\t\t\t\t\tend\n\t\t\t\t\t\toperator = \" +\"\n\t\t\t\t\telseif stat == \"manaRestored\" then\n\t\t\t\t\t\tprefix = \"MP Restored\"\n\t\t\t\t\telseif stat == \"cooldown\" then\n\t\t\t\t\t\tdisplayValueFunction = function(displayValue)\n\t\t\t\t\t\t\treturn displayValue .. \"s\"\n\t\t\t\t\t\tend\n\t\t\t\t\t\tstatTextColor = Color3.fromRGB(160,160,160)\n\t\t\t\t\t\tstatPriority = 6\n\t\t\t\t\telseif stat == \"range\" then\n\t\t\t\t\t\tdisplayValueFunction = function(displayValue)\n\t\t\t\t\t\t\treturn displayValue .. \"m\"\n\t\t\t\t\t\tend\n\t\t\t\t\t\tstatPriority = 3\n\t\t\t\t\telseif stat == \"cost\" or stat == \"manaCost\" then\n\t\t\t\t\t\tprefix = \"MP\"\n\t\t\t\t\t\tstatTextColor = Color3.fromRGB(0, 152, 255)\n\t\t\t\t\t\tstatPriority = 7\n\t\t\t\t\telseif stat == \"healthCost\" then\n\t\t\t\t\t\tprefix = \"HP\"\n\t\t\t\t\t\tstatTextColor = Color3.fromRGB(226, 34, 40)\n\t\t\t\t\t\tstatPriority = 7\n\t\t\t\t\tend\n\n\t\t\t\t\tprefix = string.upper(string.sub(prefix,1,1)) .. string.sub(prefix,2)\n\n\t\t\t\t\tlocal nextLevelString\n\n\t\t\t\t\tif comparisonStats and (comparisonStats[stat] == nil or comparisonStats[stat] ~= value) then\n\t\t\t\t\t\tlocal comparisonValue = comparisonStats[stat] or 0\n\t\t\t\t\t\tlocal comparisonDisplayValue = displayValueFunction and displayValueFunction(comparisonValue) or comparisonValue\n\t\t\t\t\t\tnextLevelString = {\n\t\t\t\t\t\t\ttext = \"→ \" .. comparisonDisplayValue;\n\t\t\t\t\t\t\ttextColor3 = Color3.fromRGB(220, 181, 25);\n\t\t\t\t\t\t\tfont = Enum.Font.SourceSansBold;\n\t\t\t\t\t\t\ttextSize = 23;\n\t\t\t\t\t\t}\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal displayValue = displayValueFunction and displayValueFunction(value) or value\n\n\t\t\t\t\tlocal fragment = uiCreator.createTextFragmentLabels(itemHoverFrame.stats.container,{\n\n\t\t\t\t\t\t--[[\n\t\t\t\t\t\t{text = (statValue >= 0 and \"+\" or \"\") .. statValue .. (statName:find(\"Percent\") and \"%\" or \"\")},\n\t\t\t\t\t\t{text = statName:gsub(\"Percent\", \"\"):gsub(\"Flat\", \"\")}\n\t\t\t\t\t\t]]\n\t\t\t\t\t\t{text = prefix..operator, textColor3 = statTextColor; textSize = 19;},\n\t\t\t\t\t\t{text = tostring(displayValue); font = Enum.Font.SourceSansBold, textColor3 = statTextColor; textSize = 23;},\n\t\t\t\t\t\tnextLevelString\n\t\t\t\t\t})\n\t\t\t\t\tnumStats = numStats + 1\n\t\t\t\t\tfragment.LayoutOrder = statPriority\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tlocal itemNameTextSize \t\t\t= textService:GetTextSize(itemHoverFrame.header.itemName.Text, titleTextSize, itemHoverFrame.header.itemName.Font, Vector2.new())\n\n\t\tlocal itemDescriptionTextSize \t= textService:GetTextSize(itemHoverFrame.main.mainContents.itemDescription.Text, itemHoverFrame.main.mainContents.itemDescription.TextSize, itemHoverFrame.main.mainContents.itemDescription.Font, Vector2.new())\n\n\t\tlocal itemNameLines \t\t= 1 -- math.ceil(itemNameTextSize.X / itemHoverFrame.header.itemName.AbsoluteSize.X)\n\t\tlocal itemDescriptionLines \t= math.ceil(itemDescriptionTextSize.X / itemHoverFrame.main.mainContents.itemDescription.AbsoluteSize.X)\n\t\t--[[\n\t\tlocal headerYSize = itemNameTextSize.Y * itemNameLines + itemHoverFrame.header.itemType.TextBounds.Y + 5\n\n\t\titemHoverFrame.header.Size \t\t\t= UDim2.new(1, -10, 0, headerYSize)\n\t\titemHoverFrame.header.itemName.Size = UDim2.new(1, -5, 0, itemNameTextSize.Y * itemNameLines)\n\t\titemHoverFrame.stats.container.Size = UDim2.new(1, 0, 0, numStats * 18)\n\t\titemHoverFrame.stats.Size \t\t\t= UDim2.new(1, 0, 0, numStats * 18 + 20)\n\t\titemHoverFrame.main.mainContents.itemDescription.Size = UDim2.new(1, 0, 0, itemDescriptionTextSize.Y * itemDescriptionLines + 10)\n\n\t\t--local y = headerYSize + itemDescriptionTextSize.Y * itemDescriptionLines + 20 + numStats * 18 + 30 + 10\n\t\tlocal y = headerYSize + itemHoverFrame.main.mainContents.AbsoluteSize.Y + numStats * 18 + 20\t+ (numStats > 0 and 20 or 0)\n\n\n\t\t]]\n\n\n\t\tlocal headerYSize = itemNameTextSize.Y * itemNameLines\n\n\t\titemHoverFrame.header.Size \t\t\t= UDim2.new(1, 0, 0, headerYSize)\n\t\titemHoverFrame.header.itemName.Size = UDim2.new(0, itemNameTextSize.X, 0, itemNameTextSize.Y * itemNameLines)\n\t\titemHoverFrame.header.itemName.stars.Visible = false\n\t\titemHoverFrame.stats.container.Size = UDim2.new(1, 0, 0, numStats * 19)\n\t\titemHoverFrame.stats.Size \t\t\t= UDim2.new(1, 0, 0, numStats * 19)\n\n\t\titemHoverFrame.stats.Visible = numStats > 0\n\n\t\titemHoverFrame.main.mainContents.itemDescription.Size = UDim2.new(1, 0, 0, itemDescriptionTextSize.Y * itemDescriptionLines + 10)\n\n\n\t\tlocal y = 16\n\t\tfor i,child in pairs(itemHoverFrame:GetChildren()) do\n\t\t\tif child:IsA(\"GuiObject\") and child.Visible then\n\t\t\t\ty = y + child.AbsoluteSize.Y + itemHoverFrame.UIListLayout.Padding.Offset\n\t\t\tend\n\t\tend\n\n\t\tlocal openSize = UDim2.new(0, 320 + 4, 0, y + 4)\n\n\t\trootFrame.Size = openSize\n\n\n\t\titemHoverFrame.header.itemName.TextColor3 = titleColor\n\t\titemHoverFrame.header.itemName.cuteDecor.ImageColor3 = titleColor\n\t\titemHoverFrame.main.thumbnail.BorderColor3 = titleColor\n\t\trootFrame.UIScale.Scale = Modules.input.menuScale\n\t\treposition()\n\t\trootFrame.Visible = true\n\t\trootFrame.UIScale.Scale = Modules.input.menuScale\n\t\trootFrame.contents.Visible = true\n\n\tend\n\n\n\n\n\n\tnetwork:create(\"populateItemHoverFrameWithAbility\",\"BindableFunction\",\"OnInvoke\", populateItemHoverFrameWithAbility)\n\n\tlocal function populateItemHoverFrame(itemBaseData, newSource, inventorySlotData, additionalInfo)\n\n\t\titemHoverFrame.header.itemName.Position = UDim2.new(0, 18,0, 3)\n\t\titemHoverFrame.header.itemName.cuteDecor.Visible = true\n\t\titemHoverFrame.header.itemName.cuteDecor.Image = \"rbxassetid://2528902611\"\n\n--\t\titemHoverFrame.main.thumbnail.BackgroundTransparency = 0.2\n\n\t\titemHoverFrame.main.thumbnailBG.Visible = true\n\n\t\tModules.input.setCurrentFocusFrame(rootFrame)\n\n\t\titemHoverFrame.main.mainContents.abilityInfo.Visible = false\n\n\t\titemHoverFrame.main.Visible = true\n\n\t\tnewSource = newSource or \"pickup\"\n\t\tmodule.source = newSource\n\n\t\tif itemBaseData == nil then\n\t\t\trootFrame.Visible = false\n\t\t\trootFrame.contents.Visible = false\n\t\t\tmodule.source = \"none\"\n\t\t\treturn false\n\t\tend\n\n\t\trootFrame.Size = UDim2.new(0, 320 + 4, 0, 40 + 4)\n\n\t\tif not rootFrame.Visible then\n\t\t\trootFrame.contents.Visible = false\n\t\tend\n\n\n\t\titemHoverFrame.header.itemName.AutoLocalize = false\n\n\t\tif inventorySlotData and inventorySlotData.customName then\n\t\t\titemHoverFrame.header.itemName.Font = Enum.Font.SourceSansItalic\n--\t\t\titemHoverFrame.header.itemName.AutoLocalize = false\n\t\telse\n\t\t\titemHoverFrame.header.itemName.Font = Enum.Font.SourceSansBold\n--\t\t\titemHoverFrame.header.itemName.AutoLocalize = true\n\t\tend\n\n\t\tcleanup()\n\n\t\tlocal itemBaseName = itemBaseData.name and localization.translate(itemBaseData.name, itemHoverFrame.header.itemName)\n\t\tlocal itemname = inventorySlotData and inventorySlotData.customName or itemBaseName or \"???\"\n\n\t\tlocal attribute = inventorySlotData.attribute\n\t\tif attribute then\n\t\t\tlocal attributeData = itemAttributes[attribute]\n\t\t\tif attributeData then\n\t\t\t\tif attributeData.color then\n\t\t\t\t\titemHoverFrame.main.thumbnailBG.ImageColor3 = attributeData.color\n\t\t\t\tend\n\t\t\t\tif attributeData.prefix and not inventorySlotData.customName then\n\t\t\t\t\tlocal attributeName = localization.translate(attributeData.prefix, itemHoverFrame.header.itemName)\n\t\t\t\t\titemname = attributeName .. \" \" .. itemname\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\titemname = itemname .. ((inventorySlotData and inventorySlotData.upgrades and inventorySlotData.upgrades > 0 and \" +\"..(inventorySlotData.successfulUpgrades or 0)) or \"\")\n\n\t\titemHoverFrame.header.itemName.Text \t= itemname\n\n\t\titemHoverFrame.main.mainContents.itemDescription.AutoLocalize = false\n\t\tlocal desc = itemBaseData.description and localization.translate(itemBaseData.description, itemHoverFrame.main.mainContents.itemDescription)\n\t\titemHoverFrame.main.mainContents.itemDescription.Text \t= desc or \"Unknown\"\n\t\titemHoverFrame.header.itemType.Text \t= itemBaseData.category and string.upper(itemBaseData.category:sub(1, 1)) .. itemBaseData.category:sub(2) or \"Unknown\"\n\n\t\t--[[\n\t\tlocal itemNameTextSize \t\t\t= textService:GetTextSize(itemBaseData.name, 20, Enum.Font.SourceSansBold, Vector2.new())\n\t\tlocal itemDescriptionTextSize \t= textService:GetTextSize(itemBaseData.description, 14, Enum.Font.SourceSansItalic, Vector2.new())\n\t\t]]\n\t\tlocal itemNameTextSize \t\t\t= textService:GetTextSize(itemHoverFrame.header.itemName.Text, titleTextSize, itemHoverFrame.header.itemName.Font, Vector2.new())\n\t\tlocal itemDescriptionTextSize \t= textService:GetTextSize(itemHoverFrame.main.mainContents.itemDescription.Text, itemHoverFrame.main.mainContents.itemDescription.TextSize, itemHoverFrame.main.mainContents.itemDescription.Font, Vector2.new())\n\n\n\n\t\tlocal push = 0\n\n\t\t-- old scrolls indicator (defunct)\n\t\t--[[\n\t\tif inventorySlotData.enchantments and #inventorySlotData.enchantments > 0 then\n\t\t\tfor i, child in pairs(itemHoverFrame.enchantments:GetChildren()) do\n\t\t\t\tif child:IsA(\"GuiObject\") then\n\t\t\t\t\tchild:Destroy()\n\t\t\t\tend\n\t\t\tend\n\t\t\tfor i, enchantment in pairs(inventorySlotData.enchantments) do\n\t\t\t\tlocal enchantmentBaseData = itemLookup[enchantment.id]\n\t\t\t\tlocal enchantment = enchantmentBaseData.enchantments[enchantment.state]\n\t\t\t\tlocal frame = script.enchantment:Clone()\n\t\t\t\tlocal tierColor = module.tierColors[enchantment.tier + 1]\n\t\t\t\tframe.frame.ImageColor3 = tierColor\n\t\t\t\tframe.shine.ImageColor3 = tierColor\n\t\t\t\tframe.item.Image = enchantmentBaseData.image\n\t\t\t\tframe.LayoutOrder = 10-(enchantment.tier)\n\t\t\t\tframe.Parent = itemHoverFrame.enchantments\n\t\t\t\tModules.fx.setFlash(frame.frame, enchantment.tier >= 1)\n\t\t\tend\n\t\t\titemHoverFrame.enchantments.Visible = true\n\t\t\tpush = push + 36\n\t\tend\n\t\t]]\n\n\t\tif (inventorySlotData and inventorySlotData.soulbound) or itemBaseData.soulbound then\n\t\t\titemHoverFrame.soulbound.Visible = true\n\t\t\tpush = push + 24\n\t\tend\n\n\t\tif itemBaseData.perks then\n\t\t\tfor perkName, active in pairs(itemBaseData.perks) do\n\t\t\t\tif active then\n\t\t\t\t\tlocal perkData = perkLookup[perkName]\n\t\t\t\t\tif perkData then\n\t\t\t\t\t\tlocal perk = script.perk:Clone()\n\t\t\t\t\t\tperk.title.Text = perkData.title\n\t\t\t\t\t\tperk.description.Text = perkData.description\n\t\t\t\t\t\tif perkData.color then\n\t\t\t\t\t\t\tperk.ImageColor3 = perkData.color\n\t\t\t\t\t\tend\n\t\t\t\t\t\tperk.Visible = true\n\t\t\t\t\t\tperk.Parent = itemHoverFrame\n\t\t\t\t\t\tpush = push + 46\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif additionalInfo and additionalInfo.notOwned then\n\t\t\titemHoverFrame.notOwned.Visible = true\n\t\t\tpush = push + 24\n\t\tend\n\n\n\t\tlocal itemNameLines = 1\n--\t\tlocal itemNameLines \t\t= math.ceil(itemNameTextSize.X / itemHoverFrame.header.itemName.AbsoluteSize.X)\n\t\tlocal itemDescriptionLines \t= math.ceil(itemDescriptionTextSize.X / itemHoverFrame.main.mainContents.itemDescription.AbsoluteSize.X)\n\n\n\n\n\n\t\titemHoverFrame.main.thumbnail.Image \t\t\t= itemBaseData.image\n\n\t\titemHoverFrame.main.mainContents.equippableClasses.Visible = itemBaseData.category and itemBaseData.category == \"equipment\"\n\t\titemHoverFrame.main.mainContents.equippableClasses.requirements.reqLevel.Visible = false\n--\t\titemHoverFrame.main.mainContents.equippableClasses.requirements.reqLevel\n\t\tif itemBaseData.minLevel then\n\t\t\titemHoverFrame.main.mainContents.equippableClasses.requirements.reqLevel.Visible = true\n\t\t\titemHoverFrame.main.mainContents.equippableClasses.requirements.reqLevel.Text = \"REQ Lvl. \"..itemBaseData.minLevel or 0\n\t\t\tlocal level = network:invoke(\"getCacheValueByNameTag\", \"level\") or 1\n\t\t\titemHoverFrame.main.mainContents.equippableClasses.requirements.reqLevel.TextColor3 = Color3.fromRGB(203, 203, 203)\n\t\t\tif itemBaseData.minLevel > level then\n\t\t\t\titemHoverFrame.main.mainContents.equippableClasses.requirements.reqLevel.TextColor3 = Color3.fromRGB(203, 69, 71)\n\t\t\tend\n\t\tend\n\n\t\tlocal canEquipClass = false\n\n\t\tlocal class = itemBaseData.minimumClass or \"adventurer\"\n\t\tclass = class:lower()\n\n\t\tif class == \"adventurer\" then\n\t\t\tcanEquipClass = true\n\t\telseif network:invoke(\"getCacheValueByNameTag\", \"class\"):lower() == class then\n\t\t\tcanEquipClass = true\n\t\telse\n\t\t\tlocal abilityBooks = network:invoke(\"getCacheValueByNameTag\", \"abilityBooks\")\n\t\t\tcanEquipClass = abilityBooks[class] ~= nil\n\t\tend\n\n\t\tlocal classTree = {\n\t\t\t[\"hunter\"] = \"hunter\";\n\t\t\t[\"assassin\"] = \"hunter\";\n\t\t\t[\"trickster\"] = \"hunter\";\n\t\t\t[\"ranger\"] = \"hunter\";\n\t\t\t[\"mage\"] = \"mage\";\n\t\t\t[\"sorcerer\"] = \"mage\";\n\t\t\t[\"warlock\"] = \"mage\";\n\t\t\t[\"cleric\"] = \"mage\";\n\t\t\t[\"warrior\"] = \"warrior\";\n\t\t\t[\"paladin\"] = \"warrior\";\n\t\t\t[\"knight\"] = \"warrior\";\n\t\t\t[\"berserker\"] = \"warrior\";\n\t\t}\n\n\t\tlocal classRoot = classTree[class]\n\t\tclassRoot = classRoot or class\n\n\t\tfor i,obj in pairs(itemHoverFrame.main.mainContents.equippableClasses:GetChildren()) do\n\t\t\tif obj:IsA(\"ImageLabel\") then\n\t\t\t\tif classRoot and classRoot == obj.Name then\n\t\t\t\t\tobj.ImageTransparency = 0\n\t\t\t\t\tobj.Image = \"rbxgameasset://Images/emblem_\"..class\n\t\t\t\telse\n\t\t\t\t\tobj.ImageTransparency = 0.7\n\t\t\t\t\tobj.Image = \"rbxgameasset://Images/emblem_\"..obj.Name\n\t\t\t\tend\n\n\t\t\t\tif canEquipClass then\n\t\t\t\t\tobj.ImageColor3 = Color3.fromRGB(173, 173, 173)\n\t\t\t\telse\n\t\t\t\t\tobj.ImageColor3 = Color3.fromRGB(203, 69, 71)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\t-- clear previous stats\n\t\tfor i, obj in pairs(itemHoverFrame.stats.container:GetChildren()) do\n\t\t\tif not obj:IsA(\"UIListLayout\") then\n\t\t\t\tobj:Destroy()\n\t\t\tend\n\t\tend\n\n\n\t\tlocal numStats = 0\n\n\t\tlocal totalStats, statBonuses = getItemInfo(itemBaseData, inventorySlotData)\n\n\t\tlocal equippedStats\n\n\t\tlocal equipped = network:invoke(\"getCacheValueByNameTag\", \"equipment\")\n\n\t\t-- look for an equipped item to compare this to\n\t\tlocal correspendingEquipmentInventoryData\n\t\tfor i,equipment in pairs(equipped) do\n\t\t\tif itemBaseData.equipmentSlot and equipment.position and itemBaseData.equipmentSlot == equipment.position then\n\t\t\t\tcorrespendingEquipmentInventoryData = equipment\n\t\t\t\tbreak\n\t\t\tend\n\t\tend\n\t\tif newSource ~= \"equipment\" then\n\t\t\tif correspendingEquipmentInventoryData then\n\t\t\t\tlocal correspondingEquipmentBaseData = itemLookup[correspendingEquipmentInventoryData.id]\n\t\t\t\tif correspondingEquipmentBaseData then\n\t\t\t\t\tequippedStats = getItemInfo(correspondingEquipmentBaseData,correspendingEquipmentInventoryData)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tfor statName,statValue in pairs(totalStats) do\n\t\t\tif statValue ~= 0 then\n\n\t\t\t\tlocal statDisplayName = #statName <=3 and statName:upper() or statName:sub(1,1):upper() .. statName:sub(2)\n\n\t\t\t\tlocal statDisplayValue\n\t\t\t\tlocal statTextColor = Color3.new(160,160,160)\n\n\t\t\t\tlocal statPriority = 5\n\t\t\t\tlocal statValueSize = 24\n\n\t\t\t\tif statName == \"baseDamage\" then\n\t\t\t\t\tstatDisplayName = \"Weapon Attack\"\n\t\t\t\t\tstatPriority = 1\n\t\t\t\telseif statName == \"defense\" then\n\t\t\t\t\tstatPriority = 1\n\t\t\t\telseif statName == \"woodcutting\" then\n\t\t\t\t\tstatPriority = 2\n\t\t\t\t\tstatDisplayName = \"Woodcutting Power\"\n\t\t\t\telseif statName == \"mining\" then\n\t\t\t\t\tstatPriority = 2\n\t\t\t\t\tstatDisplayName = \"Mining Power\"\n\t\t\t\telseif statName == \"damageTakenMulti\" then\n\t\t\t\t\tstatDisplayName = \"Damage Taken\"\n\t\t\t\t\tstatPriority = 3\n\t\t\t\t\tstatDisplayValue = statValue * 100 .. \"%\" .. (statBonuses[statName] and statBonuses[statName] > 0 and \" (+\" .. statBonuses[statName] * 100 .. \"%)\" or \"\")\n\t\t\t\telseif statName == \"damageGivenMulti\" then\n\t\t\t\t\tstatDisplayName = \"Damage Given\"\n\t\t\t\t\tstatPriority = 3\n\t\t\t\t\tstatDisplayValue = statValue * 100 .. \"%\" .. (statBonuses[statName] and statBonuses[statName] > 0 and \" (+\" .. statBonuses[statName] * 100 .. \"%)\" or \"\")\n\t\t\t\telseif statName == \"magicalDamage\" then\n\t\t\t\t\tstatDisplayName = \"Magic Attack\"\n\t\t\t\t\tstatPriority = 3\n\t\t\t\telseif statName == \"rangedDamage\" then\n\t\t\t\t\tstatDisplayName = \"Ranged Attack\"\n\t\t\t\t\tstatPriority = 3\n\t\t\t\telseif statName == \"physicalDamage\" then\n\t\t\t\t\tstatDisplayName = \"Physical Attack\"\n\t\t\t\t\tstatPriority = 3\n\t\t\t\telseif statName == \"magicalDefense\" then\n\t\t\t\t\tstatDisplayName = \"Magic Defense\"\n\t\t\t\t\tstatPriority = 3\n\t\t\t\telseif statName == \"rangedDefense\" then\n\t\t\t\t\tstatDisplayName = \"Projectile Defense\"\n\t\t\t\t\tstatPriority = 3\n\t\t\t\telseif statName == \"physicalDefense\" then\n\t\t\t\t\tstatDisplayName = \"Physical Defense\"\n\t\t\t\t\tstatPriority = 3\n\t\t\t\telseif statName == \"maxMana\" then\n\t\t\t\t\tstatDisplayName = \"Max MP\"\n\t\t\t\t\tstatPriority = 4\n\t\t\t\telseif statName == \"maxHealth\" then\n\t\t\t\t\tstatDisplayName = \"Max HP\"\n\t\t\t\t\tstatPriority = 4\n\t\t\t\telseif statName == \"healthRegen\" then\n\t\t\t\t\tstatDisplayName = \"HP Recovery\"\n\t\t\t\t\tstatPriority = 5\n\t\t\t\t\tstatDisplayValue = statValue .. \"%\" .. (statBonuses[statName] and statBonuses[statName] > 0 and \" (+\" .. statBonuses[statName] .. \"%)\" or \"\")\n\t\t\t\telseif statName == \"manaRegen\" then\n\t\t\t\t\tstatDisplayName = \"MP Recovery\"\n\t\t\t\t\tstatPriority = 5\n\t\t\t\t\tstatDisplayValue = statValue .. \"%\" .. (statBonuses[statName] and statBonuses[statName] > 0 and \" (+\" .. statBonuses[statName] .. \"%)\" or \"\")\n\t\t\t\telseif statName == \"criticalStrikeChance\" then\n\t\t\t\t\tstatDisplayName = \"Critical Hits\"\n\t\t\t\t\tstatDisplayValue = statValue * 100 .. \"%\" .. (statBonuses[statName] and statBonuses[statName] > 0 and \" (+\" .. statBonuses[statName] * 100 .. \"%)\" or \"\")\n\t\t\t\t\tstatPriority = 5\n\t\t\t\telseif statName == \"blockChance\" then\n\t\t\t\t\tstatDisplayName = \"Block Chance\"\n\t\t\t\t\tstatDisplayValue = statValue * 100 .. \"%\" .. (statBonuses[statName] and statBonuses[statName] > 0 and \" (+\" .. statBonuses[statName] * 100 .. \"%)\" or \"\")\n\t\t\t\t\tstatPriority = 5\n\n\t\t\t\telseif statName == \"greed\" then\n\t\t\t\t\tstatDisplayName = \"Greed\"\n\t\t\t\t\tstatPriority = 6\n\t\t\t\t\tstatDisplayValue = statValue * 100 .. \"%\" .. (statBonuses[statName] and statBonuses[statName] > 0 and \" (+\" .. statBonuses[statName] * 100 .. \"%)\" or \"\")\n\t\t\t\telseif statName == \"wisdom\" then\n\t\t\t\t\tstatDisplayName = \"XP\"\n\t\t\t\t\tstatPriority = 6\n\t\t\t\t\tstatDisplayValue = statValue * 100 .. \"%\" .. (statBonuses[statName] and statBonuses[statName] > 0 and \" (+\" .. statBonuses[statName] * 100 .. \"%)\" or \"\")\n\t\t\t\telseif statName:find(\"_totalMultiplicative\") then\n\t\t\t\t\tstatDisplayName = statName:gsub(\"_totalMultiplicative\", \"\")\n\t\t\t\t\tstatDisplayName = statDisplayName:sub(1, 1):upper()..statDisplayName:sub(2)\n\t\t\t\t\tstatPriority = 7\n\t\t\t\t\tstatDisplayValue = (statValue * 100)..\"%\"\n\t\t\t\telseif statName == \"attackSpeed\" then\n\t\t\t\t\tstatDisplayName = \"Attack Speed\"\n\t\t\t\t\tstatPriority = 10\n\t\t\t\t\tstatValueSize = 19\n\t\t\t\t\tstatDisplayValue = ({\"Very slow\", \"Slow\", \"Normal\", \"Fast\", \"Very fast\"})[statValue] or statValue\n\t\t\t\tend\n\n\t\t\t\t-- if a name isn't right, just change it here\n\t\t\t\tlocal replacements = {\n\t\t\t\t\t[\"Walkspeed\"] = \"Movement Speed\",\n\t\t\t\t}\n\t\t\t\tstatDisplayName = replacements[statDisplayName] or statDisplayName\n\n\t\t\t\tif not statDisplayValue then\n\t\t\t\t\tstatDisplayValue = (statValue .. (statBonuses[statName] and\n\t\t\t\t\t\t((statBonuses[statName] > 0 and \" (+\" .. statBonuses[statName] .. \")\" ) or\n\t\t\t\t\t\t(statBonuses[statName] < 0 and \" (\" .. statBonuses[statName] .. \")\"))\n\t\t\t\t\t\tor \"\"))\n\t\t\t\tend\n\n\t\t\t\tlocal comparisonExtension\n\n\t\t\t\tif equippedStats and equippedStats[statName] and equippedStats[statName] ~= 0 then\n\n\t\t\t\t\tlocal difference = ((statValue - equippedStats[statName]) / equippedStats[statName])\n\t\t\t\t\tif statName == \"damageTakenMulti\" then\n\t\t\t\t\t\tdifference = -difference\n\t\t\t\t\tend\n\t\t\t\t\tif difference == difference then\n\t\t\t\t\t\tlocal comparisonText\n\t\t\t\t\t\tlocal comparisonColor\n\t\t\t\t\t\tif statValue == equippedStats[statName] or math.abs(difference) <= 0.01 then\n\t\t\t\t\t\t\t--comparisonText = \"=\"\n\t\t\t\t\t\telseif difference >= 0 then\n\t\t\t\t\t\t\tcomparisonText = \"↑\"\n\t\t\t\t\t\t\tcomparisonColor = Color3.fromRGB(150,255,150)\n\t\t\t\t\t\t\t--[[\n\t\t\t\t\t\t\tif difference >= 1 then\n\t\t\t\t\t\t\t\tcomparisonText = \"↑↑↑\"\n\t\t\t\t\t\t\telseif difference >= 0.25 then\n\t\t\t\t\t\t\t\tcomparisonText = \"↑↑\"\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t]]\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tcomparisonText = \"↓\"\n\t\t\t\t\t\t\tcomparisonColor = Color3.fromRGB(255,150,150)\n\t\t\t\t\t\t\t--[[\n\t\t\t\t\t\t\tif difference <= -0.5 then\n\t\t\t\t\t\t\t\tcomparisonText = \"↓↓↓\"\n\t\t\t\t\t\t\telseif difference <= -0.2 then\n\t\t\t\t\t\t\t\tcomparisonText = \"↓↓\"\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t]]\n\t\t\t\t\t\tend\n\t\t\t\t\t\tif comparisonText then\n\t\t\t\t\t\t\tcomparisonExtension = {text = comparisonText; textSize=20; font=Enum.Font.SourceSansBold; textColor3 = comparisonColor or Color3.new(0.6,0.6,0.6), autoLocalize = false}\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif statValue > 0 then\n\t\t\t\t\tstatDisplayValue = statDisplayValue\n\t\t\t\tend\n\t\t\t\tstatDisplayName = localization.translate(statDisplayName, itemHoverFrame.stats.container) .. \": \"\n\n\t\t\t\tlocal textContainer = uiCreator.createTextFragmentLabels(itemHoverFrame.stats.container, {\n\n\t\t\t\t\t--[[\n\t\t\t\t\t{text = (statValue >= 0 and \"+\" or \"\") .. statValue .. (statName:find(\"Percent\") and \"%\" or \"\")},\n\t\t\t\t\t{text = statName:gsub(\"Percent\", \"\"):gsub(\"Flat\", \"\")}\n\t\t\t\t\t]]\n\t\t\t\t\t{text = statDisplayName, textColor3 = statTextColor, textSize = 19, autoLocalize = false},\n\t\t\t\t\t{text = statDisplayValue, font = Enum.Font.SourceSansBold, textSize = statValueSize, textColor3 = statTextColor, autoLocalize = false},\n\t\t\t\t\tcomparisonExtension\n\t\t\t\t})\n\t\t\t\ttextContainer.LayoutOrder = statPriority\n\t\t\t\tnumStats = numStats + 1\n\t\t\t\tif statBonuses[statName] and statBonuses[statName] > 0 then\n\t\t\t\t\tif statName == \"baseDamage\" then\n\t\t\t\t\t\tlocal totalDamage = totalStats[\"baseDamage\"] or 0.1\n\t\t\t\t\t\tlocal modifierBaseDamage = statBonuses[\"baseDamage\"] or 0\n\t\t\t\t\t\tlocal difference = totalDamage / (totalDamage - modifierBaseDamage)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif inventorySlotData and inventorySlotData.upgrades then\n\t\t\tlocal maxUpgrades = (itemBaseData.maxUpgrades or 0) + (inventorySlotData.bonusUpgrades or 0)\n\n\t\t\tlocal upgradesLeft = maxUpgrades - inventorySlotData.upgrades\n\t\t\tlocal suffix = upgradesLeft == 1 and \"upgrade attempt remaining\" or \"upgrade attempts remaining\"\n\n\t\t\tlocal label = uiCreator.createTextFragmentLabels(itemHoverFrame.stats.container, {\n\t\t\t\t{text = (tostring(upgradesLeft)..\" \"..suffix); font = Enum.Font.SourceSans; textColor3 = Color3.new(1,1,1); textTransparency = 0.5; autoLocalize = true}\n\t\t\t})\n\t\t\tlabel.LayoutOrder = 7\n\t\t\tnumStats = numStats + 1\n\n\t\tend\n\t\t--[[\n\t\trootFrame.itemType.Visible = true\n\n\t\tfor i,itemTypeLabel in pairs(rootFrame.itemType:GetChildren()) do\n\t\t\tif itemBaseData.category and itemBaseData.category == itemTypeLabel.Name then\n\t\t\t\titemTypeLabel.Visible = true\n\t\t\telse\n\t\t\t\titemTypeLabel.Visible = false\n\t\t\tend\n\t\tend\n\t\t]]\n\n\n\t\tlocal titleColor\n\t\tif inventorySlotData then\n\t\t\ttitleColor = getTitleColorForInventorySlotData(inventorySlotData)\n\t\tend\n\n\t\ttitleColor = titleColor or itemBaseData.nameColor or Color3.new(1,1,1)\n\n\n\t\titemHoverFrame.header.itemName.TextColor3 = titleColor\n\t\titemHoverFrame.header.itemName.cuteDecor.ImageColor3 = titleColor\n\n\t\tlocal itemType = itemBaseData.itemType\n\t\titemHoverFrame.header.itemName.cuteDecor.Image = \"rbxgameasset://Images/category_\"..itemType\n\n\t\tlocal maxUpgrades = itemBaseData.maxUpgrades or 0\n\t\t-- stars next to item name (defunct)\n\t\t--[[\n\t\tfor i,star in pairs(itemHoverFrame.header.itemName.stars:GetChildren()) do\n\t\t\tif star:IsA(\"ImageLabel\") then\n\t\t\t\tlocal n = tonumber(star.Name)\n\t\t\t\tstar.ImageColor3 = Color3.fromRGB(255, 255, 255)\n\t\t\t\tstar.ImageTransparency = 0.9\n\t\t\t\tstar.LayoutOrder = 12\n\t\t\t\tstar.Visible = n <= maxUpgrades\n\t\t\t\tstar.Image = \"rbxassetid://3763830087\"\n\t\t\tend\n\t\tend\n\t\tlocal n = 0\n\t\tif inventorySlotData.enchantments then\n\t\t\tfor i, enchantmentData in pairs(inventorySlotData.enchantments) do\n\t\t\t\tlocal enchantmentBaseData = itemLookup[enchantmentData.id].enchantments[enchantmentData.state]\n\t\t\t\tlocal star = itemHoverFrame.header.itemName.stars:FindFirstChild(tostring(i))\n\t\t\t\tstar.ImageTransparency = 0\n\t\t\t\tlocal tier = enchantmentBaseData.tier or 0\n--\t\t\t\tstar.ImageColor3 = module.tierColors[tier]\n\t\t\t\tstar.ImageColor3 = titleColor\n\t\t\t\tstar.LayoutOrder = 10 - tier\n\t\t\t\tstar.Visible = true\n\t\t\t\tn = n + 1\n\t\t\tend\n\t\tend\n\t\tlocal fails = math.clamp((inventorySlotData.upgrades or 0) - (inventorySlotData.successfulUpgrades or 0), 0, maxUpgrades)\n\t\tfor i=1,fails do\n\t\t\tlocal star = itemHoverFrame.header.itemName.stars:FindFirstChild(tostring(n+i))\n\t\t\tif star then\n\t\t\t\tstar.LayoutOrder = 11\n\t\t\t\tstar.ImageTransparency = 0\n\t\t\t\tstar.ImageColor3 = module.tierColors[-1]\n\t\t\t\tstar.Image = \"rbxassetid://3799648173\"\n\t\t\tend\n\t\tend\n\t\t]]\n\n\t\titemHoverFrame.main.thumbnailBG.frame.ImageColor3 = titleColor\n\t\titemHoverFrame.main.thumbnailBG.shine.ImageColor3 = titleColor\n\n\n\t\titemHoverFrame.main.thumbnail.BorderColor3 = titleColor\n\n\n\t\t--inventorySlotData and inventorySlotData.upgrades\n\n\n\t\titemHoverFrame.main.thumbnail.ImageColor3 = Color3.new(1,1,1)\n\n\t\tlocal customTag = (inventorySlotData and inventorySlotData.customTag) or (itemBaseData and itemBaseData.customTag)\n\n\t\tif customTag then\n\n\t\t\tlocal label = uiCreator.createTextFragmentLabels(itemHoverFrame.stats.container,customTag)\n\t\t\tlabel.LayoutOrder = 12\n\t\t\tnumStats = numStats + 1\n\t\tend\n\n\t\tlocal perks = {}\n\t\tif itemBaseData.perkData then\n\t\t\ttable.insert(perks, itemBaseData.perkData)\n\t\tend\n\t\tif inventorySlotData.perkData then\n\t\t\ttable.insert(perks, inventorySlotData.perkData)\n\t\tend\n\t\tfor i,perkData in pairs(perks) do\n\t\t\tlocal label = uiCreator.createTextFragmentLabels(itemHoverFrame.stats.container, {\n\t\t\t\t{text = itemBaseData.perkData.title; font = Enum.Font.SourceSansBold; textColor3 = module.tierColors[perkData.tier or 1]; textTransparency = 0; autoLocalize = true}\n\t\t\t})\n\t\t\tlabel.LayoutOrder = 9\n\t\t\tnumStats = numStats + 1\n\t\tend\n\n\t\tif inventorySlotData and inventorySlotData.dye then\n\t\t\titemHoverFrame.main.thumbnail.ImageColor3 = Color3.fromRGB(inventorySlotData.dye.r, inventorySlotData.dye.g, inventorySlotData.dye.b)\n\t\t\tlocal label = uiCreator.createTextFragmentLabels(itemHoverFrame.stats.container, {\n\t\t\t\t{text = \"Dyed\"; font = Enum.Font.SourceSansBold; textColor3 = Color3.fromRGB(inventorySlotData.dye.r, inventorySlotData.dye.g, inventorySlotData.dye.b); textTransparency = 0}\n\t\t\t})\n\t\t\tlabel.LayoutOrder = 10\n\t\t\tnumStats = numStats + 1\n\t\tend\n\n\n\t\tif inventorySlotData and inventorySlotData.customStory then\n\t\t\tlocal label = uiCreator.createTextFragmentLabels(itemHoverFrame.stats.container, {\n\t\t\t\t{text = \"\"; font = Enum.Font.SourceSans; textTransparency = 1; autoLocalize = false;}\n\t\t\t})\n\t\t\tlabel.LayoutOrder = 11\n\t\t\tlocal label = uiCreator.createTextFragmentLabels(itemHoverFrame.stats.container, {\n\t\t\t\t{text = \"\\\"\"..inventorySlotData.customStory..\"\\\"\"; font = Enum.Font.Antique; textColor3 = Color3.new(1, 1, 1); textTransparency = 0.2; autoLocalize = false;}\n\t\t\t})\n\t\t\tlabel.LayoutOrder = 11\n\t\t\tnumStats = numStats + 2\n\t\tend\n\n\t\tlocal headerYSize = itemNameTextSize.Y * itemNameLines\n\n\t\titemHoverFrame.header.Size \t\t\t= UDim2.new(1, 0, 0, headerYSize)\n\t\titemHoverFrame.header.itemName.Size = UDim2.new(0, itemNameTextSize.X, 0, itemNameTextSize.Y * itemNameLines)\n--\t\titemHoverFrame.header.itemName.stars.Visible = true\n\t\titemHoverFrame.stats.container.Size = UDim2.new(1, 0, 0, numStats * 20)\n\t\titemHoverFrame.stats.Size \t\t\t= UDim2.new(1, 0, 0, numStats * 20)\n\n\t\titemHoverFrame.stats.Visible = numStats > 0\n\n\t\titemHoverFrame.main.mainContents.itemDescription.Size = UDim2.new(1, 0, 0, itemDescriptionTextSize.Y * itemDescriptionLines + 10)\n\n\t\t--local y = headerYSize + itemDescriptionTextSize.Y * itemDescriptionLines + 20 + numStats * 18 + 30 + 10\n\n\n\n--\t\tlocal y = headerYSize + mainContentsSize + numStats * 18 + (numStats > 0 and 20 or 0) + push\n\n\t\tlocal y = 10\n\t\tfor i,child in pairs(itemHoverFrame:GetChildren()) do\n\t\t\tif child:IsA(\"GuiObject\") and child.Visible then\n\t\t\t\ty = y + child.AbsoluteSize.Y + itemHoverFrame.UIListLayout.Padding.Offset\n\t\t\tend\n\t\tend\n\n\n\t\tlocal openSize = UDim2.new(0, 320 + 4, 0, y + 4)\n\n\t\trootFrame.Size = openSize\n\t\trootFrame.contents.Visible = true\n\n\t\tif module.source ~= \"pickup\" and module.source ~= \"none\" then\n\t\t\trootFrame.UIScale.Scale = Modules.input.menuScale\n\t\t\treposition()\n\t\t\trootFrame.Visible = true\n\t\tend\n\n\t\t--rootFrame.Size = openSize\n\n\t\t-- returns the\n\t\t--return\n\t\t--\ttweenService:Create(itemHoverFrame, BASIC_TWEEN_INFO, {Size = openSize}),\n\t\t--\ttweenService:Create(itemHoverFrame, BASIC_TWEEN_INFO, {Size = UDim2.new(0, 195, 0, headerYSize + itemDescriptionTextSize.Y * itemDescriptionLines + 15 + (18 + 5) + 5)})\n\tend\n\n\tnetwork:create(\"populateItemHoverFrame\",\"BindableFunction\",\"OnInvoke\",populateItemHoverFrame)\n\n\tlocal function getItemBaseDataFromItemsPart(itemPart)\n\t\tlocal itemDataModule = itemsDataFolder:FindFirstChild(itemPart.Name)\n\t\tif itemDataModule then\n\t\t\tlocal itemBaseData \t\t\t= require(itemDataModule)\n\t\t\titemBaseData.physItemName \t= itemPart.Name\n\n\t\t\treturn itemBaseData\n\t\tend\n\tend\n\n\tlocal function getClosestItem()\n\t\tif player.Character and player.Character.PrimaryPart then\n\t\t\tlocal closestItem, closestItemDistance = nil, ITEM_ACQUISITION_RANGE\n\t\t\tfor i, item in pairs(itemsFolder:GetChildren()) do\n\t\t\t\tlocal distanceAway = utilities.magnitude(player.Character.PrimaryPart.Position - item.Position)\n\t\t\t\tif (not closestItem and distanceAway <= closestItemDistance) or (closestItem and distanceAway < closestItemDistance) then\n\t\t\t\t\tif utilities.playerCanPickUpItem(player, item) then\n\t\t\t\t\t\tclosestItem \t\t= item\n\t\t\t\t\t\tclosestItemDistance = distanceAway\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\treturn closestItem, closestItemDistance\n\t\tend\n\tend\n\n\n\t--game:GetService(\"RunService\"):BindToRenderStep(\"itemHoverRepos\",Enum.RenderPriority.Camera - 5,reposition)\n\n\n\n\tlocal function onInputChanged(inputObject)\n\t\tif inputObject.UserInputType == Enum.UserInputType.MouseMovement then\n\t\t\tlocal hitPart, hitPosition, distanceAway = raycastFromScreenPositionForItems(inputObject.Position.X, inputObject.Position.Y)\n\n\t\t\tif hitPart then\n\n\t\t\t\tlocal item = (hitPart.Parent == itemsFolder and hitPart) or (hitPart.Parent.Parent == itemsFolder and hitPart.Parent) or (hitPart.Parent.Parent.Parent == itemsFolder and hitPart.Parent.Parent)\n\n\t\t\t\tlocal itemBaseData = getItemBaseDataFromItemsPart(item)\n\n\t\t\t\tlocal isOwner = utilities.playerCanPickUpItem(player, item)\n\t\t\t\tlocal additionalInfo = {}\n\n\t\t\t\tif not isOwner then\n\t\t\t\t\tadditionalInfo.notOwned = true\n\t\t\t\tend\n\n\t\t\t\tif itemBaseData then\n\t\t\t\t\tif not currentItemHoverData or currentItemHoverData.item ~= item then\n\t\t\t\t\t\tif item:FindFirstChild(\"metadata\") and item.metadata.Value ~= \"\" then\n\t\t\t\t\t\t\tlocal regularSizeAnimation, pickupSizeAnimation = populateItemHoverFrame(itemBaseData, \"pickup\", game:GetService(\"HttpService\"):JSONDecode(item.metadata.Value), additionalInfo)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tlocal regularSizeAnimation, pickupSizeAnimation = populateItemHoverFrame(itemBaseData, \"pickup\", nil, additionalInfo)\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\t--[[\n\t\t\t\t\t\tcurrentItemHoverData = {\n\t\t\t\t\t\t\titem \t\t\t\t\t= hitPart;\n\t\t\t\t\t\t\titemBaseData \t\t\t= itemBaseData;\n\t\t\t\t\t\t\tregularSizeAnimation \t= regularSizeAnimation;\n\t\t\t\t\t\t\tpickupSizeAnimation \t= pickupSizeAnimation;\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\t]]\n\t\t\t\t\tend\n\n\t\t\t\t\t--if distanceAway <= ITEM_ACQUISITION_RANGE then\n\t\t\t\t\t--\tcurrentItemHoverData.pickupSizeAnimation:Play()\n\t\t\t\t\t--else\n\t\t\t\t\t--\tcurrentItemHoverData.regularSizeAnimation:Play()\n\t\t\t\t\t--end\n\n\t\t\t\t\tif not rootFrame.Visible then\n\t\t\t\t\t\tmodule.source = \"pickup\"\n\t\t\t\t\t\trootFrame.UIScale.Scale = Modules.input.menuScale\n\t\t\t\t\t\treposition()\n\t\t\t\t\t\trootFrame.Visible = true\n\n\t\t\t\t\tend\n\n\t\t\t\t\t--[[\n\n\t\t\t\t\tlocal targetPosition = UDim2.new(0, inputObject.Position.X + 20, 0, inputObject.Position.Y + 10)\n\n\t\t\t\t\t-- clamp Y axis to bottom side of the screen\n\t\t\t\t\tif targetPosition.Y.Offset + rootFrame.AbsoluteSize.Y > gameUI.AbsoluteSize.Y then\n\t\t\t\t\t\ttargetPosition = UDim2.new(0, targetPosition.X.Offset, 0, gameUI.AbsoluteSize.Y - rootFrame.AbsoluteSize.Y)\n\t\t\t\t\tend\n\n\t\t\t\t\t-- clamp X axis to right side of the screen\n\t\t\t\t\tif targetPosition.X.Offset + rootFrame.AbsoluteSize.X > gameUI.AbsoluteSize.X then\n\t\t\t\t\t\ttargetPosition = UDim2.new(0, gameUI.AbsoluteSize.X - rootFrame.AbsoluteSize.X, 0, targetPosition.Y.Offset)\n\t\t\t\t\tend\n\n\t\t\t\t\trootFrame.Position = targetPosition\n\t\t\t\t\t]]\n\t\t\t\telse\n\t\t\t\t\tif rootFrame.Visible and module.source == \"pickup\" then\n\t\t\t\t\t\trootFrame.Visible = false\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tcurrentItemHoverData = nil\n\n\t\t\t\tif rootFrame.Visible and module.source == \"pickup\" then\n\t\t\t\t\trootFrame.Visible = false\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal itemBackup\n\n\tlocal function outQuad(t, d)\n\t\tlocal b = 0\n\t\tlocal c = 1\n\t\tt = t / d\n\t\treturn -c * t * (t - 2) + b\n\tend\n\n\tlocal RunService = game:GetService(\"RunService\")\n\n\tlocal function attract(representation)\n\n\t\tlocal startpos = representation.CFrame\n\t\tlocal start = tick()\n\t\tlocal duration = 0.5\n\t\twhile tick() - start <= duration do\n\t\t\tRunService.Heartbeat:wait()\n\t\t\tif representation then\n\t\t\t\tif game.Players.LocalPlayer.Character and game.Players.LocalPlayer.Character.PrimaryPart then\n\t\t\t\t\trepresentation.CFrame = startpos:lerp(game.Players.LocalPlayer.Character.PrimaryPart.CFrame, outQuad(tick()-start,duration))\n\t\t\t\telse\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\n\tlocal function onPickUpItemRequestFromServer(metadata, success, value, representationOverride, resultMessage)\n\t\t-- bere edit: show display name\n\t\tlocal realItem = itemLookup[metadata.id]\n\n\n\t\tif success then\n\t\t\tif realItem.id == 1 then\n\t\t\t\t--[[\n\t\t\t\tlocal prompt = uiCreator.createInteractionPrompt(nil,\n\t\t\t\t\t{text = \"Obtained\"; textColor3 = Color3.fromRGB(120,120,120)},\n\t\t\t\t\t{text = (value or 0)..\" gold\"; textColor3 = Color3.fromRGB(255, 255, 0)}\n\t\t\t\t)\n\n\t\t\t\tprompt:setBackgroundColor3(Color3.fromRGB(190, 190, 190))\n\t\t\t\tprompt:setExpireTime(1.5)\n\t\t\t\t]]\n\t\t\t\tuiCreator.showCurrency(value)\n\t\t\telseif realItem.autoConsume and resultMessage then\n\t\t\t\tlocal prompt = uiCreator.createInteractionPrompt(nil,\n\t\t\t\t\t{text = resultMessage; textColor3 = Color3.fromRGB(70,70,70)}\n\t\t\t\t)\n\n\t\t\t\tprompt:setBackgroundColor3(Color3.fromRGB(190, 190, 190))\n\t\t\t\tprompt:setExpireTime(5)\n\n\t\t\t\tif realItem.useSound then\n\t\t\t\t\tutilities.playSound(realItem.useSound)\n\t\t\t\tend\n\t\t\telse\n\n\t\t\t\tuiCreator.showItemPickup(realItem, value, metadata)\n\t\t\t\t-- auto-bind first 2 consumables\n\n\t\t\t\t-- edit: dont\n\t\t\t\t--[[\n\t\t\t\tif realItem.category == \"consumable\" and realItem.activationEffect then\n\t\t\t\t\tlocal hotbarSlotPairing = network:invoke(\"getHotbarSlotPairing\")\n\t\t\t\t\tlocal itemAlreadyExists\n\t\t\t\t\tfor i, hotbarSlot in pairs(hotbarSlotPairing) do\n\t\t\t\t\t\tlocal hotbarButton = hotbarSlot.button\n\t\t\t\t\t\tlocal hotbarData = hotbarSlot.data\n\t\t\t\t\t\tif hotbarData and hotbarData.id and hotbarData.id == realItem.id then\n\t\t\t\t\t\t\titemAlreadyExists = true\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\tif not itemAlreadyExists then\n\t\t\t\t\t\tfor i, hotbarSlot in pairs(hotbarSlotPairing) do\n\t\t\t\t\t\t\tlocal hotbarButton = hotbarSlot.button\n\t\t\t\t\t\t\tlocal hotbarData = hotbarSlot.data\n\t\t\t\t\t\t\tif hotbarData == nil or hotbarData.id == nil or hotbarData.id <= 0 then\n\n\t\t\t\t\t\t\t\tlocal num = string.gsub(hotbarButton.Name,\"[^.0-9]+\",\"\")\n\t\t\t\t\t\t\t\tnum = tonumber(num)\n\n\t\t\t\t\t\t\t\tif num == 1 or num == 2 then -- 1 and 2 reserved for consumables\n\n\t\t\t\t\t\t\t\t\tlocal bindsuccess = network:invokeServer(\"registerHotbarSlotData\", mapping.dataType.item, realItem.id, tonumber(num))\n\t\t\t\t\t\t\t\t\tif bindsuccess then\n\t\t\t\t\t\t\t\t\t\tif game.ReplicatedStorage.sounds:FindFirstChild(\"idolPickup\") then\n\t\t\t\t\t\t\t\t\t\t\tutilities.playSound(\"idolPickup\")\n\t\t\t\t\t\t\t\t\t\t\t--game.ReplicatedStorage.sounds.idolPickup:Play()\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\tfor i=1,4 do\n\t\t\t\t\t\t\t\t\t\t\tlocal flare = hotbarButton.flare:Clone()\n\t\t\t\t\t\t\t\t\t\t\tflare.Name = \"flareCopy\"\n\t\t\t\t\t\t\t\t\t\t\tflare.Parent = hotbarButton\n\t\t\t\t\t\t\t\t\t\t\tflare.Visible = true\n\t\t\t\t\t\t\t\t\t\t\tflare.Size = UDim2.new(1,4,1,4)\n\t\t\t\t\t\t\t\t\t\t\tflare.Position = UDim2.new(0.5,0,1,2)\n\t\t\t\t\t\t\t\t\t\t\tflare.AnchorPoint = Vector2.new(0.5,1)\n\t\t\t\t\t\t\t\t\t\t\tlocal y = (180 - 40*i)\n\t\t\t\t\t\t\t\t\t\t\tlocal x = (14 - 2*i)\n\t\t\t\t\t\t\t\t\t\t\tlocal EndPosition = UDim2.new(0.5,0,1,2)\n\t\t\t\t\t\t\t\t\t\t\tlocal EndSize = UDim2.new(1,x,1,y)\n\t\t\t\t\t\t\t\t\t\t\ttween(flare,{\"Position\",\"Size\",\"ImageTransparency\"},{EndPosition, EndSize, 1},0.5*i)\n\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\t]]\n\n\t\t\tend\n\n\n\n\n\t\t\tlocal Character = game.Players.LocalPlayer.Character\n\t\t\tif itemBackup and Character and Character.PrimaryPart and not representationOverride then\n\t\t\t\tlocal representation = itemBackup:Clone()\n\n\t\t\t\titemBackup:Destroy()\n\t\t\t\titemBackup = nil\n\n\t\t\t\trepresentation.Parent = workspace.CurrentCamera\n\t\t\t\trepresentation.Anchored = true\n\t\t\t\trepresentation.CanCollide = false\n\n\t\t\t\tlocal sound\n\t\t\t\tif representation.Name == \"monster idol\" then\n\t\t\t\t\tsound = utilities.playSound(\"idolPickup\")\n\t\t\t\telseif representation:FindFirstChild(\"Legendary\") and representation.Legendary.Enabled then\n\t\t\t\t\tsound = utilities.playSound(\"legendaryItemPickup\")\n\t\t\t\telseif representation:FindFirstChild(\"Rare\") and representation.Rare.Enabled then\n\t\t\t\t\tsound = utilities.playSound(\"rareItemPickup\")\n\t\t\t\telse\n\t\t\t\t\tsound = utilities.playSound(\"pickup\")\n\t\t\t\tend\n\n\n\t\t\t\tif representation:IsA(\"BasePart\") then\n\t\t\t\t\trepresentation.CanCollide = false\n\t\t\t\tend\n\t\t\t\tfor _, descend in pairs(representation:GetDescendants()) do\n\t\t\t\t\tif descend:IsA(\"BasePart\") then\n\t\t\t\t\t\tdescend.CanCollide = false\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tattract(representation)\n\n\t\t\t\tif sound then\n\t\t\t\t\tpcall(function() -- ahhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\n\t\t\t\t\t\tsound.Parent = workspace.CurrentCamera\n\t\t\t\t\t\tgame.Debris:AddItem(sound, 2)\n\t\t\t\t\tend)\n\t\t\t\tend\n\t\t\t\trepresentation:Destroy()\n\n\t\t\tend\n\t\telse\n\t\t\tif resultMessage == \"full-inventory\" then\n\t\t\t\tnetwork:fire(\"alert\", {text = \"Your inventory is full for this item's category.\"})\n\t\t\t\twarn(\"inventory full?\")\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function itemsRecieved(items, money)\n\t\tif money and money > 0 then\n\t\t\ttable.insert(items, {id = 1; value = money})\n\t\tend\n\t\tfor i,item in pairs(items) do\n\t\t\tlocal realItem = itemLookup[item.id or item.id]\n\t\t\tif realItem then\n\t\t\t\tonPickUpItemRequestFromServer(item, true, (item.value or item.stacks), true)\n\t\t\t\tif (realItem.rarity and realItem.rarity == \"Legendary\") then\n\t\t\t\t\tutilities.playSound(\"legendaryItemPickup\")\n\t\t\t\telseif (realItem.rarity and realItem.rarity == \"Rare\") or (realItem.category and realItem.category == \"equipment\") then\n\t\t\t\t\tutilities.playSound(\"rareItemPickup\")\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tnetwork:create(\"displayRewards\", \"BindableFunction\", \"OnInvoke\", itemsRecieved)\n\n\n\n\tnetwork:connect(\"itemsRecieved\",\"OnClientEvent\",itemsRecieved)\n\n\tlocal itemPickupData = {}\n\n\tlocal function processPickupKeyDown(id)\n\t\tif id == \"pickup\" and pickupInteractionPromptTable.item then\n\t\t\titemBackup = pickupInteractionPromptTable.item\n\t\t\tlocal success, returnValue = network:invokeServer(\"pickUpItemRequest\", pickupInteractionPromptTable.item)\n\n\t\t\tif not success then\n\t\t\t\tnetwork:fire(\"alert\", {id = pickupInteractionPromptTable.item; text = \"Pick-up failed: \" .. returnValue})\n\t\t\tend\n\n\t\t\tpickupInteractionPromptTable.item = nil\n\t\tend\n\tend\n\n\tlocal keyDown = false\n\n\tlocal inputObject\n\n\tlocal function inputGained(newInputObject)\n\t\tinputObject = newInputObject\n\t\tif inputObject and inputObject.UserInputState == Enum.UserInputState.Begin then\n\t\t\tkeyDown = true\n\t\t\tprocessPickupKeyDown(\"pickup\")\n\t\telse\n\t\t\tkeyDown = false\n\t\tend\n\tend\n\n\tmodule.pickupInputGained = inputGained\n\n\tnetwork:invoke(\"addInputAction\", \"pick up\", inputGained, \"F\")\n\n\tlocal function main()\n\t\tuserInputService.InputChanged:connect(onInputChanged)\n\t\t--network:connect(\"pickUpItemRequest\", \"OnClientEvent\", onPickUpItemRequestFromServer)--onPickUpItemRequestFromServer)\n\t\tnetwork:connect(\"notifyPlayerPickUpItem\", \"OnClientEvent\", onPickUpItemRequestFromServer)\n\n\t\t-- todo: make it so this isn't always running somehow?\n\t\twhile wait(1 / 15) do\n\t\t\tif inputObject and inputObject.UserInputState == Enum.UserInputState.Begin then\n\t\t\t\tkeyDown = true\n\t\t\telse\n\t\t\t\tkeyDown = false\n\t\t\tend\n\n\t\t\tlocal actionObject = Modules.input.actions[\"pick up\"]\n\t\t\tlocal keybind = actionObject and actionObject.bindedTo or \"F\"\n\t\t\tmodule.closestItem = getClosestItem()\n\t\t\tif not currentItemHoverData then\n\t\t\t\tif module.closestItem then\n\t\t\t\t\tif pickupInteractionPromptTable.item ~= module.closestItem then\n\t\t\t\t\t\tif not pickupInteractionPromptTable.itemBaseData or pickupInteractionPromptTable.itemBaseData.physItemName ~= module.closestItem.Name then\n\t\t\t\t\t\t\tlocal itemBaseData = getItemBaseDataFromItemsPart(module.closestItem)\n\t\t\t\t\t\t\tif itemBaseData then\n\t\t\t\t\t\t\t\tpickupInteractionPromptTable.itemBaseData = itemBaseData\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tpickupInteractionPromptTable.item = module.closestItem\n\n\t\t\t\t\t\tpickupInteractionPromptTable.prompt:updateTextFragments(false,\n\t\t\t\t\t\t\t{text = \"Pick up\"},\n\t\t\t\t\t\t\t{text = pickupInteractionPromptTable.itemBaseData and pickupInteractionPromptTable.itemBaseData.name or module.closestItem.Name; textColor3 = Color3.fromRGB(143, 120, 255)}\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\tif keyDown and pickupInteractionPromptTable.item then\n\t\t\t\t\t\t\tprocessPickupKeyDown(\"pickup\")\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tif not pickupInteractionPromptTable.prompt.isHiding then\n\t\t\t\t\t\tpickupInteractionPromptTable.prompt:hide()\n\n\t\t\t\t\t\tpickupInteractionPromptTable.item \t\t\t= nil\n\t\t\t\t\t\tpickupInteractionPromptTable.itemBaseData \t= nil\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tif pickupInteractionPromptTable.item ~= currentItemHoverData.item then\n\t\t\t\t\tpickupInteractionPromptTable.item \t\t\t= currentItemHoverData.item\n\t\t\t\t\tpickupInteractionPromptTable.itemBaseData \t= currentItemHoverData.itemBaseData\n\n\t\t\t\t\tpickupInteractionPromptTable.prompt:updateTextFragments(false,\n\t\t\t\t\t\t{text = \"Pick up\"},\n\t\t\t\t\t\t{text = pickupInteractionPromptTable.itemBaseData and pickupInteractionPromptTable.itemBaseData.name or module.closestItem.Name; textColor3 = Color3.fromRGB(143, 120, 255)}\n\t\t\t\t\t)\n\n\n\t\t\t\t\tif keyDown and pickupInteractionPromptTable.item then\n\t\t\t\t\t\tprocessPickupKeyDown(\"pickup\")\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tspawn(main)\n\nend\n\n\n--\n\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/leaderboard.lua",
    "content": "-- leaderboard module by berezaa\n\nlocal module = {}\nlocal player = game.Players.localPlayer\nlocal playerGui = player.PlayerGui\nlocal ui = playerGui.gameUI.leaderboard\n\nfunction module.init(Modules)\n\n\tlocal network = Modules.network\n\tlocal httpService = game:GetService(\"HttpService\")\n\n\t-- server browser hookup\n\tspawn(function()\n\t\tlocal serversDataValue = game.ReplicatedStorage:WaitForChild(\"serversData\")\n\t\tlocal function update()\n\t\t\tlocal servers = serversDataValue.Value ~= \"\" and httpService:JSONDecode(serversDataValue.Value)\n\t\t\tlocal serverCount = 0\n\t\t\tif servers then\n\t\t\t\tfor serverId, serverData in pairs(servers) do\n\t\t\t\t\tif serverId ~= game.JobId then\n\t\t\t\t\t\tserverCount = serverCount + 1\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\t\tui.top.leave.Visible = servers and serverCount > 0\n\t\tend\n\t\tupdate()\n\t\tserversDataValue.Changed:connect(update)\n\t\t-- can't direct ref .open bc of init race condition\n\t\tui.top.leave.Activated:Connect(function()\n\t\t\tModules.serverBrowser.open()\n\t\tend)\n\tend)\n\n\tlocal function repareRender(viewport, player, data)\n\n\t\tif viewport:FindFirstChild(\"entity\") then\n\t\t\tviewport.entity:Destroy()\n\t\tend\n\n\t\tif viewport:FindFirstChild(\"entity2\") then\n\t\t\tviewport.entity2:Destroy()\n\t\tend\n\n\t\tlocal camera = viewport.CurrentCamera\n\t\tif camera == nil then\n\t\t\tcamera = Instance.new(\"Camera\")\n\t\t\tcamera.Parent = viewport\n\t\t\tviewport.CurrentCamera = camera\n\t\tend\n\n\t\tlocal client = player\n\t\tlocal character = player.Character\n\t\tlocal mask = viewport.characterMask\n\n\t\tlocal characterAppearanceData = {}\n\t\tcharacterAppearanceData.equipment \t= data.equipment or {}\n\t\tcharacterAppearanceData.accessories = data.accessories or {}\n\n\t\tlocal characterRender = network:invoke(\"createRenderCharacterContainerFromCharacterAppearanceData\",mask, characterAppearanceData or {}, client)\n\t\tcharacterRender.Parent = workspace.CurrentCamera\n\n\t\tlocal animationController = characterRender.entity:WaitForChild(\"AnimationController\")\n\t\t--[[\n\t\tlocal track = animationController:LoadAnimation(mask.idle)\n\t\ttrack.Looped = true\n\t\ttrack.Priority = Enum.AnimationPriority.Idle\n\t\ttrack:Play()\n\t\t]]\n\n\n\t\tlocal currentEquipment = network:invoke(\"getCurrentlyEquippedForRenderCharacter\", characterRender.entity)\n\n\t\t--[[\n\t\tlocal weaponType do\n\t\t\tif currentEquipment[\"1\"] then\n\t\t\t\tweaponType = currentEquipment[\"1\"].baseData.equipmentType\n\t\t\tend\n\t\tend\n\t\t]]\n\n\t\tlocal weaponType\n\n\t\tlocal track = network:invoke(\"getMovementAnimationForCharacter\", animationController, \"idling\", weaponType, nil)\n\n\t\tif track then\n\t\t\tif typeof(track) == \"Instance\" then\n\t\t\t\ttrack:Play()\n\t\t\telseif typeof(track) == \"table\" then\n\t\t\t\tfor ii, obj in pairs(track) do\n\t\t\t\t\tobj:Play()\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tspawn(function()\n\t\t\t\twhile true do\n\t\t\t\t\twait()\n\n\t\t\t\t\tif typeof(track) == \"Instance\" then\n\t\t\t\t\t\tif track.Length > 0 then\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tend\n\t\t\t\t\telseif typeof(track) == \"table\" then\n\t\t\t\t\t\tlocal isGood = true\n\t\t\t\t\t\tfor ii, obj in pairs(track) do\n\t\t\t\t\t\t\tif track.Length == 0 then\n\t\t\t\t\t\t\t\tisGood = false\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif isGood then\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif characterRender then\n\n\t\t\t\t\tif viewport:FindFirstChild(\"entity\") then\n\t\t\t\t\t\tviewport.entity:Destroy()\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal entity \t= characterRender.entity\n\t\t\t\t\tentity.Parent \t= viewport\n\n\t\t\t\t\tcharacterRender:Destroy()\n\t\t\t\t\tlocal focus \t= CFrame.new(entity.PrimaryPart.Position + entity.PrimaryPart.CFrame.lookVector * 6.3, entity.PrimaryPart.Position) * CFrame.new(3,0,0)\n\t\t\t\t\tcamera.CFrame \t= CFrame.new(focus.p + Vector3.new(0,1.5,0), entity.PrimaryPart.Position + Vector3.new(0,0.5,0))\n\t\t\t\tend\n\t\t\tend)\n\t\telse\n\t\t\tlocal track = animationController:LoadAnimation(mask.idle)\n\t\t\ttrack.Looped = true\n\t\t\ttrack.Priority = Enum.AnimationPriority.Idle\n\t\t\ttrack:Play()\n\t\tend\n\tend\n\n\n\tlocal playerCardTemplate = ui.content.sample:Clone()\n\tui.content.sample:Destroy()\n\n\tlocal selectedPlayerCard\n\n\tlocal function update()\n\t\t-- update player cards\n\t\tlocal playerCardCount = 0\n\n\t\tfor i, player in pairs(game.Players:GetPlayers()) do\n\t\t\tif player:FindFirstChild(\"DataLoaded\") then\n\t\t\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart:FindFirstChild(\"appearance\") then\n\t\t\t\t\tlocal appearance = player.Character.PrimaryPart.appearance.Value\n\t\t\t\t\tlocal data = game:GetService(\"HttpService\"):JSONDecode(appearance)\n\t\t\t\t\tif data then\n\n\t\t\t\t\t\tlocal playerCard = ui.content:FindFirstChild(player.Name)\n\t\t\t\t\t\tif playerCard == nil then\n\t\t\t\t\t\t\tplayerCard = playerCardTemplate:Clone()\n\t\t\t\t\t\t\tplayerCard.Name = player.Name\n\t\t\t\t\t\t\tplayerCard.Parent = ui.content\n\t\t\t\t\t\t\tplayerCard.Visible = true\n\t\t\t\t\t\t\tplayerCard.Activated:connect(function()\n\t\t\t\t\t\t\t\tModules.inspectPlayer.open(player)\n\t\t\t\t\t\t\t\tModules.inspectPlayerPreview.close()\n\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\tplayerCard.MouseEnter:connect(function()\n\t\t\t\t\t\t\t\tselectedPlayerCard = playerCard\n\t\t\t\t\t\t\t\tModules.inspectPlayerPreview.open(player, playerCard)\n\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\tplayerCard.SelectionGained:connect(function()\n\t\t\t\t\t\t\t\tselectedPlayerCard = playerCard\n\t\t\t\t\t\t\t\tModules.inspectPlayerPreview.open(player, playerCard)\n\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\tplayerCard.MouseLeave:connect(function()\n\t\t\t\t\t\t\t\tif selectedPlayerCard == playerCard then\n\t\t\t\t\t\t\t\t\tselectedPlayerCard = nil\n\t\t\t\t\t\t\t\t\tModules.inspectPlayerPreview.close()\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\tplayerCard.SelectionLost:connect(function()\n\t\t\t\t\t\t\t\tif selectedPlayerCard == playerCard then\n\t\t\t\t\t\t\t\t\tselectedPlayerCard = nil\n\t\t\t\t\t\t\t\t\tModules.inspectPlayerPreview.close()\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\tplayerCard.LayoutOrder = 22\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif playerCard.appearance.Value ~= appearance and Modules.loadingScreen.loaded then\n\t\t\t\t\t\t\tplayerCard.appearance.Value = appearance\n\t\t\t\t\t\t\trepareRender(playerCard.character.ViewportFrame, player, data)\n\t\t\t\t\t\tend\n\t\t\t\t\t\tlocal gold = player:FindFirstChild(\"gold\") and player.gold.Value or 0\n\t\t\t\t\t\tModules.money.setLabelAmount(playerCard.content.money, gold)\n\n\t\t\t\t\t\tlocal level = player:FindFirstChild(\"level\") and player.level.Value or 0\n\t\t\t\t\t\tplayerCard.content.level.value.Text = \"Lvl.\" .. level\n\n\t\t\t\t\t\tplayerCard.content.icon.ImageLabel.Image = \"https://www.roblox.com/headshot-thumbnail/image?userId=\" .. player.userId .. \"&width=100&height=100&format=png\"\n\t\t\t\t\t\tplayerCard.content.username.Text = player.Name\n\n\t\t\t\t\t\tlocal localPlayer = game.Players.LocalPlayer\n\t\t\t\t\t\tlocal origin = localPlayer.Character and localPlayer.Character.PrimaryPart and localPlayer.Character.PrimaryPart.Position\n\t\t\t\t\t\torigin = origin or Vector3.new()\n\n\t\t\t\t\t\tlocal class = player:FindFirstChild(\"class\") and player.class.Value:lower() or \"unknown\"\n\t\t\t\t\t\tlocal emblemVisible\n\t\t\t\t\t\tif class:lower() ~= \"adventurer\" then\n\t\t\t\t\t\t\tplayerCard.content.emblem.Image = \"rbxgameasset://Images/emblem_\"..class:lower()\n\t\t\t\t\t\t\tplayerCard.content.emblem.Visible = true\n\t\t\t\t\t\t\temblemVisible = true\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tplayerCard.content.emblem.Visible = false\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tlocal playerColor = Color3.fromRGB(208, 208, 208)\n\n\t\t\t\t\t\tlocal distance = (player.Character.PrimaryPart.Position - origin).magnitude\n\t\t\t\t\t\t-- do not update layoutOrder based on distance when leaderboard is selected\n\t\t\t\t\t\tlocal layoutOrder = (selectedPlayerCard == nil) and math.clamp(math.ceil(math.sqrt(distance)), 1, 20)\n\n\t\t\t\t\t\tif player == localPlayer then\n\t\t\t\t\t\t\tlayoutOrder = -1\n\t\t\t\t\t\t\tplayerColor = Color3.fromRGB(255, 206, 89)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tlocal partyInfo = network:invoke(\"getCurrentPartyInfo\")\n\t\t\t\t\t\t\tif partyInfo and partyInfo.members then\n\t\t\t\t\t\t\t\tfor i,partyMemberInfo in pairs(partyInfo.members) do\n\t\t\t\t\t\t\t\t\tif player == partyMemberInfo.player then\n\t\t\t\t\t\t\t\t\t\tlayoutOrder = 0\n\t\t\t\t\t\t\t\t\t\tplayerColor = Color3.fromRGB(87, 255, 255)\n\t\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tplayerCard.content.icon.BorderColor3 = playerColor\n\t\t\t\t\t\tplayerCard.content.emblem.ImageColor3 = playerColor\n\t\t\t\t\t\tplayerCard.content.username.TextColor3 = playerColor\n\t\t\t\t\t\t--playerCard.premium.ImageColor3 = playerColor\n\t\t\t\t\t\t--playerCard.premium.Visible = player.MembershipType == Enum.MembershipType.Premium\n\t\t\t\t\t\tif layoutOrder then\n\t\t\t\t\t\t\tplayerCard.LayoutOrder = layoutOrder\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tplayerCardCount = playerCardCount + 1\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tui.content.CanvasSize = UDim2.new(0, 0, 0, 35 * playerCardCount)\n\n\t\t-- remove un-used cards\n\t\tfor i, card in pairs(ui.content:GetChildren()) do\n\t\t\tif card:IsA(\"GuiObject\") and game.Players:FindFirstChild(card.Name) == nil then\n\t\t\t\tcard:Destroy()\n\t\t\tend\n\t\tend\n\n\tend\n\n\tspawn(function()\n\t\twhile wait(1) do\n\t\t\tupdate()\n\t\tend\n\tend)\n\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/loadingScreen.lua",
    "content": "local module = {}\n\nmodule.loaded = false\n\nfunction module.init(Modules)\n\t\n\t-- disabled\n\tmodule.loaded = true\n\tif true then\n\t\treturn\n\tend\n\t\n\tlocal contentProvider \t= game:GetService(\"ContentProvider\")\n\tlocal runService \t\t= game:GetService(\"RunService\")\n\tlocal teleService\t\t= game:GetService(\"TeleportService\")\n\t\n\tlocal teleportData = teleService:GetLocalPlayerTeleportData() or {}\n\t\n\tlocal arrivingFrom = teleportData.arrivingFrom\n\n\t\n\t-- no need to load assets if you are arriving from a teleport\n\n\tif arrivingFrom and arrivingFrom ~= 2015602902 and arrivingFrom ~= 2376885433 and arrivingFrom ~= 2015602902 then\n\t\tmodule.loaded = true\n\t\treturn false\n\tend\n\t\n\tlocal tween \t\t\t= Modules.tween\n\t\n\tlocal contentList = {}\n\t\n\tlocal loading = true\n\t\n\ttable.insert(contentList, game.ReplicatedStorage:WaitForChild(\"characterAnimations\"))\n\ttable.insert(contentList, game.ReplicatedStorage:WaitForChild(\"abilityAnimations\"))\n\ttable.insert(contentList, game.ReplicatedStorage:WaitForChild(\"sounds\"))\n\ttable.insert(contentList, game:GetService(\"StarterGui\"))\n\ttable.insert(contentList, game.ReplicatedStorage:WaitForChild(\"itemData\"))\n\ttable.insert(contentList, game.ReplicatedStorage:WaitForChild(\"abilityLookup\"))\n\ttable.insert(contentList, game.ReplicatedStorage:WaitForChild(\"accessoryLookup\"))\n\t\n\t\n\tlocal contents = script.Parent.contents\n\t\n\tspawn(function()\n\t\tlocal maxQueueSize = contentProvider.RequestQueueSize\n\t\twhile loading do\n\t\t\tlocal queueSize = contentProvider.RequestQueueSize\n\t\t\tif queueSize > maxQueueSize then\n\t\t\t\tmaxQueueSize = queueSize\n\t\t\tend\n\t\t\t\n\t\t\tlocal loadedAssetCount = maxQueueSize - queueSize\n\t\t\t\n\t\t\tcontents.value.Text = tostring(loadedAssetCount) .. \"/\" .. tostring(maxQueueSize)\n\t\t\t\n\t\t\tcontents.spinner.Rotation = contents.spinner.Rotation + 1\n\t\t\trunService.Heartbeat:wait()\n\t\tend\n\tend)\n\t\n\t\n\tspawn(function()\n\t\t-- make sure the loading UI is loaded in\n\t\tcontentProvider:PreloadAsync({script.Parent})\n\t\tscript.Parent.Visible = true\n\t\tcontentProvider:PreloadAsync(contentList)\n\t\tmodule.loaded = true\n\t\tloading = false\n\t\tcontents.spinner.Image = \"rbxassetid://2528903599\"\n--\t\tcontents.spinner.ImageColor3 = Color3.fromRGB(132, 255, 98)\n\t\tcontents.spinner.Rotation = 0\n\t\tcontents.value.Text = \"All done!\"\n--\t\tcontents.value.TextColor3 = Color3.fromRGB(132, 255, 98)\n\n\t\ttween(contents.spinner,{\"ImageColor3\"},{Color3.fromRGB(132, 255, 98)}, 0.5)\n\t\ttween(contents.value,{\"TextColor3\"},{Color3.fromRGB(132, 255, 98)}, 0.5)\n\t\t\n\n\t\tfor i=1,4 do\n\t\t\tlocal flare = script.Parent.flare:Clone()\n\t\t\tflare.Name = \"flareCopy\"\n\t\t\tflare.Parent = script.Parent\n\t\t\tflare.Visible = true\n\t\t\tflare.Size = UDim2.new(1,4,1,4)\n\t\t\tflare.Position = UDim2.new(0,-2,0.5,0)\n\t\t\tflare.AnchorPoint = Vector2.new(0,0.5)\n\t\t\tlocal x = (180 - 40*i)\n\t\t\tlocal y = (14 - 2*i)\n\t\t\tlocal EndPosition = UDim2.new(0,-y/2,0.5,0)\n\t\t\tlocal EndSize = UDim2.new(1,x,1,y)\n\t\t\ttween(flare,{\"Position\",\"Size\",\"ImageTransparency\"},{EndPosition, EndSize, 1},0.5*i)\n\t\tend\t\t\n\t\t\n\t\twait(2)\n\t\ttween(contents, {\"Position\"}, {UDim2.new(-1,-20,0,0)}, 0.5)\n\t\twait(0.5)\n\t\t\n\t\tscript.Parent.Visible = false\n\tend)\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/loreBook.lua",
    "content": "local module = {}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal modules = require(replicatedStorage.modules)\nlocal network \t= modules.load(\"network\")\nlocal utilities = modules.load(\"utilities\")\n\nlocal util = {}\nutil.network = network\n\nlocal player = game.Players.LocalPlayer\nlocal playerGui = player.PlayerGui\nlocal ui = playerGui.gameUI.loreBook\n\nlocal onPage = 1\nlocal currentPages = {}\n\nlocal textContent = ui.bookHolder.pages.lore.info.textcontent\n\nfunction module.init(Modules)\n\n\tlocal function updatePage()\n\n\t\tif currentPages[onPage] and currentPages[onPage].text then\n\t\t\ttextContent.Text = currentPages[onPage].text\n\t\t\tif onPage >= #currentPages then\n\t\t\t\tonPage = #currentPages -- to be sure\n\t\t\t\tui.bookHolder.pages.lore.next.Visible = false\n\t\t\telse\n\t\t\t\tui.bookHolder.pages.lore.next.Visible = true\n\t\t\tend\n\n\t\t\tif onPage <= 1 then\n\t\t\t\tonPage = 1\n\t\t\t\tui.bookHolder.pages.lore.prev.Visible = false\n\t\t\telse\n\t\t\t\tui.bookHolder.pages.lore.prev.Visible = true\n\t\t\tend\n\t\t\tui.bookHolder.pages.lore.title.Text = \"Page \"..onPage\n\n\t\t\tif currentPages[onPage].openFunc then\n\t\t\t\tcurrentPages[onPage].openFunc(util)\n\t\t\tend\n\t\t\treturn true\n\t\tend\n\t\treturn false\n\tend\n\n\tlocal function createBook(pages, color)\n\t\tui.bookCover.ImageColor3 = color or Color3.fromRGB(38, 42, 58)\n\t\tcurrentPages = pages\n\t\tonPage = 1\n\t\tlocal success = updatePage()\n\t\tif success then\n\t\t\tmodule.open()\n\t\tend\n\t\treturn true\n\tend\n\n\n\tlocal function main()\n\n\t\tnetwork:create(\"openLoreBookFromClient\", \"BindableFunction\")\n\t\tnetwork:connect(\"openLoreBookFromClient\", \"OnInvoke\", createBook)\n\n\n\t\tnetwork:connect(\"openLoreBookFromServer\", \"OnClientEvent\", createBook)\n\n\n\n\t\tui.bookHolder.pages.lore.next.MouseButton1Click:connect(function()\n\t\t\tif onPage < #currentPages then\n\t\t\t\tonPage = onPage + 1\n\t\t\t\tupdatePage()\n\t\t\tend\n\t\tend)\n\n\t\tui.bookHolder.pages.lore.prev.MouseButton1Click:connect(function()\n\t\t\tif onPage > 1 then\n\t\t\t\tonPage = onPage - 1\n\t\t\t\tupdatePage()\n\t\t\tend\n\t\tend)\n\n\t\tui.close.MouseButton1Click:connect(function()\n\t\t\tif ui.Visible then\n\t\t\t\tModules.focus.toggle(ui)\n\t\t\tend\n\n\t\tend)\n\tend\n\n\tfunction module.open()\n\t\tif not ui.Visible then\n\t\t\tui.UIScale.Scale = (Modules.input.menuScale + .15 or 1.25) --* 0.75\n\t\t--\tModules.tween(ui.UIScale, {\"Scale\"}, (Modules.input.menuScale or 1), 0.5, Enum.EasingStyle.Bounce)\n\t\tend\n\t\tModules.focus.toggle(ui)\n\n\tend\n\n\tmain()\n\nend\n\n\n\n\n\nreturn module"
  },
  {
    "path": "src/StarterGui/menuButtons.lua",
    "content": "local module = {}\n\nlocal ui = script.Parent.gameUI.right.buttons\n\nfunction module.init(Modules)\n\n\tlocal tween = Modules.tween\n\n\tui.openEquipment.Activated:connect(function()\n\t\tModules.equipment.show()\n\tend)\n\tui.openInventory.Activated:connect(function()\n\t\tModules.inventory.show()\n\tend)\n\tui.openAbilities.Activated:connect(function()\n\t\tModules.abilities.show()\n\tend)\n\tui.openSettings.Activated:connect(function()\n\t\tModules.settings.show()\n\tend)\n\n\tfor _, button in pairs(ui:GetChildren()) do\n\t\tif button:IsA(\"GuiButton\") then\n\t\t\tlocal function selected()\n\t\t\t\t-- Hide other buttons\n\t\t\t\tfor _, button in pairs(ui:GetChildren()) do\n\t\t\t\t\tif button:IsA(\"GuiButton\") then\n\t\t\t\t\t\tbutton.ZIndex = 1\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tbutton.ZIndex = 2\n\t\t\t\tlocal position = button.Position\n\t\t\t\tlocal newPosition = UDim2.new(position.X.Scale, position.X.Offset, position.Y.Scale, -36)\n\t\t\t\ttween(button, {\"Position\"}, newPosition, 0.5)\n\t\t\tend\n\t\t\tlocal function unselected()\n\t\t\t\tlocal position = button.Position\n\t\t\t\tlocal newPosition = UDim2.new(position.X.Scale, position.X.Offset, position.Y.Scale, 0)\n\t\t\t\ttween(button, {\"Position\"}, newPosition, 0.5)\n\t\t\tend\n\t\t\tbutton.MouseEnter:connect(selected)\n\t\t\tbutton.SelectionGained:connect(selected)\n\t\t\tbutton.MouseLeave:connect(unselected)\n\t\t\tbutton.SelectionLost:connect(unselected)\n\t\tend\n\tend\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/mobileButtons.lua",
    "content": "\nlocal module = {}\n\nlocal player = game:GetService(\"Players\").LocalPlayer\nlocal gui = player.PlayerGui.gameUI.mobileButtons\n\nfunction module.init(Modules)\n\tlocal control = Modules.control\n\tlocal itemAcquistion = Modules.itemAcquistion\n\n\tgui.jump.Activated:Connect(function()\n\t\tcontrol.doJump()\n\tend)\n\n\tgui.pickup.InputBegan:Connect(function(Input)\n\t\titemAcquistion.pickupInputGained(Input)\n\tend)\nend\n\nreturn module\n\n\n"
  },
  {
    "path": "src/StarterGui/money.lua",
    "content": "-- WOAH MONEY XDDDDDDD\n-- hand crafted in San Mateo, CA by Andrew \"the rock\" Berezaa ;P\n\nlocal module = {}\n\nlocal player = game:GetService(\"Players\").LocalPlayer\nlocal gui = player.PlayerGui.gameUI.bottomRight.money\n\nmodule.suffixes = {\"k\",\"M\",\"B\",\"T\",\"qd\",\"Qn\",\"sx\",\"Sp\",\"O\",\"N\",\"de\",\"Ud\",\"DD\",\"tdD\",\"qdD\",\"QnD\",\"sxD\",\"SpD\",\"OcD\",\"NvD\",\"Vgn\",\"UVg\",\"DVg\",\"TVg\",\"qtV\",\"QnV\",\"SeV\",\"SPG\",\"OVG\",\"NVG\",\"TGN\",\"UTG\",\"DTG\",\"tsTG\",\"qtTG\",\"QnTG\",\"ssTG\",\"SpTG\",\"OcTG\",\"NoTG\",\"QdDR\",\"uQDR\",\"dQDR\",\"tQDR\",\"qdQDR\",\"QnQDR\",\"sxQDR\",\"SpQDR\",\"OQDDr\",\"NQDDr\",\"qQGNT\",\"uQGNT\",\"dQGNT\",\"tQGNT\",\"qdQGNT\",\"QnQGNT\",\"sxQGNT\",\"SpQGNT\", \"OQQGNT\",\"NQQGNT\",\"SXGNTL\"}                                              \n\n\nlocal function shorten(Input)\n\tlocal Negative = Input < 0\n\tInput = math.abs(Input)\n\n\tlocal Paired = false\n\tfor i,v in pairs(module.suffixes) do\n\t\tif not (Input >= 10^(3*i)) then\n\t\t\tInput = Input / 10^(3*(i-1))\n\t\t\tlocal isComplex = (string.find(tostring(Input),\".\") and string.sub(tostring(Input),4,4) ~= \".\")\n\t\t\tInput = string.sub(tostring(Input),1,(isComplex and 4) or 3) .. (module.suffixes[i-1] or \"\")\n\t\t\tPaired = true\n\t\t\tbreak;\n\t\tend\n\tend\n\tif not Paired then\n\t\tlocal Rounded = math.floor(Input)\n\t\tInput = tostring(Rounded)\n\tend\n\n\tif Negative then\n\t\treturn \"-\"..Input\n\tend\n\treturn Input\nend\n\nfunction module.setLabelAmount(label, amount, costInfo)\n\t\n\tlocal negative = false\n\tif amount < 0 then\n\t\tnegative = true\n\t\tamount = math.abs(amount)\n\tend\n\t\n\tif costInfo and costInfo.costType and costInfo.costType ~= \"money\" then\n\t\tlabel.icon.Image = costInfo.icon or \"\"\n\t\tlabel.amount.TextColor3 = costInfo.textColor or Color3.new(1,1,1)\n\t\tlabel.amount.Text =  shorten(amount)\n\telse\n\t\tif amount >= 10^6 then\n\t\t\tlabel.icon.Image = \"rbxassetid://2536432897\"\n\t\t\tlabel.amount.TextColor3 = Color3.fromRGB(255, 236, 123)\n\t\t\tif amount >= 10^8 then\n\t\t\t\t-- no decimals\n\t\t\t\tlabel.amount.Text = tostring(math.floor(amount/10^6))\n\t\t\telse\n\t\t\t\t-- one decimal point\n\t\t\t\tlabel.amount.Text = tostring(math.floor(amount/10^5)/10)\n\t\t\tend\t\t\t\n\t\telseif amount >= 10^3 then\n\t\t\t-- silver\n\t\t\tlabel.icon.Image = \"rbxassetid://2535600034\"\n\t\t\tlabel.amount.TextColor3 = Color3.fromRGB(223, 223, 223)\n\t\t\tif amount >= 10^5 then\n\t\t\t\t-- no decimals\n\t\t\t\tlabel.amount.Text = tostring(math.floor(amount/10^3))\n\t\t\telse\n\t\t\t\t-- one decimal point\n\t\t\t\tlabel.amount.Text = tostring(math.floor(amount/10^2)/10)\n\t\t\tend\n\t\telse\n\t\t\t-- bronze\n\t\t\tlabel.icon.Image = \"rbxassetid://2535600080\"\n\t\t\tlabel.amount.TextColor3 = Color3.fromRGB(255, 170, 149)\n\t\t\tlabel.amount.Text = tostring(math.floor(amount))\n\t\tend\t\t\n\tend\n\t\n\tif negative then\n\t\tlabel.amount.Text = \"-\"..label.amount.Text\n\tend\n\t\n\tlocal len = game.TextService:GetTextSize(label.amount.Text, label.amount.TextSize, label.amount.Font, Vector2.new(0,0)).X + 2\n\t\n\tlocal padding = label:FindFirstChild(\"padding\") and label.padding.Value or 0\n\t\n\tlabel.amount.Size = UDim2.new(0, len + padding, label.amount.Size.Y.Scale, label.amount.Size.Y.Offset + padding)\n\t\nend\n\t\nmodule.labels = {}\t\n\t\nlocal lastGoldValue = 0\t\n\nfunction module.subscribeToPlayerMoney(label)\n\tlocal lastMouseOver\n\tlabel.MouseEnter:connect(function()\n\t\tif lastGoldValue > 999 then\n\t\t\tif not label.Parent:FindFirstChild(\"exactMoney\") then return end\n\t\t\tlocal thisMouseOver = tick()\n\t\t\tlastMouseOver = thisMouseOver\n\t\t\tlabel.Parent.exactMoney.Visible = true\n\t\t\twait(10)\n\t\t\tif lastMouseOver == thisMouseOver then\n\t\t\t\tlabel.Parent.exactMoney.Visible = false\n\t\t\tend\n\t\tend\n\tend)\n\tlabel.MouseLeave:connect(function()\n\t\tif not label.Parent:FindFirstChild(\"exactMoney\") then return end\n\t\tlabel.Parent.exactMoney.Visible = false\n\tend)\t\t\t\n\t\n\ttable.insert(module.labels, label)\nend\n\nfunction module.init(Modules)\n\t\n\tlocal network = Modules.network\n\t\n\n\t\n\n\t\n\tfunction module.subscribeToPlayerMoney(label)\n\t\t\n\t\tlocal gold = network:invoke(\"getCacheValueByNameTag\", \"gold\")\n\t\tmodule.setLabelAmount(label, gold)\n\t\tlastGoldValue = gold\n\n\t\t\n\t\tif label.Parent:FindFirstChild(\"exactMoney\") then\n\t\t\tlabel.Parent.exactMoney.Text = math.floor(lastGoldValue)\n\t\t\tlocal xBounds = game.TextService:GetTextSize(label.Parent.exactMoney.Text, label.Parent.exactMoney.TextSize, label.Parent.exactMoney.Font, Vector2.new(0,0)).X\n\t\t\tlabel.Parent.exactMoney.Size = UDim2.new(0, xBounds + 16 + 10, 0, 26 + 10)\t\n\t\t\t\n\t\t\tlocal lastMouseOver\n\t\t\tlabel.MouseEnter:connect(function()\n\t\t\t\tif lastGoldValue > 999 then\n\t\t\t\t\tlocal thisMouseOver = tick()\n\t\t\t\t\tlastMouseOver = thisMouseOver\n\t\t\t\t\tlabel.Parent.exactMoney.Visible = true\n\t\t\t\t\twait(10)\n\t\t\t\t\tif lastMouseOver == thisMouseOver then\n\t\t\t\t\t\tlabel.Parent.exactMoney.Visible = false\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend)\n\t\t\tlabel.MouseLeave:connect(function()\n\t\t\t\tlabel.Parent.exactMoney.Visible = false\n\t\t\tend)\t\t\t\t\n\t\tend\n\t\t\n\t\ttable.insert(module.labels, label)\n\n\n\t\n\tend\t\n\t\n\t\n\tlocal function onDataChange(key, value)\n\t\tif key == \"gold\" then\n\t\t\tlastGoldValue = value\n\t\t\tfor i,label in pairs(module.labels) do\n\t\t\t\tmodule.setLabelAmount(label, value)\n\t\t\t\t\n\t\t\t\tif label.Parent:FindFirstChild(\"exactMoney\") then\n\t\t\t\t\tlabel.Parent.exactMoney.Text = math.floor(lastGoldValue)\n\t\t\t\t\tlocal xBounds = game.TextService:GetTextSize(label.Parent.exactMoney.Text, label.Parent.exactMoney.TextSize, label.Parent.exactMoney.Font, Vector2.new(0,0)).X\n\t\t\t\t\tlabel.Parent.exactMoney.Size = UDim2.new(0, xBounds + 16 + 10, 0, 26 + 10)\t\n\t\t\t\tend\t\t\t\t\n\t\t\tend\n\n\t\tend\n\tend\t\n\t\n\t-- update any subscribed labels from before module.init\n\tonDataChange(\"gold\", network:invoke(\"getCacheValueByNameTag\", \"gold\"))\n\tmodule.subscribeToPlayerMoney(gui)\n\t\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onDataChange)\t\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/monsterBook.lua",
    "content": "-- Monster book thingie by the honorable lord ber\n\nlocal module = {}\n\nlocal player = game:GetService(\"Players\").LocalPlayer\nlocal ui = player.PlayerGui.gameUI.monsterBook\nfunction module.close()\n\tui.Visible = false\nend\n\nfunction module.open(newBook)\n\t\nend\n\n\nlocal extentsSizeCache = {}\n\nfunction module.init(Modules)\n\t\n\tspawn(function()\n\t\t\n\t\tlocal network = Modules.network\n\t\t\n\t\tif game.Players.LocalPlayer:FindFirstChild(\"bountyHunter\") then\n\t\t\t\t\n\t\t\t\n\t\t\n\t\t\tlocal bountyBookButton = Modules.input.menuButtons[\"openMonsterBook\"]\n\t\t\n\t\t\tlocal bookFrame = ui\n\t\t\t\n\t\t\tlocal currentTab\n\t\t\t\n\t\t\tlocal monsterBookData = {}\n\t\t\t\n\t\t\tlocal pages = ui.bookHolder.pages\n\t\t\t\n\t\t\tlocal monsterLookup = require(game.ReplicatedStorage.monsterLookup)\n\t\t\tlocal itemLookup = require(game.ReplicatedStorage.itemData)\n\t\t\tlocal levels = Modules.levels\n\t\t\n\t\t\tlocal currentMonsterName\n\t\t\t\n\t\t\tpages.monster.close.Activated:connect(function()\n\t\t\t\tpages.monster.Visible = false\n\t\t\t\tpages.main.Visible = true\n\t\t\tend)\n\t\t\tpages.monster.claim.Activated:connect(function()\n\t\t\t\tif currentMonsterName then\n\t\t\t\t\tlocal success, reason = network:invokeServer(\"playerRequest_claimBounty\", currentMonsterName)\n\t\t\t\t\tif success then\n\t\t\t\t\t\tpages.monster.Visible = false\n\t\t\t\t\t\tModules.utilities.playSound(\"questTurnedIn\")\n\t\t\t\t\t\tpages.main.Visible = true\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend)\n\t\t\t\n\t\t\tlocal centerOfMassCache = {}\n\t\t\t\n\t\t\tlocal function getCenterOfMassOfModel(model)\n\t\t\t\tlocal totalVotedPosition = Vector3.new()\n\t\t\t\tlocal totalVotes = 0\n\t\t\t\tfor i,part in pairs(model:GetDescendants()) do\n\t\t\t\t\tif part:IsA(\"BasePart\") then\n\t\t\t\t\t\tlocal center = part.Position\n\t\t\t\t\t\tlocal mass = part:GetMass()\n\t\t\t\t\t\ttotalVotedPosition = totalVotedPosition + (center * mass)\n\t\t\t\t\t\ttotalVotes = totalVotes + mass\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\treturn totalVotedPosition / totalVotes\n\t\t\tend\n\t\t\t\n\t\t\tlocal idolCaps = {\n\t\t\t\t[\"1\"] = 5;\n\t\t\t\t[\"2\"] = 10;\n\t\t\t\t[\"3\"] = 15;\n\t\t\t\t[\"4\"] = 20;\n\t\t\t\t[\"5\"] = 25;\n\t\t\t\t[\"6\"] = 30;\n\t\t\t\t[\"99\"] = 15;\n\t\t\t}\n\t\t\t\n\t\t\tlocal existingMonsterViewports = {}\n\t\t\t\n\t\t\tlocal function getMonsterViewport(monsterName)\n\t\t\t\t\n\t\t\t\tif existingMonsterViewports[monsterName] then\n\t\t\t\t\treturn existingMonsterViewports[monsterName]\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\tlocal viewport = script.ViewportFrame:Clone()\n\t\t\t\t\n\t\t\t\tlocal monster = monsterLookup[monsterName]\n\t\t\t\tlocal monsterModule = monster.module\n\t\t\t\t\n\t\t\t\t\n\t\t\t\tlocal entity\n\t\t\t\t\n\t\t\t\tif monsterModule:FindFirstChild(\"displayEntity\") then\n\t\t\t\t\tentity = monsterModule.displayEntity:Clone()\n\t\t\t\telse\n\t\t\t\t\tentity = monsterModule.entity:Clone()\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t\tif entity:FindFirstChild(\"animations\") and entity.animations:FindFirstChild(\"idling\") then\n\t\t\t\t\tentity.Parent = workspace\n\t\t\t\t\tentity.PrimaryPart.Anchored = true\n\t\t\t\t\tlocal entityController = entity:FindFirstChild(\"AnimationController\")\n\t\t\t\t\tgame.ContentProvider:PreloadAsync({entity.animations.idling})\n\t\t\t\t\tentityController:LoadAnimation(entity.animations.idling):Play()\t\n\t\t\t\t\twait(0.1)\t\t\t\n\t\t\t\t\tlocal oldEntity = entity\n\t\t\t\t\tentity = entity:Clone()\n\t\t\t\t\toldEntity:Destroy()\n\t\t\t\tend\n\t\t\n\t\t\t\t\n\t\t\t\tentity.Parent = viewport\n\t\t\t\tentity:SetPrimaryPartCFrame(CFrame.new())\n\t\t\t\tlocal extents = extentsSizeCache[monsterModule.Name]\n\t\t\t\tif extents == nil then\n\t\t\t\t\textents = entity:GetExtentsSize() \n\t\t\t\t\textentsSizeCache[monsterModule.Name] = extents\n\t\t\t\tend\t\t\n\t\t\t\tlocal centerOfMass = centerOfMassCache[monsterModule.Name]\n\t\t\t\tif centerOfMass == nil then\n\t\t\t\t\tcenterOfMass = getCenterOfMassOfModel(entity)\n\t\t\t\t\tcenterOfMassCache[monsterModule.Name] = centerOfMass\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\tlocal camera = Instance.new(\"Camera\")\n\t\t\t\t\n\t\t\t\tlocal min = math.min(extents.x, extents.z)\n\t\t\t\tlocal multi = Vector3.new(extents.z, extents.y, extents.x)/min\n\t\t\t\t\n\t\t\t\tlocal pos = ((centerOfMass + entity.PrimaryPart.Position)/2) + (extents * Vector3.new(0.5,0.1,-0.8) * multi)\n\t\t\t\tcamera.CameraType = Enum.CameraType.Scriptable\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\tcamera.CFrame = CFrame.new(pos, centerOfMass) * (monster.cameraOffset or CFrame.new())\n\t\t\t\tcamera.Parent = viewport\n\t\t\t\tviewport.CurrentCamera = camera\t\n\t\t\t\t\n\t\t\t\texistingMonsterViewports[monsterName] = viewport\n\t\t\t\t\n\t\t\t\treturn viewport\t\n\t\t\tend\n\t\t\t\n\t\t\tlocal function updateTabPage()\n\t\t\t\t-- clear existing children\n\t\t\t\tfor i,child in pairs(pages.main:GetChildren()) do\n\t\t\t\t\tif child:IsA(\"GuiButton\") then\n\t\t\t\t\t\tchild:Destroy()\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\t-- populate with new buttons\n\t\t\t\tfor i,monsterModule in pairs(game.ReplicatedStorage.monsterLookup:GetChildren()) do\n\t\t\t\t\tlocal monster = monsterLookup[monsterModule.Name]\n\t\t\t\t\tif monster and monster.monsterBookPage and monster.monsterBookPage == tonumber(currentTab.Name) then\n\t\t\t\t\t\t\n\t\t\t\t\t\tlocal idolCap = idolCaps[currentTab.Name]\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\tlocal monsterButton = script.monster:Clone()\n\t\t\t\t\t\tmonsterButton.Name = monsterModule.Name\n\t\t\t\t\t\tmonsterButton.LayoutOrder = monster.level or 999\n\t\t\t\t\t\tmonsterButton.alert.Visible = false\n\t\t\t\t\t\t\n\t\t\t\t\t\tlocal viewport = getMonsterViewport(monsterModule.Name):Clone()\n\t\t\t\t\t\tif monsterButton:FindFirstChild(\"ViewportFrame\") then\n\t\t\t\t\t\t\tmonsterButton.ViewportFrame:Destroy()\n\t\t\t\t\t\tend\n\t\t\t\t\t\tviewport.Parent = monsterButton\n\t\t\t\t\t\tmonsterButton.money.Visible = false\n\t\t\t\t\t\tmonsterButton.progress.Visible = false\n\t\t\t\t\t\t\t\n\t\t\t\t\t\tlocal playerBountyData = monsterBookData[monsterModule.Name]\n\t\t\t\t\t\t\n\t\t\t\t\t\tlocal kills = playerBountyData and playerBountyData.kills or 0\n\t\t\t\t\t\tlocal lastBounty = playerBountyData and playerBountyData.lastBounty or 0\n\t\t\t\t\t\tif kills > 0 or lastBounty > 0 then\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tlocal bountyPageInfo = levels.bountyPageInfo[tostring(monster.monsterBookPage)]\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tlocal bountyInfo = bountyPageInfo[lastBounty + 1]\t\n\t\t\t\t\t\t\tif bountyInfo then\n\t\t\t\t\t\t\t\t-- this line is duplicated in manager_player\n\t\t\t\t\t\t\t\tlocal goldReward = levels.getBountyGoldReward(bountyInfo, monster)\n\t\t\t\t\t\t\t\tModules.money.setLabelAmount(\n\t\t\t\t\t\t\t\t\tmonsterButton.money,\n\t\t\t\t\t\t\t\t\tgoldReward\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\tmonsterButton.money.Visible = true\n\t\t\t\t\t\t\t\tmonsterButton.progress.Visible = true\n\t\t\t\t\t\t\t\tmonsterButton.progress.amount.Text = tostring(playerBountyData.kills) .. \"/\" .. tostring(bountyInfo.kills)\n\t\t\t\t\t\t\t\tmonsterButton.progress.xp.value.Size = UDim2.new(math.clamp(playerBountyData.kills/bountyInfo.kills,0,1),0,1,0)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif kills >= bountyInfo.kills then\n\t\t\t\t\t\t\t\tmonsterButton.alert.Visible = true\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tmonsterButton.tooltip.Value = monsterModule.Name\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tmonsterButton.Activated:connect(function()\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t-- display monster\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tcurrentMonsterName = monsterModule.Name\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tpages.monster.progress.Visible = false\n\t\t\t\t\t\t\t\tpages.monster.money.Visible = false\n\t\t\t\t\t\t\t\tpages.monster.claim.Visible = false\n\t\t\t\t\t\t\t\tif bountyInfo then\n\t\t\t\t\t\t\t\t\tlocal goldReward = levels.getBountyGoldReward(bountyInfo, monster)\n\t\t\t\t\t\t\t\t\tModules.money.setLabelAmount(\n\t\t\t\t\t\t\t\t\t\tpages.monster.money,\n\t\t\t\t\t\t\t\t\t\tgoldReward\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\tpages.monster.money.Visible = true\n\t\t\t\t\t\t\t\t\tpages.monster.progress.Visible = true\n\t\t\t\t\t\t\t\t\tpages.monster.progress.xp.value.Size = UDim2.new(math.clamp(playerBountyData.kills/bountyInfo.kills,0,1),0,1,0)\n\t\t\t\t\t\t\t\t\tif playerBountyData.kills >= bountyInfo.kills then\n\t\t\t\t\t\t\t\t\t\tpages.monster.claim.Visible = true\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tif pages.monster.holder:FindFirstChild(\"ViewportFrame\") then\n\t\t\t\t\t\t\t\t\tpages.monster.holder.ViewportFrame:Destroy()\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tmonsterButton.ViewportFrame:Clone().Parent = pages.monster.holder\n\t\t\t\t--\t\t\t\tpages.monster.progress.xp.value.Size = UDim2.new(math.clamp(idols/idolCap,0,1),0,1,0)\n\t\t\t\t\t\t\t\tpages.monster.title.Text = monsterModule.Name\n\t\t\t\t\t\t\t\tpages.monster.info.level.Text = \"Lvl. \"..tostring(monster.level or \"?\")\n\t\t\t\t\t\t\t\tpages.monster.info.health.Text = (monster.maxHealth or \"???\") ..\" HP\"\n\t\t\t\t\t\t\t\t\n\t\t\t\t--\t\t\t\tlocal bonus = 0.5 * math.clamp(idols / idolCap, 0, 1)\n\t\t\t\t--\t\t\t\tpages.monster.info.bonus.title.Text = \"+\" .. tostring(math.ceil(bonus * 100)) .. \"% EXP\"\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tpages.monster.Visible = true\n\t\t\t\t\t\t\t\tpages.main.Visible = false\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t-- clear existing loot\n\t\t\t\t\t\t\t\tfor i,lootObject in pairs(pages.monster.loot:GetChildren()) do\n\t\t\t\t\t\t\t\t\tif lootObject:isA(\"GuiObject\") then\n\t\t\t\t\t\t\t\t\t\tlootObject:Destroy()\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tlocal lootToShow = {}\n\t\t\t\t\t\t\t\tlocal addedLoot = {}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t-- remove duplicates\n\t\t\t\t\t\t\t\tif monster.lootDrops then\n\t\t\t\t\t\t\t\t\tfor i,lootDropData in pairs(monster.lootDrops) do\n\t\t\t\t\t\t\t\t\t\tif lootDropData.id ~= 1 then\n\t\t\t\t\t\t\t\t\t\t\tlocal realItem = itemLookup[lootDropData.id or lootDropData.itemName]\n\t\t\t\t\t\t\t\t\t\t\tif realItem then\n\t\t\t\t\t\t\t\t\t\t\t\tif not addedLoot[realItem.name] then\n\t\t\t\t\t\t\t\t\t\t\t\t\ttable.insert(lootToShow, lootDropData)\n\t\t\t\t\t\t\t\t\t\t\t\t\taddedLoot[realItem.name] = true\n\t\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tlocal rows = math.ceil(#lootToShow/4)\n\t\t\t\t\t\t\t\tpages.monster.loot.CanvasSize = UDim2.new(0,0,0,61 * rows)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tfor i, loot in pairs(lootToShow) do\n\t\t\t\t\t\t\t\t\tlocal lootObject = script.inventoryItemTemplate:Clone()\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\tlocal realItem = itemLookup[loot.id or loot.itemName]\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\tlootObject.item.Image = realItem.image or \"rbxassetid://2679574493\"\n\t\t\t\t\t\t\t\t\tlootObject.locked.Visible = false\n\t\t\t\t\t\t\t\t\tlootObject.item.Visible = true\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\tlocal percentDropChance = \"?%\"\n\t\t\t\t\t\t\t\t\tif loot.spawnChance then\n\t\t\t\t\t\t\t\t\t\tif loot.spawnChance >= 0.1 then\n\t\t\t\t\t\t\t\t\t\t\tpercentDropChance = tostring(math.floor(loot.spawnChance * 1000)/10)..\"%\"\n\t\t\t\t\t\t\t\t\t\telseif loot.spawnChance >= 0.01 then\n\t\t\t\t\t\t\t\t\t\t\tpercentDropChance = tostring(math.floor(loot.spawnChance * 10000)/100)..\"%\"\n\t\t\t\t\t\t\t\t\t\telseif loot.spawnChance >= 0.001 then\n\t\t\t\t\t\t\t\t\t\t\tpercentDropChance = tostring(math.floor(loot.spawnChance * 100000)/1000)..\"%\"\n\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\tpercentDropChance = tostring(math.floor(loot.spawnChance * 1000000)/10000)..\"%\"\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\n\t\t\t\t\t\t\t\t\tlootObject.item.tooltip.Value = realItem.name .. \" - \" .. percentDropChance\n\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\tlootObject.Parent = pages.monster.loot\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tend)\n\t\t\t\t\t\telse\n\t\t\t\t\t--\t\tmonsterButton.ViewportFrame.Visible = false\n\t\t\t\t\t\t\tmonsterButton.ViewportFrame.ImageColor3 = Color3.new(0,0,0  )\n\t\t\t\t\t\t\tmonsterButton.ViewportFrame.ImageTransparency = 0.7\n\t\t\t\t\t\t\tmonsterButton.progress.Visible = false\n\t\t\t\t\t\t\t\n\t\t\t\t\t--\t\tmonsterButton.locked.Visible = true\n\t\t\t\t\t\t\tmonsterButton.ImageColor3 = Color3.fromRGB(60, 60, 60)\n\t\t\t\t\t\t\tmonsterButton.ImageTransparency = 0.5\n\t\t\t\t\t\t\tmonsterButton.shadow.ImageTransparency = 0.5\t\t\t\t\t\n\t\t\t\t\t\tend\n\t\t\t\t\t\tmonsterButton.Parent = pages.main\n\t\t\t\t\t\t\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\t\t\n\t\t\tfunction module.open()\n\t\t\t\tif not ui.Visible then\n\t\t\t\t\tui.UIScale.Scale = (Modules.input.menuScale or 1) * 0.75\n\t\t\t\t\tModules.tween(ui.UIScale, {\"Scale\"}, (Modules.input.menuScale or 1), 0.5, Enum.EasingStyle.Bounce)\n\t\t\t\tend\n\t\t\t\tif currentTab then\n\t\t\t\t\tupdateTabPage()\n\t\t\t\tend\t\t\t\t\t\t\n\t\t\t\tModules.focus.toggle(ui)\n\t\t\t\tpages.main.Visible = true\n\t\t\t\tpages.monster.Visible = false\t\t\n\t\t\tend\t\t\n\t\t\t\n\t\t\tfunction module.close()\n\t\t\t\tif ui.Visible then\n\t\t\t\t\tModules.focus.toggle(ui)\t\n\t\t\t\tend\t\n\t\t\tend\n\t\t\tui.close.Activated:connect(module.close)\n\t\t\t\n\t\t\tfunction module.loadTab(tab)\n\t\t\t\tcurrentTab = tab\n\t\t\t\tlocal tabNumber = tonumber(tab.Name)\n\t\t\t\tfor i,otherTab in pairs(tab.Parent:GetChildren()) do\n\t\t\t\t\tif otherTab:IsA(\"GuiObject\") then\n\t\t\t\t\t\totherTab.Size = UDim2.new(0, 50, 0, 40)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\ttab.Size = UDim2.new(0, 60, 0, 40)\n\t\t\t\tlocal col = tab.ImageColor3\n\t\t\t\tbookFrame.bookCover.ImageColor3 = Color3.new((col.r + 0.15) * 0.6, (col.g + 0.15) * 0.6, (col.b + 0.15) * 0.6)\n\t\t\t\tscript.inventoryItemTemplate.ImageColor3 = Color3.new((col.r + 0.15) * 0.3, (col.g + 0.15) * 0.3, (col.b + 0.15) * 0.3)\n\t\t\t\t\n\t\t\t\tscript.monster.ImageColor3 = Color3.new((col.r + 0.15) * 0.9, (col.g + 0.15) * 0.9, (col.b + 0.15) * 0.9)\n\t\t\t\tpages.monster.holder.ImageColor3 = script.monster.ImageColor3\n\t\t\t\tscript.inventoryItemTemplate.locked.ImageColor3 = Color3.new((col.r + 0.1) * 0.92, (col.g + 0.1) * 0.92, (col.b + 0.1) * 0.92)\n\t\t--\t\tscript.monster.ViewportFrame.BackgroundColor3 = script.monster.ImageColor3\n\t\t\t\tscript.monster.progress.xp.value.ImageColor3 = col\n\t\t\t\tpages.monster.progress.xp.value.ImageColor3 = col\n\t\t\t\tpages.monster.info.bonus.title.TextColor3 = col\n\t\t\t\tpages.monster.holder.level.TextColor3 = col\n\t\t\t\t\n\t\t\t\tpages.main.Visible = true\n\t\t\t\tpages.monster.Visible = false\n\t\t\t\tupdateTabPage()\n\t\t\tend\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\tfor i,tabButton in pairs(bookFrame.tabs:GetChildren()) do\n\t\t\t\tif tabButton:IsA(\"GuiButton\") then\n\t\t\t\t\ttabButton.Activated:connect(function()\n\t\t\t\t\t\tmodule.loadTab(tabButton)\n\t\t\t\t\tend)\n\t\t\t\tend\n\t\t\tend\n\t\t\t\n\t\t\tlocal existingAlertTotal\n\t\t\t\n\t\t\tlocal function monsterBookDataUpdated(monsterBookData)\n\t\t\t\tlocal alertTotal = 0\t\n\t\t\t\tfor monsterName, playerBountyData in pairs(monsterBookData) do\n\t\t\t\t\tlocal kills = playerBountyData and playerBountyData.kills or 0\n\t\t\t\t\tlocal lastBounty = playerBountyData and playerBountyData.lastBounty or 0\n\t\t\t\t\t\t\t\n\t\t\t\t\tlocal monster = monsterLookup[monsterName]\n\t\t\t\t\tif monster and monster.monsterBookPage then\n\t\t\t\t\t\tlocal bountyPageInfo = levels.bountyPageInfo[tostring(monster.monsterBookPage)]\n\t\t\t\t\t\tlocal bountyInfo = bountyPageInfo[lastBounty + 1]\t\n\t\t\t\t\t\tif bountyInfo then\t\n\t\t\t\t\t\t\tif kills >= bountyInfo.kills then\n\t\t\t\t\t\t\t\talertTotal = alertTotal + 1\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\t\t\t\t\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\tbountyBookButton.alert.value.Text = tostring(alertTotal)\n\t\t\t\tbountyBookButton.alert.Visible = alertTotal > 0\n\t\t\t\t\n\t\t\t\tif existingAlertTotal and alertTotal > existingAlertTotal then\n\t\t\t\t\tlocal textObject = {\n\t\t\t\t\t\ttext = \"You completed a monster bounty!\";\n\t\t\t\t\t\ttextColor3 = Color3.fromRGB(255, 85, 70);\n\t\t\t\t\t\tid = \"newpoints\";\n\t\t\t\t\t}\n\t\t\t\t\tModules.notifications.alert(textObject,4, \"idolPickup\")\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\t\n\t\t\t\texistingAlertTotal = alertTotal\t\t\n\t\t\tend\n\t\t\t\n\t\t\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", function(index, value)\n\t\t\t\tif index == \"bountyBook\" then\n\t\t\t\t\tlocal existingMonsterBookData = monsterBookData\n\t\t\t\t\tmonsterBookData = value\n\t\t\t\t\t\t\n\t\t\t\t\tmonsterBookDataUpdated(monsterBookData)\n\t\t\t\t\t\n\t\t\t\t\tif currentTab and ui.Visible then\n\t\t\t\t\t\tupdateTabPage()\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend)\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\tspawn(function()\n\t\t\t\tfor i, monster in pairs(game.ReplicatedStorage.monsterLookup:GetChildren()) do\n\t\t\t\t\tgetMonsterViewport(monster.Name)\n\t\t\t\tend\n\t\t\t\tif not currentTab then\n\t\t\t\t\tmodule.loadTab(bookFrame.tabs[\"1\"])\n\t\t\t\tend\n\t\t\t\tmonsterBookData = network:invoke(\"getCacheValueByNameTag\", \"bountyBook\")\n\t\t\t\tmonsterBookDataUpdated(monsterBookData)\n\t\t\tend)\t\t\t\n\n\t\t\t\n\t\tend\n\tend)\n\t\n\t\n\nend\n\n\n\n\n\n\n\n\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/notifications.lua",
    "content": "local module = {}\nlocal alerts = {}\n\nfunction module.alert()\n\nend\n\nfunction module.init(Modules)\n\t\n\tif game.ReplicatedStorage:FindFirstChild(\"alertsOffset\") then\n\t\tscript.Parent.Position = UDim2.new(0.5, 0, 0, script.Parent.Position.Y.Offset + game.ReplicatedStorage.alertsOffset.Value)\n\tend\n\t\n\tlocal tween = Modules.tween\n\tlocal network = Modules.network\n\tlocal utilities = Modules.utilities\n\t\n\tfunction module.alert(textObject, duration, soundeffect)\n\t\tduration = duration or 4\n\t\t\n\t\tif soundeffect and game.ReplicatedStorage.assets.sounds:FindFirstChild(soundeffect) then\n\t\t\tutilities.playSound(soundeffect)\n\t\t\t--game.ReplicatedStorage.sounds[soundeffect]:Play()\n\t\tend\n\t\t\n\t\tlocal isNewAlert = false\n\t\n\t\tlocal alert\n\t\tif textObject.id and alerts[textObject.id] then\n\t\t\talert \t\t\t\t\t\t= alerts[textObject.id].alert\n\t\t\talerts[textObject.id].start = tick()\n\t\telse\n\t\t\talert \t\t= script.Parent:WaitForChild(\"alert\"):clone()\n\t\t\tisNewAlert \t= true\n\t\t\t\n\t\t\tif textObject.id then\n\t\t\t\talerts[textObject.id] = {alert = alert; start = tick()}\n\t\t\tend\n\t\tend\n\t\t\n\t\talert.TextLabel.Text \t\t\t\t= textObject.text or textObject.Text or \"\"\n\t\talert.TextLabel.TextColor3 \t\t\t= textObject.textColor3 or textObject.Color or Color3.new(1,1,1)\n\t\talert.TextLabel.TextStrokeColor3 \t= textObject.textStrokeColor3 or Color3.new(0,0,0)\n\t\talert.TextLabel.Font\t\t\t\t= textObject.font or textObject.Font or Enum.Font.SourceSansBold\n\t\talert.TextLabel.BackgroundColor3\t= textObject.backgroundColor3 or Color3.new(1,1,1)\n\t\t\n\t\tlocal textBounds = game.TextService:GetTextSize(alert.TextLabel.Text,alert.TextLabel.TextSize,alert.TextLabel.Font,Vector2.new())\n\t\talert.TextLabel.Size = UDim2.new(0,textBounds.X + 20,1,0)\n\t\t\n\t\talert.Parent = script.Parent\n\t\talert.Visible = true\n\t\t\n\t\tif isNewAlert then\n\t\t\tlocal textTransparency  \t\t= textObject.textTransparency or 0\n\t\t\tlocal textStrokeTransparency \t= textObject.textStrokeTransparency or 0\n\t\t\t\n\t\t\tlocal backgroundTransparency \t= textObject.backgroundTransparency or 1\n\t\t\t\n\t\t\talert.TextLabel.TextTransparency \t\t= 1\n\t\t\talert.TextLabel.BackgroundTransparency\t= 1\n\t\t\talert.TextLabel.TextStrokeTransparency \t= 1\n\t\t\n\t\t\ttween(alert.TextLabel, {\"TextTransparency\", \"TextStrokeTransparency\", \"BackgroundTransparency\"}, {textTransparency, textStrokeTransparency, backgroundTransparency}, 0.5)\n\t\telse\n\t\t\talert.TextLabel.TextTransparency  \t\t= textObject.textTransparency or 0\n\t\t\talert.TextLabel.TextStrokeTransparency \t= textObject.textStrokeTransparency or 0\n\t\t\talert.TextLabel.BackgroundTransparency \t= textObject.backgroundTransparency or 1\t\t\t\n\t\tend\n\t\t\n\t\tspawn(function()\n\t\t\twait(duration)\n\t\t\t\n\t\t\tif textObject.id and alerts[textObject.id] then\n\t\t\t\tif alerts[textObject.id].alert == alert then\n\n\t\t\t\t\talerts[textObject.id] = nil\t\t\t\t\t\n\t\t\t\t\ttween(alert.TextLabel,{\"TextTransparency\", \"TextStrokeTransparency\", \"BackgroundTransparency\"}, {1, 1, 1}, 0.5)\n\t\t\t\t\tgame.Debris:AddItem(alert, 0.5)\n\t\t\t\tend\n\t\t\telseif alert and alert.Parent == script.Parent then\n\t\t\t\ttween(alert.TextLabel,{\"TextTransparency\", \"TextStrokeTransparency\", \"BackgroundTransparency\"}, {1, 1, 1}, 0.5)\n\t\t\t\tgame.Debris:AddItem(alert, 0.5)\t\t\t\t\n\t\t\tend\n\t\t\t\n\t\tend)\n\tend\t\n\t\n\tnetwork:create(\"alert\",\"BindableEvent\", \"Event\", module.alert)\n\tnetwork:connect(\"alertPlayerNotification\", \"OnClientEvent\", module.alert)\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/party.lua",
    "content": "-- local party menu \n-- berezaa\nlocal module = {}\n\nlocal localPlayer = game:GetService(\"Players\").LocalPlayer\nlocal gui = localPlayer.PlayerGui.gameUI.party\n\n\nfunction module.init(Modules)\n\t\n\tlocal network = Modules.network\n\tlocal tween = Modules.tween\n\tlocal fx = Modules.fx\n\tlocal utilities = Modules.utilities\n\t\n\tlocal isPartyLeader = false\n\tlocal playerCards = {}\n\t\n\tmodule.currentPartyInfo = nil\n\t\n\tnetwork:create(\"getCurrentPartyInfo\",\"BindableFunction\",\"OnInvoke\",function()\n\t\tif module.currentPartyInfo then\n\t\t\tmodule.currentPartyInfo.isClientPartyLeader = isPartyLeader\n\t\tend\n\t\treturn module.currentPartyInfo\n\tend)\n\n\tlocal function setInviteFrameSize()\n\t\t-- gui.contents.invite.Size = UDim2.new(0,250,0,50)\n\t\tlocal xSize = 100\n\t\t\n\t\tif gui.contents.invite.textBox.Visible then\n\t\t\txSize = xSize + 150\n\t\tend\n\t\tif gui.contents.invite.leave.Visible then\n\t\t\txSize = xSize + 50\n\t\tend\n\t\t\n\t\tgui.contents.invite.Size = UDim2.new(0,xSize,0,60)\n\tend\t\n\n\tlocal function updateInviteButton()\n\t\tif gui.contents.invite.textBox.Visible then\n\t\t\tgui.contents.invite.button.Active = true\n\t\t\tif game.Players:FindFirstChild(gui.contents.invite.textBox.Text) then\n\t\t\t\tgui.contents.invite.button.ImageColor3 = Color3.fromRGB(82, 255, 71)\n\t\t\t\tgui.contents.invite.button.detail.Text = \">\"\n\t\t\telse\n\t\t\t\tgui.contents.invite.button.ImageColor3 = Color3.fromRGB(247, 138, 64)\n\t\t\t\tgui.contents.invite.button.detail.Text = \"-\"\t\t\n\t\t\tend\t\t\t\t\t\n\t\telse\n\t\t\tgui.contents.invite.button.detail.Text = \"+\"\n\t\t\tif module.currentPartyInfo == nil or #module.currentPartyInfo.members < 6 then\n\t\t\t\tgui.contents.invite.button.ImageColor3 = Color3.fromRGB(93, 249, 249)\n\t\t\t\tgui.contents.invite.button.Active = true\n\t\t\telse\n\t\t\t\tgui.contents.invite.button.ImageColor3 = Color3.fromRGB(180,180,180)\n\t\t\t\tgui.contents.invite.button.Active = false\n\t\t\tend\t\n\t\tend\n\t\tsetInviteFrameSize()\n\n\tend\n\t\n\tupdateInviteButton()\n\t\n\tlocal lastSelectedPartyManifest\n\t\n\tgui.contents.invite.textBox.Changed:connect(updateInviteButton)\n\t\n\n\t\n\tgui.contents.invite.leave.MouseButton1Click:connect(function()\n\t\tgui.contents.invite.leave.ImageColor3 = Color3.new(0.7,0.7,0.7)\n\t\tnetwork:invokeServer(\"playerRequest_leaveParty\")\n\t\tgui.contents.invite.leave.ImageColor3 = Color3.fromRGB(246, 58, 63)\n\tend)\n\t\n\tlocal function closeInviteWindow()\n\t\tModules.focus.cleanup()\n\t\t\n\t\tlocal invite = gui.contents.invite\n\t\tinvite.button.Visible = true\n\t\tinvite.ImageLabel.Visible = true\n\t\t\n\t\tgui.contents.invite.textBox.Visible = false\n\t\ttween(gui.contents.invite,{\"ImageTransparency\"},0.7,0.3)\n\t\t\n\t\tif lastSelectedPartyManifest then\n\t\t\tif lastSelectedPartyManifest and lastSelectedPartyManifest.Parent then\n\t\t\t\ttween(lastSelectedPartyManifest,{\"ImageTransparency\",\"ImageColor3\"},{0.5,Color3.new(0,0,0)},0.5)\n\t\t\t\tlastSelectedPartyManifest.details.Visible = false\n\t\t\tend\t\n\t\t\tlastSelectedPartyManifest = nil\t\t\n\t\tend\n\t\t\n\t\tupdateInviteButton()\n\tend\n\t\n\tlocal function openInviteWindow()\n\t\tif module.currentPartyInfo == nil or #module.currentPartyInfo.members < 6 then\n\t\t\tgui.contents.invite.textBox.Text = \"\"\n\t\t\tgui.contents.invite.textBox.Visible = true\n\t\t\ttween(gui.contents.invite,{\"ImageTransparency\"},0,0.3)\n\t\tend\n\t\tif Modules.input.mode.Value == \"xbox\" then\n\t\t\tif game.GuiService.SelectedObject and game.GuiService.SelectedObject:IsDescendantOf(gui) then\n\t\t\t\tcloseInviteWindow()\n\t\t\telse\n\t\t\t\tModules.focus.change(gui)\n\t\t\t\tgame.GuiService.SelectedObject = gui.contents.invite\t\t\t\t\n\t\t\tend\n\t\tend\n\t\tupdateInviteButton()\n\tend\n\t\n\tgui.contents.invite.button.MouseButton1Click:connect(function()\n\t\tif gui.contents.invite.button.Active then\n\t\t\tlocal buttonText = gui.contents.invite.button.detail.Text\n\t\t\tif buttonText == \">\" then\n\t\t\t\tlocal success, reason = false, \"Could not find player\"\n\t\t\t\tlocal targetPlayer = game.Players:FindFirstChild(gui.contents.invite.textBox.Text)\n\t\t\t\tif targetPlayer then\n\t\t\t\t\tsuccess, reason = network:invokeServer(\"playerRequest_invitePlayerToMyParty\", targetPlayer)\n\t\t\t\tend\n\t\t\t\tlocal invite = gui.contents.invite\n\t\t\t\tlocal duration = 1\n\t\t\t\tif success then\n\t\t\t\t\tModules.notifications.alert({text = \"Invited \"..targetPlayer.Name..\" to the party.\"}, 2)\n\t\t\t\t\t\n\t\t\t\t\tinvite.button.Visible = false\n\t\t\t\t\tinvite.leave.Visible = false\n\t\t\t\t\tinvite.textBox.Visible = false\n\n\t\t\t\t\tinvite.ImageLabel.Visible = false\n\t\t\t\t\tlocal ribben = fx.statusRibbon(invite, \"Party invite sent!\", \"success\", duration, UDim2.new(0,0,0.5,0))\n\t\t\t\t\tribben.Size = UDim2.new(0,150,0,30)\n\t\t\t\t\tinvite.Size = UDim2.new(0,200,0,60)\n\t\t\t\t\t--gui.contents.invite.textBox.Text = \"\"\n\t\t\t\t\t\n\t\t\t\telseif reason then\n\t\t\t\t\tduration = 2\n\t\t\t\t\tinvite.button.Visible = false\n\t\t\t\t\tinvite.leave.Visible = false\n\t\t\t\t\tinvite.textBox.Visible = false\n\t\n\t\t\t\t\tinvite.ImageLabel.Visible = false\t\t\t\t\t\n\t\t\t\t\tlocal ribben = fx.statusRibbon(invite, reason, \"fail\", duration, UDim2.new(0,0,0.5,0))\n\t\t\t\t\tribben.Size = UDim2.new(0,150,0,30)\n\t\t\t\t\tinvite.Size = UDim2.new(0,200,0,60)\n\t\t\t\tend\n\t\t\t\twait(duration)\n\t\t\t\tcloseInviteWindow()\n\t\t\t\t--\n\t\t\telseif buttonText == \"-\" then\n\t\t\t\tcloseInviteWindow()\n\t\t\telseif buttonText == \"+\" then\n\t\t\t\topenInviteWindow()\n\t\t\tend\n\t\tend\n\tend)\n\t\n\t\n\tlocal function clearPlayerCard(card, i)\n\t\tif i == nil then\n\t\t\tfor e, playerCard in pairs(playerCards) do\n\t\t\t\tif playerCard == card then\n\t\t\t\t\ti = e\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\t\n\t\tif i then\t\n\t\t\tif card.manifest then\n\t\t\t\tcard.manifest:Destroy()\n\t\t\tend\n\t\t\tif card.connections then\n\t\t\t\tfor e,connection in pairs(card.connections) do\n\t\t\t\t\tconnection:disconnect()\n\t\t\t\tend\n\t\t\tend\n\t\t\tplayerCards[i] = nil\n\t\tend\t\n\tend\n\t\n\tlocal function clearParty()\n\t\tfor i,card in pairs(playerCards) do\n\t\t\tclearPlayerCard(card, i)\n\t\tend\t\t\n\t\tplayerCards = {}\n\tend\n\t\n\tlocal teleporting = false\n\t\n\tlocal function applyNameTag(manifest, player)\n\t\tlocal playerCharacter = player.Character and player.Character.PrimaryPart and player.Character\n\t\tlocal partyInfo = module.currentPartyInfo\n\t\tif partyInfo.teleportState == \"pending\" and partyInfo.teleportPosition and playerCharacter and utilities.magnitude(playerCharacter.PrimaryPart.Position - partyInfo.teleportPosition) <= 20 then\n\t\t\tmanifest.header.class.Image = \"rbxassetid://2528902744\"\n\t\t\tmanifest.header.class.Visible = true\n\t\t\tmanifest.header.class.ImageColor3 = Color3.new(0.1,1,0.1)\n\t\t\tmanifest.header.username.TextColor3 = Color3.new(0.1,1,0.1)\n\t\telse\n\t\t\tlocal class = player:FindFirstChild(\"class\") and player.class.Value:lower() or \"unknown\"\n\t\t\tif class:lower() ~= \"adventurer\" then\n\t\t\t\tmanifest.header.class.Image = \"rbxgameasset://Images/emblem_\"..class:lower()\n\t\t\t\tmanifest.header.class.Visible = true\n\t\t\telse\n\t\t\t\tmanifest.header.class.Visible = false\n\t\t\tend\t\t\t\n\t\t\tmanifest.header.class.ImageColor3 = Color3.new(1,1,1)\n\t\t\tmanifest.header.username.TextColor3 = Color3.new(1,1,1)\t\t\t\t\t\t\t\t\t\t\n\t\tend\t\t\t\t\t\t\n\tend\t\n\t\n\tlocal function updatePartyInfo(partyInfo)\n\t\t\n\t\tlocal isOldPartyInfo = false\n\t\t\n\t\tif partyInfo == nil then\n\t\t\tisOldPartyInfo = true\n\t\t\tpartyInfo = network:invokeServer(\"playerRequest_getMyPartyData\")\n\t\tend\n\t\t\n\t\tmodule.currentPartyInfo = partyInfo\n\t\tgui.contents.invite.leave.Visible = partyInfo ~= nil\n\t\t\n\t\tupdateInviteButton()\n\t\t\n\t\tif partyInfo then\n\t\t\t\n\t\t\tif not isOldPartyInfo then\n\n\t\t\t\tif partyInfo.status then\n\t\t\t\t\tModules.notifications.alert(partyInfo.status, 3)\n\t\t\t\t\tgame.StarterGui:SetCore(\"ChatMakeSystemMessage\", {Text = partyInfo.status.text; Color = partyInfo.status.textColor3 or Color3.new(0.7,0.7,0.7)})\n\t\t\t\tend\n\t\t\t\n\t\t\tend\n\t\t\t\n\t\t\tscript.partyTeleportBeacon.Enabled = false\n\t\t\tscript.partyTeleportBeacon.Adornee = nil\n\t\t\t\n\t\t\tif partyInfo.teleportState == \"pending\" then\n\t\t\t\tif not teleporting then\n\t\t\t\t\tteleporting = true\n\t\t\t\t\tlocal alert = {\n\t\t\t\t\t\ttext = \"The party leader has initiated a teleport. Group up!\";\n\t\t\t\t\t\ttextColor3 = Color3.new(1,1,1);\n\t\t\t\t\t\tbackgroundColor3 = Color3.new(0,1,0.2);\n\t\t\t\t\t\tbackgroundTransparency = 0;\n\t\t\t\t\t\ttextStrokeTransparency = 1;\n\t\t\t\t\t}\n\t\t\t\t\tModules.notifications.alert(alert, 4)\n\t\t\t\t\t-- update dat name tag to account for people entering/leaving the teleport zone\n\t\t\t\t\tspawn(function()\n\t\t\t\t\t\twhile module.currentPartyInfo and module.currentPartyInfo.teleportState == \"pending\" do\n\t\t\t\t\t\t\tfor i,partyMemberInfo in pairs(module.currentPartyInfo.members) do\n\t\t\t\t\t\t\t\tlocal player = partyMemberInfo.player\n\t\t\t\t\t\t\t\tlocal playerCard = playerCards[player]\n\t\t\t\t\t\t\t\tif playerCard and playerCard.manifest then\n\t\t\t\t\t\t\t\t\tapplyNameTag(playerCard.manifest, player)\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\twait(1/10)\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\t\t\t\t\t\n\t\t\t\tend\n\t\t\telseif partyInfo.teleportState == nil or partyInfo.teleportState == \"none\" then\n\t\t\t\tif teleporting then\n\t\t\t\t\tteleporting = false\n\t\t\t\t\tlocal alert = {\n\t\t\t\t\t\ttext = \"The party leader canceled the teleport.\";\n\t\t\t\t\t\ttextColor3 = Color3.new(1,1,1);\n\t\t\t\t\t\tbackgroundColor3 = Color3.new(0.9,0.1,0.1);\n\t\t\t\t\t\tbackgroundTransparency = 0;\n\t\t\t\t\t\ttextStrokeTransparency = 1;\n\t\t\t\t\t}\n\t\t\t\t\tModules.notifications.alert(alert, 3)\t\t\t\t\t\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal partyMembers = {}\n\t\t\t\n\t\t\t-- Make sure every member of the party has a card\n\t\t\tfor i,partyMemberInfo in pairs(partyInfo.members) do\n\t\t\t\t\n\t\t\t\tlocal player = partyMemberInfo.player\n\t\t\t\t\n\t\t\t\tif player == localPlayer then\n\t\t\t\t\tisPartyLeader = partyMemberInfo.isLeader\n\t\t\t\tend\t\t\t\n\t\t\t\t\n\t\t\t\tpartyMembers[player] = true\t\n\n\t\t\t\t\n\t\t\t\tif player ~= localPlayer and not playerCards[player] then\n\t\t\t\t\t\n\t\t\t\t\tlocal playerCard = {}\n\t\t\t\t\tplayerCard.player = player\n\t\t\t\t\t\n\t\t\t\t\tlocal manifest = script.playerInfo:Clone()\n\t\t\t\t\tmanifest.Name = player.Name\n\t\t\t\t\tmanifest.header.username.Text = player.Name\n\t\t\t\t\t\n\t\t\t\t\tapplyNameTag(manifest, player)\n\t\t\t\t\t\n\t\t\t\t\tmanifest.header.leader.Visible = partyMemberInfo.isLeader\n\t\t\t\t\t\n\t\t\t\t\tlocal connections = {}\n\t\t\t\t\t\n\n\t\t\t\t\t\n\t\t\t\t\tlocal function onMouseEnter()\n\t\t\t\t\t\tif lastSelectedPartyManifest and lastSelectedPartyManifest.Parent and lastSelectedPartyManifest ~= manifest then\n\t\t\t\t\t\t\ttween(lastSelectedPartyManifest,{\"ImageTransparency\",\"ImageColor3\"},{0.5,Color3.new(0,0,0)},0.5)\n\t\t\t\t\t\t\tlastSelectedPartyManifest.details.Visible = false\n\t\t\t\t\t\tend\t\n\t\t\t\t\t\tlastSelectedPartyManifest = manifest\n\t\t\t\t\t\tif not manifest.details.Visible then\n\t\t\t\t\t\t\ttween(manifest,{\"ImageTransparency\",\"ImageColor3\"},{0.5,Color3.new(0,0,0.7)},0.5)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\t\n\t\t\t\t\tlocal function onMouseLeave()\n\t\t\t\t\t\tif not manifest.details.Visible then\n\t\t\t\t\t\t\ttween(manifest,{\"ImageTransparency\",\"ImageColor3\"},{0.5,Color3.new(81, 81, 81)},0.5)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\n\t\t\t\t\tlocal function onActivated()\n\t\t\t\t\t\tif manifest.details.Visible then\n\t\t\t\t\t\t\tmanifest.details.Visible = false\n\t\t\t\t\t\t\tonMouseLeave()\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tif lastSelectedPartyManifest and lastSelectedPartyManifest.Parent and lastSelectedPartyManifest ~= manifest then\n\t\t\t\t\t\t\t\ttween(lastSelectedPartyManifest,{\"ImageTransparency\",\"ImageColor3\"},{0.5,Color3.new(81, 81, 81)},0.5)\n\t\t\t\t\t\t\t\tlastSelectedPartyManifest.details.Visible = false\n\t\t\t\t\t\t\tend\t\n\t\t\t\t\t\t\tlastSelectedPartyManifest = manifest\n\t\t\t\t\t\t\tmanifest.details.Visible = true\n\t\t\t\t\t\t\tmanifest.details.kick.Visible = isPartyLeader\n\t\t\t\t\t\t\ttween(manifest,{\"ImageTransparency\",\"ImageColor3\"},{0,Color3.new(0,0,0.85)},0.5)\t\t\t\t\t\t\t\t\n\t\t\t\t\t\tend\t\t\t\n\t\t\t\t\tend\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\tmanifest.MouseEnter:connect(onMouseEnter)\n\t\t\t\t\tmanifest.SelectionGained:connect(onMouseEnter)\n\t\t\t\t\t\n\t\t\t\t\tmanifest.MouseLeave:connect(onMouseLeave)\n\t\t\t\t\tmanifest.SelectionLost:connect(onMouseLeave)\n\t\t\t\t\t\n\t\t\t\t\tmanifest.Activated:connect(onActivated)\n\t\t\t\t\t\n\t\t\t\t\tmanifest.details.kick.Activated:connect(function()\n\t\t\t\t\t\tif player then\n\t\t\t\t\t\t\tlocal success, reason = network:invokeServer(\"playerRequest_leaveParty\", player)\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\t\t\t\t\t\n\t\t\t\t\tmanifest.details.friendRequest.Activated:connect(function()\n\t\t\t\t\t\tif player then\n\t\t\t\t\t\t\tgame.StarterGui:SetCore(\"PromptSendFriendRequest\", player)\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\n\t\t\t\t\tlocal function updatePlayerHealthDisplay()\n\t\t\t\t\t\tif player and player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart:FindFirstChild(\"health\") and player.Character.PrimaryPart:FindFirstChild(\"maxHealth\") then\n\t\t\t\t\t\t\tmanifest.healthBar.value.Size = UDim2.new(player.Character.PrimaryPart.health.Value/player.Character.PrimaryPart.maxHealth.Value,0,1,0)\n\t\t\t\t\t\t\tmanifest.healthBar.value.bar.ImageColor3 = Color3.fromRGB(255, 0, 4)\n\t\t\t\t\t\t\tmanifest.healthBar.title.Text = tostring(math.ceil(player.Character.PrimaryPart.health.Value)) .. \"/\" .. tostring(player.Character.PrimaryPart.maxHealth.Value)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tmanifest.healthBar.value.Size = UDim2.new(1,0,1,0)\n\t\t\t\t\t\t\tmanifest.healthBar.value.bar.ImageColor3 = Color3.fromRGB(130, 130, 130)\n\t\t\t\t\t\t\tmanifest.healthBar.title.Text = \"???\"\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\t\n\t\t\t\t\tlocal function characterAdded(character)\n\t\t\t\t\t\tupdatePlayerHealthDisplay()\n\t\t\t\t\t\tlocal startTime = tick()\n\t\t\t\t\t\trepeat wait() until player.Character.PrimaryPart or tick - startTime > 5 \n\t\t\t\t\t\tif player.Character.PrimaryPart then\n\t\t\t\t\t\t\tlocal healthValue = player.Character.PrimaryPart:WaitForChild(\"health\",5)\n\t\t\t\t\t\t\tlocal maxHealthValue = player.Character.PrimaryPart:WaitForChild(\"maxHealth\",5)\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\ttable.insert(connections, healthValue.Changed:connect(updatePlayerHealthDisplay)) \n\t\t\t\t\t\t\ttable.insert(connections, maxHealthValue.Changed:connect(updatePlayerHealthDisplay))\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\t\n\t\t\t\t\tif player.Character then\n\t\t\t\t\t\tcharacterAdded(player.Character)\n\t\t\t\t\tend\n\t\t\t\t\t\n\t\t\t\t\ttable.insert(connections, player.CharacterAdded:connect(characterAdded))\n\t\t\t\t\t\n\n\t\t\t\t\t\n\t\t\t\t\tplayerCard.manifest = manifest\n\t\t\t\t\tplayerCard.connections = connections\n\t\t\t\t\t--table.insert(playerCards, playerCard)\n\t\t\t\t\t\n\t\t\t\t\tplayerCards[player] = playerCard\n\t\t\t\t\t\n\t\t\t\t\tmanifest.Parent = gui.contents\n\t\t\t\t\tmanifest.Visible = true\n\t\t\t\tend\n\t\t\t\t\n\n\t\t\t\t\n\t\t\tend\n\t\t\t\n\t\t\t-- clear any cards that are no longer party members\n\t\t\tfor i,playerCard in pairs(playerCards) do\n\t\t\t\tif not partyMembers[playerCard.player] then\n\t\t\t\t\tclearPlayerCard(playerCard, i)\n\t\t\t\tend\n\t\t\tend\n\t\t\t\n\t\t\t-- other gui stuff\n\t\t\tif #module.currentPartyInfo.members >= 6 then\n\t\t\t\t\n\t\t\t\tcloseInviteWindow()\n\t\t\tend\n\t\t\t\n\t\telse -- not in a party\n\t\t\tclearParty()\n\t\tend\n\tend\n\t\n\tupdatePartyInfo()\n\tnetwork:connect(\"signal_myPartyDataChanged\", \"OnClientEvent\", updatePartyInfo)\n\t\n\t-- handle party requests\n\t\n\tnetwork:connect(\"signal_playerInvitedToParty\", \"OnClientEvent\", function(playerInviting, inviteId)\n\t\tif playerInviting then\n\t\t\tlocal accepted = Modules.prompting.prompt(playerInviting.Name..\" wants you to join their party.\")\n\t\t\tif accepted then\n\t\t\t\tlocal success, reason = network:invokeServer(\"playerRequest_acceptMyPartyInvitation\", inviteId)\n\t\t\tend\n\t\tend\n\tend)\n\t\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/playerInfo.lua",
    "content": "-- Player name, health, mana, etc. display\n-- berezaa\nlocal module = {}\n\n\nlocal characterPrimaryPart \n\nlocal player = game:GetService(\"Players\").LocalPlayer\nlocal gui = player.PlayerGui.gameUI.playerInfo\nlocal selected = false\n\n\n-- todo\nlocal Rand = Random.new(os.time())\n\nfunction module.init(Modules)\n\t\n\tlocal network = Modules.network\n\t\n\tlocal fx = Modules.fx\n\t\n\tlocal oldxp = 0\n\n\tlocal ColorEffect = Instance.new(\"ColorCorrectionEffect\")\n\tColorEffect.Name = \"DamageColor\"\n\tColorEffect.Parent = game.Lighting\n\t\n\tlocal BlurEffect = Instance.new(\"BlurEffect\")\n\tBlurEffect.Name = \"DamageBlur\"\n\tBlurEffect.Parent = game.Lighting\n\tBlurEffect.Size = 0\n\t\n\tlocal tween = Modules.tween\n\t\n\t--gui.header.username.value.Text = game.Players.LocalPlayer.Name\n\t\n\tlocal function updateNameTag(class)\n\t\tlocal label = gui.header.username.value\n\t\tlabel.Text = game.Players.LocalPlayer.Name\n\t\t\n\t\tlocal xSize = game.TextService:GetTextSize(label.Text, label.TextSize, label.Font, Vector2.new()).X + 15\n\t\t\n\t\tclass = string.lower(class or network:invoke(\"getCacheValueByNameTag\", \"class\") or \"Unknown\")\n\t\tif class:lower() ~= \"adventurer\" then\n\t\t\tgui.header.username.icon.Image = \"rbxgameasset://Images/emblem_\"..class:lower()\n\t\t\tgui.header.username.icon.Visible = true\n\t\t\tlabel.Size = UDim2.new(1, -25,1, 0)\n\t\t\txSize = xSize + 25\n\t\telse\n\t\t\tgui.header.username.icon.Visible = false\n\t\t\tlabel.Size = UDim2.new(1, 0,1, 0)\n\t\tend\n\t\tgui.header.username.Size = UDim2.new(0, xSize + 10, 0, 26 + 10)\n\tend\n\tupdateNameTag()\n\n\n\t\n\trepeat wait() until game.Players.LocalPlayer.Character\n\tlocal character = game.Players.LocalPlayer.Character\n\twhile not character.PrimaryPart and character.Parent and character:IsDescendantOf(workspace) do\n\t\tlocal primaryPart = character:WaitForChild(\"hitbox\", 1)\n\t\t\n\t\tif primaryPart then\n\t\t\tcharacter.PrimaryPart = primaryPart\n\t\t\tbreak\n\t\telse\n\t\t\twarn(\"Waiting for PrimaryPart in\", script.Name)\n\t\tend\n\tend\n\t\n\tcharacterPrimaryPart = character.PrimaryPart\n\n\tcharacterPrimaryPart:WaitForChild(\"health\")\n\t\n\tlocal lastHealth = characterPrimaryPart.health.Value\n\t\n\tlocal function healthRefresh()\n\t\tlocal delta = math.clamp(characterPrimaryPart.health.Value - lastHealth, -characterPrimaryPart.maxHealth.Value * 0.25, characterPrimaryPart.maxHealth.Value * 0.25) / (characterPrimaryPart.maxHealth.Value * 0.25)\n\t\tlastHealth = characterPrimaryPart.health.Value\n\t\t\n\t\tlocal percent = math.clamp(characterPrimaryPart.health.Value / characterPrimaryPart.maxHealth.Value, 0, 1) \t\t\n\t\tlocal manaPercent = math.clamp(characterPrimaryPart.mana.Value / characterPrimaryPart.maxMana.Value, 0, 1)\n\t\t\n\t\tgui.content.healthBar.value.Size = UDim2.new(percent,0,1,0)\n\t\tgui.content.healthBar.title.Text = math.floor(characterPrimaryPart.health.Value + 0.5) .. \"/\" .. math.floor(characterPrimaryPart.maxHealth.Value + 0.5)\n\t\t\n\t\tgui.content.manaBar.value.Size = UDim2.new(manaPercent,0,1,0)\n\t\tgui.content.manaBar.title.Text = math.floor(characterPrimaryPart.mana.Value + 0.5) .. \"/\" .. math.floor(characterPrimaryPart.maxMana.Value + 0.5)\n\t\t\n\t\tif delta < 0 then\n\t\t\tlocal thresh = (0.9) * (math.abs(delta))\n\t\t\tlocal duration = 0.15 + thresh / 1.4\n\t\t\t\n\t\t\ttween(ColorEffect,{\"TintColor\",\"Contrast\"},{Color3.fromRGB(255,255 - thresh * 150,255 - thresh * 150),thresh/3},duration/2)\n\t\t\ttween(BlurEffect,{\"Size\"},thresh * 5,duration/2)\n\t\t\tspawn(function()\n\t\t\t\twait(duration/2)\n\t\t\t\ttween(ColorEffect,{\"TintColor\",\"Contrast\"},{Color3.fromRGB(255,255,255),0},duration/2)\n\t\t\t\ttween(BlurEffect,{\"Size\"},0,duration/2)\n\t\t\tend)\n\t\t\tspawn(function()\n\t\t\t\tfor i=1,3 do\n\t\t\t\t\tgui.content.healthBarUnder.Visible = true\n\t\t\t\t\twait(0.08)\n\t\t\t\t\tgui.content.healthBarUnder.Visible = false\n\t\t\t\t\twait(0.08)\n\t\t\t\tend\n\t\t\tend)\n\t\telse\n\t\t\tif characterPrimaryPart.health.Value - lastHealth > 5 and delta > 0.01 then\n\t\t\t\tlocal thresh = 0.3 + math.abs(delta) \n\t\t\t\tlocal duration = thresh / 1.4 \n\t\t\t\ttween(ColorEffect,{\"TintColor\",\"Contrast\"},{Color3.fromRGB(255 - thresh * 150,255,255 - thresh * 150),-thresh/3},duration/2)\n\t\t\t\tspawn(function()\n\t\t\t\t\twait(duration/1.5)\n\t\t\t\t\ttween(ColorEffect,{\"TintColor\",\"Contrast\"},{Color3.fromRGB(255,255,255),0},duration/2)\n\t\t\t\tend)\t\n\t\t\tend\t\t\n\t\tend\n\tend\t\n\t\n\tcharacterPrimaryPart.health.Changed:connect(healthRefresh)\n\tcharacterPrimaryPart.mana.Changed:connect(healthRefresh)\n\tcharacterPrimaryPart.maxHealth.Changed:connect(healthRefresh)\n\tcharacterPrimaryPart.maxMana.Changed:connect(healthRefresh)\n\t\n\thealthRefresh()\t\n\t\n--\tgui.header.xp.Visible = true\n\t\n\tlocal levels = Modules.levels\n\t\n\tlocal network = Modules.network\n\t\n\tlocal function setup()\n\t\tlocal value = network:invoke(\"getCacheValueByNameTag\", \"exp\")\n\t\tlocal level = network:invoke(\"getCacheValueByNameTag\", \"level\")\n\t\t\n\t\tlocal label = gui.header.level.value\n\t\tlabel.Text = \"Lvl. \"..level\n\t\t\n\t\tlocal xSize = game.TextService:GetTextSize(label.Text, label.TextSize, label.Font, Vector2.new()).X + 16\n\t\tgui.header.level.Size = UDim2.new(0, xSize + 10, 0, 26 + 10)\n\t\t\n\t\tlocal xp \t\t\t\t\t\t= value\n\t\tlocal needed \t\t\t\t\t= math.floor(levels.getEXPToNextLevel(level))\t\n\t\toldxp \t\t\t\t\t\t\t= value\n\t\tgui.header.xp.title.Text \t= \"XP: \" .. xp .. \"/\" .. needed\n\t\tgui.header.xp.value.Size \t= UDim2.new(xp/needed,0,1,0)\n\t\tgui.header.xp.instant.Size = gui.header.xp.value.Size\n\t\t--[[\n\t\tlocal gold = network:invoke(\"getCacheValueByNameTag\", \"gold\")\n\t\tgui.header.gold.Text = \"$\"..gold\n\t\t]]\n\tend\n\t\n\tlocal function onDataChange(key, value)\n\t\tif key == \"class\" then\n\t\t\tupdateNameTag(value)\n\t\telseif key == \"gold\" then\n\n\t--\t\tif game.ReplicatedStorage:FindFirstChild(\"sounds\") and game.ReplicatedStorage.sounds:FindFirstChild(\"coins\") then\n\t--\t\t\tgame.ReplicatedStorage.sounds.coins:Play()\n\t--\t\tend\n\n\t\telseif key == \"level\" then\n\t\t\tlocal col = gui.header.xp.value.ImageColor3\n\t\t\tgui.header.xp.ImageColor3 = col\n\t\t\ttween(gui.header.xp, {\"ImageColor3\"},Color3.new(col.r + 0.2, col.g + 0.2, col.b + 0.2),0.4) \n\t\t\tgui.header.xp.pulse.ImageTransparency = 0\n\t\t\tgui.header.xp.pulse.ImageColor3 = col\n\t\t\tgui.header.xp.pulse.Size = UDim2.new(1,0,1,0)\n\t\t\tgui.header.xp.pulse.Visible = true\n\t\t\ttween(gui.header.xp.pulse,{\"Size\",\"ImageTransparency\"},{UDim2.new(1,140,1,140),1},0.5)\n\t\t\t\n\t\t\twait(0.4)\n\t\t\ttween(gui.header.xp,{\"ImageColor3\"},Color3.fromRGB(15, 15, 15),1)\n\t\telseif key == \"exp\" then\n\t\t\tlocal level = network:invoke(\"getCacheValueByNameTag\", \"level\")\n\t\t\tlocal label = gui.header.level.value\n\t\t\tlabel.Text = \"Lvl. \"..level\n\t\t\t\n\t\t\tlocal xSize = game.TextService:GetTextSize(label.Text, label.TextSize, label.Font, Vector2.new()).X + 16\n\t\t\tgui.header.level.Size = UDim2.new(0, xSize + 10, 0, 26 + 10)\n\t\t\t--local xp = math.floor(levels.getEXPPastCurrentLevel(value))\n\t\t\tlocal xp = value\n\t\t\tlocal needed = math.floor( levels.getEXPToNextLevel(level))\n\t\t\tgui.header.xp.title.Text = \"XP: \" .. math.floor(xp) .. \"/\" .. needed\n\t\t\n\t\t\t-- ahh crying internally\n\t\t\t\n\t\t\tlocal change = xp - oldxp\n\n\t\t\tlocal notice = gui.content.noticeTemplate:Clone()\n\t\t\tnotice.Name = \"Notice\"\n\t\t\tnotice.TextTransparency = 1\n\t\t\tnotice.TextStrokeTransparency = 1\n\t\t\tnotice.Parent = gui.content\n\t\t\tnotice.Visible = true\n\t\t\tnotice.Text = \"+\"..math.floor(change)..\" EXP\"\t\n\t\t\tnotice.Position = UDim2.new(1,Rand:NextInteger(-10,50),0,Rand:NextInteger(30,70))\n\t\t\tModules.tween(notice,{\"Position\"},notice.Position + UDim2.new(0,0,0,-100),3)\n\t\t\tModules.tween(notice,{\"TextTransparency\",\"TextStrokeTransparency\"},{0,0.7},1.5)\n\t\t\t\n\t\t\tlocal sampleParticle = gui.header.xp.value.tip.sample\n\t\t\tfor i=1,6 do\n\t\t\t\tlocal particle = sampleParticle:Clone()\n\t\t\t\tparticle.Rotation = math.random(1,90)\n\t\t\t\tparticle.Parent = sampleParticle.Parent\n\t\t\t\tparticle.Visible = true\n\t\t\t\ttween(particle,{\"Rotation\", \"Position\", \"Size\", \"BackgroundTransparency\"},\n\t\t\t\t{particle.Rotation + math.random(100,200), UDim2.new(0, math.random(3,25),0.5,math.random(-20,20)), UDim2.new(0,16,0,16), 1},math.random(60,130)/100)\n\t\t\t\tgame.Debris:AddItem(particle,1.5)\n\t\t\tend\n\t\t\t\n\t\t\tif xp < oldxp then\n\t\t\t\tModules.tween(gui.header.xp.value,{\"Size\"},{UDim2.new(1,0,1,0)},0.5)\n\t\t\t\tgui.header.xp.instant.Size = UDim2.new(1,0,1,0)\n\t\t\t\tspawn(function()\n\t\t\t\t\twait(0.25)\n\t\t\t\t\tgui.header.xp.value.Size = UDim2.new(1,0,0,0)\n\t\t\t\t\tlocal goal = UDim2.new(xp/needed,0,1,0)\n\t\t\t\t\tModules.tween(gui.header.xp.value,{\"Size\"},{goal},0.5)\n\t\t\t\t\tgui.header.xp.instant.Size = goal\n\t\t\t\tend)\n\t\t\telse\n\t\t\t\tModules.tween(gui.header.xp.value,{\"Size\"},{UDim2.new(xp/needed,0,1,0)},1)\n\t\t\t\tgui.header.xp.instant.Size = UDim2.new(xp/needed,0,1,0)\n\t\t\tend\n\t\t\t\n--\t\t\tgui.header.xp.Visible = true\n\t\t\toldxp = value\n\t\t\t\n\t\t\tspawn(function()\n\t\t\t\twait(0.5)\n\t\t\t\tModules.tween(notice,{\"TextTransparency\",\"TextStrokeTransparency\"},{1,1},1.5)\n\t\t\t\twait(3)\n\t\t\t\tif oldxp == value and not selected then\n\t\t\t\t--\tgui.header.xp.Visible = false\n\t\t\t\tend\n\t\t\t\tend)\n\t\t\t\t\n\t\tend\n\tend\t\n\t\n\tgui.header.xp.MouseEnter:connect(function()\n\t\tgui.header.xp.title.Visible = true\n\t\tselected = true\n\tend)\n\t\n\tgui.header.xp.MouseLeave:connect(function()\n\t\tgui.header.xp.title.Visible = false\n\t\tselected = false\n\tend)\n\t\n\t\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onDataChange)\n\tsetup()\nend\n\nreturn module"
  },
  {
    "path": "src/StarterGui/playerInfoClone.lua",
    "content": "-- Player name, health, mana, etc. display\n-- berezaa\nlocal module = {}\n\nlocal characterPrimaryPart\nlocal ui = script.Parent.gameUI.playerInfo\n\nlocal selected = false\n\n-- todo\nlocal Rand = Random.new(os.time())\n\nfunction module.init(Modules)\n\n\tlocal network = Modules.network\n\tlocal fx = Modules.fx\n\tlocal oldxp = 0\n\n\tlocal ColorEffect = Instance.new(\"ColorCorrectionEffect\")\n\tColorEffect.Name = \"DamageColor\"\n\tColorEffect.Parent = game.Lighting\n\n\tlocal BlurEffect = Instance.new(\"BlurEffect\")\n\tBlurEffect.Name = \"DamageBlur\"\n\tBlurEffect.Parent = game.Lighting\n\tBlurEffect.Size = 0\n\n\tlocal tween = Modules.tween\n\n\t--ui.header.username.value.Text = game.Players.LocalPlayer.Name\n\n\tlocal function updateNameTag(class)\n\t\tlocal label = ui.header.username.value\n\t\tlabel.Text = game.Players.LocalPlayer.Name\n\n\t\tlocal xSize = game.TextService:GetTextSize(label.Text, label.TextSize, label.Font, Vector2.new()).X + 15\n\n\t\tclass = string.lower(class or network:invoke(\"getCacheValueByNameTag\", \"class\") or \"Unknown\")\n\t\tif class:lower() ~= \"adventurer\" then\n\t\t\tui.header.username.icon.Image = \"rbxgameasset://Images/emblem_\"..class:lower()\n\t\t\tui.header.username.icon.Visible = true\n\t\t\tlabel.Size = UDim2.new(1, -25,1, 0)\n\t\t\txSize = xSize + 25\n\t\telse\n\t\t\tui.header.username.icon.Visible = false\n\t\t\tlabel.Size = UDim2.new(1, 0,1, 0)\n\t\tend\n\t\tui.header.username.Size = UDim2.new(0, xSize + 10, 0, 26 + 10)\n\tend\n\tspawn(updateNameTag)\n\n\n\n\trepeat wait() until game.Players.LocalPlayer.Character\n\tlocal character = game.Players.LocalPlayer.Character\n\twhile not character.PrimaryPart and character.Parent and character:IsDescendantOf(workspace) do\n\t\tlocal primaryPart = character:WaitForChild(\"hitbox\", 1)\n\n\t\tif primaryPart then\n\t\t\tcharacter.PrimaryPart = primaryPart\n\t\t\tbreak\n\t\tend\n\tend\n\n\tcharacterPrimaryPart = character.PrimaryPart\n\n\tcharacterPrimaryPart:WaitForChild(\"health\")\n\n\tlocal lastHealth = characterPrimaryPart.health.Value\n\n\tlocal function healthRefresh()\n\t\tlocal delta = math.clamp(characterPrimaryPart.health.Value - lastHealth, -characterPrimaryPart.maxHealth.Value * 0.25, characterPrimaryPart.maxHealth.Value * 0.25) / (characterPrimaryPart.maxHealth.Value * 0.25)\n\t\tlastHealth = characterPrimaryPart.health.Value\n\n\t\tlocal percent = math.clamp(characterPrimaryPart.health.Value / characterPrimaryPart.maxHealth.Value, 0, 1)\n\t\tlocal manaPercent = math.clamp(characterPrimaryPart.mana.Value / characterPrimaryPart.maxMana.Value, 0, 1)\n\n\t\tui.content.healthBar.value.Size = UDim2.new(percent,0,1,0)\n\t\tui.content.healthBar.title.Text = math.floor(characterPrimaryPart.health.Value + 0.5) .. \"/\" .. math.floor(characterPrimaryPart.maxHealth.Value + 0.5)\n\n\t\tui.content.manaBar.value.Size = UDim2.new(manaPercent,0,1,0)\n\t\tui.content.manaBar.title.Text = math.floor(characterPrimaryPart.mana.Value + 0.5) .. \"/\" .. math.floor(characterPrimaryPart.maxMana.Value + 0.5)\n\n\t\tif delta < 0 then\n\t\t\tlocal thresh = (0.9) * (math.abs(delta))\n\t\t\tlocal duration = 0.15 + thresh / 1.4\n\n\t\t\ttween(ColorEffect,{\"TintColor\",\"Contrast\"},{Color3.fromRGB(255,255 - thresh * 150,255 - thresh * 150),thresh/3},duration/2)\n\t\t\ttween(BlurEffect,{\"Size\"},thresh * 5,duration/2)\n\t\t\tspawn(function()\n\t\t\t\twait(duration/2)\n\t\t\t\ttween(ColorEffect,{\"TintColor\",\"Contrast\"},{Color3.fromRGB(255,255,255),0},duration/2)\n\t\t\t\ttween(BlurEffect,{\"Size\"},0,duration/2)\n\t\t\tend)\n\t\t\tspawn(function()\n\t\t\t\tfor i=1,3 do\n\t\t\t\t\tui.content.healthBarUnder.Visible = true\n\t\t\t\t\twait(0.08)\n\t\t\t\t\tui.content.healthBarUnder.Visible = false\n\t\t\t\t\twait(0.08)\n\t\t\t\tend\n\t\t\tend)\n\t\telse\n\t\t\tif characterPrimaryPart.health.Value - lastHealth > 5 and delta > 0.01 then\n\t\t\t\tlocal thresh = 0.3 + math.abs(delta)\n\t\t\t\tlocal duration = thresh / 1.4\n\t\t\t\ttween(ColorEffect,{\"TintColor\",\"Contrast\"},{Color3.fromRGB(255 - thresh * 150,255,255 - thresh * 150),-thresh/3},duration/2)\n\t\t\t\tspawn(function()\n\t\t\t\t\twait(duration/1.5)\n\t\t\t\t\ttween(ColorEffect,{\"TintColor\",\"Contrast\"},{Color3.fromRGB(255,255,255),0},duration/2)\n\t\t\t\tend)\n\t\t\tend\n\t\tend\n\tend\n\n\tcharacterPrimaryPart.health.Changed:connect(healthRefresh)\n\tcharacterPrimaryPart.mana.Changed:connect(healthRefresh)\n\tcharacterPrimaryPart.maxHealth.Changed:connect(healthRefresh)\n\tcharacterPrimaryPart.maxMana.Changed:connect(healthRefresh)\n\n\thealthRefresh()\n\n\tui.header.xp.Visible = true\n\n\tlocal levels = Modules.levels\n\n\tlocal network = Modules.network\n\n\tlocal function setup()\n\t\tlocal value = network:invoke(\"getCacheValueByNameTag\", \"exp\")\n\t\tlocal level = network:invoke(\"getCacheValueByNameTag\", \"level\")\n\n\t\tlocal label = ui.header.level.value\n\t\tlabel.Text = \"Lvl. \"..level\n\n\t\tlocal xSize = game.TextService:GetTextSize(label.Text, label.TextSize, label.Font, Vector2.new()).X + 16\n\t\tui.header.level.Size = UDim2.new(0, xSize + 10, 0, 26 + 10)\n\n\t\tlocal xp \t\t\t\t\t\t= value\n\t\tlocal needed \t\t\t\t\t= math.floor(levels.getEXPToNextLevel(level))\n\t\toldxp \t\t\t\t\t\t\t= value\n\t\tui.header.xp.title.Text \t= \"XP: \" .. xp .. \"/\" .. needed\n\t\tui.header.xp.value.Size \t= UDim2.new(xp/needed,0,1,0)\n\t\tui.header.xp.instant.Size = ui.header.xp.value.Size\n\t\t--[[\n\t\tlocal gold = network:invoke(\"getCacheValueByNameTag\", \"gold\")\n\t\tui.header.gold.Text = \"$\"..gold\n\t\t]]\n\tend\n\n\tlocal function onDataChange(key, value)\n\t\tif key == \"class\" then\n\t\t\tupdateNameTag(value)\n\t\telseif key == \"gold\" then\n\n\t--\t\tif game.ReplicatedStorage:FindFirstChild(\"sounds\") and game.ReplicatedStorage.sounds:FindFirstChild(\"coins\") then\n\t--\t\t\tgame.ReplicatedStorage.sounds.coins:Play()\n\t--\t\tend\n\n\t\telseif key == \"level\" then\n\t\t\tlocal col = ui.header.xp.value.ImageColor3\n\t\t\tui.header.xp.ImageColor3 = col\n\t\t\ttween(ui.header.xp, {\"ImageColor3\"},Color3.new(col.r + 0.2, col.g + 0.2, col.b + 0.2),0.4)\n\t\t\tui.header.xp.pulse.ImageTransparency = 0\n\t\t\tui.header.xp.pulse.ImageColor3 = col\n\t\t\tui.header.xp.pulse.Size = UDim2.new(1,0,1,0)\n\t\t\tui.header.xp.pulse.Visible = true\n\t\t\ttween(ui.header.xp.pulse,{\"Size\",\"ImageTransparency\"},{UDim2.new(1,140,1,140),1},0.5)\n\n\t\t\twait(0.4)\n\t\t\ttween(ui.header.xp,{\"ImageColor3\"},Color3.fromRGB(15, 15, 15),1)\n\t\telseif key == \"exp\" then\n\t\t\tlocal level = network:invoke(\"getCacheValueByNameTag\", \"level\")\n\t\t\tlocal label = ui.header.level.value\n\t\t\tlabel.Text = \"Lvl. \"..level\n\n\t\t\tlocal xSize = game.TextService:GetTextSize(label.Text, label.TextSize, label.Font, Vector2.new()).X + 16\n\t\t\tui.header.level.Size = UDim2.new(0, xSize + 10, 0, 26 + 10)\n\t\t\t--local xp = math.floor(levels.getEXPPastCurrentLevel(value))\n\t\t\tlocal xp = value\n\t\t\tlocal needed = math.floor( levels.getEXPToNextLevel(level))\n\t\t\tui.header.xp.title.Text = \"XP: \" .. math.floor(xp) .. \"/\" .. needed\n\n\t\t\t-- ahh crying internally\n\n\t\t\tlocal change = xp - oldxp\n\t\t\t--[[\n\t\t\tlocal notice = ui.content.noticeTemplate:Clone()\n\t\t\tnotice.Name = \"Notice\"\n\t\t\tnotice.TextTransparency = 1\n\t\t\tnotice.TextStrokeTransparency = 1\n\t\t\tnotice.Parent = ui.content\n\t\t\tnotice.Visible = true\n\t\t\tnotice.Text = \"+\"..math.floor(change)..\" EXP\"\n\t\t\tnotice.Position = UDim2.new(1,Rand:NextInteger(-10,50),0,Rand:NextInteger(30,70))\n\t\t\tModules.tween(notice,{\"Position\"},notice.Position + UDim2.new(0,0,0,-100),3)\n\t\t\tModules.tween(notice,{\"TextTransparency\",\"TextStrokeTransparency\"},{0,0.7},1.5)\n\t\t\t]]\n\n\t\t\tlocal sampleParticle = ui.header.xp.value.tip.sample\n\t\t\tfor i=1,10 do\n\t\t\t\tlocal particle = sampleParticle:Clone()\n\t\t\t\tparticle.Rotation = math.random(1,90)\n\t\t\t\tparticle.Parent = sampleParticle.Parent\n\t\t\t\tparticle.Visible = true\n\t\t\t\ttween(particle,{\"Rotation\", \"Position\", \"Size\", \"BackgroundTransparency\"},\n\t\t\t\t{particle.Rotation + math.random(100,200), UDim2.new(0, math.random(3,25),0.5,math.random(-20,20)), UDim2.new(0,16,0,16), 1},math.random(60,130)/100)\n\t\t\t\tgame.Debris:AddItem(particle,1.5)\n\t\t\tend\n\n\t\t\tif xp < oldxp then\n\t\t\t\tModules.tween(ui.header.xp.value,{\"Size\"},{UDim2.new(1,0,1,0)},0.5)\n\t\t\t\tui.header.xp.instant.Size = UDim2.new(1,0,1,0)\n\t\t\t\tspawn(function()\n\t\t\t\t\twait(0.25)\n\t\t\t\t\tui.header.xp.value.Size = UDim2.new(1,0,0,0)\n\t\t\t\t\tlocal goal = UDim2.new(xp/needed,0,1,0)\n\t\t\t\t\tModules.tween(ui.header.xp.value,{\"Size\"},{goal},0.5)\n\t\t\t\t\tui.header.xp.instant.Size = goal\n\t\t\t\tend)\n\t\t\telse\n\t\t\t\tModules.tween(ui.header.xp.value,{\"Size\"},{UDim2.new(xp/needed,0,1,0)},1)\n\t\t\t\tui.header.xp.instant.Size = UDim2.new(xp/needed,0,1,0)\n\t\t\tend\n\n\t\t\tui.header.xp.Visible = true\n\t\t\toldxp = value\n\t\t\t--[[\n\t\t\tspawn(function()\n\t\t\t\twait(0.5)\n\t\t\t\tModules.tween(notice,{\"TextTransparency\",\"TextStrokeTransparency\"},{1,1},1.5)\n\t\t\t\twait(3)\n\t\t\t\tif oldxp == value and not selected then\n\t\t\t\t--\tui.header.xp.Visible = false\n\t\t\t\tend\n\t\t\t\tend)\n\t\t\t]]\n\t\tend\n\tend\n\n\tui.header.xp.MouseEnter:connect(function()\n\t\tui.header.xp.title.Visible = true\n\t\tselected = true\n\tend)\n\n\tui.header.xp.MouseLeave:connect(function()\n\t\tui.header.xp.title.Visible = false\n\t\tselected = false\n\tend)\n\n\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onDataChange)\n\tspawn(setup)\nend\n\nreturn module"
  },
  {
    "path": "src/StarterGui/playerInteract.lua",
    "content": "local module = {}\n\nlocal activePlayer\nlocal ui = script.Parent.gameUI.interactFrame.interact\n\nfunction module.init(Modules)\n\n\tlocal network = Modules.network\n\n\t-- todo: fix\n\tlocal animationInterface = Modules.animationInterface\n\n\tfunction module.show(player)\n\n\t\tactivePlayer = player\n\n\t\tif Modules.input.mode.Value ~= \"mobile\" then\n\t\t\tui.contents.username.Text = player.Name\n\t\t\tui.contents.level.Text = \"Lvl. \"..tostring(player:FindFirstChild(\"level\") and player.level.Value or 0)\n\t\t\tui.interact.Visible = true\n\t\t\tui.options.Visible = false\n\t\t\tui.Size = UDim2.new(0, 700,0, 105)\n\t\t\tui.Visible = true\n\t\tend\n\tend\n\n\tfunction module.activate(player)\n\t\tModules.inspectPlayer.open(player)\n\t\tanimationInterface:replicatePlayerAnimationSequence(\"emoteAnimations\", \"interaction_greeting\")\n\tend\n\n\tfunction module.hide()\n\t\tactivePlayer = nil\n\t\tui.Visible = false\n\tend\n\tmodule.hide()\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/products.lua",
    "content": "local module = {}\n\nlocal runService = game:GetService(\"RunService\")\nlocal ui = script.Parent.gameUI.products\n\nfunction module.open()\n\tui.Visible = not ui.Visible\nend\n\n-- module.remapTarget\n\nlocal userSettings\n\nfunction module.init(Modules)\n\n\tlocal network = Modules.network\n\n\tui.close.Activated:connect(function()\n\t\tModules.focus.toggle(ui)\n\tend)\n\n\tfunction module.open()\n\t\tif not ui.Visible then\n\t\t\tui.UIScale.Scale = (Modules.input.menuScale or 1) * 0.75\n\t\t\tModules.tween(ui.UIScale, {\"Scale\"}, (Modules.input.menuScale or 1), 0.5, Enum.EasingStyle.Bounce)\n\t\tend\n\t\tModules.focus.toggle(ui)\n\tend\n\n\tfor i,product in pairs(ui.contents:GetChildren()) do\n\t\tif product:FindFirstChild(\"productId\") and product:FindFirstChild(\"buy\") then\n\t\t\tproduct.buy.Activated:connect(function()\n\t\t\t\tgame.MarketplaceService:PromptProductPurchase(game.Players.LocalPlayer, product.productId.Value)\n\t\t\tend)\n\t\tend\n\tend\n\n\t--network:invokeServer(\"requestChangePlayerSetting\", \"clearingInteraction\", true)\n\nend\n\nreturn module"
  },
  {
    "path": "src/StarterGui/prompting.lua",
    "content": "-- Input prompt by Locard, modified by berezaa\n-- Imported from Miner's Haven\nlocal module = {}\n\nlocal runService = game:GetService(\"RunService\")\n\nlocal promptOut\nlocal promptFrame = script.Parent.gameUI.leftBar.promptFrame\nlocal buttonCons = {}\nlocal currentDecision\n\nfunction module.forceClose()\n\tif promptOut then\n\t\tcurrentDecision = false\n\tend\nend\n\nfunction module.isPrompting()\n\treturn promptOut\nend\n\n-- moved under module.init\nfunction module.prompt(headerText)\n\t\nend\n\nmodule.PROMPT_EXPIRE_TIME = 30\n\nfunction module.init(Modules)\n\n\tlocal tween = Modules.tween\n\n\tpromptFrame.Visible = false\n\t\n\tlocal promptQueue = {}\n\t\n\tlocal currentPrompt\n\n\t\n\tlocal function displayPrompt(prompt)\n\t\tcurrentPrompt = prompt\n\n\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(30, 30, 30)\n\t\tpromptFrame.Visible = true\n\t\t\n\t\tpromptFrame.curve.yes.Visible = true\n\t\tpromptFrame.curve.no.Visible = true\t\t\n\t\t\n\t\tpromptFrame.curve.Visible = true\n\t\t\n\n\t\t\n\t\tpromptFrame.curve.title.Text = prompt.text\n\t\t\n\t\tpromptFrame.curve.Position = UDim2.new(-1,0,0,0)\t\t\n\t\ttween(promptFrame.curve, {\"Position\"}, UDim2.new(0,0,0,0), 0.6)\t\t\n\tend\n\t\n\tlocal function addPrompt(prompt)\n\t\ttable.insert(promptQueue, prompt)\n\t\t\n\t\tif #promptQueue == 1 or promptQueue[1] == prompt then\n\t\t\tdisplayPrompt(prompt)\n\t\tend\n\t\t\n\t\tfor i=1,3 do\n\t\t\tlocal flare = script.Parent.flare:Clone()\n\t\t\tflare.Name = \"flareCopy\"\n\t\t\tflare.Parent = script.Parent\n\t\t\tflare.Visible = true\n\t\t\tflare.Size = UDim2.new(1,4,1,4)\n\t\t\tflare.Position = UDim2.new(0,-2,0.5,0)\n\t\t\tflare.AnchorPoint = Vector2.new(0,0.5)\n\t\t\tlocal x = (180 - 40*i)\n\t\t\tlocal y = (14 - 2*i)\n\t\t\tlocal EndPosition = UDim2.new(0,-y/2,0.5,0)\n\t\t\tlocal EndSize = UDim2.new(1,x,1,y)\n\t\t\ttween(flare,{\"Position\",\"Size\",\"ImageTransparency\"},{EndPosition, EndSize, 1},0.5*i)\n\t\tend\t\t\t\n\tend\n\t\n\tlocal function makeUserResponse(userResponse)\n\t\tif currentPrompt then\n\t\t\t-- register the user's response\n\t\t\tcurrentPrompt.userResponse = userResponse\n\t\t\t-- remove the response from queue\n\t\t\tfor i,prompt in pairs(promptQueue) do\n\t\t\t\tif prompt == currentPrompt then\n\t\t\t\t\ttable.remove(promptQueue, i)\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\t\t\n\t\t\tcurrentPrompt = nil\t\n\n\t\t\t-- fancy transition out\t\t\t\n\t\t\tlocal transitionCurve = promptFrame.curve:Clone()\n\t\t\ttransitionCurve.Name = \"transition\"\n\t\t\ttransitionCurve.ZIndex = transitionCurve.ZIndex + 1\n\t\t\ttransitionCurve.Parent = promptFrame\n\t\t\t\n\t\t\ttween(transitionCurve, {\"Position\"}, UDim2.new(-1,-10,0,0), 0.6) \n\t\t\t\n\t\t\tgame.Debris:AddItem(transitionCurve, 0.6)\n\t\t\tspawn(function()\n\t\t\t\twait(0.6)\n\t\t\t\tif not currentPrompt then\n\t\t\t\t\tpromptFrame.Visible = false\n\t\t\t\tend\n\t\t\tend)\n\t\t\t\n\t\t\tpromptFrame.curve.Visible = false\n\t\t\t\n\t\t\t-- show the next response if applicable\n\t\t\tlocal nextPrompt = promptQueue[1]\n\t\t\tif nextPrompt then\n\t\t\t\tdisplayPrompt(nextPrompt)\n\t\t\tend\t\t\t\n\t\t\t\t\t\n\t\tend\n\tend\t\n\t\n\tfunction module.prompt(headerText)\n\t\t\n\t\tlocal promptStartTime = tick()\n\t\tlocal prompt = {text = headerText; timestamp = promptStartTime}\n\t\taddPrompt(prompt)\n\t\t\n\t\t\n\t\trepeat wait() until prompt.userResponse ~= nil or tick() - promptStartTime >= module.PROMPT_EXPIRE_TIME\n\t\t\n\t\t-- remove the response from queue (if expired)\n\t\tif tick() - promptStartTime >= module.PROMPT_EXPIRE_TIME then\n\t\t\tif prompt == currentPrompt then\n\t\t\t\tmakeUserResponse(false)\n\t\t\telse\t\t\t\n\t\t\t\tfor i,prompt in pairs(promptQueue) do\n\t\t\t\t\tif prompt == currentPrompt then\n\t\t\t\t\t\ttable.remove(promptQueue, i)\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\t\t\t\t\t\n\t\t\tend\n\t\tend\n\t\t\n\t\treturn prompt.userResponse or false\n\t\t\n\tend\n\t\n\n\n\tpromptFrame.curve.no.MouseButton1Click:Connect(function()\n\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(30, 7, 8)\n\t\tpromptFrame.curve.yes.Visible = false\n\t\tpromptFrame.curve.no.Visible = false\n\t\tmakeUserResponse(false)\n\tend)\n\tpromptFrame.curve.yes.MouseButton1Click:Connect(function()\n\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(8, 30, 10)\n\t\tpromptFrame.curve.yes.Visible = false\n\t\tpromptFrame.curve.no.Visible = false\t\t\t\t\n\t\tmakeUserResponse(true)\t\n\tend)\t\n\t\n\t\n\t\n\t\n\t-- old stuff no one cares about:\n\t\n\tfunction module.prompt_old(headerText)\n\t\t\t\n\t\t\n\t\tif promptOut then\n\t\t\treturn false\n\t\tend\n\n\n\t\tpromptFrame.Position = UDim2.new(0.5,0,0.5,0)\n\t\t\n\t\t\n\t\t-- temp measure\n\t\t\n\t\tlocal function selfIsSelected()\n\t\t\tlocal obj = game:GetService(\"GuiService\").SelectedObject\n\t\t\t\n\t\t\tif obj then\n\t\t\t\treturn obj:IsDescendantOf(promptFrame)\n\t\t\telse\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\t\n\t\t\n\t\tpromptOut = true\n\t\t\n\t\tlocal transitionOut \n\t\t\n\t\t--First we initiate the prompt\n\n\t\tpromptFrame.curve.title.Text = headerText\n\t\t\n\t\tlocal con0 = promptFrame.curve.no.MouseButton1Click:Connect(function()\n\t\t\t--Modules.Menu.sounds.Click:Play()\n\t\t\tif promptOut then\n\t\t\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(30, 7, 8)\n\t\t\t\tpromptFrame.curve.yes.Visible = false\n\t\t\t\tpromptFrame.curve.no.Visible = false\n\t\t\t\tcurrentDecision = false\n\t\t\tend\n\t\tend)\n\t\tlocal con1 = promptFrame.curve.yes.MouseButton1Click:Connect(function()\n\t\t\t--Modules.Menu.sounds.Click:Play()\n\t\t\tif promptOut then\n\t\t\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(8, 30, 10)\n\t\t\t\tpromptFrame.curve.yes.Visible = false\n\t\t\t\tpromptFrame.curve.no.Visible = false\t\t\t\t\n\t\t\t\tcurrentDecision = true\n\t\t\tend\n\t\tend)\n\t\t\n\t\ttween(promptFrame.curve, {\"Position\"}, UDim2.new(0,0,0,0), 0)\n\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(30, 30, 30)\n\t\tpromptFrame.Visible = true\n\t\tpromptFrame.curve.yes.Visible = true\n\t\tpromptFrame.curve.no.Visible = true\t\t\n\t\tfor i=1,3 do\n\t\t\tlocal flare = script.Parent.flare:Clone()\n\t\t\tflare.Name = \"flareCopy\"\n\t\t\tflare.Parent = script.Parent\n\t\t\tflare.Visible = true\n\t\t\tflare.Size = UDim2.new(1,4,1,4)\n\t\t\tflare.Position = UDim2.new(0,-2,0.5,0)\n\t\t\tflare.AnchorPoint = Vector2.new(0,0.5)\n\t\t\tlocal x = (180 - 40*i)\n\t\t\tlocal y = (14 - 2*i)\n\t\t\tlocal EndPosition = UDim2.new(0,-y/2,0.5,0)\n\t\t\tlocal EndSize = UDim2.new(1,x,1,y)\n\t\t\ttween(flare,{\"Position\",\"Size\",\"ImageTransparency\"},{EndPosition, EndSize, 1},0.5*i)\n\t\tend\t\t\t\n\n\t\t--Transition the stuff into the screen\n\t\t--spawn(function()\n\t\t\t-- use my tween function nerd\n\t\t\t\n\n\n\t\t--promptFrame.Absorb.Visible = true\n\t\t--Modules.Menu.tween(promptFrame,{\"BackgroundTransparency\"}, .5, 0.7, Enum.EasingStyle.Quint)\n\t\t--Modules.Menu.tween(promptFrame.InputPrompt, {\"Position\"}, UDim2.new(0.5,0,0.5,0), 0.7, Enum.EasingStyle.Quint)\n\t\t\t\n\t\t\t--[[\n\t\t\t\n\t\t\tlocal startT = tick()\n\t\t\tfor i = 1,60*deltaT do\n\t\t\t\tif transitionOut then\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\t\tlocal now = tick() - startT\n\t\t\t\tlocal a = now/deltaT\n\t\t\t\tlocal inputFrameY = quint(-.5,.5,a)\n\t\t\t\tlocal bgTransparency = quint(1,.6,a)\n\t\t\t\tpromptFrame.BackgroundTransparency = bgTransparency\n\t\t\t\tpromptFrame.InputPrompt.Position = UDim2.new(0,0,inputFrameY,0)\n\t\t\t\trunService.Heartbeat:Wait()\n\t\t\tend\n\t\t\t]]\n\n\t\t--end)\n\t\t\n\t\t\n\t\t--Yield the thread until an answer pops up\t\n\t\trepeat\n\t\t\t--[[\n\t\t\tlocal Xbox = Modules.Input.mode.Value == \"Xbox\"\n\t\t\tif Xbox and not selfIsSelected() then\n\t\t\t\tModules.Focus.stealFocus(promptFrame.InputPrompt)\n\t\t\tend\n\t\t\t]]\n\t\t\trunService.Heartbeat:Wait()\n\t\tuntil currentDecision ~= nil\n\t\t\n\t\tlocal thisDecision = currentDecision\n\t\tcurrentDecision = nil\t\n\t\t\t\n\t\t--Disconnect the buttons\n\t\tcon0:Disconnect()\n\t\tcon1:Disconnect()\n\t\t\n\t\t--make prompt ready\n\t\tpromptOut = false\n\t\ttween(promptFrame.curve, {\"Position\"}, UDim2.new(-1,-10,0,0), 0.6)\n\t\tspawn(function()\n\t\t\twait(0.6)\n\t\t\tif not promptOut then\n\t\t\t\tpromptFrame.Visible = false\t\n\t\t\tend\n\t\tend)\n\t--\tspawn(function()\n\t\t\n\t--\tpromptFrame.Absorb.Visible = false\n\t--\tModules.Menu.tween(promptFrame,{\"BackgroundTransparency\"}, 1, 0.7, Enum.EasingStyle.Quint)\n\t--\tModules.Menu.tween(promptFrame.InputPrompt, {\"Position\"}, UDim2.new(0.5,0,0,-250), 0.7, Enum.EasingStyle.Quint)\t\t\t\n\t\t--[[\n\t\tlocal startT = tick()\n\t\tfor i = 1,60*deltaT do\n\t\t\tif promptOut then\n\t\t\t\tbreak\n\t\t\tend\n\t\t\tlocal now = tick() - startT\n\t\t\tlocal a = now/deltaT\n\t\t\tlocal inputFrameY = quint(.5,-.5,a)\n\t\t\tlocal bgTransparency = quint(.6,1,a)\n\t\t\tpromptFrame.BackgroundTransparency = bgTransparency\n\t\t\tpromptFrame.InputPrompt.Position = UDim2.new(0,0,inputFrameY,0)\n\t\t\trunService.Heartbeat:Wait()\n\t\tend\n\t\t]]\n\t--\tend)\n\t\n\t\t\n\n\t\treturn thisDecision\n\tend\t\n\t\n\t\n\tModules.network:create(\"promptAction\",\"BindableFunction\",\"OnInvoke\",module.prompt)\nend\n\n\nreturn module"
  },
  {
    "path": "src/StarterGui/prompting_Fullscreen.lua",
    "content": "-- Input prompt by Locard, modified by berezaa\n-- Imported from Miner's Haven\nlocal module = {}\n\nlocal runService = game:GetService(\"RunService\")\n\nlocal player = game:GetService(\"Players\").LocalPlayer\n\nlocal promptOut\nlocal promptFrame = player.PlayerGui.gameUI.promptFullscreen\nlocal buttonCons = {}\nlocal currentDecision\n\nfunction module.forceClose()\n\tif promptOut then\n\t\tcurrentDecision = false\n\tend\nend\n\nfunction module.isPrompting()\n\treturn promptOut\nend\n\n-- moved under module.init\nfunction module.prompt(headerText)\n\t\nend\n\nmodule.PROMPT_EXPIRE_TIME = 30\n\nfunction module.init(Modules)\n\n\tlocal tween = Modules.tween\n\n\tpromptFrame.Visible = false\n\t\n\tlocal promptQueue = {}\n\t\n\tlocal currentPrompt\n\n\t\n\tlocal function displayPrompt(prompt)\n\t\tcurrentPrompt = prompt\n\n\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(30, 30, 30)\n\t\tpromptFrame.Visible = true\n\t\t\n\t\tpromptFrame.curve.yes.Visible = true\n\t\tpromptFrame.curve.no.Visible = true\t\t\n\t\t\n\t\tpromptFrame.curve.Visible = true\n\t\t\n\n\t\t\n\t\tpromptFrame.curve.title.Text = prompt.text\n\t\t\n\t\n\tend\n\t\n\tlocal function addPrompt(prompt)\n\t\ttable.insert(promptQueue, prompt)\n\t\t\n\t\tif #promptQueue == 1 or promptQueue[1] == prompt then\n\t\t\tdisplayPrompt(prompt)\n\t\tend\n\t\t\t\n\tend\n\t\n\tlocal function makeUserResponse(userResponse)\n\t\tif currentPrompt then\n\t\t\t-- register the user's response\n\t\t\tcurrentPrompt.userResponse = userResponse\n\t\t\t-- remove the response from queue\n\t\t\tfor i,prompt in pairs(promptQueue) do\n\t\t\t\tif prompt == currentPrompt then\n\t\t\t\t\ttable.remove(promptQueue, i)\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\tend\n\t\t\t\n\t\t\tcurrentPrompt = nil\t\n\n\t\t\t-- fancy transition out\t\t\t\n\n\t\t\tspawn(function()\n\t\t\t\twait()\n\t\t\t\tif not currentPrompt then\n\t\t\t\t\tpromptFrame.Visible = false\n\t\t\t\tend\n\t\t\tend)\n\t\t\t\n\t\t\tpromptFrame.curve.Visible = false\n\t\t\t\n\t\t\t-- show the next response if applicable\n\t\t\tlocal nextPrompt = promptQueue[1]\n\t\t\tif nextPrompt then\n\t\t\t\tdisplayPrompt(nextPrompt)\n\t\t\tend\t\t\t\n\t\t\t\t\t\n\t\tend\n\tend\t\n\t\n\tfunction module.prompt(headerText)\n\t\t\n\t\tlocal promptStartTime = tick()\n\t\tlocal prompt = {text = headerText; timestamp = promptStartTime}\n\t\taddPrompt(prompt)\n\t\t\n\t\t\n\t\trepeat wait() until prompt.userResponse ~= nil or tick() - promptStartTime >= module.PROMPT_EXPIRE_TIME\n\t\t\n\t\t-- remove the response from queue (if expired)\n\t\tif tick() - promptStartTime >= module.PROMPT_EXPIRE_TIME then\n\t\t\tif prompt == currentPrompt then\n\t\t\t\tmakeUserResponse(false)\n\t\t\telse\t\t\t\n\t\t\t\tfor i,prompt in pairs(promptQueue) do\n\t\t\t\t\tif prompt == currentPrompt then\n\t\t\t\t\t\ttable.remove(promptQueue, i)\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\t\t\t\t\t\n\t\t\tend\n\t\tend\n\t\t\n\t\treturn prompt.userResponse or false\n\t\t\n\tend\n\t\n\n\n\tpromptFrame.curve.no.MouseButton1Click:Connect(function()\n\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(30, 7, 8)\n\t\tpromptFrame.curve.yes.Visible = false\n\t\tpromptFrame.curve.no.Visible = false\n\t\tmakeUserResponse(false)\n\tend)\n\tpromptFrame.curve.yes.MouseButton1Click:Connect(function()\n\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(8, 30, 10)\n\t\tpromptFrame.curve.yes.Visible = false\n\t\tpromptFrame.curve.no.Visible = false\t\t\t\t\n\t\tmakeUserResponse(true)\t\n\tend)\t\n\t\n\t\n\t\n\t\n\t-- old stuff no one cares about:\n\t\n\tfunction module.prompt_old(headerText)\n\t\t\t\n\t\t\n\t\tif promptOut then\n\t\t\treturn false\n\t\tend\n\n\n\t\t\n\t\t\n\t\t-- temp measure\n\t\t\n\t\tlocal function selfIsSelected()\n\t\t\tlocal obj = game:GetService(\"GuiService\").SelectedObject\n\t\t\t\n\t\t\tif obj then\n\t\t\t\treturn obj:IsDescendantOf(promptFrame)\n\t\t\telse\n\t\t\t\treturn false\n\t\t\tend\n\t\tend\t\n\t\t\n\t\tpromptOut = true\n\t\t\n\t\tlocal transitionOut \n\t\t\n\t\t--First we initiate the prompt\n\n\t\tpromptFrame.curve.title.Text = headerText\n\t\t\n\t\tlocal con0 = promptFrame.curve.no.MouseButton1Click:Connect(function()\n\t\t\t--Modules.Menu.sounds.Click:Play()\n\t\t\tif promptOut then\n\t\t\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(30, 7, 8)\n\t\t\t\tpromptFrame.curve.yes.Visible = false\n\t\t\t\tpromptFrame.curve.no.Visible = false\n\t\t\t\tcurrentDecision = false\n\t\t\tend\n\t\tend)\n\t\tlocal con1 = promptFrame.curve.yes.MouseButton1Click:Connect(function()\n\t\t\t--Modules.Menu.sounds.Click:Play()\n\t\t\tif promptOut then\n\t\t\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(8, 30, 10)\n\t\t\t\tpromptFrame.curve.yes.Visible = false\n\t\t\t\tpromptFrame.curve.no.Visible = false\t\t\t\t\n\t\t\t\tcurrentDecision = true\n\t\t\tend\n\t\tend)\n\t\t\n\n\t\tpromptFrame.curve.ImageColor3 = Color3.fromRGB(30, 30, 30)\n\t\tpromptFrame.Visible = true\n\t\tpromptFrame.curve.yes.Visible = true\n\t\tpromptFrame.curve.no.Visible = true\t\t\n\t\n\n\t\t--Transition the stuff into the screen\n\t\t--spawn(function()\n\t\t\t-- use my tween function nerd\n\t\t\t\n\n\n\t\t--promptFrame.Absorb.Visible = true\n\t\t--Modules.Menu.tween(promptFrame,{\"BackgroundTransparency\"}, .5, 0.7, Enum.EasingStyle.Quint)\n\t\t--Modules.Menu.tween(promptFrame.InputPrompt, {\"Position\"}, UDim2.new(0.5,0,0.5,0), 0.7, Enum.EasingStyle.Quint)\n\t\t\t\n\t\t\t--[[\n\t\t\t\n\t\t\tlocal startT = tick()\n\t\t\tfor i = 1,60*deltaT do\n\t\t\t\tif transitionOut then\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\t\tlocal now = tick() - startT\n\t\t\t\tlocal a = now/deltaT\n\t\t\t\tlocal inputFrameY = quint(-.5,.5,a)\n\t\t\t\tlocal bgTransparency = quint(1,.6,a)\n\t\t\t\tpromptFrame.BackgroundTransparency = bgTransparency\n\t\t\t\tpromptFrame.InputPrompt.Position = UDim2.new(0,0,inputFrameY,0)\n\t\t\t\trunService.Heartbeat:Wait()\n\t\t\tend\n\t\t\t]]\n\n\t\t--end)\n\t\t\n\t\t\n\t\t--Yield the thread until an answer pops up\t\n\t\trepeat\n\t\t\t--[[\n\t\t\tlocal Xbox = Modules.Input.mode.Value == \"Xbox\"\n\t\t\tif Xbox and not selfIsSelected() then\n\t\t\t\tModules.Focus.stealFocus(promptFrame.InputPrompt)\n\t\t\tend\n\t\t\t]]\n\t\t\trunService.Heartbeat:Wait()\n\t\tuntil currentDecision ~= nil\n\t\t\n\t\tlocal thisDecision = currentDecision\n\t\tcurrentDecision = nil\t\n\t\t\t\n\t\t--Disconnect the buttons\n\t\tcon0:Disconnect()\n\t\tcon1:Disconnect()\n\t\t\n\t\t--make prompt ready\n\t\tpromptOut = false\n\n\n\t\tpromptFrame.Visible = false\t\n\n\t--\tspawn(function()\n\t\t\n\t--\tpromptFrame.Absorb.Visible = false\n\t--\tModules.Menu.tween(promptFrame,{\"BackgroundTransparency\"}, 1, 0.7, Enum.EasingStyle.Quint)\n\t--\tModules.Menu.tween(promptFrame.InputPrompt, {\"Position\"}, UDim2.new(0.5,0,0,-250), 0.7, Enum.EasingStyle.Quint)\t\t\t\n\t\t--[[\n\t\tlocal startT = tick()\n\t\tfor i = 1,60*deltaT do\n\t\t\tif promptOut then\n\t\t\t\tbreak\n\t\t\tend\n\t\t\tlocal now = tick() - startT\n\t\t\tlocal a = now/deltaT\n\t\t\tlocal inputFrameY = quint(.5,-.5,a)\n\t\t\tlocal bgTransparency = quint(.6,1,a)\n\t\t\tpromptFrame.BackgroundTransparency = bgTransparency\n\t\t\tpromptFrame.InputPrompt.Position = UDim2.new(0,0,inputFrameY,0)\n\t\t\trunService.Heartbeat:Wait()\n\t\tend\n\t\t]]\n\t--\tend)\n\t\n\t\t\n\n\t\treturn thisDecision\n\tend\t\n\t\n\t\n\tModules.network:create(\"promptActionFullscreen\",\"BindableFunction\",\"OnInvoke\",module.prompt)\nend\n\n\nreturn module"
  },
  {
    "path": "src/StarterGui/questLog.lua",
    "content": "local module = {}\n\nfunction module.open()\n\t\nend\n--[[\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t= modules.load(\"network\")\n\t\tlocal utilities = modules.load(\"utilities\")\n\t\tlocal questUtil = modules.load(\"client_quest_util\")\n\tlocal itemData = require(replicatedStorage.itemData)\n\tlocal questLookup \t= require(replicatedStorage.questLookup)\n-- todo: remove this disgusting absolute reference\n--local uiCreator = require(script.Parent.Parent.Parent.Parent:WaitForChild(\"uiCreator\"))\n\nlocal player \t\t\t\t\t\t= game.Players.LocalPlayer\n\nlocal instructFrame\t\t\t\t\t= script.Parent:WaitForChild(\"contents\").logFrame.curve.questInstructions.contents\nlocal selectFrame\t\t\t\t\t= script.Parent:WaitForChild(\"contents\").quests.curve.contents\n--local scrollingMask = script.Parent.Parent.Parent.Parent:WaitForChild(\"scrollingMask\")\n\nlocal currentQuestOpenId \nlocal activeQuests = {}\nlocal completedQuests = {}\nlocal unassignedQuests = {}\n\nlocal lastSelected\nlocal openQuestOnJoinId \n\n\nfunction module.init(Modules)\n\t\n\tlocal function createSingleNote(text, completed, centered, col)\n\t\tlocal slot = script.slot:Clone()\n\t\tslot.Text = text\n\t\tif completed then\n\t\t\tslot.TextTransparency = .5\n\t\tend\n\t\tif centered then\n\t\t\tslot.TextXAlignment = \"Center\"\n\t\tend\n\t\t\n\t\t\n\t\tlocal sizeY = game.TextService:GetTextSize(text, slot.TextSize, slot.Font, instructFrame.AbsoluteSize).Y + 25\n\t\tslot.Size = UDim2.new(1,-12,0,sizeY)\n\t\t\n\t\tif col then\n\t\t\tslot.TextColor3 = col\n\t\tend\n\t\t\n\t\tslot.Parent = instructFrame\n\t\tinstructFrame.CanvasSize = UDim2.new(0,0,0,instructFrame.UIListLayout.AbsoluteContentSize.Y + 30)\n\t\t\n\tend\n\t\n\t\n\tlocal function drawQuestNotes(id)\n\t\tif id == nil then return end\n\t\tcurrentQuestOpenId = id -- needed don't get rid of me\n\t\tlocal quests = network:invoke(\"getCacheValueByNameTag\", \"quests\")\n\t\t\n\t\tfor i, child in pairs(instructFrame:GetChildren()) do\n\t\t\tif child.Name == \"slot\" then\n\t\t\t\tchild:Destroy()\n\t\t\tend\n\t\tend\n\t\t\n\t\t\n\t\t\n\t\tlocal baseState = \"\"\n\t\tlocal questLookUpData = questLookup[id] -- for finding the text, triggertypes, to add to the notes\n\t\tlocal playerQuestData                   -- for displaying progress. if nil then the quest is not started\n\t\t\t\n\t\t\t\n\t\tlocal header = script.Parent:WaitForChild(\"contents\").logFrame.curve.header\n\t\tlocal titleT = header.quest\n\t\ttitleT.Text = questLookUpData.questLineName\n\t\t\n\t\theader.titledecor.Size = UDim2.new(0,titleT.TextBounds.X+16,0,36)\n\t\t\n\t\t\n\t\t\n\t\tfor i, questData in pairs(quests.active) do\n\t\t\tif questData.id == id and questData.objectives[1].started then\n\t\t\t\tbaseState = \"active\"\n\t\t\t\tplayerQuestData = questData\n\t\t\t\tcreateSingleNote(questLookUpData.questLineDescription, false, true)\n\t\t\tend\n\t\tend\n\t\t\n\t\tfor i, questData in pairs(quests.completed) do\n\t\t\tif questData.id == id and baseState ~= \"active\" then\n\t\t\t\tbaseState = \"completed\"\n\t\t\t\tplayerQuestData = questData\n\t\t\t\tcreateSingleNote(questLookUpData.questLineDescription, true, true)\n\t\t\tend\n\t\tend\n\t\t\n\t\tif baseState == \"\" then\n\t\t\tbaseState = \"unassigned\"\n\t\tend\n\t\t\n\t\tif playerQuestData and baseState == \"active\" then\n\t\t\tfor i, objectiveData in pairs(playerQuestData.objectives) do\n\t\t\t\n\t\t\t\tif not objectiveData.started then\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\tlocal objectiveReference = questLookUpData.objectives[i]\n\t\t\t\t\n\t\t\t\tlocal stepsTotal = #objectiveData.steps\n\t\t\t\tlocal stepsCompleted = 0\n\t\t\t\t\n\t\t\t\tfor ii, stepData in pairs(objectiveData.steps) do\n\t\t\t\t\t\n\t\t\t\t\tlocal stepReference = questLookUpData.objectives[i].steps[ii]\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\tlocal triggerType = questLookUpData.objectives[i].steps[ii].triggerType\n\t\t\t\t\tlocal constructedString = \"\"\n\t\t\t\t\t\n\t\t\t\t\tif stepReference.overridingNote then\n\t\t\t\t\t\tconstructedString = stepReference.overridingNote\n\t\t\t\t\telse\n\t\t\t\t\t\t\n\t\t\t\t\t\tlocal completedAmount = math.clamp(stepData.completion.amount, 0, stepData.requirement.amount)\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\tconstructedString = \"[\"..(completedAmount)..\"/\"..(stepData.requirement.amount)..\"]\"\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\tif stepReference.accompanyingNote then\n\t\t\t\t\t\t\tconstructedString = constructedString..\" \"..stepReference.accompanyingNote\n\t\t\t\t\t\telseif triggerType == \"monster-killed\" then\n\t\t\t\t\t\t\tif stepReference.requirement.isGiant then\n\t\t\t\t\t\t\t\tconstructedString = constructedString..\" Giant \"..stepReference.requirement.monsterName..\" kills\"\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tconstructedString = constructedString..\" \"..stepReference.requirement.monsterName..\" kills\"\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\n\t\t\t\t\t\telseif triggerType == \"item-collected\" then\n\t\t\t\t\t\t\tlocal name = itemData[stepReference.requirement.id].name\n\t\t\t\t\t\t\tconstructedString = constructedString..\" \"..name ..\" collected\"\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\tif stepData.completion.amount >= stepData.requirement.amount then\n\t\t\t\t\t\tstepsCompleted = stepsCompleted + 1\n\t\t\t\t\tend\n\t\t\t\t\t\n\t\t\t\t\tif stepReference.isSequentialStep then\n\t\t\t\t\t\tlocal checkCompletedSteps = 0\n\t\t\t\t\t\tfor iii, stepData in pairs(objectiveData.steps) do\n\t\t\t\t\t\t\tif iii >= ii then break end\n\t\t\t\t\t\t\tif stepData.completion.amount >= stepData.requirement.amount then\n\t\t\t\t\t\t\t\tcheckCompletedSteps = checkCompletedSteps + 1\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\t\t\n\t\t\t\t\t\tif checkCompletedSteps >= ii - 1 then\n\t\t\t\t\t\t\tif stepReference.hideNote == nil then\n\t\t\t\t\t\t\t\tcreateSingleNote(constructedString, stepData.completion.amount >= stepData.requirement.amount, false)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\tif stepReference.hideNote == nil then\n\t\t\t\t\t\t\tcreateSingleNote(constructedString, stepData.completion.amount >= stepData.requirement.amount, false)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\tif stepsCompleted >= stepsTotal then\n\t\t\t\t\tif playerQuestData.currentObjective > i then\n\t\t\t\t\t\t\n\t\t\t\t\t\tif playerQuestData.objectives[i + 1].started then\n\t\t\t\t\t\t\t-- case 1\n\t\t\t\t\t\t\t-- player has turned in the current objective AND started the next objective\n\t\t\t\t\t\t\tcreateSingleNote(objectiveReference.completedNotes, true, false)\n\t\t\t\t\t\t\tcreateSingleNote(objectiveReference.handingNotes, true, true)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\t-- case 2\n\t\t\t\t\t\t\t-- player has turned in the current objective BUT has not started the next objective. (player could not have completed the quest)\n\t\t\t\t\t\t\tcreateSingleNote(objectiveReference.completedNotes, true, false)\n\t\t\t\t\t\t\tcreateSingleNote(objectiveReference.handingNotes, false, true)\n\t\t\t\t\t\tend\n\t\t\t\t\t\t\n\t\t\t\t\telse\n\t\t\t\t\t\t\n\t\t\t\t\t\tif baseState == \"completed\" then\n\t\t\t\t\t\t\t-- case 3 player has completed the quest\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tcreateSingleNote(objectiveReference.completedNotes, true, false)\n\t\t\t\t\t\t\tcreateSingleNote(objectiveReference.handingNotes, false, true)\n\t\t\t\t\t\telseif baseState == \"active\" then\n\t\t\t\t\t\t\t-- case 4 player needs to turn in the quest\n\t\t\t\t\t\t\tcreateSingleNote(objectiveReference.completedNotes, false, false, Color3.fromRGB(255, 210, 29))\n\t\t\t\t\t\t\t\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\telseif baseState == \"completed\" then\n\t\t\t\n\t\t\tlocal completedNotes = \"Quest Completed!\"\n\t\t\tif playerQuestData.failed then\n\t\t\t\tcompletedNotes = \"Quest Failed.\"\n\t\t\tend\n\t\t\t\n\t\t\tcreateSingleNote(completedNotes, false, true)\n\t\t\tif questLookUpData.questEndedNote then\n\t\t\t\tcreateSingleNote(questLookUpData.questEndedNote, false, false)\n\t\t\tend\n\t\t\t\n\t\telse\n\t\t\t-- quest is unassigend, display data accordingly\n\t\t\t--createSingleNote(\"my name jef\", false, false)\n\t\tend\n\t\t\n\t\t\n\t\tinstructFrame.CanvasPosition = Vector2.new(0,instructFrame.UIListLayout.AbsoluteContentSize.Y)\n\tend\n\n\t\n\tlocal function drawQuestSelectButton(id, col)\n\t\tlocal slot = script.slotB:Clone()\n\t\t\t\t\n\t\tslot.itemName.Text = questLookup[id].questLineName\n\t\tslot.itemName.TextColor3 = col\n\t\tslot.frame.ImageColor3 = col\n\t\tslot.shine.ImageColor3 = col\n\t\tslot.MouseButton1Click:connect(function()\n\t\t\t\t\t-- update\n\t\t\tcurrentQuestOpenId = id\n\t\t\tdrawQuestNotes(id)\n\t\tend)\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\tslot.Parent = selectFrame\n\t\t\t\t\n\t\tselectFrame.CanvasSize = UDim2.new(0,0,0,selectFrame.UIListLayout.AbsoluteContentSize.Y + 10)\n\t\t\t\t\n\t\tif selectFrame.UIListLayout.AbsoluteContentSize.Y >= selectFrame.AbsoluteSize.Y then\n\t\t\tfor i, child in pairs(selectFrame:GetChildren()) do\n\t\t\t\tif child.Name == \"slotB\" then\n\t\t\t\t\tchild.Size = UDim2.new(1,-16,0,45)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\t\n\t\n\tlocal function updateQuestLog(updateQuestData)\n\t\tif updateQuestData then\n\t\t\tactiveQuests = {}\n\t\t\tcompletedQuests = {}\n\t\t\tunassignedQuests = {}\n\t\t\t\n\t\t\t--active first\n\t\t\tfor i, quest in pairs(updateQuestData.active) do\n\t\t\t\tif quest.objectives[1].started then\n\t\t\t\t\ttable.insert(activeQuests, quest.id)\n\t\t\t\tend\n\t\t\tend\n\t\t\t\n\t\t\tfor i, quest in pairs(updateQuestData.completed) do\n\t\t\t\tlocal repeated = false\n\t\t\t\tfor ii, id in pairs(activeQuests) do\n\t\t\t\t\tif id == quest.id then\n\t\t\t\t\t\trepeated = true\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tif not repeated then\n\t\t\t\t\ttable.insert(completedQuests, quest.id)\n\t\t\t\tend\n\t\t\tend\n\t\t\t\n\t\t\t\n\t\t\tfor key, val in pairs(questLookup) do\n\t\t\t\t\n\t\t\t\tlocal testKey = tonumber(key)\n\t\t\t\tif testKey then\n\t\t\t\t\tlocal unassigned =  true\n\t\t\t\t\tfor i, id in pairs(activeQuests) do\n\t\t\t\t\t\tif id == testKey then\n\t\t\t\t\t\t\tunassigned = false\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\t\n\t\t\t\t\tfor i, id in pairs(completedQuests) do\n\t\t\t\t\t\tif id == testKey then\n\t\t\t\t\t\t\tunassigned = false\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\t\n\t\t\t\t\tif unassigned then\n\t\t\t\t\t\ttable.insert(unassignedQuests, testKey)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\t\t\n\t\t\n\t\t\t\n\t\t\tfor i, child in pairs(selectFrame:GetChildren()) do\n\t\t\t\tif child.Name == \"slotB\" then\n\t\t\t\t\tchild:Destroy()\n\t\t\t\tend\n\t\t\tend\n\t\t\t\n\t\t\t\n\t\t\tlocal alertQuests = 0\n\t\t\tfor i, id in pairs(activeQuests) do\n\t\t\t\tlocal state = questUtil.getPlayerQuestStateByQuestId(id)\n\t\t\t\tif state == 9 then\n\t\t\t\t\tif openQuestOnJoinId == nil then\n\t\t\t\t\t\topenQuestOnJoinId  = id\n\t\t\t\t\tend\n\t\t\t\t\tdrawQuestSelectButton(id, Color3.fromRGB(255, 210, 29))\n\t\t\t\t\talertQuests = alertQuests + 1\n\t\t\t\tend\n\t\t\t\t\n\t\t\tend\n\t\t\t\n\t\t\tfor i, id in pairs(activeQuests) do\n\t\t\t\tlocal state = questUtil.getPlayerQuestStateByQuestId(id)\n\t\t\t\tif state ~= 9 then\t\n\t\t\t\t\tif openQuestOnJoinId == nil then\n\t\t\t\t\t\topenQuestOnJoinId  = id\n\t\t\t\t\tend\t\n\t\t\t\t\tdrawQuestSelectButton(id, Color3.fromRGB(255,255,255))\n\t\t\t\tend\n\t\t\t\t\n\t\t\tend\n\t\t\t\n\t\t\t\n\t\t\t--for i, id in pairs(unassignedQuests) do\n\t\t\t--\tdrawQuestSelectButton(id, Color3.fromRGB(147, 147, 147))\n\t\t\t--end\n\t\t\t\n\t\t\tfor i, id in pairs(completedQuests) do\n\t\t\t\tdrawQuestSelectButton(id, Color3.fromRGB(147, 147, 147))\n\t\t\tend\n\t\t\t\n\t\t\tif currentQuestOpenId == nil and openQuestOnJoinId ~= nil then\n\t\t\t\tdrawQuestNotes(openQuestOnJoinId)\n\t\t\telse\n\t\t\t\tdrawQuestNotes(currentQuestOpenId)\n\t\t\tend\n\t\t\t\n\t\t\tif alertQuests > 0 then\n\t\t\t\tscript.Parent.Parent.right.buttons.openQuestLog.alert.value.Text = alertQuests\n\t\t\t\tscript.Parent.Parent.right.buttons.openQuestLog.alert.Visible = true\n\t\t\telse\n\t\t\t\tscript.Parent.Parent.right.buttons.openQuestLog.alert.Visible = false\n\t\t\tend\n\t\t\t\n\t\t\t\n\t\tend\n\tend\n\t\n\t\n\tlocal function onPropogationRequestToSelf(propogationNameTag, propogationData)\n\t\tif propogationNameTag == \"quests\" then\n\t\t\tupdateQuestLog(propogationData)\n\t\tend\n\tend\n\t\n\tlocal function main()\n\t\t\n\t\tupdateQuestLog(network:invoke(\"getCacheValueByNameTag\", \"quests\"))\n\t\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\t\t\n\t\tnetwork:create(\"displayQuestInQuestLog\", \"BindableEvent\")\n\t\tnetwork:connect(\"displayQuestInQuestLog\", \"Event\", drawQuestNotes)\n\t\t\n\t\t\n\t\tscript.Parent.close.MouseButton1Click:connect(function()\n\t\t\tModules.focus.toggle(script.Parent)\n\t\tend)\n\tend\n\t\n\tfunction module.open()\n\t\t\n\t\tif not script.Parent.Visible then\n\t\t\tscript.Parent.UIScale.Scale = (Modules.input.menuScale or 1) * 0.75\n\t\t\tModules.tween(script.Parent.UIScale, {\"Scale\"}, (Modules.input.menuScale or 1), 0.5, Enum.EasingStyle.Bounce)\n\t\tend\t\t\t\t\n\t\tModules.focus.toggle(script.Parent)\n\t\t\n\tend\t\n\t\n\tmain()\n\nend\n\t\n\n\t\n]]\n\nreturn module"
  },
  {
    "path": "src/StarterGui/save.client.lua",
    "content": "local resetUI = script.Parent:WaitForChild(\"resetUI\")\n\nresetUI.Enabled = false\nresetUI.saveMe.Visible = false\ngame.Players.LocalPlayer:WaitForChild(\"DataLoaded\", 60)\n\nwait(5)\nif (not resetUI.Parent.gameUI.Enabled) and (not resetUI.Parent.customize.Enabled) then\n\tresetUI.Enabled = true\n\tresetUI.saveMe.Visible = true\n\n\n\tlocal function showReport()\n\t\tlocal text = \"\"\n\t\tfor _, entry in pairs(game.LogService:GetLogHistory()) do\n\t\t\ttext = text .. \"  [[\"..entry.message..\"]]  \"\n\t\tend\n\t\tresetUI.incident.contents.Text = text\n\t\tresetUI.incident.Visible = true\n\tend\n\n\tresetUI.incident.continue.Activated:connect(function()\n\t\tlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\t\tlocal network = modules.load(\"network\")\n\t\tnetwork:invokeServer(\"playerRequest_returnToMainMenu\")\n\t\tresetUI.incident.continue:Destroy()\n\tend)\n\n\tresetUI.saveMe.Activated:connect(function()\n\t\tresetUI.saveMe.Visible = false\n\t\tshowReport()\n\tend)\nend"
  },
  {
    "path": "src/StarterGui/serverBrowser.lua",
    "content": "local module = {}\n\nlocal player = game:GetService(\"Players\").LocalPlayer\nlocal gui = player.PlayerGui.gameUI.serverBrowser\n\nfunction module.open()\n\nend\n\nfunction module.init(Modules)\n\tlocal httpService = game:GetService(\"HttpService\")\n\tlocal servers\n\tlocal serversDataValue\n\tlocal network = Modules.network\n\n\tgui.close.MouseButton1Click:connect(function()\n\t\tModules.focus.toggle(gui)\n\tend)\n\n\tgui.header.serverId.Text = \"This server's ID: \"..string.sub(game.JobId, 1, 8)\n\n\tfunction module.open()\n\t\tif not gui.Visible then\n\t\t\tgui.UIScale.Scale = (Modules.input.menuScale or 1) * 0.75\n\t\t\tModules.tween(gui.UIScale, {\"Scale\"}, (Modules.input.menuScale or 1), 0.5, Enum.EasingStyle.Bounce)\n\t\tend\n\t\tModules.focus.toggle(gui)\n\tend\n\n\tlocal function updated()\n\t\tservers = serversDataValue.Value ~= \"\" and httpService:JSONDecode(serversDataValue.Value) or {}\n\t\tfor i,child in pairs(gui.servers:GetChildren()) do\n\t\t\tif child:IsA(\"GuiObject\") then\n\t\t\t\tchild:Destroy()\n\t\t\tend\n\t\tend\n\t\tservers[game.JobId] = {\n\t\t\tplayers = #game.Players:GetChildren();\n\t\t}\n\t\tfor jobId, serverData in pairs(servers) do\n\t\t\tlocal button = gui.sample:Clone()\n\t\t\tbutton.id.Text = string.sub(jobId, 1, 8)\n\t\t\tbutton.Name = jobId\n\t\t\t-- same place so same MaxPlayers?\n\t\t\tlocal serverFilled = serverData.players / game.Players.MaxPlayers\n\t\t\tbutton.players.progress.Size = UDim2.new(serverFilled, 0, 1, 0)\n\t\t\tif serverFilled >= 0.9 then\n\t\t\t\tbutton.players.progress.BackgroundColor3 = Color3.fromRGB(255, 0, 4)\n\t\t\telseif serverFilled >= 0.75 then\n\t\t\t\tbutton.players.progress.BackgroundColor3 = Color3.fromRGB(255, 191, 0)\n\t\t\telse\n\t\t\t\tbutton.players.progress.BackgroundColor3 = Color3.fromRGB(12, 255, 0)\n\t\t\tend\n\t\t\tbutton.LayoutOrder = 100 - math.floor(serverFilled * 100)\n\n\t\t\tbutton.leave.Visible = true\n\n\t\t\tlocal mainColor = Color3.new(1,1,1)\n\t\t\tif jobId == game.JobId then\n\t\t\t\tbutton.LayoutOrder = -1\n\t\t\t\tmainColor = Color3.fromRGB(0, 255, 255)\n\t\t\t\tbutton.leave.Visible = false\n\t\t\tend\n\t\t\tbutton.id.TextColor3 = mainColor\n\t\t\tbutton.players.BorderColor3 = mainColor\n\t\t\tbutton.players.progress.BorderColor3 = mainColor\n\n\t\t\tlocal idString = string.gsub(jobId,\"[^%d*]\",\"\")\n\t\t\tlocal seed = math.floor((tonumber(idString)^(1/2))) + 1\n\t\t\tlocal rand = Random.new(seed)\n\t\t\tbutton.ImageColor3 = Color3.new(\n\t\t\t\trand:NextNumber(),\n\t\t\t\trand:NextNumber(),\n\t\t\t\trand:NextNumber()\n\t\t\t)\n\n\t\t\tbutton.leave.Activated:connect(function()\n\t\t\t\tlocal success, err = network:invokeServer(\"playerRequest_teleportToJobId\", jobId)\n\t\t\tend)\n\n\t\t\tbutton.Parent = gui.servers\n\t\tend\n\tend\n\n\tlocal function main()\n\t\tserversDataValue = game.ReplicatedStorage:WaitForChild(\"serversData\")\n\t\tupdated()\n\t\tserversDataValue.Changed:connect(updated)\n\tend\n\n\tspawn(main)\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/serverMessage.lua",
    "content": "local module = {}\n\nlocal ui = script.Parent.gameUI.leftBar.serverMessage\n\nfunction module.init(Modules)\n\n\tlocal network = Modules.network\n\tlocal configuration = Modules.configuration\n\tlocal tween = Modules.tween\n\n\tlocal currentMessage\n\n\tfunction module.displayMessage(message)\n\n\t\tui.contents.ImageColor3 = Color3.fromRGB(18, 18, 18)\n\n\t\tui.Visible = true\n\n\t\tcurrentMessage = message\n\n\t\tui.contents.body.text.Text = currentMessage\n\t\tlocal textBounds = ui.contents.body.text.TextBounds\n\n\t\tui.Size = UDim2.new(0, 280, 0, 62 + textBounds.Y)\n\n\t\tfor i=1,4 do\n\t\t\tlocal flare = ui.flare:Clone()\n\t\t\tflare.Name = \"flareCopy\"\n\t\t\tflare.Parent = ui\n\t\t\tflare.Visible = true\n\t\t\tflare.Size = UDim2.new(1,4,1,4)\n\t\t\tflare.Position = UDim2.new(0,-2,0.5,0)\n\t\t\tflare.AnchorPoint = Vector2.new(0,0.5)\n\t\t\tlocal x = (180 - 40*i)\n\t\t\tlocal y = (14 - 2*i)\n\t\t\tlocal EndPosition = UDim2.new(0,-y/2,0.5,0)\n\t\t\tlocal EndSize = UDim2.new(1,x,1,y)\n\t\t\ttween(flare,{\"Position\",\"Size\",\"ImageTransparency\"},{EndPosition, EndSize, 1},0.5*i)\n\t\tend\n\tend\n\n\tfunction module.hide()\n\t\tif ui.Visible then\n\t\t\tcurrentMessage = \"\"\n\t\t\tui.Visible = false\n\t\tend\n\tend\n\n\tui.contents.Activated:connect(module.hide)\n\n\tui.contents.MouseEnter:connect(function()\n\t\tui.contents.ImageColor3 = Color3.fromRGB(12, 0, 63)\n\tend)\n\n\tui.contents.MouseLeave:connect(function()\n\t\tui.contents.ImageColor3 = Color3.fromRGB(18, 18, 18)\n\tend)\n\n\tspawn(function()\n\t\tnetwork:connect(\"gameConfigurationUpdated\", \"Event\", function(playerConfiguration)\n\t\t\tlocal gameMessage = playerConfiguration.gameDisplayMessage\n\t\t\tif gameMessage and #gameMessage > 0 then\n\t\t\t\tmodule.displayMessage(gameMessage)\n\t\t\telse\n\t\t\t\tmodule.hide()\n\t\t\tend\n\t\tend)\n\n\n\t\tlocal gameMessage = configuration.getConfigurationValue(\"gameDisplayMessage\")\n\t\tif gameMessage and #gameMessage > 0 then\n\t\t\tmodule.displayMessage(gameMessage)\n\t\telse\n\t\t\tmodule.hide()\n\t\tend\n\tend)\nend\n\n\n\n--playerConfiguration.gameDisplayMessage\n\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/settings.lua",
    "content": "local module = {}\n\nlocal runService = game:GetService(\"RunService\")\n\nlocal player = game.Players.LocalPlayer\nlocal playerGui = player.PlayerGui\n\nlocal ui = playerGui.gameUI.menu_settings\n\n\nfunction module.show()\n\tui.Visible = not ui.Visible\nend\nfunction module.hide()\n\tui.Visible = false\nend\n\nui.close.Activated:connect(module.hide)\n\nfunction module.refreshKeybinds()\n\twarn(\"settings.refreshKeybinds not ready\")\nend\n\n\n-- module.remapTarget\n\nlocal userSettings\n\nfunction module.init(Modules)\n\n\tlocal network = Modules.network\n\n\tui.close.Activated:connect(function()\n\t\tModules.focus.toggle(ui)\n\tend)\n\n\t-- PAGE NAVIGATION\n\n\n\tlocal function openPage(pageName)\n\t\tfor i,page in pairs(ui.pages:GetChildren()) do\n\t\t\tif page:IsA(\"GuiObject\") then\n\t\t\t\tpage.Visible = false\n\t\t\tend\n\t\tend\n\t\tfor i,pageButton in pairs(ui.header.buttons:GetChildren()) do\n\t\t\tif pageButton:IsA(\"ImageButton\") then\n\t\t\t\tpageButton.ImageColor3 = Color3.fromRGB(212, 212, 212)\n\t\t\tend\n\t\tend\n\t\tlocal targetPage = ui.pages:FindFirstChild(pageName)\n\t\tlocal targetPageButton = ui.header.buttons:FindFirstChild(pageName)\n\t\ttargetPage.Visible = true\n\t\ttargetPageButton.ImageColor3 = Color3.fromRGB(90, 225, 255)\n\tend\n\n\topenPage(\"options\")\n\n\tfor i,pageButton in pairs(ui.header.buttons:GetChildren()) do\n\t\tif pageButton:IsA(\"GuiButton\") then\n\t\t\tpageButton.Activated:connect(function()\n\t\t\t\topenPage(pageButton.Name)\n\t\t\tend)\n\t\tend\n\tend\n\n\t-- OPTIONS\n\n\t-- volume slider\n\tlocal volumeFrame = ui.pages.options.volume\n\n\tvolumeFrame.bar.InputBegan:connect(function(input)\n\t\tif input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then\n\n\t\t\tlocal startPointX = volumeFrame.bar.AbsolutePosition.X\n\t\t\tlocal endPointX = volumeFrame.bar.AbsolutePosition.X + volumeFrame.bar.AbsoluteSize.X\n\n\t\t\tlocal volume = volumeFrame.bar.slider.Position.X.Scale\n\n\t\t\twhile input.UserInputState ~= Enum.UserInputState.Cancel and input.UserInputState ~= Enum.UserInputState.End do\n\n\t\t\t\tlocal inputPointX = input.Position.X - startPointX\n\n\t\t\t\tlocal newVolume = math.clamp(inputPointX / (endPointX - startPointX), 0, 1)\n\n\t\t\t\tif newVolume <= 0.05 then\n\t\t\t\t\tnewVolume = 0\n\t\t\t\tend\n\n\t\t\t\tif volume ~= newVolume then\n\t\t\t\t\t-- fire volume changed\n\t\t\t\t\tnetwork:fire(\"musicVolumeChanged\", newVolume)\n\t\t\t\t\tvolume = newVolume\n\t\t\t\tend\n\n\t\t\t\tvolumeFrame.bar.slider.Position = UDim2.new(volume, 0, 0.5, 0)\n\t\t\t\trunService.Heartbeat:wait()\n\t\t\tend\n\n\t\t\t-- post volume changes to server\n\t\t\tif userSettings and (userSettings.musicVolume == nil or userSettings.musicVolume ~= volume) then\n\t\t\t\tnetwork:invokeServer(\"requestChangePlayerSetting\", \"musicVolume\", volume)\n\t\t\tend\n\n\t\tend\n\tend)\n\n\t-- main menu\n\tui.pages.mainMenu.mainMenu.Activated:Connect(function()\n\t\tnetwork:invokeServer(\"playerRequest_returnToMainMenu\")\n\tend)\n\n\t-- KEYBINDS\n\n\tlocal keybinds = ui.pages.keybinds\n\n\tlocal keybindSample = keybinds:WaitForChild(\"sampleAction\")\n\tkeybindSample.Visible = false\n\tkeybindSample.Parent = script\n\n\tmodule.keybindSample = keybindSample\n\n\tlocal function buttonClicked(button)\n\t\tlocal oldTarget = module.remapTarget\n\n\t\tif oldTarget then\n\t\t\toldTarget.ImageColor3 = keybindSample.ImageColor3\n\t\t\tif oldTarget == button then\n\t\t\t\tmodule.remapTarget = nil\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\n\t\tmodule.remapTarget = button\n\t\tbutton.ImageColor3 = Color3.fromRGB(61, 255, 74)\n\tend\n\n\tfunction module.open()\n\t\tModules.focus.toggle(ui)\n\tend\n\n\tfunction module.refreshKeybinds()\n\n\t\t-- after hitting my head with a brick twenty times, this is where I call it a day\n\t\tif keybindSample:FindFirstChild(\"title\") == nil then\n\t\t\treturn false\n\t\tend\n\n\t\tmodule.remapTarget = nil\n\n\t\tfor i,child in pairs(keybinds:GetChildren()) do\n\t\t\tif child:IsA(\"GuiObject\") then\n\t\t\t\tchild:Destroy()\n\t\t\tend\n\t\tend\n\n\t\tlocal count = 0\n\n\t\tfor actionName,action in pairs(Modules.input.actions) do\n\t\t\tlocal keybind = keybindSample:Clone()\n\t\t\tkeybind.Name = actionName\n\t\t\tkeybind.title.Text = actionName\n\t\t\tkeybind.Parent = keybinds\n\t\t\tkeybind.LayoutOrder = action.priority\n\t\t\tkeybind.Visible = true\n\n\t\t\tkeybind.MouseButton1Click:connect(function()\n\t\t\t\tbuttonClicked(keybind)\n\t\t\tend)\n\n\t\t\tcount = count + 1\n\t\tend\n\n\t\tlocal ysize = 10 + math.ceil(count/2) * (keybindSample.AbsoluteSize.Y + 5)\n\t\tkeybinds.CanvasSize = UDim2.new(0,0,0,ysize)\n\n\n\tend\n\n\tuserSettings = network:invoke(\"getCacheValueByNameTag\", \"userSettings\")\n\n\tif userSettings.musicVolume then\n\t\tvolumeFrame.bar.slider.Position = UDim2.new(userSettings.musicVolume, 0, 0.5, 0)\n\tend\n\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", function(key, value)\n\t\tif key == \"userSettings\" then\n\t\t\tuserSettings = value\n\t\t\tif userSettings.musicVolume then\n\t\t\t\tvolumeFrame.bar.slider.Position = UDim2.new(userSettings.musicVolume, 0, 0.5, 0)\n\t\t\tend\n\t\tend\n\tend)\n\n\t--network:invokeServer(\"requestChangePlayerSetting\", \"clearingInteraction\", true)\n\nend\n\nreturn module"
  },
  {
    "path": "src/StarterGui/shop.lua",
    "content": "-- local interface for shop\n-- author: not damien\n\n\nlocal module = {}\nlocal ui = script.Parent.gameUI.menu_shop\n\nfunction module.open()\n\nend\n\nfunction module.close()\n\tui.Visible = false\nend\n\n--[[\n\tFOR DAMIEN:\n\n\tto initiate a sell in the shop, set currentItem.Value to the INTERNAL NAME of the item being sold.\n\tit will be converted to item id when sending to server\n\n\tset amount.Value to how many items are in the stack. This should be the default sell amount\n\n\tset slotInfo to whatever you need to, or find the other \"for damien:\" comment and edit that part of code\n\n\n--]]\n\n\n\n-- todo: store in NPC instead of hard-coding\nlocal defaultShopItems = {\"health potion\", \"health potion 2\", \"wooden club\", \"wooden sword\", \"pitchfork\", \"oak axe\"}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal itemData = require(replicatedStorage.itemData)\n\nfunction module.init(Modules)\n\tlocal network = Modules.network\n\tlocal fx = Modules.fx\n\tlocal tween = Modules.tween\n\tlocal utilities = Modules.utilities\n\tlocal economy = Modules.economy\n\n\tfunction module.close(fromInteract)\n\t\tui.Visible = false\n\t\tif not fromInteract then\n\t\t\twarn(\"stop interact\")\n\t\t\tModules.interaction.stopInteract()\n\t\tend\n\tend\n\t-- ugly hack\n\tui.close.Activated:connect(function() module.close() end)\n\n\tlocal shopItems\n\n\tfunction module.open()\n\t\tModules.focus.toggle(ui)\n\t\tif ui.Visible then\n\t\t\tnetwork:fire(\"localSignal_shopOpened\")\n\t\tend\n\tend\n\n\t-- Reset the buy/sell frame\n\tlocal currentItemData = {}\n\tlocal inventoryModule\n\n\n\n\n\t-- Update the buy/sell frame\n\tlocal function update()\n\n\n\n\t\tui.buy.content.amount.value.Text \t= tostring(ui.amount.Value)\n\t\tui.sell.selling.amount.value.Text \t= tostring(ui.amount.Value)\n\n\t\tif not currentItemData.itemBaseData then\n--\t\t\tui.sell.selling.Visible = false\n--\t\t\tui.sell.empty.Visible = true\n\t\t\tui.sell.Visible = false\n\t\t\tui.sell.item.itemThumbnail.Image = \"\"\n\t\t\tui.buy.Visible = false\n\t\t\treturn\n\t\tend\n\n\t\tlocal itemName \t= currentItemData.itemBaseData.module.Name\n\n\n\n\t\tfor i,shopItem in pairs(ui.curve.contents:GetChildren()) do\n\t\t\tif shopItem:IsA(\"ImageButton\") then\n\n\t\t\t\tif shopItem.Name ~= itemName then\n\t\t\t\t\tshopItem.ImageColor3 = Color3.fromRGB(30, 30, 30)\n\t\t\t\t\tshopItem.frame.Inner.ImageColor3 = Color3.fromRGB(30, 30, 30)\n\t\t\t\telse\n\t\t\t\t\tshopItem.ImageColor3 = Color3.fromRGB(9, 39, 58)\n\t\t\t\t\tshopItem.frame.Inner.ImageColor3 = Color3.fromRGB(9, 39, 58)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif currentItemData.itemBaseData then\n\t\t--\tui.sell.empty.Visible = false\n\t\t\tui.sell.selling.Visible = true\n\t\t\tui.sell.Visible = true\n\n\t\t\tif currentItemData.itemBaseData.name then\n\t\t\t\t--local sellValue = currentItemData.itemBaseData.sellValue or 1\n\t\t\t\tif ui.buy.Visible and currentItemData.itemBaseData.buyValue then\n\t\t\t\t\tui.buy.content.itemName.Text = currentItemData.itemBaseData.name\n\t\t\t\t\tui.buy.item.itemThumbnail.Image = currentItemData.itemBaseData.image or \"rbxassetid://2679574493\"\n--\t\t\t\t\tui.buy.content.cost.Text \t\t= currentItemData.itemBaseData.buyValue * ui.amount.Value\n\n\t\t\t\t\tif currentItemData.costInfo then\n\t\t\t\t\t\tModules.money.setLabelAmount(\n\t\t\t\t\t\t\tui.buy.content.cost,\n\t\t\t\t\t\t\tcurrentItemData.cost * ui.amount.Value,\n\t\t\t\t\t\t\tcurrentItemData.costInfo\n\t\t\t\t\t\t)\n\t\t\t\t\telse\n\t\t\t\t\t\tlocal statistics_final \t= network:invoke(\"getCacheValueByNameTag\", \"nonSerializeData\").statistics_final\n\t\t\t\t\t\tlocal coinCostReduction = 1 - math.clamp(statistics_final.merchantCostReduction, 0, 1)\n\n\t\t\t\t\t\tModules.money.setLabelAmount(\n\t\t\t\t\t\t\tui.buy.content.cost,\n\t\t\t\t\t\t\tmath.clamp(currentItemData.cost * ui.amount.Value * coinCostReduction, 1, math.huge),\n\t\t\t\t\t\t\tcurrentItemData.costInfo\n\t\t\t\t\t\t)\n\t\t\t\t\tend\n\n\t\t\t\telseif ui.sell.Visible then\n\t\t\t\t\tui.sell.selling.itemName.Text \t= currentItemData.itemBaseData.name\n\t\t\t\t\tui.sell.item.itemThumbnail.Image = currentItemData.itemBaseData.image or \"rbxassetid://2679574493\"\n\n\t\t\t\t\tlocal inventorySlotData = currentItemData.inventorySlotData or {}\n\n\t\t\t\t\tlocal titleColor\n\t\t\t\t\tif inventorySlotData then\n\t\t\t\t\t\ttitleColor = Modules.itemAcquistion.getTitleColorForInventorySlotData(inventorySlotData)\n\t\t\t\t\tend\n\n\t\t\t\t\tui.sell.selling.itemName.TextColor3 = titleColor or Color3.new(1,1,1)\n\n\t\t\t\t\tlocal inventoryItem = ui.sell.item.curve\n\n\t\t\t\t\tinventoryItem.stars.Visible = false\n\t\t\t\t\tlocal upgrades = inventorySlotData.successfulUpgrades\n\t\t\t\t\tif upgrades then\n\t\t\t\t\t\tfor i,child in pairs(inventoryItem.stars:GetChildren()) do\n\t\t\t\t\t\t\tif child:IsA(\"ImageLabel\") then\n\t\t\t\t\t\t\t\tchild.ImageColor3 = titleColor or Color3.new(1,1,1)\n\t\t\t\t\t\t\t\tchild.Visible = false\n\t\t\t\t\t\t\telseif child:IsA(\"TextLabel\") then\n\t\t\t\t\t\t\t\tchild.TextColor3 = titleColor or Color3.new(1,1,1)\n\t\t\t\t\t\t\t\tchild.Visible = false\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\t\tinventoryItem.stars.Visible = true\n\t\t\t\t\t\tif upgrades <= 3 then\n\t\t\t\t\t\t\tfor i,star in pairs(inventoryItem.stars:GetChildren()) do\n\t\t\t\t\t\t\t\tlocal score = tonumber(star.Name)\n\t\t\t\t\t\t\t\tif score then\n\t\t\t\t\t\t\t\t\tstar.Visible = score <= upgrades\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tinventoryItem.stars.exact.Visible = false\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tinventoryItem.stars[\"1\"].Visible = true\n\t\t\t\t\t\t\tinventoryItem.stars.exact.Visible = true\n\t\t\t\t\t\t\tinventoryItem.stars.exact.Text = upgrades\n\t\t\t\t\t\tend\n\t\t\t\t\t\tinventoryItem.stars.Visible = true\n\n\t\t\t\t\tend\n\n--\t\t\t\t\tui.sell.selling.cost.Text \t\t= (currentItemData.itemBaseData.sellValue or 1) * ui.amount.Value\n\t\t\t\t\tlocal sellValue = economy.getSellValue(currentItemData.itemBaseData, currentItemData.inventorySlotData)\n\t\t\t\t\tModules.money.setLabelAmount(ui.sell.selling.cost, (sellValue) * ui.amount.Value)\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\tui.buy.Visible = false\n--\t\t\tui.sell.empty.Visible = true\n--\t\t\tui.sell.selling.Visible = false\n\t\t\tui.sell.Visible = false\n\t\tend\n\tend\n\n\tlocal function reset()\n\t\tcurrentItemData = {}\n\t\tui.ethyr.Visible = false\n\t\tupdate()\n\t\tif Modules.input.mode.Value == \"xbox\" then\n\t\t\tgame.GuiService.SelectedObject = ui.sell.item.itemThumbnail\n\t\tend\n\tend\n\n\treset()\n\n\t-- Request to buy the currently selected item\n\tlocal function requestBuy()\n\t\tif ui.buy.Visible and inventoryModule then\n\t\t\tif currentItemData.itemBaseData then\n\t\t\t\tif currentItemData.itemBaseData.buyValue then\n\t\t\t\t\t-- todo: client-side check of money\n\t\t\t\t\tlocal id = currentItemData.itemBaseData.id\n\t\t\t\t\tlocal amount = ui.amount.Value\n\n\t\t\t\t\tlocal itemCostInfo = currentItemData.costInfo\n\n\t\t\t\t\tlocal success, reason = network:invokeServer(\"playerRequest_buyItemFromShop\", {id = id, costInfo = itemCostInfo}, amount, inventoryModule)\n\n\t\t\t\t\t-- todo: feedback for request status\n\t\t\t\t\tif success then\n\t\t\t\t\t\tfx.statusRibbon(ui, \"Bought \"..currentItemData.itemBaseData.name, \"success\", 3)\n\t\t\t\t\telse\n\t\t\t\t\t\tfx.statusRibbon(ui, \"Failed to purchase.\", \"fail\", 3)\n\t\t\t\t\t\twarn(reason)\n\t\t\t\t\t\tif itemCostInfo.costType == \"ethyr\" then\n\t\t\t\t\t\t\tModules.products.open()\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t\treset()\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tui.buy.content.no.MouseButton1Click:connect(reset)\n\tui.sell.selling.no.MouseButton1Click:connect(reset)\n\tui.amount.Changed:connect(update)\n\n\n\t-- Set the buy frame (disables shop frame)\n\tlocal function promptBuyItem(itemName, cost, costInfo)\n\t\tui.sell.Visible = false\n\t\tui.buy.Visible = true\n\n\t\tif Modules.input.mode.Value == \"xbox\" then\n\t\t\tgame.GuiService.SelectedObject = ui.buy.content.no\n\t\tend\n\t\t--[[\n\t\tif costInfo and costInfo.costType and costInfo.costType == \"ethyr\" then\n\n\t\t\tui.ethyr.Visible = true\n\t\telse\n\t\t\tui.ethyr.Visible = false\n\t\tend\n\t\t]]\n\t\tif currentItemData.itemBaseData and currentItemData.itemBaseData.name == itemName and ui.amount.Value == 1 then\n\t\t\tupdate()\n\t\tend\n\n\t\tui.amount.Value = 1\n\tend\n\n\tlocal function promptSellItem(itemName)\n\t\tui.sell.Visible \t\t\t= true\n\t\tui.sell.selling.Visible \t= true\n--\t\tui.sell.empty.Visible = false\n\t\tui.buy.Visible \t\t\t= true\n\n\t\tif currentItemData.itemBaseData and currentItemData.itemBaseData.name == itemName and ui.amount.Value == 1 then\n\t\t\tupdate()\n\t\tend\n\n\t\t-- for damien: make this the amount you need\n\t\tui.amount.Value = 1\n\tend\n\n\tlocal function int__setCurrentItem(inventorySlotData, isSelling, cost, costInfo)\n\n\t\tlocal itemId = inventorySlotData.id\n\n\t\tlocal inventorySlotDataPosition = inventorySlotData.position\n\n\t\t-- default to the size of the selected stack\n\t\tlocal quantity = inventorySlotData.stacks or 1\n\t\tui.amount.Value = quantity\n\n\n\n\t\tcurrentItemData = { id = itemId; inventorySlotDataPosition = inventorySlotDataPosition; inventorySlotData = inventorySlotData; itemBaseData = itemData[itemId]; cost = cost; costInfo = costInfo; }\n\n\t\t-- toggle\n\t\tui.buy.Visible \t= not isSelling\n\t\tui.sell.Visible \t= not not isSelling\n\n\t\tupdate()\n\tend\n\n\n\tnetwork:create(\"shop_setCurrentItem\", \"BindableFunction\", \"OnInvoke\", int__setCurrentItem)\n\n\t-- Request to sell the currently selected item\n\tlocal function requestSell()\n\t\tif ui.sell.Visible and ui.sell.selling.Visible then\n\t\t\tif not currentItemData.itemBaseData then reset() return end\n\n\t\t\tlocal itemName = currentItemData.itemBaseData.name\n\n\t\t\tif currentItemData.itemBaseData then\n\t\t\t\tif currentItemData.itemBaseData.cantSell then\n\t\t\t\t\treturn false\n\t\t\t\tend\n\n\t\t\t\tlocal itemName = currentItemData.itemBaseData.name\n\n\t\t\t\tlocal id = currentItemData.id\n\t\t\t\tlocal amount = ui.amount.Value\n\t\t\t\t-- for damien: may need to change this\n\t\t\t\tlocal category = network:invoke(\"getCurrentInventoryCategory\")\n\t\t\t\tlocal success = network:invokeServer(\"playerRequest_sellItemToShop\", {id = id; position = currentItemData.inventorySlotDataPosition}, amount)\n\n\t\t\t\tif success then\n\t\t\t\t\tfx.statusRibbon(ui, \"Sold \"..itemName, \"gold\", 3)\n\t\t\t\telse\n\t\t\t\t\tfx.statusRibbon(ui, \"'fraid you can't do that :/\", \"fail\", 3)\n\t\t\t\tend\n\t\t\t\treset()\n\n\t\t\tend\n\t\tend\n\tend\n\n\tui.buy.content.yes.MouseButton1Click:connect(requestBuy)\n\tui.sell.selling.yes.MouseButton1Click:connect(requestSell)\n\n\t-- todo: exponential increase/decrease\n\tlocal function increase()\n\t\tif ui.amount.Value < 999 then\n\t\t\tui.amount.Value = ui.amount.Value + 1\n\t\tend\n\tend\n\tlocal function decrease()\n\t\tif ui.amount.Value > 1 then\n\t\t\tui.amount.Value = ui.amount.Value - 1\n\t\tend\n\tend\n\n\tlocal function buyAmountValueChanged()\n\t\tlocal buyAmount = tonumber(ui.buy.content.amount.value.Text)\n\n\t\tif buyAmount then\n\t\t\tui.amount.Value = buyAmount\n\n\t\t\tupdate()\n\t\tend\n\tend\n\n\tlocal function sellAmountValueChanged()\n\t\tlocal sellAmount = tonumber(ui.sell.selling.amount.value.Text)\n\n\t\tif sellAmount then\n\t\t\tui.amount.Value = sellAmount\n\n\t\t\tupdate()\n\t\tend\n\tend\n\n\tui.buy.content.amount.increase.MouseButton1Click:connect(increase)\n\tui.buy.content.amount.decrease.MouseButton1Click:connect(decrease)\n\n\tui.sell.selling.amount.increase.MouseButton1Click:connect(increase)\n\tui.sell.selling.amount.decrease.MouseButton1Click:connect(decrease)\n\n\tui.buy.content.amount.value.FocusLost:connect(buyAmountValueChanged)\n\tui.sell.selling.amount.value.FocusLost:connect(sellAmountValueChanged)\n\n\t-- Setup\n\n\tlocal lastSelected\n\n\tlocal sample = ui.curve.contents:WaitForChild(\"sampleItem\")\n\tsample.Visible = false\n\tsample.Parent = script\n\n\tlocal function refreshShopInventory()\n\n\t\tui.ethyr.Visible = false\n\n\t\tfor i,existingItem in pairs(ui.curve.contents:GetChildren()) do\n\t\t\tif existingItem:IsA(\"GuiObject\") and existingItem:FindFirstChild(\"itemName\") then\n\t\t\t\texistingItem:Destroy()\n\t\t\tend\n\t\tend\n\n\t\tlocal actualShopItems = 0\n\n\t\tfor i,shopItemInfo in pairs(shopItems) do\n\n\t\t\tlocal shopItemName\n\t\t\tlocal costInfo\n\t\t\tlocal cost\n\n\t\t\tif typeof(shopItemInfo) == \"string\" then\n\t\t\t\tshopItemName = shopItemInfo\n\t\t\t\tcostInfo = {}\n\t\t\telseif typeof(shopItemInfo) == \"table\" then\n\t\t\t\tshopItemName = shopItemInfo.itemName\n\t\t\t\tcostInfo = shopItemInfo.costInfo\n\t\t\t\tif costInfo and costInfo.costType and costInfo.costType == \"ethyr\" then\n\n\t\t\t--\t\tui.ethyr.Visible = true\n\n\t\t\t\tend\n\t\t\t\tcost = shopItemInfo.cost\n\t\t\telse\n\t\t\t\terror(\"Invalid shopItemInfo type. Failed to refresh shop inventory.\")\n\t\t\tend\n\n\n\n\t\t\tlocal itemBaseData = itemData[shopItemName]\n\t\t\tif itemBaseData and itemBaseData.name then\n\n\t\t\t\tlocal costType = costInfo and costInfo.costType or \"money\"\n\t\t\t\tcost = cost or itemBaseData.buyValue\n\n\t\t\t\tif cost and costType then\n\n\t\t\t\t\tlocal shopItem = sample:Clone()\n\t\t\t\t\tshopItem.Name = shopItemName\n\t\t\t\t\tshopItem.itemName.Text = itemBaseData.name\n\n\t\t\t\t\tlocal itemType = itemBaseData.itemType\n\t\t\t\t\tshopItem.itemName.cuteDecor.Image = \"rbxgameasset://Images/category_\"..itemType\n\n\n\t\t\t\t\tshopItem.item.itemThumbnail.Image = itemBaseData.image or \"rbxassetid://2679574493\"\n\n--\t\t\t\t\tshopItem.cost.Text = \"$\"..itemBaseData.buyValue\n\t\t\t\t\tModules.money.setLabelAmount(shopItem.cost, cost, costInfo)\n\t\t\t\t\t--[[\n\t\t\t\t\tif costInfo.textColor then\n\t\t\t\t\t\tshopItem.cost.amount.TextColor3 = costInfo.textColor\n\t\t\t\t\tend\n\t\t\t\t\tif costInfo.icon then\n\t\t\t\t\t\tshopItem.cost.icon.Image = costInfo.icon\n\t\t\t\t\tend\n\n\t\t\t\t\tif costType ~= \"money\" and cost > 999 then\n\t\t\t\t\t\tshopItem.cost.amount.Text = cost\n\t\t\t\t\tend\n\t\t\t\t\t]]\n\n\t\t\t\t\tshopItem.Parent = ui.curve.contents\n\t\t\t\t\tshopItem.LayoutOrder = i\n\t\t\t\t\tshopItem.Visible = true\n\n\t\t\t\t\tshopItem.MouseButton1Click:connect(function()\n\t\t\t\t\t\tint__setCurrentItem(itemBaseData, nil, cost, costInfo)\n\t\t\t\t\t\tpromptBuyItem(shopItemName, cost, costInfo)\n\t\t\t\t\tend)\n\t\t\t\t\tshopItem.item.itemThumbnail.MouseButton1Click:connect(function()\n\t\t\t\t\t\tint__setCurrentItem(itemBaseData, nil, cost, costInfo)\n\t\t\t\t\t\tpromptBuyItem(shopItemName, cost, costInfo)\n\t\t\t\t\tend)\n\n\t\t\t\t\tlocal inventorySlotData = {id = itemBaseData.id}\n\n\t\t\t\t\tif shopItemInfo.attributes then\n\t\t\t\t\t\tfor attribute, value in pairs(shopItemInfo.attributes) do\n\t\t\t\t\t\t\tif not inventorySlotData[attribute] then\n\t\t\t\t\t\t\t\tinventorySlotData[attribute] = value\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal function selected()\n\t\t\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\", itemBaseData, \"shop\", inventorySlotData)\n\t\t\t\t\t\tlastSelected = shopItem\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal function deselected()\n\t\t\t\t\t\tif lastSelected == shopItem then\n\t\t\t\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tshopItem.item.itemThumbnail.MouseEnter:connect(selected)\n\t\t\t\t\tshopItem.item.itemThumbnail.MouseLeave:connect(deselected)\n\n\t\t\t\t\tshopItem.SelectionGained:connect(selected)\n\t\t\t\t\tshopItem.SelectionLost:connect(deselected)\n\n\t\t\t\t\tlocal titleColor\n\t\t\t\t\tif itemBaseData then\n\t\t\t\t\t\ttitleColor = Modules.itemAcquistion.getTitleColorForInventorySlotData(inventorySlotData)\n\t--\t\t\t\t\t\ttitleColor = Modules.itemAcquistion.getTitleColorForInventorySlotData(itemBaseData)\n\t\t\t\t\tend\n\n--\t\t\t\t\ttitleColor = titleColor or itemBaseData.nameColor\n\n\t\t\t\t\tshopItem.shine.Visible = titleColor ~= nil\n\t\t\t\t\tshopItem.shine.ImageColor3 = titleColor or Color3.fromRGB(179, 178, 185)\n\t\t\t\t\tshopItem.frame.ImageColor3 = titleColor or Color3.fromRGB(106, 105, 107)\n\t\t\t\t\tshopItem.shine.ImageColor3 = titleColor or Color3.fromRGB(179, 178, 185)\n\t\t\t\t\tshopItem.itemName.TextColor3 = titleColor or Color3.new(1,1,1)\n\n\t\t\t\t\tModules.fx.setFlash(shopItem.frame, shopItem.shine.Visible)\n\n\n\n\n\n\n\n\n\n\t\t\t\t\tactualShopItems = actualShopItems + 1\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tui.curve.contents.CanvasSize = UDim2.new(0, 0, 0, 20 + math.ceil(actualShopItems) * (70 + 5))\n\tend\n\n\tfunction module.open(shopObject)\n\t\treset()\n\t\tui.curve.contents.CanvasPosition = Vector2.new()\n\t\tif ui.Visible then\n\t\t\tui.Visible = not ui.Visible\n\t\telse\n\t\t\tnetwork:fire(\"localSignal_shopOpened\")\n\t\t\t--ui.Visible = true\n--\t\t\tui.Parent.Parent.UIScale.Scale = (Modules.input.menuScale or 1) * 0.75\n--\t\t\tModules.tween(ui.Parent.Parent.UIScale, {\"Scale\"}, (Modules.input.menuScale or 1), 1, Enum.EasingStyle.Bounce)\n\n\t\t\tif shopObject then\n\t\t\t\tif shopObject:FindFirstChild(\"inventory\") then\n\t\t\t\t\tinventoryModule = shopObject.inventory\n\t\t\t\t\tshopItems \t\t= require(shopObject.inventory)\n\t\t\t\t\tif inventoryModule:FindFirstChild(\"shopName\") then\n\t\t\t\t\t\tui.header.itemType.Text = inventoryModule:FindFirstChild(\"shopName\").Value\n\t\t\t\t\telse\n\t\t\t\t\t\tui.header.itemType.Text = \"Merchant\"\n\t\t\t\t\tend\n\n\t\t\t\telse\n\t\t\t\t\tinventoryModule = nil\n\t\t\t\t\tshopItems \t\t= {}\n\t\t\t\tend\n\n\t\t\t\tif shopObject:IsA(\"BasePart\") then\n\t\t\t\t\t--local cf = shopObject.CFrame:ToWorldSpace(camCf)\n\t\t\t\t\t--network:invoke(\"lockCameraPosition\",cf)\n\t\t\t\tend\n\t\t\tend\n\t\t\trefreshShopInventory()\n\n--\t\t\tModules.playerMenu.selectMenu(ui, Modules[script.Name])\n\t\t\tui.Visible = true\n\t\t\t--[[\n\t\t\tui.Visible = true\n\t\t\tif ui.Parent.tradeFrame.Visible then\n\t\t\t\tui.Parent.tradeFrame.Visible = false\n\t\t\t\tModules.trading.endTrade(true)\n\t\t\tend\n\t\t\tui.Parent.equipFrame.Visible = false\n\t\t\tui.Parent.Parent.Visible = true\n\t\t\tModules.focus.change(ui.Parent.Parent)\n\t\t\t]]\n\t\tend\n\tend\n\tnetwork:create(\"openShop\",\"BindableFunction\",\"OnInvoke\", module.open)\n\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/social.lua",
    "content": "local module = {}\n\nmodule.friends = {}\n\nmodule.places = {\n\t[\"Main Menu\"] = 2376885433;\n\t[\"Free Demo\"] = 2015602902;\n\t[\"Mushtown\"] = 2064647391;\n\t[\"Mushroom Forest\"] = 2035250551;\n\t[\"Mushroom Grotto\"] = 2060360203;\n\t[\"The Clearing\"] = 2060556572;\n\t[\"Altdorf\"] = 2119298605;\n\t[\"Farmlands\"] = 2180867434;\n\t[\"Enchanted Forest\"] = 2260598172;\n\t[\"Redwood Pass\"] = 2376890690;\n\t[\"Seaside Path\"] = 2093766642;\n\t[\"Testing Environment\"] = 2061558182;\t\n}\n\nlocal places = module.places\n\nlocal function isPlaceInGame(placeId)\n\tfor i,place in pairs(places) do\n\t\tif place == placeId then\n\t\t\treturn true\n\t\tend\n\tend\nend\n\n\nlocal player = game.Players.LocalPlayer\n\nfunction module.init(Modules)\n\t\n\tlocal function updateFriends()\n\t\tlocal success, err = pcall(function()\n\t\t\tlocal friendsInfo = player:GetFriendsOnline()\n\t\t\tlocal friendsList = {}\n\t\t\tfor i,friend in pairs(friendsInfo) do\n\t\t\t\tif friend.IsOnline and friend.PlaceId and isPlaceInGame(friend.PlaceId) then\n\t\t\t\t\tfriendsList[friend.VisitorId] = friend\n\t\t\t\tend\n\t\t\tend\n\t\t\tmodule.friends = friendsList\n\t\tend)\n\t\tif not success then\n\t\t\twarn(\"Failed to fetch online friends\")\n\t\t\twarn(err)\n\t\tend\n\tend\n\t\n\tspawn(function()\n\t\tupdateFriends()\n\t\twhile wait(30) do\n\t\t\tupdateFriends()\n\t\tend\n\tend)\n\t\n\t\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/stamina.lua",
    "content": "local module = {}\n\nlocal player = game.Players.LocalPlayer\nlocal ui = script.Parent.gameUI.stamina\n\nfunction module.init(Modules)\n\n\tui.BackgroundTransparency = 1\n\tui.value.BackgroundTransparency = 1\n\tui.Active = false\n\tui.Visible = true\n\n\tlocal function updateStamina()\n\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart:FindFirstChild(\"stamina\") and player.Character.PrimaryPart:FindFirstChild(\"maxStamina\") then\n\t\t\tif player.Character.PrimaryPart.stamina.Value < player.Character.PrimaryPart.maxStamina.Value then\n\n\t\t\t\tui.Size = UDim2.new(0, 10, 0, player.Character.PrimaryPart.maxStamina.Value * 10)\n\n\t\t\t\tModules.tween(ui.value,{\"Size\"},UDim2.new(1,0,player.Character.PrimaryPart.stamina.Value / player.Character.PrimaryPart.maxStamina.Value,0),0.1, Enum.EasingStyle.Linear)\n\t\t\t\tif not ui.Active then\n\t\t\t\t\tui.Active = true\n\t\t\t\t\tModules.tween(ui.value,{\"BackgroundTransparency\"},0,0.5)\n\t\t\t\t\tModules.tween(ui,{\"BackgroundTransparency\"},0,0.5)\n\t\t\t\tend\n\t\t\t\tif player.Character.PrimaryPart.stamina.Value <= 0 then\n\t\t\t\t\tui.value.Visible = false\n\t\t\t\t\tspawn(function()\n\t\t\t\t\t\tfor i=1,5 do\n\t\t\t\t\t\t\tui.BorderSizePixel = 2\n\t\t\t\t\t\t\twait(0.1)\n\t\t\t\t\t\t\tui.BorderSizePixel = 0\n\t\t\t\t\t\t\twait(0.1)\n\t\t\t\t\t\tend\n\t\t\t\t\t\tif player.Character.PrimaryPart.stamina.Value <= 0 and Modules.network:invoke(\"doesPlayerHaveStatusEffect\", \"heat exhausted\") then\n\t\t\t\t\t\t\tui.BorderSizePixel = 2\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\t\t\t\telse\n\t\t\t\t\tui.value.Visible = true\n\t\t\t\t\tui.BorderSizePixel = 0\n\t\t\t\t\tlocal color = Color3.fromRGB(46, 153, 46)\n\t\t\t\t\tif Modules.network:invoke(\"doesPlayerHaveStatusEffect\", \"empower\", nil, \"haste\")  then\n\t\t\t\t\t\tcolor = Color3.fromRGB(45, 191, 162)\n\t\t\t\t\telseif Modules.network:invoke(\"doesPlayerHaveStatusEffect\", \"heat exhausted\") then\n\t\t\t\t\t\tcolor = Color3.fromRGB(255, 255, 127)\n\t\t\t\t\tend\n\t\t\t\t\tui.value.BackgroundColor3 = color\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tif ui.Active then\n\t\t\t\t\tModules.tween(ui.value,{\"Size\"},UDim2.new(1,0,1,0),0.1, Enum.EasingStyle.Linear)\n\t\t\t\t\tui.Active = false\n\t\t\t\t\tModules.tween(ui.value,{\"BackgroundTransparency\"},1,0.5)\n\t\t\t\t\tModules.tween(ui,{\"BackgroundTransparency\"},1,0.5)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tspawn(function()\n\t\tlocal startTime = tick()\n\t\trepeat wait() until (player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart:FindFirstChild(\"stamina\") and player.Character.PrimaryPart:FindFirstChild(\"maxStamina\")) or tick() - startTime > 10\n\t\tupdateStamina()\n\t\tplayer.Character.PrimaryPart.stamina.Changed:connect(updateStamina)\n\tend)\n\n\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/statusBars.lua",
    "content": "-- Player name, health, mana, etc. display\n-- berezaa\nlocal module = {}\n\nlocal characterPrimaryPart\n\nlocal frame = script.Parent.gameUI.bottomRight.statusBars\n\nlocal healthUI  = frame.healthBar.value\nlocal damageUI \t\t= frame.healthBar.damageValue\nlocal tweenService \t= game:GetService(\"TweenService\")\n\nlocal damageHealthTween\n\n-- the higher this number the more 'bursty' damage looks\nlocal healthBurstFactor = 1\n\nfunction module.init(Modules)\n\tfor _, child in pairs(game.Lighting:GetChildren()) do\n\t\tif child.Name == \"DamageColor\" or child.Name == \"DamageBlur\" then\n\t\t\tchild:Destroy()\n\t\tend\n\tend\n\n\tlocal ColorEffect = Instance.new(\"ColorCorrectionEffect\")\n\tColorEffect.Name = \"DamageColor\"\n\tColorEffect.Parent = game.Lighting\n\n\tlocal BlurEffect = Instance.new(\"BlurEffect\")\n\tBlurEffect.Name = \"DamageBlur\"\n\tBlurEffect.Parent = game.Lighting\n\tBlurEffect.Size = 0\n\n\tlocal tween = Modules.tween\n\n\tlocal damagedScreen = frame.Parent.Parent.vin_low_health\n\n\t--frame.header.username.value.Text = game.Players.LocalPlayer.Name\n\n\n\tfunction module.manaWarning()\n\t\tspawn(function()\n\t\t\tfor _ = 1, 3 do\n--\t\t\t\tframe.manaBar.frame.Visible = true\n\t\t\t\tframe.manaBar.ImageColor3 = Color3.fromRGB(134, 17, 17)\n\t\t\t\twait()\n--\t\t\t\tframe.manaBar.frame.Visible = false\n\t\t\t\tframe.manaBar.ImageColor3 = Color3.fromRGB(16, 16, 16)\n\t\t\t\twait()\n\t\t\tend\n\t\tend)\n\tend\n\n\n\trepeat wait() until game.Players.LocalPlayer.Character\n\tlocal character = game.Players.LocalPlayer.Character\n\twhile not character.PrimaryPart and character.Parent and character:IsDescendantOf(workspace) do\n\t\tlocal primaryPart = character:WaitForChild(\"hitbox\", 1)\n\n\t\tif primaryPart then\n\t\t\tcharacter.PrimaryPart = primaryPart\n\t\t\tbreak\n\t\tend\n\tend\n\n\tcharacterPrimaryPart = character.PrimaryPart\n\n\tcharacterPrimaryPart:WaitForChild(\"health\")\n\n\tlocal lastHealth = characterPrimaryPart.health.Value\n\n\tlocal function healthRefresh(ini)\n\t\tlocal delta = characterPrimaryPart.health.Value - lastHealth\n\t\tlocal change = characterPrimaryPart.health.Value - lastHealth\n\t\tlastHealth = characterPrimaryPart.health.Value\n\n\t\tlocal humanoidHealth \t= characterPrimaryPart.health.Value\n\t\tlocal humanoidMaxHealth = characterPrimaryPart.maxHealth.Value\n\n\t\tlocal percent = math.clamp(characterPrimaryPart.health.Value / characterPrimaryPart.maxHealth.Value, 0, 1)\n\t\tlocal manaPercent = math.clamp(characterPrimaryPart.mana.Value / characterPrimaryPart.maxMana.Value, 0, 1)\n\n\n\t\tlocal percentDelta = delta/humanoidMaxHealth\n\t\tlocal healthPercent = humanoidHealth/humanoidMaxHealth\n\n\n\t\tdamagedScreen.Visible = healthPercent < 0.4 and healthPercent > 0\n\t\tif damagedScreen.Visible then\n\t\t\tdamagedScreen.UIScale.Scale = 1 + healthPercent * 10\n\t\tend\n\n\t\tif ini then\n\t\t\tframe.healthBar.value.Size = UDim2.new(math.max(percent,0),0,1,0)\n\t\t\tframe.manaBar.value.Size = UDim2.new(math.max(manaPercent,0),0,1,0)\n\t\telse\n\t\t\ttween(frame.healthBar.value, {\"Size\"}, UDim2.new(math.max(percent,0),0,1,0), 0.3)\n\t\t\ttween(frame.manaBar.value, {\"Size\"}, UDim2.new(math.max(manaPercent,0),0,1,0), 0.3)\n\t\tend\n\t\tframe.healthBar.title.Text = math.floor(characterPrimaryPart.health.Value + 0.5) .. \"/\" .. math.floor(characterPrimaryPart.maxHealth.Value + 0.5)\n\n\n\t\tframe.manaBar.title.Text = math.floor(characterPrimaryPart.mana.Value + 0.5) .. \"/\" .. math.floor(characterPrimaryPart.maxMana.Value + 0.5)\n\n\t\tif delta < 0 then\n\t\t\tlocal thresh = percentDelta^2 + math.clamp(1-healthPercent, 0, 1)^3\n\t\t\tlocal duration = 0.15 + thresh^3\n\n\t\t\ttween(ColorEffect,{\"TintColor\",\"Contrast\",\"Saturation\"},{Color3.fromRGB(255,255 - thresh * 150,255 - thresh * 150),thresh*2, thresh},duration/3)\n\t\t\ttween(BlurEffect,{\"Size\"},thresh * 5,duration/3)\n\t\t\tspawn(function()\n\t\t\t\twait(duration/2)\n\t\t\t\ttween(ColorEffect,{\"TintColor\",\"Contrast\",\"Saturation\"},{Color3.fromRGB(255,255,255),0, 0},duration/2)\n\t\t\t\ttween(BlurEffect,{\"Size\"},0,duration/2)\n\t\t\tend)\n\t\t\tspawn(function()\n\n\n\t\t\t\tlocal healthpercent = characterPrimaryPart.health.Value/characterPrimaryPart.maxHealth.Value\n\t\t\t\tlocal changepercent = math.abs(change/characterPrimaryPart.maxHealth.Value)\n\n\t\t\t\tlocal repre = frame.healthBar.instant:Clone()\n\t\t\t\trepre.Name = \"instantClone\"\n\t\t\t\trepre.Size = UDim2.new(changepercent, 5, 1, 0)\n\t\t\t\trepre.Position = UDim2.new(healthpercent, -5, 0.5, 0)\n\t\t\t\trepre.Parent = frame.healthBar\n\t\t\t\trepre.Visible = true\n\n\t\t\t\ttween(repre, {\"Size\", \"ImageTransparency\"}, {UDim2.new(changepercent, 5, 1 + changepercent * 30, 0), 1}, 0.25 + changepercent * 2)\n\t\t\t\tgame.Debris:AddItem(repre, 0.25 + changepercent * 2)\n\n\t\t\t\t--[[\n\t\t\t\tfor i=1,3 do\n\t\t\t\t\tframe.healthBar.ImageColor3 = Color3.new(1,1,1)\n\t\t\t\t\twait(0.08)\n\t\t\t\t\tframe.healthBar.ImageColor3 = Color3.fromRGB(16, 16, 16)\n\t\t\t\t\twait(0.08)\n\t\t\t\tend\n\t\t\t\t]]\n\n\t\t\tend)\n\n\t\t\tlocal healthDelta = -change\n\n\n\t\t\tif healthDelta > 0 then\n\t\t\t\tif damageHealthTween and damageHealthTween.PlaybackState == Enum.PlaybackState.Playing then\n\t\t\t\t\tdamageHealthTween:Cancel()\n\t\t\t\tend\n\n\t\t\t\thealthUI.Size = UDim2.new(humanoidHealth / humanoidMaxHealth, 0, 1, 0)\n\n\t\t\t\tdamageUI.Position \t\t\t\t= UDim2.new(math.clamp(humanoidHealth / humanoidMaxHealth - 0.1, 0, 1), 0, 0, 0)\n\t\t\t\tdamageUI.Size \t\t\t\t\t= UDim2.new(math.clamp(healthDelta / humanoidMaxHealth + 0.1, 0, 1), 0, 1, 0)\n\t\t\t\tdamageUI.bar.ImageColor3 \t\t= Color3.fromRGB(255, 255, 150 + 50 * (healthDelta / humanoidMaxHealth))\n\t\t\t\t--damageUI.bar.ImageTransparency \t= 1\n\n\t\t\t\tlocal tweenInfo = TweenInfo.new(\n\t\t\t\t\t0.5 * (healthDelta / humanoidMaxHealth) ^ (1 / 3) * healthBurstFactor,\n\t\t\t\t\tEnum.EasingStyle.Quart,\n\t\t\t\t\tEnum.EasingDirection.Out,\n\t\t\t\t\t0,\n\t\t\t\t\tfalse,\n\t\t\t\t\t0\n\t\t\t\t)\n\n\t\t\t\tdamageHealthTween = tweenService:Create(\n\t\t\t\t\tdamageUI.bar,\n\t\t\t\t\ttweenInfo,\n\t\t\t\t\t{ImageColor3 = Color3.fromRGB(255, 44, 44)}\n\t\t\t\t\t--{ImageColor3 = Color3.fromRGB(255, 44, 44); ImageTransparency = 0}\n\t\t\t\t)\n\n\t\t\t\tlocal connection\n\t\t\t\tconnection = damageHealthTween.Completed:connect(function(playbackState)\n\t\t\t\t\tconnection:disconnect()\n\n\t\t\t\t\tif playbackState == Enum.PlaybackState.Completed then\n\t\t\t\t\t\ttweenInfo = TweenInfo.new(\n\t\t\t\t\t\t\t0.5 * (1 - healthDelta / humanoidMaxHealth) ^ (1 / 3) * healthBurstFactor,\n\t\t\t\t\t\t\tEnum.EasingStyle.Quad,\n\t\t\t\t\t\t\tEnum.EasingDirection.Out,\n\t\t\t\t\t\t\t0,\n\t\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t\t0\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\tdamageHealthTween = tweenService:Create(\n\t\t\t\t\t\t\tdamageUI,\n\t\t\t\t\t\t\ttweenInfo,\n\t\t\t\t\t\t\t{Size = UDim2.new(0, 0, 1, 0)}\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\tdamageHealthTween:Play()\n\t\t\t\t\tend\n\t\t\t\tend)\n\n\t\t\t\tdamageHealthTween:Play()\n\t\t\tend\n\n\n\t\telse\n\t\t\tif characterPrimaryPart.health.Value - lastHealth > 5 and delta > 0.01 then\n\t\t\t\tlocal thresh = 0.3 + math.abs(delta)\n\t\t\t\tlocal duration = thresh / 1.4\n\t\t\t\ttween(ColorEffect,{\"TintColor\",\"Contrast\"},{Color3.fromRGB(255 - thresh * 150,255,255 - thresh * 150),-thresh/3},duration/2)\n\t\t\t\tspawn(function()\n\t\t\t\t\twait(duration/1.5)\n\t\t\t\t\ttween(ColorEffect,{\"TintColor\",\"Contrast\"},{Color3.fromRGB(255,255,255),0},duration/2)\n\t\t\t\tend)\n\t\t\tend\n\t\tend\n\tend\n\n\tcharacterPrimaryPart.health.Changed:connect(healthRefresh)\n\tcharacterPrimaryPart.mana.Changed:connect(healthRefresh)\n\tcharacterPrimaryPart.maxHealth.Changed:connect(healthRefresh)\n\tcharacterPrimaryPart.maxMana.Changed:connect(healthRefresh)\n\n\thealthRefresh(true)\n\n\tlocal function inputUpdate()\n\t\t--[[\n\t\tif Modules.input.mode.Value == \"mobile\" then\n\t\t\tframe.UIListLayout.FillDirection = Enum.FillDirection.Vertical\n\t\t\tframe.UIListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Right\n\t\t\tframe.Position = UDim2.new(1, -295,1, -74)\n\t\t\tframe.AnchorPoint = Vector2.new(1,0)\n\t\telse\n\t\t\tframe.UIListLayout.FillDirection = Enum.FillDirection.Horizontal\n\t\t\tframe.UIListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center\n\t\t\tframe.Size = UDim2.new(0.65,0,0,50)\n\t\t\tframe.Position = UDim2.new(0.5, 0, 1, -120)\n\t\t\tframe.AnchorPoint = Vector2.new(0.5,0)\n\t\tend\n\t\t]]\n\n\tend\n\n\tModules.input.mode.changed:connect(inputUpdate)\n\tinputUpdate()\n\nend\n\nreturn module"
  },
  {
    "path": "src/StarterGui/statusEffects.lua",
    "content": "-- local status effect display\n-- berezaa\nlocal module = {}\nlocal ui = script.Parent.gameUI.statusEffects\n\nfunction module.init(Modules)\n\n\tlocal tween = Modules.tween\n\tlocal utilities = Modules.utilities\n\n\tlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\t\tlocal itemLookup = require(replicatedStorage:WaitForChild(\"itemData\"))\n\t\tlocal abilityLookup = require(replicatedStorage:WaitForChild(\"abilityLookup\"))\n\n\tlocal function updateStatusEffects(newValue)\n\t\tfor i, child in pairs(ui.contents:GetChildren()) do\n\t\t\tif child:IsA(\"GuiObject\") then\n\t\t\t\tchild:Destroy()\n\t\t\tend\n\t\tend\n\n\t\tlocal character = game.Players.LocalPlayer.Character\n\t\tif character and character.PrimaryPart then\n\n\t\t\tlocal success, statusEffectData = utilities.safeJSONDecode(newValue)\n\n\t\t\tif success then\n\t\t\t\tfor i, statusEffect in pairs(statusEffectData) do\n\t\t\t\t\tif (not statusEffect.hideInStatusBar) and (not statusEffect.statusEffectModifier.hideInStatusBar) then\n\t\t\t\t\t\tlocal indicator = ui.sample:Clone()\n\n\t\t\t\t\t\tlocal sourceType = statusEffect.sourceType\n\t\t\t\t\t\tlocal sourceId = statusEffect.sourceId\n\t\t\t\t\t\tlocal variant = statusEffect.variant\n\t\t\t\t\t\tlocal executionData = {variant = variant}\n\n\t\t\t\t\t\tif not statusEffect.icon then\n\t\t\t\t\t\t\tif sourceType == \"item\" then\n\t\t\t\t\t\t\t\tlocal itemData = itemLookup[sourceId]\n\t\t\t\t\t\t\t\tindicator.itemIcon.Image = itemData.image\n\t\t\t\t\t\t\t\tindicator.itemIcon.Visible = true\n\t\t\t\t\t\t\telseif sourceType == \"ability\" then\n\t\t\t\t\t\t\t\tlocal abilityData = abilityLookup[sourceId](nil, executionData)\n\t\t\t\t\t\t\t\tindicator.Image = abilityData.image\n\t\t\t\t\t\t\t\tindicator.itemIcon.Visible = false\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tlocal abilityData = abilityLookup[sourceId]\n\t\t\t\t\t\t\tindicator.itemIcon.Image = statusEffect.icon\n\t\t\t\t\t\t\tindicator.itemIcon.Visible = true\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tindicator.Parent = ui.contents\n\t\t\t\t\t\tindicator.Visible = true\n\n\t\t\t\t\t\tif statusEffect.ticksNeeded then\n\t\t\t\t\t\t\tlocal durationLeft = (statusEffect.ticksNeeded - statusEffect.ticksMade) / Modules.configuration.getConfigurationValue(\"activeStatusEffectTickTimePerSecond\")\n\n\t\t\t\t\t\t\tindicator.progress.Size = UDim2.new(1, 0, 1 - (durationLeft / statusEffect.statusEffectModifier.duration), 0)\n\t\t\t\t\t\t\ttween(indicator.progress, {\"Size\"}, UDim2.new(1,0,1,0), durationLeft - 0.5, Enum.EasingStyle.Linear)\n\n\t\t\t\t\t\t\tgame.Debris:AddItem(indicator, durationLeft)\n\t\t\t\t\t\telse\n\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\t--[{\"sourceId\":2,\"duration\":45,\"id\":\"regenerate\",\"durationLeft\":45,\"sourceType\":\"ability\"}]\n\tend\n\n\tupdateStatusEffects(game.Players.LocalPlayer.Character.PrimaryPart.statusEffectsV2.Value)\n\tgame.Players.LocalPlayer.Character.PrimaryPart.statusEffectsV2.changed:connect(updateStatusEffects)\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/storage.lua",
    "content": "local module = {}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal modules = require(replicatedStorage.modules)\nlocal network \t= modules.load(\"network\")\nlocal utilities = modules.load(\"utilities\")\nlocal itemData = require(replicatedStorage.itemData)\n\n-- todo: remove this disgusting absolute reference\n\nlocal player = game.Players.LocalPlayer\nlocal playerGui = player.PlayerGui\nlocal ui = playerGui.gameUI.menu_storage\n\nlocal content = ui.Frame.content\n\nlocal lastStorageDataReceived = nil\nlocal storageSlotPairing = {}\n\nlocal lastSelected\n\n\nfunction module.init(Modules)\n\tlocal uiCreator = Modules.uiCreator\n\n\tlocal function onStorageItemMouseEnter(storageItem)\n\t\tlastSelected = storageItem\n\t\tlocal storageSlotData = storageSlotPairing[storageItem]\n\t\tif storageSlotData then\n\t\t\tlocal itemBaseData = itemData[storageSlotData.id]\n\t\t\tif itemBaseData then\n\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\", itemBaseData, \"storage\", storageSlotData)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function onStorageItemMouseLeave(storageItem)\n\t\tif lastSelected == storageItem then\n\t\t\t-- clears last selected\n\t\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\t\tend\n\tend\n\n\tlocal storageItemTemplate = ui:WaitForChild(\"storageItemTemplate\")\n\n\tlocal function onStorageItemDoubleClicked(storageItem)\n\t\tif storageSlotPairing[storageItem] then\n\t\t\t-- transfer item back to storage\n\t\t\tlocal success, reason = network:invokeServer(\"playerRequest_transferStorageToInventory\", storageSlotPairing[storageItem])\n\n\t\tend\n\tend\n\n\tlocal function onGetStorageSlotDataFromStorageItem(storageItem)\n\t\treturn storageSlotPairing[storageItem]\n\tend\n\n\tlocal function updateStorage(storageData)\n\t\tif storageData then\n\t\t\tlastStorageDataReceived = storageData\n\t\tend\n\n\t\tlocal currentSelected = game.GuiService.SelectedObject\n\t\tlocal selectionName, selectionParent\n\t\tif currentSelected and currentSelected:IsDescendantOf(ui) then\n\t\t\tselectionName = currentSelected.Name\n\t\t\tselectionParent = currentSelected.Parent\n\t\tend\n\n\t\tif lastStorageDataReceived then\n\t\t\tstorageSlotPairing = {}\n\t\t\tfor i, storageItem in pairs(content:GetChildren()) do\n\t\t\t\tif storageItem:FindFirstChild(\"item\") then\n\t\t\t\t\tstorageItem:Destroy()\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal currCells\t= 0\n\n\t\t\tfor i, storageSlotData in pairs(lastStorageDataReceived) do\n\n\t\t\t\tlocal storageItemBaseData = itemData[storageSlotData.id]\n\n\t\t\t\tif storageItemBaseData then\n\n\t\t\t\t\t\tlocal storageItem \t\t\t\t\t\t\t\t= storageItemTemplate:Clone()\n\n\t\t\t\t\t\tif storageItemBaseData.canBeBound then\n\t\t\t\t\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\t\t\t\t\ttag.Name = \"bindable\"\n\t\t\t\t\t\t\ttag.Parent = storageItem\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tstorageItem.ImageTransparency \t\t\t\t\t= 0\n\t\t\t\t\t\tstorageItem.item.Image \t\t\t\t\t\t\t\t= storageItemBaseData.image\n\t\t\t\t\t\tstorageItem.item.ImageColor3 = Color3.new(1,1,1)\n\t\t\t\t\t\tif storageSlotData.dye then\n\t\t\t\t\t\t\tstorageItem.item.ImageColor3 = Color3.fromRGB(storageSlotData.dye.r, storageSlotData.dye.g, storageSlotData.dye.b)\n\t\t\t\t\t\tend\n\t\t\t\t\t\tstorageItem.item:WaitForChild(\"duplicateCount\").Text \t= (storageSlotData.stacks and storageItemBaseData.canStack and storageSlotData.stacks > 1) and tostring(storageSlotData.stacks) or \"\"\n\t\t\t\t\t\tstorageItem.Parent \t\t\t\t\t\t\t\t= content\n\t\t\t\t\t\tstorageItem.LayoutOrder \t\t\t\t\t\t\t= i\n\t\t\t\t\t\tstorageItem.Name \t\t\t\t\t\t\t\t\t= tostring(i)\n\n\t\t\t\t\t\tlocal storageDataCopy = utilities.copyTable(storageSlotData)\n\t\t\t\t\t\tstorageDataCopy.position = i\n\n\t\t\t\t\t\tstorageSlotPairing[storageItem.item] = storageDataCopy\n\n\n\t\t\t\t\t\tlocal titleColor\n\t\t\t\t\t\tif storageSlotData then\n\t\t\t\t\t\t\ttitleColor = Modules.itemAcquistion.getTitleColorForInventorySlotData(storageSlotData)\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\ttitleColor = titleColor or storageItemBaseData.nameColor\n\n\t\t\t\t\t\tstorageItem.frame.ImageColor3 = titleColor or Color3.fromRGB(106, 105, 107)\n\t\t\t\t\t\tstorageItem.shine.ImageColor3 = titleColor or Color3.fromRGB(179, 178, 185)\n\t\t\t\t\t\tstorageItem.shine.ImageTransparency = titleColor and 0.47 or 0.66\n\n\t\t\t\t\t\tstorageItem.item.MouseEnter:connect(function() onStorageItemMouseEnter(storageItem.item) end)\n\t\t\t\t\t\tstorageItem.item.SelectionGained:connect(function() onStorageItemMouseEnter(storageItem.item) end)\n\n\t\t\t\t\t\tstorageItem.item.MouseLeave:connect(function() onStorageItemMouseLeave(storageItem.item) end)\n\t\t\t\t\t\tstorageItem.item.SelectionLost:connect(function() onStorageItemMouseLeave(storageItem.item) end)\n\n\n\t\t\t\t\t\tuiCreator.drag.setIsDragDropFrame(storageItem.item)\n\t\t\t\t\t\tuiCreator.setIsDoubleClickFrame(storageItem.item, 0.2, onStorageItemDoubleClicked)\n\n\n\t\t\t\t\t\tcurrCells = currCells + 1\n\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tfor i = 1, 20 do\n\t\t\t\tif not content:FindFirstChild(tostring(i)) then\n\t\t\t\t\tlocal storageItem \t\t\t\t= ui.storageItemTemplate:Clone()\n\t\t\t\t\tstorageItem.item.duplicateCount.Text \t= \"\"\n\t\t\t\t\tstorageItem.item.Image = \"\"\n\t\t\t\t\tstorageItem.item.Visible = true\n\t\t\t\t\tstorageItem.frame.Visible = false\n\t\t\t\t\tstorageItem.shine.Visible = false\n\t\t\t\t\tstorageItem.ImageTransparency = 0.5\n\t\t\t\t\tstorageItem.shadow.ImageTransparency = 0.5\n\t\t\t\t\tstorageItem.Name \t\t\t\t\t= tostring(i)\n\t\t\t\t\tstorageItem.LayoutOrder \t\t\t= i\n\t\t\t\t\tstorageItem.Parent \t\t\t\t= content\n\n\t\t\t\t\tuiCreator.drag.setIsDragDropFrame(storageItem.item)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tcontent.CanvasSize = UDim2.new(0, 0, 0, math.ceil(math.max(currCells, 20) / 5) * 66)\n\n\t\t\tif selectionParent and selectionName then\n\t\t\t\tlocal newSelection = selectionParent:FindFirstChild(selectionName)\n\t\t\t\tif newSelection then\n\t\t\t\t\tgame.GuiService.SelectedObject = newSelection\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\n\tfunction module.open()\n\n\t\tif ui.Visible then\n\t\t\tModules.playerMenu.closeSelected()\n\t\t\tif ui.Parent.Parent.Visible then\n\t\t\t\tModules.focus.toggle(ui.Parent.Parent)\n\t\t\tend\n\t\telse\n\t\t\t--ui.Visible = true\n\t\t\t--[[\n\t\t\tui.Parent.Parent.UIScale.Scale = (Modules.input.menuScale or 1) * 0.75\n\t\t\tModules.tween(ui.Parent.Parent.UIScale, {\"Scale\"}, (Modules.input.menuScale or 1), 1, Enum.EasingStyle.Bounce)\n\t\t\t]]\n\t\t\tlocal globalData = network:invoke(\"getCacheValueByNameTag\", \"globalData\")\n\t\t\tif globalData and globalData.itemStorage then\n\t\t\t\tupdateStorage(globalData.itemStorage)\n\t\t\t\tModules.playerMenu.selectMenu(ui, Modules[script.Name])\n\t\t\tend\n\n\t\tend\n\tend\n\n\n\tfunction module.close()\n\t\tModules.playerMenu.closeSelected()\n\t\tif ui.Parent.Parent.Visible then\n\t\t\tModules.focus.toggle(ui.Parent.Parent)\n\t\tend\n\tend\n\n\n\tlocal function onPropogationRequestToSelf(propogationNameTag, propogationData)\n\t\tif propogationNameTag == \"globalData\" and ui.Visible and propogationData.itemStorage then\n\t\t\tupdateStorage(propogationData.itemStorage)\n\t\tend\n\tend\n\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\n\tnetwork:create(\"getStorageSlotDataFromStorageItem\", \"BindableFunction\", \"OnInvoke\", onGetStorageSlotDataFromStorageItem)\n\n\tnetwork:create(\"openStorage\", \"BindableFunction\", \"OnInvoke\", function()\n\t\tmodule.open()\n\tend)\n\tnetwork:create(\"closeStorage\", \"BindableFunction\", \"OnInvoke\", function()\n\t\tmodule.close()\n\tend)\nend\n\nreturn module"
  },
  {
    "path": "src/StarterGui/taxi.lua",
    "content": "local module = {}\n\nlocal ui = script.Parent.gameUI.dialogueFrame.contents.taxi\n\nfunction module.init(Modules)\n\n\tlocal network = Modules.network\n\tlocal utilities = Modules.utilities\n\n\tlocal locations = network:invoke(\"getCacheValueByNameTag\", \"locations\")\n\n\tlocal buttonCount\n\n\tlocal contents = ui.contents\n\n\tlocal buttonCount = 0\n\n\tfor placeIdString, placeData in pairs(locations) do\n\n\t\tlocal placeId = tonumber(placeIdString)\n\n\t\tif placeId ~= game.PlaceId then\n\t\t\tlocal originPlaceId = utilities.originPlaceId(placeId)\n\n\t\t\tlocal button = ui.sample:Clone()\n\t\t\tbutton.Name = placeIdString\n\t\t\tbutton.LayoutOrder = math.floor((os.time() - placeData.visited) ^ 0.25)\n\t\t\tbutton.location.Text = \"-\"\n\n\t\t\tbutton.BGHolder.BG.Image = \"https://www.roblox.com/Thumbs/Asset.ashx?width=768&height=432&assetId=\"..originPlaceId\n\t\t\tbutton.BGHolder.BG.Visible = true\n\n\t\t\tspawn(function()\n\t\t\t\tlocal info = game.MarketplaceService:GetProductInfo(originPlaceId ,Enum.InfoType.Asset)\n\t\t\t\tif info then\n\t\t\t\t\tbutton.location.Text = info.Name\n\t\t\t\tend\n\t\t\tend)\n\t\t\tbutton.location.Visible = true\n\t\t\tbutton.Parent = contents\n\t\t\tbutton.Active = true\n\t\t\tbutton.Selectable = true\n\t\t\tbutton.Visible = true\n\t\t\tbuttonCount = buttonCount + 1\n\t\t\tbutton.location.TextWrapped = true\n\t\t\tbutton.Activated:connect(function()\n\t\t\t\tnetwork:invoke(\"dialogueMoveToId\", \"spawns\", {taxiLocation = placeIdString, taxiLocationName = button.location.Text})\n\t\t\tend)\n\t\tend\n\tend\n\n\tif buttonCount < 12 then\n\t\tfor i = buttonCount, 11 do\n\t\t\tlocal button = ui.sample:Clone()\n\t\t\tbutton.Name = \"empty\"\n\t\t\tbutton.BGHolder.BG.Visible = false\n\t\t\tbutton.location.Visible = false\n\t\t\tbutton.empty.Visible = true\n\t\t\tbutton.LayoutOrder = 999\n\n\t\t\tlocal tooltip = Instance.new(\"StringValue\")\n\t\t\ttooltip.Name = \"tooltip\"\n\t\t\ttooltip.Value = \"Undiscovered location\"\n\t\t\ttooltip.Parent = button\n\n\t\t\tbutton.Parent = contents\n\t\t\tbutton.Active = true\n\t\t\tbutton.Selectable = true\n\t\t\tbutton.Visible = true\n\t\t\tbuttonCount = buttonCount + 1\n\n\t\t\tbutton.Activated:connect(function()\n\t\t\t\tnetwork:invoke(\"dialogueMoveToId\", \"undiscovered\")\n\t\t\tend)\n\t\tend\n\tend\n\n\tlocal rows = math.ceil(buttonCount / 2)\n\tcontents.CanvasSize = UDim2.new(0, 0, 0, rows * (contents.UIGridLayout.CellSize.Y.Offset + contents.UIGridLayout.CellPadding.Y.Offset) + 10)\n\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterGui/textInputPrompt.lua",
    "content": "local module = {}\n\nlocal ui = script.Parent.gameUI.textInputPrompt\n\nfunction module.init(Modules)\n\tlocal network = Modules.network\n\tlocal connection\n\tlocal currentPrompt\n\n\tfunction module.hide()\n\t\tif connection then\n\t\t\tconnection:disconnect()\n\t\t\tcurrentPrompt = nil\n\t\tend\n\n\t\tif ui.Visible then\n\t\t\tModules.focus.toggle(ui)\n\t\tend\n\tend\n\n\tfunction module.prompt(info)\n\n\t\tlocal prompt = info.prompt or \".....\"\n\n\t\tif connection then\n\t\t\tconnection:disconnect()\n\t\tend\n\t\tcurrentPrompt = prompt\n\n\t\tui.Frame.code.TextBox.Text = \"\"\n\t\tui.Frame.code.TextBox.PlaceholderText = prompt\n\t\tif not ui.Visible then\n\t\t\tModules.focus.toggle(ui)\n\t\tend\n\n\t\tlocal input\n\n\t\tconnection = ui.Frame.send.Activated:connect(function()\n\t\t\tinput = ui.Frame.code.TextBox.Text\n\t\tend)\n\n\t\trepeat wait() until input or currentPrompt ~= prompt\n\n\t\tif input then\n\t\t\tmodule.hide()\n\t\t\treturn input\n\t\tend\n\n\tend\n\n\tnetwork:create(\"textInputPrompt\", \"BindableFunction\", \"OnInvoke\", module.prompt)\n\n\tui.Frame.close.Activated:connect(module.hide)\nend\n\nreturn module"
  },
  {
    "path": "src/StarterGui/trading.lua",
    "content": "-- local trading ui\n-- written by berezaa & polymorphic\n\n-- jesus christ if there is a competition for most cursed script it may very well be this one\n\nlocal module = {}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal modules = require(replicatedStorage.modules)\nlocal network \t\t= modules.load(\"network\")\nlocal utilities \t= modules.load(\"utilities\")\nlocal mapping \t\t= modules.load(\"mapping\")\nlocal configuration = modules.load(\"configuration\")\nlocal itemLookup = require(replicatedStorage.itemData)\n\nlocal player = game.Players.LocalPlayer\nlocal tradeFrame = script.Parent.gameUI.menu_trade\n\nlocal inventorySlotPairing = {}\n\n-- Called after all modules are required\nfunction module.init(Modules)\n\n\tlocal yourTrade \t\t\t= tradeFrame.yourTrade\n\tlocal theirTrade \t\t\t= tradeFrame.theirTrade\n\tlocal lastTradeSessionData \t= nil\n\n\tlocal lastSelected\n\n\tlocal myInventoryTransferDataCollection = nil\n\n\tlocal function onGoldInputAmountChanged()\n\t\tif yourTrade.amount.Visible then\n\t\t\tif yourTrade.amount.TextBox.Text ~= \"\" then\n\t\t\t\tlocal currentGold = network:invoke(\"getCacheValueByNameTag\", \"gold\")\n\n\t\t\t\t-- clamp to your current gold amount\n\t\t\t\tyourTrade.amount.TextBox.Text = tostring(math.clamp(tonumber(yourTrade.amount.TextBox.Text) or 0, 0, tonumber(currentGold)))\n\n\t\t\t\tyourTrade.add.ImageColor3 = Color3.new(0, 1, 0)\n\t\t\t\tyourTrade.add.inner.Text = \">\"\n\t\t\telse\n\t\t\t\tyourTrade.add.ImageColor3 = Color3.new(1, 0, 0)\n\t\t\t\tyourTrade.add.inner.Text = \"-\"\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function onTradeSessionChanged(tradeSessionData)\n\n\tend\n\n\tlocal function onPlayerDragInventoryItemIntoTradeWindow(inventorySlotData)\n\t\tif myInventoryTransferDataCollection then\n\n\t\tend\n\tend\n\n\tlocal function tradeEnded()\n\n\tend\n\n\n\n\t-- returns my playerTradeData, other player's playerTradeData\n\tlocal function getMyPlayerTradeData(tradeSessionData)\n\t\treturn tradeSessionData.playerTradeSessionData_player1.player == player and tradeSessionData.playerTradeSessionData_player1 or tradeSessionData.playerTradeSessionData_player2, tradeSessionData.playerTradeSessionData_player1.player == player and tradeSessionData.playerTradeSessionData_player2 or tradeSessionData.playerTradeSessionData_player1\n\tend\n\n\t-- Modules.trading.startTrade\n\tfunction module.startTrade(tradeSessionData)\n\n\t\t-- we don't know if our client is the trade starter (player1)\n\t\tlocal playerTradeData, otherPlayerTradeData = getMyPlayerTradeData(tradeSessionData)\n\n\t\ttheirTrade.title.Text \t= otherPlayerTradeData.player.Name\n\t\tyourTrade.title.Text \t= \"Your offer\"\n\n\t\t-- show tradeFrame, and set lastTradeSessionData to keep track\n\t\ttradeFrame.Visible \t\t= true\n\t\tlastTradeSessionData \t= tradeSessionData\n\n\t\t-- reset myInventoryTransferDataCollection\n\t\tmyInventoryTransferDataCollection = {}\n\n\t\t-- hide equipment\n\t\tModules.equipment.hide()\n\t\ttradeFrame.Visible = true\n--\t\ttradeFrame.Parent.Visible = true\n\t\tif not tradeFrame.Parent.Parent.Visible then\n\t\t\tModules.playerMenu.open()\n\t\tend\n\tend\n\n\n\n\tfunction module.endTrade(wasInitiatedByClient)\n\t\ttradeFrame.Visible = false\n\t\tModules.equipment.show()\n\n\t\t-- if the client is ending the trade, then let the server know we're not interested anymore\n\t\tif wasInitiatedByClient and lastTradeSessionData then\n\t\t\tnetwork:invokeServer(\"playerRequest_cancelTrade\", lastTradeSessionData.guid)\n\t\tend\n\n\t\t-- reset internals\n\t\tmyInventoryTransferDataCollection \t= nil\n\t\tlastTradeSessionData \t\t\t\t= nil\n\tend\n\n\tmodule.tradeCollection = {}\n\n\tfunction module.clearLocalTradeSlot(button)\n\t\tinventorySlotPairing[button] = nil\n\t\tmodule.tradeCollection[button.Name] = nil\n\tend\n\n\tfunction module.setLocalTradeSlot(i, inventorySlotData)\n\t\tlocal inventoryReal = itemLookup[inventorySlotData.id]\n\n\n\t\t-- check for existing conflicts\n\t\tfor e, tradeSlotData in pairs(module.tradeCollection) do\n\n\t\t\tif tradeSlotData and tradeSlotData.position == inventorySlotData.position then\n\t\t\t\tlocal tradeReal = itemLookup[tradeSlotData.id]\n\t\t\t\tif inventoryReal and tradeReal then\n\t\t\t\t\tif inventoryReal.category == tradeReal.category then\n\t\t\t\t\t\tmodule.tradeCollection[tostring(e)] = nil\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\tend\n\n\t\tinventorySlotData.stacks = inventorySlotData.stacks or 1\n\n\t\tmodule.tradeCollection[tostring(i)] = inventorySlotData\n\n\t\tnetwork:invokeServer(\"playerRequest_updatePlayerTradeSessionData\", lastTradeSessionData.guid, \"inventoryTransferDataCollection\", module.tradeCollection)\n\tend\n\n\tfunction module.processDoubleClickFromInventory(inventorySlotData)\n\t\tif inventorySlotData then\n\t\t\tlocal inventoryReal = itemLookup[inventorySlotData.id]\n\t\t\tif inventoryReal then\n\n\t\t\t\tlocal takenButtons = {}\n\n\t\t\t\tfor i, tradeSlotData in pairs(module.tradeCollection) do\n\t\t\t\t\tif tradeSlotData then\n\t\t\t\t\t\ttakenButtons[i] = true\n\t\t\t\t\t\tif tradeSlotData.id == inventorySlotData.id and tradeSlotData.position == inventorySlotData.position then\n\t\t\t\t\t\t\t-- item already in the trade, remove it.\n\t\t\t\t\t\t\tmodule.tradeCollection[i] = nil\n\t\t\t\t\t\t\treturn network:invokeServer(\"playerRequest_updatePlayerTradeSessionData\", lastTradeSessionData.guid, \"inventoryTransferDataCollection\", module.tradeCollection)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tfor i,button in pairs(tradeFrame.yourTrade.contents:GetChildren()) do\n\t\t\t\t\tif button:IsA(\"GuiButton\") and not takenButtons[button.Name] then\n\t\t\t\t\t\tmodule.tradeCollection[button.Name] = inventorySlotData\n\t\t\t\t\t\treturn network:invokeServer(\"playerRequest_updatePlayerTradeSessionData\", lastTradeSessionData.guid, \"inventoryTransferDataCollection\", module.tradeCollection)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tfunction module.swap(button1, button2)\n\t\tlocal button1pairing = inventorySlotPairing[button1]\n\t\tlocal button2pairing = inventorySlotPairing[button2]\n\n\t\tmodule.tradeCollection[button1.Name] = button2pairing\n\t\tmodule.tradeCollection[button2.Name] = button1pairing\n\n\t\tnetwork:invokeServer(\"playerRequest_updatePlayerTradeSessionData\", lastTradeSessionData.guid, \"inventoryTransferDataCollection\", module.tradeCollection)\n\n\n\tend\n\n\tlocal function onTradeSessionChanged(tradeSessionData)\n--\t\tif lastTradeSessionData == nil or lastTradeSessionData.guid ~= tradeSessionData.guid then\n\t\tif lastTradeSessionData ~= tradeSessionData then\n\t\t\tmodule.startTrade(tradeSessionData)\n\t\tend\n\n\t\t-- wooo!!!\n\t\t-- todo: make this do something pretty\n\t\tif tradeSessionData.state == \"canceled\" or tradeSessionData.state == \"completed\" then\n\t\t\tmodule.tradeCollection = {}\n\t\t\tmodule.endTrade()\n\t\tend\n\n\t\ttradeFrame.countdown.Visible = false\n\n\t\tif tradeSessionData.state == \"countdown\" then\n\t\t\ttradeFrame.countdown.Text = \"6\"\n\t\t\ttradeFrame.countdown.Visible = true\n\t\t\tspawn(function()\n\t\t\t\tlocal count = 6\n\t\t\t\twhile lastTradeSessionData.state == \"countdown\" and tradeFrame.countdown.Text == tostring(count) and count > 0 do\n\t\t\t\t\tcount = count - 1\n\t\t\t\t\ttradeFrame.countdown.Text = tostring(count)\n\t\t\t\t\twait(1)\n\t\t\t\tend\n\t\t\t\tif count <= 0 then\n\t\t\t\t\ttradeFrame.countdown.Visible = false\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\n\t\tlocal yourPlayerTradeSessionData = (tradeSessionData.playerTradeSessionData_player1.player == player and tradeSessionData.playerTradeSessionData_player1) or (tradeSessionData.playerTradeSessionData_player2.player == player and tradeSessionData.playerTradeSessionData_player2) or {}\n\t\tlocal theirPlayerTradeSessionData = (tradeSessionData.playerTradeSessionData_player1.player == player and tradeSessionData.playerTradeSessionData_player2) or (tradeSessionData.playerTradeSessionData_player2.player == player and tradeSessionData.playerTradeSessionData_player1) or {}\n\n\t\t-- gold\n--\t\ttradeFrame.yourTrade.gold.Text = yourPlayerTradeSessionData.gold or 0\n--\t\ttradeFrame.theirTrade.gold.Text = theirPlayerTradeSessionData.gold or 0\n\n\t\tModules.money.setLabelAmount(tradeFrame.yourTrade.gold, yourPlayerTradeSessionData.gold or 0)\n\t\tModules.money.setLabelAmount(tradeFrame.theirTrade.gold, theirPlayerTradeSessionData.gold or 0)\n\n\t\t-- trade state\n\t\ttradeFrame.yourTrade.approved.Visible = false\n\t\ttradeFrame.yourTrade.denied.Visible = false\n\t\ttradeFrame.theirTrade.approved.Visible = false\n\t\ttradeFrame.theirTrade.denied.Visible = false\n\n\t\ttradeFrame.yourTrade.ImageColor3 = Color3.fromRGB(189, 189, 189)\n\t\ttradeFrame.theirTrade.ImageColor3 = Color3.fromRGB(139, 139, 139)\n\n\t\tif theirPlayerTradeSessionData.state == \"approved\" then\n\t\t\ttradeFrame.theirTrade.approved.Visible = true\n\t\t\ttradeFrame.theirTrade.ImageColor3 = Color3.fromRGB(136, 192, 132)\n\t\telseif theirPlayerTradeSessionData.state == \"denied\" then\n\t\t\ttradeFrame.theirTrade.denied.Visible = true\n\t\t\ttradeFrame.theirTrade.ImageColor3 = Color3.fromRGB(188, 109, 111)\n\t\tend\n\n\t\tif yourPlayerTradeSessionData.state == \"approved\" then\n\t\t\ttradeFrame.yourTrade.approved.Visible = true\n\t\t\ttradeFrame.yourTrade.ImageColor3 = Color3.fromRGB(97, 141, 98)\n\t\telseif yourPlayerTradeSessionData.state == \"denied\" then\n\t\t\ttradeFrame.yourTrade.denied.Visible = true\n\t\t\ttradeFrame.yourTrade.ImageColor3 = Color3.fromRGB(138, 87, 88)\n\t\tend\n\n\t\tlocal yourCollection = yourPlayerTradeSessionData.inventoryTransferDataCollection or {}\n\t\tlocal theirCollection = theirPlayerTradeSessionData.inventoryTransferDataCollection or {}\n\n\t\tmodule.tradeCollection = yourCollection\n\n\t\tlocal yourButtons = tradeFrame.yourTrade.contents:GetChildren()\n\n\t\tfor i, button in pairs(yourButtons) do\n\t\t\tif button:IsA(\"ImageButton\") then\n\t\t\t\tbutton.Image = \"\"\n\t\t\t\tinventorySlotPairing[button] = nil\n\t\t\t\tbutton.duplicateCount.Visible = false\n\t\t\t\tlocal buttonItemData = yourCollection[button.Name] or {}\n\t\t\t\tif buttonItemData then\n\t\t\t\t\tlocal itemBaseData = itemLookup[buttonItemData.id]\n\t\t\t\t\tif itemBaseData then\n\t\t\t\t\t\tbutton.Image = itemBaseData.image\n\t\t\t\t\t\tbutton.ImageColor3 = Color3.new(1,1,1)\n\t\t\t\t\t\tif buttonItemData.dye then\n\t\t\t\t\t\t\tbutton.ImageColor3 = Color3.fromRGB(buttonItemData.dye.r, buttonItemData.dye.g, buttonItemData.dye.b)\n\t\t\t\t\t\tend\n\t\t\t\t\t\tinventorySlotPairing[button] = buttonItemData\n\t\t\t\t\tend\n\t\t\t\t\tif buttonItemData.stacks and buttonItemData.stacks > 1 then\n\t\t\t\t\t\tbutton.duplicateCount.Text = tostring(buttonItemData.stacks)\n\t\t\t\t\t\tbutton.duplicateCount.Visible = true\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tlocal theirButtons = tradeFrame.theirTrade.contents:GetChildren()\n\n\t\tfor i, button in pairs(theirButtons) do\n\t\t\tif button:IsA(\"ImageButton\") then\n\t\t\t\tbutton.Image = \"\"\n\t\t\t\tinventorySlotPairing[button] = nil\n\t\t\t\tbutton.duplicateCount.Visible = false\n\t\t\t\tlocal buttonItemData = theirCollection[button.Name] or {}\n\t\t\t\tif buttonItemData then\n\t\t\t\t\tlocal itemBaseData = itemLookup[buttonItemData.id]\n\t\t\t\t\tif itemBaseData then\n\t\t\t\t\t\tbutton.Image = itemBaseData.image\n\t\t\t\t\t\tbutton.ImageColor3 = Color3.new(1,1,1)\n\t\t\t\t\t\tif buttonItemData.dye then\n\t\t\t\t\t\t\tbutton.ImageColor3 = Color3.fromRGB(buttonItemData.dye.r, buttonItemData.dye.g, buttonItemData.dye.b)\n\t\t\t\t\t\tend\n\t\t\t\t\t\tinventorySlotPairing[button] = buttonItemData\n\t\t\t\t\tend\n\t\t\t\t\tif buttonItemData.stacks and buttonItemData.stacks > 1 then\n\t\t\t\t\t\tbutton.duplicateCount.Text = tostring(buttonItemData.stacks)\n\t\t\t\t\t\tbutton.duplicateCount.Visible = true\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\n\n\tlocal function setYourGoldAmount(amount)\n--\t\tyourTrade.gold.Text = \"$\"..tostring(amount)\n\n\t\tModules.money.setLabelAmount(tradeFrame.yourTrade.gold, tonumber(amount))\n\t\t-- >>> network: communicate trade change to server\n\n\t\tnetwork:invokeServer(\"playerRequest_updatePlayerTradeSessionData\", lastTradeSessionData.guid, \"gold\", tonumber(amount))\n\tend\n\n\tlocal function onMouseButton1Click_addMoneyButton()\n\t\tif yourTrade.amount.Visible then\n\t\t\t-- change the amount of money you are giving\n\t\t\tif yourTrade.amount.TextBox.Text ~= \"\" then\n\t\t\t\tlocal currentGold = network:invoke(\"getCacheValueByNameTag\", \"gold\")\n\n\t\t\t\t-- clamp to your current gold amount\n\t\t\t\tlocal amount = tostring(math.clamp(tonumber(yourTrade.amount.TextBox.Text) or 0, 0, tonumber(currentGold)))\n\n\t\t\t\tif amount then\n\t\t\t\t\tsetYourGoldAmount(amount)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tyourTrade.amount.Visible = false\n\t\t\tyourTrade.add.ImageColor3 = Color3.new(1,1,0)\n\t\t\tyourTrade.add.inner.Text = \"+\"\n\t\telse\n\t\t\t-- open the change money input\n\t\t\tyourTrade.amount.TextBox.Text = \"\"\n\t\t\tyourTrade.amount.Visible = true\n\t\t\tyourTrade.add.ImageColor3 = Color3.new(1,0,0)\n\t\t\tyourTrade.add.inner.Text = \"-\"\n\t\tend\n\tend\n\n\tlocal function onPlayerRequest_requestOpenTradeWithPlayerReceived(playerTradeInitiator, tradeSessionId)\n\t\tlocal success = network:invoke(\"promptAction\", playerTradeInitiator.Name .. \" wants to trade! Would you like to trade with them?\")\n\n\t\tif success then\n\t\t\tnetwork:invokeServer(\"playerRequest_acceptTradeRequest\", tradeSessionId)\n\t\tend\n\tend\n\n\ttradeFrame.buttons.leave.Activated:connect(function()\n\t\tmodule.endTrade(true)\n\tend)\n\n\ttradeFrame.buttons.accept.Activated:connect(function()\n\t\tnetwork:invokeServer(\"playerRequest_updatePlayerTradeSessionData\", lastTradeSessionData.guid, \"state\", \"approved\")\n\tend)\n\n\ttradeFrame.buttons.deny.Activated:connect(function()\n\t\tnetwork:invokeServer(\"playerRequest_updatePlayerTradeSessionData\", lastTradeSessionData.guid, \"state\", \"denied\")\n\tend)\n\n\tyourTrade.add.MouseButton1Click:connect(onMouseButton1Click_addMoneyButton)\n\tyourTrade.amount.TextBox:GetPropertyChangedSignal(\"Text\"):connect(onGoldInputAmountChanged)\n\n\tnetwork:connect(\"signal_playerTradeRequest\", \"OnClientEvent\", onPlayerRequest_requestOpenTradeWithPlayerReceived)\n\tnetwork:connect(\"signal_tradeSessionChanged\", \"OnClientEvent\", onTradeSessionChanged)\n\n\t--postInit\n\tspawn(function()\n\t\tlocal network = Modules.network\n\n\n\t\tlocal function onInventoryItemMouseEnter(inventoryItem)\n\t\t\tlastSelected = inventoryItem\n\t\t\tlocal inventorySlotData = inventorySlotPairing[inventoryItem]\n\t\t\tif inventorySlotData then\n\t\t\t\tlocal itemBaseData = itemLookup[inventorySlotData.id]\n\t\t\t\tif itemBaseData then\n\t\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\", itemBaseData, \"inventory\", inventorySlotData)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tlocal function onInventoryItemMouseLeave(inventoryItem)\n\t\t\tif lastSelected == inventoryItem then\n\t\t\t\t-- clears last selected\n\t\t\t\tnetwork:invoke(\"populateItemHoverFrame\")\n\t\t\tend\n\t\tend\n\n\t\tfor i,guiObject in pairs(tradeFrame:GetDescendants()) do\n\t\t\tif guiObject:IsA(\"GuiObject\") and guiObject:FindFirstChild(\"draggableFrame\") then\n\t\t\t\tguiObject.MouseEnter:connect(function() onInventoryItemMouseEnter(guiObject) end)\n\t\t\t\tguiObject.MouseLeave:connect(function() onInventoryItemMouseLeave(guiObject) end)\n\t\t\t\tguiObject.SelectionGained:connect(function() onInventoryItemMouseEnter(guiObject) end)\n\t\t\t\tguiObject.SelectionLost:connect(function() onInventoryItemMouseLeave(guiObject) end)\n\t\t\tend\n\t\tend\n\tend)\nend\n\nreturn module"
  },
  {
    "path": "src/StarterGui/uiCreator.lua",
    "content": "-- uiCreator controls most dyanmic ui elements (text labels, item notifcations, etc.) as well as button draggingb between menus\n\nlocal module = {}\nmodule.drag = {}\n\n-- service declarations\nlocal textService = game:GetService(\"TextService\")\nlocal tweenService = game:GetService(\"TweenService\")\nlocal userInputService = game:GetService(\"UserInputService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\n-- module requirements\nlocal modules = require(replicatedStorage.modules)\nlocal network = modules.load(\"network\")\nlocal utilities = modules.load(\"utilities\")\nlocal mapping = modules.load(\"mapping\")\nlocal tween\t= modules.load(\"tween\")\nlocal localization = modules.load(\"localization\")\n\nlocal itemData = require(replicatedStorage:WaitForChild(\"itemData\"))\nlocal itemAttributes = require(replicatedStorage:WaitForChild(\"itemAttributes\"))\n\nlocal BASE_TWEEN_INFO = TweenInfo.new(0.3, Enum.EasingStyle.Quad, Enum.EasingDirection.Out, 0, false, 0)\n\nlocal interactionPromptCache = {}\n\nlocal player = game.Players.LocalPlayer\nlocal playerGui = player.PlayerGui\n\nlocal dragDropFrameCollection = {}\nlocal currentDragFrameOriginator = nil\n\n-- ui references\nlocal ui = playerGui.gameUI\nlocal menu_inventory = ui.menu_inventory\nlocal menu_trade = ui.menu_trade\nlocal menu_enchant = ui.menu_enchant\nlocal menu_shop = ui.menu_shop\nlocal menu_storage = ui.menu_storage\nlocal menu_equipment = ui.menu_equipment\nlocal menu_abilities = ui.menu_abilities\n\nlocal interactionPromptsFrame = ui.interactionPrompts\n\nlocal effects = script.Parent.effects\n\nlocal dragDropMask = ui.dragDropMask\n\nnetwork:create(\"setGameUIEnabled\", \"BindableEvent\", \"Event\", function(enabled)\n\tui.Enabled = enabled\nend)\n\nlocal Modules\n\nfunction module.init(mods)\n\tModules = mods\n\n\tlocal function inputUpdate()\n\t\tif Modules.input.mode.Value == \"mobile\" then\n\t\t\tui.interactionPrompts.Position = UDim2.new(1,-110,1,-200)\n\t\t\tui.interactionPrompts.UIScale.Scale = Modules.input.menuScale or 1\n\t\telse\n\t\t\tui.interactionPrompts.Position = UDim2.new(1,-110,1,-130)\n\t\t\tui.interactionPrompts.UIScale.Scale = 1\n\t\tend\n\tend\n\tinputUpdate()\n\tModules.input.mode.Changed:connect(inputUpdate)\nend\n\n\nlocal IS_PROCESSING_INVENTORY_SLOT_SWITCH = false\n\nlocal lastMoneyUpdateTime\n\nfunction module.showCurrency(amount)\n\tif not Modules then\n\t\treturn false\n\tend\n\n\n\tutilities.playSound(\"coins\")\n\n\tlocal template = effects.moneyObtained:Clone()\n\n\tlocal count = template:FindFirstChild(\"count\")\n\n\tif count then\n\t\ttemplate.backdrop.UIScale.Scale = 1.15 + math.clamp(count.Value/150,0,0.75)\n\t\ttween(template.backdrop.UIScale, {\"Scale\"}, 1, 0.5)\n\telse\n\t\ttemplate.Size = UDim2.new(0,0,0,42)\n\t\tcount = Instance.new(\"IntValue\")\n\t\tcount.Name = \"count\"\n\t\tcount.Value = 0\n\t\tcount.Parent = template\n\tend\n\n\t-- display coin effect\n\tlocal iconImage = \"rbxassetid://2535600080\"\n\tlocal iconAmount = 1\n\tif amount >= 1e6 then\n\t\ticonImage = \"rbxassetid://2536432897\"\n\t\tif amount >= 5e8 then\n\t\t\ticonAmount = 4\n\t\telseif amount >= 1e8 then\n\t\t\ticonAmount = 3\n\t\telseif amount >= 1e7 then\n\t\t\ticonAmount = 2\n\t\tend\n\telseif amount >= 1e3 then\n\t\t-- silver\n\t\ticonImage = \"rbxassetid://2535600034\"\n\t\tif amount >= 5e5 then\n\t\t\ticonAmount = 4\n\t\telseif amount >= 1e5 then\n\t\t\ticonAmount = 3\n\t\telseif amount >= 1e4 then\n\t\t\ticonAmount = 2\n\t\tend\n\telse\n\t\t-- bronze\n\t\ticonImage = \"rbxassetid://2535600080\"\n\t\tif amount >= 5e2 then\n\t\t\ticonAmount = 4\n\t\telseif amount >= 1e2 then\n\t\t\ticonAmount = 3\n\t\telseif amount >= 1e1 then\n\t\t\ticonAmount = 2\n\t\tend\n\tend\n\tfor _ = 1, iconAmount do\n\t\tlocal coin = effects.coin:Clone()\n\t\tlocal finalPosition = UDim2.new(0.5, math.random(-100,100), 0.5, math.random(-50,50))\n\t\tcoin.Parent = template\n\t\tcoin.Visible = true\n\t\tcoin.Image = iconImage\n\t\tcoin.ImageTransparency = 1\n\t\tcoin.Size = UDim2.new(0,20,0,20)\n\t\ttween(coin, {\"Position\", \"ImageTransparency\", \"Size\"}, {finalPosition, 0, UDim2.new(0,32,0,32)}, 1, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)\n\t\tdelay(1, function()\n\t\t\tif coin and coin.Parent then\n\t\t\t\ttween(coin, {\"ImageTransparency\"}, {1}, 0.5)\n\t\t\t\tgame.Debris:AddItem(coin, 0.5)\n\t\t\tend\n\t\tend)\n\tend\n\n\n\n\tcount.Value = count.Value + 1\n\n\tlocal moneyUpdateTime = tick()\n\tlastMoneyUpdateTime = moneyUpdateTime\n\n\ttemplate.amount.Value = template.amount.Value + amount\n\n\tlocal totalAmount = template.amount.Value\n\n\tModules.money.setLabelAmount(template.backdrop.money, totalAmount)\n\n\tlocal xSize = template.backdrop.money.amount.AbsoluteSize.X + 32 + 16\n\ttemplate.backdrop.money.Size = UDim2.new(0, xSize, template.backdrop.money.Size.Y.Scale, template.backdrop.money.Size.Y.Offset)\n\n\ttemplate.Visible = true\n\ttemplate.Parent = interactionPromptsFrame\n\tlocal duration = 5\n\n\tlocal goalSize = UDim2.new(0, xSize + 35, 0, 42)\n\n\ttween(template,{\"Size\"},goalSize,0.5)\n\tspawn(function()\n\t\twait(duration)\n\n\t--\tif lastMoneyUpdateTime == moneyUpdateTime then\n\t\t\ttween(template,{\"Size\"},UDim2.new(0,0,0,42),0.5)\n\t\t\twait(0.5)\n\t\t\tif lastMoneyUpdateTime == moneyUpdateTime then\n\t\t\t\ttemplate:Destroy()\n\t\t\tend\n\t--\tend\n\n\n\tend)\nend\n\nfunction module.showLootUnlock(monsterViewport, realItem, tabColor, flareColor)\n\tflareColor = flareColor or tabColor\n\tlocal template = effects.monsterBook:Clone()\n\ttemplate.backdrop.contents.thumbnail.Image = realItem.image\n\ttemplate.backdrop.contents.holder:ClearAllChildren()\n\tmonsterViewport:Clone().Parent = template.backdrop.contents.holder\n\ttemplate.backdrop.ImageColor3 = tabColor\n\ttemplate.flare.ImageColor3 = flareColor\n\tlocal goalSize = template.Size\n\n\ttemplate.Visible = true\n\ttemplate.Parent = interactionPromptsFrame\n\n\tlocal indicator = template:WaitForChild(\"flare\"):clone()\n\tindicator.Parent = template\n\tindicator.Size = UDim2.new(1,6,1,0)\n\tindicator.Position = UDim2.new(1,0,0.5,0)\n\tindicator.Visible = true\n\tfor i=1,4 do\n\t\tlocal flare = template:WaitForChild(\"flare\"):clone()\n\t\tflare.Parent = template\n\t\tflare.Visible = true\n\t\tflare.Size = UDim2.new(1,4,1,4)\n\t\tflare.Position = UDim2.new(0.5,0,0.5,0)\n\t\tflare.AnchorPoint = Vector2.new(0.5,0.5)\n\t\tlocal x = (260 - 40*i)\n\t\tlocal y = (14 - 2*i)\n\n\t\tlocal EndSize = UDim2.new(1,x,1,y)\n\t\ttween(flare,{\"Size\",\"ImageTransparency\"},{EndSize, 1},0.7*i)\n\tend\n\n\ttween(template,{\"Size\"},goalSize,0.5)\n\tspawn(function()\n\t\twait(10)\n\t\ttween(template,{\"Size\"},UDim2.new(0,0,0,60),0.5)\n\t\twait(0.5)\n\t\ttemplate:Destroy()\n\tend)\nend\n\nfunction module.showItemPickup(realItem, amount, metadata)\n\n\tamount = metadata.stacks or amount or 1\n\n\n\tlocal itemname = realItem.name .. ((metadata and metadata.upgrades and metadata.upgrades > 0 and \" +\"..(metadata.successfulUpgrades or 0)) or \"\") or \"Unknown\"\n\n\tmetadata = metadata or {}\n\tmetadata.id = metadata.id or realItem.id\n\n\tlocal attributeColor\n\tif metadata.attribute then\n\t\tlocal attribute = itemAttributes[metadata.attribute]\n\t\tif attribute then\n\t\t\tattributeColor = attribute.color\n\t\t\tif attribute.prefix then\n\t\t\t\titemname = attribute.prefix .. \" \" .. itemname\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal template = effects.itemObtained:Clone()\n\ttemplate.Size = UDim2.new(0,0,0,60)\n\n\ttemplate.Name = itemname\n\n\ttemplate.backdrop.contents.item.attribute.Visible = false\n\n\tif attributeColor then\n\t\ttemplate.backdrop.contents.item.attribute.ImageColor3 = attributeColor\n\t\ttemplate.backdrop.contents.item.attribute.Visible = true\n\tend\n\n\ttemplate.backdrop.contents.title.Text = itemname\n--\ttemplate.backdrop.contents.thumbnail.Image = realItem.image\n\ttemplate.backdrop.contents.item.thumbnail.Image = realItem.image\n\n\ttemplate.amount.Value = template.amount.Value + amount\n\tlocal currentAmount = template.amount.Value\n\ttemplate.backdrop.contents.item.thumbnail.duplicateCount.Text = currentAmount\n\ttemplate.backdrop.contents.item.thumbnail.duplicateCount.Visible = currentAmount > 1\n\n\tlocal titleColor, itemTier\n\tif itemData then\n\t\ttitleColor, itemTier = Modules.itemAcquistion.getTitleColorForInventorySlotData(metadata)\n\tend\n\n\ttemplate.backdrop.contents.item.shine.Visible = titleColor ~= nil and itemTier and itemTier > 1\n\ttemplate.backdrop.contents.item.shine.ImageColor3 = titleColor or Color3.fromRGB(179, 178, 185)\n\ttemplate.backdrop.contents.item.frame.ImageColor3 = (itemTier and itemTier > 1 and titleColor) or Color3.fromRGB(106, 105, 107)\n\ttemplate.backdrop.contents.item.shine.ImageColor3 = titleColor or Color3.fromRGB(179, 178, 185)\n\n\ttemplate.backdrop.contents.title.TextColor3 = titleColor or Color3.new(1,1,1)\n\n\ttemplate.backdrop.contents.item.thumbnail.ImageColor3 = Color3.new(1,1,1)\n\n\tlocal dye = metadata and metadata.dye\n\tif dye then\n\t\ttemplate.backdrop.contents.item.thumbnail.ImageColor3 = Color3.fromRGB(dye.r, dye.g, dye.b)\n\tend\n\n\ttemplate.Visible = true\n\ttemplate.Parent = interactionPromptsFrame\n\n\tModules.fx.setFlash(template.backdrop.contents.item.frame, template.backdrop.contents.item.shine.Visible)\n\n\tlocal extents = game.TextService:GetTextSize(template.backdrop.contents.title.Text,18,Enum.Font.SourceSansBold,Vector2.new(90,36))\n\tlocal goalSize = UDim2.new(0,125+extents.X,0,60)\n\n\ttemplate.backdrop.contents.title.Size = UDim2.new(0,extents.X+40,1,0)\n\n\tlocal indicator\n\n\tlocal duration = 2\n\tif (realItem.rarity and realItem.rarity == \"Legendary\") then\n\t\tduration = duration + 2.5\n\t\tindicator = template:WaitForChild(\"flare\"):clone()\n\t\tindicator.Parent = template\n\t\tindicator.Size = UDim2.new(1,8,1,0)\n\t\tindicator.AnchorPoint = Vector2.new(0.5,0.5)\n\t\tindicator.Position = UDim2.new(0.5,0,0.5,0)\n\t\tindicator.ImageColor3 = Color3.fromRGB(174, 34, 234)\n\t\tindicator.Visible = true\n\t\tfor i=1,6 do\n\t\t\tlocal flare = template:WaitForChild(\"flare\"):clone()\n\t\t\tflare.Parent = template\n\t\t\tflare.Visible = true\n\t\t\tflare.ImageColor3 = Color3.fromRGB(174, 34, 234)\n\t\t\tflare.Size = UDim2.new(1,4,1,4)\n\t\t\tflare.Position = UDim2.new(0.5,0,0.5,0)\n\t\t\tflare.AnchorPoint = Vector2.new(0.5,0.5)\n\t\t\tlocal x = (1000 - 53*i)\n\t\t\tlocal y = (28 - 3*i)\n\t\t\tlocal EndPosition = UDim2.new(1,y/2,0.5,0)\n\t\t\tlocal EndSize = UDim2.new(1,x,1,y)\n\t\t\ttween(flare,{\"Size\",\"ImageTransparency\"},{EndSize, 1},0.7*i)\n\t\tend\n\telseif (realItem.rarity and realItem.rarity == \"Rare\") or (realItem.category and realItem.category == \"equipment\") then\n\t\tduration = duration + 1.5\n\t\tindicator = template:WaitForChild(\"flare\"):clone()\n\t\tindicator.Parent = template\n\t\tindicator.Size = UDim2.new(1,8,1,0)\n\t\tindicator.AnchorPoint = Vector2.new(0.5,0.5)\n\t\tindicator.Position = UDim2.new(0.5,0,0.5,0)\n\t\tindicator.Visible = true\n\t\tfor i=1,4 do\n\t\t\tlocal flare = template:WaitForChild(\"flare\"):clone()\n\t\t\tflare.Parent = template\n\t\t\tflare.Visible = true\n\t\t\tflare.Size = UDim2.new(1,4,1,4)\n\t\t\tflare.Position = UDim2.new(0.5,0,0.5,0)\n\t\t\tflare.AnchorPoint = Vector2.new(0.5,0.5)\n\t\t\tlocal x = (500 - 40*i)\n\t\t\tlocal y = (14 - 2*i)\n\n\t\t\tlocal EndSize = UDim2.new(1,x,1,y)\n\t\t\ttween(flare,{\"Size\",\"ImageTransparency\"},{EndSize, 1},0.7*i)\n\t\tend\n\n\tend\n\ttween(template,{\"Size\"},goalSize,0.5)\n\tspawn(function()\n\t\twait(duration)\n\t\tif indicator then\n\t\t\ttween(indicator, {\"ImageTransparency\"}, 1, 0.5)\n\t\tend\n\t\tif template.Parent and currentAmount == template.amount.Value then\n\t\t\ttween(template,{\"Size\"},UDim2.new(0,0,0,60),0.5)\n\t\t\tif template.Parent and currentAmount == template.amount.Value then\n\t\t\t\twait(0.5)\n\t\t\t\ttemplate:Destroy()\n\t\t\tend\n\t\tend\n\tend)\nend\n\nlocal interactionPromptTextLabelTemplate = effects:WaitForChild(\"interactionPromptTextLabel\")\n\nfunction module.createTextFragmentLabels(parent, textFragments)\n\tlocal textOffsetX \t= 0\n\tlocal textOffsetY \t= 0\n\n\tlocal originalTextSize\n\n\tlocal container \t= Instance.new(\"Frame\")\n\tlocal textYSize \t= 0\n\n\tlocal originalTextFragmentSize\n\n\tlocal lines = 1\n\n\tfor i, textFragmentData in ipairs(textFragments) do\n\n\t\tlocal textColor = textFragmentData.textColor3 or Color3.fromRGB(15,15,15)\n\t\tlocal font = textFragmentData.font or Enum.Font.SourceSans\n--\t\tlocal autoLocalize = (textFragmentData.autoLocalize == nil and true) or textFragmentData.autoLocalize\n\t\tlocal autoLocalize = false\n\t\t-- automatically pull from localization module\n\t\tif textFragmentData.autoLocalize == nil or textFragmentData.autoLocalize then\n\t\t\ttextFragmentData.text = localization.translate(textFragmentData.text, parent)\n\t\tend\n\t\tlocal textTransparency = textFragmentData.textTransparency or 0\n\n\t\tlocal textSize = textFragmentData.textSize or 18\n\n\t\tlocal textFragmentSize = textService:GetTextSize(textFragmentData.text, textSize, font, Vector2.new())\n\t\t-- standardize Y size so you can have big-text effects without offsetting the text /ber\n\t\tlocal standardTextYSize\n\t\tif originalTextFragmentSize then\n\t\t\ttextFragmentSize = Vector2.new(textFragmentSize.X, originalTextFragmentSize.Y)\n\t\telse\n\t\t\toriginalTextFragmentSize = textFragmentSize\n\t\tend\n\t\tstandardTextYSize = textSize\n\t\t-- multi-line support\n\t\tif textOffsetX + textFragmentSize.X + 3 > parent.AbsoluteSize.X then\n\t\t\t-- split the fragment's text into smaller pieces by word\n\t\t\tlocal fragmentFragments = {}\n\t\t\tfor word in string.gmatch(textFragmentData.text, \"%S+\") do\n\t\t\t\ttable.insert(fragmentFragments, word)\n\t\t\tend\n\n\n\n\t\t\tlocal currentFragmentQueue = {}\n\t\t\tlocal currentFragmentQueueXSize = 0\n\t\t\tfor i, word in pairs(fragmentFragments) do\n\t\t\t\tlocal wordSize = textService:GetTextSize(word, textSize, font, Vector2.new())\n\n\t\t\t\tif textOffsetX + currentFragmentQueueXSize + wordSize.X + 3 > parent.AbsoluteSize.X then\n\t\t\t\t\t-- exceeded line!\n\t\t\t\t\t-- dump the queue into a text label, then push this word into next queue\n\t\t\t\t\tlines = lines + 1\n\t\t\t\t\tif #currentFragmentQueue > 0 then\n\t\t\t\t\t\tlocal putTogetherFragment = \"\"\n\t\t\t\t\t\tfor ii, wordFragment in pairs(currentFragmentQueue) do\n\t\t\t\t\t\t\tputTogetherFragment = putTogetherFragment .. wordFragment\n\n\t\t\t\t\t\t\tif ii ~= #currentFragmentQueue then\n\t\t\t\t\t\t\t\tputTogetherFragment = putTogetherFragment .. \" \"\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tlocal textFragmentTextLabel = interactionPromptTextLabelTemplate:Clone()\n\n\n\n\t\t\t\t\t\ttextFragmentTextLabel.AutoLocalize\t= autoLocalize\n\t\t\t\t\t\ttextFragmentTextLabel.TextSize\t\t= textSize\n\t\t\t\t\t\ttextFragmentTextLabel.TextColor3 \t= textColor\n\t\t\t\t\t\ttextFragmentTextLabel.Position \t\t= UDim2.new(0, textOffsetX, 0, textOffsetY)\n\t\t\t\t\t\ttextFragmentTextLabel.Size \t\t\t= UDim2.new(0, currentFragmentQueueXSize, 0, standardTextYSize )\n\t\t\t\t\t\ttextFragmentTextLabel.Text \t\t\t= putTogetherFragment\n\t\t\t\t\t\ttextFragmentTextLabel.Font\t\t\t= font\n\t\t\t\t\t\ttextFragmentTextLabel.Parent \t\t= container\n\t\t\t\t\t\ttextFragmentTextLabel.TextTransparency = textTransparency\n\n\t\t\t\t\tend\n\n\t\t\t\t\ttextOffsetY \t\t\t\t= textOffsetY + standardTextYSize\n\t\t\t\t\ttextOffsetX \t\t\t\t= 0\n\t\t\t\t\tcurrentFragmentQueue \t\t= {}\n\t\t\t\t\tcurrentFragmentQueueXSize \t= wordSize.X\n\n\t\t\t\t\ttable.insert(currentFragmentQueue, word)\n\t\t\t\telse\n\t\t\t\t\tcurrentFragmentQueueXSize = currentFragmentQueueXSize + wordSize.X + 3\n\n\t\t\t\t\ttable.insert(currentFragmentQueue, word)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif #currentFragmentQueue > 0 then\n\t\t\t\tlocal putTogetherFragment = \"\"\n\t\t\t\tfor ii, wordFragment in pairs(currentFragmentQueue) do\n\t\t\t\t\tputTogetherFragment = putTogetherFragment .. wordFragment\n\n\t\t\t\t\tif ii ~= #currentFragmentQueue then\n\t\t\t\t\t\tputTogetherFragment = putTogetherFragment .. \" \"\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tlocal textFragmentTextLabel \t\t= interactionPromptTextLabelTemplate:Clone()\n\t\t\t\ttextFragmentTextLabel.TextSize\t\t= textSize\n\t\t\t\ttextFragmentTextLabel.TextColor3 \t= textColor\n\t\t\t\ttextFragmentTextLabel.Position \t\t= UDim2.new(0, textOffsetX, 0, textOffsetY)\n\n\t\t\t\ttextFragmentTextLabel.Size \t\t\t= UDim2.new(0, currentFragmentQueueXSize, 0, standardTextYSize)\n\t\t\t\ttextFragmentTextLabel.Text \t\t\t= putTogetherFragment\n\t\t\t\ttextFragmentTextLabel.Font\t\t\t= font\n\t\t\t\ttextFragmentTextLabel.Parent \t\t= container\n\t\t\t\ttextFragmentTextLabel.TextTransparency = textTransparency\n\n\t\t\t\ttextOffsetX = textOffsetX + currentFragmentQueueXSize + 3\n\n\n\t\t\tend\n\t\t\tif textYSize <= 0 then\n\t\t\t\ttextYSize = standardTextYSize\n\t\t\tend\n\n\t\telse\n\n\t\t\tif textYSize <= 0 then\n\t\t\t\ttextYSize = standardTextYSize\n\t\t\tend\n\n\t\t\tlocal textFragmentTextLabel \t\t= interactionPromptTextLabelTemplate:Clone()\n\t\t\ttextFragmentTextLabel.TextSize\t\t= textSize\n\t\t\ttextFragmentTextLabel.TextColor3 \t= textColor\n\t\t\t-- ugly hack for item tooltip stats im sorry\n\t\t\ttextFragmentTextLabel.Position \t\t= UDim2.new(0, textOffsetX, textYSize ~= standardTextYSize and 0.5 or 0, textOffsetY)\n\t\t\ttextFragmentTextLabel.AnchorPoint\t= Vector2.new(0,textYSize ~= standardTextYSize and 0.5 or 0)\n\t\t\ttextFragmentTextLabel.Size \t\t\t= UDim2.new(0, textFragmentSize.X, 0, standardTextYSize )\n\t\t\ttextFragmentTextLabel.Text \t\t\t= textFragmentData.text\n\t\t\ttextFragmentTextLabel.Font\t\t\t= font\n\t\t\ttextFragmentTextLabel.Parent \t\t= container\n\t\t\ttextFragmentTextLabel.TextTransparency = textTransparency\n\n\n\n\t\t\tif #textFragments > 1 then\n\t\t\t\ttextOffsetX = textOffsetX + textFragmentSize.X + 3\n\t\t\telse\n\t\t\t\ttextOffsetX = textOffsetX + textFragmentSize.X\n\t\t\tend\n\t\tend\n\tend\n\n\n\n\tcontainer.Size \t\t\t\t\t\t= UDim2.new(1, 0, 0, textYSize * lines)\n\tcontainer.BackgroundTransparency \t= 1\n\tcontainer.Parent \t\t\t\t\t= parent\n\n\treturn container, textOffsetY, textOffsetX\nend\n\nnetwork:create(\"createTextFragmentLabels\", \"BindableFunction\", \"OnInvoke\", module.createTextFragmentLabels)\n\nlocal function buildInteractionPromptText(promptInteractionInterface, textFragments, eventsData, eventSignal, noAnimation)\n\tlocal textOffsetX = 0\n\tlocal textOffsetY = 0\n\n\tlocal interactionPromptCopy = promptInteractionInterface.manifest\n\n\t-- clear previous ui in here\n\tfor i, v in pairs(interactionPromptCopy.curve.contents:GetChildren()) do\n\t\tif not v:isA(\"UIPadding\") then\n\t\t\tv:Destroy()\n\t\tend\n\tend\n\n\tif interactionPromptCopy[\"pick up\"].Visible then\n\t\ttextOffsetX = 10\n\t\tinteractionPromptCopy.curve.Size = UDim2.new(1,-25,0,28)\n\t\tinteractionPromptCopy.curve.Position = UDim2.new(0.5,25,0.5,0)\n\t\tinteractionPromptCopy.LayoutOrder = 10\n\telse\n\t\tinteractionPromptCopy.curve.Size = UDim2.new(1,0,0,28)\n\t\tinteractionPromptCopy.curve.Position = UDim2.new(0.5,0,0.5,0)\n\tend\n\n\tfor i, textFragmentData in pairs(textFragments) do\n\t\tlocal textFragmentSize = textService:GetTextSize(textFragmentData.text, interactionPromptTextLabelTemplate.TextSize, interactionPromptTextLabelTemplate.Font, Vector2.new())\n\t\tlocal textColor = textFragmentData.textColor3 or Color3.fromRGB(170, 170, 170)\n\t\tlocal text = textFragmentData.text or \"\"\n\n\t\tif not textFragmentData.eventType or (textFragmentData.eventType == \"key\" and textFragmentData.id) then\n\t\t\tlocal textFragmentTextLabel \t\t= interactionPromptTextLabelTemplate:Clone()\n\t\t\ttextFragmentTextLabel.TextColor3 \t= textColor\n\t\t\ttextFragmentTextLabel.Position \t\t= UDim2.new(0, textOffsetX, 0, textOffsetY)\n\t\t\ttextFragmentTextLabel.Size \t\t\t= UDim2.new(0, textFragmentSize.X, 0, textFragmentSize.Y)\n\t\t\ttextFragmentTextLabel.Text \t\t\t= text\n\t\t\ttextFragmentTextLabel.Parent \t\t= interactionPromptCopy.curve.contents\n\n\t\t\tif textFragmentData.eventType == \"key\" and textFragmentData.id and textFragmentData.keyCode and not eventsData[textFragmentData.id] then\n\t\t\t\teventsData[textFragmentData.id] = userInputService.InputBegan:connect(function(inputObject)\n\t\t\t\t\tif inputObject.UserInputType == Enum.UserInputType.Keyboard and inputObject.KeyCode == textFragmentData.keyCode then\n\t\t\t\t\t\tif eventSignal then\n\t\t\t\t\t\t\teventSignal:Fire(textFragmentData.id)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\tend\n\n\t\t\tif #textFragments > 1 then\n\t\t\t\ttextOffsetX = textOffsetX + textFragmentSize.X + 3\n\t\t\telse\n\t\t\t\ttextOffsetX = textOffsetX + textFragmentSize.X\n\t\t\tend\n\t\tend\n\tend\n\n\n\n\tif not noAnimation or promptInteractionInterface.isHiding then\n\t\tpromptInteractionInterface.isHiding = false\n\n\t\tlocal y = 18 + 10\n\n\t\tif interactionPromptCopy[\"pick up\"].Visible then\n\t\t\ttextOffsetX = textOffsetX + 40\n\t\t\tinteractionPromptCopy.LayoutOrder = 10\n\t\t\ty = 40\n\t\tend\n\n\t\tif noAnimation then\n\n\n\n\t\t\tinteractionPromptCopy.Size = UDim2.new(0, textOffsetX + 15, 0, y)\n\t\telse\n\n\t\t\tlocal y = 18 + 10\n\n\t\t\tif interactionPromptCopy[\"pick up\"].Visible then\n\t\t\t\tinteractionPromptCopy.Parent = interactionPromptsFrame\n\t\t\t\tinteractionPromptCopy.LayoutOrder = 10\n\t\t\t\ty = 40\n\t\t\telse\n\t\t\t\tinteractionPromptCopy.Parent = interactionPromptsFrame\n\t\t\tend\n\n\t\t\tlocal openAnimation = tweenService:Create(interactionPromptCopy, BASE_TWEEN_INFO, {Size = UDim2.new(0, textOffsetX + 15, 0, y)})\n\t\t\topenAnimation:Play()\n\t\tend\n\tend\nend\n\nlocal interactionPromptTemplate = effects:WaitForChild(\"interactionPrompt\")\nfunction module.createInteractionPrompt(properties, ...)\n\tproperties = properties or {}\n\tlocal promptId = properties.itemName or properties.promptId\n\tlocal value = properties.value\n\n\t--\" x\"..tostring(value) or \"\"\n\n\tlocal textFragments = {...}\n\n\tlocal textDisplayedTime = tick()\n\n\n\tlocal interactionPromptCopy, eventsData, eventSignal, doShowNoAnimation\n\tif promptId and interactionPromptCache[promptId] then\n\t\tinteractionPromptCopy \t= interactionPromptCache[promptId].interactionPromptCopy\n\t\teventsData \t\t\t\t= interactionPromptCache[promptId].eventsData\n\t\teventSignal \t\t\t= interactionPromptCache[promptId].eventSignal\n\t\tinteractionPromptCache[promptId].textDisplayedTime\t= textDisplayedTime\n\n\t\tif properties.itemName and value then\n\t\t\tlocal existingValue = interactionPromptCache[promptId].value or 0\n\t\t\tvalue = value + existingValue\n\t\t\tinteractionPromptCache[promptId].value = value\n\t\t\tinteractionPromptCopy.curve.UIScale.Scale = 1.15 + math.clamp(value/150,0,0.75)\n\t\t\ttween(interactionPromptCopy.curve.UIScale, {\"Scale\"}, 1, 0.5)\n\t\tend\n\telse\n\t\tinteractionPromptCopy \t= interactionPromptTemplate:Clone()\n\t\teventsData \t\t\t\t= {}\n\t\teventSignal \t\t\t= Instance.new(\"BindableEvent\")\n\t\tdoShowNoAnimation \t\t= false\n\n\t\tif promptId and not interactionPromptCache[promptId] then\n\t\t\tinteractionPromptCache[promptId] = {}\n\t\t\t\tinteractionPromptCache[promptId].interactionPromptCopy \t= interactionPromptCopy\n\t\t\t\tinteractionPromptCache[promptId].eventsData \t\t\t= eventsData\n\t\t\t\tinteractionPromptCache[promptId].eventSignal \t\t\t= eventSignal\n\t\t\t\tinteractionPromptCache[promptId].value\t\t\t\t\t= value\n\t\t\t\tinteractionPromptCache[promptId].textDisplayedTime\t\t= textDisplayedTime\n\t\tend\n\tend\n\n\tif value and value ~= 1 then\n\t\ttable.insert(textFragments,{text = \"x\"..tostring(value); textColor3 = Color3.fromRGB(120,120,120)})\n\tend\n\n\n\tlocal promptInteractionInterface = {} do\n\t\tpromptInteractionInterface.manifest \t= interactionPromptCopy\n\t\tpromptInteractionInterface.eventSignal \t= eventSignal\n\t\tpromptInteractionInterface.isHiding \t= false\n\n\t\tlocal function __intCleanup()\n\t\t\tif promptId == nil or interactionPromptCache[promptId].textDisplayedTime == textDisplayedTime then\n\t\t\t\t-- wipe connections\n\t\t\t\tif eventsData then\n\t\t\t\t\tfor i, v in pairs(eventsData) do\n\t\t\t\t\t\tv:disconnect()\n\t\t\t\t\tend\n\n\t\t\t\t\teventsData = nil\n\t\t\t\tend\n\n\t\t\t\tif eventSignal then\n\t\t\t\t\teventSignal:Destroy()\n\t\t\t\t\teventSignal = nil\n\t\t\t\tend\n\n\t\t\t\t-- delete the actual ui\n\t\t\t\tif interactionPromptCopy then\n\t\t\t\t\tinteractionPromptCopy:Destroy()\n\t\t\t\t\tinteractionPromptCopy = nil\n\t\t\t\tend\n\n\t\t\t\tif promptId then\n\t\t\t\t\tinteractionPromptCache[promptId] = nil\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tlocal y = 18 + 10\n\t\tinteractionPromptCopy[\"pick up\"].Visible = false\n\t\tinteractionPromptCopy.mobilePrompt.Visible = false\n\t\tif properties.promptId then\n\t\t\ty = 40\n\t\t\tinteractionPromptCopy.LayoutOrder = 10\n\t\t\tinteractionPromptCopy[\"pick up\"].Visible = true\n\t\t\tinteractionPromptCopy.mobilePrompt.Visible = true\n\t\tend\n\n\t\tfunction promptInteractionInterface:close(noAnimation)\n\t\t\tif promptId == nil or interactionPromptCache[promptId].textDisplayedTime == textDisplayedTime then\n\t\t\t\tif not noAnimation and interactionPromptCopy then\n\t\t\t\t\tlocal closeAnimation = tweenService:Create(interactionPromptCopy, BASE_TWEEN_INFO, {Size = UDim2.new(0, 0, 0, y)})\n\n\t\t\t\t\tcloseAnimation.Completed:connect(function()\n\n\t\t\t\t\t\t__intCleanup()\n\n\t\t\t\t\tend)\n\n\t\t\t\t\tcloseAnimation:Play()\n\t\t\t\telseif noAnimation then\n\t\t\t\t\t__intCleanup()\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tfunction promptInteractionInterface:hide(noAnimation)\n\t\t\tpromptInteractionInterface.isHiding = true\n\n\t\t\tlocal closeAnimation = tweenService:Create(interactionPromptCopy, BASE_TWEEN_INFO, {Size = UDim2.new(0, 0, 0, y)})\n\t\t\tcloseAnimation:Play()\n\t\tend\n\n\t\tfunction promptInteractionInterface:setExpireTime(timeToExpire, hideInstead, noAnimation)\n\t\t\tdelay(timeToExpire, function()\n\t\t\t\tif promptId == nil or interactionPromptCache[promptId].textDisplayedTime == textDisplayedTime then\n\t\t\t\t\tif not hideInstead then\n\t\t\t\t\t\tpromptInteractionInterface:close(noAnimation)\n\t\t\t\t\telse\n\t\t\t\t\t\tpromptInteractionInterface:hide(noAnimation)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\n\t\tfunction promptInteractionInterface:setBackgroundColor3(backgroundColor3)\n\t\t\tif interactionPromptCopy then\n\t\t\t\tinteractionPromptCopy.curve.ImageColor3 = backgroundColor3\n\t\t\tend\n\t\tend\n\n\t\tfunction promptInteractionInterface:updateTextFragments(doShowNoAnimation, ...)\n\t\t\tif interactionPromptCopy then\n\t\t\t\tbuildInteractionPromptText(promptInteractionInterface, {...}, eventsData, eventSignal, doShowNoAnimation)\n\t\t\tend\n\t\tend\n\tend\n\n\t-- eventTypes -- 'key', 'click'\n\t-- todo: implement multiple lines\n\tbuildInteractionPromptText(promptInteractionInterface, textFragments, eventsData, eventSignal, doShowNoAnimation)\n\n\treturn promptInteractionInterface\nend\n\nfunction module.showEtcItemPickup(realItem, value, metadata)\n\tlocal prompt = module.createInteractionPrompt({itemName = realItem.name; value = metadata.stacks or 1;},\n\t\t{text = \"Obtained\"; textColor3 = Color3.fromRGB(120,120,120)},\n\t\t{text = realItem.name; textColor3 = Color3.fromRGB(143, 120, 255)}\n\t)\n\n\tprompt:setBackgroundColor3(Color3.fromRGB(190, 190, 190))\n\tprompt:setExpireTime(4)\nend\n\nlocal function isPositionInsideFrame(absPosition, frame)\n\tlocal relative = (frame.AbsolutePosition - absPosition) / frame.AbsoluteSize\n\n\treturn\n\t\trelative.X >= -0.55 and relative.X <= 0.55\n\t\tand relative.Y >= -0.55 and relative.Y <= 0.55\nend\n\nlocal function processSwap(buttonFrom, buttonTo, isRightClickTrigger, extraData)\n\tif IS_PROCESSING_INVENTORY_SLOT_SWITCH then return false end\n\tif not buttonFrom then return false end\n\tif (buttonTo == buttonFrom and not (extraData and extraData.originSlotData)) then return false end\n--\tif buttonFrom.Image == \"\" then return false end\n\n\tIS_PROCESSING_INVENTORY_SLOT_SWITCH = true\n\tif buttonFrom:IsDescendantOf(menu_trade.yourTrade) then\n\t\tif buttonTo:IsDescendantOf(menu_trade.yourTrade) then\n\t\t\tModules.trading.swap(buttonFrom, buttonTo)\n\t\telse\n\t\t\tModules.trading.clearLocalTradeSlot(buttonFrom)\n\t\tend\n\telseif buttonFrom:IsDescendantOf(menu_storage) then\n\t\tif buttonTo and buttonTo:IsDescendantOf(menu_inventory) then\n\t\t\tlocal buttonFromStorageSlotData = network:invoke(\"getStorageSlotDataFromStorageItem\", buttonFrom)\n\n\t\t\tif buttonFromStorageSlotData then\n\t\t\t\tlocal success, reason = network:invokeServer(\"playerRequest_transferStorageToInventory\", buttonFromStorageSlotData)\n\t\t\tend\n\t\tend\n\telseif buttonFrom:IsDescendantOf(menu_inventory) then\n\t\tif buttonTo then\n\t\t\tif buttonTo:IsDescendantOf(menu_enchant) then\n\t\t\t\tlocal buttonFromInventorySlot, buttonFromType = network:invoke(\"getInventorySlotDataByInventorySlotUI\", buttonFrom)\n\t\t\t\tif buttonFromInventorySlot and buttonFromType == \"ability\" then\n\t\t\t\t\tModules.enchant.dragItem(buttonFromInventorySlot)\n\t\t\t\tend\n\t\t\telseif buttonTo:IsDescendantOf(menu_storage) then\n\t\t\t\tlocal buttonFromInventorySlot, buttonFromType = network:invoke(\"getInventorySlotDataByInventorySlotUI\", buttonFrom)\n\t\t\t\tif buttonFromInventorySlot and buttonFromType == \"item\" then\n\t\t\t\t\tlocal success, reason = network:invokeServer(\"playerRequest_transferInventoryToStorage\", buttonFromInventorySlot)\n\t\t\t\tend\n\t\t\telseif buttonTo:IsDescendantOf(menu_inventory) then\n\t\t\t\tlocal buttonFromInventorySlot, buttonFromType = network:invoke(\"getInventorySlotDataByInventorySlotUI\", buttonFrom)\n\t\t\t\tlocal buttonToInventorySlot, buttonToType = network:invoke(\"getInventorySlotDataByInventorySlotUI\", buttonTo)\n\n\t\t\t\tif buttonFromInventorySlot and buttonFromType == \"item\" then\n\t\t\t\t\tlocal fromBaseItemData \t= itemData[buttonFromInventorySlot.id]\n\t\t\t\t\tlocal toBaseItemData \t= buttonToInventorySlot and itemData[buttonToInventorySlot.id] or nil\n\n\t\t\t\t\tif fromBaseItemData then\n\t\t\t\t\t\tif toBaseItemData then\n\t\t\t\t\t\t\tif fromBaseItemData.category ~= \"equipment\" and toBaseItemData.category ~= \"equipment\" and fromBaseItemData.id == toBaseItemData.id then\n\t\t\t\t\t\t\t\t-- from and to are same item, merge stacks\n\t\t\t\t\t\t\t\tlocal currentCategory \t= network:invoke(\"getCurrentInventoryCategory\")\n\t\t\t\t\t\t\t\tlocal success \t\t\t= network:invokeServer(\"requestSplitInventorySlotDataStack\", currentCategory, buttonFromInventorySlot.position, buttonToInventorySlot.position, buttonFromInventorySlot.stacks)\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t-- from and to are different items, swap them\n\t\t\t\t\t\t\t\tlocal buttonFrom_image \t= buttonFrom.Image\n\t\t\t\t\t\t\t\tlocal buttonFrom_stacks = buttonFrom.duplicateCount.Text\n\n\t\t\t\t\t\t\t\tbuttonFrom.Image \t\t\t\t= buttonTo.Image\n\t\t\t\t\t\t\t\tbuttonFrom.duplicateCount.Text \t= buttonTo.duplicateCount.Text\n\n\t\t\t\t\t\t\t\tbuttonTo.Image \t\t\t\t\t= buttonFrom_image\n\t\t\t\t\t\t\t\tbuttonTo.duplicateCount.Text \t= buttonFrom_stacks\n\t\t\t\t\t\t\t\tlocal currentCategory \t= network:invoke(\"getCurrentInventoryCategory\")\n\t\t\t\t\t\t\t\tlocal success \t\t\t= network:invokeServer(\"switchInventorySlotData\", currentCategory, buttonFromInventorySlot.position, buttonToInventorySlot.position)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tif isRightClickTrigger and fromBaseItemData.canStack then\n\t\t\t\t\t\t\t\tlocal currentCategory \t= network:invoke(\"getCurrentInventoryCategory\")\n\t\t\t\t\t\t\t\tlocal success \t\t\t= network:invokeServer(\"requestSplitInventorySlotDataStack\", currentCategory, buttonFromInventorySlot.position, tonumber(buttonTo.Parent.Name), 1)\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t-- to is empty\n\t\t\t\t\t\t\t\tlocal buttonFrom_image \t= buttonFrom.Image\n\t\t\t\t\t\t\t\tlocal buttonFrom_stacks = buttonFrom.duplicateCount.Text\n\n\t\t\t\t\t\t\t\tbuttonFrom.Image \t\t\t\t= \"\"\n\t\t\t\t\t\t\t\tbuttonFrom.duplicateCount.Text \t= \"\"\n\n\t\t\t\t\t\t\t\tbuttonTo.Image \t\t\t\t\t= buttonFrom_image\n\t\t\t\t\t\t\t\tbuttonTo.duplicateCount.Text \t= buttonFrom_stacks\n\n\t\t\t\t\t\t\t\t-- switching item with blank\n\t\t\t\t\t\t\t\tlocal currentCategory \t= network:invoke(\"getCurrentInventoryCategory\")\n\t\t\t\t\t\t\t\tlocal success \t\t\t= network:invokeServer(\"switchInventorySlotData\", currentCategory, buttonFromInventorySlot.position, tonumber(buttonTo.Parent.Name))\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\t-- from is empty, no thanks.\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telseif buttonTo:IsDescendantOf(ui.bottomRight.hotbarFrame) then\n\t\t\t\t-- inventory to hotbarFrame\n\n\t\t\t\tlocal inventorySlotData, buttonFromType = network:invoke(\"getInventorySlotDataByInventorySlotUI\", buttonFrom)\n\t\t\t\tif inventorySlotData then\n\t\t\t\t\tif buttonFromType == \"item\" then\n\t\t\t\t\t\tlocal inventoryItemBaseData = itemData[inventorySlotData.id]\n\t\t\t\t\t\tif inventoryItemBaseData and inventoryItemBaseData.category == \"consumable\" and inventoryItemBaseData.canBeBound then\n\t\t\t\t\t\t\tlocal num = string.gsub(buttonTo.Name,\"[^.0-9]+\",\"\")\n\t\t\t\t\t\t\tif tonumber(num) == 10 then num = 0 end\n\t\t\t\t\t\t\tnetwork:invokeServer(\"registerHotbarSlotData\", mapping.dataType.item, inventorySlotData.id, tonumber(num))\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telseif buttonTo:IsDescendantOf(menu_equipment) then\n\t\t\t\t-- inventory to equip\n\t\t\t\tlocal inventorySlotData, buttonFromType = network:invoke(\"getInventorySlotDataByInventorySlotUI\", buttonFrom)\n\t\t\t\tlocal equipmentSlotData = network:invoke(\"getEquipmentSlotDataByEquipmentSlotUI\", buttonTo)\n\n\t\t\t\tif inventorySlotData and buttonFromType == \"item\" then\n\t\t\t\t\tif equipmentSlotData then\n\t\t\t\t\t\t-- check if item is valid\n\t\t\t\t\t\tlocal itemFromBaseData = itemData[inventorySlotData.id]\n\n\t\t\t\t\t\t-- part of the following check is commented out by Davdiii\n\t\t\t\t\t\t-- i think we can trust the server to do this validation, can't we?\n\t\t\t\t\t\t-- i'm not altogether too concerned about the moment it'll take for\n\t\t\t\t\t\t-- the server to tell us off if we're wrong, besides we wait anyway\n\t\t\t\t\t\tif itemFromBaseData and itemFromBaseData.isEquippable --[[and buttonTo.Parent.Name == mapping.getMappingByValue(\"equipmentPosition\", itemFromBaseData.equipmentSlot)]] then\n\t\t\t\t\t\t\t-- instantly update client frame, server will force refresh if it was wrong.\n\t\t\t\t\t\t\t-- this rewards good behaviour, and punishes bad behaviour with delays\n\t\t\t\t\t\t\tlocal buttonFrom_image \t= buttonFrom.Image\n\n\n\n\n\t\t\t\t\t\t\tlocal currentCategory = network:invoke(\"getCurrentInventoryCategory\")\n\t\t\t\t\t\t\tlocal success = network:invokeServer(\"transferInventoryToEquipment\", currentCategory, inventorySlotData.position, mapping.equipmentPosition[buttonTo.Parent.Name])\n\n\t\t\t\t\t\t\tif success then\n\t\t\t\t\t\t\t\tbuttonFrom.Image \t= buttonTo.Image\n\t\t\t\t\t\t\t\tbuttonTo.Image \t\t= buttonFrom_image\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\telseif itemFromBaseData.applyScroll then\n\n\t\t\t\t\t\t\tlocal itemBaseData_enchantment \t= itemData[inventorySlotData.id]\n\n\t\t\t\t\t\t\tlocal continue = true\n\n\t\t\t\t\t\t\tif itemBaseData_enchantment.dye then\n\n\t\t\t\t\t\t\t\tlocal equipmentBaseData = itemData[equipmentSlotData.id]\n\n\t\t\t\t\t\t\t\tif not Modules.dyePreview.prompt(itemBaseData_enchantment, equipmentBaseData) then\n\t\t\t\t\t\t\t\t\tcontinue = false\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif continue then\n\t\t\t\t\t\t\t\tlocal pos = buttonTo.AbsolutePosition + buttonTo.AbsoluteSize/2\n\n\t\t\t\t\t\t\t\tlocal playerInput = {}\n\n\t\t\t\t\t\t\t\tif itemBaseData_enchantment and itemBaseData_enchantment.playerInputFunction then\n\t\t\t\t\t\t\t\t\tplayerInput = itemBaseData_enchantment.playerInputFunction()\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tlocal success, scrollApplied, newInventorySlotData, status = network:invokeServer(\"playerRequest_enchantEquipment\", inventorySlotData, equipmentSlotData, \"equipment\", playerInput)\n\n\n\t\t\t\t\t\t\t\tif status then\n\t\t\t\t\t\t\t\t\tspawn(function()\n\t\t\t\t\t\t\t\t\t\twait(0.5)\n\n\t\t\t\t\t\t\t\t\t\tnetwork:fire(\"alert\", status)\n\t\t\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif success and scrollApplied and newInventorySlotData then\n\t\t\t\t\t\t\t\t\tspawn(function()\n\t\t\t\t\t\t\t\t\t\twait(0.5)\n\n\n\n\t\t\t\t\t\t\t\t\t\tlocal ringInfo = {\n\t\t\t\t\t\t\t\t\t\t\tcolor = Modules.itemAcquistion.getTitleColorForInventorySlotData(newInventorySlotData) or Color3.new(1,1,1);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tModules.fx.ring(ringInfo, pos)\n\t\t\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\tlocal inventoryItemBaseData = itemData[inventorySlotData.id]\n\n\t\t\t\t\t\t-- as expressed above, Davidii commented out part of this check\n\t\t\t\t\t\t-- let the server do this validation! what's the big deal?\n\t\t\t\t\t\tif inventoryItemBaseData.isEquippable --[[and buttonTo.Parent.Name == mapping.getMappingByValue(\"equipmentPosition\", inventoryItemBaseData.equipmentSlot)]] then\n\t\t\t\t\t\t\t-- inventory slot is empty\n\t\t\t\t\t\t\tlocal currentCategory = network:invoke(\"getCurrentInventoryCategory\")\n\n\t\t\t\t\t\t\tlocal success = network:invokeServer(\"transferInventoryToEquipment\", currentCategory, tonumber(buttonFrom.Parent.Name), mapping.equipmentPosition[buttonTo.Parent.Name])\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telseif buttonTo:IsDescendantOf(menu_shop) then\n\t\t\t\tlocal inventorySlotData, buttonFromType = network:invoke(\"getInventorySlotDataByInventorySlotUI\", buttonFrom)\n\t\t\t\tif inventorySlotData and buttonFromType == \"item\" then\n\t\t\t\t\tlocal inventoryItemBaseData = itemData[inventorySlotData.id]\n\t\t\t\t\tif inventoryItemBaseData then\n\t\t\t\t\t\tnetwork:invoke(\"shop_setCurrentItem\", inventorySlotData, true)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telseif buttonTo:IsDescendantOf(menu_trade.yourTrade) then\n\n\n\t\t\t\tlocal inventorySlotData, buttonFromType = network:invoke(\"getInventorySlotDataByInventorySlotUI\", buttonFrom)\n\t\t\t\tif inventorySlotData and buttonFromType == \"item\" then\n\n\t\t\t\t\tModules.trading.setLocalTradeSlot(buttonTo.Name, inventorySlotData)\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\t-- buttonTo is nil, dragged onto overworld\n\t\t\tlocal inventorySlotData, buttonFromType = network:invoke(\"getInventorySlotDataByInventorySlotUI\", buttonFrom)\n\t\t\tif inventorySlotData and buttonFromType == \"item\" then\n\t\t\t\tlocal inventoryReal = itemData[inventorySlotData.id] or {name = \"...wait what????????\"}\n\t\t\t\tlocal message = \"Are you sure you want to drop your \"..inventoryReal.name..\"?\"\n\n\n\t\t\t\tif inventoryReal.soulbound then\n\t\t\t\t\tmessage = \"DESTROY your \"..inventoryReal.name..\"?\"\n\t\t\t\tend\n\n\n\n\t\t\t\tlocal accepted = Modules.prompting_Fullscreen.prompt(message)\n\t\t\t\tif accepted then\n\n\t\t\t\t\tif inventoryReal.soulbound and not Modules.prompting_Fullscreen.prompt(\"⚠ ARE YOU SURE you want to DESTROY your \" ..inventoryReal.name..\"? This action cannot be undone! ⚠\") then\n\t\t\t\t\t\treturn false\n\t\t\t\t\tend\n\n\n\t\t\t\t\tlocal success, errorMessage = network:invokeServer(\"playerRequest_dropItem\", inventorySlotData)\n\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\telseif buttonFrom:IsDescendantOf(ui.bottomRight.hotbarFrame) then\n\t\tif buttonTo then\n\t\t\tif buttonTo:IsDescendantOf(ui.bottomRight.hotbarFrame) then\n\n\t\t\t\tlocal fromData = (extraData and extraData.originSlotData) or network:invoke(\"getHotbarSlotDataByHotbarSlotUI\", buttonFrom)\n\t\t\t\tlocal toData = network:invoke(\"getHotbarSlotDataByHotbarSlotUI\", buttonTo)\n\n\t\t\t\tlocal toNum = string.gsub(buttonTo.Name,\"[^.0-9]+\",\"\")\n\t\t\t\tif tonumber(toNum) == 10 then toNum = 0 end\n\n\t\t\t\tnetwork:invokeServer(\"registerHotbarSlotData\", fromData.dataType, fromData.id, tonumber(toNum))\n\n\t\t\t\tlocal fromNum = string.gsub(buttonFrom.Name,\"[^.0-9]+\",\"\")\n\t\t\t\tif tonumber(fromNum) == 10 then fromNum = 0 end\n\n\t\t\t\tif buttonTo ~= buttonFrom then\n\t\t\t\t\tif toData then\n\t\t\t\t\t\tnetwork:invokeServer(\"registerHotbarSlotData\", toData.dataType, toData.id, tonumber(fromNum))\n\t\t\t\t\telse\n\t\t\t\t\t\tnetwork:invokeServer(\"registerHotbarSlotData\", nil, nil, tonumber(fromNum))\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\tend\n\t\telse\n\t\t\tlocal hotbarSlotData = network:invoke(\"getHotbarSlotDataByHotbarSlotUI\", buttonFrom)\n\t\t\tif hotbarSlotData then\n\t\t\t\t-- dragged into overworld\n\t\t\t\tnetwork:invokeServer(\"registerHotbarSlotData\", nil, nil, hotbarSlotData.position)\n\t\t\tend\n\t\tend\n\telseif buttonFrom:IsDescendantOf(menu_equipment) then\n\t\tif buttonTo then\n\n\t\t\tif buttonTo:IsDescendantOf(menu_enchant) then\n\t\t\t\tlocal equipmentSlotData = network:invoke(\"getEquipmentSlotDataByEquipmentSlotUI\", buttonFrom)\n\t\t\t\tif equipmentSlotData then\n\t\t\t\t\tModules.enchant.dragItem(equipmentSlotData, \"equipment\")\n\t\t\t\tend\n\n\t\t\telseif buttonTo:IsDescendantOf(menu_inventory) then\n\t\t\t\t-- equip to inventory\n\t\t\t\tlocal inventorySlotData, buttonToType = network:invoke(\"getInventorySlotDataByInventorySlotUI\", buttonTo)\n\t\t\t\tlocal equipmentSlotData = network:invoke(\"getEquipmentSlotDataByEquipmentSlotUI\", buttonFrom)\n\n\t\t\t\tif inventorySlotData and buttonToType == \"item\" then\n\t\t\t\t\tif equipmentSlotData then\n\t\t\t\t\t\t-- check if item is valid\n\t\t\t\t\t\tlocal itemFromBaseData = itemData[inventorySlotData.id]\n\t\t\t\t\t\tif itemFromBaseData and itemFromBaseData.isEquippable and buttonFrom.Parent.Name == mapping.getMappingByValue(\"equipmentPosition\", itemFromBaseData.equipmentSlot) then\n\t\t\t\t\t\t\t-- instantly update client frame, server will force refresh if it was wrong.\n\t\t\t\t\t\t\t-- this rewards good behaviour, and punishes bad behaviour with delays\n\t\t\t\t\t\t\tlocal buttonTo_image = buttonTo.Image\n\n\t\t\t\t\t\t\tbuttonTo.Image \t\t= buttonFrom.Image\n\t\t\t\t\t\t\tbuttonFrom.Image \t= buttonTo_image\n\n\t\t\t\t\t\t\tlocal currentCategory = network:invoke(\"getCurrentInventoryCategory\")\n\t\t\t\t\t\t\tlocal success = network:invokeServer(\"transferInventoryToEquipment\", currentCategory, inventorySlotData.position, mapping.equipmentPosition[buttonFrom.Parent.Name])\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tlocal equipmentItemBaseData = itemData[equipmentSlotData.id]\n\t\t\t\t\tlocal currentCategory = network:invoke(\"getCurrentInventoryCategory\")\n\n\t\t\t\t\tlocal success = network:invokeServer(\"transferInventoryToEquipment\", currentCategory, tonumber(buttonTo.Parent.Name), mapping.equipmentPosition[buttonFrom.Parent.Name])\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\t-- dragged into overworld\n\t\tend\n\telseif buttonFrom:IsDescendantOf(menu_abilities) then\n\t\tif buttonTo and buttonTo:IsDescendantOf(ui.bottomRight.hotbarFrame) then\n\t\t\tlocal abilityData = network:invoke(\"getAbilitySlotDataByAbilitySlotUI\", buttonFrom)\n\t\t\tif abilityData.id then\n\t\t\t\tlocal num = string.gsub(buttonTo.Name,\"[^.0-9]+\",\"\")\n\t\t\t\tif tonumber(num) == 10 then num = 0 end\n\t\t\t\tnetwork:invokeServer(\"registerHotbarSlotData\", mapping.dataType.ability, abilityData.id, tonumber(num))\n\t\t\tend\n\t\tend\n\tend\n\n\tIS_PROCESSING_INVENTORY_SLOT_SWITCH = false\nend\n\nmodule.processSwap = processSwap\n\nlocal function getDropTarget(exclusion)\n\tlocal targetDrop\n\n\tlocal function recur(guiObject)\n\t\tif guiObject:IsA(\"GuiObject\") and guiObject.Visible then\n\n\t\t\tif guiObject:FindFirstChild(\"draggableFrame\") and guiObject ~= exclusion and isPositionInsideFrame(dragDropMask.AbsolutePosition, guiObject) then\n\t\t\t\ttargetDrop = guiObject\n\t\t\tend\n\t\t\tfor i, gui in pairs(guiObject:GetChildren()) do\n\t\t\t\trecur(gui)\n\t\t\tend\n\t\tend\n\tend\n\n\tfor i, gui in pairs(ui:GetChildren()) do\n\t\trecur(gui)\n\tend\n\n\treturn targetDrop\nend\n\nlocal function isDragDropFrame(frame)\n\tfor _, _frame in pairs(dragDropFrameCollection) do\n\t\tif _frame == frame then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal runService = game:GetService(\"RunService\")\n\nlocal function update(input)\n\tif currentDragFrameOriginator then\n\t\tif dragDropMask.ImageTransparency ~= 0 then\n\t\t\tcurrentDragFrameOriginator.ImageTransparency = 0.6\n\t\t\tif currentDragFrameOriginator:IsDescendantOf(menu_inventory) then\n\t\t\t\tcurrentDragFrameOriginator.duplicateCount.Visible = false\n\t\t\tend\n\n\t\t\tdragDropMask.Image = currentDragFrameOriginator.Image\n\t\t\tdragDropMask.ImageTransparency = 0\n\t\tend\n\n\t\tdragDropMask.Position = UDim2.new(0, input.Position.X - 25, 0, input.Position.Y - 25)\n\tend\nend\n\n\nfunction module.drag.setIsDragDropFrame(frame)\n\tif not frame:IsA(\"ImageLabel\") and not frame:IsA(\"ImageButton\") then\n\t\terror(\"Only ImageButtons and ImageLabels can be DragDropFrames\")\n\t\treturn\n\tend\n\n\tif isDragDropFrame(frame) then return end\n\n\tlocal function onInputBegan_UI(inputObject)\n\t\tif not frame.Active then\n\t\t\treturn false\n\t\tend\n\n\t\tif (inputObject.UserInputType == Enum.UserInputType.MouseButton1 or inputObject.UserInputType == Enum.UserInputType.Touch) and inputObject.UserInputState == Enum.UserInputState.Begin then\n\t\t\tif frame.ImageTransparency < 1 then\n\t\t\t\tlocal extraData = {}\n\t\t\t\tcurrentDragFrameOriginator = frame\n\n\t\t\t\tlocal startTime = tick()\n\t\t\t\tlocal startPosition = inputObject.Position\n\t\t\t\trepeat runService.RenderStepped:wait()\n\n\t\t\t\t\tif inputObject.UserInputType == Enum.UserInputType.Touch then\n\t\t\t\t\t\tupdate(inputObject)\n\t\t\t\t\tend\n\n\t\t\t\t\t-- input ended naturally\n\t\t\t\t\tif inputObject.UserInputState == Enum.UserInputState.End then\n\t\t\t\t\t\tif currentDragFrameOriginator == frame then\n\n\t\t\t\t\t\t\tlocal finalPosition = inputObject.Position\n\t\t\t\t\t\t\tif tick() - startTime > 0.1 and utilities.magnitude(startPosition - finalPosition) > 26 then\n--\t\t\t\t\t\t\t\tlocal dropTarget = getDropTarget(frame)\n\t\t\t\t\t\t\t\tlocal dropTarget = getDropTarget()\n\n\t\t\t\t\t\t\t\tspawn(function()\n\t\t\t\t\t\t\t\t\tif processSwap(frame, dropTarget, nil, extraData) then\n\t\t\t\t\t\t\t\t\t\t-- was accepted by client ???\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t-- was denied by client ???\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend)\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\tuntil inputObject.UserInputState == Enum.UserInputState.End or inputObject.UserInputState == Enum.UserInputState.Cancel\n\n\t\t\t\tif currentDragFrameOriginator == frame then\n\t\t\t\t\tcurrentDragFrameOriginator = nil\n\n\t\t\t\t\tif frame:IsDescendantOf(menu_inventory) then\n\t\t\t\t\t\tframe.duplicateCount.Visible = true\n\t\t\t\t\tend\n\n\t\t\t\t\t-- reset stuff!\n\t\t\t\t\tdragDropMask.ImageTransparency \t= 1\n\t\t\t\t\tdragDropMask.Position \t\t\t= UDim2.new(-1, -100, -1, -100)\n\t\t\t\t\tframe.ImageTransparency \t\t= 0\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tframe.InputBegan:connect(onInputBegan_UI)\n\n\ttable.insert(dragDropFrameCollection, frame)\nend\n\nfunction module.setIsDoubleClickFrame(imageButton, timePeriod, callback)\n\tlocal timeOfLastClick\n\timageButton.MouseButton1Click:connect(function() network:invoke(\"populateItemHoverFrame\") callback(imageButton) end)\n\n\tif imageButton and imageButton.Parent then\n\n\t\tlocal mouseEnterScale = Instance.new(\"UIScale\")\n\t\tmouseEnterScale.Parent = imageButton\n\n\t\tlocal z = imageButton.Parent.ZIndex\n\n\t\tlocal bc\n\n\t\tif imageButton.Parent:IsA(\"ImageLabel\") or imageButton.Parent:IsA(\"ImageButton\") then\n\t\t\tbc = imageButton.Parent.ImageColor3\n\t\tend\n\n\t\tlocal shine\n\n\t\tif imageButton.Parent:FindFirstChild(\"shine\") then\n\t\t\tshine = imageButton.Parent.shine.ImageTransparency\n\t\tend\n\n\t\timageButton.MouseEnter:connect(function()\n\t\t\timageButton.Parent.ZIndex = z + 1\n\t\t\ttween(mouseEnterScale, {\"Scale\"}, {1.1}, 0.4)\n\t\t\tif bc then\n\t\t\t\ttween(imageButton.Parent, {\"ImageColor3\"}, {Color3.new(bc.r * 0.65, bc.g * 0.65, bc.b * 0.65)}, 0.6)\n\t\t\tend\n\t\t\tif shine then\n\t\t\t\ttween(imageButton.Parent.shine, {\"ImageTransparency\"}, {shine/1.5}, 0.6)\n\t\t\tend\n\t\tend)\n\n\t\timageButton.MouseLeave:connect(function()\n\t\t\timageButton.Parent.ZIndex = z\n\t\t\ttween(mouseEnterScale, {\"Scale\"}, {1.0}, 0.4)\n\t\t\tif bc then\n\t\t\t\ttween(imageButton.Parent, {\"ImageColor3\"}, {bc}, 0.6)\n\t\t\tend\n\t\t\tif shine then\n\t\t\t\ttween(imageButton.Parent.shine, {\"ImageTransparency\"}, {shine}, 0.6)\n\t\t\tend\n\t\tend)\n\n\tend\nend\n\nlocal isEnchanting = false\nfunction module.setIsEnchantingFrame(imageButton, callback)\n\tlocal timeOfLastClick\n\tlocal isEnchanting = false\n\n\tlocal function onInputBegan_ButtonClicked()\n\t\t-- register first click!\n\t\tif not timeOfLastClick then\n\t\t\ttimeOfLastClick = tick()\n\n\t\t\t-- exit\n\t\t\treturn\n\t\tend\n\n\t\t-- calculate time since last click\n\t\tlocal timeSinceLastClick = tick() - timeOfLastClick\n\t\tif timeSinceLastClick < 0.1 then\n\t\t\t-- double clicked\n\t\t\tisEnchanting = true\n\t\tend\n\n\t\t-- reset time since last click\n\t\ttimeOfLastClick = nil\n\tend\n\n\timageButton.MouseButton1Click:connect(onInputBegan_ButtonClicked)\nend\n\n\n\nlocal function onInputChanged(inputObject)\n\tif currentDragFrameOriginator and inputObject.UserInputType == Enum.UserInputType.MouseMovement then\n\t\tupdate(inputObject)\n\tend\nend\n\nlocal function onInputBegan(inputObject)\n\tif currentDragFrameOriginator and inputObject.UserInputType == Enum.UserInputType.MouseButton2 then\n\t\tlocal dropTarget = getDropTarget(currentDragFrameOriginator)\n\t\tif dropTarget then\n\t\t\tprocessSwap(currentDragFrameOriginator, dropTarget, true)\n\t\tend\n\tend\nend\n\n\nuserInputService.InputChanged:connect(onInputChanged)\nuserInputService.InputBegan:connect(onInputBegan)\n\nfor i, obj in pairs(ui:GetDescendants()) do\n\tif obj.Name == \"draggableFrame\" then\n\n\t\tmodule.drag.setIsDragDropFrame(obj.Parent)\n\tend\nend\n\n\nreturn module"
  },
  {
    "path": "src/StarterGui/unstucker.lua",
    "content": "-- just a couple of things that should be run every time the player respawns\n-- essentially, why should we ever start a live arrested or casting? I say no\n-- this fixes a bug with Magic Bomb where if you die during it, the ability\n-- just breaks entirely and you end up stuck for all eternity. Let's not.\n--\n-- Davidii\n\nlocal modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nspawn(function()\n\t--network:fire(\"setIsPlayerCastingAbility\", false)\n\tnetwork:invoke(\"setCharacterArrested\", false)\nend)\n\nreturn {}"
  },
  {
    "path": "src/StarterGui/verifications.lua",
    "content": "local module = {}\n\nlocal player = game:GetService(\"Players\").LocalPlayer\nlocal gui = player.PlayerGui.gameUI.verify\n\nfunction module.init(Modules)\n\t\n\tlocal network = Modules.network\n\t\n\tgame.Players.LocalPlayer.Chatted:Connect(function(text)\n\t\tif text == \"/verify\" then\n\t\t\tModules.focus.toggle(gui)\n\t\tend\n\tend)\n\tgui.Frame.send.Activated:connect(function()\n\t\tlocal success = network:invokeServer(\"playerRequest_redeemcode\", gui.Frame.code.TextBox.Text)\n\t\tif success then\n\t\t\tlocal textObject = {\n\t\t\t\ttext = \"You have been verified!\";\n\t\t\t\ttextColor3 = Color3.new(0,0,0);\n\t\t\t\tbackgroundColor3 = Color3.fromRGB(0,255,150);\n\t\t\t\tbackgroundTransparency = 0;\n\t\t\t\ttextStrokeTransparency = 1;\n\t\t\t}\n\t\t\tModules.notifications.alert(textObject, 3)\n\t\telse\n\t\t\tlocal textObject = {\n\t\t\t\ttext = \"Invalid verification code.\";\n\t\t\t\ttextColor3 = Color3.new(0,0,0);\n\t\t\t\tbackgroundColor3 = Color3.fromRGB(255,100,0);\n\t\t\t\tbackgroundTransparency = 0;\n\t\t\t\ttextStrokeTransparency = 1;\n\t\t\t}\n\t\t\tModules.notifications.alert(textObject, 3)\t\t\t\n\t\tend\n\t\tModules.focus.toggle(gui)\n\tend)\n\tgui.Frame.close.Activated:connect(function()\n\t\tModules.focus.toggle(gui)\n\tend)\nend\n\nreturn module"
  },
  {
    "path": "src/StarterPlayer/StarterCharacterScripts/Animate.client.lua",
    "content": "--[[\n\nlocal userInputService = game:GetService(\"UserInputService\")\n-- todo: implement particle support for sprinting\n-- todo: do not play when jumping, or not sprinting\n\nlocal tweenService \t= game:GetService(\"TweenService\")\nlocal camera \t\t= workspace.CurrentCamera\nlocal player \t\t= game.Players.LocalPlayer\nlocal character \t= player.Character\n\nlocal TWEEN_INFO = TweenInfo.new(1 / 3, Enum.EasingStyle.Quad, Enum.EasingDirection.Out, 0, false, 0)\n\nlocal cameraSprinting \t= tweenService:Create(camera, TWEEN_INFO, {FieldOfView = 80})\nlocal cameraWalking \t= tweenService:Create(camera, TWEEN_INFO, {FieldOfView = 70})\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t= modules.load(\"network\")\n\nlocal animations = require(replicatedStorage.playerAnimationData)\nlocal currentlyPlayingAnimations = {}\n\nlocal function playAnimation(animationName, blendingId, doPlayRepeat)\n\tif not animationName then return end\n\tif not blendingId then return end\n\t\n\tif not currentlyPlayingAnimations[blendingId] or (doPlayRepeat or currentlyPlayingAnimations[blendingId] ~= animations[animationName].animationTrack) then\n\t\tif currentlyPlayingAnimations[blendingId] then\n\t\t\tcurrentlyPlayingAnimations[blendingId]:Stop()\n\t\t\tcurrentlyPlayingAnimations[blendingId] = nil\n\t\tend\n\t\t\n\t\tlocal animationTrack \t\t\t\t\t= animations[animationName].animationTrack\n\t\tcurrentlyPlayingAnimations[blendingId] \t= animationTrack\n\t\t\n\t\t-- play it\n\t\tanimationTrack:Play(0.15, 1, animations[animationName].speed or 1)\n\tend\nend\n\nlocal function onHumanoid_Jumping(isActive)\n\tif isActive then\n\t\tplayAnimation(\"jumping\", \"action\", true)\n\tend\nend\n\nlocal function onCharacterAdded(character)\n\tlocal humanoid = character:WaitForChild(\"Humanoid\")\n\t\n\thumanoid.Jumping:connect(function(isActive)\n\t\tstates.isInAir = true\n\t\t\n\t\tcharacter.RightFoot.ParticleEmitter.Enabled = false\n\t\tcharacter.LeftFoot.ParticleEmitter.Enabled \t= false\n\tend)\n\t\n\thumanoid.FreeFalling:connect(function(isActive)\n\t\tif not states.isActive then\n\t\t\tstates.isInAir = false\n\t\t\t\n\t\t\tif states.isSprinting then\n\t\t\t\tcharacter.RightFoot.ParticleEmitter.Enabled = true\n\t\t\t\tcharacter.LeftFoot.ParticleEmitter.Enabled \t= true\n\t\t\tend\n\t\tend\n\tend)\nend\n\nlocal function main()\n\tnetwork:create(\"characterStateChanged\", \"BindableEvent\")\n\t\n\tuserInputService.InputBegan:connect(onInputBegan)\n\tuserInputService.InputEnded:connect(onInputEnded)\nend\n\nmain()\n\n--]]"
  },
  {
    "path": "src/StarterPlayer/StarterCharacterScripts/Health.client.lua",
    "content": "-- No regen allowed! --"
  },
  {
    "path": "src/StarterPlayer/StarterCharacterScripts/LocalTeleportStuff.client.lua",
    "content": "local Player = game.Players.LocalPlayer\n\n-- populates player scripts in case the stuff expected to be there is not there\n\nif Player.PlayerScripts:FindFirstChild(\"assetsLoaded\") == nil then\n\t--Player.PlayerScripts:ClearAllChildren()\n\tfor e,Script in pairs(game.StarterPlayer.StarterPlayerScripts:GetChildren()) do\n\t\tif Player.PlayerScripts:FindFirstChild(Script.Name) == nil then\n\t\t\tScript.Parent = Player.PlayerScripts\n\t\tend\n\tend\t\t\n\t\nend\n"
  },
  {
    "path": "src/StarterPlayer/StarterCharacterScripts/Sound.client.lua",
    "content": "-- S I L E N C E --\n\n-- :D"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/ChatScript/ChatMain/ChannelsBar.lua",
    "content": "--\t// FileName: ChannelsBar.lua\n--\t// Written by: Xsitsu\n--\t// Description: Manages creating, destroying, and displaying ChannelTabs.\n\nlocal module = {}\n\nlocal PlayerGui = game:GetService(\"Players\").LocalPlayer:WaitForChild(\"PlayerGui\")\n\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal modulesFolder = script.Parent\nlocal moduleChannelsTab = require(modulesFolder:WaitForChild(\"ChannelsTab\"))\nlocal MessageSender = require(modulesFolder:WaitForChild(\"MessageSender\"))\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal CurveUtil = require(modulesFolder:WaitForChild(\"CurveUtil\"))\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:CreateGuiObjects(targetParent)\n\tlocal BaseFrame = Instance.new(\"Frame\")\n\tBaseFrame.Selectable = false\n\tBaseFrame.Size = UDim2.new(1, 0, 1, 0)\n\tBaseFrame.BackgroundTransparency = 1\n\tBaseFrame.Parent = targetParent\n\n\tlocal ScrollingBase = Instance.new(\"Frame\")\n\tScrollingBase.Selectable = false\n\tScrollingBase.Name = \"ScrollingBase\"\n\tScrollingBase.BackgroundTransparency = 1\n\tScrollingBase.ClipsDescendants = true\n\tScrollingBase.Size = UDim2.new(1, 0, 1, 0)\n\tScrollingBase.Position = UDim2.new(0, 0, 0, 0)\n\tScrollingBase.Parent = BaseFrame\n\n\tlocal ScrollerSizer = Instance.new(\"Frame\")\n\tScrollerSizer.Selectable = false\n\tScrollerSizer.Name = \"ScrollerSizer\"\n\tScrollerSizer.BackgroundTransparency = 1\n\tScrollerSizer.Size = UDim2.new(1, 0, 1, 0)\n\tScrollerSizer.Position = UDim2.new(0, 0, 0, 0)\n\tScrollerSizer.Parent = ScrollingBase\n\n\tlocal ScrollerFrame = Instance.new(\"Frame\")\n\tScrollerFrame.Selectable = false\n\tScrollerFrame.Name = \"ScrollerFrame\"\n\tScrollerFrame.BackgroundTransparency = 1\n\tScrollerFrame.Size = UDim2.new(1, 0, 1, 0)\n\tScrollerFrame.Position = UDim2.new(0, 0, 0, 0)\n\tScrollerFrame.Parent = ScrollerSizer\n\n\tlocal LeaveConfirmationFrameBase = Instance.new(\"Frame\")\n\tLeaveConfirmationFrameBase.Selectable = false\n\tLeaveConfirmationFrameBase.Size = UDim2.new(1, 0, 1, 0)\n\tLeaveConfirmationFrameBase.Position = UDim2.new(0, 0, 0, 0)\n\tLeaveConfirmationFrameBase.ClipsDescendants = true\n\tLeaveConfirmationFrameBase.BackgroundTransparency = 1\n\tLeaveConfirmationFrameBase.Parent = BaseFrame\n\n\tlocal LeaveConfirmationFrame = Instance.new(\"Frame\")\n\tLeaveConfirmationFrame.Selectable = false\n\tLeaveConfirmationFrame.Name = \"LeaveConfirmationFrame\"\n\tLeaveConfirmationFrame.Size = UDim2.new(1, 0, 1, 0)\n\tLeaveConfirmationFrame.Position = UDim2.new(0, 0, 1, 0)\n\tLeaveConfirmationFrame.BackgroundTransparency = 0.6\n\tLeaveConfirmationFrame.BorderSizePixel = 0\n\tLeaveConfirmationFrame.BackgroundColor3 = Color3.new(0, 0, 0)\n\tLeaveConfirmationFrame.Parent = LeaveConfirmationFrameBase\n\n\tlocal InputBlocker = Instance.new(\"TextButton\")\n\tInputBlocker.Selectable = false\n\tInputBlocker.Size = UDim2.new(1, 0, 1, 0)\n\tInputBlocker.BackgroundTransparency = 1\n\tInputBlocker.Text = \"\"\n\tInputBlocker.Parent = LeaveConfirmationFrame\n\n\tlocal LeaveConfirmationButtonYes = Instance.new(\"TextButton\")\n\tLeaveConfirmationButtonYes.Selectable = false\n\tLeaveConfirmationButtonYes.Size = UDim2.new(0.25, 0, 1, 0)\n\tLeaveConfirmationButtonYes.BackgroundTransparency = 1\n\tLeaveConfirmationButtonYes.Font = ChatSettings.DefaultFont\n\tLeaveConfirmationButtonYes.TextSize = 18\n\tLeaveConfirmationButtonYes.TextStrokeTransparency = 0.75\n\tLeaveConfirmationButtonYes.Position = UDim2.new(0, 0, 0, 0)\n\tLeaveConfirmationButtonYes.TextColor3 = Color3.new(0, 1, 0)\n\tLeaveConfirmationButtonYes.Text = \"Confirm\"\n\tLeaveConfirmationButtonYes.Parent = LeaveConfirmationFrame\n\n\tlocal LeaveConfirmationButtonNo = LeaveConfirmationButtonYes:Clone()\n\tLeaveConfirmationButtonNo.Parent = LeaveConfirmationFrame\n\tLeaveConfirmationButtonNo.Position = UDim2.new(0.75, 0, 0, 0)\n\tLeaveConfirmationButtonNo.TextColor3 = Color3.new(1, 0, 0)\n\tLeaveConfirmationButtonNo.Text = \"Cancel\"\n\n\tlocal LeaveConfirmationNotice = Instance.new(\"TextLabel\")\n\tLeaveConfirmationNotice.Selectable = false\n\tLeaveConfirmationNotice.Size = UDim2.new(0.5, 0, 1, 0)\n\tLeaveConfirmationNotice.Position = UDim2.new(0.25, 0, 0, 0)\n\tLeaveConfirmationNotice.BackgroundTransparency = 1\n\tLeaveConfirmationNotice.TextColor3 = Color3.new(1, 1, 1)\n\tLeaveConfirmationNotice.TextStrokeTransparency = 0.75\n\tLeaveConfirmationNotice.Text = \"Leave channel <XX>?\"\n\tLeaveConfirmationNotice.Font = ChatSettings.DefaultFont\n\tLeaveConfirmationNotice.TextSize = 18\n\tLeaveConfirmationNotice.Parent = LeaveConfirmationFrame\n\n\tlocal LeaveTarget = Instance.new(\"StringValue\")\n\tLeaveTarget.Name = \"LeaveTarget\"\n\tLeaveTarget.Parent = LeaveConfirmationFrame\n\n\tlocal outPos = LeaveConfirmationFrame.Position\n\tLeaveConfirmationButtonYes.MouseButton1Click:connect(function()\n\t\tMessageSender:SendMessage(string.format(\"/leave %s\", LeaveTarget.Value), nil)\n\t\tLeaveConfirmationFrame:TweenPosition(outPos, Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.2, true)\n\tend)\n\tLeaveConfirmationButtonNo.MouseButton1Click:connect(function()\n\t\tLeaveConfirmationFrame:TweenPosition(outPos, Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.2, true)\n\tend)\n\n\n\n\tlocal scale = 0.7\n\tlocal scaleOther = (1 - scale) / 2\n\tlocal pageButtonImage = \"rbxasset://textures/ui/Chat/TabArrowBackground.png\"\n\tlocal pageButtonArrowImage = \"rbxasset://textures/ui/Chat/TabArrow.png\"\n\n\t--// ToDo: Remove these lines when the assets are put into trunk.\n\t--// These grab unchanging versions hosted on the site, and not from the content folder.\n\tpageButtonImage = \"rbxassetid://471630199\"\n\tpageButtonArrowImage = \"rbxassetid://471630112\"\n\n\n\tlocal PageLeftButton = Instance.new(\"ImageButton\", BaseFrame)\n\tPageLeftButton.Selectable = ChatSettings.GamepadNavigationEnabled\n\tPageLeftButton.Name = \"PageLeftButton\"\n\tPageLeftButton.SizeConstraint = Enum.SizeConstraint.RelativeYY\n\tPageLeftButton.Size = UDim2.new(scale, 0, scale, 0)\n\tPageLeftButton.BackgroundTransparency = 1\n\tPageLeftButton.Position = UDim2.new(0, 4, scaleOther, 0)\n\tPageLeftButton.Visible = false\n\tPageLeftButton.Image = pageButtonImage\n\tlocal ArrowLabel = Instance.new(\"ImageLabel\", PageLeftButton)\n\tArrowLabel.Name = \"ArrowLabel\"\n\tArrowLabel.BackgroundTransparency = 1\n\tArrowLabel.Size = UDim2.new(0.4, 0, 0.4, 0)\n\tArrowLabel.Image = pageButtonArrowImage\n\n\tlocal PageRightButtonPositionalHelper = Instance.new(\"Frame\", BaseFrame)\n\tPageRightButtonPositionalHelper.Selectable = false\n\tPageRightButtonPositionalHelper.BackgroundTransparency = 1\n\tPageRightButtonPositionalHelper.Name = \"PositionalHelper\"\n\tPageRightButtonPositionalHelper.Size = PageLeftButton.Size\n\tPageRightButtonPositionalHelper.SizeConstraint = PageLeftButton.SizeConstraint\n\tPageRightButtonPositionalHelper.Position = UDim2.new(1, 0, scaleOther, 0)\n\n\tlocal PageRightButton = PageLeftButton:Clone()\n\tPageRightButton.Parent = PageRightButtonPositionalHelper\n\tPageRightButton.Name = \"PageRightButton\"\n\tPageRightButton.Size = UDim2.new(1, 0, 1, 0)\n\tPageRightButton.SizeConstraint = Enum.SizeConstraint.RelativeXY\n\tPageRightButton.Position = UDim2.new(-1, -4, 0, 0)\n\n\tlocal positionOffset = UDim2.new(0.05, 0, 0, 0)\n\n\tPageRightButton.ArrowLabel.Position = UDim2.new(0.3, 0, 0.3, 0) + positionOffset\n\tPageLeftButton.ArrowLabel.Position = UDim2.new(0.3, 0, 0.3, 0) - positionOffset\n\tPageLeftButton.ArrowLabel.Rotation = 180\n\n\n\tself.GuiObject = BaseFrame\n\n\tself.GuiObjects.BaseFrame = BaseFrame\n\tself.GuiObjects.ScrollerSizer = ScrollerSizer\n\tself.GuiObjects.ScrollerFrame = ScrollerFrame\n\tself.GuiObjects.PageLeftButton = PageLeftButton\n\tself.GuiObjects.PageRightButton = PageRightButton\n\tself.GuiObjects.LeaveConfirmationFrame = LeaveConfirmationFrame\n\tself.GuiObjects.LeaveConfirmationNotice = LeaveConfirmationNotice\n\n\tself.GuiObjects.PageLeftButtonArrow = PageLeftButton.ArrowLabel\n\tself.GuiObjects.PageRightButtonArrow = PageRightButton.ArrowLabel\n\tself:AnimGuiObjects()\n\n\tPageLeftButton.MouseButton1Click:connect(function() self:ScrollChannelsFrame(-1) end)\n\tPageRightButton.MouseButton1Click:connect(function() self:ScrollChannelsFrame(1) end)\n\n\tself:ScrollChannelsFrame(0)\nend\n\n\nfunction methods:UpdateMessagePostedInChannel(channelName)\n\tlocal tab = self:GetChannelTab(channelName)\n\tif (tab) then\n\t\ttab:UpdateMessagePostedInChannel()\n\telse\n\t\twarn(\"ChannelsTab '\" .. channelName .. \"' does not exist!\")\n\tend\nend\n\nfunction methods:AddChannelTab(channelName)\n\tif (self:GetChannelTab(channelName)) then\n\t\terror(\"Channel tab '\" .. channelName .. \"'already exists!\")\n\tend\n\n\tlocal tab = moduleChannelsTab.new(channelName)\n\ttab.GuiObject.Parent = self.GuiObjects.ScrollerFrame\n\tself.ChannelTabs[channelName:lower()] = tab\n\n\tself.NumTabs = self.NumTabs + 1\n\tself:OrganizeChannelTabs()\n\n\tif (ChatSettings.RightClickToLeaveChannelEnabled) then\n\t\ttab.NameTag.MouseButton2Click:connect(function()\n\t\t\tself.LeaveConfirmationNotice.Text = string.format(\"Leave channel %s?\", tab.ChannelName)\n\t\t\tself.LeaveConfirmationFrame.LeaveTarget.Value = tab.ChannelName\n\t\t\tself.LeaveConfirmationFrame:TweenPosition(UDim2.new(0, 0, 0, 0), Enum.EasingDirection.In, Enum.EasingStyle.Quad, 0.2, true)\n\t\tend)\n\tend\n\n\treturn tab\nend\n\nfunction methods:RemoveChannelTab(channelName)\n\tif (not self:GetChannelTab(channelName)) then\n\t\terror(\"Channel tab '\" .. channelName .. \"'does not exist!\")\n\tend\n\n\tlocal indexName = channelName:lower()\n\tself.ChannelTabs[indexName]:Destroy()\n\tself.ChannelTabs[indexName] = nil\n\n\tself.NumTabs = self.NumTabs - 1\n\tself:OrganizeChannelTabs()\nend\n\nfunction methods:GetChannelTab(channelName)\n\treturn self.ChannelTabs[channelName:lower()]\nend\n\nfunction methods:OrganizeChannelTabs()\n\tlocal order = {}\n\n\ttable.insert(order, self:GetChannelTab(ChatSettings.GeneralChannelName))\n\ttable.insert(order, self:GetChannelTab(\"System\"))\n\n\tfor tabIndexName, tab in pairs(self.ChannelTabs) do\n\t\tif (tab.ChannelName ~= ChatSettings.GeneralChannelName and tab.ChannelName ~= \"System\") then\n\t\t\ttable.insert(order, tab)\n\t\tend\n\tend\n\n\tfor index, tab in pairs(order) do\n\t\ttab.GuiObject.Position = UDim2.new(index - 1, 0, 0, 0)\n\tend\n\n\t--// Dynamic tab resizing\n\tself.GuiObjects.ScrollerSizer.Size = UDim2.new(1 / math.max(1, math.min(ChatSettings.ChannelsBarFullTabSize, self.NumTabs)), 0, 1, 0)\n\n\tself:ScrollChannelsFrame(0)\nend\n\nfunction methods:ResizeChannelTabText(textSize)\n\tfor i, tab in pairs(self.ChannelTabs) do\n\t\ttab:SetTextSize(textSize)\n\tend\nend\n\nfunction methods:ScrollChannelsFrame(dir)\n\tif (self.ScrollChannelsFrameLock) then return end\n\tself.ScrollChannelsFrameLock = true\n\n\tlocal tabNumber = ChatSettings.ChannelsBarFullTabSize\n\n\tlocal newPageNum = self.CurPageNum + dir\n\tif (newPageNum < 0) then\n\t\tnewPageNum = 0\n\telseif (newPageNum > 0 and newPageNum + tabNumber > self.NumTabs) then\n\t\tnewPageNum = self.NumTabs - tabNumber\n\tend\n\n\tself.CurPageNum = newPageNum\n\n\tlocal tweenTime = 0.15\n\tlocal endPos = UDim2.new(-self.CurPageNum, 0, 0, 0)\n\n\tself.GuiObjects.PageLeftButton.Visible = (self.CurPageNum > 0)\n\tself.GuiObjects.PageRightButton.Visible = (self.CurPageNum + tabNumber < self.NumTabs)\n\n\tif dir == 0 then\n\t\tself.ScrollChannelsFrameLock = false\n\t\treturn\n\tend\n\n\tlocal function UnlockFunc()\n\t\tself.ScrollChannelsFrameLock = false\n\tend\n\n\tself:WaitUntilParentedCorrectly()\n\n\tself.GuiObjects.ScrollerFrame:TweenPosition(endPos, Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, tweenTime, true, UnlockFunc)\nend\n\nfunction methods:FadeOutBackground(duration)\n\tfor channelName, channelObj in pairs(self.ChannelTabs) do\n\t\tchannelObj:FadeOutBackground(duration)\n\tend\n\n\tself.AnimParams.Background_TargetTransparency = 1\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeInBackground(duration)\n\tfor channelName, channelObj in pairs(self.ChannelTabs) do\n\t\tchannelObj:FadeInBackground(duration)\n\tend\n\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeOutText(duration)\n\tfor channelName, channelObj in pairs(self.ChannelTabs) do\n\t\tchannelObj:FadeOutText(duration)\n\tend\nend\n\nfunction methods:FadeInText(duration)\n\tfor channelName, channelObj in pairs(self.ChannelTabs) do\n\t\tchannelObj:FadeInText(duration)\n\tend\nend\n\nfunction methods:AnimGuiObjects()\n\tself.GuiObjects.PageLeftButton.ImageTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.GuiObjects.PageRightButton.ImageTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.GuiObjects.PageLeftButtonArrow.ImageTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.GuiObjects.PageRightButtonArrow.ImageTransparency = self.AnimParams.Background_CurrentTransparency\nend\n\nfunction methods:InitializeAnimParams()\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_CurrentTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(0)\nend\n\nfunction methods:Update(dtScale)\n\tfor channelName, channelObj in pairs(self.ChannelTabs) do\n\t\tchannelObj:Update(dtScale)\n\tend\n\n\tself.AnimParams.Background_CurrentTransparency = CurveUtil:Expt(\n\t\t\tself.AnimParams.Background_CurrentTransparency,\n\t\t\tself.AnimParams.Background_TargetTransparency,\n\t\t\tself.AnimParams.Background_NormalizedExptValue,\n\t\t\tdtScale\n\t)\n\n\tself:AnimGuiObjects()\nend\n\n--// ToDo: Move to common modules\nfunction methods:WaitUntilParentedCorrectly()\n\twhile (not self.GuiObject:IsDescendantOf(game:GetService(\"Players\").LocalPlayer)) do\n\t\tself.GuiObject.AncestryChanged:wait()\n\tend\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.GuiObject = nil\n\tobj.GuiObjects = {}\n\n\tobj.ChannelTabs = {}\n\tobj.NumTabs = 0\n\tobj.CurPageNum = 0\n\n\tobj.ScrollChannelsFrameLock = false\n\n\tobj.AnimParams = {}\n\n\tobj:InitializeAnimParams()\n\n\tChatSettings.SettingsChanged:connect(function(setting, value)\n\t\tif (setting == \"ChatChannelsTabTextSize\") then\n\t\t\tobj:ResizeChannelTabText(value)\n\t\tend\n\tend)\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/ChatScript/ChatMain/ChannelsTab.lua",
    "content": "--\t// FileName: ChannelsTab.lua\n--\t// Written by: Xsitsu\n--\t// Description: Channel tab button for selecting current channel and also displaying if currently selected.\n\nlocal module = {}\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal modulesFolder = script.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal CurveUtil = require(modulesFolder:WaitForChild(\"CurveUtil\"))\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nlocal function CreateGuiObjects()\n\tlocal BaseFrame = Instance.new(\"Frame\")\n\tBaseFrame.Selectable = false\n\tBaseFrame.Size = UDim2.new(1, 0, 1, 0)\n\tBaseFrame.BackgroundTransparency = 1\n\n\tlocal gapOffsetX = 1\n\tlocal gapOffsetY = 1\n\n\tlocal BackgroundFrame = Instance.new(\"Frame\")\n\tBackgroundFrame.Selectable = false\n\tBackgroundFrame.Name = \"BackgroundFrame\"\n\tBackgroundFrame.Size = UDim2.new(1, -gapOffsetX * 2, 1, -gapOffsetY * 2)\n\tBackgroundFrame.Position = UDim2.new(0, gapOffsetX, 0, gapOffsetY)\n\tBackgroundFrame.BackgroundTransparency = 1\n\tBackgroundFrame.Parent = BaseFrame\n\n\tlocal UnselectedFrame = Instance.new(\"Frame\")\n\tUnselectedFrame.Selectable = false\n\tUnselectedFrame.Name = \"UnselectedFrame\"\n\tUnselectedFrame.Size = UDim2.new(1, 0, 1, 0)\n\tUnselectedFrame.Position = UDim2.new(0, 0, 0, 0)\n\tUnselectedFrame.BorderSizePixel = 0\n\tUnselectedFrame.BackgroundColor3 = ChatSettings.ChannelsTabUnselectedColor\n\tUnselectedFrame.BackgroundTransparency = 0.6\n\tUnselectedFrame.Parent = BackgroundFrame\n\n\tlocal SelectedFrame = Instance.new(\"Frame\")\n\tSelectedFrame.Selectable = false\n\tSelectedFrame.Name = \"SelectedFrame\"\n\tSelectedFrame.Size = UDim2.new(1, 0, 1, 0)\n\tSelectedFrame.Position = UDim2.new(0, 0, 0, 0)\n\tSelectedFrame.BorderSizePixel = 0\n\tSelectedFrame.BackgroundColor3 = ChatSettings.ChannelsTabSelectedColor\n\tSelectedFrame.BackgroundTransparency = 1\n\tSelectedFrame.Parent = BackgroundFrame\n\n\tlocal SelectedFrameBackgroundImage = Instance.new(\"ImageLabel\")\n\tSelectedFrameBackgroundImage.Selectable = false\n\tSelectedFrameBackgroundImage.Name = \"BackgroundImage\"\n\tSelectedFrameBackgroundImage.BackgroundTransparency = 1\n\tSelectedFrameBackgroundImage.BorderSizePixel = 0\n\tSelectedFrameBackgroundImage.Size = UDim2.new(1, 0, 1, 0)\n\tSelectedFrameBackgroundImage.Position = UDim2.new(0, 0, 0, 0)\n\tSelectedFrameBackgroundImage.ScaleType = Enum.ScaleType.Slice\n\tSelectedFrameBackgroundImage.Parent = SelectedFrame\n\n\tSelectedFrameBackgroundImage.BackgroundTransparency = 0.6 - 1\n\tlocal rate = 1.2 * 1\n\tSelectedFrameBackgroundImage.BackgroundColor3 = Color3.fromRGB(78 * rate, 84 * rate, 96 * rate)\n\n\tlocal borderXOffset = 2\n\tlocal blueBarYSize = 4\n\tlocal BlueBarLeft = Instance.new(\"ImageLabel\")\n\tBlueBarLeft.Selectable = false\n\tBlueBarLeft.Size = UDim2.new(0.5, -borderXOffset, 0, blueBarYSize)\n\tBlueBarLeft.BackgroundTransparency = 1\n\tBlueBarLeft.ScaleType = Enum.ScaleType.Slice\n\tBlueBarLeft.SliceCenter = Rect.new(3,3,32,21)\n\tBlueBarLeft.Parent = SelectedFrame\n\n\tlocal BlueBarRight = BlueBarLeft:Clone()\n\tBlueBarRight.Parent = SelectedFrame\n\n\tBlueBarLeft.Position = UDim2.new(0, borderXOffset, 1, -blueBarYSize)\n\tBlueBarRight.Position = UDim2.new(0.5, 0, 1, -blueBarYSize)\n\tBlueBarLeft.Image = \"rbxasset://textures/ui/Settings/Slider/SelectedBarLeft.png\"\n\tBlueBarRight.Image = \"rbxasset://textures/ui/Settings/Slider/SelectedBarRight.png\"\n\n\tBlueBarLeft.Name = \"BlueBarLeft\"\n\tBlueBarRight.Name = \"BlueBarRight\"\n\n\tlocal NameTag = Instance.new(\"TextButton\")\n\tNameTag.Selectable = ChatSettings.GamepadNavigationEnabled\n\tNameTag.Size = UDim2.new(1, 0, 1, 0)\n\tNameTag.Position = UDim2.new(0, 0, 0, 0)\n\tNameTag.BackgroundTransparency = 1\n\tNameTag.Font = ChatSettings.DefaultFont\n\tNameTag.TextSize = ChatSettings.ChatChannelsTabTextSize\n\tNameTag.TextColor3 = Color3.new(1, 1, 1)\n\tNameTag.TextStrokeTransparency = 0.75\n\tNameTag.Parent = BackgroundFrame\n\n\tlocal NameTagNonSelect = NameTag:Clone()\n\tlocal NameTagSelect = NameTag:Clone()\n\tNameTagNonSelect.Parent = UnselectedFrame\n\tNameTagSelect.Parent = SelectedFrame\n\tNameTagNonSelect.Font = Enum.Font.SourceSans\n\tNameTagNonSelect.Active = false\n\tNameTagSelect.Active = false\n\n\tlocal NewMessageIconFrame = Instance.new(\"Frame\")\n\tNewMessageIconFrame.Selectable = false\n\tNewMessageIconFrame.Size = UDim2.new(0, 18, 0, 18)\n\tNewMessageIconFrame.Position = UDim2.new(0.8, -9, 0.5, -9)\n\tNewMessageIconFrame.BackgroundTransparency = 1\n\tNewMessageIconFrame.Parent = BackgroundFrame\n\n\tlocal NewMessageIcon = Instance.new(\"ImageLabel\")\n\tNewMessageIcon.Selectable = false\n\tNewMessageIcon.Size = UDim2.new(1, 0, 1, 0)\n\tNewMessageIcon.BackgroundTransparency = 1\n\tNewMessageIcon.Image = \"rbxasset://textures/ui/Chat/MessageCounter.png\"\n\tNewMessageIcon.Visible = false\n\tNewMessageIcon.Parent = NewMessageIconFrame\n\n\tlocal NewMessageIconText = Instance.new(\"TextLabel\")\n\tNewMessageIconText.Selectable = false\n\tNewMessageIconText.BackgroundTransparency = 1\n\tNewMessageIconText.Size = UDim2.new(0, 13, 0, 9)\n\tNewMessageIconText.Position = UDim2.new(0.5, -7, 0.5, -7)\n\tNewMessageIconText.Font = ChatSettings.DefaultFont\n\tNewMessageIconText.TextSize = 14\n\tNewMessageIconText.TextColor3 = Color3.new(1, 1, 1)\n\tNewMessageIconText.Text = \"\"\n\tNewMessageIconText.Parent = NewMessageIcon\n\n\treturn BaseFrame, NameTag, NameTagNonSelect, NameTagSelect, NewMessageIcon, UnselectedFrame, SelectedFrame\nend\n\nfunction methods:Destroy()\n\tself.GuiObject:Destroy()\nend\n\nfunction methods:UpdateMessagePostedInChannel(ignoreActive)\n\tif (self.Active and (ignoreActive ~= true)) then return end\n\n\tlocal count = self.UnreadMessageCount + 1\n\tself.UnreadMessageCount = count\n\n\tlocal label = self.NewMessageIcon\n\tlabel.Visible = true\n\tlabel.TextLabel.Text = (count < 100) and tostring(count) or \"!\"\n\n\tlocal tweenTime = 0.15\n\tlocal tweenPosOffset = UDim2.new(0, 0, -0.1, 0)\n\n\tlocal curPos = label.Position\n\tlocal outPos = curPos + tweenPosOffset\n\tlocal easingDirection = Enum.EasingDirection.Out\n\tlocal easingStyle = Enum.EasingStyle.Quad\n\n\tlabel.Position = UDim2.new(0, 0, -0.15, 0)\n\tlabel:TweenPosition(UDim2.new(0, 0, 0, 0), easingDirection, easingStyle, tweenTime, true)\n\nend\n\nfunction methods:SetActive(active)\n\tself.Active = active\n\tself.UnselectedFrame.Visible = not active\n\tself.SelectedFrame.Visible = active\n\n\tif (active) then\n\t\tself.UnreadMessageCount = 0\n\t\tself.NewMessageIcon.Visible = false\n\n\t\tself.NameTag.Font = Enum.Font.SourceSansBold\n\telse\n\t\tself.NameTag.Font = Enum.Font.SourceSans\n\n\tend\nend\n\nfunction methods:SetTextSize(textSize)\n\tself.NameTag.TextSize = textSize\nend\n\nfunction methods:FadeOutBackground(duration)\n\tself.AnimParams.Background_TargetTransparency = 1\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeInBackground(duration)\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeOutText(duration)\n\tself.AnimParams.Text_TargetTransparency = 1\n\tself.AnimParams.Text_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\n\tself.AnimParams.TextStroke_TargetTransparency = 1\n\tself.AnimParams.TextStroke_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeInText(duration)\n\tself.AnimParams.Text_TargetTransparency = 0\n\tself.AnimParams.Text_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\n\tself.AnimParams.TextStroke_TargetTransparency = 0.75\n\tself.AnimParams.TextStroke_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:AnimGuiObjects()\n\tself.UnselectedFrame.BackgroundTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.SelectedFrame.BackgroundImage.BackgroundTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.SelectedFrame.BlueBarLeft.ImageTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.SelectedFrame.BlueBarRight.ImageTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.NameTagNonSelect.TextTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.NameTagNonSelect.TextStrokeTransparency = self.AnimParams.Background_CurrentTransparency\n\n\tself.NameTag.TextTransparency = self.AnimParams.Text_CurrentTransparency\n\tself.NewMessageIcon.ImageTransparency = self.AnimParams.Text_CurrentTransparency\n\tself.WhiteTextNewMessageNotification.TextTransparency = self.AnimParams.Text_CurrentTransparency\n\tself.NameTagSelect.TextTransparency = self.AnimParams.Text_CurrentTransparency\n\n\tself.NameTag.TextStrokeTransparency = self.AnimParams.TextStroke_CurrentTransparency\n\tself.WhiteTextNewMessageNotification.TextStrokeTransparency = self.AnimParams.TextStroke_CurrentTransparency\n\tself.NameTagSelect.TextStrokeTransparency = self.AnimParams.TextStroke_CurrentTransparency\nend\n\nfunction methods:InitializeAnimParams()\n\tself.AnimParams.Text_TargetTransparency = 0\n\tself.AnimParams.Text_CurrentTransparency = 0\n\tself.AnimParams.Text_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(0)\n\n\tself.AnimParams.TextStroke_TargetTransparency = 0.75\n\tself.AnimParams.TextStroke_CurrentTransparency = 0.75\n\tself.AnimParams.TextStroke_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(0)\n\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_CurrentTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(0)\nend\n\nfunction methods:Update(dtScale)\n\tself.AnimParams.Background_CurrentTransparency = CurveUtil:Expt(\n\t\t\tself.AnimParams.Background_CurrentTransparency,\n\t\t\tself.AnimParams.Background_TargetTransparency,\n\t\t\tself.AnimParams.Background_NormalizedExptValue,\n\t\t\tdtScale\n\t)\n\tself.AnimParams.Text_CurrentTransparency = CurveUtil:Expt(\n\t\t\tself.AnimParams.Text_CurrentTransparency,\n\t\t\tself.AnimParams.Text_TargetTransparency,\n\t\t\tself.AnimParams.Text_NormalizedExptValue,\n\t\t\tdtScale\n\t)\n\tself.AnimParams.TextStroke_CurrentTransparency = CurveUtil:Expt(\n\t\t\tself.AnimParams.TextStroke_CurrentTransparency,\n\t\t\tself.AnimParams.TextStroke_TargetTransparency,\n\t\t\tself.AnimParams.TextStroke_NormalizedExptValue,\n\t\t\tdtScale\n\t)\n\n\tself:AnimGuiObjects()\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new(channelName)\n\tlocal obj = setmetatable({}, methods)\n\n\tlocal BaseFrame, NameTag, NameTagNonSelect, NameTagSelect, NewMessageIcon, UnselectedFrame, SelectedFrame = CreateGuiObjects()\n\tobj.GuiObject = BaseFrame\n\tobj.NameTag = NameTag\n\tobj.NameTagNonSelect = NameTagNonSelect\n\tobj.NameTagSelect = NameTagSelect\n\tobj.NewMessageIcon = NewMessageIcon\n\tobj.UnselectedFrame = UnselectedFrame\n\tobj.SelectedFrame = SelectedFrame\n\n\tobj.BlueBarLeft = SelectedFrame.BlueBarLeft\n\tobj.BlueBarRight = SelectedFrame.BlueBarRight\n\tobj.BackgroundImage = SelectedFrame.BackgroundImage\n\tobj.WhiteTextNewMessageNotification = obj.NewMessageIcon.TextLabel\n\n\tobj.ChannelName = channelName\n\tobj.UnreadMessageCount = 0\n\tobj.Active = false\n\n\tobj.GuiObject.Name = \"Frame_\" .. obj.ChannelName\n\n\tif (string.len(channelName) > ChatSettings.MaxChannelNameLength) then\n\t\tchannelName = string.sub(channelName, 1, ChatSettings.MaxChannelNameLength - 3) .. \"...\"\n\tend\n\n\t--obj.NameTag.Text = channelName\n\n\tobj.NameTag.Text = \"\"\n\tobj.NameTagNonSelect.Text = channelName\n\tobj.NameTagSelect.Text = channelName\n\n\tobj.AnimParams = {}\n\n\tobj:InitializeAnimParams()\n\tobj:AnimGuiObjects()\n\tobj:SetActive(false)\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/ChatScript/ChatMain/ChatBar.lua",
    "content": "--\t// FileName: ChatBar.lua\n--\t// Written by: Xsitsu\n--\t// Description: Manages text typing and typing state.\n\nlocal module = {}\n\nlocal UserInputService = game:GetService(\"UserInputService\")\nlocal RunService = game:GetService(\"RunService\")\nlocal Players = game:GetService(\"Players\")\nlocal TextService = game:GetService(\"TextService\")\nlocal LocalPlayer = Players.LocalPlayer\n\nwhile not LocalPlayer do\n\tPlayers.PlayerAdded:wait()\n\tLocalPlayer = Players.LocalPlayer\nend\n\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal modulesFolder = script.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal CurveUtil = require(modulesFolder:WaitForChild(\"CurveUtil\"))\n\nlocal commandModules = clientChatModules:WaitForChild(\"CommandModules\")\nlocal WhisperModule = require(commandModules:WaitForChild(\"Whisper\"))\n\nlocal MessageSender = require(modulesFolder:WaitForChild(\"MessageSender\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:CreateGuiObjects(targetParent)\n\tself.ChatBarParentFrame = targetParent\n\n\tlocal backgroundImagePixelOffset = 7\n\tlocal textBoxPixelOffset = 5\n\n\tlocal BaseFrame = Instance.new(\"Frame\")\n\tBaseFrame.Selectable = false\n\tBaseFrame.Size = UDim2.new(1, 0, 1, 0)\n\tBaseFrame.BackgroundTransparency = 0.6\n\tBaseFrame.BorderSizePixel = 0\n\tBaseFrame.BackgroundColor3 = ChatSettings.ChatBarBackGroundColor\n\tBaseFrame.Parent = targetParent\n\n\tlocal BoxFrame = Instance.new(\"Frame\")\n\tBoxFrame.Selectable = false\n\tBoxFrame.Name = \"BoxFrame\"\n\tBoxFrame.BackgroundTransparency = 0.6\n\tBoxFrame.BorderSizePixel = 0\n\tBoxFrame.BackgroundColor3 = ChatSettings.ChatBarBoxColor\n\tBoxFrame.Size = UDim2.new(1, -backgroundImagePixelOffset * 2, 1, -backgroundImagePixelOffset * 2)\n\tBoxFrame.Position = UDim2.new(0, backgroundImagePixelOffset, 0, backgroundImagePixelOffset)\n\tBoxFrame.Parent = BaseFrame\n\n\tlocal TextBoxHolderFrame = Instance.new(\"Frame\")\n\tTextBoxHolderFrame.BackgroundTransparency = 1\n\tTextBoxHolderFrame.Size = UDim2.new(1, -textBoxPixelOffset * 2, 1, -textBoxPixelOffset * 2)\n\tTextBoxHolderFrame.Position = UDim2.new(0, textBoxPixelOffset, 0, textBoxPixelOffset)\n\tTextBoxHolderFrame.Parent = BoxFrame\n\n\tlocal TextBox = Instance.new(\"TextBox\")\n\tTextBox.Selectable = ChatSettings.GamepadNavigationEnabled\n\tTextBox.Name = \"ChatBar\"\n\tTextBox.BackgroundTransparency = 1\n\tTextBox.Size = UDim2.new(1, 0, 1, 0)\n\tTextBox.Position = UDim2.new(0, 0, 0, 0)\n\tTextBox.TextSize = ChatSettings.ChatBarTextSize\n\tTextBox.Font = ChatSettings.ChatBarFont\n\tTextBox.TextColor3 = ChatSettings.ChatBarTextColor\n\tTextBox.TextTransparency = 0.4\n\tTextBox.TextStrokeTransparency = 1\n\tTextBox.ClearTextOnFocus = false\n\tTextBox.TextXAlignment = Enum.TextXAlignment.Left\n\tTextBox.TextYAlignment = Enum.TextYAlignment.Top\n\tTextBox.TextWrapped = true\n\tTextBox.Text = \"\"\n\tTextBox.Parent = TextBoxHolderFrame\n\n\tlocal MessageModeTextButton = Instance.new(\"TextButton\")\n\tMessageModeTextButton.Selectable = false\n\tMessageModeTextButton.Name = \"MessageMode\"\n\tMessageModeTextButton.BackgroundTransparency = 1\n\tMessageModeTextButton.Position = UDim2.new(0, 0, 0, 0)\n\tMessageModeTextButton.TextSize = ChatSettings.ChatBarTextSize\n\tMessageModeTextButton.Font = ChatSettings.ChatBarFont\n\tMessageModeTextButton.TextXAlignment = Enum.TextXAlignment.Left\n\tMessageModeTextButton.TextWrapped = true\n\tMessageModeTextButton.Text = \"\"\n\tMessageModeTextButton.Size = UDim2.new(0, 0, 0, 0)\n\tMessageModeTextButton.TextYAlignment = Enum.TextYAlignment.Center\n\tMessageModeTextButton.TextColor3 = self:GetDefaultChannelNameColor()\n\tMessageModeTextButton.Visible = true\n\tMessageModeTextButton.Parent = TextBoxHolderFrame\n\n\tlocal TextLabel = Instance.new(\"TextLabel\")\n\tTextLabel.Selectable = false\n\tTextLabel.TextWrapped = true\n\tTextLabel.BackgroundTransparency = 1\n\tTextLabel.Size = TextBox.Size\n\tTextLabel.Position = TextBox.Position\n\tTextLabel.TextSize = TextBox.TextSize\n\tTextLabel.Font = TextBox.Font\n\tTextLabel.TextColor3 = TextBox.TextColor3\n\tTextLabel.TextTransparency = TextBox.TextTransparency\n\tTextLabel.TextStrokeTransparency = TextBox.TextStrokeTransparency\n\tTextLabel.TextXAlignment = TextBox.TextXAlignment\n\tTextLabel.TextYAlignment = TextBox.TextYAlignment\n\tTextLabel.Text = \"...\"\n\tTextLabel.Parent = TextBoxHolderFrame\n\n\tself.GuiObject = BaseFrame\n\tself.TextBox = TextBox\n\tself.TextLabel  = TextLabel\n\n\tself.GuiObjects.BaseFrame = BaseFrame\n\tself.GuiObjects.TextBoxFrame = BoxFrame\n\tself.GuiObjects.TextBox = TextBox\n\tself.GuiObjects.TextLabel = TextLabel\n\tself.GuiObjects.MessageModeTextButton = MessageModeTextButton\n\n\tself:AnimGuiObjects()\n\tself:SetUpTextBoxEvents(TextBox, TextLabel, MessageModeTextButton)\n\tif self.UserHasChatOff then\n\t\tself:DoLockChatBar()\n\tend\n\tself.eGuiObjectsChanged:Fire()\nend\n\n-- Used to lock the chat bar when the user has chat turned off.\nfunction methods:DoLockChatBar()\n\tif self.TextLabel then\n\t\tif LocalPlayer.UserId > 0 then\n\t\t\tself.TextLabel.Text = ChatLocalization:Get(\n\t\t\t\t\"GameChat_ChatMessageValidator_SettingsError\",\n\t\t\t\t\"To chat in game, turn on chat in your Privacy Settings.\"\n\t\t\t)\n\t\telse\n\t\t\tself.TextLabel.Text = ChatLocalization:Get(\n\t\t\t\t\"GameChat_SwallowGuestChat_Message\",\n\t\t\t\t\"Sign up to chat in game.\"\n\t\t\t)\n\t\tend\n\t\tself:CalculateSize()\n\tend\n\tif self.TextBox then\n\t\tself.TextBox.Active = false\n\t\tself.TextBox.Focused:connect(function()\n\t\t\tself.TextBox:ReleaseFocus()\n\t\tend)\n\tend\nend\n\nfunction methods:SetUpTextBoxEvents(TextBox, TextLabel, MessageModeTextButton)\n\t-- Clean up events from a previous setup.\n\tfor name, conn in pairs(self.TextBoxConnections) do\n\t\tconn:disconnect()\n\t\tself.TextBoxConnections[name] = nil\n\tend\n\n\t--// Code for getting back into general channel from other target channel when pressing backspace.\n\tself.TextBoxConnections.UserInputBegan = UserInputService.InputBegan:connect(function(inputObj, gpe)\n\t\tif (inputObj.KeyCode == Enum.KeyCode.Backspace) then\n\t\t\tif (self:IsFocused() and TextBox.Text == \"\") then\n\t\t\t\tself:SetChannelTarget(ChatSettings.GeneralChannelName)\n\t\t\tend\n\t\tend\n\tend)\n\n\tself.TextBoxConnections.TextBoxChanged = TextBox.Changed:connect(function(prop)\n\t\tif prop == \"AbsoluteSize\" then\n\t\t\tself:CalculateSize()\n\t\t\treturn\n\t\tend\n\n\t\tif prop ~= \"Text\" then\n\t\t\treturn\n\t\tend\n\n\t\tself:CalculateSize()\n\n\t\tif (string.len(TextBox.Text) > ChatSettings.MaximumMessageLength) then\n\t\t\tTextBox.Text = string.sub(TextBox.Text, 1, ChatSettings.MaximumMessageLength)\n\t\t\treturn\n\t\tend\n\n\t\tif not self.InCustomState then\n\t\t\tlocal customState = self.CommandProcessor:ProcessInProgressChatMessage(TextBox.Text, self.ChatWindow, self)\n\t\t\tif customState then\n\t\t\t\tself.InCustomState = true\n\t\t\t\tself.CustomState = customState\n\t\t\tend\n\t\telse\n\t\t\tself.CustomState:TextUpdated()\n\t\tend\n\tend)\n\n\tlocal function UpdateOnFocusStatusChanged(isFocused)\n\t\tif isFocused or TextBox.Text ~= \"\" then\n\t\t\tTextLabel.Visible = false\n\t\telse\n\t\t\tTextLabel.Visible = true\n\t\tend\n\tend\n\n\tself.TextBoxConnections.MessageModeClick = MessageModeTextButton.MouseButton1Click:connect(function()\n\t\tif MessageModeTextButton.Text ~= \"\" then\n\t\t\tself:SetChannelTarget(ChatSettings.GeneralChannelName)\n\t\tend\n\tend)\n\n\tself.TextBoxConnections.TextBoxFocused = TextBox.Focused:connect(function()\n\t\tif not self.UserHasChatOff then\n\t\t\tself:CalculateSize()\n\t\t\tUpdateOnFocusStatusChanged(true)\n\t\tend\n\tend)\n\n\tself.TextBoxConnections.TextBoxFocusLost = TextBox.FocusLost:connect(function(enterPressed, inputObject)\n\t\tself:CalculateSize()\n\t\tif (inputObject and inputObject.KeyCode == Enum.KeyCode.Escape) then\n\t\t\tTextBox.Text = \"\"\n\t\tend\n\t\tUpdateOnFocusStatusChanged(false)\n\tend)\nend\n\nfunction methods:GetTextBox()\n\treturn self.TextBox\nend\n\nfunction methods:GetMessageModeTextButton()\n\treturn self.GuiObjects.MessageModeTextButton\nend\n\n-- Deprecated in favour of GetMessageModeTextButton\n-- Retained for compatibility reasons.\nfunction methods:GetMessageModeTextLabel()\n\treturn self:GetMessageModeTextButton()\nend\n\nfunction methods:IsFocused()\n\tif self.UserHasChatOff then\n\t\treturn false\n\tend\n\n\treturn self:GetTextBox():IsFocused()\nend\n\nfunction methods:GetVisible()\n\treturn self.GuiObject.Visible\nend\n\nfunction methods:CaptureFocus()\n\tif not self.UserHasChatOff then\n\t\tself:GetTextBox():CaptureFocus()\n\tend\nend\n\nfunction methods:ReleaseFocus(didRelease)\n\tself:GetTextBox():ReleaseFocus(didRelease)\nend\n\nfunction methods:ResetText()\n\tself:GetTextBox().Text = \"\"\nend\n\nfunction methods:SetText(text)\n\tself:GetTextBox().Text = text\nend\n\nfunction methods:GetEnabled()\n\treturn self.GuiObject.Visible\nend\n\nfunction methods:SetEnabled(enabled)\n\tif self.UserHasChatOff then\n\t\t-- The chat bar can not be removed if a user has chat turned off so that\n\t\t-- the chat bar can display a message explaining that chat is turned off.\n\t\tself.GuiObject.Visible = true\n\telse\n\t\tself.GuiObject.Visible = enabled\n\tend\nend\n\nfunction methods:SetTextLabelText(text)\n\tif not self.UserHasChatOff then\n\t\tself.TextLabel.Text = text\n\tend\nend\n\nfunction methods:SetTextBoxText(text)\n\tself.TextBox.Text = text\nend\n\nfunction methods:GetTextBoxText()\n\treturn self.TextBox.Text\nend\n\nfunction methods:ResetSize()\n\tself.TargetYSize = 0\n\tself:TweenToTargetYSize()\nend\n\nlocal function measureSize(textObj)\n\treturn TextService:GetTextSize(\n\t\ttextObj.Text,\n\t\ttextObj.TextSize,\n\t\ttextObj.Font,\n\t\tVector2.new(textObj.AbsoluteSize.X, 10000)\n\t)\nend\n\nfunction methods:CalculateSize()\n\tif self.CalculatingSizeLock then\n\t\treturn\n\tend\n\tself.CalculatingSizeLock = true\n\n\tlocal textSize = nil\n\tlocal bounds = nil\n\n\tif self:IsFocused() or self.TextBox.Text ~= \"\" then\n\t\ttextSize = self.TextBox.TextSize\n\t\tbounds = measureSize(self.TextBox).Y\n\telse\n\t\ttextSize = self.TextLabel.TextSize\n\t\tbounds = measureSize(self.TextLabel).Y\n\tend\n\n\tlocal newTargetYSize = bounds - textSize\n\tif (self.TargetYSize ~= newTargetYSize) then\n\t\tself.TargetYSize = newTargetYSize\n\t\tself:TweenToTargetYSize()\n\tend\n\n\tself.CalculatingSizeLock = false\nend\n\nfunction methods:TweenToTargetYSize()\n\tlocal endSize = UDim2.new(1, 0, 1, self.TargetYSize)\n\tlocal curSize = self.GuiObject.Size\n\n\tlocal curAbsoluteSizeY = self.GuiObject.AbsoluteSize.Y\n\tself.GuiObject.Size = endSize\n\tlocal endAbsoluteSizeY = self.GuiObject.AbsoluteSize.Y\n\tself.GuiObject.Size = curSize\n\n\tlocal pixelDistance = math.abs(endAbsoluteSizeY - curAbsoluteSizeY)\n\tlocal tweeningTime = math.min(1, (pixelDistance * (1 / self.TweenPixelsPerSecond))) -- pixelDistance * (seconds per pixels)\n\n\tlocal success = pcall(function() self.GuiObject:TweenSize(endSize, Enum.EasingDirection.Out, Enum.EasingStyle.Quad, tweeningTime, true) end)\n\tif (not success) then\n\t\tself.GuiObject.Size = endSize\n\tend\nend\n\nfunction methods:SetTextSize(textSize)\n\tif not self:IsInCustomState() then\n\t\tif self.TextBox then\n\t\t\tself.TextBox.TextSize = textSize\n\t\tend\n\t\tif self.TextLabel then\n\t\t\tself.TextLabel.TextSize = textSize\n\t\tend\n\tend\nend\n\nfunction methods:GetDefaultChannelNameColor()\n\tif ChatSettings.DefaultChannelNameColor then\n\t\treturn ChatSettings.DefaultChannelNameColor\n\tend\n\treturn Color3.fromRGB(35, 76, 142)\nend\n\nfunction methods:SetChannelTarget(targetChannel)\n\tlocal messageModeTextButton = self.GuiObjects.MessageModeTextButton\n\tlocal textBox = self.TextBox\n\tlocal textLabel = self.TextLabel\n\n\tself.TargetChannel = targetChannel\n\n\tif not self:IsInCustomState() then\n\t\tif targetChannel ~= ChatSettings.GeneralChannelName then\n\t\t\tmessageModeTextButton.Size = UDim2.new(0, 1000, 1, 0)\n\t\t\tmessageModeTextButton.Text = string.format(\"[%s] \", targetChannel)\n\n\t\t\tlocal channelNameColor = self:GetChannelNameColor(targetChannel)\n\t\t\tif channelNameColor then\n\t\t\t\tmessageModeTextButton.TextColor3 = channelNameColor\n\t\t\telse\n\t\t\t\tmessageModeTextButton.TextColor3 = self:GetDefaultChannelNameColor()\n\t\t\tend\n\n\t\t\tlocal xSize = messageModeTextButton.TextBounds.X\n\t\t\tmessageModeTextButton.Size = UDim2.new(0, xSize, 1, 0)\n\t\t\ttextBox.Size = UDim2.new(1, -xSize, 1, 0)\n\t\t\ttextBox.Position = UDim2.new(0, xSize, 0, 0)\n\t\t\ttextLabel.Size = UDim2.new(1, -xSize, 1, 0)\n\t\t\ttextLabel.Position = UDim2.new(0, xSize, 0, 0)\n\t\telse\n\t\t\tmessageModeTextButton.Text = \"\"\n\t\t\tmessageModeTextButton.Size = UDim2.new(0, 0, 0, 0)\n\t\t\ttextBox.Size = UDim2.new(1, 0, 1, 0)\n\t\t\ttextBox.Position = UDim2.new(0, 0, 0, 0)\n\t\t\ttextLabel.Size = UDim2.new(1, 0, 1, 0)\n\t\t\ttextLabel.Position = UDim2.new(0, 0, 0, 0)\n\t\tend\n\tend\nend\n\nfunction methods:IsInCustomState()\n\treturn self.InCustomState\nend\n\nfunction methods:ResetCustomState()\n\tif self.InCustomState then\n\t\tself.CustomState:Destroy()\n\t\tself.CustomState = nil\n\t\tself.InCustomState = false\n\n\t\tself.ChatBarParentFrame:ClearAllChildren()\n\t\tself:CreateGuiObjects(self.ChatBarParentFrame)\n\t\tself:SetTextLabelText(\n\t\t\tChatLocalization:Get(\n\t\t\t\t\"GameChat_ChatMain_ChatBarText\",\n\t\t\t\t'To chat click here or press \"/\" key'\n\t\t\t)\n\t\t)\n\tend\nend\n\nfunction methods:EnterWhisperState(player)\n\tself:ResetCustomState()\n\tself:CaptureFocus()\n\tif WhisperModule.CustomStateCreator then\n\t\tself.CustomState = WhisperModule.CustomStateCreator(\n\t\t\tplayer,\n\t\t\tself.ChatWindow,\n\t\t\tself,\n\t\t\tChatSettings\n\t\t)\n\t\tself.InCustomState = true\n\telse\n\t\tself:SetText(\"/w \" .. player.Name)\n\tend\nend\n\nfunction methods:GetCustomMessage()\n\tif self.InCustomState then\n\t\treturn self.CustomState:GetMessage()\n\tend\n\treturn nil\nend\n\nfunction methods:CustomStateProcessCompletedMessage(message)\n\tif self.InCustomState then\n\t\treturn self.CustomState:ProcessCompletedMessage()\n\tend\n\treturn false\nend\n\nfunction methods:FadeOutBackground(duration)\n\tself.AnimParams.Background_TargetTransparency = 1\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\n\tself:FadeOutText(duration)\nend\n\nfunction methods:FadeInBackground(duration)\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\n\tself:FadeInText(duration)\nend\n\nfunction methods:FadeOutText(duration)\n\tself.AnimParams.Text_TargetTransparency = 1\n\tself.AnimParams.Text_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeInText(duration)\n\tself.AnimParams.Text_TargetTransparency = 0.4\n\tself.AnimParams.Text_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:AnimGuiObjects()\n\tself.GuiObject.BackgroundTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.GuiObjects.TextBoxFrame.BackgroundTransparency = self.AnimParams.Background_CurrentTransparency\n\n\tself.GuiObjects.TextLabel.TextTransparency = self.AnimParams.Text_CurrentTransparency\n\tself.GuiObjects.TextBox.TextTransparency = self.AnimParams.Text_CurrentTransparency\n\tself.GuiObjects.MessageModeTextButton.TextTransparency = self.AnimParams.Text_CurrentTransparency\nend\n\nfunction methods:InitializeAnimParams()\n\tself.AnimParams.Text_TargetTransparency = 0.4\n\tself.AnimParams.Text_CurrentTransparency = 0.4\n\tself.AnimParams.Text_NormalizedExptValue = 1\n\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_CurrentTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = 1\nend\n\nfunction methods:Update(dtScale)\n\tself.AnimParams.Text_CurrentTransparency = CurveUtil:Expt(\n\t\t\tself.AnimParams.Text_CurrentTransparency,\n\t\t\tself.AnimParams.Text_TargetTransparency,\n\t\t\tself.AnimParams.Text_NormalizedExptValue,\n\t\t\tdtScale\n\t)\n\tself.AnimParams.Background_CurrentTransparency = CurveUtil:Expt(\n\t\t\tself.AnimParams.Background_CurrentTransparency,\n\t\t\tself.AnimParams.Background_TargetTransparency,\n\t\t\tself.AnimParams.Background_NormalizedExptValue,\n\t\t\tdtScale\n\t)\n\n\tself:AnimGuiObjects()\nend\n\nfunction methods:SetChannelNameColor(channelName, channelNameColor)\n\tself.ChannelNameColors[channelName] = channelNameColor\n\tif self.GuiObjects.MessageModeTextButton.Text == channelName then\n\t\tself.GuiObjects.MessageModeTextButton.TextColor3 = channelNameColor\n\tend\nend\n\nfunction methods:GetChannelNameColor(channelName)\n\treturn self.ChannelNameColors[channelName]\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new(CommandProcessor, ChatWindow)\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.GuiObject = nil\n\tobj.ChatBarParentFrame = nil\n\tobj.TextBox = nil\n\tobj.TextLabel = nil\n\tobj.GuiObjects = {}\n\tobj.eGuiObjectsChanged = Instance.new(\"BindableEvent\")\n\tobj.GuiObjectsChanged = obj.eGuiObjectsChanged.Event\n\tobj.TextBoxConnections = {}\n\n\tobj.InCustomState = false\n\tobj.CustomState = nil\n\n\tobj.TargetChannel = nil\n\tobj.CommandProcessor = CommandProcessor\n\tobj.ChatWindow = ChatWindow\n\n\tobj.TweenPixelsPerSecond = 500\n\tobj.TargetYSize = 0\n\n\tobj.AnimParams = {}\n\tobj.CalculatingSizeLock = false\n\n\tobj.ChannelNameColors = {}\n\n\tobj.UserHasChatOff = false\n\n\tobj:InitializeAnimParams()\n\n\tChatSettings.SettingsChanged:connect(function(setting, value)\n\t\tif (setting == \"ChatBarTextSize\") then\n\t\t\tobj:SetTextSize(value)\n\t\tend\n\tend)\n\n\tcoroutine.wrap(function()\n\t\tlocal success, canLocalUserChat = pcall(function()\n\t\t\treturn Chat:CanUserChatAsync(LocalPlayer.UserId)\n\t\tend)\n\t\tlocal canChat = success and (RunService:IsStudio() or canLocalUserChat)\n\t\tif canChat == false then\n\t\t\tobj.UserHasChatOff = true\n\t\t\tobj:DoLockChatBar()\n\t\tend\n\tend)()\n\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/ChatScript/ChatMain/ChatChannel.lua",
    "content": "--\t// FileName: ChatChannel.lua\n--\t// Written by: Xsitsu\n--\t// Description: ChatChannel class for handling messages being added and removed from the chat channel.\n\nlocal module = {}\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal modulesFolder = script.Parent\n\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:Destroy()\n\tself.Destroyed = true\nend\n\nfunction methods:SetActive(active)\n\tif active == self.Active then\n\t\treturn\n\tend\n\tif active == false then\n\t\tself.MessageLogDisplay:Clear()\n\telse\n\t\tself.MessageLogDisplay:SetCurrentChannelName(self.Name)\n\t\tfor i = 1, #self.MessageLog do\n\t\t\tself.MessageLogDisplay:AddMessage(self.MessageLog[i])\n\t\tend\n\tend\n\tself.Active = active\nend\n\nfunction methods:UpdateMessageFiltered(messageData)\n\tlocal searchIndex = 1\n\tlocal searchTable = self.MessageLog\n\tlocal messageObj = nil\n\twhile (#searchTable >= searchIndex) do\n\t\tlocal obj = searchTable[searchIndex]\n\n\t\tif (obj.ID == messageData.ID) then\n\t\t\tmessageObj = obj\n\t\t\tbreak\n\t\tend\n\n\t\tsearchIndex = searchIndex + 1\n\tend\n\n\tif messageObj then\n\t\tmessageObj.Message = messageData.Message\n\t\tmessageObj.IsFiltered = true\n\t\tif self.Active then\n\t\t\tself.MessageLogDisplay:UpdateMessageFiltered(messageObj)\n\t\tend\n\telse\n\t\t-- We have not seen this filtered message before, but we should still add it to our log.\n\t\tself:AddMessageToChannelByTimeStamp(messageData)\n\tend\nend\n\nfunction methods:AddMessageToChannel(messageData)\n\ttable.insert(self.MessageLog, messageData)\n\tif self.Active then\n\t\tself.MessageLogDisplay:AddMessage(messageData)\n\tend\n\tif #self.MessageLog > ChatSettings.MessageHistoryLengthPerChannel then\n\t\tself:RemoveLastMessageFromChannel()\n\tend\nend\n\nfunction methods:InternalAddMessageAtTimeStamp(messageData)\n\tfor i = 1, #self.MessageLog do\n\t\tif messageData.Time < self.MessageLog[i].Time then\n\t\t\ttable.insert(self.MessageLog, i, messageData)\n\t\t\treturn\n\t\tend\n\tend\n\ttable.insert(self.MessageLog, messageData)\nend\n\nfunction methods:AddMessagesToChannelByTimeStamp(messageLog, startIndex)\n\tfor i = startIndex, #messageLog do\n\t\tself:InternalAddMessageAtTimeStamp(messageLog[i])\n\tend\n\twhile #self.MessageLog > ChatSettings.MessageHistoryLengthPerChannel do\n\t\ttable.remove(self.MessageLog, 1)\n\tend\n\tif self.Active then\n\t\tself.MessageLogDisplay:Clear()\n\t\tfor i = 1, #self.MessageLog do\n\t\t\tself.MessageLogDisplay:AddMessage(self.MessageLog[i])\n\t\tend\n\tend\nend\n\nfunction methods:AddMessageToChannelByTimeStamp(messageData)\n\tif #self.MessageLog >= 1 then\n\t\t-- These are the fast cases to evalutate.\n\t\tif self.MessageLog[1].Time > messageData.Time then\n\t\t\treturn\n\t\telseif messageData.Time >= self.MessageLog[#self.MessageLog].Time then\n\t\t\tself:AddMessageToChannel(messageData)\n\t\t\treturn\n\t\tend\n\n\t\tfor i = 1, #self.MessageLog do\n\t\t\tif messageData.Time < self.MessageLog[i].Time then\n\t\t\t\ttable.insert(self.MessageLog, i, messageData)\n\n\t\t\t\tif #self.MessageLog > ChatSettings.MessageHistoryLengthPerChannel then\n\t\t\t\t\tself:RemoveLastMessageFromChannel()\n\t\t\t\tend\n\n\t\t\t\tif self.Active then\n\t\t\t\t\tself.MessageLogDisplay:AddMessageAtIndex(messageData, i)\n\t\t\t\tend\n\n\t\t\t\treturn\n\t\t\tend\n\t\tend\n\telse\n\t\tself:AddMessageToChannel(messageData)\n\tend\nend\n\nfunction methods:RemoveLastMessageFromChannel()\n\ttable.remove(self.MessageLog, 1)\n\n\tif self.Active then\n\t\tself.MessageLogDisplay:RemoveLastMessage()\n\tend\nend\n\nfunction methods:ClearMessageLog()\n\tself.MessageLog = {}\n\n\tif self.Active then\n\t\tself.MessageLogDisplay:Clear()\n\tend\nend\n\nfunction methods:RegisterChannelTab(tab)\n\tself.ChannelTab = tab\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new(channelName, messageLogDisplay)\n\tlocal obj = setmetatable({}, methods)\n\tobj.Destroyed = false\n\tobj.Active = false\n\n\tobj.MessageLog = {}\n\tobj.MessageLogDisplay = messageLogDisplay\n\tobj.ChannelTab = nil\n\tobj.Name = channelName\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/ChatScript/ChatMain/ChatWindow.lua",
    "content": "--\t// FileName: ChatWindow.lua\n--\t// Written by: Xsitsu\n--\t// Description: Main GUI window piece. Manages ChatBar, ChannelsBar, and ChatChannels.\n\nlocal module = {}\n\nlocal Players = game:GetService(\"Players\")\nlocal Chat = game:GetService(\"Chat\")\nlocal LocalPlayer = Players.LocalPlayer\nlocal PlayerGui = LocalPlayer:WaitForChild(\"PlayerGui\")\n\nlocal PHONE_SCREEN_WIDTH = 640\nlocal TABLET_SCREEN_WIDTH = 1024\n\nlocal DEVICE_PHONE = 1\nlocal DEVICE_TABLET = 2\nlocal DEVICE_DESKTOP = 3\n\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal modulesFolder = script.Parent\nlocal moduleChatChannel = require(modulesFolder:WaitForChild(\"ChatChannel\"))\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal CurveUtil = require(modulesFolder:WaitForChild(\"CurveUtil\"))\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nfunction getClassicChatEnabled()\n\tif ChatSettings.ClassicChatEnabled ~= nil then\n\t\treturn ChatSettings.ClassicChatEnabled\n\tend\n\treturn Players.ClassicChat\nend\n\nfunction getBubbleChatEnabled()\n\tif ChatSettings.BubbleChatEnabled ~= nil then\n\t\treturn ChatSettings.BubbleChatEnabled\n\tend\n\treturn Players.BubbleChat\nend\n\nfunction bubbleChatOnly()\n \treturn not getClassicChatEnabled() and getBubbleChatEnabled()\nend\n\n-- only merge property defined on target\nfunction mergeProps(source, target)\n\tif not source or not target then return end\n\tfor prop, value in pairs(source) do\n\t\tif target[prop] ~= nil then\n\t\t\ttarget[prop] = value\n\t\tend\n\tend\nend\n\nfunction methods:CreateGuiObjects(targetParent)\n\tlocal userDefinedChatWindowStyle \n\tpcall(function()\n\t\tuserDefinedChatWindowStyle= Chat:InvokeChatCallback(Enum.ChatCallbackType.OnCreatingChatWindow, nil)\n\tend)\n\n\t-- merge the userdefined settings with the ChatSettings\n\tmergeProps(userDefinedChatWindowStyle, ChatSettings)\n\n\tlocal BaseFrame = Instance.new(\"Frame\")\n\tBaseFrame.BackgroundTransparency = 1\n\tBaseFrame.Active = ChatSettings.WindowDraggable\n\tBaseFrame.Parent = targetParent\n    BaseFrame.AutoLocalize = false\n\n\tlocal ChatBarParentFrame = Instance.new(\"Frame\")\n\tChatBarParentFrame.Selectable = false\n\tChatBarParentFrame.Name = \"ChatBarParentFrame\"\n\tChatBarParentFrame.BackgroundTransparency = 1\n\tChatBarParentFrame.Parent = BaseFrame\n\n\tlocal ChannelsBarParentFrame = Instance.new(\"Frame\")\n\tChannelsBarParentFrame.Selectable = false\n\tChannelsBarParentFrame.Name = \"ChannelsBarParentFrame\"\n\tChannelsBarParentFrame.BackgroundTransparency = 1\n\tChannelsBarParentFrame.Position = UDim2.new(0, 0, 0, 0)\n\tChannelsBarParentFrame.Parent = BaseFrame\n\n\tlocal ChatChannelParentFrame = Instance.new(\"Frame\")\n\tChatChannelParentFrame.Selectable = false\n\tChatChannelParentFrame.Name = \"ChatChannelParentFrame\"\n\tChatChannelParentFrame.BackgroundTransparency = 1\n\tChatChannelParentFrame.BackgroundColor3 = ChatSettings.BackGroundColor\n\tChatChannelParentFrame.BackgroundTransparency = 0.6\n\tChatChannelParentFrame.BorderSizePixel = 0\n\tChatChannelParentFrame.Parent = BaseFrame\n\n\tlocal ChatResizerFrame = Instance.new(\"ImageButton\")\n\tChatResizerFrame.Selectable = false\n\tChatResizerFrame.Image = \"\"\n\tChatResizerFrame.BackgroundTransparency = 0.6\n\tChatResizerFrame.BorderSizePixel = 0\n\tChatResizerFrame.Visible = false\n\tChatResizerFrame.BackgroundColor3 = ChatSettings.BackGroundColor\n\tChatResizerFrame.Active = true\n\tif bubbleChatOnly() then\n\t\tChatResizerFrame.Position = UDim2.new(1, -ChatResizerFrame.AbsoluteSize.X, 0, 0)\n\telse\n\t\tChatResizerFrame.Position = UDim2.new(1, -ChatResizerFrame.AbsoluteSize.X, 1, -ChatResizerFrame.AbsoluteSize.Y)\n\tend\n\tChatResizerFrame.Parent = BaseFrame\n\n\tlocal ResizeIcon = Instance.new(\"ImageLabel\")\n\tResizeIcon.Selectable = false\n\tResizeIcon.Size = UDim2.new(0.8, 0, 0.8, 0)\n\tResizeIcon.Position = UDim2.new(0.2, 0, 0.2, 0)\n\tResizeIcon.BackgroundTransparency = 1\n\tResizeIcon.Image = \"rbxassetid://261880743\"\n\tResizeIcon.Parent = ChatResizerFrame\n\n\tlocal function GetScreenGuiParent()\n\t\t--// Travel up parent list until you find the ScreenGui that the chat window is parented to\n\t\tlocal screenGuiParent = BaseFrame\n\t\twhile (screenGuiParent and not screenGuiParent:IsA(\"ScreenGui\")) do\n\t\t\tscreenGuiParent = screenGuiParent.Parent\n\t\tend\n\n\t\treturn screenGuiParent\n\tend\n\n\n\tlocal deviceType = DEVICE_DESKTOP\n\n\tlocal screenGuiParent = GetScreenGuiParent()\n\tif (screenGuiParent.AbsoluteSize.X <= PHONE_SCREEN_WIDTH) then\n\t\tdeviceType = DEVICE_PHONE\n\n\telseif (screenGuiParent.AbsoluteSize.X <= TABLET_SCREEN_WIDTH) then\n\t\tdeviceType = DEVICE_TABLET\n\n\tend\n\n\tlocal checkSizeLock = false\n\tlocal function doCheckSizeBounds()\n\t\tif (checkSizeLock) then return end\n\t\tcheckSizeLock = true\n\n\t\tif (not BaseFrame:IsDescendantOf(PlayerGui)) then return end\n\n\t\tlocal screenGuiParent = GetScreenGuiParent()\n\n\t\tlocal minWinSize = ChatSettings.MinimumWindowSize\n\t\tlocal maxWinSize = ChatSettings.MaximumWindowSize\n\n\t\tlocal forceMinY = ChannelsBarParentFrame.AbsoluteSize.Y + ChatBarParentFrame.AbsoluteSize.Y\n\n\t\tlocal minSizePixelX = (minWinSize.X.Scale * screenGuiParent.AbsoluteSize.X) + minWinSize.X.Offset\n\t\tlocal minSizePixelY = math.max((minWinSize.Y.Scale * screenGuiParent.AbsoluteSize.Y) + minWinSize.Y.Offset, forceMinY)\n\n\t\tlocal maxSizePixelX = (maxWinSize.X.Scale * screenGuiParent.AbsoluteSize.X) + maxWinSize.X.Offset\n\t\tlocal maxSizePixelY = (maxWinSize.Y.Scale * screenGuiParent.AbsoluteSize.Y) + maxWinSize.Y.Offset\n\n\t\tlocal absSizeX = BaseFrame.AbsoluteSize.X\n\t\tlocal absSizeY = BaseFrame.AbsoluteSize.Y\n\n\t\tif (absSizeX < minSizePixelX) then\n\t\t\tlocal offset = UDim2.new(0, minSizePixelX - absSizeX, 0, 0)\n\t\t\tBaseFrame.Size = BaseFrame.Size + offset\n\n\t\telseif (absSizeX > maxSizePixelX) then\n\t\t\tlocal offset = UDim2.new(0, maxSizePixelX - absSizeX, 0, 0)\n\t\t\tBaseFrame.Size = BaseFrame.Size + offset\n\n\t\tend\n\n\t\tif (absSizeY < minSizePixelY) then\n\t\t\tlocal offset = UDim2.new(0, 0, 0, minSizePixelY - absSizeY)\n\t\t\tBaseFrame.Size = BaseFrame.Size + offset\n\n\t\telseif (absSizeY > maxSizePixelY) then\n\t\t\tlocal offset = UDim2.new(0, 0, 0, maxSizePixelY - absSizeY)\n\t\t\tBaseFrame.Size = BaseFrame.Size + offset\n\n\t\tend\n\n\t\tlocal xScale = BaseFrame.AbsoluteSize.X / screenGuiParent.AbsoluteSize.X\n\t\tlocal yScale = BaseFrame.AbsoluteSize.Y / screenGuiParent.AbsoluteSize.Y\n\t\tBaseFrame.Size = UDim2.new(xScale, 0, yScale, 0)\n\n\t\tcheckSizeLock = false\n\tend\n\n\n\tBaseFrame.Changed:connect(function(prop)\n\t\tif (prop == \"AbsoluteSize\") then\n\t\t\tdoCheckSizeBounds()\n\t\tend\n\tend)\n\n\n\n\tChatResizerFrame.DragBegin:connect(function(startUdim)\n\t\tBaseFrame.Draggable = false\n\tend)\n\n\tlocal function UpdatePositionFromDrag(atPos)\n\t\tif ChatSettings.WindowDraggable == false and ChatSettings.WindowResizable == false then\n\t\t\treturn\n\t\tend\n\t\tlocal newSize = atPos - BaseFrame.AbsolutePosition + ChatResizerFrame.AbsoluteSize\n\t\tBaseFrame.Size = UDim2.new(0, newSize.X, 0, newSize.Y)\n\t\tif bubbleChatOnly() then\n\t\t\tChatResizerFrame.Position = UDim2.new(1, -ChatResizerFrame.AbsoluteSize.X, 0, 0)\n\t\telse\n\t\t\tChatResizerFrame.Position = UDim2.new(1, -ChatResizerFrame.AbsoluteSize.X, 1, -ChatResizerFrame.AbsoluteSize.Y)\n\t\tend\n\tend\n\n\tChatResizerFrame.DragStopped:connect(function(endX, endY)\n\t\tBaseFrame.Draggable = ChatSettings.WindowDraggable\n\t\t--UpdatePositionFromDrag(Vector2.new(endX, endY))\n\tend)\n\n\tlocal resizeLock = false\n\tChatResizerFrame.Changed:connect(function(prop)\n\t\tif (prop == \"AbsolutePosition\" and not BaseFrame.Draggable) then\n\t\t\tif (resizeLock) then return end\n\t\t\tresizeLock = true\n\n\t\t\tUpdatePositionFromDrag(ChatResizerFrame.AbsolutePosition)\n\n\t\t\tresizeLock = false\n\t\tend\n\tend)\n\n\tlocal function CalculateChannelsBarPixelSize(textSize)\n\t\tif (deviceType == DEVICE_PHONE) then\n\t\t\ttextSize = textSize or ChatSettings.ChatChannelsTabTextSizePhone\n\t\telse\n\t\t\ttextSize = textSize or ChatSettings.ChatChannelsTabTextSize\n\t\tend\n\n\t\tlocal channelsBarTextYSize = textSize\n\t\tlocal chatChannelYSize = math.max(32, channelsBarTextYSize + 8) + 2\n\n\t\treturn chatChannelYSize\n\tend\n\n\tlocal function CalculateChatBarPixelSize(textSize)\n\t\tif (deviceType == DEVICE_PHONE) then\n\t\t\ttextSize = textSize or ChatSettings.ChatBarTextSizePhone\n\t\telse\n\t\t\ttextSize = textSize or ChatSettings.ChatBarTextSize\n\t\tend\n\n\t\tlocal chatBarTextSizeY = textSize\n\t\tlocal chatBarYSize = chatBarTextSizeY + (7 * 2) + (5 * 2)\n\n\t\treturn chatBarYSize\n\tend\n\n\tif bubbleChatOnly() then\n\t\tChatBarParentFrame.Position = UDim2.new(0, 0, 0, 0)\n\t\tChannelsBarParentFrame.Visible = false\n\t\tChannelsBarParentFrame.Active = false\n\t\tChatChannelParentFrame.Visible = false\n\t\tChatChannelParentFrame.Active = false\n\n\t\tlocal useXScale = 0\n\t\tlocal useXOffset = 0\n\n\t\tlocal screenGuiParent = GetScreenGuiParent()\n\n\t\tif (deviceType == DEVICE_PHONE) then\n\t\t\tuseXScale = ChatSettings.DefaultWindowSizePhone.X.Scale\n\t\t\tuseXOffset = ChatSettings.DefaultWindowSizePhone.X.Offset\n\n\t\telseif (deviceType == DEVICE_TABLET) then\n\t\t\tuseXScale = ChatSettings.DefaultWindowSizeTablet.X.Scale\n\t\t\tuseXOffset = ChatSettings.DefaultWindowSizeTablet.X.Offset\n\n\t\telse\n\t\t\tuseXScale = ChatSettings.DefaultWindowSizeTablet.X.Scale\n\t\t\tuseXOffset = ChatSettings.DefaultWindowSizeTablet.X.Offset\n\n\t\tend\n\n\t\tlocal chatBarYSize = CalculateChatBarPixelSize()\n\n\t\tBaseFrame.Size = UDim2.new(useXScale, useXOffset, 0, chatBarYSize)\n\t\tBaseFrame.Position = ChatSettings.DefaultWindowPosition\n\n\telse\n\n\t\tlocal screenGuiParent = GetScreenGuiParent()\n\n\t\tif (deviceType == DEVICE_PHONE) then\n\t\t\tBaseFrame.Size = ChatSettings.DefaultWindowSizePhone\n\n\t\telseif (deviceType == DEVICE_TABLET) then\n\t\t\tBaseFrame.Size = ChatSettings.DefaultWindowSizeTablet\n\n\t\telse\n\t\t\tBaseFrame.Size = ChatSettings.DefaultWindowSizeDesktop\n\n\t\tend\n\n\t\tBaseFrame.Position = ChatSettings.DefaultWindowPosition\n\n\tend\n\n\tif (deviceType == DEVICE_PHONE) then\n\t\tChatSettings.ChatWindowTextSize = ChatSettings.ChatWindowTextSizePhone\n\t\tChatSettings.ChatChannelsTabTextSize = ChatSettings.ChatChannelsTabTextSizePhone\n\t\tChatSettings.ChatBarTextSize = ChatSettings.ChatBarTextSizePhone\n\tend\n\n\tlocal function UpdateDraggable(enabled)\n\t\tBaseFrame.Active = enabled\n\t\tBaseFrame.Draggable = enabled\n\tend\n\n\tlocal function UpdateResizable(enabled)\n\t\tChatResizerFrame.Visible = enabled\n\t\tChatResizerFrame.Draggable = enabled\n\n\t\tlocal frameSizeY = ChatBarParentFrame.Size.Y.Offset\n\n\t\tif (enabled) then\n\t\t\tChatBarParentFrame.Size = UDim2.new(1, -frameSizeY - 2, 0, frameSizeY)\n\t\t\tif not bubbleChatOnly() then\n\t\t\t\tChatBarParentFrame.Position = UDim2.new(0, 0, 1, -frameSizeY)\n\t\t\tend\n\t\telse\n\t\t\tChatBarParentFrame.Size = UDim2.new(1, 0, 0, frameSizeY)\n\t\t\tif not bubbleChatOnly() then\n\t\t\t\tChatBarParentFrame.Position = UDim2.new(0, 0, 1, -frameSizeY)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function UpdateChatChannelParentFrameSize()\n\t\tlocal channelsBarSize = CalculateChannelsBarPixelSize()\n\t\tlocal chatBarSize = CalculateChatBarPixelSize()\n\n\t\tif (ChatSettings.ShowChannelsBar) then\n\t\t\tChatChannelParentFrame.Size = UDim2.new(1, 0, 1, -(channelsBarSize + chatBarSize + 2 + 2))\n\t\t\tChatChannelParentFrame.Position = UDim2.new(0, 0, 0, channelsBarSize + 2)\n\n\t\telse\n\t\t\tChatChannelParentFrame.Size = UDim2.new(1, 0, 1, -(chatBarSize + 2 + 2))\n\t\t\tChatChannelParentFrame.Position = UDim2.new(0, 0, 0, 2)\n\n\t\tend\n\tend\n\n\tlocal function UpdateChatChannelsTabTextSize(size)\n\t\tlocal channelsBarSize = CalculateChannelsBarPixelSize(size)\n\t\tChannelsBarParentFrame.Size = UDim2.new(1, 0, 0, channelsBarSize)\n\n\t\tUpdateChatChannelParentFrameSize()\n\tend\n\n\tlocal function UpdateChatBarTextSize(size)\n\t\tlocal chatBarSize = CalculateChatBarPixelSize(size)\n\n\t\tChatBarParentFrame.Size = UDim2.new(1, 0, 0, chatBarSize)\n\t\tif not bubbleChatOnly() then\n\t\t\tChatBarParentFrame.Position = UDim2.new(0, 0, 1, -chatBarSize)\n\t\tend\n\n\t\tChatResizerFrame.Size = UDim2.new(0, chatBarSize, 0, chatBarSize)\n\t\tChatResizerFrame.Position = UDim2.new(1, -chatBarSize, 1, -chatBarSize)\n\n\t\tUpdateChatChannelParentFrameSize()\n\t\tUpdateResizable(ChatSettings.WindowResizable)\n\tend\n\n\tlocal function UpdateShowChannelsBar(enabled)\n\t\tChannelsBarParentFrame.Visible = enabled\n\t\tUpdateChatChannelParentFrameSize()\n\tend\n\n\tUpdateChatChannelsTabTextSize(ChatSettings.ChatChannelsTabTextSize)\n\tUpdateChatBarTextSize(ChatSettings.ChatBarTextSize)\n\tUpdateDraggable(ChatSettings.WindowDraggable)\n\tUpdateResizable(ChatSettings.WindowResizable)\n\tUpdateShowChannelsBar(ChatSettings.ShowChannelsBar)\n\n\tChatSettings.SettingsChanged:connect(function(setting, value)\n\t\tif (setting == \"WindowDraggable\") then\n\t\t\tUpdateDraggable(value)\n\n\t\telseif (setting == \"WindowResizable\") then\n\t\t\tUpdateResizable(value)\n\n\t\telseif (setting == \"ChatChannelsTabTextSize\") then\n\t\t\tUpdateChatChannelsTabTextSize(value)\n\n\t\telseif (setting == \"ChatBarTextSize\") then\n\t\t\tUpdateChatBarTextSize(value)\n\n\t\telseif (setting == \"ShowChannelsBar\") then\n\t\t\tUpdateShowChannelsBar(value)\n\n\t\tend\n\tend)\n\n\tself.GuiObject = BaseFrame\n\n\tself.GuiObjects.BaseFrame = BaseFrame\n\tself.GuiObjects.ChatBarParentFrame = ChatBarParentFrame\n\tself.GuiObjects.ChannelsBarParentFrame = ChannelsBarParentFrame\n\tself.GuiObjects.ChatChannelParentFrame = ChatChannelParentFrame\n\tself.GuiObjects.ChatResizerFrame = ChatResizerFrame\n\tself.GuiObjects.ResizeIcon = ResizeIcon\n\tself:AnimGuiObjects()\nend\n\nfunction methods:GetChatBar()\n\treturn self.ChatBar\nend\n\nfunction methods:RegisterChatBar(ChatBar)\n\tself.ChatBar = ChatBar\n\tself.ChatBar:CreateGuiObjects(self.GuiObjects.ChatBarParentFrame)\nend\n\nfunction methods:RegisterChannelsBar(ChannelsBar)\n\tself.ChannelsBar = ChannelsBar\n\tself.ChannelsBar:CreateGuiObjects(self.GuiObjects.ChannelsBarParentFrame)\nend\n\nfunction methods:RegisterMessageLogDisplay(MessageLogDisplay)\n\tself.MessageLogDisplay = MessageLogDisplay\n\tself.MessageLogDisplay.GuiObject.Parent = self.GuiObjects.ChatChannelParentFrame\nend\n\nfunction methods:AddChannel(channelName)\n\tif (self:GetChannel(channelName)) then\n\t\terror(\"Channel '\" .. channelName .. \"' already exists!\")\n\t\treturn\n\tend\n\n\tlocal channel = moduleChatChannel.new(channelName, self.MessageLogDisplay)\n\tself.Channels[channelName:lower()] = channel\n\n\tchannel:SetActive(false)\n\n\tlocal tab = self.ChannelsBar:AddChannelTab(channelName)\n\ttab.NameTag.MouseButton1Click:connect(function()\n\t\tself:SwitchCurrentChannel(channelName)\n\tend)\n\n\tchannel:RegisterChannelTab(tab)\n\n\treturn channel\nend\n\nfunction methods:GetFirstChannel()\n\t--// Channels are not indexed numerically, so this function is necessary.\n\t--// Grabs and returns the first channel it happens to, or nil if none exist.\n\tfor i, v in pairs(self.Channels) do\n\t\treturn v\n\tend\n\treturn nil\nend\n\nfunction methods:RemoveChannel(channelName)\n\tif (not self:GetChannel(channelName)) then\n\t\terror(\"Channel '\" .. channelName .. \"' does not exist!\")\n\tend\n\n\tlocal indexName = channelName:lower()\n\n\tlocal needsChannelSwitch = false\n\tif (self.Channels[indexName] == self:GetCurrentChannel()) then\n\t\tneedsChannelSwitch = true\n\n\t\tself:SwitchCurrentChannel(nil)\n\tend\n\n\tself.Channels[indexName]:Destroy()\n\tself.Channels[indexName] = nil\n\n\tself.ChannelsBar:RemoveChannelTab(channelName)\n\n\tif (needsChannelSwitch) then\n\t\tlocal generalChannelExists = (self:GetChannel(ChatSettings.GeneralChannelName) ~= nil)\n\t\tlocal removingGeneralChannel = (indexName == ChatSettings.GeneralChannelName:lower())\n\n\t\tlocal targetSwitchChannel = nil\n\n\t\tif (generalChannelExists and not removingGeneralChannel) then\n\t\t\ttargetSwitchChannel = ChatSettings.GeneralChannelName\n\t\telse\n\t\t\tlocal firstChannel = self:GetFirstChannel()\n\t\t\ttargetSwitchChannel = (firstChannel and firstChannel.Name or nil)\n\t\tend\n\n\t\tself:SwitchCurrentChannel(targetSwitchChannel)\n\tend\n\n\tif not ChatSettings.ShowChannelsBar then\n\t\tif self.ChatBar.TargetChannel == channelName then\n\t\t\tself.ChatBar:SetChannelTarget(ChatSettings.GeneralChannelName)\n\t\tend\n\tend\nend\n\nfunction methods:GetChannel(channelName)\n\treturn channelName and self.Channels[channelName:lower()] or nil\nend\n\nfunction methods:GetTargetMessageChannel()\n\tif (not ChatSettings.ShowChannelsBar) then\n\t\treturn self.ChatBar.TargetChannel\n\telse\n\t\tlocal curChannel = self:GetCurrentChannel()\n\t\treturn curChannel and curChannel.Name\n\tend\nend\n\nfunction methods:GetCurrentChannel()\n\treturn self.CurrentChannel\nend\n\nfunction methods:SwitchCurrentChannel(channelName)\n\tif (not ChatSettings.ShowChannelsBar) then\n\t\tlocal targ = self:GetChannel(channelName)\n\t\tif (targ) then\n\t\t\tself.ChatBar:SetChannelTarget(targ.Name)\n\t\tend\n\n\t\tchannelName = ChatSettings.GeneralChannelName\n\tend\n\n\tlocal cur = self:GetCurrentChannel()\n\tlocal new = self:GetChannel(channelName)\n\tif new == nil then\n\t\terror(string.format(\"Channel '%s' does not exist.\", channelName))\n\tend\n\n\tif (new ~= cur) then\n\t\tif (cur) then\n\t\t\tcur:SetActive(false)\n\t\t\tlocal tab = self.ChannelsBar:GetChannelTab(cur.Name)\n\t\t\ttab:SetActive(false)\n\t\tend\n\n\t\tif (new) then\n\t\t\tnew:SetActive(true)\n\t\t\tlocal tab = self.ChannelsBar:GetChannelTab(new.Name)\n\t\t\ttab:SetActive(true)\n\t\tend\n\n\t\tself.CurrentChannel = new\n\tend\n\nend\n\nfunction methods:UpdateFrameVisibility()\n\tself.GuiObject.Visible = (self.Visible and self.CoreGuiEnabled)\nend\n\nfunction methods:GetVisible()\n\treturn self.Visible\nend\n\nfunction methods:SetVisible(visible)\n\tself.Visible = visible\n\tself:UpdateFrameVisibility()\nend\n\nfunction methods:GetCoreGuiEnabled()\n\treturn self.CoreGuiEnabled\nend\n\nfunction methods:SetCoreGuiEnabled(enabled)\n\tself.CoreGuiEnabled = enabled\n\tself:UpdateFrameVisibility()\nend\n\nfunction methods:EnableResizable()\n\tself.GuiObjects.ChatResizerFrame.Active = true\nend\n\nfunction methods:DisableResizable()\n\tself.GuiObjects.ChatResizerFrame.Active = false\nend\n\nfunction methods:FadeOutBackground(duration)\n\tself.ChannelsBar:FadeOutBackground(duration)\n\tself.MessageLogDisplay:FadeOutBackground(duration)\n\tself.ChatBar:FadeOutBackground(duration)\n\n\tself.AnimParams.Background_TargetTransparency = 1\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeInBackground(duration)\n\tself.ChannelsBar:FadeInBackground(duration)\n\tself.MessageLogDisplay:FadeInBackground(duration)\n\tself.ChatBar:FadeInBackground(duration)\n\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(duration)\nend\n\nfunction methods:FadeOutText(duration)\n\tself.MessageLogDisplay:FadeOutText(duration)\n\tself.ChannelsBar:FadeOutText(duration)\nend\n\nfunction methods:FadeInText(duration)\n\tself.MessageLogDisplay:FadeInText(duration)\n\tself.ChannelsBar:FadeInText(duration)\nend\n\nfunction methods:AnimGuiObjects()\n\tself.GuiObjects.ChatChannelParentFrame.BackgroundTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.GuiObjects.ChatResizerFrame.BackgroundTransparency = self.AnimParams.Background_CurrentTransparency\n\tself.GuiObjects.ResizeIcon.ImageTransparency = self.AnimParams.Background_CurrentTransparency\nend\n\nfunction methods:InitializeAnimParams()\n\tself.AnimParams.Background_TargetTransparency = 0.6\n\tself.AnimParams.Background_CurrentTransparency = 0.6\n\tself.AnimParams.Background_NormalizedExptValue = CurveUtil:NormalizedDefaultExptValueInSeconds(0)\nend\n\nfunction methods:Update(dtScale)\n\tself.ChatBar:Update(dtScale)\n\tself.ChannelsBar:Update(dtScale)\n\tself.MessageLogDisplay:Update(dtScale)\n\n\tself.AnimParams.Background_CurrentTransparency = CurveUtil:Expt(\n\t\t\tself.AnimParams.Background_CurrentTransparency,\n\t\t\tself.AnimParams.Background_TargetTransparency,\n\t\t\tself.AnimParams.Background_NormalizedExptValue,\n\t\t\tdtScale\n\t)\n\n\tself:AnimGuiObjects()\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.GuiObject = nil\n\tobj.GuiObjects = {}\n\n\tobj.ChatBar = nil\n\tobj.ChannelsBar = nil\n\tobj.MessageLogDisplay = nil\n\n\tobj.Channels = {}\n\tobj.CurrentChannel = nil\n\n\tobj.Visible = true\n\tobj.CoreGuiEnabled = true\n\n\tobj.AnimParams = {}\n\n\tobj:InitializeAnimParams()\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/ChatScript/ChatMain/CommandProcessor.lua",
    "content": "--\t// FileName: ProcessCommands.lua\n--\t// Written by: TheGamer101\n--\t// Description: Module for processing commands using the client CommandModules\n\nlocal module = {}\nlocal methods = {}\nmethods.__index = methods\n\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal commandModules = clientChatModules:WaitForChild(\"CommandModules\")\nlocal commandUtil = require(commandModules:WaitForChild(\"Util\"))\nlocal modulesFolder = script.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\n\nfunction methods:SetupCommandProcessors()\n\tlocal commands = commandModules:GetChildren()\n\tfor i = 1, #commands do\n\t\tif commands[i]:IsA(\"ModuleScript\") then\n\t\t\tif commands[i].Name ~= \"Util\" then\n\t\t\t\tlocal commandProcessor = require(commands[i])\n\t\t\t\tlocal processorType = commandProcessor[commandUtil.KEY_COMMAND_PROCESSOR_TYPE]\n\t\t\t\tlocal processorFunction = commandProcessor[commandUtil.KEY_PROCESSOR_FUNCTION]\n\t\t\t\tif processorType == commandUtil.IN_PROGRESS_MESSAGE_PROCESSOR then\n\t\t\t\t\ttable.insert(self.InProgressMessageProcessors, processorFunction)\n\t\t\t\telseif processorType == commandUtil.COMPLETED_MESSAGE_PROCESSOR then\n\t\t\t\t\ttable.insert(self.CompletedMessageProcessors, processorFunction)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction methods:ProcessCompletedChatMessage(message, ChatWindow)\n\tfor i = 1, #self.CompletedMessageProcessors do\n\t\tlocal processedCommand = self.CompletedMessageProcessors[i](message, ChatWindow, ChatSettings)\n\t\tif processedCommand then\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\nfunction methods:ProcessInProgressChatMessage(message, ChatWindow, ChatBar)\n\tfor i = 1, #self.InProgressMessageProcessors do\n\t\tlocal customState = self.InProgressMessageProcessors[i](message, ChatWindow, ChatBar, ChatSettings)\n\t\tif customState then\n\t\t\treturn customState\n\t\tend\n\tend\n\treturn nil\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.CompletedMessageProcessors = {}\n\tobj.InProgressMessageProcessors = {}\n\n\tobj:SetupCommandProcessors()\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/ChatScript/ChatMain/CurveUtil.lua",
    "content": "local CurveUtil = {\t}\nlocal DEFAULT_THRESHOLD = 0.01\n\nfunction CurveUtil:Expt(start, to, pct, dt_scale)\n\tif math.abs(to - start) < DEFAULT_THRESHOLD then\n\t\treturn to\n\tend\n\n\tlocal y = CurveUtil:Expty(start,to,pct,dt_scale)\n\n\t--rtv = start + (to - start) * timescaled_friction--\n\tlocal delta = (to - start) * y\n\treturn start + delta\nend\n\nfunction CurveUtil:Expty(start, to, pct, dt_scale)\n\t--y = e ^ (-a * timescale)--\n\tlocal friction = 1 - pct\n\tlocal a = -math.log(friction)\n\treturn 1 - math.exp(-a * dt_scale)\nend\n\nfunction CurveUtil:Sign(val)\n\tif val > 0 then\n\t\treturn 1\n\telseif val < 0 then\n\t\treturn -1\n\telse\n\t\treturn 0\n\tend\nend\n\nfunction CurveUtil:BezierValForT(p0, p1, p2, p3, t)\n\tlocal cp0 = (1 - t) * (1 - t) * (1 - t)\n\tlocal cp1 = 3 * t * (1-t)*(1-t)\n\tlocal cp2 = 3 * t * t * (1 - t)\n\tlocal cp3 = t * t * t\n\treturn cp0 * p0 + cp1 * p1 + cp2 * p2 + cp3 * p3\nend\n\nCurveUtil._BezierPt2ForT = { x = 0; y = 0 }\nfunction CurveUtil:BezierPt2ForT(\n\tp0x, p0y,\n\tp1x, p1y,\n\tp2x, p2y,\n\tp3x, p3y,\n\tt)\n\n\tCurveUtil._BezierPt2ForT.x = CurveUtil:BezierValForT(p0x,p1x,p2x,p3x,t)\n\tCurveUtil._BezierPt2ForT.y = CurveUtil:BezierValForT(p0y,p1y,p2y,p3y,t)\n\treturn CurveUtil._BezierPt2ForT\nend\n\nfunction CurveUtil:YForPointOf2PtLine(pt1, pt2, x)\n\t--(y - y1\u0010)/(x - x1) = m--\n\tlocal m = (pt1.y - pt2.y) / (pt1.x - pt2.x)\n\t--y - mx = b--\n\tlocal b = pt1.y - m * pt1.x\n\treturn m * x + b\nend\n\nfunction CurveUtil:DeltaTimeToTimescale(s_frame_delta_time)\n\treturn s_frame_delta_time / (1.0 / 60.0)\nend\n\nfunction CurveUtil:SecondsToTick(sec)\n\treturn (1 / 60.0) / sec\nend\n\nfunction CurveUtil:ExptValueInSeconds(threshold, start, seconds)\n\t\treturn 1 - math.pow((threshold / start), 1 / (60.0 * seconds))\nend\n\nfunction CurveUtil:NormalizedDefaultExptValueInSeconds(seconds)\n\t\treturn self:ExptValueInSeconds(DEFAULT_THRESHOLD, 1, seconds)\nend\n\nreturn CurveUtil\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/ChatScript/ChatMain/MessageLabelCreator.lua",
    "content": "--\t// FileName: MessageLabelCreator.lua\n--\t// Written by: Xsitsu\n--\t// Description: Module to handle taking text and creating stylized GUI objects for display in ChatWindow.\n\nlocal OBJECT_POOL_SIZE = 50\n\nlocal module = {}\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal messageCreatorModules = clientChatModules:WaitForChild(\"MessageCreatorModules\")\nlocal messageCreatorUtil = require(messageCreatorModules:WaitForChild(\"Util\"))\nlocal modulesFolder = script.Parent\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal moduleObjectPool = require(modulesFolder:WaitForChild(\"ObjectPool\"))\nlocal MessageSender = require(modulesFolder:WaitForChild(\"MessageSender\"))\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\n-- merge properties on both table to target\nfunction mergeProps(source, target)\n\tif not source then return end\n\tfor prop, value in pairs(source) do\n\t\ttarget[prop] = value\n\tend\nend\n\nfunction ReturnToObjectPoolRecursive(instance, objectPool)\n\tlocal children = instance:GetChildren()\n\tfor i = 1, #children do\n\t\tReturnToObjectPoolRecursive(children[i], objectPool)\n\tend\n\tinstance.Parent = nil\n\tobjectPool:ReturnInstance(instance)\nend\n\nfunction GetMessageCreators()\n\tlocal typeToFunction = {}\n\tlocal creators = messageCreatorModules:GetChildren()\n\tfor i = 1, #creators do\n\t\tif creators[i]:IsA(\"ModuleScript\") then\n\t\t\tif creators[i].Name ~= \"Util\" then\n\t\t\t\tlocal creator = require(creators[i])\n\t\t\t\ttypeToFunction[creator[messageCreatorUtil.KEY_MESSAGE_TYPE]] = creator[messageCreatorUtil.KEY_CREATOR_FUNCTION]\n\t\t\tend\n\t\tend\n\tend\n\treturn typeToFunction\nend\n\nfunction methods:WrapIntoMessageObject(messageData, createdMessageObject)\n\tlocal BaseFrame = createdMessageObject[messageCreatorUtil.KEY_BASE_FRAME]\n\tlocal BaseMessage = nil\n\tif messageCreatorUtil.KEY_BASE_MESSAGE then\n\t\tBaseMessage = createdMessageObject[messageCreatorUtil.KEY_BASE_MESSAGE]\n\tend\n\tlocal UpdateTextFunction = createdMessageObject[messageCreatorUtil.KEY_UPDATE_TEXT_FUNC]\n\tlocal GetHeightFunction = createdMessageObject[messageCreatorUtil.KEY_GET_HEIGHT]\n\tlocal FadeInFunction = createdMessageObject[messageCreatorUtil.KEY_FADE_IN]\n\tlocal FadeOutFunction = createdMessageObject[messageCreatorUtil.KEY_FADE_OUT]\n\tlocal UpdateAnimFunction = createdMessageObject[messageCreatorUtil.KEY_UPDATE_ANIMATION]\n\n\tlocal obj = {}\n\n\tobj.ID = messageData.ID\n\tobj.BaseFrame = BaseFrame\n\tobj.BaseMessage = BaseMessage\n\tobj.UpdateTextFunction = UpdateTextFunction or function() warn(\"NO MESSAGE RESIZE FUNCTION\") end\n\tobj.GetHeightFunction = GetHeightFunction\n\tobj.FadeInFunction = FadeInFunction\n\tobj.FadeOutFunction = FadeOutFunction\n\tobj.UpdateAnimFunction = UpdateAnimFunction\n\tobj.ObjectPool = self.ObjectPool\n\tobj.Destroyed = false\n\n\tfunction obj:Destroy()\n\t\tReturnToObjectPoolRecursive(self.BaseFrame, self.ObjectPool)\n\t\tself.Destroyed = true\n\tend\n\n\treturn obj\nend\n\nfunction methods:CreateMessageLabel(messageData, currentChannelName)\n\n\tmessageData.Channel = currentChannelName\n\tlocal extraDeveloperFormatTable\n\tpcall(function()\n\t\textraDeveloperFormatTable = Chat:InvokeChatCallback(Enum.ChatCallbackType.OnClientFormattingMessage, messageData)\n\tend)\n\tmessageData.ExtraData = messageData.ExtraData or {}\n\tmergeProps(extraDeveloperFormatTable, messageData.ExtraData)\n\n\tlocal messageType = messageData.MessageType\n\tif self.MessageCreators[messageType] then\n\t\tlocal createdMessageObject = self.MessageCreators[messageType](messageData, currentChannelName)\n\t\tif createdMessageObject then\n\t\t\treturn self:WrapIntoMessageObject(messageData, createdMessageObject)\n\t\tend\n\telseif self.DefaultCreatorType then\n\t\tlocal createdMessageObject = self.MessageCreators[self.DefaultCreatorType](messageData, currentChannelName)\n\t\tif createdMessageObject then\n\t\t\treturn self:WrapIntoMessageObject(messageData, createdMessageObject)\n\t\tend\n\telse\n\t\terror(\"No message creator available for message type: \" ..messageType)\n\tend\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\n\tobj.ObjectPool = moduleObjectPool.new(OBJECT_POOL_SIZE)\n\tobj.MessageCreators = GetMessageCreators()\n\tobj.DefaultCreatorType = messageCreatorUtil.DEFAULT_MESSAGE_CREATOR\n\n\tmessageCreatorUtil:RegisterObjectPool(obj.ObjectPool)\n\n\treturn obj\nend\n\nfunction module:GetStringTextBounds(text, font, textSize, sizeBounds)\n\treturn messageCreatorUtil:GetStringTextBounds(text, font, textSize, sizeBounds)\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/ChatScript/ChatMain/MessageLogDisplay.lua",
    "content": "--\t// FileName: MessageLogDisplay.lua\n--\t// Written by: Xsitsu, TheGamer101\n--\t// Description: ChatChannel window for displaying messages.\n\nlocal module = {}\nmodule.ScrollBarThickness = 4\n\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal Chat = game:GetService(\"Chat\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal modulesFolder = script.Parent\nlocal moduleMessageLabelCreator = require(modulesFolder:WaitForChild(\"MessageLabelCreator\"))\nlocal CurveUtil = require(modulesFolder:WaitForChild(\"CurveUtil\"))\n\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\n\nlocal FlagFixChatMessageLogPerformance = false do\n\tlocal ok, value = pcall(function()\n\t\treturn UserSettings():IsUserFeatureEnabled(\"UserFixChatMessageLogPerformance\")\n\tend)\n\tif ok then\n\t\tFlagFixChatMessageLogPerformance = value\n\tend\nend\n\nlocal MessageLabelCreator = moduleMessageLabelCreator.new()\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nlocal function CreateGuiObjects()\n\tlocal BaseFrame = Instance.new(\"Frame\")\n\tBaseFrame.Selectable = false\n\tBaseFrame.Size = UDim2.new(1, 0, 1, 0)\n\tBaseFrame.BackgroundTransparency = 1\n\n\tlocal Scroller = Instance.new(\"ScrollingFrame\")\n\tScroller.Selectable = ChatSettings.GamepadNavigationEnabled\n\tScroller.Name = \"Scroller\"\n\tScroller.BackgroundTransparency = 1\n\tScroller.BorderSizePixel = 0\n\tScroller.Position = UDim2.new(0, 0, 0, 3)\n\tScroller.Size = UDim2.new(1, -4, 1, -6)\n\tScroller.CanvasSize = UDim2.new(0, 0, 0, 0)\n\tScroller.ScrollBarThickness = module.ScrollBarThickness\n\tScroller.Active = false\n\tScroller.Parent = BaseFrame\n\n\tlocal Layout\n\tif FlagFixChatMessageLogPerformance then\n\t\tLayout = Instance.new(\"UIListLayout\")\n\t\tLayout.SortOrder = Enum.SortOrder.LayoutOrder\n\t\tLayout.Parent = Scroller\n\tend\n\n\treturn BaseFrame, Scroller, Layout\nend\n\nfunction methods:Destroy()\n\tself.GuiObject:Destroy()\n\tself.Destroyed = true\nend\n\nfunction methods:SetActive(active)\n\tself.GuiObject.Visible = active\nend\n\nfunction methods:UpdateMessageFiltered(messageData)\n\tlocal messageObject = nil\n\tlocal searchIndex = 1\n\tlocal searchTable = self.MessageObjectLog\n\n\twhile (#searchTable >= searchIndex) do\n\t\tlocal obj = searchTable[searchIndex]\n\n\t\tif obj.ID == messageData.ID then\n\t\t\tmessageObject = obj\n\t\t\tbreak\n\t\tend\n\n\t\tsearchIndex = searchIndex + 1\n\tend\n\n\tif messageObject then\n\t\tlocal isScrolledDown = self:IsScrolledDown()\n\t\tmessageObject.UpdateTextFunction(messageData)\n\t\tif FlagFixChatMessageLogPerformance then\n\t\t\tself:PositionMessageLabelInWindow(messageObject, isScrolledDown)\n\t\telse\n\t\t\tself:ReorderAllMessages()\n\t\tend\n\tend\nend\n\nfunction methods:AddMessage(messageData)\n\tself:WaitUntilParentedCorrectly()\n\n\tlocal messageObject = MessageLabelCreator:CreateMessageLabel(messageData, self.CurrentChannelName)\n\tif messageObject == nil then\n\t\treturn\n\tend\n\n\ttable.insert(self.MessageObjectLog, messageObject)\n\tself:PositionMessageLabelInWindow(messageObject)\nend\n\nfunction methods:AddMessageAtIndex(messageData, index)\n\tlocal messageObject = MessageLabelCreator:CreateMessageLabel(messageData, self.CurrentChannelName)\n\tif messageObject == nil then\n\t\treturn\n\tend\n\n\ttable.insert(self.MessageObjectLog, index, messageObject)\n\n\tlocal wasScrolledToBottom = self:IsScrolledDown()\n\tself:ReorderAllMessages()\n\tif wasScrolledToBottom then\n\t\tself.Scroller.CanvasPosition = Vector2.new(0, math.max(0, self.Scroller.CanvasSize.Y.Offset - self.Scroller.AbsoluteSize.Y))\n\tend\nend\n\nfunction methods:RemoveLastMessage()\n\tself:WaitUntilParentedCorrectly()\n\n\tlocal lastMessage = self.MessageObjectLog[1]\n\t-- remove with FlagFixChatMessageLogPerformance\n\tlocal posOffset = UDim2.new(0, 0, 0, lastMessage.BaseFrame.AbsoluteSize.Y)\n\n\tlastMessage:Destroy()\n\ttable.remove(self.MessageObjectLog, 1)\n\n\tif not FlagFixChatMessageLogPerformance then\n\t\tfor i, messageObject in pairs(self.MessageObjectLog) do\n\t\t\tmessageObject.BaseFrame.Position = messageObject.BaseFrame.Position - posOffset\n\t\tend\n\n\t\tself.Scroller.CanvasSize = self.Scroller.CanvasSize - posOffset\n\tend\nend\n\nfunction methods:IsScrolledDown()\n\tlocal yCanvasSize = self.Scroller.CanvasSize.Y.Offset\n\tlocal yContainerSize = self.Scroller.AbsoluteWindowSize.Y\n\tlocal yScrolledPosition = self.Scroller.CanvasPosition.Y\n\n\tif FlagFixChatMessageLogPerformance then\n\t\treturn\n\t\t\tyCanvasSize < yContainerSize or\n\t\t\tyCanvasSize + yScrolledPosition >= yContainerSize - 5\n\telse\n\t\treturn (yCanvasSize < yContainerSize or\n\t\t\tyCanvasSize - yScrolledPosition <= yContainerSize + 5)\n\tend\nend\n\nfunction methods:PositionMessageLabelInWindow(messageObject, isScrolledDown)\n\tself:WaitUntilParentedCorrectly()\n\n\tlocal baseFrame = messageObject.BaseFrame\n\n\tif FlagFixChatMessageLogPerformance then\n\t\tif isScrolledDown == nil then\n\t\t\tisScrolledDown = self:IsScrolledDown()\n\t\tend\n\t\tbaseFrame.LayoutOrder = messageObject.ID\n\telse\n\t\tbaseFrame.Parent = self.Scroller\n\t\tbaseFrame.Position = UDim2.new(0, 0, 0, self.Scroller.CanvasSize.Y.Offset)\n\tend\n\n\tbaseFrame.Size = UDim2.new(1, 0, 0, messageObject.GetHeightFunction(self.Scroller.AbsoluteSize.X))\n\tif FlagFixChatMessageLogPerformance then\n\t\tbaseFrame.Parent = self.Scroller\n\tend\n\n\tif messageObject.BaseMessage then\n\t\tif FlagFixChatMessageLogPerformance then\n\t\t\tfor i = 1, 10 do\n\t\t\t\tif messageObject.BaseMessage.TextFits then\n\t\t\t\t\tbreak\n\t\t\t\tend\n\n\t\t\t\tlocal trySize = self.Scroller.AbsoluteSize.X - i\n\t\t\t\tbaseFrame.Size = UDim2.new(1, 0, 0, messageObject.GetHeightFunction(trySize))\n\t\t\tend\n\t\telse\n\t\t\tlocal trySize = self.Scroller.AbsoluteSize.X\n\t\t\tlocal minTrySize = math.min(self.Scroller.AbsoluteSize.X - 10, 0)\n\t\t\twhile not messageObject.BaseMessage.TextFits do\n\t\t\t\ttrySize = trySize - 1\n\t\t\t\tif trySize < minTrySize then\n\t\t\t\t\tbreak\n\t\t\t\tend\n\t\t\t\tbaseFrame.Size = UDim2.new(1, 0, 0, messageObject.GetHeightFunction(trySize))\n\t\t\tend\n\t\tend\n\tend\n\n\tif FlagFixChatMessageLogPerformance then\n\t\tif isScrolledDown then\n\t\t\tlocal scrollValue = self.Scroller.CanvasSize.Y.Offset - self.Scroller.AbsoluteSize.Y\n\t\t\tself.Scroller.CanvasPosition = Vector2.new(0, math.max(0, scrollValue))\n\t\tend\n\telse\n\t\tisScrolledDown = self:IsScrolledDown()\n\n\t\tlocal add = UDim2.new(0, 0, 0, baseFrame.Size.Y.Offset)\n\t\tself.Scroller.CanvasSize = self.Scroller.CanvasSize + add\n\n\t\tif isScrolledDown then\n\t\t\tself.Scroller.CanvasPosition = Vector2.new(0, math.max(0, self.Scroller.CanvasSize.Y.Offset - self.Scroller.AbsoluteSize.Y))\n\t\tend\n\tend\nend\n\nfunction methods:ReorderAllMessages()\n\tself:WaitUntilParentedCorrectly()\n\n\t--// Reordering / reparenting with a size less than 1 causes weird glitches to happen with scrolling as repositioning happens.\n\tif self.GuiObject.AbsoluteSize.Y < 1 then return end\n\n\tlocal oldCanvasPositon = self.Scroller.CanvasPosition\n\tlocal wasScrolledDown = self:IsScrolledDown()\n\n\tself.Scroller.CanvasSize = UDim2.new(0, 0, 0, 0)\n\tfor i, messageObject in pairs(self.MessageObjectLog) do\n\t\tself:PositionMessageLabelInWindow(messageObject)\n\tend\n\n\tif not wasScrolledDown then\n\t\tself.Scroller.CanvasPosition = oldCanvasPositon\n\tend\nend\n\nfunction methods:Clear()\n\tfor i, v in pairs(self.MessageObjectLog) do\n\t\tv:Destroy()\n\tend\n\tself.MessageObjectLog = {}\n\n\tif not FlagFixChatMessageLogPerformance then\n\t\tself.Scroller.CanvasSize = UDim2.new(0, 0, 0, 0)\n\tend\nend\n\nfunction methods:SetCurrentChannelName(name)\n\tself.CurrentChannelName = name\nend\n\nfunction methods:FadeOutBackground(duration)\n\t--// Do nothing\nend\n\nfunction methods:FadeInBackground(duration)\n\t--// Do nothing\nend\n\nfunction methods:FadeOutText(duration)\n\tfor i = 1, #self.MessageObjectLog do\n\t\tif self.MessageObjectLog[i].FadeOutFunction then\n\t\t\tself.MessageObjectLog[i].FadeOutFunction(duration, CurveUtil)\n\t\tend\n\tend\nend\n\nfunction methods:FadeInText(duration)\n\tfor i = 1, #self.MessageObjectLog do\n\t\tif self.MessageObjectLog[i].FadeInFunction then\n\t\t\tself.MessageObjectLog[i].FadeInFunction(duration, CurveUtil)\n\t\tend\n\tend\nend\n\nfunction methods:Update(dtScale)\n\tfor i = 1, #self.MessageObjectLog do\n\t\tif self.MessageObjectLog[i].UpdateAnimFunction then\n\t\t\tself.MessageObjectLog[i].UpdateAnimFunction(dtScale, CurveUtil)\n\t\tend\n\tend\nend\n\n--// ToDo: Move to common modules\nfunction methods:WaitUntilParentedCorrectly()\n\twhile (not self.GuiObject:IsDescendantOf(game:GetService(\"Players\").LocalPlayer)) do\n\t\tself.GuiObject.AncestryChanged:wait()\n\tend\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\tobj.Destroyed = false\n\n\tlocal BaseFrame, Scroller, Layout = CreateGuiObjects()\n\tobj.GuiObject = BaseFrame\n\tobj.Scroller = Scroller\n\tobj.Layout = Layout\n\n\tobj.MessageObjectLog = {}\n\n\tobj.Name = \"MessageLogDisplay\"\n\tobj.GuiObject.Name = \"Frame_\" .. obj.Name\n\n\tobj.CurrentChannelName = \"\"\n\n\tobj.GuiObject:GetPropertyChangedSignal(\"AbsoluteSize\"):Connect(function()\n\t\tspawn(function() obj:ReorderAllMessages() end)\n\tend)\n\n\tif FlagFixChatMessageLogPerformance then\n\t\tlocal wasScrolledDown = true\n\n\t\tobj.Layout:GetPropertyChangedSignal(\"AbsoluteContentSize\"):Connect(function()\n\t\t\tlocal size = obj.Layout.AbsoluteContentSize\n\t\t\tobj.Scroller.CanvasSize = UDim2.new(0, 0, 0, size.Y)\n\t\t\tif wasScrolledDown then\n\t\t\t\tlocal windowSize = obj.Scroller.AbsoluteWindowSize\n\t\t\t\tobj.Scroller.CanvasPosition = Vector2.new(0, size.Y - windowSize.Y)\n\t\t\tend\n\t\tend)\n\n\t\tobj.Scroller:GetPropertyChangedSignal(\"CanvasPosition\"):Connect(function()\n\t\t\twasScrolledDown = obj:IsScrolledDown()\n\t\tend)\n\tend\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/ChatScript/ChatMain/MessageSender.lua",
    "content": "--\t// FileName: MessageSender.lua\n--\t// Written by: Xsitsu\n--\t// Description: Module to centralize sending message functionality.\n\nlocal module = {}\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal modulesFolder = script.Parent\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:SendMessage(message, toChannel)\n\tself.SayMessageRequest:FireServer(message, toChannel)\nend\n\nfunction methods:RegisterSayMessageFunction(func)\n\tself.SayMessageRequest = func\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new()\n\tlocal obj = setmetatable({}, methods)\n\tobj.SayMessageRequest = nil\n\n\treturn obj\nend\n\nreturn module.new()\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/ChatScript/ChatMain/ObjectPool.lua",
    "content": "--\t// FileName: ObjectPool.lua\n--\t// Written by: TheGamer101\n--\t// Description: An object pool class used to avoid unnecessarily instantiating Instances.\n\nlocal module = {}\n--////////////////////////////// Include\n--//////////////////////////////////////\nlocal modulesFolder = script.Parent\n\n--////////////////////////////// Methods\n--//////////////////////////////////////\nlocal methods = {}\nmethods.__index = methods\n\nfunction methods:GetInstance(className)\n  if self.InstancePoolsByClass[className] == nil then\n    self.InstancePoolsByClass[className] = {}\n  end\n  local availableInstances = #self.InstancePoolsByClass[className]\n  if availableInstances > 0 then\n    local instance = self.InstancePoolsByClass[className][availableInstances]\n    table.remove(self.InstancePoolsByClass[className])\n    return instance\n  end\n  return Instance.new(className)\nend\n\nfunction methods:ReturnInstance(instance)\n  if self.InstancePoolsByClass[instance.ClassName] == nil then\n    self.InstancePoolsByClass[instance.ClassName] = {}\n  end\n  if #self.InstancePoolsByClass[instance.ClassName] < self.PoolSizePerType then\n    table.insert(self.InstancePoolsByClass[instance.ClassName], instance)\n  else\n    instance:Destroy()\n  end\nend\n\n--///////////////////////// Constructors\n--//////////////////////////////////////\n\nfunction module.new(poolSizePerType)\n\tlocal obj = setmetatable({}, methods)\n\tobj.InstancePoolsByClass = {}\n\tobj.Name = \"ObjectPool\"\n  obj.PoolSizePerType = poolSizePerType\n\n\treturn obj\nend\n\nreturn module\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/ChatScript/ChatMain/init.lua",
    "content": "--\t// FileName: ChatMain.lua\n--\t// Written by: Xsitsu\n--\t// Description: Main module to handle initializing chat window UI and hooking up events to individual UI pieces.\n\nlocal moduleApiTable = {}\n\n--// This section of code waits until all of the necessary RemoteEvents are found in EventFolder.\n--// I have to do some weird stuff since people could potentially already have pre-existing\n--// things in a folder with the same name, and they may have different class types.\n--// I do the useEvents thing and set EventFolder to useEvents so I can have a pseudo folder that\n--// the rest of the code can interface with and have the guarantee that the RemoteEvents they want\n--// exist with their desired names.\n\nlocal FILTER_MESSAGE_TIMEOUT = 60\n\nlocal RunService = game:GetService(\"RunService\")\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal Chat = game:GetService(\"Chat\")\nlocal StarterGui = game:GetService(\"StarterGui\")\n\nlocal DefaultChatSystemChatEvents = ReplicatedStorage:WaitForChild(\"DefaultChatSystemChatEvents\")\nlocal EventFolder = ReplicatedStorage:WaitForChild(\"DefaultChatSystemChatEvents\")\nlocal clientChatModules = Chat:WaitForChild(\"ClientChatModules\")\nlocal ChatConstants = require(clientChatModules:WaitForChild(\"ChatConstants\"))\nlocal ChatSettings = require(clientChatModules:WaitForChild(\"ChatSettings\"))\nlocal messageCreatorModules = clientChatModules:WaitForChild(\"MessageCreatorModules\")\nlocal MessageCreatorUtil = require(messageCreatorModules:WaitForChild(\"Util\"))\n\nlocal ChatLocalization = nil\npcall(function() ChatLocalization = require(game:GetService(\"Chat\").ClientChatModules.ChatLocalization) end)\nif ChatLocalization == nil then ChatLocalization = {} function ChatLocalization:Get(key,default) return default end end\n\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal configuration = modules.load(\"configuration\")\n\nlocal numChildrenRemaining = 10 -- #waitChildren returns 0 because it's a dictionary\nlocal waitChildren =\n{\n\tOnNewMessage = \"RemoteEvent\",\n\tOnMessageDoneFiltering = \"RemoteEvent\",\n\tOnNewSystemMessage = \"RemoteEvent\",\n\tOnChannelJoined = \"RemoteEvent\",\n\tOnChannelLeft = \"RemoteEvent\",\n\tOnMuted = \"RemoteEvent\",\n\tOnUnmuted = \"RemoteEvent\",\n\tOnMainChannelSet = \"RemoteEvent\",\n\n\tSayMessageRequest = \"RemoteEvent\",\n\tGetInitDataRequest = \"RemoteFunction\",\n}\n-- waitChildren/EventFolder does not contain all the remote events, because the server version could be older than the client version.\n-- In that case it would not create the new events.\n-- These events are accessed directly from DefaultChatSystemChatEvents\n\nlocal useEvents = {}\n\nlocal FoundAllEventsEvent = Instance.new(\"BindableEvent\")\n\nfunction TryRemoveChildWithVerifyingIsCorrectType(child)\n\tif (waitChildren[child.Name] and child:IsA(waitChildren[child.Name])) then\n\t\twaitChildren[child.Name] = nil\n\t\tuseEvents[child.Name] = child\n\t\tnumChildrenRemaining = numChildrenRemaining - 1\n\tend\nend\n\nfor i, child in pairs(EventFolder:GetChildren()) do\n\tTryRemoveChildWithVerifyingIsCorrectType(child)\nend\n\nif (numChildrenRemaining > 0) then\n\tlocal con = EventFolder.ChildAdded:connect(function(child)\n\t\tTryRemoveChildWithVerifyingIsCorrectType(child)\n\t\tif (numChildrenRemaining < 1) then\n\t\t\tFoundAllEventsEvent:Fire()\n\t\tend\n\tend)\n\n\tFoundAllEventsEvent.Event:wait()\n\tcon:disconnect()\n\n\tFoundAllEventsEvent:Destroy()\nend\n\nEventFolder = useEvents\n\n\n\n--// Rest of code after waiting for correct events.\n\nlocal UserInputService = game:GetService(\"UserInputService\")\nlocal RunService = game:GetService(\"RunService\")\n\nlocal Players = game:GetService(\"Players\")\nlocal LocalPlayer = Players.LocalPlayer\n\nwhile not LocalPlayer do\n\tPlayers.ChildAdded:wait()\n\tLocalPlayer = Players.LocalPlayer\nend\n\nlocal canChat = true\n\nlocal ChatDisplayOrder = 6\nif ChatSettings.ScreenGuiDisplayOrder ~= nil then\n\tChatDisplayOrder = ChatSettings.ScreenGuiDisplayOrder\nend\n\nlocal PlayerGui = LocalPlayer:WaitForChild(\"PlayerGui\")\nlocal GuiParent = Instance.new(\"ScreenGui\")\nGuiParent.Name = \"Chat\"\nGuiParent.ResetOnSpawn = false\nGuiParent.DisplayOrder = ChatDisplayOrder\nGuiParent.Parent = PlayerGui\n\nlocal DidFirstChannelsLoads = false\n\nlocal modulesFolder = script\n\nlocal moduleChatWindow = require(modulesFolder:WaitForChild(\"ChatWindow\"))\nlocal moduleChatBar = require(modulesFolder:WaitForChild(\"ChatBar\"))\nlocal moduleChannelsBar = require(modulesFolder:WaitForChild(\"ChannelsBar\"))\nlocal moduleMessageLabelCreator = require(modulesFolder:WaitForChild(\"MessageLabelCreator\"))\nlocal moduleMessageLogDisplay = require(modulesFolder:WaitForChild(\"MessageLogDisplay\"))\nlocal moduleChatChannel = require(modulesFolder:WaitForChild(\"ChatChannel\"))\nlocal moduleCommandProcessor = require(modulesFolder:WaitForChild(\"CommandProcessor\"))\n\nlocal ChatWindow = moduleChatWindow.new()\nlocal ChannelsBar = moduleChannelsBar.new()\nlocal MessageLogDisplay = moduleMessageLogDisplay.new()\nlocal CommandProcessor = moduleCommandProcessor.new()\nlocal ChatBar = moduleChatBar.new(CommandProcessor, ChatWindow)\n\nChatWindow:CreateGuiObjects(GuiParent)\n\nChatWindow:RegisterChatBar(ChatBar)\nChatWindow:RegisterChannelsBar(ChannelsBar)\nChatWindow:RegisterMessageLogDisplay(MessageLogDisplay)\n\nMessageCreatorUtil:RegisterChatWindow(ChatWindow)\n\nlocal MessageSender = require(modulesFolder:WaitForChild(\"MessageSender\"))\nMessageSender:RegisterSayMessageFunction(EventFolder.SayMessageRequest)\n\n\n\nif (UserInputService.TouchEnabled) then\n\tChatBar:SetTextLabelText(ChatLocalization:Get(\"GameChat_ChatMain_ChatBarText\",'Tap here to chat'))\nelse\n\tChatBar:SetTextLabelText(ChatLocalization:Get(\"GameChat_ChatMain_ChatBarTextTouch\",'To chat click here or press \"/\" key'))\nend\n\nspawn(function()\n\tlocal CurveUtil = require(modulesFolder:WaitForChild(\"CurveUtil\"))\n\tlocal animationFps = ChatSettings.ChatAnimationFPS or 20.0\n\n\tlocal updateWaitTime = 1.0 / animationFps\n\tlocal lastTick = tick()\n\twhile true do\n\t\tlocal currentTick = tick()\n\t\tlocal tickDelta = currentTick - lastTick\n\t\tlocal dtScale = CurveUtil:DeltaTimeToTimescale(tickDelta)\n\n\t\tif dtScale ~= 0 then\n\t\t\tChatWindow:Update(dtScale)\n\t\tend\n\n\t\tlastTick = currentTick\n\t\twait(updateWaitTime)\n\tend\nend)\n\n\n\n\n--////////////////////////////////////////////////////////////////////////////////////////////\n--////////////////////////////////////////////////////////////// Code to do chat window fading\n--////////////////////////////////////////////////////////////////////////////////////////////\nfunction CheckIfPointIsInSquare(checkPos, topLeft, bottomRight)\n\treturn (topLeft.X <= checkPos.X and checkPos.X <= bottomRight.X and\n\t\ttopLeft.Y <= checkPos.Y and checkPos.Y <= bottomRight.Y)\nend\n\nlocal backgroundIsFaded = false\nlocal textIsFaded = false\nlocal lastTextFadeTime = 0\nlocal lastBackgroundFadeTime = 0\n\nlocal fadedChanged = Instance.new(\"BindableEvent\")\nlocal mouseStateChanged = Instance.new(\"BindableEvent\")\nlocal chatBarFocusChanged = Instance.new(\"BindableEvent\")\n\nfunction DoBackgroundFadeIn(setFadingTime)\n\tlastBackgroundFadeTime = tick()\n\tbackgroundIsFaded = false\n\tfadedChanged:Fire()\n\tChatWindow:FadeInBackground((setFadingTime or ChatSettings.ChatDefaultFadeDuration))\n\n\tlocal currentChannelObject = ChatWindow:GetCurrentChannel()\n\tif (currentChannelObject) then\n\n\t\tlocal Scroller = MessageLogDisplay.Scroller\n\t\tScroller.ScrollingEnabled = true\n\t\tScroller.ScrollBarThickness = moduleMessageLogDisplay.ScrollBarThickness\n\tend\nend\n\nfunction DoBackgroundFadeOut(setFadingTime)\n\tlastBackgroundFadeTime = tick()\n\tbackgroundIsFaded = true\n\tfadedChanged:Fire()\n\tChatWindow:FadeOutBackground((setFadingTime or ChatSettings.ChatDefaultFadeDuration))\n\n\tlocal currentChannelObject = ChatWindow:GetCurrentChannel()\n\tif (currentChannelObject) then\n\n\t\tlocal Scroller = MessageLogDisplay.Scroller\n\t\tScroller.ScrollingEnabled = false\n\t\tScroller.ScrollBarThickness = 0\n\tend\nend\n\nfunction DoTextFadeIn(setFadingTime)\n\tlastTextFadeTime = tick()\n\ttextIsFaded = false\n\tfadedChanged:Fire()\n\tChatWindow:FadeInText((setFadingTime or ChatSettings.ChatDefaultFadeDuration) * 0)\nend\n\nfunction DoTextFadeOut(setFadingTime)\n\tlastTextFadeTime = tick()\n\ttextIsFaded = true\n\tfadedChanged:Fire()\n\tChatWindow:FadeOutText((setFadingTime or ChatSettings.ChatDefaultFadeDuration))\nend\n\nfunction DoFadeInFromNewInformation()\n\tDoTextFadeIn()\n\tif ChatSettings.ChatShouldFadeInFromNewInformation then\n\t\tDoBackgroundFadeIn()\n\tend\nend\n\nfunction InstantFadeIn()\n\tDoBackgroundFadeIn(0)\n\tDoTextFadeIn(0)\nend\n\nfunction InstantFadeOut()\n\tDoBackgroundFadeOut(0)\n\tDoTextFadeOut(0)\nend\n\nlocal mouseIsInWindow = nil\nfunction UpdateFadingForMouseState(mouseState)\n\tmouseIsInWindow = mouseState\n\n\tmouseStateChanged:Fire()\n\n\tif (ChatBar:IsFocused()) then return end\n\n\tif (mouseState) then\n\t\tDoBackgroundFadeIn()\n\t\tDoTextFadeIn()\n\telse\n\t\tDoBackgroundFadeIn()\n\tend\nend\n\n\nspawn(function()\n\twhile true do\n\t\tRunService.RenderStepped:wait()\n\n\t\twhile (mouseIsInWindow or ChatBar:IsFocused()) do\n\t\t\tif (mouseIsInWindow) then\n\t\t\t\tmouseStateChanged.Event:wait()\n\t\t\tend\n\t\t\tif (ChatBar:IsFocused()) then\n\t\t\t\tchatBarFocusChanged.Event:wait()\n\t\t\tend\n\t\tend\n\n\t\tif (not backgroundIsFaded) then\n\t\t\tlocal timeDiff = tick() - lastBackgroundFadeTime\n\t\t\tif (timeDiff > ChatSettings.ChatWindowBackgroundFadeOutTime) then\n\t\t\t\tDoBackgroundFadeOut()\n\t\t\tend\n\n\t\telseif (not textIsFaded) then\n\t\t\tlocal timeDiff = tick() - lastTextFadeTime\n\t\t\tif (timeDiff > ChatSettings.ChatWindowTextFadeOutTime) then\n\t\t\t\tDoTextFadeOut()\n\t\t\tend\n\n\t\telse\n\t\t\tfadedChanged.Event:wait()\n\n\t\tend\n\n\tend\nend)\n\nfunction getClassicChatEnabled()\n\tif ChatSettings.ClassicChatEnabled ~= nil then\n\t\treturn ChatSettings.ClassicChatEnabled\n\tend\n\treturn Players.ClassicChat\nend\n\nfunction getBubbleChatEnabled()\n\tif ChatSettings.BubbleChatEnabled ~= nil then\n\t\treturn ChatSettings.BubbleChatEnabled\n\tend\n\treturn Players.BubbleChat\nend\n\nfunction bubbleChatOnly()\n \treturn not getClassicChatEnabled() and getBubbleChatEnabled()\nend\n\nfunction UpdateMousePosition(mousePos)\n\tif not (moduleApiTable.Visible and moduleApiTable.IsCoreGuiEnabled and (moduleApiTable.TopbarEnabled or ChatSettings.ChatOnWithTopBarOff)) then return end\n\n\tif bubbleChatOnly() then\n\t\treturn\n\tend\n\n\tlocal windowPos = ChatWindow.GuiObject.AbsolutePosition\n\tlocal windowSize = ChatWindow.GuiObject.AbsoluteSize\n\n\tlocal newMouseState = CheckIfPointIsInSquare(mousePos, windowPos, windowPos + windowSize)\n\tif (newMouseState ~= mouseIsInWindow) then\n\t\tUpdateFadingForMouseState(newMouseState)\n\tend\nend\n\nUserInputService.InputChanged:connect(function(inputObject)\n\tif (inputObject.UserInputType == Enum.UserInputType.MouseMovement) then\n\t\tlocal mousePos = Vector2.new(inputObject.Position.X, inputObject.Position.Y)\n\t\tUpdateMousePosition(mousePos)\n\tend\nend)\n\nUserInputService.TouchTap:connect(function(tapPos, gameProcessedEvent)\n\tUpdateMousePosition(tapPos[1])\nend)\n\nUserInputService.TouchMoved:connect(function(inputObject, gameProcessedEvent)\n\tlocal tapPos = Vector2.new(inputObject.Position.X, inputObject.Position.Y)\n\tUpdateMousePosition(tapPos)\nend)\n\n--[[\n\tUserInputService.Changed:connect(function(prop)\n\t\tif prop == \"MouseBehavior\" then\n\t\t\tif UserInputService.MouseBehavior == Enum.MouseBehavior.LockCenter then\n\t\t\t\tlocal windowPos = ChatWindow.GuiObject.AbsolutePosition\n\t\t\t\tlocal windowSize = ChatWindow.GuiObject.AbsoluteSize\n\t\t\t\tlocal screenSize = GuiParent.AbsoluteSize\n\t\n\t\t\t\tlocal centerScreenIsInWindow = CheckIfPointIsInSquare(screenSize/2, windowPos, windowPos + windowSize)\n\t\t\t\tif centerScreenIsInWindow then\n\t\t\t\t\tUserInputService.MouseBehavior = Enum.MouseBehavior.Default\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\n]]\n\n--// Start and stop fading sequences / timers\nUpdateFadingForMouseState(true)\nUpdateFadingForMouseState(false)\n\n\n--////////////////////////////////////////////////////////////////////////////////////////////\n--///////////// Code to talk to topbar and maintain set/get core backwards compatibility stuff\n--////////////////////////////////////////////////////////////////////////////////////////////\nlocal Util = {}\ndo\n\tfunction Util.Signal()\n\t\tlocal sig = {}\n\n\t\tlocal mSignaler = Instance.new('BindableEvent')\n\n\t\tlocal mArgData = nil\n\t\tlocal mArgDataCount = nil\n\n\t\tfunction sig:fire(...)\n\t\t\tmArgData = {...}\n\t\t\tmArgDataCount = select('#', ...)\n\t\t\tmSignaler:Fire()\n\t\tend\n\n\t\tfunction sig:connect(f)\n\t\t\tif not f then error(\"connect(nil)\", 2) end\n\t\t\treturn mSignaler.Event:connect(function()\n\t\t\t\tf(unpack(mArgData, 1, mArgDataCount))\n\t\t\tend)\n\t\tend\n\n\t\tfunction sig:wait()\n\t\t\tmSignaler.Event:wait()\n\t\t\tassert(mArgData, \"Missing arg data, likely due to :TweenSize/Position corrupting threadrefs.\")\n\t\t\treturn unpack(mArgData, 1, mArgDataCount)\n\t\tend\n\n\t\treturn sig\n\tend\nend\n\n\nfunction SetVisibility(val)\n\tChatWindow:SetVisible(val)\n\tmoduleApiTable.VisibilityStateChanged:fire(val)\n\tmoduleApiTable.Visible = val\n\n\tif (moduleApiTable.IsCoreGuiEnabled) then\n\t\tif (val) then\n\t\t\tInstantFadeIn()\n\t\telse\n\t\t\tInstantFadeOut()\n\t\tend\n\tend\nend\n\ndo\n\tmoduleApiTable.TopbarEnabled = true\n\tmoduleApiTable.MessageCount = 0\n\tmoduleApiTable.Visible = true\n\tmoduleApiTable.IsCoreGuiEnabled = true\n\n\tfunction moduleApiTable:ToggleVisibility()\n\t\tSetVisibility(not ChatWindow:GetVisible())\n\tend\n\n\tfunction moduleApiTable:SetVisible(visible)\n\t\tif (ChatWindow:GetVisible() ~= visible) then\n\t\t\tSetVisibility(visible)\n\t\tend\n\tend\n\n\tfunction moduleApiTable:FocusChatBar()\n\t\tChatBar:CaptureFocus()\n\tend\n\n\tfunction moduleApiTable:EnterWhisperState(player)\n\t\tChatBar:EnterWhisperState(player)\n\tend\n\n\tfunction moduleApiTable:GetVisibility()\n\t\treturn ChatWindow:GetVisible()\n\tend\n\n\tfunction moduleApiTable:GetMessageCount()\n\t\treturn self.MessageCount\n\tend\n\n\tfunction moduleApiTable:TopbarEnabledChanged(enabled)\n\t\tself.TopbarEnabled = enabled\n\t\tself.CoreGuiEnabled:fire(game:GetService(\"StarterGui\"):GetCoreGuiEnabled(Enum.CoreGuiType.Chat))\n\tend\n\n\tfunction moduleApiTable:IsFocused(useWasFocused)\n\t\treturn ChatBar:IsFocused()\n\tend\n\n\tmoduleApiTable.ChatBarFocusChanged = Util.Signal()\n\tmoduleApiTable.VisibilityStateChanged = Util.Signal()\n\tmoduleApiTable.MessagesChanged = Util.Signal()\n\n\n\tmoduleApiTable.MessagePosted = Util.Signal()\n\tmoduleApiTable.CoreGuiEnabled = Util.Signal()\n\n\tmoduleApiTable.ChatMakeSystemMessageEvent = Util.Signal()\n\tmoduleApiTable.ChatWindowPositionEvent = Util.Signal()\n\tmoduleApiTable.ChatWindowSizeEvent = Util.Signal()\n\tmoduleApiTable.ChatBarDisabledEvent = Util.Signal()\n\n\n\tfunction moduleApiTable:fChatWindowPosition()\n\t\treturn ChatWindow.GuiObject.Position\n\tend\n\n\tfunction moduleApiTable:fChatWindowSize()\n\t\treturn ChatWindow.GuiObject.Size\n\tend\n\n\tfunction moduleApiTable:fChatBarDisabled()\n\t\treturn not ChatBar:GetEnabled()\n\tend\n\n\n\n\tfunction moduleApiTable:SpecialKeyPressed(key, modifiers)\n\t\tif (key == Enum.SpecialKey.ChatHotkey) then\n\t\t\tif canChat then\n\t\t\t\tDoChatBarFocus()\n\t\t\tend\n\t\tend\n\tend\nend\n\nmoduleApiTable.CoreGuiEnabled:connect(function(enabled)\n\tmoduleApiTable.IsCoreGuiEnabled = enabled\n\n\tenabled = enabled and (moduleApiTable.TopbarEnabled or ChatSettings.ChatOnWithTopBarOff)\n\n\tChatWindow:SetCoreGuiEnabled(enabled)\n\n\tif (not enabled) then\n\t\tChatBar:ReleaseFocus()\n\t\tInstantFadeOut()\n\telse\n\t\tInstantFadeIn()\n\tend\nend)\n\nfunction trimTrailingSpaces(str)\n\tlocal lastSpace = #str\n\twhile lastSpace > 0 do\n\t\t--- The pattern ^%s matches whitespace at the start of the string. (Starting from lastSpace)\n\t\tif str:find(\"^%s\", lastSpace) then\n\t\t\tlastSpace = lastSpace - 1\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\treturn str:sub(1, lastSpace)\nend\n\nmoduleApiTable.ChatMakeSystemMessageEvent:connect(function(valueTable)\n\tif (valueTable[\"Text\"] and type(valueTable[\"Text\"]) == \"string\") then\n\t\twhile (not DidFirstChannelsLoads) do wait() end\n\n\t\tlocal channel = ChatSettings.GeneralChannelName\n\t\tlocal channelObj = ChatWindow:GetChannel(channel)\n\n\t\tif (channelObj) then\n\t\t\tlocal messageObject = {\n\t\t\t\tID = -1,\n\t\t\t\tFromSpeaker = nil,\n\t\t\t\tSpeakerUserId = 0,\n\t\t\t\tOriginalChannel = channel,\n\t\t\t\tIsFiltered = true,\n\t\t\t\tMessageLength = string.len(valueTable.Text),\n\t\t\t\tMessage = trimTrailingSpaces(valueTable.Text),\n\t\t\t\tMessageType = ChatConstants.MessageTypeSetCore,\n\t\t\t\tTime = os.time(),\n\t\t\t\tExtraData = valueTable,\n\t\t\t}\n\t\t\tchannelObj:AddMessageToChannel(messageObject)\n\t\t\tChannelsBar:UpdateMessagePostedInChannel(channel)\n\n\t\t\tmoduleApiTable.MessageCount = moduleApiTable.MessageCount + 1\n\t\t\tmoduleApiTable.MessagesChanged:fire(moduleApiTable.MessageCount)\n\t\tend\n\tend\nend)\n\nmoduleApiTable.ChatBarDisabledEvent:connect(function(disabled)\n\tif canChat then\n\t\tChatBar:SetEnabled(not disabled)\n\t\tif (disabled) then\n\t\t\tChatBar:ReleaseFocus()\n\t\tend\n\tend\nend)\n\nmoduleApiTable.ChatWindowSizeEvent:connect(function(size)\n\tChatWindow.GuiObject.Size = size\nend)\n\nmoduleApiTable.ChatWindowPositionEvent:connect(function(position)\n\tChatWindow.GuiObject.Position = position\nend)\n\n--////////////////////////////////////////////////////////////////////////////////////////////\n--///////////////////////////////////////////////// Code to hook client UI up to server events\n--////////////////////////////////////////////////////////////////////////////////////////////\n\nfunction DoChatBarFocus()\n\tif (not ChatWindow:GetCoreGuiEnabled()) then return end\n\tif (not ChatBar:GetEnabled()) then return end\n\n\tif (not ChatBar:IsFocused() and ChatBar:GetVisible()) then\n\t\tmoduleApiTable:SetVisible(true)\n\t\tInstantFadeIn()\n\t\tChatBar:CaptureFocus()\n\t\tmoduleApiTable.ChatBarFocusChanged:fire(true)\n\tend\nend\n\nchatBarFocusChanged.Event:connect(function(focused)\n\tmoduleApiTable.ChatBarFocusChanged:fire(focused)\nend)\n\nfunction DoSwitchCurrentChannel(targetChannel)\n\tif (ChatWindow:GetChannel(targetChannel)) then\n\t\tChatWindow:SwitchCurrentChannel(targetChannel)\n\tend\nend\n\nfunction SendMessageToSelfInTargetChannel(message, channelName, extraData)\n\tlocal channelObj = ChatWindow:GetChannel(channelName)\n\tif (channelObj) then\n\t\tlocal messageData =\n\t\t{\n\t\t\tID = -1,\n\t\t\tFromSpeaker = nil,\n\t\t\tSpeakerUserId = 0,\n\t\t\tOriginalChannel = channelName,\n\t\t\tIsFiltered = true,\n\t\t\tMessageLength = string.len(message),\n\t\t\tMessage = trimTrailingSpaces(message),\n\t\t\tMessageType = ChatConstants.MessageTypeSystem,\n\t\t\tTime = os.time(),\n\t\t\tExtraData = extraData,\n\t\t}\n\n\t\tchannelObj:AddMessageToChannel(messageData)\n\tend\nend\n\nfunction chatBarFocused()\n\tif (not mouseIsInWindow) then\n\t\tDoBackgroundFadeIn()\n\t\tif (textIsFaded) then\n\t\t\tDoTextFadeIn()\n\t\tend\n\tend\n\n\tchatBarFocusChanged:Fire(true)\nend\n\n--// Event for making player say chat message.\nfunction chatBarFocusLost(enterPressed, inputObject)\n\tDoBackgroundFadeIn()\n\tchatBarFocusChanged:Fire(false)\n\n\tif (enterPressed) then\n\t\tlocal message = ChatBar:GetTextBox().Text\n\n\t\tif ChatBar:IsInCustomState() then\n\t\t\tlocal customMessage = ChatBar:GetCustomMessage()\n\t\t\tif customMessage then\n\t\t\t\tmessage = customMessage\n\t\t\tend\n\t\t\tlocal messageSunk = ChatBar:CustomStateProcessCompletedMessage(message)\n\t\t\tChatBar:ResetCustomState()\n\t\t\tif messageSunk then\n\t\t\t\treturn\n\t\t\tend\n\t\tend\n\n\t\tmessage = string.sub(message, 1, ChatSettings.MaximumMessageLength)\n\n\t\tChatBar:GetTextBox().Text = \"\"\n\n\t\tif message ~= \"\" then\n\t\t\t--// Sends signal to eventually call Player:Chat() to handle C++ side legacy stuff.\n\t\t\tmoduleApiTable.MessagePosted:fire(message)\n\n\t\t\tif not CommandProcessor:ProcessCompletedChatMessage(message, ChatWindow) then\n\t\t\t\tif ChatSettings.DisallowedWhiteSpace then\n\t\t\t\t\tfor i = 1, #ChatSettings.DisallowedWhiteSpace do\n\t\t\t\t\t\tif ChatSettings.DisallowedWhiteSpace[i] == \"\\t\" then\n\t\t\t\t\t\t\tmessage = string.gsub(message, ChatSettings.DisallowedWhiteSpace[i], \" \")\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tmessage = string.gsub(message, ChatSettings.DisallowedWhiteSpace[i], \"\")\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tmessage = string.gsub(message, \"\\n\", \"\")\n\t\t\t\tmessage = string.gsub(message, \"[ ]+\", \" \")\n\n\t\t\t\tlocal targetChannel = ChatWindow:GetTargetMessageChannel()\n\t\t\t\tif targetChannel then\n\t\t\t\t\tMessageSender:SendMessage(message, targetChannel)\n\t\t\t\telse\n\t\t\t\t\tMessageSender:SendMessage(message, nil)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\tend\nend\n\nlocal ChatBarConnections = {}\nfunction setupChatBarConnections()\n\tfor i = 1, #ChatBarConnections do\n\t\tChatBarConnections[i]:Disconnect()\n\tend\n\tChatBarConnections = {}\n\n\tlocal focusLostConnection = ChatBar:GetTextBox().FocusLost:connect(chatBarFocusLost)\n\ttable.insert(ChatBarConnections, focusLostConnection)\n\n\tlocal focusGainedConnection = ChatBar:GetTextBox().Focused:connect(chatBarFocused)\n\ttable.insert(ChatBarConnections, focusGainedConnection)\nend\n\nsetupChatBarConnections()\nChatBar.GuiObjectsChanged:connect(setupChatBarConnections)\n\nfunction getEchoMessagesInGeneral()\n\tif ChatSettings.EchoMessagesInGeneralChannel == nil then\n\t\treturn true\n\tend\n\treturn ChatSettings.EchoMessagesInGeneralChannel\nend\n\nEventFolder.OnMessageDoneFiltering.OnClientEvent:connect(function(messageData)\n\tif not ChatSettings.ShowUserOwnFilteredMessage then\n\t\tif messageData.FromSpeaker == LocalPlayer.Name then\n\t\t\treturn\n\t\tend\n\tend\n\n\tlocal channelName = messageData.OriginalChannel\n\tlocal channelObj = ChatWindow:GetChannel(channelName)\n\tif channelObj then\n\t\tchannelObj:UpdateMessageFiltered(messageData)\n\tend\n\n\tif getEchoMessagesInGeneral() and ChatSettings.GeneralChannelName and channelName ~= ChatSettings.GeneralChannelName then\n\t\tlocal generalChannel = ChatWindow:GetChannel(ChatSettings.GeneralChannelName)\n\t\tif generalChannel then\n\t\t\tgeneralChannel:UpdateMessageFiltered(messageData)\n\t\tend\n\tend\nend)\n\nEventFolder.OnNewMessage.OnClientEvent:connect(function(messageData, channelName)\n\tlocal channelObj = ChatWindow:GetChannel(channelName)\n\tif (channelObj) then\n\t\tchannelObj:AddMessageToChannel(messageData)\n\n\t\tif (messageData.FromSpeaker ~= LocalPlayer.Name) then\n\t\t\tChannelsBar:UpdateMessagePostedInChannel(channelName)\n\t\tend\n\n\t\tif getEchoMessagesInGeneral() and ChatSettings.GeneralChannelName and channelName ~= ChatSettings.GeneralChannelName then\n\t\t\tlocal generalChannel = ChatWindow:GetChannel(ChatSettings.GeneralChannelName)\n\t\t\tif generalChannel then\n\t\t\t\tgeneralChannel:AddMessageToChannel(messageData)\n\t\t\tend\n\t\tend\n\n\t\tmoduleApiTable.MessageCount = moduleApiTable.MessageCount + 1\n\t\tmoduleApiTable.MessagesChanged:fire(moduleApiTable.MessageCount)\n\n\t\tDoFadeInFromNewInformation()\n\tend\nend)\n\nEventFolder.OnNewSystemMessage.OnClientEvent:connect(function(messageData, channelName)\n\tchannelName = channelName or \"System\"\n\n\tlocal channelObj = ChatWindow:GetChannel(channelName)\n\tif (channelObj) then\n\t\tchannelObj:AddMessageToChannel(messageData)\n\n\t\tChannelsBar:UpdateMessagePostedInChannel(channelName)\n\n\t\tmoduleApiTable.MessageCount = moduleApiTable.MessageCount + 1\n\t\tmoduleApiTable.MessagesChanged:fire(moduleApiTable.MessageCount)\n\n\t\tDoFadeInFromNewInformation()\n\n\t\tif getEchoMessagesInGeneral() and ChatSettings.GeneralChannelName and channelName ~= ChatSettings.GeneralChannelName then\n\t\t\tlocal generalChannel = ChatWindow:GetChannel(ChatSettings.GeneralChannelName)\n\t\t\tif generalChannel then\n\t\t\t\tgeneralChannel:AddMessageToChannel(messageData)\n\t\t\tend\n\t\tend\n\tend\nend)\n\n\nfunction HandleChannelJoined(channel, welcomeMessage, messageLog, channelNameColor, addHistoryToGeneralChannel,\n\taddWelcomeMessageToGeneralChannel)\n\tif ChatWindow:GetChannel(channel) then\n\t\t--- If the channel has already been added, remove it first.\n\t\tChatWindow:RemoveChannel(channel)\n\tend\n\n\tif (channel == ChatSettings.GeneralChannelName) then\n\t\tDidFirstChannelsLoads = true\n\tend\n\n\tif channelNameColor then\n\t\tChatBar:SetChannelNameColor(channel, channelNameColor)\n\tend\n\n\tlocal channelObj = ChatWindow:AddChannel(channel)\n\n\tif (channelObj) then\n\t\tif (channel == ChatSettings.GeneralChannelName) then\n\t\t\tDoSwitchCurrentChannel(channel)\n\t\tend\n\n\t\tif (messageLog) then\n\t\t\tlocal startIndex = 1\n\t\t\tif #messageLog > ChatSettings.MessageHistoryLengthPerChannel then\n\t\t\t\tstartIndex = #messageLog - ChatSettings.MessageHistoryLengthPerChannel\n\t\t\tend\n\n\t\t\tfor i = startIndex, #messageLog do\n\t\t\t\tchannelObj:AddMessageToChannel(messageLog[i])\n\t\t\tend\n\n\t\t\tif getEchoMessagesInGeneral() and addHistoryToGeneralChannel then\n\t\t\t\tif ChatSettings.GeneralChannelName and channel ~= ChatSettings.GeneralChannelName then\n\t\t\t\t\tlocal generalChannel = ChatWindow:GetChannel(ChatSettings.GeneralChannelName)\n\t\t\t\t\tif generalChannel then\n\t\t\t\t\t\tgeneralChannel:AddMessagesToChannelByTimeStamp(messageLog, startIndex)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif (welcomeMessage ~= \"\") then\n\t\t\tlocal welcomeMessageObject = {\n\t\t\t\tID = -1,\n\t\t\t\tFromSpeaker = nil,\n\t\t\t\tSpeakerUserId = 0,\n\t\t\t\tOriginalChannel = channel,\n\t\t\t\tIsFiltered = true,\n\t\t\t\tMessageLength = string.len(welcomeMessage),\n\t\t\t\tMessage = trimTrailingSpaces(welcomeMessage),\n\t\t\t\tMessageType = ChatConstants.MessageTypeWelcome,\n\t\t\t\tTime = os.time(),\n\t\t\t\tExtraData = nil,\n\t\t\t}\n\t\t\tchannelObj:AddMessageToChannel(welcomeMessageObject)\n\n\t\t\tif getEchoMessagesInGeneral() and addWelcomeMessageToGeneralChannel and not ChatSettings.ShowChannelsBar then\n\t\t\t\tif channel ~= ChatSettings.GeneralChannelName then\n\t\t\t\t\tlocal generalChannel = ChatWindow:GetChannel(ChatSettings.GeneralChannelName)\n\t\t\t\t\tif generalChannel then\n\t\t\t\t\t\tgeneralChannel:AddMessageToChannel(welcomeMessageObject)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tDoFadeInFromNewInformation()\n\tend\n\nend\n\nEventFolder.OnChannelJoined.OnClientEvent:connect(function(channel, welcomeMessage, messageLog, channelNameColor)\n\tHandleChannelJoined(channel, welcomeMessage, messageLog, channelNameColor, false, true)\nend)\n\nEventFolder.OnChannelLeft.OnClientEvent:connect(function(channel)\n\tChatWindow:RemoveChannel(channel)\n\n\tDoFadeInFromNewInformation()\nend)\n\nEventFolder.OnMuted.OnClientEvent:connect(function(channel)\n\t--// Do something eventually maybe?\n\t--// This used to take away the chat bar in channels the player was muted in.\n\t--// We found out this behavior was inconvenient for doing chat commands though.\nend)\n\nEventFolder.OnUnmuted.OnClientEvent:connect(function(channel)\n\t--// Same as above.\nend)\n\nEventFolder.OnMainChannelSet.OnClientEvent:connect(function(channel)\n\tDoSwitchCurrentChannel(channel)\nend)\n\ncoroutine.wrap(function()\n\t-- ChannelNameColorUpdated may not exist if the client version is older than the server version.\n\tlocal ChannelNameColorUpdated = DefaultChatSystemChatEvents:WaitForChild(\"ChannelNameColorUpdated\", 5)\n\tif ChannelNameColorUpdated then\n\t\tChannelNameColorUpdated.OnClientEvent:connect(function(channelName, channelNameColor)\n\t\t\tChatBar:SetChannelNameColor(channelName, channelNameColor)\n\t\tend)\n\tend\nend)()\n\n\n--- Interaction with SetCore Player events.\n\nlocal PlayerBlockedEvent = nil\nlocal PlayerMutedEvent = nil\nlocal PlayerUnBlockedEvent = nil\nlocal PlayerUnMutedEvent = nil\n\n\n-- This is pcalled because the SetCore methods may not be released yet.\npcall(function()\n\tPlayerBlockedEvent = StarterGui:GetCore(\"PlayerBlockedEvent\")\n\tPlayerMutedEvent = StarterGui:GetCore(\"PlayerMutedEvent\")\n\tPlayerUnBlockedEvent = StarterGui:GetCore(\"PlayerUnblockedEvent\")\n\tPlayerUnMutedEvent = StarterGui:GetCore(\"PlayerUnmutedEvent\")\nend)\n\nfunction SendSystemMessageToSelf(message)\n\tlocal currentChannel = ChatWindow:GetCurrentChannel()\n\n\tif currentChannel then\n\t\tlocal messageData =\n\t\t{\n\t\t\tID = -1,\n\t\t\tFromSpeaker = nil,\n\t\t\tSpeakerUserId = 0,\n\t\t\tOriginalChannel = currentChannel.Name,\n\t\t\tIsFiltered = true,\n\t\t\tMessageLength = string.len(message),\n\t\t\tMessage = trimTrailingSpaces(message),\n\t\t\tMessageType = ChatConstants.MessageTypeSystem,\n\t\t\tTime = os.time(),\n\t\t\tExtraData = nil,\n\t\t}\n\n\t\tcurrentChannel:AddMessageToChannel(messageData)\n\tend\nend\n\nfunction MutePlayer(player)\n\tlocal mutePlayerRequest = DefaultChatSystemChatEvents:FindFirstChild(\"MutePlayerRequest\")\n\tif mutePlayerRequest then\n\t\treturn mutePlayerRequest:InvokeServer(player.Name)\n\tend\n\treturn false\nend\n\nif PlayerBlockedEvent then\n\tPlayerBlockedEvent.Event:connect(function(player)\n\t\tif MutePlayer(player) then\n\t\t\tSendSystemMessageToSelf(\n\t\t\t\tstring.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatMain_SpeakerHasBeenBlocked\",\n\t\t\t\t\t\tstring.format(\"Speaker '%s' has been blocked.\", player.Name)\n\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\",player.Name\n\t\t\t\t)\n\t\t\t)\n\t\tend\n\tend)\nend\n\nif PlayerMutedEvent then\n\tPlayerMutedEvent.Event:connect(function(player)\n\t\tif MutePlayer(player) then\n\t\t\tSendSystemMessageToSelf(\n\t\t\t\tstring.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatMain_SpeakerHasBeenMuted\",\n\t\t\t\t\t\tstring.format(\"Speaker '%s' has been muted.\", player.Name)\n\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\", player.Name\n\t\t\t\t)\n\t\t\t)\n\t\tend\n\tend)\nend\n\nfunction UnmutePlayer(player)\n\tlocal unmutePlayerRequest = DefaultChatSystemChatEvents:FindFirstChild(\"UnMutePlayerRequest\")\n\tif unmutePlayerRequest then\n\t\treturn unmutePlayerRequest:InvokeServer(player.Name)\n\tend\n\treturn false\nend\n\nif PlayerUnBlockedEvent then\n\tPlayerUnBlockedEvent.Event:connect(function(player)\n\t\tif UnmutePlayer(player) then\n\t\t\tSendSystemMessageToSelf(\n\t\t\t\tstring.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatMain_SpeakerHasBeenUnBlocked\",\n\t\t\t\t\t\tstring.format(\"Speaker '%s' has been unblocked.\", player.Name)\n\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\",player.Name\n\t\t\t\t)\n\t\t\t)\n\t\tend\n\tend)\nend\n\nif PlayerUnMutedEvent then\n\tPlayerUnMutedEvent.Event:connect(function(player)\n\t\tif UnmutePlayer(player) then\n\t\t\tSendSystemMessageToSelf(\n\t\t\t\tstring.gsub(\n\t\t\t\t\tChatLocalization:Get(\n\t\t\t\t\t\t\"GameChat_ChatMain_SpeakerHasBeenUnMuted\",\n\t\t\t\t\t\tstring.format(\"Speaker '%s' has been unmuted.\", player.Name)\n\t\t\t\t\t),\n\t\t\t\t\t\"{RBX_NAME}\",player.Name\n\t\t\t\t)\n\t\t\t)\n\t\tend\n\tend)\nend\n\n-- Get a list of blocked users from the corescripts.\n-- Spawned because this method can yeild.\nspawn(function()\n\t-- Pcalled because this method is not released on all platforms yet.\n\tif LocalPlayer.UserId > 0 then\n\t\tpcall(function()\n\t\t\tlocal blockedUserIds = StarterGui:GetCore(\"GetBlockedUserIds\")\n\t\t\tif #blockedUserIds > 0 then\n\t\t\t\tlocal setInitalBlockedUserIds = DefaultChatSystemChatEvents:FindFirstChild(\"SetBlockedUserIdsRequest\")\n\t\t\t\tif setInitalBlockedUserIds then\n\t\t\t\t\tsetInitalBlockedUserIds:FireServer(blockedUserIds)\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\tend\nend)\n\nspawn(function()\n\tlocal success, canLocalUserChat = pcall(function()\n\t\treturn Chat:CanUserChatAsync(LocalPlayer.UserId)\n\tend)\n\tif success then\n\t\tcanChat = RunService:IsStudio() or canLocalUserChat\n\tend\nend)\n\nlocal initData = EventFolder.GetInitDataRequest:InvokeServer()\n\n-- Handle joining general channel first.\nfor i, channelData in pairs(initData.Channels) do\n\tif channelData[1] == ChatSettings.GeneralChannelName then\n\t\tHandleChannelJoined(channelData[1], channelData[2], channelData[3], channelData[4], true, false)\n\tend\nend\n\nfor i, channelData in pairs(initData.Channels) do\n\tif channelData[1] ~= ChatSettings.GeneralChannelName then\n\t\tHandleChannelJoined(channelData[1], channelData[2], channelData[3], channelData[4], true, false)\n\tend\nend\n\nreturn moduleApiTable\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/ChatScript/init.client.lua",
    "content": "--\t// FileName: ChatScript.lua\n--\t// Written by: Xsitsu\n--\t// Description: Hooks main chat module up to Topbar in corescripts.\n\nlocal StarterGui = game:GetService(\"StarterGui\")\nlocal GuiService = game:GetService(\"GuiService\")\nlocal ChatService = game:GetService(\"Chat\")\n\nlocal MAX_COREGUI_CONNECTION_ATTEMPTS = 10\n\nlocal ClientChatModules = ChatService:WaitForChild(\"ClientChatModules\")\nlocal ChatSettings = require(ClientChatModules:WaitForChild(\"ChatSettings\"))\n\nlocal function DoEverything()\n\tlocal Chat = require(script:WaitForChild(\"ChatMain\"))\n\n\tlocal containerTable = {}\n\tcontainerTable.ChatWindow = {}\n\tcontainerTable.SetCore = {}\n\tcontainerTable.GetCore = {}\n\n\tcontainerTable.ChatWindow.ChatTypes = {}\n\tcontainerTable.ChatWindow.ChatTypes.BubbleChatEnabled = ChatSettings.BubbleChatEnabled\n\tcontainerTable.ChatWindow.ChatTypes.ClassicChatEnabled = ChatSettings.ClassicChatEnabled\n\n\t--// Connection functions\n\tlocal function ConnectEvent(name)\n\t\tlocal event = Instance.new(\"BindableEvent\")\n\t\tevent.Name = name\n\t\tcontainerTable.ChatWindow[name] = event\n\n\t\tevent.Event:connect(function(...) Chat[name](Chat, ...) end)\n\tend\n\n\tlocal function ConnectFunction(name)\n\t\tlocal func = Instance.new(\"BindableFunction\")\n\t\tfunc.Name = name\n\t\tcontainerTable.ChatWindow[name] = func\n\n\t\tfunc.OnInvoke = function(...) return Chat[name](Chat, ...) end\n\tend\n\n\tlocal function ReverseConnectEvent(name)\n\t\tlocal event = Instance.new(\"BindableEvent\")\n\t\tevent.Name = name\n\t\tcontainerTable.ChatWindow[name] = event\n\n\t\tChat[name]:connect(function(...) event:Fire(...) end)\n\tend\n\n\tlocal function ConnectSignal(name)\n\t\tlocal event = Instance.new(\"BindableEvent\")\n\t\tevent.Name = name\n\t\tcontainerTable.ChatWindow[name] = event\n\n\t\tevent.Event:connect(function(...) Chat[name]:fire(...) end)\n\tend\n\n\tlocal function ConnectSetCore(name)\n\t\tlocal event = Instance.new(\"BindableEvent\")\n\t\tevent.Name = name\n\t\tcontainerTable.SetCore[name] = event\n\n\t\tevent.Event:connect(function(...) Chat[name..\"Event\"]:fire(...) end)\n\tend\n\n\tlocal function ConnectGetCore(name)\n\t\tlocal func = Instance.new(\"BindableFunction\")\n\t\tfunc.Name = name\n\t\tcontainerTable.GetCore[name] = func\n\n\t\tfunc.OnInvoke = function(...) return Chat[\"f\"..name](...) end\n\tend\n\n\t--// Do connections\n\tConnectEvent(\"ToggleVisibility\")\n\tConnectEvent(\"SetVisible\")\n\tConnectEvent(\"FocusChatBar\")\n\tConnectEvent(\"EnterWhisperState\")\n\tConnectFunction(\"GetVisibility\")\n\tConnectFunction(\"GetMessageCount\")\n\tConnectEvent(\"TopbarEnabledChanged\")\n\tConnectFunction(\"IsFocused\")\n\n\tReverseConnectEvent(\"ChatBarFocusChanged\")\n\tReverseConnectEvent(\"VisibilityStateChanged\")\n\tReverseConnectEvent(\"MessagesChanged\")\n\tReverseConnectEvent(\"MessagePosted\")\n\n\tConnectSignal(\"CoreGuiEnabled\")\n\n\tConnectSetCore(\"ChatMakeSystemMessage\")\n\tConnectSetCore(\"ChatWindowPosition\")\n\tConnectSetCore(\"ChatWindowSize\")\n\tConnectGetCore(\"ChatWindowPosition\")\n\tConnectGetCore(\"ChatWindowSize\")\n\tConnectSetCore(\"ChatBarDisabled\")\n\tConnectGetCore(\"ChatBarDisabled\")\n\n\tConnectEvent(\"SpecialKeyPressed\")\n\n\tSetCoreGuiChatConnections(containerTable)\nend\n\nfunction SetCoreGuiChatConnections(containerTable)\n\tlocal tries = 0\n\twhile tries < MAX_COREGUI_CONNECTION_ATTEMPTS do\n\t\ttries = tries + 1\n\t\tlocal success, ret = pcall(function() StarterGui:SetCore(\"CoreGuiChatConnections\", containerTable) end)\n\t\tif success then\n\t\t\tbreak\n\t\tend\n\t\tif not success and tries == MAX_COREGUI_CONNECTION_ATTEMPTS then\n\t\t\terror(\"Error calling SetCore CoreGuiChatConnections: \" .. ret)\n\t\tend\n\t\twait()\n\tend\nend\n\nfunction checkBothChatTypesDisabled()\n\tif ChatSettings.BubbleChatEnabled ~= nil then\n\t\tif ChatSettings.ClassicChatEnabled ~= nil then\n\t\t\treturn not (ChatSettings.BubbleChatEnabled or ChatSettings.ClassicChatEnabled)\n\t\tend\n\tend\n\treturn false\nend\n\nif (not GuiService:IsTenFootInterface()) and (not game:GetService('UserInputService').VREnabled) then\n\tif not checkBothChatTypesDisabled() then\n\t\tDoEverything()\n\telse\n\t\tlocal containerTable = {}\n\t\tcontainerTable.ChatWindow = {}\n\n\t\tcontainerTable.ChatWindow.ChatTypes = {}\n\t\tcontainerTable.ChatWindow.ChatTypes.BubbleChatEnabled = false\n\t\tcontainerTable.ChatWindow.ChatTypes.ClassicChatEnabled = false\n\t\tSetCoreGuiChatConnections(containerTable)\n\tend\nend\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/PlayerScriptsLoader.client.lua",
    "content": "-- this exists just to block the Roblox-injected script of the same name"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/animations/axeAnimations.lua",
    "content": "return {\n\tstrike1 = {\n\t\tanimationId = \"rbxassetid://5346949146\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/animations/bowAnimations.lua",
    "content": "-- animations on character\n\nreturn {\n\tfiring_bow = {\n--\t\tanimationId_2 \t= \"rbxassetid://2726824751\";\n\t\tanimationId \t= \"rbxassetid://2726792530\";\n\t\tlooped \t\t\t= false;\n--\t\tpriority \t\t= Enum.AnimationPriority.Action;\n\t\tpriority \t\t= Enum.AnimationPriority.Action;\n\t};\n\t\n\tstretching_bow = {\n\t\tanimationId \t= \"rbxassetid://2728300592\";\n--\t\tanimationId_2\t= \"rbxassetid://2766719385\";\n\t\tlooped \t\t\t= false;\n\t\tpriority \t\t= Enum.AnimationPriority.Action;\n--\t\tpriority_2\t\t= Enum.AnimationPriority.Core;\n\t};\n\t\n\tstretching_bow_stance = {\n\t\tanimationId = \"rbxassetid://4719315890\",\n\t\tlooped = false,\n\t\tpriority = Enum.AnimationPriority.Action,\n\t},\n\t\n\tfiring_bow_stance = {\n\t\tanimationId = \"rbxassetid://4719339750\",\n\t\tlooped = false,\n\t\tpriority = Enum.AnimationPriority.Action,\n\t},\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/animations/bowToolAnimations_noChar.lua",
    "content": "return {\n\tfire = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2726670196\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= false;\n\t};\n\t\n\tstretch = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2726680362\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= false;\n\t};\n\t\n\tstretchHold = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2726682464\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/animations/daggerAnimations.lua",
    "content": "return {\n\tstrike1 = {\n\t\tanimationId = \"rbxassetid://2351304598\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n\t\n\tstrike2 = {\n\t\tanimationId = \"rbxassetid://2351309121\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n\n\tstrike3 = {\n\t\tanimationId = \"rbxassetid://3391395031\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/animations/dualAnimations.lua",
    "content": "return {\n\tstrike1 = {\n\t\tanimationId = \"rbxassetid://4089519973\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n\t\n\tstrike2 = {\n\t\tanimationId = \"rbxassetid://4089525456\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n\t\n\tstrike3 = {\n\t\tanimationId = \"rbxassetid://4089535231\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/animations/emoteAnimations.lua",
    "content": "return {\n\tidling_kicking = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2510465751\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= false;\n\t};\n\t\n\tinteraction_greeting = {\n\t\tanimationId = \"rbxassetid://2267540472\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= false;\n\t};\n\t\n\tconsume_consumable = {\n\t\tanimationId = \"rbxassetid://2535303304\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= false;\n\t};\n\t\n\tconsume_loop = {\n\t\tanimationId = \"rbxassetid://3583773947\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n\t\n\tblock = {\n\t\tanimationId = \"rbxassetid://2860393735\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= false;\n\t};\n\t\n\tdance = {\n\t\tanimationId = \"rbxassetid://3518747957\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n\t\n\tbeg = {\n\t\tanimationId = \"rbxassetid://3869526669\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= false;\n\t};\n\t\n\tbeg_hold = {\n\t\tanimationId = \"rbxassetid://3869527720\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n\t\n\tsnap_dance = {\n\t\tanimationId = \"rbxassetid://3453659601\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n\t\n\thappy_dance = {\n\t\tanimationId = \"rbxassetid://3518766746\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n\t\n\tdance2 = {\n\t\tanimationId = \"rbxassetid://3518775810\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n\t\n\tdance3 = {\n\t\tanimationId = \"rbxassetid://3518775005\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n\t\n\t[\"oh yea\"] = {\n\t\tanimationId = \"rbxassetid://3909142929\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n\t\n\thype = {\n\t\tanimationId = \"rbxassetid://3909136684\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n\t\n\tflex = {\n\t\tanimationId = \"rbxassetid://3518780485\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= false;\n\t};\n\t\n\tguitar = {\n\t\tanimationId = \"rbxassetid://3518798639\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= false;\n\t};\n\t\n\tpanic = {\n\t\tanimationId = \"rbxassetid://3909113110\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n\t\n\thandstand = {\n\t\tanimationId = \"rbxassetid://3518781118\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n\t-- jumping jacks\n\tjumps = {\n\t\tanimationId = \"rbxassetid://3518781913\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n\t\n\ttadaa = {\n\t\tanimationId = \"rbxassetid://3518782478\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= false;\n\t};\n\t\n\tcheer = {\n\t\tanimationId = \"rbxassetid://3909256590\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= false;\n\t};\n\t\n\tsit = {\n\t\tanimationId = \"rbxassetid://3559077811\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n\t\n\tpushups = {\n\t\tanimationId = \"rbxassetid://3299614643\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n\t\n\twave = {\n\t\tanimationId = \"rbxassetid://2267540472\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= false;\n\t};\n\t\n\tpoint = {\n\t\tanimationId = \"rbxassetid://3453662693\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= false;\n\t};\n\t\n\t\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/animations/fishing-rodAnimations.lua",
    "content": "return {\n\t[\"cast-line\"] = {\n\t\tanimationId = \"rbxassetid://2532986567\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n\t\n\t[\"reel-line\"] = {\n\t\tanimationId = \"rbxassetid://2532988892\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/animations/greatswordAnimations.lua",
    "content": "return {\n\tstrike1 = {\n\t\tanimationId = \"rbxassetid://4882568220\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n\t\n\tstrike2 = {\n\t\tanimationId = \"rbxassetid://4882574024\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n\t\n\tstrike3 = {\n\t\tanimationId = \"rbxassetid://4882577637\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t}\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/animations/init.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/animations/movementAnimations.lua",
    "content": "return {\n\tjumping = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2035198843\";\n\t\tlooped \t\t= false;\n\t\tspeed \t\t= 1.5;\n\t\tpriority \t= Enum.AnimationPriority.Movement;\n\t};\n\t\n\tgettingUp = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=3197470516\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Movement;\n\t};\n\t\n\tgettingUp1 = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=3197470516\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Movement;\n\t};\n\t\n\tgettingUp2 = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=3197470516\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Movement;\n\t};\n\t\n\tdouble_jumping = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2410331166\";\n\t\tlooped \t\t= false;\n\t\tspeed \t\t= 1.5;\n\t\tpriority \t= Enum.AnimationPriority.Movement;\n\t};\n\t\n\twalking = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2036632924\";\n\t\tlooped \t\t= true;\n\t\tspeed \t\t= 1.25;\n\t};\n\t\n\twalking_greatsword = {\n\t\tanimationId = \"rbxassetid://4875881518\";\n\t\tlooped \t\t= true;\n\t\tspeed \t\t= 1.25;\n\t\tpriority = Enum.AnimationPriority.Action,\n\t};\n\t\n\tsprinting_greatsword = {\n\t\tanimationId_2 = \"http://www.roblox.com/asset/?id=3875293975\";\n\t\tanimationId = \"http://www.roblox.com/asset/?id=4087202408\";\n\t\tlooped \t\t= true;\n\t};\n\t\n\tsprinting_swordAndShield = {\n\t\tanimationId = \"rbxassetid://4272393904\",\n\t\tlooped = true\n\t},\n\t\n\twalking_sword_dual = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=3996219245\";\n\t\tlooped \t\t= true;\n\t};\n\t\n\tswimming = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=3277470794\";\n\t\tlooped \t\t= true;\n\t};\n\t\n\tfalling = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2475304222\";\n\t\tlooped \t\t= true;\n\t};\n\t\n\twalking_exhausted = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2407527752\";\n\t\tlooped \t\t= true;\n\t\tspeed \t\t= 1.25;\n\t};\n\t\n\tsprinting = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2041173462\";\n\t\tlooped \t\t= true;\n\t};\n\t\n\tsprinting_sword_dual = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=3996229378\";\n\t\tlooped \t\t= true;\n\t};\n\t\n\tsprintingWithWeapon = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2036643580\";\n\t\tlooped \t\t= true;\n\t};\n\t\n\tidling = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2066794263\";\n\t\tlooped \t\t= true;\n\t\thasVariants = true;\n\t\tpriority \t= Enum.AnimationPriority.Idle;\n\t};\n\t\n\tidling_exhausted = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2407526978\";\n\t\tanimationId_2 = \"http://www.roblox.com/asset/?id=2066794263\";\n\t\tlooped \t\t= true;\n\t\tpriority \t= Enum.AnimationPriority.Idle;\n\t};\n\t\n\tidling_greatsword = {\n\t\tanimationId = \"rbxassetid://4882493831\";\n\t\tlooped \t\t= true;\n\t\tpriority \t= Enum.AnimationPriority.Idle;\n\t};\n\t\n\tidling_dagger = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2351348658\";\n\t\tlooped \t\t= true;\n\t\tpriority \t= Enum.AnimationPriority.Idle;\n\t};\n\t\n\tidling_bow = {\n\t\tanimationId \t= \"http://www.roblox.com/asset/?id=2715891793\";\n\t\tanimationId_2 \t= \"http://www.roblox.com/asset/?id=2715924622\";\n\t\tlooped \t\t\t= true;\n\t\tpriority \t\t= Enum.AnimationPriority.Idle;\n\t};\n\t\n\tidling_staff = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=3244807491\";\n\t\tlooped \t\t= true;\n\t\tpriority \t= Enum.AnimationPriority.Idle;\n\t};\n\t\n\tidling_sword_dual = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=3996111126\";\n\t\tlooped \t\t= true;\n\t\tpriority \t= Enum.AnimationPriority.Idle;\n\t};\n\t\n\twarrior_idling = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=3244862244\";\n\t\tlooped \t\t= true;\n\t\tpriority \t= Enum.AnimationPriority.Idle;\n\t};\n\t\n\tidling_bow_streched = {\n\t\tanimationId \t= \"http://www.roblox.com/asset/?id=2726808172\";\n\t\tanimationId_2 \t= \"http://www.roblox.com/asset/?id=2726809979\";\n\t\tlooped \t\t\t= true;\n\t\tpriority \t= Enum.AnimationPriority.Idle;\n\t};\n\t\n\twalking_bow_streched = {\n\t\tanimationId \t= \"http://www.roblox.com/asset/?id=2726815878\";\n\t\tanimationId_2 \t= \"http://www.roblox.com/asset/?id=2726809979\";\n\t\tlooped \t\t\t= true;\n\t};\n\t\n\tdead = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2071060180\";\n\t\tlooped \t\t= false;\n\t};\n\t\n\tdead_loop = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2074091647\";\n\t\tlooped \t\t= true;\n\t};\n\t\n\tsitting = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2274649431\";\n\t\tlooped \t\t= true;\n\t};\n\t\n\tfishing = {\n\t\tanimationId = \"http://www.roblox.com/asset/?id=2532987375\";\n\t\tlooped \t\t= true;\n\t};\n\t\n\tconsume_consumable = {\n\t\tanimationId = \"rbxassetid://2535303304\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= false;\n\t};\n\t\n\tconsume_loop = {\n\t\tanimationId = \"rbxassetid://3583773947\";\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t\tlooped \t\t= true;\n\t};\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/animations/pickaxeAnimations.lua",
    "content": "return {\n\tstrike1 = {\n\t\tanimationId = \"rbxassetid://5347066717\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/animations/staffAnimations.lua",
    "content": "return {\n\tstrike1 = {\n\t\tanimationId = \"rbxassetid://2065686536\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n\t\n\tstrike2 = {\n\t\tanimationId = \"rbxassetid://2035777674\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n\t\n\tstrike3 = {\n\t\tanimationId = \"rbxassetid://3391395031\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/animations/swordAndShieldAnimations.lua",
    "content": "return {\n\tstrike1 = {\n\t\tanimationId = \"rbxassetid://2065686536\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n\t\n\tstrike2 = {\n\t\tanimationId = \"rbxassetid://2035777674\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n\t\n\tstrike3 = {\n\t\tanimationId = \"rbxassetid://4272381396\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/animations/swordAnimations.lua",
    "content": "return {\n\tstrike1 = {\n\t\tanimationId = \"rbxassetid://2065686536\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n\t\n\tstrike2 = {\n\t\tanimationId = \"rbxassetid://2035777674\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n\t\n\tstrike3 = {\n\t\tanimationId = \"rbxassetid://3391395031\";\n\t\tlooped \t\t= false;\n\t\tpriority \t= Enum.AnimationPriority.Action;\n\t};\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/attackableScript.lua",
    "content": "local modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\nlocal tween = modules.load(\"tween\")\nlocal effects = modules.load(\"effects\")\nlocal utilities = modules.load(\"utilities\")\n\nlocal model = script.Parent.Parent\nlocal chestRootModel = model.Parent\nlocal originalCFrame = model:GetPrimaryPartCFrame()\n\nlocal attackable = {}\n\nlocal health = 5\nlocal isOpen = false\n\nlocal heartbeatConnection = nil\nlocal activeTween = nil\n\nlocal animControl = script.Parent.Parent:FindFirstChild(\"AnimationController\")\nlocal glow = script.Parent.Parent:FindFirstChild(\"Glow\")\n\nlocal loop = animControl:LoadAnimation(script.Parent.Parent.chestOpenLoop)\nlocal openAnim = animControl:LoadAnimation(script.Parent.Parent.chestOpen)\n\nopenAnim.Looped = false\nopenAnim.Priority = Enum.AnimationPriority.Action\n\nloop.Looped = true\nloop.Priority = Enum.AnimationPriority.Core\n\nlocal function openChest()\n\tlocal rewards, status = network:invoke(\"openTreasureChest_client\", chestRootModel)\n\tif rewards then\n\t\tisOpen = true\n\telseif status then\n\t\tnetwork:fire(\"alert\", {text = status}, 1)\n\tend\nend\n\nlocal function doHealthReduction()\n\thealth = math.max(health - 1, 0)\n\t\n\tif health <= 0 and not isOpen then\n\t\topenChest()\n\tend\nend\n\nlocal function doShake()\n\tlocal primaryPart = model.PrimaryPart\n\tlocal rootCFrame =\toriginalCFrame * CFrame.new(0, -primaryPart.Size.Y/2, 0) * CFrame.Angles(0, math.pi * 2 * math.random(), 0)\n\tlocal offset = rootCFrame:ToObjectSpace(originalCFrame)\n\t\n\tlocal dummyPart = Instance.new(\"Part\")\n\tdummyPart.CFrame = rootCFrame\n\t\n\tlocal easeInTime = 0.2\n\tlocal easeOutTime = 1\n\t\n\tif heartbeatConnection then\n\t\theartbeatConnection:Disconnect()\n\t\theartbeatConnection = nil\n\tend\n\t\n\tif activeTween then activeTween:Pause() activeTween:Destroy() activeTween = nil end\n\t\n\tactiveTween = tween(dummyPart, {\"CFrame\"}, rootCFrame * CFrame.Angles(0, 0, 0.2), easeInTime, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)\n\tactiveTween.Completed:connect(function()\n\t\tactiveTween = tween(dummyPart, {\"CFrame\"}, rootCFrame, easeOutTime, Enum.EasingStyle.Elastic, Enum.EasingDirection.Out)\n\t\tactiveTween.Completed:connect(function()\n\t\t\tactiveTween = nil\n\t\t\tdummyPart:Destroy()\n\t\t\tif heartbeatConnection then\n\t\t\t\theartbeatConnection:Disconnect()\n\t\t\t\theartbeatConnection = nil\n\t\t\tend\n\t\tend)\n\tend)\n\t\n\theartbeatConnection = effects.onHeartbeatFor(easeInTime + easeOutTime, function()\n\t\tmodel:SetPrimaryPartCFrame(dummyPart.CFrame:ToWorldSpace(offset))\n\tend)\nend\n\nfunction attackable.onAttackedClient()\n\tdoShake()\n\tdoHealthReduction()\nend\n\nfunction attackable.onAttackedServer(player)\n\nend\n\nreturn attackable"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/axe.lua",
    "content": "local axe \t\t\t= {}\naxe.isEquipped = false\n\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal httpService \t\t= game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\n\nlocal abilityAnimations = game:GetService(\"ReplicatedStorage\"):WaitForChild(\"assets\"):WaitForChild(\"abilityAnimations\")\n\n\nlocal modules = require(replicatedStorage.modules)\nlocal network \t\t= modules.load(\"network\")\n\nlocal currentDamageGUID = httpService:GenerateGUID(false)\n\n-- todo: a job for someone else: convert these modules\nlocal animationInterface = require(script.Parent.Parent.Parent:WaitForChild(\"contents\"):WaitForChild(\"animationInterface\"))--network:invoke(\"getPlayerCoreService\", \"animationInterface\")\n\n-- internal stuff specific to the sword\nlocal animationsForAnimationController\n\nlocal slashAnimationConnection\n\nlocal isWithinSlash1Window \t\t= false\nlocal isWithinSlash2Window \t\t= false\nlocal isWithinDamageSequence \t= false\n\nlocal currentWeaponManifest\nlocal myClientCharacterContainer\nlocal playerAbilitiesSlotDataCollection\n\nlocal player \t\t\t= game.Players.LocalPlayer\nlocal isPlayerSprinting = false\n\nlocal function onCharacterStateChanged(state, value)\n\tif state == \"isSprinting\" then\n\t\tisPlayerSprinting = value\n\tend\nend\n\nlocal isDamageSequenceEnabled = false\nlocal function startDamageSequencePolling()\n\tif isDamageSequenceEnabled then return end\n\tisDamageSequenceEnabled = true\n\n\twhile isDamageSequenceEnabled do\n\t\tif animationsForAnimationController.axeAnimations.strike1.IsPlaying then\n            if isWithinDamageSequence then\n\t\t\t\tnetwork:invoke(\"performClientDamageCycle\", \"equipment\", nil, currentDamageGUID)\n\t\t\tend\n\n\t\t\twait(1 / 20)\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\n\tisDamageSequenceEnabled = false\nend\n\nlocal function onSlashAnimationTrackStopped()\n\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\n\tif slashAnimationConnection then\n\t\tslashAnimationConnection:disconnect()\n\t\tslashAnimationConnection = nil\n\tend\n\n\tif slashAnimationKeyframeConnection then\n\t\tslashAnimationKeyframeConnection:disconnect()\n\t\tslashAnimationKeyframeConnection = nil\n\tend\n\n\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\tcurrentWeaponManifest.Trail.Enabled = false\n\tend\nend\n\n-- slash1PeriodStart\n-- slash2PeriodStart\n-- startDamageSequence\n-- stopDamageSequence\nlocal function onSlashAnimationKeyframeReached(keyframeName)\n\tif keyframeName == \"slash1PeriodStart\" then\n\t\tisWithinSlash1Window = true\n\t\tdelay(3 / 10, function()\n\t\t\tisWithinSlash1Window = false\n        end)\n\n    elseif keyframeName == \"startDamageSequence\" then\n\n\t\tlocal swingSound = currentWeaponManifest:FindFirstChild(\"Swing\")\n\t\tif swingSound == nil then\n\t\t\tswingSound = Instance.new(\"Sound\")\n\t\t\tswingSound.Volume = 1\n\t\t\tswingSound.MaxDistance = 50\n\t\t\tswingSound.SoundId = \"rbxassetid://2069260907\"\n\t\t\tswingSound.Name = \"Swing\"\n\t\t\tswingSound.Parent = currentWeaponManifest\n\t\tend\n\n\t\tswingSound:Play()\n\t\tisWithinDamageSequence = true\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = true\n        end\n\n\telseif keyframeName == \"stopDamageSequence\" then\n\t\tisWithinDamageSequence = false\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = false\n\t\tend\n\tend\nend\n\nfunction axe:attack()\n\t-- make sure we can't slash if these conditions are true\n\n\tif not animationsForAnimationController or not animationsForAnimationController.axeAnimations then\n\t\treturn\n\telseif isPlayerSprinting then\n\t\treturn\n\telseif not currentWeaponManifest then\n\t\treturn\n\telseif not player.Character or not player.Character.PrimaryPart or player.Character.PrimaryPart.state.Value == \"dead\" then\n\t\treturn\n\telseif animationsForAnimationController.axeAnimations.strike1.IsPlaying and (not isWithinSlash2Window or not canPlayerDoubleSlash)then --or not canPlayerDoubleSlash\n\n\t\treturn\n\tend\n\t-- have to do it this way for now, no reference to ability animations  in animationsForAnimationController\n\tlocal animController = myClientCharacterContainer.entity.AnimationController\n\tfor i, track in pairs(animController:GetPlayingAnimationTracks()) do\n\t\tif track.Name == \"rock_throw_upper\" or track.Name == \"rock_throw_upper_loop\" then\n\t\t\treturn\n\t\tend\n\tend\n\n\tif slashAnimationConnection then\n\t\tslashAnimationConnection:disconnect()\n\t\tslashAnimationConnection = nil\n\tend\n\n\tif slashAnimationKeyframeConnection then\n\t\tslashAnimationKeyframeConnection:disconnect()\n\t\tslashAnimationKeyframeConnection = nil\n\tend\n\n\n\tslashAnimationConnection = animationsForAnimationController.axeAnimations.strike1.Stopped:connect(function()\n\t\tisWithinDamageSequence = false\n\t\tisWithinSlash1Window = false\n\t\tisWithinSlash2Window = false\n\tend)\n\n\tslashAnimationKeyframeConnection = animationsForAnimationController.axeAnimations.strike1.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\tanimationInterface:replicateClientAnimationSequence(\"axeAnimations\", \"strike1\")\n\t-- start damage sequence\n\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\tspawn(startDamageSequencePolling)\nend\n\nfunction axe:equip()\n\tisWithinSlash1Window \t= false\n\tisWithinSlash2Window \t= false\n\tisWithinDamageSequence \t= false\n\tisDamageSequenceEnabled = false\n\t--local\n\tmyClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif myClientCharacterContainer then\n\t\tcurrentWeaponManifest \t\t\t\t= network:invoke(\"getCurrentWeaponManifest\")\n\t\tanimationsForAnimationController \t= animationInterface:getAnimationsForAnimationController(myClientCharacterContainer.entity.AnimationController)\n\n\t--\tlocal grip = myClientCharacterContainer.entity:FindFirstChild(\"Grip\", true)\n\t--\tif grip then\n\t--\t\t-- force an update\n\t--\t\tonGripPropertyChanged(grip, \"Part1\")\n\t--\tend\n\tend\nend\n\nfunction axe:unequip()\n\nend\n\nlocal function onPropogationRequestToSelf(propogationNameTag, propogationValue)\n\tif propogationNameTag == \"abilities\" then\n\t\tplayerAbilitiesSlotDataCollection = propogationValue\n\tend\nend\n\nlocal function main()\n\tonPropogationRequestToSelf(\"abilities\", network:invoke(\"getCacheValueByNameTag\", \"abilities\"))\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\tnetwork:connect(\"characterStateChanged\", \"Event\", onCharacterStateChanged)\nend\n\nmain()\n\nreturn axe"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/bow.lua",
    "content": "local hitDebounceTable \t= {}\nlocal bow \t\t\t\t= {}\n\tbow.isEquipped = false\n\nlocal runService \t\t= game:GetService(\"RunService\")\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t\t\t= modules.load(\"network\")\n\t\tlocal utilities \t\t= modules.load(\"utilities\")\n\t\tlocal client_utilities \t= modules.load(\"client_utilities\")\n\t\tlocal detection \t\t= modules.load(\"detection\")\n\t\tlocal placeSetup \t\t= modules.load(\"placeSetup\")\n\t\tlocal projectile \t\t= modules.load(\"projectile\")\n\t\tlocal configuration \t= modules.load(\"configuration\")\n\t\tlocal damage \t\t\t= modules.load(\"damage\")\n\t\tlocal tween \t\t\t= modules.load(\"tween\")\n\nlocal animationInterface = require(script.Parent.Parent.Parent:WaitForChild(\"contents\"):WaitForChild(\"animationInterface\"))--network:invoke(\"getPlayerCoreService\", \"animationInterface\")\n\nlocal entityRenderCollectionFolder \t\t= placeSetup.awaitPlaceFolder(\"entityRenderCollection\")\nlocal entityManifestCollectionFolder \t= placeSetup.awaitPlaceFolder(\"entityManifestCollection\")\nlocal itemsFolder \t\t\t\t\t\t= placeSetup.awaitPlaceFolder(\"items\")\nlocal entitiesFolder \t\t\t\t\t= placeSetup.awaitPlaceFolder(\"entities\")\n\n-- internal stuff specific to the bow\nlocal isBowCharging = false\nlocal currentArrow\n\nlocal currentWeaponManifest\nlocal bowAnimationsForTool\nlocal animationsForAnimationController\n\nlocal camera \t\t\t= workspace.CurrentCamera\nlocal player \t\t\t= game.Players.LocalPlayer\nlocal isPlayerSprinting = false\n\nlocal function getRayClosestPoint(ray, targetPos)\n\tlocal AP = targetPos - ray.Origin\n\tlocal AB = (ray.Origin + ray.Direction) - ray.Origin\n\n\tlocal dotProduct \t= AP:Dot(AB) / AB:Dot(AB)\n\tdotProduct \t\t\t= math.clamp(dotProduct, 0, 1)\n\n\treturn ray.Origin + dotProduct * AB\nend\n\nlocal function onCharacterStateChanged(state, value)\n\tif state == \"isSprinting\" then\n\t\tisPlayerSprinting = value\n\tend\nend\n\nlocal function onBowToolStretchStopped()\n\tif isBowCharging and bowAnimationsForTool.stretchHold then\n\t\tbowAnimationsForTool.stretchHold:Play()\n\tend\n\n\tif animationsForAnimationController.bowAnimations.stretching_bow.IsPlaying then\n\t\tanimationsForAnimationController.bowAnimations.stretching_bow:Stop()\n\tend\n\nend\n\nlocal bowPullTime\n\nlocal function playerHasArrowToShoot(numArrows)\n\tlocal inventory = network:invoke(\"getCacheValueByNameTag\", \"inventory\") or {}\n\n\tfor i, inventorySlotData in pairs(inventory) do\n\t\tif inventorySlotData.id == 87 and inventorySlotData.stacks >= 1 then\n\t\t\treturn true, math.min(inventorySlotData.stacks, numArrows)\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal BOW_CHARGE_TIME = 0.8\nlocal ARROWS_PER_SECOND = 1 / BOW_CHARGE_TIME\n\nlocal arrowsToFire = 0\n\nfunction bow:attackRangerStance()\n\tlocal hasArrows, numArrows = playerHasArrowToShoot(1)\n\n\tif not hasArrows then\n\t\tnetwork:fire(\"alert\", {text = \"You don't have any arrows!\", id = \"noarrows\"}, 3)\n\t\treturn false\n\tend\n\n\tnetwork:invoke(\"setIsJumpEnabled\", false)\n\tnetwork:invoke(\"setIsSprintingEnabled\", false)\n\tnetwork:invoke(\"setIsChanneling\", true)\n\n\tisBowCharging = true\n\n\tanimationInterface:replicatePlayerAnimationSequence(\"bowAnimations\", \"stretching_bow_stance\", nil, {\n\t\tattackSpeed = 1,\n\t\tnumArrows = numArrows,\n\t\tfiringSeed = math.random(1,2048)\n\t})\n\n\t-- this is intentionally a wait in order to delay\n\t-- the auto-attack loop that calls this function\n\t-- so that spam-clicking can't cheese shots per second\n\twait(0.8)\n\n\tif not isBowCharging then\n\t\treturn\n\telseif not playerHasArrowToShoot(1) then\n\t\tnetwork:invoke(\"setIsJumpEnabled\", true)\n\t\tnetwork:invoke(\"setIsSprintingEnabled\", true)\n\t\tnetwork:invoke(\"setIsChanneling\", false)\n\n\t\tisBowCharging = false\n\n\t\treturn\n\tend\n\n\tlocal abilityExecutionData = network:invoke(\"getAbilityExecutionData\")\n\tabilityExecutionData.bowChargeTime = configuration.getConfigurationValue(\"maxBowChargeTime\")\n\n\tanimationInterface:replicatePlayerAnimationSequence(\"bowAnimations\", \"firing_bow_stance\", nil, abilityExecutionData)\n\n\twait(0.9)\n\n\tnetwork:invoke(\"setIsJumpEnabled\", true)\n\tnetwork:invoke(\"setIsSprintingEnabled\", true)\n\tnetwork:invoke(\"setIsChanneling\", false)\n\n\tisBowCharging = false\nend\n\nlocal timeSinceChargeBow\nfunction bow:attack(inputObject)\n\t-- make sure we can't slash if these conditions are true\n\tif not animationsForAnimationController or not animationsForAnimationController.bowAnimations then\n\t\tprint\"no bow anim\"\n\t\treturn\n\telseif isPlayerSprinting then\n\t\tprint\"is sprint\"\n\t\treturn\n\telseif not bowAnimationsForTool then\n\t\tprint\"no bowtool anim\"\n\t\treturn\n\telseif not currentWeaponManifest then\n\t\tprint(\"no weapon\")\n\t\treturn\n\telseif not player.Character or not player.Character.PrimaryPart or player.Character.PrimaryPart.state.Value == \"dead\" then\n\t\tprint\"is dead\"\n\t\treturn\n\telseif isBowCharging then\n\t\tprint\"already charging\"\n\t\treturn\n--\telseif network:invoke(\"getCharacterMovementStates\").isInAir then\n--\t\treturn\n\tend\n\n\tif inputObject.UserInputState ~= Enum.UserInputState.Begin then print\"not begin\" return end\n\n\tif utilities.doesEntityHaveStatusEffect(player.Character.PrimaryPart, \"ranger stance\") then\n\t\tself:attackRangerStance()\n\n\t\treturn\n\tend\n\n\tlocal stats = network:invoke(\"getCacheValueByNameTag\", \"nonSerializeData\").statistics_final\n\tlocal maxNumArrows = utilities.calculateNumArrowsFromDex(stats.dex)\n\n\tlocal hasArrows, numArrows = playerHasArrowToShoot(maxNumArrows)\n\n\tif not hasArrows then\n\t\tnetwork:fire(\"alert\", {text = \"You don't have any arrows!\", id = \"noarrows\"}, 3)\n\t\treturn false\n\tend\n\n\tnetwork:invoke(\"setIsJumpEnabled\", false)\n\tnetwork:invoke(\"setIsSprintingEnabled\", false)\n\tnetwork:invoke(\"setIsChanneling\", true)\n\n\tisBowCharging = true\n\n\t-- the bow isn't quite fast enough on its own, so secretly boost attack speed behind the scenes\n\tlocal baseAttackSpeedBoost = 0.33\n\tlocal bonus = (stats.attackSpeed / 2) + baseAttackSpeedBoost\n\tlocal attackSpeedScalar = 1 + bonus\n\n\tanimationInterface:replicatePlayerAnimationSequence(\"bowAnimations\", \"stretching_bow\", nil, {\n\t\tattackSpeed = stats.attackSpeed,\n\t\tnumArrows \t= numArrows,\n\t\tfiringSeed \t= math.random(1,2048)\n\t})\n\n\t-- wait for bow to let go of holding\n\tbowPullTime = tick()\n\n\twhile inputObject.UserInputState ~= Enum.UserInputState.End do\n\t\trunService.Stepped:wait()\n\tend\n\n\tself:fireArrow()\nend\n\nfunction bow:release()\n\t-- dummy function to dissuade the system from calling this\n\t-- one click should lead to one arrow at least no matter what\nend\n\nfunction bow:fireArrow()\n\tlocal stats = network:invoke(\"getCacheValueByNameTag\", \"nonSerializeData\").statistics_final\n\tlocal maxNumArrows = utilities.calculateNumArrowsFromDex(stats.dex)\n\n\tif not isBowCharging then\n\t\treturn\n\telseif not playerHasArrowToShoot(maxNumArrows) then\n\t\tnetwork:invoke(\"setIsJumpEnabled\", true)\n\t\tnetwork:invoke(\"setIsSprintingEnabled\", true)\n\t\tnetwork:invoke(\"setIsChanneling\", false)\n\n\t\tisBowCharging = false\n\n\t\treturn\n\tend\n\n\tisBowCharging = false\n\n\tlocal attackSpeedScalar = 1 + stats.attackSpeed\n\n\tlocal abilityExecutionData = network:invoke(\"getAbilityExecutionData\")\n\t\tabilityExecutionData.bowChargeTime = tick() - bowPullTime\n\n\tbowPullTime = nil\n\n\tnetwork:invoke(\"setIsJumpEnabled\", true)\n\tnetwork:invoke(\"setIsSprintingEnabled\", true)\n\tnetwork:invoke(\"setIsChanneling\", false)\n\n\tif abilityExecutionData.bowChargeTime <= configuration.getConfigurationValue(\"minBowChargeTime\") then\n\t\tabilityExecutionData.canceled = true\n\tend\n\n\tlocal currentArrow = network:invoke(\"getPlayerRenderDataByNameTag\", player, \"primaryArrow\")\n\tif not abilityExecutionData.canceled and currentArrow then\n\t\tlocal targets = damage.getDamagableTargets(player)\n\t\tlocal hitPart, hitPos, hitNormal, hitMaterial, hitRay = client_utilities.raycastFromCurrentScreenPoint({entityRenderCollectionFolder; itemsFolder; entitiesFolder})\n\n\t\tlocal ray = Ray.new(currentArrow.Position, (hitPos - currentArrow.Position).unit * 50)\n\n\t\tlocal closestHitbox, closestDist, closestProjection = nil, configuration.getConfigurationValue(\"bowSnapTargetMaxDistance\"), nil\n\t\tfor i, targetHitbox in pairs(targets) do\n\t\t\tlocal pointOnRay = getRayClosestPoint(ray, targetHitbox.Position)\n\t\t\tlocal boxProjection = detection.projection_Box(targetHitbox.CFrame, targetHitbox.Size, pointOnRay)\n\n\t\t\tlocal distanceMissed = (pointOnRay - boxProjection).magnitude\n\n\t\t\tif distanceMissed <= closestDist then\n\t\t\t\tclosestDist \t\t= distanceMissed\n\t\t\t\tclosestHitbox \t\t= targetHitbox\n\t\t\t\tclosestProjection \t= boxProjection\n\t\t\tend\n\t\tend\n\n\t\tif closestHitbox then\n\t\t\t-- target monster\n\t\t\tabilityExecutionData[\"target-position\"] = closestProjection\n\n\t\t\t-- target monster\n\t\t\tabilityExecutionData[\"target-velocity\"] = closestHitbox.Velocity\n\n\t\t\t-- target monster distance away\n\t\t\tif player.Character and player.Character.PrimaryPart then\n\t\t\t\tabilityExecutionData[\"target-distance-away\"] = (closestHitbox.Position - player.Character.PrimaryPart.Position).magnitude\n\t\t\tend\n\t\tend\n\tend\n\n\tanimationInterface:replicatePlayerAnimationSequence(\"bowAnimations\", \"firing_bow\", nil, abilityExecutionData)\nend\n\nfunction bow:equip()\n\tlocal myClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif myClientCharacterContainer then\n\t\tcurrentWeaponManifest \t\t\t\t= network:invoke(\"getCurrentWeaponManifest\")\n\t\tanimationsForAnimationController \t= animationInterface:getAnimationsForAnimationController(myClientCharacterContainer.entity.AnimationController)\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"AnimationController\") then\n\t\t\tbowAnimationsForTool = animationInterface:getAnimationsForAnimationController(currentWeaponManifest.AnimationController, \"bowToolAnimations_noChar\").bowToolAnimations_noChar\n\t\tend\n\tend\nend\n\nfunction bow:unequip()\n\tnetwork:fire(\"replicateClientCharacterWeaponStateChanged\", \"bow\", nil)\nend\n\nlocal function main()\n\tnetwork:connect(\"characterStateChanged\", \"Event\", onCharacterStateChanged)\nend\n\nmain()\n\nreturn bow"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/bow.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/dagger.lua",
    "content": "local dagger \t\t\t= {}\n\tdagger.isEquipped = false\n\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal httpService \t\t= game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\n\nlocal abilityAnimations = game:GetService(\"ReplicatedStorage\"):WaitForChild(\"assets\"):WaitForChild(\"abilityAnimations\")\n\n\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t\t= modules.load(\"network\")\n\t\tlocal utilities \t= modules.load(\"utilities\")\n\t\tlocal detection \t= modules.load(\"detection\")\n\t\tlocal placeSetup \t= modules.load(\"placeSetup\")\n\nlocal currentDamageGUID = httpService:GenerateGUID(false)\n\nlocal animationInterface = require(script.Parent.Parent.Parent:WaitForChild(\"contents\"):WaitForChild(\"animationInterface\"))--network:invoke(\"getPlayerCoreService\", \"animationInterface\")\n\n-- internal stuff specific to the dagger\nlocal animationControllerLoaded\nlocal attackSequenceLength\nlocal animationsForAnimationController\n\nlocal slashAnimationConnection\n\nlocal isWithinSlash1Window \t\t= false\nlocal isWithinSlash2Window \t\t= false\nlocal isWithinDamageSequence \t= false\nlocal canPlayerDoubleSlash \t\t= false\nlocal canPlayerTripleSlash \t\t= false\n\nlocal currentWeaponManifest\nlocal myClientCharacterContainer\nlocal playerAbilitiesSlotDataCollection\n\nlocal player \t\t\t= game.Players.LocalPlayer\nlocal isPlayerSprinting = false\n\nlocal function onCharacterStateChanged(state, value)\n\tif state == \"isSprinting\" then\n\t\tisPlayerSprinting = value\n\tend\nend\n\nlocal function doesPlayerHaveAbilityUnlocked(abilityId, variant)\n\tif playerAbilitiesSlotDataCollection then\n\t\tfor _, abilitySlotData in pairs(playerAbilitiesSlotDataCollection) do\n\t\t\tif abilitySlotData.id == abilityId and abilitySlotData.rank > 0 then\n\t\t\t\tif variant then\n\t\t\t\t\treturn abilitySlotData.variant == variant\n\t\t\t\telse\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal isDamageSequenceEnabled = false\nlocal function startDamageSequencePolling()\n\tif isDamageSequenceEnabled then return end\n\tisDamageSequenceEnabled = true\n\n\twhile isDamageSequenceEnabled do\n\t\tif animationsForAnimationController.daggerAnimations.strike1.IsPlaying or animationsForAnimationController.daggerAnimations.strike2.IsPlaying or animationsForAnimationController.daggerAnimations.strike3.IsPlaying then\n\t\t\tif isWithinDamageSequence then\n\t\t\t\tnetwork:invoke(\"performClientDamageCycle\", \"equipment\", nil, currentDamageGUID)\n\t\t\tend\n\n\t\t\twait(1 / 20)\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\n\tisDamageSequenceEnabled = false\nend\n\nlocal function onSlashAnimationTrackStopped()\n\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\n\tif slashAnimationConnection then\n\t\tslashAnimationConnection:disconnect()\n\t\tslashAnimationConnection = nil\n\tend\n\n\tif slashAnimationKeyframeConnection then\n\t\tslashAnimationKeyframeConnection:disconnect()\n\t\tslashAnimationKeyframeConnection = nil\n\tend\n\n\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\tcurrentWeaponManifest.Trail.Enabled = false\n\tend\nend\n\n-- slash1PeriodStart\n-- slash2PeriodStart\n-- startDamageSequence\n-- stopDamageSequence\nlocal function onSlashAnimationKeyframeReached(keyframeName)\n\tif keyframeName == \"slash1PeriodStart\" then\n\t\tcurrentWeaponManifest.Trail.Color = ColorSequence.new(Color3.new(1,0,0))\n\t\tisWithinSlash1Window = true\n--\t\tdelay(3 / 10, function()\n--\t\t\tisWithinSlash1Window = false\n--\t\tend)\n\telseif keyframeName == \"slash2PeriodStart\" then\n\t\tcurrentWeaponManifest.Trail.Color = ColorSequence.new(Color3.new(0,1,0))\n\t\tisWithinSlash2Window = true\n--\t\tdelay(3 / 10, function()\n--\t\t\tisWithinSlash2Window = false\n--\t\tend)\n\telseif keyframeName == \"startDamageSequence\" then\n\t\tcurrentWeaponManifest.Trail.Color = ColorSequence.new(Color3.new(1,1,1))\n\t\tlocal swingSound = currentWeaponManifest:FindFirstChild(\"Swing\")\n\t\tif swingSound == nil then\n\t\t\tswingSound = Instance.new(\"Sound\")\n\t\t\tswingSound.Volume = 1\n\t\t\tswingSound.MaxDistance = 50\n\t\t\tswingSound.SoundId = \"rbxassetid://2069260907\"\n\t\t\tswingSound.Name = \"Swing\"\n\t\t\tswingSound.Parent = currentWeaponManifest\n\t\tend\n\n\t\tswingSound:Play()\n\t\tisWithinDamageSequence = true\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = true\n\t\tend\n\telseif keyframeName == \"stopDamageSequence\" then\n\t\tisWithinDamageSequence = false\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = false\n\t\tend\n\tend\nend\n\nfunction dagger:attack()\n\t-- make sure we can't slash if these conditions are true\n\n\tif not animationsForAnimationController or not animationsForAnimationController.daggerAnimations then\n\t\treturn\n\telseif isPlayerSprinting then\n\t\treturn\n\telseif not currentWeaponManifest then\n\t\treturn\n\telseif not player.Character or not player.Character.PrimaryPart or player.Character.PrimaryPart.state.Value == \"dead\" then\n\t\treturn\n\telseif animationsForAnimationController.daggerAnimations.strike1.IsPlaying and ((not isWithinSlash2Window) or (not canPlayerDoubleSlash))then --or not canPlayerDoubleSlash\n\t\treturn\n\telseif animationsForAnimationController.daggerAnimations.strike2.IsPlaying and not isWithinSlash1Window then\n\t\treturn\n\telseif animationsForAnimationController.daggerAnimations.strike3.IsPlaying then\n\t\treturn\n\tend\n\n\t-- have to do it this way for now, no reference to ability animations  in animationsForAnimationController\n\tlocal animController = myClientCharacterContainer.entity.AnimationController\n\tfor i, track in pairs(animController:GetPlayingAnimationTracks()) do\n\t\tif track.Name == \"rock_throw_upper\" or track.Name == \"rock_throw_upper_loop\" then\n\t\t\treturn\n\t\tend\n\tend\n\n\tif animationsForAnimationController.daggerAnimations.strike1.IsPlaying and isWithinSlash2Window then\n\t\tisWithinSlash1Window = false\n\t\tisWithinSlash2Window = false\n\t\tif slashAnimationConnection then\n\t\t\tslashAnimationConnection:disconnect()\n\t\t\tslashAnimationConnection = nil\n\t\tend\n\n\t\tif slashAnimationKeyframeConnection then\n\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\tslashAnimationKeyframeConnection = nil\n\t\tend\n\n\t\tanimationsForAnimationController.daggerAnimations.strike1:Stop()\n\n\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.daggerAnimations.strike2.Stopped:connect(onSlashAnimationTrackStopped)\n\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.daggerAnimations.strike2.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\tanimationInterface:replicateClientAnimationSequence(\"daggerAnimations\", \"strike2\")\n\n\t\t-- start damage sequence\n\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\tspawn(startDamageSequencePolling)\n\telseif (not animationsForAnimationController.daggerAnimations.strike1.IsPlaying) and ((not animationsForAnimationController.daggerAnimations.strike2.IsPlaying) or isWithinSlash1Window) then\n\t\tif canPlayerTripleSlash and isWithinSlash1Window then\n\t\t\tisWithinSlash1Window = false\n\t\t\tisWithinSlash2Window = false\n\t\t\tif slashAnimationConnection then\n\t\t\t\tslashAnimationConnection:disconnect()\n\t\t\t\tslashAnimationConnection = nil\n\t\t\tend\n\n\t\t\tif slashAnimationKeyframeConnection then\n\t\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\t\tslashAnimationKeyframeConnection = nil\n\t\t\tend\n\n\t\t\tanimationsForAnimationController.daggerAnimations.strike2:Stop()\n\n\t\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.daggerAnimations.strike3.Stopped:connect(onSlashAnimationTrackStopped)\n\t\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.daggerAnimations.strike3.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\t\tanimationInterface:replicateClientAnimationSequence(\"daggerAnimations\", \"strike3\")\n\n\t\t\t-- start damage sequence\n\t\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\t\tspawn(startDamageSequencePolling)\n\t\telse\n\t\t\tisWithinSlash1Window = false\n\t\t\tisWithinSlash2Window = false\n\t\t\tif slashAnimationConnection then\n\t\t\t\tslashAnimationConnection:disconnect()\n\t\t\t\tslashAnimationConnection = nil\n\t\t\tend\n\n\t\t\tif slashAnimationKeyframeConnection then\n\t\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\t\tslashAnimationKeyframeConnection = nil\n\t\t\tend\n\n\t\t\tanimationsForAnimationController.daggerAnimations.strike2:Stop()\n\n\t\t\tslashAnimationConnection = animationsForAnimationController.daggerAnimations.strike1.Stopped:connect(function()\n\t\t\t\tisWithinDamageSequence = false\n\t\t\tend)\n\n\t\t\tslashAnimationKeyframeConnection = animationsForAnimationController.daggerAnimations.strike1.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\t\tanimationInterface:replicateClientAnimationSequence(\"daggerAnimations\", \"strike1\")\n\n\t\t\t-- start damage sequence\n\t\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\t\tspawn(startDamageSequencePolling)\n\t\tend\n\tend\nend\n\nfunction dagger:equip()\n\tisWithinSlash1Window \t= false\n\tisWithinSlash2Window \t= false\n\tisWithinDamageSequence \t= false\n\tisDamageSequenceEnabled = false\n\t--local\n\tmyClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif myClientCharacterContainer then\n\t\tcurrentWeaponManifest \t\t\t\t= network:invoke(\"getCurrentWeaponManifest\")\n\t\tanimationsForAnimationController \t= animationInterface:getAnimationsForAnimationController(myClientCharacterContainer.entity.AnimationController)\n\n\t--\tlocal grip = myClientCharacterContainer.entity:FindFirstChild(\"Grip\", true)\n\t--\tif grip then\n\t--\t\t-- force an update\n\t--\t\tonGripPropertyChanged(grip, \"Part1\")\n\t--\tend\n\tend\nend\n\nfunction dagger:unequip()\n\nend\n\nlocal function onPropogationRequestToSelf(propogationNameTag, propogationValue)\n\tif propogationNameTag == \"abilities\" then\n\t\tplayerAbilitiesSlotDataCollection = propogationValue\n\n\t\tcanPlayerDoubleSlash = doesPlayerHaveAbilityUnlocked(3)\n\t\tcanPlayerTripleSlash = doesPlayerHaveAbilityUnlocked(3, \"tripleSlash\")\n\tend\nend\n\nlocal function main()\n\tonPropogationRequestToSelf(\"abilities\", network:invoke(\"getCacheValueByNameTag\", \"abilities\"))\n\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\tnetwork:connect(\"characterStateChanged\", \"Event\", onCharacterStateChanged)\nend\n\nmain()\n\nreturn dagger"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/dagger.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/dagger_old.lua",
    "content": "local dagger = {}\n\tdagger.isEquipped = false\n\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal httpService \t\t= game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t\t= modules.load(\"network\")\n\t\tlocal utilities \t= modules.load(\"utilities\")\n\t\tlocal detection \t= modules.load(\"detection\")\n\t\tlocal placeSetup \t= modules.load(\"placeSetup\")\n\nlocal currentDamageGUID = httpService:GenerateGUID(false)\n\nlocal animationInterface = require(script.Parent.Parent.Parent:WaitForChild(\"contents\"):WaitForChild(\"animationInterface\"))--network:invoke(\"getPlayerCoreService\", \"animationInterface\")\n\nlocal myClientCharacterContainer\n\n-- internal stuff specific to the dagger\nlocal animationControllerLoaded\nlocal attackSequenceLength\nlocal animationsForAnimationController\n\nlocal slashAnimationConnection\n\nlocal isWithinSlash1Window \t\t= false\nlocal isWithinSlash2Window \t\t= false\nlocal isWithinDamageSequence \t= false\nlocal canPlayerDoubleSlash \t\t= false\n\nlocal currentWeaponManifest\nlocal playerAbilitiesSlotDataCollection\n\nlocal player \t\t\t= game.Players.LocalPlayer\nlocal isPlayerSprinting = false\n\nlocal function onCharacterStateChanged(state, value)\n\tif state == \"isSprinting\" then\n\t\tisPlayerSprinting = value\n\tend\nend\n\nlocal function doesPlayerHaveAbilityUnlocked(abilityId)\n\tif playerAbilitiesSlotDataCollection then\n\t\tfor _, abilitySlotData in pairs(playerAbilitiesSlotDataCollection) do\n\t\t\tif abilitySlotData.id == abilityId and abilitySlotData.rank > 0 then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal isDamageSequenceEnabled = false\nlocal function startDamageSequencePolling()\n\tif isDamageSequenceEnabled then return end\n\tisDamageSequenceEnabled = true\n\n\twhile isDamageSequenceEnabled do\n\t\tif animationsForAnimationController.daggerAnimations.strike1.IsPlaying or animationsForAnimationController.daggerAnimations.strike2.IsPlaying then\n\t\t\tif isWithinDamageSequence then\n\t\t\t\t-- todo: consider just using serverHitbox in `monsterManifestCollectionFolder` ?\n\t\t\t\tnetwork:invoke(\"performClientDamageCycle\", \"equipment\", nil, currentDamageGUID)\n\t\t\tend\n\n\t\t\twait(1 / 20)\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\n\tisDamageSequenceEnabled = false\nend\n\nlocal function onSlashAnimationTrackStopped()\n\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\n\tif slashAnimationConnection then\n\t\tslashAnimationConnection:disconnect()\n\t\tslashAnimationConnection = nil\n\tend\n\n\tif slashAnimationKeyframeConnection then\n\t\tslashAnimationKeyframeConnection:disconnect()\n\t\tslashAnimationKeyframeConnection = nil\n\tend\n\n\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\tcurrentWeaponManifest.Trail.Enabled = false\n\tend\nend\n\n-- slash1PeriodStart\n-- slash2PeriodStart\n-- startDamageSequence\n-- stopDamageSequence\nlocal function onSlashAnimationKeyframeReached(keyframeName)\n\tif keyframeName == \"slash1PeriodStart\" then\n\t\tisWithinSlash1Window = true\n\t\tdelay(3 / 10, function()\n\t\t\tisWithinSlash1Window = false\n\t\tend)\n\telseif keyframeName == \"slash2PeriodStart\" then\n\t\tisWithinSlash2Window = true\n\t\tdelay(3 / 10, function()\n\t\t\tisWithinSlash2Window = false\n\t\tend)\n\telseif keyframeName == \"startDamageSequence\" then\n\n\t\tlocal swingSound = currentWeaponManifest:FindFirstChild(\"Swing\")\n\t\tif swingSound == nil then\n\t\t\tswingSound = Instance.new(\"Sound\")\n\t\t\tswingSound.Volume = 1\n\t\t\tswingSound.MaxDistance = 50\n\t\t\tswingSound.SoundId = \"rbxassetid://2069260907\"\n\t\t\tswingSound.Name = \"Swing\"\n\t\t\tswingSound.Parent = currentWeaponManifest\n\t\tend\n\n\t\tswingSound:Play()\n\t\tisWithinDamageSequence = true\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = true\n\t\tend\n\telseif keyframeName == \"stopDamageSequence\" then\n\t\tisWithinDamageSequence = false\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = false\n\t\tend\n\tend\nend\n\nfunction dagger:attack()\n\t-- make sure we can't slash if these conditions are true\n\n\tif not animationsForAnimationController or not animationsForAnimationController.swordAnimations then\n\t\treturn\n\telseif isPlayerSprinting then\n\t\treturn\n\telseif not currentWeaponManifest then\n\t\treturn\n\telseif not player.Character or not player.Character.PrimaryPart or player.Character.PrimaryPart.state.Value == \"dead\" then\n\t\treturn\n\telseif animationsForAnimationController.swordAnimations.strike1.IsPlaying and (not isWithinSlash2Window or not canPlayerDoubleSlash)then\n\t\treturn\n\telseif animationsForAnimationController.swordAnimations.strike2.IsPlaying and not isWithinSlash1Window then\n\t\treturn\n\tend\n\n\t-- have to do it this way for now, no reference to ability animations  in animationsForAnimationController\n\tlocal animController = myClientCharacterContainer.entity.AnimationController\n\tfor i, track in pairs(animController:GetPlayingAnimationTracks()) do\n\t\tif track.Name == \"rock_throw_upper\" or track.Name == \"rock_throw_upper_loop\" then\n\t\t\treturn\n\t\tend\n\tend\n\n\tif animationsForAnimationController.daggerAnimations.strike1.IsPlaying and isWithinSlash2Window and canPlayerDoubleSlash then\n\t\tif slashAnimationConnection then\n\t\t\tslashAnimationConnection:disconnect()\n\t\t\tslashAnimationConnection = nil\n\t\tend\n\n\t\tif slashAnimationKeyframeConnection then\n\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\tslashAnimationKeyframeConnection = nil\n\t\tend\n\n\t\tanimationsForAnimationController.daggerAnimations.strike1:Stop()\n\n\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.daggerAnimations.strike2.Stopped:connect(onSlashAnimationTrackStopped)\n\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.daggerAnimations.strike2.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\tanimationInterface:replicatePlayerAnimationSequence(\"daggerAnimations\", \"strike2\")\n\n\t\t-- start damage sequence\n\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\tspawn(startDamageSequencePolling)\n\telseif not animationsForAnimationController.daggerAnimations.strike1.IsPlaying and (not animationsForAnimationController.daggerAnimations.strike2.IsPlaying or isWithinSlash1Window) then\n\t\tif slashAnimationConnection then\n\t\t\tslashAnimationConnection:disconnect()\n\t\t\tslashAnimationConnection = nil\n\t\tend\n\n\t\tif slashAnimationKeyframeConnection then\n\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\tslashAnimationKeyframeConnection = nil\n\t\tend\n\n\t\tanimationsForAnimationController.daggerAnimations.strike2:Stop()\n\n\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.daggerAnimations.strike1.Stopped:connect(onSlashAnimationTrackStopped)\n\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.daggerAnimations.strike1.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\tanimationInterface:replicatePlayerAnimationSequence(\"daggerAnimations\", \"strike1\")\n\n\t\t-- start damage sequence\n\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\tspawn(startDamageSequencePolling)\n\tend\nend\n\nfunction dagger:equip()\n\tisWithinSlash1Window \t= false\n\tisWithinSlash2Window \t= false\n\tisWithinDamageSequence \t= false\n\tisDamageSequenceEnabled = false\n\n\tmyClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif myClientCharacterContainer then\n\t\tcurrentWeaponManifest \t\t\t\t= network:invoke(\"getCurrentWeaponManifest\")\n\t\tanimationsForAnimationController \t= animationInterface:getAnimationsForAnimationController(myClientCharacterContainer.entity.AnimationController)\n\n\t--\tlocal grip = myClientCharacterContainer.entity:FindFirstChild(\"Grip\", true)\n\t--\tif grip then\n\t--\t\t-- force an update\n\t--\t\tonGripPropertyChanged(grip, \"Part1\")\n\t--\tend\n\tend\nend\n\nfunction dagger:unequip()\n\nend\n\nlocal function onPropogationRequestToSelf(propogationNameTag, propogationValue)\n\tif propogationNameTag == \"abilities\" then\n\t\tplayerAbilitiesSlotDataCollection = propogationValue\n\n\t\tif doesPlayerHaveAbilityUnlocked(3) then\n\t\t\tcanPlayerDoubleSlash = true\n\t\telse\n\t\t\tcanPlayerDoubleSlash = false\n\t\tend\n\tend\nend\n\nlocal function main()\n\tonPropogationRequestToSelf(\"abilities\", network:invoke(\"getCacheValueByNameTag\", \"abilities\"))\n\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\tnetwork:connect(\"characterStateChanged\", \"Event\", onCharacterStateChanged)\nend\n\nmain()\n\nreturn dagger"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/dagger_old.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/dual.lua",
    "content": "local dual \t\t\t= {}\n\tdual.isEquipped = false\n\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal httpService \t\t= game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal abilityAnimations = game:GetService(\"ReplicatedStorage\"):WaitForChild(\"assets\"):WaitForChild(\"abilityAnimations\")\n\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t\t= modules.load(\"network\")\n\t\tlocal utilities \t= modules.load(\"utilities\")\n\t\tlocal detection \t= modules.load(\"detection\")\n\t\tlocal placeSetup \t= modules.load(\"placeSetup\")\n\nlocal currentDamageGUID = httpService:GenerateGUID(false)\n\nlocal animationInterface = require(script.Parent.Parent.Parent:WaitForChild(\"contents\"):WaitForChild(\"animationInterface\"))--network:invoke(\"getPlayerCoreService\", \"animationInterface\")\n\n-- internal stuff specific to the dual\nlocal animationControllerLoaded\nlocal attackSequenceLength\nlocal animationsForAnimationController\n\nlocal slashAnimationConnection\n\nlocal isWithinSlash1Window \t\t= false\nlocal isWithinSlash2Window \t\t= false\nlocal isWithinDamageSequence \t= false\nlocal canPlayerDoubleSlash \t\t= false\nlocal canPlayerTripleSlash \t\t= false\n\nlocal weaponManifestRight, weaponManifestLeft\nlocal myClientCharacterContainer\nlocal playerAbilitiesSlotDataCollection\n\nlocal player \t\t\t= game.Players.LocalPlayer\nlocal isPlayerSprinting = false\n\nlocal function onCharacterStateChanged(state, value)\n\tif state == \"isSprinting\" then\n\t\tisPlayerSprinting = value\n\tend\nend\n\nlocal function doesPlayerHaveAbilityUnlocked(abilityId)\n\tif playerAbilitiesSlotDataCollection then\n\t\tfor _, abilitySlotData in pairs(playerAbilitiesSlotDataCollection) do\n\t\t\tif abilitySlotData.id == abilityId and abilitySlotData.rank > 0 then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal isDamageSequenceEnabled = false\nlocal function startDamageSequencePolling()\n\tif isDamageSequenceEnabled then return end\n\tisDamageSequenceEnabled = true\n\n\twhile isDamageSequenceEnabled do\n\t\tif animationsForAnimationController.dualAnimations.strike1.IsPlaying or animationsForAnimationController.dualAnimations.strike2.IsPlaying or animationsForAnimationController.dualAnimations.strike3.IsPlaying then\n\t\t\tif isWithinDamageSequence then\n\t\t\t\tnetwork:invoke(\"performClientDamageCycle\", \"equipment\", nil, currentDamageGUID)\n\t\t\tend\n\n\t\t\twait(1 / 20)\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\n\tisDamageSequenceEnabled = false\nend\n\nlocal function onSlashAnimationTrackStopped()\n\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\n\tif slashAnimationConnection then\n\t\tslashAnimationConnection:disconnect()\n\t\tslashAnimationConnection = nil\n\tend\n\n\tif slashAnimationKeyframeConnection then\n\t\tslashAnimationKeyframeConnection:disconnect()\n\t\tslashAnimationKeyframeConnection = nil\n\tend\nend\n\n-- slash1PeriodStart\n-- slash2PeriodStart\n-- startDamageSequence\n-- stopDamageSequence\nlocal function onSlashAnimationKeyframeReached(keyframeName)\n\tlocal anims = animationsForAnimationController.dualAnimations\n\tif not anims then return end\n\n\tlocal weaponManifests\n\tif anims.strike1.IsPlaying then\n\t\tweaponManifests = {weaponManifestRight}\n\telseif anims.strike2.IsPlaying then\n\t\tweaponManifests = {weaponManifestLeft}\n\telseif anims.strike3.IsPlaying then\n\t\tweaponManifests = {weaponManifestRight, weaponManifestLeft}\n\telse\n\t\t-- this should hopefully not happen\n\t\tweaponManifests = {}\n\tend\n\n\tif keyframeName == \"slash1PeriodStart\" then\n\t\tisWithinSlash1Window = true\n\t\tdelay(0.6, function()\n\t\t\tisWithinSlash1Window = false\n\t\tend)\n\telseif keyframeName == \"slash2PeriodStart\" then\n\t\tisWithinSlash2Window = true\n\t\tdelay(0.6, function()\n\t\t\tisWithinSlash2Window = false\n\t\tend)\n\telseif keyframeName == \"startDamageSequence\" then\n\t\tisWithinDamageSequence = true\n\n\t\tfor _, currentWeaponManifest in pairs(weaponManifests) do\n\t\t\tlocal swingSound = currentWeaponManifest:FindFirstChild(\"Swing\")\n\t\t\tif swingSound == nil then\n\t\t\t\tswingSound = Instance.new(\"Sound\")\n\t\t\t\tswingSound.Volume = 1\n\t\t\t\tswingSound.MaxDistance = 50\n\t\t\t\tswingSound.SoundId = \"rbxassetid://2069260907\"\n\t\t\t\tswingSound.Name = \"Swing\"\n\t\t\t\tswingSound.Parent = currentWeaponManifest\n\t\t\tend\n\n\t\t\tswingSound:Play()\n\n\t\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\t\tcurrentWeaponManifest.Trail.Enabled = true\n\t\t\tend\n\t\tend\n\telseif keyframeName == \"stopDamageSequence\" then\n\t\tisWithinDamageSequence = false\n\n\t\tfor _, currentWeaponManifest in pairs(weaponManifests) do\n\t\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\t\tcurrentWeaponManifest.Trail.Enabled = false\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function resetConnections()\n\tif slashAnimationConnection then\n\t\tslashAnimationConnection:disconnect()\n\t\tslashAnimationConnection = nil\n\tend\n\tif slashAnimationKeyframeConnection then\n\t\tslashAnimationKeyframeConnection:disconnect()\n\t\tslashAnimationKeyframeConnection = nil\n\tend\nend\n\nfunction dual:attack()\n\tif not animationsForAnimationController then return end\n\n\tlocal anims = animationsForAnimationController.dualAnimations\n\n\tif not anims then\n\t\treturn\n\telseif isPlayerSprinting then\n\t\treturn\n\telseif not (weaponManifestRight and weaponManifestRight) then\n\t\treturn\n\telseif not player.Character or not player.Character.PrimaryPart or player.Character.PrimaryPart.state.Value == \"dead\" then\n\t\treturn\n\telseif anims.strike1.IsPlaying then\n\t\treturn\n\telseif anims.strike2.IsPlaying then\n\t\treturn\n\telseif anims.strike3.IsPlaying then\n\t\treturn\n\tend\n\n\t-- should slash1 if we're not playing it or we're following through from\n\t-- slash2 if we're not a triple slash character\n\tlocal shouldSlash1 = (not anims.strike1.IsPlaying) or (isWithinSlash1Window and (not canPlayerTripleSlash))\n\n\t-- should slash2 if we're not playing it and we're following through from\n\t-- slash1 and we can double slash\n\tlocal shouldSlash2 = (not anims.strike2.IsPlaying) and isWithinSlash2Window and canPlayerDoubleSlash\n\n\t-- should slash3 if we're not playing it and we're following through from\n\t-- slash2 and we can triple slash\n\tlocal shouldSlash3 = (not anims.strike3.IsPlaying) and isWithinSlash1Window and canPlayerTripleSlash\n\n\t-- so... let's do the slash we're supposed to be doing\n\t-- prioritize slash3 because sometimes slash1 thinks it's better\n\tif shouldSlash3 then\n\t\tisWithinSlash1Window = false\n\n\t\tresetConnections()\n\t\tslashAnimationConnection = anims.strike3.Stopped:Connect(onSlashAnimationTrackStopped)\n\t\tslashAnimationKeyframeConnection = anims.strike3.KeyframeReached:Connect(onSlashAnimationKeyframeReached)\n\n\t\tanimationInterface:replicateClientAnimationSequence(\"dualAnimations\", \"strike3\")\n\n\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\tspawn(startDamageSequencePolling)\n\n\telseif shouldSlash2 then\n\t\tisWithinSlash2Window = false\n\n\t\tresetConnections()\n\t\tslashAnimationConnection = anims.strike2.Stopped:Connect(onSlashAnimationTrackStopped)\n\t\tslashAnimationKeyframeConnection = anims.strike2.KeyframeReached:Connect(onSlashAnimationKeyframeReached)\n\n\t\tanimationInterface:replicateClientAnimationSequence(\"dualAnimations\", \"strike2\")\n\n\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\tspawn(startDamageSequencePolling)\n\n\telseif shouldSlash1 then\n\t\tisWithinSlash1Window = false\n\n\t\tresetConnections()\n\t\tslashAnimationConnection = anims.strike1.Stopped:Connect(onSlashAnimationTrackStopped)\n\t\tslashAnimationKeyframeConnection = anims.strike1.KeyframeReached:Connect(onSlashAnimationKeyframeReached)\n\n\t\tanimationInterface:replicateClientAnimationSequence(\"dualAnimations\", \"strike1\")\n\n\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\tspawn(startDamageSequencePolling)\n\tend\nend\n\nfunction dual:equip()\n\tisWithinSlash1Window \t= false\n\tisWithinSlash2Window \t= false\n\tisWithinDamageSequence \t= false\n\tisDamageSequenceEnabled = false\n\t--local\n\tmyClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif myClientCharacterContainer then\n\t\tlocal currentlyEquipped = network:invoke(\"getCurrentlyEquippedForRenderCharacter\", myClientCharacterContainer.entity)\n\t\tweaponManifestRight = currentlyEquipped[\"1\"].manifest\n\t\tweaponManifestLeft  = currentlyEquipped[\"11\"].manifest\n\n\t\tanimationsForAnimationController \t= animationInterface:getAnimationsForAnimationController(myClientCharacterContainer.entity.AnimationController)\n\n\t--\tlocal grip = myClientCharacterContainer.entity:FindFirstChild(\"Grip\", true)\n\t--\tif grip then\n\t--\t\t-- force an update\n\t--\t\tonGripPropertyChanged(grip, \"Part1\")\n\t--\tend\n\tend\nend\n\nfunction dual:unequip()\n\nend\n\nlocal function onPropogationRequestToSelf(propogationNameTag, propogationValue)\n\tif propogationNameTag == \"abilities\" then\n\t\tplayerAbilitiesSlotDataCollection = propogationValue\n\n\t\tcanPlayerDoubleSlash = doesPlayerHaveAbilityUnlocked(3)\n\t\tcanPlayerTripleSlash = doesPlayerHaveAbilityUnlocked(30)\n\tend\nend\n\nlocal function main()\n\tonPropogationRequestToSelf(\"abilities\", network:invoke(\"getCacheValueByNameTag\", \"abilities\"))\n\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\tnetwork:connect(\"characterStateChanged\", \"Event\", onCharacterStateChanged)\nend\n\nmain()\n\nreturn dual"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/dual.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/fishing-rod.lua",
    "content": "local fishingPole \t\t\t= {}\n\tfishingPole.isEquipped = false\n\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal httpService \t\t= game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t\t\t= modules.load(\"network\")\n\t\tlocal utilities \t\t= modules.load(\"utilities\")\n\t\tlocal detection \t\t= modules.load(\"detection\")\n\t\tlocal placeSetup \t\t= modules.load(\"placeSetup\")\n\t\tlocal projectile \t\t= modules.load(\"projectile\")\n\t\tlocal client_utilities \t= modules.load(\"client_utilities\")\n\nlocal currentDamageGUID = httpService:GenerateGUID(false)\n\nlocal animationInterface = require(script.Parent.Parent.Parent:WaitForChild(\"contents\"):WaitForChild(\"animationInterface\"))--network:invoke(\"getPlayerCoreService\", \"animationInterface\")\n\n-- internal stuff specific to the fishingPole\nlocal animationControllerLoaded\nlocal attackSequenceLength\n\nlocal slashAnimationConnection\n\nlocal isWithinSlash1Window \t\t= false\nlocal isWithinSlash2Window \t\t= false\nlocal isWithinDamageSequence \t= false\nlocal canPlayerDoubleSlash \t\t= false\n\nlocal currentWeaponManifest\nlocal playerAbilitiesSlotDataCollection\n\nlocal player = game.Players.LocalPlayer\n\nlocal isFishing \t\t\t= false\nlocal isProcessing \t\t\t= false\nlocal fishingPoleManifest\nlocal currentBob\n\nlocal timesFishing = 0\n\n\n\nfunction fishingPole:attack()\n\tif not isProcessing then\n\t\tisProcessing = true\n\n\t\tif not isFishing then\n\t\t\ttimesFishing = timesFishing + 1\n\t\t\tlocal myClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\t\t\tif not myClientCharacterContainer or not myClientCharacterContainer:FindFirstChild(\"entity\") then return false end\n\n\t\t\tlocal currentlyEquipped \t= network:invoke(\"getCurrentlyEquippedForRenderCharacter\", myClientCharacterContainer.entity)\n\t\t\tlocal currentWeaponManifest = currentlyEquipped[\"1\"] and currentlyEquipped[\"1\"].manifest\n\t\t\tif not currentWeaponManifest then return end\n\n\t\t\tlocal _, targetPosition = client_utilities.raycastFromCurrentScreenPoint({myClientCharacterContainer})\n\n\t\t\tanimationInterface:replicatePlayerAnimationSequence(\"fishing-rodAnimations\", \"cast-line\", nil, {targetPosition = targetPosition})\n\n\t\t\tnetwork:invoke(\"setCharacterArrested\", true)\n\t\t\tnetwork:invoke(\"setCharacterMovementState\", \"isFishing\", true)\n\t\t\t--network:invokeServer(\"playerRequest_startFishing\")\n\t\telse\n\t\t\tanimationInterface:replicatePlayerAnimationSequence(\"fishing-rodAnimations\", \"reel-line\")\n\n\t\t\tisFishing \t\t= false\n\n\n\n\t\t\tspawn(function()\n\t\t\t\twait(1)\n\t\t\t\tisProcessing \t= false\n\t\t\tend)\n\t\tend\n\tend\nend\n\nlocal function onFishingBobBobbed()\n\tlocal clientFishingBob = network:invoke(\"getPlayerRenderDataByNameTag\", player, \"fishingBob\")\n\n\tif clientFishingBob and clientFishingBob.Parent then\n\t\tlocal currentFishing = timesFishing\n\n\t\tclientFishingBob.CFrame = clientFishingBob.CFrame - Vector3.new(0, 1, 0)\n\t\tclientFishingBob.splash:Emit(40)\n\n\t\tutilities.playSound(\"fishing_FishBite\", clientFishingBob)\n\t\twait(0.15)\n\t\tif currentFishing == timesFishing then\n\t\t\tutilities.playSound(\"fishing_FishSplashOutOfWater\", clientFishingBob)\n\t\tend\n\n\n\t\twait(0.1)\n\t\tif currentFishing == timesFishing then\n\t\t\tclientFishingBob.CFrame = clientFishingBob.CFrame - Vector3.new(0, 0.25, 0)\n\t\t\tclientFishingBob.splash:Emit(40)\n\t\tend\n\n\n\tend\nend\n\nfunction fishingPole:equip()\n\tcurrentWeaponManifest = network:invoke(\"getCurrentWeaponManifest\")\nend\n\nfunction fishingPole:unequip()\n\tif isFishing then\n\t\tnetwork:invoke(\"setCharacterMovementState\", \"isFishing\", false)\n\t\tnetwork:invoke(\"setCharacterArrested\", false)\n\n\t\tlocal currentBob = network:invoke(\"getPlayerRenderDataByNameTag\", game.Players.LocalPlayer, \"fishingBob\")\n\n\t\tif currentBob then\n\t\t\tgame:GetService(\"Debris\"):AddItem(currentBob, 1 / 30)\n\t\tend\n\n\t\tif fishingPoleManifest and fishingPoleManifest:FindFirstChild(\"line\") then\n\t\t\tfishingPoleManifest.line.Attachment1 = nil\n\t\t\tfishingPoleManifest = nil\n\t\tend\n\n\t\tisFishing = false\n\tend\nend\n\nlocal function onPropogationRequestToSelf(propogationNameTag, propogationValue)\n\tif propogationNameTag == \"abilities\" then\n\t\tplayerAbilitiesSlotDataCollection = propogationValue\n\tend\nend\n\nlocal function onFishingBobHit(hitWater)\n\tif not hitWater then\n\t\tisFishing = false\n\n\t\tnetwork:invoke(\"setCharacterMovementState\", \"isFishing\", false)\n\t\tnetwork:invoke(\"setCharacterArrested\", false)\n\t\tspawn(function()\n\t\t\twait(1)\n\t\t\tisProcessing \t= false\n\t\tend)\n\telse\n\t\tisFishing = true\n\t\tisProcessing \t= false\n\tend\n\n\nend\n\nlocal function main()\n\tonPropogationRequestToSelf(\"abilities\", network:invoke(\"getCacheValueByNameTag\", \"abilities\"))\n\n\tnetwork:create(\"fishingBobHit\", \"BindableEvent\", \"Event\", onFishingBobHit)\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\tnetwork:connect(\"signal_fishingBobBobbed\", \"OnClientEvent\", onFishingBobBobbed)\nend\n\nmain()\n\nreturn fishingPole"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/fishing-rod.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/greatsword.lua",
    "content": "local sword \t\t\t= {}\n\tsword.isEquipped = false\n\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal httpService \t\t= game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\n\nlocal abilityAnimations = game:GetService(\"ReplicatedStorage\"):WaitForChild(\"assets\"):WaitForChild(\"abilityAnimations\")\n\n\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t\t= modules.load(\"network\")\n\t\tlocal utilities \t= modules.load(\"utilities\")\n\t\tlocal detection \t= modules.load(\"detection\")\n\t\tlocal placeSetup \t= modules.load(\"placeSetup\")\n\nlocal currentDamageGUID = httpService:GenerateGUID(false)\n\nlocal animationInterface = require(script.Parent.Parent.Parent:WaitForChild(\"contents\"):WaitForChild(\"animationInterface\"))--network:invoke(\"getPlayerCoreService\", \"animationInterface\")\n\n-- internal stuff specific to the sword\nlocal animationControllerLoaded\nlocal attackSequenceLength\nlocal animationsForAnimationController\n\nlocal slashAnimationConnection\n\nlocal isWithinSlash1Window \t\t= false\nlocal isWithinSlash2Window \t\t= false\nlocal isWithinDamageSequence \t= false\nlocal canPlayerDoubleSlash \t\t= false\nlocal canPlayerTripleSlash \t\t= false\nlocal isContinueNextStrike \t\t= false\n\nlocal currentWeaponManifest\nlocal myClientCharacterContainer\nlocal playerAbilitiesSlotDataCollection\n\nlocal player \t\t\t= game.Players.LocalPlayer\nlocal isPlayerSprinting = false\n\nlocal function onCharacterStateChanged(state, value)\n\tif state == \"isSprinting\" then\n\t\tisPlayerSprinting = value\n\tend\nend\n\nlocal function doesPlayerHaveAbilityUnlocked(abilityId, variant)\n\tif playerAbilitiesSlotDataCollection then\n\t\tfor _, abilitySlotData in pairs(playerAbilitiesSlotDataCollection) do\n\t\t\tif abilitySlotData.id == abilityId and abilitySlotData.rank > 0 then\n\t\t\t\tif variant then\n\t\t\t\t\treturn abilitySlotData.variant == variant\n\t\t\t\telse\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal isDamageSequenceEnabled = false\nlocal function startDamageSequencePolling()\n\tif isDamageSequenceEnabled then return end\n\tisDamageSequenceEnabled = true\n\n\twhile isDamageSequenceEnabled do\n\t\tif animationsForAnimationController.greatswordAnimations.strike1.IsPlaying or animationsForAnimationController.greatswordAnimations.strike2.IsPlaying or animationsForAnimationController.greatswordAnimations.strike3.IsPlaying then\n\t\t\tif isWithinDamageSequence then\n\t\t\t\tnetwork:invoke(\"performClientDamageCycle\", \"equipment\", nil, currentDamageGUID)\n\t\t\tend\n\n\t\t\twait(1 / 20)\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\n\tisDamageSequenceEnabled = false\nend\n\n\n-- slash1PeriodStart\n-- slash2PeriodStart\n-- startDamageSequence\n-- stopDamageSequence\nlocal function onSlashAnimationKeyframeReached(keyframeName)\n\tif keyframeName == \"startDamageSequence\" then\n\t\tlocal swingSound = currentWeaponManifest:FindFirstChild(\"Swing\")\n\t\tif swingSound == nil then\n\t\t\tswingSound = Instance.new(\"Sound\")\n\t\t\tswingSound.PlaybackSpeed = 0.6\n\t\t\tswingSound.Volume = 1\n\t\t\tswingSound.MaxDistance = 50\n\t\t\tswingSound.SoundId = \"rbxassetid://2069260907\"\n\t\t\tswingSound.Name = \"Swing\"\n\t\t\tswingSound.Parent = currentWeaponManifest\n\t\tend\n\n\t\tswingSound:Play()\n\t\tisWithinDamageSequence = true\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = true\n\t\tend\n\telseif keyframeName == \"stopDamageSequence\" then\n\t\tisWithinDamageSequence = false\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = false\n\t\tend\n\tend\nend\n\nlocal function onSlashAnimationTrackStopped(current)\n\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\n\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\tcurrentWeaponManifest.Trail.Enabled = false\n\tend\n\n\tif isContinueNextStrike then\n\t\tif slashAnimationConnection then\n\t\t\tslashAnimationConnection:disconnect()\n\t\t\tslashAnimationConnection = nil\n\t\tend\n\n\t\tif slashAnimationKeyframeConnection then\n\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\tslashAnimationKeyframeConnection = nil\n\t\tend\n\n\t\tlocal nextStrike = (current == \"strike1\" and \"strike2\") or (current == \"strike2\" and \"strike3\")\n\n\t\tif nextStrike then\n\t\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.greatswordAnimations[nextStrike].KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\t\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.greatswordAnimations[nextStrike].Stopped:connect(function()\n\t\t\t\tonSlashAnimationTrackStopped(nextStrike)\n\t\tend)\n\n\t\t--checking if player has the ability to do nextStrike\n\t\t--strike0 represents when the player should not strike again\n\n\t\tif nextStrike == \"strike2\" then\n\t\t\tif canPlayerDoubleSlash then else\n\t\t\t\tnextStrike = \"strike0\"\n\t\t\tend\n\t\telseif nextStrike == \"strike3\" then\n\t\t\tif canPlayerTripleSlash then else\n\t\t\t\tnextStrike = \"strike0\"\n\t\t\tend\n\t\tend\n\n\n\t\t\tif nextStrike == \"strike0\" then\n\t\t\telse\n\t\t\t\tanimationInterface:replicateClientAnimationSequence(\"greatswordAnimations\", nextStrike)\n\n\t\t\t\tspawn(startDamageSequencePolling)\n\t\t\tend\n\t\tend\n\telse\n\t\tif slashAnimationConnection then\n\t\t\tslashAnimationConnection:disconnect()\n\t\t\tslashAnimationConnection = nil\n\t\tend\n\n\t\tif slashAnimationKeyframeConnection then\n\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\tslashAnimationKeyframeConnection = nil\n\t\tend\n\tend\n\n\tisContinueNextStrike = false\nend\n\nfunction sword:attack()\n\n\tif not animationsForAnimationController or not animationsForAnimationController.greatswordAnimations then\n\t\treturn\n\telseif isPlayerSprinting then\n\t\treturn\n\telseif not currentWeaponManifest then\n\t\treturn\n\telseif not player.Character or not player.Character.PrimaryPart or player.Character.PrimaryPart.state.Value == \"dead\" then\n\t\treturn\n\telseif animationsForAnimationController.greatswordAnimations.strike1.IsPlaying and isContinueNextStrike then --or not canPlayerDoubleSlash\n\t\treturn\n\telseif animationsForAnimationController.greatswordAnimations.strike2.IsPlaying and isContinueNextStrike then\n\t\treturn\n\telseif animationsForAnimationController.greatswordAnimations.strike3.IsPlaying then\n\t\treturn\n\tend\n\n\tif not animationsForAnimationController.greatswordAnimations.strike1.IsPlaying and not animationsForAnimationController.greatswordAnimations.strike2.IsPlaying and not animationsForAnimationController.greatswordAnimations.strike3.IsPlaying then\n\t\tif slashAnimationConnection then\n\t\t\tslashAnimationConnection:disconnect()\n\t\t\tslashAnimationConnection = nil\n\t\tend\n\n\t\tif slashAnimationKeyframeConnection then\n\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\tslashAnimationKeyframeConnection = nil\n\t\tend\n\n\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.greatswordAnimations.strike1.Stopped:connect(function() onSlashAnimationTrackStopped(\"strike1\") end)\n\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.greatswordAnimations.strike1.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\tanimationInterface:replicateClientAnimationSequence(\"greatswordAnimations\", \"strike1\")\n\n\t\t-- start damage sequence\n\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\tspawn(startDamageSequencePolling)\n\telse\n\t\tisContinueNextStrike = true\n\tend\nend\n\nfunction sword:equip()\n\tisWithinSlash1Window \t= false\n\tisWithinSlash2Window \t= false\n\tisWithinDamageSequence \t= false\n\tisDamageSequenceEnabled = false\n\tmyClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif myClientCharacterContainer then\n\t\tcurrentWeaponManifest \t\t\t\t= network:invoke(\"getCurrentWeaponManifest\")\n\t\tanimationsForAnimationController \t= animationInterface:getAnimationsForAnimationController(myClientCharacterContainer.entity.AnimationController)\n\tend\nend\n\nfunction sword:unequip()\n\nend\n\nlocal function onPropogationRequestToSelf(propogationNameTag, propogationValue)\n\tif propogationNameTag == \"abilities\" then\n\t\tplayerAbilitiesSlotDataCollection = propogationValue\n\n\t\tcanPlayerDoubleSlash = doesPlayerHaveAbilityUnlocked(3)\n\t\tcanPlayerTripleSlash = doesPlayerHaveAbilityUnlocked(3, \"tripleSlash\")\n\tend\nend\n\nlocal function main()\n\tonPropogationRequestToSelf(\"abilities\", network:invoke(\"getCacheValueByNameTag\", \"abilities\"))\n\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\tnetwork:connect(\"characterStateChanged\", \"Event\", onCharacterStateChanged)\nend\n\nmain()\n\nreturn sword"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/greatsword.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/init.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/pickaxe.lua",
    "content": "local axe \t\t\t= {}\naxe.isEquipped = false\n\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal httpService \t\t= game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\n\nlocal abilityAnimations = game:GetService(\"ReplicatedStorage\"):WaitForChild(\"assets\"):WaitForChild(\"abilityAnimations\")\n\n\nlocal modules = require(replicatedStorage.modules)\nlocal network \t\t= modules.load(\"network\")\n\nlocal currentDamageGUID = httpService:GenerateGUID(false)\n\nlocal animationInterface = require(script.Parent.Parent.Parent:WaitForChild(\"contents\"):WaitForChild(\"animationInterface\"))--network:invoke(\"getPlayerCoreService\", \"animationInterface\")\n\n-- internal stuff specific to the sword\nlocal animationsForAnimationController\n\nlocal slashAnimationConnection\n\nlocal isWithinSlash1Window \t\t= false\nlocal isWithinSlash2Window \t\t= false\nlocal isWithinDamageSequence \t= false\n\nlocal currentWeaponManifest\nlocal myClientCharacterContainer\nlocal playerAbilitiesSlotDataCollection\n\nlocal player \t\t\t= game.Players.LocalPlayer\nlocal isPlayerSprinting = false\n\nlocal function onCharacterStateChanged(state, value)\n\tif state == \"isSprinting\" then\n\t\tisPlayerSprinting = value\n\tend\nend\n\nlocal isDamageSequenceEnabled = false\nlocal function startDamageSequencePolling()\n\tif isDamageSequenceEnabled then return end\n\tisDamageSequenceEnabled = true\n\n\twhile isDamageSequenceEnabled do\n\t\tif animationsForAnimationController.pickaxeAnimations.strike1.IsPlaying then\n            if isWithinDamageSequence then\n\t\t\t\tnetwork:invoke(\"performClientDamageCycle\", \"equipment\", nil, currentDamageGUID)\n\t\t\tend\n\n\t\t\twait(1 / 20)\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\n\tisDamageSequenceEnabled = false\nend\n\nlocal function onSlashAnimationTrackStopped()\n\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\n\tif slashAnimationConnection then\n\t\tslashAnimationConnection:disconnect()\n\t\tslashAnimationConnection = nil\n\tend\n\n\tif slashAnimationKeyframeConnection then\n\t\tslashAnimationKeyframeConnection:disconnect()\n\t\tslashAnimationKeyframeConnection = nil\n\tend\n\n\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\tcurrentWeaponManifest.Trail.Enabled = false\n\tend\nend\n\n-- slash1PeriodStart\n-- slash2PeriodStart\n-- startDamageSequence\n-- stopDamageSequence\nlocal function onSlashAnimationKeyframeReached(keyframeName)\n\tif keyframeName == \"slash1PeriodStart\" then\n\t\tisWithinSlash1Window = true\n\t\tdelay(3 / 10, function()\n\t\t\tisWithinSlash1Window = false\n        end)\n\n    elseif keyframeName == \"startDamageSequence\" then\n\n\t\tlocal swingSound = currentWeaponManifest:FindFirstChild(\"Swing\")\n\t\tif swingSound == nil then\n\t\t\tswingSound = Instance.new(\"Sound\")\n\t\t\tswingSound.Volume = 1\n\t\t\tswingSound.MaxDistance = 50\n\t\t\tswingSound.SoundId = \"rbxassetid://2069260907\"\n\t\t\tswingSound.Name = \"Swing\"\n\t\t\tswingSound.Parent = currentWeaponManifest\n\t\tend\n\n\t\tswingSound:Play()\n\t\tisWithinDamageSequence = true\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = true\n        end\n\n\telseif keyframeName == \"stopDamageSequence\" then\n\t\tisWithinDamageSequence = false\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = false\n\t\tend\n\tend\nend\n\nfunction axe:attack()\n\t-- make sure we can't slash if these conditions are true\n\n\tif not animationsForAnimationController or not animationsForAnimationController.pickaxeAnimations then\n\t\treturn\n\telseif isPlayerSprinting then\n\t\treturn\n\telseif not currentWeaponManifest then\n\t\treturn\n\telseif not player.Character or not player.Character.PrimaryPart or player.Character.PrimaryPart.state.Value == \"dead\" then\n\t\treturn\n\telseif animationsForAnimationController.pickaxeAnimations.strike1.IsPlaying and (not isWithinSlash2Window or not canPlayerDoubleSlash)then --or not canPlayerDoubleSlash\n\n\t\treturn\n\tend\n\t-- have to do it this way for now, no reference to ability animations  in animationsForAnimationController\n\tlocal animController = myClientCharacterContainer.entity.AnimationController\n\tfor i, track in pairs(animController:GetPlayingAnimationTracks()) do\n\t\tif track.Name == \"rock_throw_upper\" or track.Name == \"rock_throw_upper_loop\" then\n\t\t\treturn\n\t\tend\n\tend\n\n\tif slashAnimationConnection then\n\t\tslashAnimationConnection:disconnect()\n\t\tslashAnimationConnection = nil\n\tend\n\n\tif slashAnimationKeyframeConnection then\n\t\tslashAnimationKeyframeConnection:disconnect()\n\t\tslashAnimationKeyframeConnection = nil\n\tend\n\n\n\tslashAnimationConnection = animationsForAnimationController.pickaxeAnimations.strike1.Stopped:connect(function()\n\t\tisWithinDamageSequence = false\n\t\tisWithinSlash1Window = false\n\t\tisWithinSlash2Window = false\n\tend)\n\n\tslashAnimationKeyframeConnection = animationsForAnimationController.pickaxeAnimations.strike1.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\tanimationInterface:replicateClientAnimationSequence(\"pickaxeAnimations\", \"strike1\")\n\t-- start damage sequence\n\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\tspawn(startDamageSequencePolling)\nend\n\nfunction axe:equip()\n\tisWithinSlash1Window \t= false\n\tisWithinSlash2Window \t= false\n\tisWithinDamageSequence \t= false\n\tisDamageSequenceEnabled = false\n\t--local\n\tmyClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif myClientCharacterContainer then\n\t\tcurrentWeaponManifest \t\t\t\t= network:invoke(\"getCurrentWeaponManifest\")\n\t\tanimationsForAnimationController \t= animationInterface:getAnimationsForAnimationController(myClientCharacterContainer.entity.AnimationController)\n\n\t--\tlocal grip = myClientCharacterContainer.entity:FindFirstChild(\"Grip\", true)\n\t--\tif grip then\n\t--\t\t-- force an update\n\t--\t\tonGripPropertyChanged(grip, \"Part1\")\n\t--\tend\n\tend\nend\n\nfunction axe:unequip()\n\nend\n\nlocal function onPropogationRequestToSelf(propogationNameTag, propogationValue)\n\tif propogationNameTag == \"abilities\" then\n\t\tplayerAbilitiesSlotDataCollection = propogationValue\n\tend\nend\n\nlocal function main()\n\tonPropogationRequestToSelf(\"abilities\", network:invoke(\"getCacheValueByNameTag\", \"abilities\"))\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\tnetwork:connect(\"characterStateChanged\", \"Event\", onCharacterStateChanged)\nend\n\nmain()\n\nreturn axe"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/staff.lua",
    "content": "local staff \t\t\t= {}\n\tstaff.isEquipped = false\n\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal httpService \t\t= game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\n\nlocal abilityAnimations = game:GetService(\"ReplicatedStorage\"):WaitForChild(\"assets\"):WaitForChild(\"abilityAnimations\")\n\n\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t\t= modules.load(\"network\")\n\t\tlocal utilities \t= modules.load(\"utilities\")\n\t\tlocal detection \t= modules.load(\"detection\")\n\t\tlocal placeSetup \t= modules.load(\"placeSetup\")\n\nlocal currentDamageGUID = httpService:GenerateGUID(false)\n\nlocal animationInterface = require(script.Parent.Parent.Parent:WaitForChild(\"contents\"):WaitForChild(\"animationInterface\"))--network:invoke(\"getPlayerCoreService\", \"animationInterface\")\n\n-- internal stuff specific to the staff\nlocal animationControllerLoaded\nlocal attackSequenceLength\nlocal animationsForAnimationController\n\nlocal slashAnimationConnection\n\nlocal isWithinSlash1Window \t\t= false\nlocal isWithinSlash2Window \t\t= false\nlocal isWithinDamageSequence \t= false\nlocal canPlayerDoubleSlash \t\t= false\nlocal canPlayerTripleSlash \t\t= false\n\nlocal currentWeaponManifest\nlocal myClientCharacterContainer\nlocal playerAbilitiesSlotDataCollection\n\nlocal player \t\t\t= game.Players.LocalPlayer\nlocal isPlayerSprinting = false\n\nlocal function onCharacterStateChanged(state, value)\n\tif state == \"isSprinting\" then\n\t\tisPlayerSprinting = value\n\tend\nend\n\nlocal function doesPlayerHaveAbilityUnlocked(abilityId, variant)\n\tif playerAbilitiesSlotDataCollection then\n\t\tfor _, abilitySlotData in pairs(playerAbilitiesSlotDataCollection) do\n\t\t\tif abilitySlotData.id == abilityId and abilitySlotData.rank > 0 then\n\t\t\t\tif variant then\n\t\t\t\t\treturn abilitySlotData.variant == variant\n\t\t\t\telse\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal isDamageSequenceEnabled = false\nlocal function startDamageSequencePolling()\n\tif isDamageSequenceEnabled then return end\n\tisDamageSequenceEnabled = true\n\n\twhile isDamageSequenceEnabled do\n\t\tif animationsForAnimationController.swordAnimations.strike1.IsPlaying or animationsForAnimationController.swordAnimations.strike2.IsPlaying or animationsForAnimationController.swordAnimations.strike3.IsPlaying then\n\t\t\tif isWithinDamageSequence then\n\t\t\t\tnetwork:invoke(\"performClientDamageCycle\", \"equipment\", nil, currentDamageGUID)\n\t\t\tend\n\n\t\t\twait(1 / 20)\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\n\tisDamageSequenceEnabled = false\nend\n\nlocal function onSlashAnimationTrackStopped()\n\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\n\tif slashAnimationConnection then\n\t\tslashAnimationConnection:disconnect()\n\t\tslashAnimationConnection = nil\n\tend\n\n\tif slashAnimationKeyframeConnection then\n\t\tslashAnimationKeyframeConnection:disconnect()\n\t\tslashAnimationKeyframeConnection = nil\n\tend\n\n\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\tcurrentWeaponManifest.Trail.Enabled = false\n\tend\nend\n\n-- slash1PeriodStart\n-- slash2PeriodStart\n-- startDamageSequence\n-- stopDamageSequence\nlocal function onSlashAnimationKeyframeReached(keyframeName)\n\tif keyframeName == \"slash1PeriodStart\" then\n\t\tisWithinSlash1Window = true\n\t\tdelay(3 / 10, function()\n\t\t\tisWithinSlash1Window = false\n\t\tend)\n\telseif keyframeName == \"slash2PeriodStart\" then\n\t\tisWithinSlash2Window = true\n\t\tdelay(3 / 10, function()\n\t\t\tisWithinSlash2Window = false\n\t\tend)\n\telseif keyframeName == \"startDamageSequence\" then\n\n\t\tlocal swingSound = currentWeaponManifest:FindFirstChild(\"Swing\")\n\t\tif swingSound == nil then\n\t\t\tswingSound = Instance.new(\"Sound\")\n\t\t\tswingSound.Volume = 1\n\t\t\tswingSound.MaxDistance = 50\n\t\t\tswingSound.SoundId = \"rbxassetid://2069260907\"\n\t\t\tswingSound.Name = \"Swing\"\n\t\t\tswingSound.Parent = currentWeaponManifest\n\t\tend\n\n\t\tswingSound:Play()\n\t\tisWithinDamageSequence = true\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = true\n\t\tend\n\telseif keyframeName == \"stopDamageSequence\" then\n\t\tisWithinDamageSequence = false\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = false\n\t\tend\n\tend\nend\n\nfunction staff:attack()\n\t-- make sure we can't slash if these conditions are true\n\n\tif not animationsForAnimationController or not animationsForAnimationController.swordAnimations then\n\t\treturn\n\telseif isPlayerSprinting then\n\t\treturn\n\telseif not currentWeaponManifest then\n\t\treturn\n\telseif not player.Character or not player.Character.PrimaryPart or player.Character.PrimaryPart.state.Value == \"dead\" then\n\t\treturn\n\telseif animationsForAnimationController.swordAnimations.strike1.IsPlaying and (not isWithinSlash2Window or not canPlayerDoubleSlash)then --or not canPlayerDoubleSlash\n\n\t\treturn\n\telseif animationsForAnimationController.swordAnimations.strike2.IsPlaying and not isWithinSlash1Window then\n\t\treturn\n\telseif animationsForAnimationController.swordAnimations.strike3.IsPlaying then\n\t\treturn\n\tend\n\n\t-- have to do it this way for now, no reference to ability animations  in animationsForAnimationController\n\tlocal animController = myClientCharacterContainer.entity.AnimationController\n\tfor i, track in pairs(animController:GetPlayingAnimationTracks()) do\n\t\tif track.Name == \"rock_throw_upper\" or track.Name == \"rock_throw_upper_loop\" then\n\t\t\treturn\n\t\tend\n\tend\n\n\tif animationsForAnimationController.swordAnimations.strike1.IsPlaying and isWithinSlash2Window then\n\t\tif slashAnimationConnection then\n\t\t\tslashAnimationConnection:disconnect()\n\t\t\tslashAnimationConnection = nil\n\t\tend\n\n\t\tif slashAnimationKeyframeConnection then\n\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\tslashAnimationKeyframeConnection = nil\n\t\tend\n\n\t\tanimationsForAnimationController.swordAnimations.strike1:Stop()\n\n\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.swordAnimations.strike2.Stopped:connect(onSlashAnimationTrackStopped)\n\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.swordAnimations.strike2.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\tanimationInterface:replicateClientAnimationSequence(\"swordAnimations\", \"strike2\")\n\n\t\t-- start damage sequence\n\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\tspawn(startDamageSequencePolling)\n\telseif not animationsForAnimationController.swordAnimations.strike1.IsPlaying and (not animationsForAnimationController.swordAnimations.strike2.IsPlaying or isWithinSlash1Window) then\n\t\tif canPlayerTripleSlash and animationsForAnimationController.swordAnimations.strike2.IsPlaying and animationsForAnimationController.swordAnimations.strike2.TimePosition >= animationsForAnimationController.swordAnimations.strike2.Length * 0.3 and animationsForAnimationController.swordAnimations.strike2.TimePosition <= animationsForAnimationController.swordAnimations.strike2.Length * 0.7 then\n\t\t\tif slashAnimationConnection then\n\t\t\t\tslashAnimationConnection:disconnect()\n\t\t\t\tslashAnimationConnection = nil\n\t\t\tend\n\n\t\t\tif slashAnimationKeyframeConnection then\n\t\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\t\tslashAnimationKeyframeConnection = nil\n\t\t\tend\n\n\t\t\tanimationsForAnimationController.swordAnimations.strike2:Stop()\n\n\t\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.swordAnimations.strike3.Stopped:connect(onSlashAnimationTrackStopped)\n\t\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.swordAnimations.strike3.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\t\tanimationInterface:replicateClientAnimationSequence(\"swordAnimations\", \"strike3\")\n\n\t\t\t-- start damage sequence\n\t\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\t\tspawn(startDamageSequencePolling)\n\t\telse\n\t\t\tif slashAnimationConnection then\n\t\t\t\tslashAnimationConnection:disconnect()\n\t\t\t\tslashAnimationConnection = nil\n\t\t\tend\n\n\t\t\tif slashAnimationKeyframeConnection then\n\t\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\t\tslashAnimationKeyframeConnection = nil\n\t\t\tend\n\n\t\t\tanimationsForAnimationController.swordAnimations.strike2:Stop()\n\n\t\t\tslashAnimationConnection = animationsForAnimationController.swordAnimations.strike1.Stopped:connect(function()\n\t\t\t\tisWithinDamageSequence = false\n\t\t\t\tisWithinSlash1Window = false\n\t\t\t\tisWithinSlash2Window = false\n\t\t\tend)\n\n\t\t\tslashAnimationKeyframeConnection = animationsForAnimationController.swordAnimations.strike1.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\t\tanimationInterface:replicateClientAnimationSequence(\"swordAnimations\", \"strike1\")\n\n\t\t\t-- start damage sequence\n\t\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\t\tspawn(startDamageSequencePolling)\n\t\tend\n\tend\nend\n\nfunction staff:equip()\n\tisWithinSlash1Window \t= false\n\tisWithinSlash2Window \t= false\n\tisWithinDamageSequence \t= false\n\tisDamageSequenceEnabled = false\n\t--local\n\tmyClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif myClientCharacterContainer then\n\t\tcurrentWeaponManifest \t\t\t\t= network:invoke(\"getCurrentWeaponManifest\")\n\t\tanimationsForAnimationController \t= animationInterface:getAnimationsForAnimationController(myClientCharacterContainer.entity.AnimationController)\n\tend\nend\n\nfunction staff:unequip()\n\nend\n\nlocal function onPropogationRequestToSelf(propogationNameTag, propogationValue)\n\tif propogationNameTag == \"abilities\" then\n\t\tplayerAbilitiesSlotDataCollection = propogationValue\n\n\t\tcanPlayerDoubleSlash = doesPlayerHaveAbilityUnlocked(3)\n\t\tcanPlayerTripleSlash = doesPlayerHaveAbilityUnlocked(3, \"tripleSlash\")\n\tend\nend\n\nlocal function main()\n\tonPropogationRequestToSelf(\"abilities\", network:invoke(\"getCacheValueByNameTag\", \"abilities\"))\n\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\tnetwork:connect(\"characterStateChanged\", \"Event\", onCharacterStateChanged)\nend\n\nmain()\n\nreturn staff"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/staff.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/staff_melee_old.lua",
    "content": "local staff \t\t\t= {}\n\tstaff.isEquipped = false\n\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal httpService \t\t= game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\n\nlocal abilityAnimations = game:GetService(\"ReplicatedStorage\"):WaitForChild(\"abilityAnimations\")\n\n\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t\t= modules.load(\"network\")\n\t\tlocal utilities \t= modules.load(\"utilities\")\n\t\tlocal detection \t= modules.load(\"detection\")\n\t\tlocal placeSetup \t= modules.load(\"placeSetup\")\n\nlocal currentDamageGUID = httpService:GenerateGUID(false)\n\nlocal animationInterface = require(script.Parent.Parent.Parent:WaitForChild(\"contents\"):WaitForChild(\"animationInterface\"))--network:invoke(\"getPlayerCoreService\", \"animationInterface\")\n\n-- internal stuff specific to the staff\nlocal animationControllerLoaded\nlocal attackSequenceLength\nlocal animationsForAnimationController\n\nlocal slashAnimationConnection\n\nlocal isWithinSlash1Window \t\t= false\nlocal isWithinSlash2Window \t\t= false\nlocal isWithinDamageSequence \t= false\nlocal canPlayerDoubleSlash \t\t= false\n\nlocal currentWeaponManifest\nlocal myClientCharacterContainer\nlocal playerAbilitiesSlotDataCollection\n\nlocal player \t\t\t= game.Players.LocalPlayer\nlocal isPlayerSprinting = false\n\nlocal function onCharacterStateChanged(state, value)\n\tif state == \"isSprinting\" then\n\t\tisPlayerSprinting = value\n\tend\nend\n\nlocal function doesPlayerHaveAbilityUnlocked(abilityId)\n\tif playerAbilitiesSlotDataCollection then\n\t\tfor _, abilitySlotData in pairs(playerAbilitiesSlotDataCollection) do\n\t\t\tif abilitySlotData.id == abilityId and abilitySlotData.rank > 0 then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal isDamageSequenceEnabled = false\nlocal function startDamageSequencePolling()\n\tif isDamageSequenceEnabled then return end\n\tisDamageSequenceEnabled = true\n\n\twhile isDamageSequenceEnabled do\n\t\tif animationsForAnimationController.swordAnimations.strike1.IsPlaying or animationsForAnimationController.swordAnimations.strike2.IsPlaying then\n\t\t\tif isWithinDamageSequence then\n\t\t\t\tnetwork:invoke(\"performClientDamageCycle\", \"equipment\", nil, currentDamageGUID)\n\t\t\tend\n\n\t\t\twait(1 / 20)\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\n\tisDamageSequenceEnabled = false\nend\n\nlocal function onSlashAnimationTrackStopped()\n\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\n\tif slashAnimationConnection then\n\t\tslashAnimationConnection:disconnect()\n\t\tslashAnimationConnection = nil\n\tend\n\n\tif slashAnimationKeyframeConnection then\n\t\tslashAnimationKeyframeConnection:disconnect()\n\t\tslashAnimationKeyframeConnection = nil\n\tend\n\n\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\tcurrentWeaponManifest.Trail.Enabled = false\n\tend\nend\n\n-- slash1PeriodStart\n-- slash2PeriodStart\n-- startDamageSequence\n-- stopDamageSequence\nlocal function onSlashAnimationKeyframeReached(keyframeName)\n\tif keyframeName == \"slash1PeriodStart\" then\n\t\tisWithinSlash1Window = true\n\t\tdelay(3 / 10, function()\n\t\t\tisWithinSlash1Window = false\n\t\tend)\n\telseif keyframeName == \"slash2PeriodStart\" then\n\t\tisWithinSlash2Window = true\n\t\tdelay(3 / 10, function()\n\t\t\tisWithinSlash2Window = false\n\t\tend)\n\telseif keyframeName == \"startDamageSequence\" then\n\n\t\tlocal swingSound = currentWeaponManifest:FindFirstChild(\"Swing\")\n\t\tif swingSound == nil then\n\t\t\tswingSound = Instance.new(\"Sound\")\n\t\t\tswingSound.Volume = 1\n\t\t\tswingSound.MaxDistance = 50\n\t\t\tswingSound.SoundId = \"rbxassetid://2069260907\"\n\t\t\tswingSound.Name = \"Swing\"\n\t\t\tswingSound.Parent = currentWeaponManifest\n\t\tend\n\n\t\tswingSound:Play()\n\t\tisWithinDamageSequence = true\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = true\n\t\tend\n\telseif keyframeName == \"stopDamageSequence\" then\n\t\tisWithinDamageSequence = false\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = false\n\t\tend\n\tend\nend\n\nfunction staff:attack()\n\t-- make sure we can't slash if these conditions are true\n\n\tif not animationsForAnimationController or not animationsForAnimationController.swordAnimations then\n\t\treturn\n\telseif isPlayerSprinting then\n\t\treturn\n\telseif not currentWeaponManifest then\n\t\treturn\n\telseif not player.Character or not player.Character.PrimaryPart or player.Character.PrimaryPart.state.Value == \"dead\" then\n\t\treturn\n\telseif animationsForAnimationController.swordAnimations.strike1.IsPlaying and (not isWithinSlash2Window or not canPlayerDoubleSlash)then --or not canPlayerDoubleSlash\n\n\t\treturn\n\telseif animationsForAnimationController.swordAnimations.strike2.IsPlaying and not isWithinSlash1Window then\n\t\treturn\n\tend\n\n\tif animationsForAnimationController.swordAnimations.strike1.IsPlaying and isWithinSlash2Window then\n\t\tif slashAnimationConnection then\n\t\t\tslashAnimationConnection:disconnect()\n\t\t\tslashAnimationConnection = nil\n\t\tend\n\n\t\tif slashAnimationKeyframeConnection then\n\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\tslashAnimationKeyframeConnection = nil\n\t\tend\n\n\t\tanimationsForAnimationController.swordAnimations.strike1:Stop()\n\n\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.swordAnimations.strike2.Stopped:connect(onSlashAnimationTrackStopped)\n\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.swordAnimations.strike2.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\tanimationInterface:replicateClientAnimationSequence(\"swordAnimations\", \"strike2\")\n\n\t\t-- start damage sequence\n\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\tspawn(startDamageSequencePolling)\n\telseif not animationsForAnimationController.swordAnimations.strike1.IsPlaying and (not animationsForAnimationController.swordAnimations.strike2.IsPlaying or isWithinSlash1Window) then\n\t\tif slashAnimationConnection then\n\t\t\tslashAnimationConnection:disconnect()\n\t\t\tslashAnimationConnection = nil\n\t\tend\n\n\t\tif slashAnimationKeyframeConnection then\n\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\tslashAnimationKeyframeConnection = nil\n\t\tend\n\n\t\tanimationsForAnimationController.swordAnimations.strike2:Stop()\n\n\t\tslashAnimationConnection = animationsForAnimationController.swordAnimations.strike1.Stopped:connect(function()\n\t\t\tisWithinDamageSequence = false\n\t\t\tisWithinSlash1Window = false\n\t\t\tisWithinSlash2Window = false\n\t\tend)\n\n\t\tslashAnimationKeyframeConnection = animationsForAnimationController.swordAnimations.strike1.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\tanimationInterface:replicateClientAnimationSequence(\"swordAnimations\", \"strike1\")\n\n\t\t-- start damage sequence\n\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\tspawn(startDamageSequencePolling)\n\tend\nend\n\nfunction staff:equip()\n\tisWithinSlash1Window \t= false\n\tisWithinSlash2Window \t= false\n\tisWithinDamageSequence \t= false\n\tisDamageSequenceEnabled = false\n\t--local\n\tmyClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif myClientCharacterContainer then\n\t\tcurrentWeaponManifest \t\t\t\t= network:invoke(\"getCurrentWeaponManifest\")\n\t\tanimationsForAnimationController \t= animationInterface:getAnimationsForAnimationController(myClientCharacterContainer.entity.AnimationController)\n\n\t--\tlocal grip = myClientCharacterContainer.entity:FindFirstChild(\"Grip\", true)\n\t--\tif grip then\n\t--\t\t-- force an update\n\t--\t\tonGripPropertyChanged(grip, \"Part1\")\n\t--\tend\n\tend\nend\n\nfunction staff:unequip()\n\nend\n\nlocal function onPropogationRequestToSelf(propogationNameTag, propogationValue)\n\tif propogationNameTag == \"abilities\" then\n\t\tplayerAbilitiesSlotDataCollection = propogationValue\n\n\t\tcanPlayerDoubleSlash = doesPlayerHaveAbilityUnlocked(3)\n\tend\nend\n\nlocal function main()\n\tonPropogationRequestToSelf(\"abilities\", network:invoke(\"getCacheValueByNameTag\", \"abilities\"))\n\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\tnetwork:connect(\"characterStateChanged\", \"Event\", onCharacterStateChanged)\nend\n\nmain()\n\nreturn staff"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/staff_melee_old.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/staff_ranged_old.lua",
    "content": "local hitDebounceTable \t= {}\nlocal staff \t\t\t= {}\n\tstaff.isEquipped = false\n\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal httpService \t\t= game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t\t= modules.load(\"network\")\n\t\tlocal utilities \t= modules.load(\"utilities\")\n\t\tlocal detection \t= modules.load(\"detection\")\n\t\tlocal placeSetup \t= modules.load(\"placeSetup\")\n\t\tlocal configuration = modules.load(\"configuration\")\n\nlocal currentDamageGUID = httpService:GenerateGUID(false)\n\nlocal animationInterface = require(script.Parent.Parent.Parent:WaitForChild(\"contents\"):WaitForChild(\"animationInterface\"))--network:invoke(\"getPlayerCoreService\", \"animationInterface\")\n\n-- internal stuff specific to the staff\nlocal animationControllerLoaded\nlocal attackSequenceLength\nlocal animationsForAnimationController\n\nlocal slashAnimationConnection\n\nlocal myClientCharacterContainer\n\nlocal isWithinSlash1Window \t\t= false\nlocal isWithinSlash2Window \t\t= false\nlocal isWithinDamageSequence \t= false\nlocal canPlayerDoubleSlash \t\t= false\n\nlocal currentWeaponManifest\nlocal playerAbilitiesSlotDataCollection\n\nlocal player \t\t\t= game.Players.LocalPlayer\nlocal isPlayerSprinting = false\n\nlocal function onCharacterStateChanged(state, value)\n\tif state == \"isSprinting\" then\n\t\tisPlayerSprinting = value\n\tend\nend\n\nlocal function doesPlayerHaveAbilityUnlocked(abilityId)\n\tif playerAbilitiesSlotDataCollection then\n\t\tfor _, abilitySlotData in pairs(playerAbilitiesSlotDataCollection) do\n\t\t\tif abilitySlotData.id == abilityId and abilitySlotData.rank > 0 then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal isDamageSequenceEnabled = false\nlocal function startDamageSequencePolling()\n\tif isDamageSequenceEnabled then return end\n\tisDamageSequenceEnabled = true\n\t-- staff uses magic attack, no polling\n\t--[[\n\twhile isDamageSequenceEnabled do\n\t\tif animationsForAnimationController.staffAnimations.strike1.IsPlaying or animationsForAnimationController.staffAnimations.strike2.IsPlaying then\n\t\t\tif isWithinDamageSequence then\n\t\t\t\t-- todo: consider just using serverHitbox in `monsterManifestCollectionFolder` ?\n\t\t\t\tnetwork:invoke(\"performClientDamageCycle\", \"equipment\", nil, currentDamageGUID)\n\t\t\tend\n\n\t\t\twait(1 / 20)\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\t]]\n\tisDamageSequenceEnabled = false\nend\n\nlocal function onSlashAnimationTrackStopped()\n\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\n\tif slashAnimationConnection then\n\t\tslashAnimationConnection:disconnect()\n\t\tslashAnimationConnection = nil\n\tend\n\n\tif slashAnimationKeyframeConnection then\n\t\tslashAnimationKeyframeConnection:disconnect()\n\t\tslashAnimationKeyframeConnection = nil\n\tend\n\n\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n--\t\tcurrentWeaponManifest.Trail.Enabled = false\n\tend\nend\n\n-- slash1PeriodStart\n-- slash2PeriodStart\n-- startDamageSequence\n-- stopDamageSequence\nlocal function onSlashAnimationKeyframeReached(keyframeName)\n\tif keyframeName == \"slash1PeriodStart\" then\n\t\tisWithinSlash1Window = true\n\t\tdelay(3 / 10, function()\n\t\t\tisWithinSlash1Window = false\n\t\tend)\n\telseif keyframeName == \"slash2PeriodStart\" then\n\t\tisWithinSlash2Window = true\n\t\tdelay(3 / 10, function()\n\t\t\tisWithinSlash2Window = false\n\t\tend)\n\telseif keyframeName == \"startDamageSequence\" then\n\n\t\tlocal swingSound = currentWeaponManifest:FindFirstChild(\"Swing\")\n\t\tif swingSound == nil then\n\t\t\tswingSound = Instance.new(\"Sound\")\n\t\t\tswingSound.Volume = 1\n\t\t\tswingSound.MaxDistance = 50\n\t\t\tswingSound.SoundId = \"rbxassetid://2069260907\"\n\t\t\tswingSound.Name = \"Swing\"\n\t\t\tswingSound.Parent = currentWeaponManifest\n\t\tend\n\n--\t\tswingSound:Play()\n\t\tisWithinDamageSequence = true\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n--\t\t\tcurrentWeaponManifest.Trail.Enabled = true\n\t\tend\n\telseif keyframeName == \"stopDamageSequence\" then\n\t\tisWithinDamageSequence = false\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n--\t\t\tcurrentWeaponManifest.Trail.Enabled = false\n\t\tend\n\tend\nend\n\nfunction staff:attack()\n\t-- make sure we can't slash if these conditions are true\n\n\tif not animationsForAnimationController or not animationsForAnimationController.staffAnimations then\n\t\treturn\n\telseif isPlayerSprinting then\n\t\treturn\n\telseif not currentWeaponManifest then\n\t\treturn\n\telseif not player.Character or not player.Character.PrimaryPart or player.Character.PrimaryPart.state.Value == \"dead\" then\n\t\treturn\n\telseif animationsForAnimationController.staffAnimations.strike1.IsPlaying and (not isWithinSlash2Window or not canPlayerDoubleSlash)then\n\t\treturn\n\telseif animationsForAnimationController.staffAnimations.strike2.IsPlaying and not isWithinSlash1Window then\n\t\treturn\n\tend\n\n\t-- have to do it this way for now, no reference to ability animations  in animationsForAnimationController\n\tlocal animController = myClientCharacterContainer.entity.AnimationController\n\tfor i, track in pairs(animController:GetPlayingAnimationTracks()) do\n\t\tif track.Name == \"rock_throw_upper\" or track.Name == \"rock_throw_upper_loop\" then\n\t\t\treturn\n\t\tend\n\tend\n\n\tlocal abilityExecutionData = network:invoke(\"getAbilityExecutionData\")\n\n\tlocal manaBasicAttackCost = configuration.getConfigurationValue(\"mageManaDrainFromBasicAttack\")\n\tif player.Character.PrimaryPart.mana.Value < manaBasicAttackCost then\n\t\tabilityExecutionData.noRangeManaAttack = true\n\tend\n\n\tif animationsForAnimationController.staffAnimations.strike1.IsPlaying and isWithinSlash2Window then\n\t\tif slashAnimationConnection then\n\t\t\tslashAnimationConnection:disconnect()\n\t\t\tslashAnimationConnection = nil\n\t\tend\n\n\t\tif slashAnimationKeyframeConnection then\n\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\tslashAnimationKeyframeConnection = nil\n\t\tend\n\n\t\tanimationsForAnimationController.staffAnimations.strike1:Stop()\n\n\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.staffAnimations.strike2.Stopped:connect(onSlashAnimationTrackStopped)\n\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.staffAnimations.strike2.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\tanimationInterface:replicatePlayerAnimationSequence(\"staffAnimations\", \"strike2\", nil, abilityExecutionData)\n\n\t\t-- start damage sequence\n\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\tspawn(startDamageSequencePolling)\n\telseif not animationsForAnimationController.staffAnimations.strike1.IsPlaying and (not animationsForAnimationController.staffAnimations.strike2.IsPlaying or isWithinSlash1Window) then\n\t\tif slashAnimationConnection then\n\t\t\tslashAnimationConnection:disconnect()\n\t\t\tslashAnimationConnection = nil\n\t\tend\n\n\t\tif slashAnimationKeyframeConnection then\n\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\tslashAnimationKeyframeConnection = nil\n\t\tend\n\n\t\tanimationsForAnimationController.staffAnimations.strike2:Stop()\n\n\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.staffAnimations.strike1.Stopped:connect(onSlashAnimationTrackStopped)\n\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.staffAnimations.strike1.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\tanimationInterface:replicatePlayerAnimationSequence(\"staffAnimations\", \"strike1\", nil, abilityExecutionData)\n\n\t\t-- start damage sequence\n\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\tspawn(startDamageSequencePolling)\n\tend\nend\n\nfunction staff:equip()\n\tisWithinSlash1Window \t= false\n\tisWithinSlash2Window \t= false\n\tisWithinDamageSequence \t= false\n\tisDamageSequenceEnabled = false\n\n\tmyClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif myClientCharacterContainer then\n\t\tcurrentWeaponManifest \t\t\t\t= network:invoke(\"getCurrentWeaponManifest\")\n\t\tanimationsForAnimationController \t= animationInterface:getAnimationsForAnimationController(myClientCharacterContainer.entity.AnimationController)\n\n\t--\tlocal grip = myClientCharacterContainer.entity:FindFirstChild(\"Grip\", true)\n\t--\tif grip then\n\t--\t\t-- force an update\n\t--\t\tonGripPropertyChanged(grip, \"Part1\")\n\t--\tend\n\tend\nend\n\nfunction staff:unequip()\n\nend\n\nlocal function onPropogationRequestToSelf(propogationNameTag, propogationValue)\n\tif propogationNameTag == \"abilities\" then\n\t\tplayerAbilitiesSlotDataCollection = propogationValue\n\n\t\tif doesPlayerHaveAbilityUnlocked(3) then\n\t\t\tcanPlayerDoubleSlash = true\n\t\telse\n\t\t\tcanPlayerDoubleSlash = false\n\t\tend\n\tend\nend\n\nlocal function main()\n\tonPropogationRequestToSelf(\"abilities\", network:invoke(\"getCacheValueByNameTag\", \"abilities\"))\n\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\tnetwork:connect(\"characterStateChanged\", \"Event\", onCharacterStateChanged)\nend\n\nmain()\n\nreturn staff"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/staff_ranged_old.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/sword.lua",
    "content": "local sword \t\t\t= {}\n\tsword.isEquipped = false\n\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal httpService \t\t= game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\n\nlocal abilityAnimations = game:GetService(\"ReplicatedStorage\"):WaitForChild(\"assets\"):WaitForChild(\"abilityAnimations\")\n\n\nlocal modules = require(replicatedStorage.modules)\nlocal network \t\t= modules.load(\"network\")\nlocal utilities \t= modules.load(\"utilities\")\nlocal detection \t= modules.load(\"detection\")\nlocal placeSetup \t= modules.load(\"placeSetup\")\n\nlocal currentDamageGUID = httpService:GenerateGUID(false)\n\nlocal animationInterface = require(script.Parent.Parent.Parent:WaitForChild(\"contents\"):WaitForChild(\"animationInterface\"))--network:invoke(\"getPlayerCoreService\", \"animationInterface\")\n\n-- internal stuff specific to the sword\nlocal animationControllerLoaded\nlocal attackSequenceLength\nlocal animationsForAnimationController\n\nlocal slashAnimationConnection\n\nlocal isWithinSlash1Window \t\t= false\nlocal isWithinSlash2Window \t\t= false\nlocal isWithinDamageSequence \t= false\nlocal canPlayerDoubleSlash \t\t= false\nlocal canPlayerTripleSlash \t\t= false\n\nlocal currentWeaponManifest\nlocal myClientCharacterContainer\nlocal playerAbilitiesSlotDataCollection\n\nlocal player \t\t\t= game.Players.LocalPlayer\nlocal isPlayerSprinting = false\n\nlocal function onCharacterStateChanged(state, value)\n\tif state == \"isSprinting\" then\n\t\tisPlayerSprinting = value\n\tend\nend\n\nlocal function doesPlayerHaveAbilityUnlocked(abilityId, variant)\n\tif playerAbilitiesSlotDataCollection then\n\t\tfor _, abilitySlotData in pairs(playerAbilitiesSlotDataCollection) do\n\t\t\tif abilitySlotData.id == abilityId and abilitySlotData.rank > 0 then\n\t\t\t\tif variant then\n\t\t\t\t\treturn abilitySlotData.variant == variant\n\t\t\t\telse\n\t\t\t\t\treturn true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal isDamageSequenceEnabled = false\nlocal function startDamageSequencePolling()\n\tif isDamageSequenceEnabled then return end\n\tisDamageSequenceEnabled = true\n\n\twhile isDamageSequenceEnabled do\n\t\tif animationsForAnimationController.swordAnimations.strike1.IsPlaying or animationsForAnimationController.swordAnimations.strike2.IsPlaying or animationsForAnimationController.swordAnimations.strike3.IsPlaying then\n\t\t\tif isWithinDamageSequence then\n\t\t\t\tnetwork:invoke(\"performClientDamageCycle\", \"equipment\", nil, currentDamageGUID)\n\t\t\tend\n\n\t\t\twait(1 / 20)\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\n\tisDamageSequenceEnabled = false\nend\n\nlocal function onSlashAnimationTrackStopped()\n\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\n\tif slashAnimationConnection then\n\t\tslashAnimationConnection:disconnect()\n\t\tslashAnimationConnection = nil\n\tend\n\n\tif slashAnimationKeyframeConnection then\n\t\tslashAnimationKeyframeConnection:disconnect()\n\t\tslashAnimationKeyframeConnection = nil\n\tend\n\n\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\tcurrentWeaponManifest.Trail.Enabled = false\n\tend\nend\n\n-- slash1PeriodStart\n-- slash2PeriodStart\n-- startDamageSequence\n-- stopDamageSequence\nlocal function onSlashAnimationKeyframeReached(keyframeName)\n\tif keyframeName == \"slash1PeriodStart\" then\n\t\tisWithinSlash1Window = true\n\t\tdelay(3 / 10, function()\n\t\t\tisWithinSlash1Window = false\n\t\tend)\n\telseif keyframeName == \"slash2PeriodStart\" then\n\t\tisWithinSlash2Window = true\n\t\tdelay(3 / 10, function()\n\t\t\tisWithinSlash2Window = false\n\t\tend)\n\telseif keyframeName == \"startDamageSequence\" then\n\n\t\tlocal swingSound = currentWeaponManifest:FindFirstChild(\"Swing\")\n\t\tif swingSound == nil then\n\t\t\tswingSound = Instance.new(\"Sound\")\n\t\t\tswingSound.Volume = 1\n\t\t\tswingSound.MaxDistance = 50\n\t\t\tswingSound.SoundId = \"rbxassetid://2069260907\"\n\t\t\tswingSound.Name = \"Swing\"\n\t\t\tswingSound.Parent = currentWeaponManifest\n\t\tend\n\n\t\tswingSound:Play()\n\t\tisWithinDamageSequence = true\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = true\n\t\tend\n\telseif keyframeName == \"stopDamageSequence\" then\n\t\tisWithinDamageSequence = false\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = false\n\t\tend\n\tend\nend\n\nfunction sword:attack()\n\t-- make sure we can't slash if these conditions are true\n\n\tif not animationsForAnimationController or not animationsForAnimationController.swordAnimations then\n\t\treturn\n\telseif isPlayerSprinting then\n\t\treturn\n\telseif not currentWeaponManifest then\n\t\treturn\n\telseif not player.Character or not player.Character.PrimaryPart or player.Character.PrimaryPart.state.Value == \"dead\" then\n\t\treturn\n\telseif animationsForAnimationController.swordAnimations.strike1.IsPlaying and (not isWithinSlash2Window or not canPlayerDoubleSlash)then --or not canPlayerDoubleSlash\n\n\t\treturn\n\telseif animationsForAnimationController.swordAnimations.strike2.IsPlaying and not isWithinSlash1Window then\n\t\treturn\n\telseif animationsForAnimationController.swordAnimations.strike3.IsPlaying then\n\t\treturn\n\tend\n\t-- have to do it this way for now, no reference to ability animations  in animationsForAnimationController\n\tlocal animController = myClientCharacterContainer.entity.AnimationController\n\tfor i, track in pairs(animController:GetPlayingAnimationTracks()) do\n\t\tif track.Name == \"rock_throw_upper\" or track.Name == \"rock_throw_upper_loop\" then\n\t\t\treturn\n\t\tend\n\tend\n\n\tif animationsForAnimationController.swordAnimations.strike1.IsPlaying and isWithinSlash2Window then\n\t\tif slashAnimationConnection then\n\t\t\tslashAnimationConnection:disconnect()\n\t\t\tslashAnimationConnection = nil\n\t\tend\n\n\t\tif slashAnimationKeyframeConnection then\n\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\tslashAnimationKeyframeConnection = nil\n\t\tend\n\n\t\tanimationsForAnimationController.swordAnimations.strike1:Stop()\n\n\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.swordAnimations.strike2.Stopped:connect(onSlashAnimationTrackStopped)\n\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.swordAnimations.strike2.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\tanimationInterface:replicateClientAnimationSequence(\"swordAnimations\", \"strike2\")\n\n\t\t-- start damage sequence\n\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\tspawn(startDamageSequencePolling)\n\telseif not animationsForAnimationController.swordAnimations.strike1.IsPlaying and (not animationsForAnimationController.swordAnimations.strike2.IsPlaying or isWithinSlash1Window) then\n\t\tif canPlayerTripleSlash and animationsForAnimationController.swordAnimations.strike2.IsPlaying and animationsForAnimationController.swordAnimations.strike2.TimePosition >= animationsForAnimationController.swordAnimations.strike2.Length * 0.3 and animationsForAnimationController.swordAnimations.strike2.TimePosition <= animationsForAnimationController.swordAnimations.strike2.Length * 0.7 then\n\t\t\tif slashAnimationConnection then\n\t\t\t\tslashAnimationConnection:disconnect()\n\t\t\t\tslashAnimationConnection = nil\n\t\t\tend\n\n\t\t\tif slashAnimationKeyframeConnection then\n\t\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\t\tslashAnimationKeyframeConnection = nil\n\t\t\tend\n\n\t\t\tanimationsForAnimationController.swordAnimations.strike2:Stop()\n\n\t\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.swordAnimations.strike3.Stopped:connect(onSlashAnimationTrackStopped)\n\t\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.swordAnimations.strike3.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\t\tanimationInterface:replicateClientAnimationSequence(\"swordAnimations\", \"strike3\")\n\n\t\t\t-- start damage sequence\n\t\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\t\tspawn(startDamageSequencePolling)\n\t\telse\n\t\t\tif slashAnimationConnection then\n\t\t\t\tslashAnimationConnection:disconnect()\n\t\t\t\tslashAnimationConnection = nil\n\t\t\tend\n\n\t\t\tif slashAnimationKeyframeConnection then\n\t\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\t\tslashAnimationKeyframeConnection = nil\n\t\t\tend\n\n\t\t\tanimationsForAnimationController.swordAnimations.strike2:Stop()\n\n\t\t\tslashAnimationConnection = animationsForAnimationController.swordAnimations.strike1.Stopped:connect(function()\n\t\t\t\tisWithinDamageSequence = false\n\t\t\t\tisWithinSlash1Window = false\n\t\t\t\tisWithinSlash2Window = false\n\t\t\tend)\n\n\t\t\tslashAnimationKeyframeConnection = animationsForAnimationController.swordAnimations.strike1.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\t\tanimationInterface:replicateClientAnimationSequence(\"swordAnimations\", \"strike1\")\n\t\t\t-- start damage sequence\n\t\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\t\tspawn(startDamageSequencePolling)\n\t\tend\n\tend\nend\n\nfunction sword:equip()\n\tisWithinSlash1Window \t= false\n\tisWithinSlash2Window \t= false\n\tisWithinDamageSequence \t= false\n\tisDamageSequenceEnabled = false\n\t--local\n\tmyClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif myClientCharacterContainer then\n\t\tcurrentWeaponManifest \t\t\t\t= network:invoke(\"getCurrentWeaponManifest\")\n\t\tanimationsForAnimationController \t= animationInterface:getAnimationsForAnimationController(myClientCharacterContainer.entity.AnimationController)\n\n\t--\tlocal grip = myClientCharacterContainer.entity:FindFirstChild(\"Grip\", true)\n\t--\tif grip then\n\t--\t\t-- force an update\n\t--\t\tonGripPropertyChanged(grip, \"Part1\")\n\t--\tend\n\tend\nend\n\nfunction sword:unequip()\n\nend\n\nlocal function onPropogationRequestToSelf(propogationNameTag, propogationValue)\n\tif propogationNameTag == \"abilities\" then\n\t\tplayerAbilitiesSlotDataCollection = propogationValue\n\n\t\tcanPlayerDoubleSlash = doesPlayerHaveAbilityUnlocked(3)\n\t\tcanPlayerTripleSlash = doesPlayerHaveAbilityUnlocked(3, \"tripleSlash\")\n\tend\nend\n\nlocal function main()\n\tonPropogationRequestToSelf(\"abilities\", network:invoke(\"getCacheValueByNameTag\", \"abilities\"))\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\tnetwork:connect(\"characterStateChanged\", \"Event\", onCharacterStateChanged)\nend\n\nmain()\n\nreturn sword"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/sword.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/damageInterfaces/swordAndShield.lua",
    "content": "local swordAndShield \t\t\t= {}\n\tswordAndShield.isEquipped = false\n\nlocal userInputService \t= game:GetService(\"UserInputService\")\nlocal httpService \t\t= game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\n\nlocal abilityAnimations = game:GetService(\"ReplicatedStorage\"):WaitForChild(\"abilityAnimations\")\n\n\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t\t= modules.load(\"network\")\n\t\tlocal utilities \t= modules.load(\"utilities\")\n\t\tlocal detection \t= modules.load(\"detection\")\n\t\tlocal placeSetup \t= modules.load(\"placeSetup\")\n\nlocal currentDamageGUID = httpService:GenerateGUID(false)\n\nlocal animationInterface = require(script.Parent.Parent.Parent:WaitForChild(\"contents\"):WaitForChild(\"animationInterface\"))--network:invoke(\"getPlayerCoreService\", \"animationInterface\")\n\n-- internal stuff specific to the swordAndShield\nlocal animationControllerLoaded\nlocal attackSequenceLength\nlocal animationsForAnimationController\n\nlocal slashAnimationConnection\n\nlocal isWithinSlash1Window \t\t= false\nlocal isWithinSlash2Window \t\t= false\nlocal isWithinDamageSequence \t= false\nlocal canPlayerDoubleSlash \t\t= false\nlocal canPlayerTripleSlash \t\t= false\n\nlocal currentWeaponManifest\nlocal myClientCharacterContainer\nlocal playerAbilitiesSlotDataCollection\n\nlocal player \t\t\t= game.Players.LocalPlayer\nlocal isPlayerSprinting = false\n\nlocal function onCharacterStateChanged(state, value)\n\tif state == \"isSprinting\" then\n\t\tisPlayerSprinting = value\n\tend\nend\n\nlocal function doesPlayerHaveAbilityUnlocked(abilityId)\n\tif playerAbilitiesSlotDataCollection then\n\t\tfor _, abilitySlotData in pairs(playerAbilitiesSlotDataCollection) do\n\t\t\tif abilitySlotData.id == abilityId and abilitySlotData.rank > 0 then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal isDamageSequenceEnabled = false\nlocal function startDamageSequencePolling()\n\tif isDamageSequenceEnabled then return end\n\tisDamageSequenceEnabled = true\n\n\twhile isDamageSequenceEnabled do\n\t\tif animationsForAnimationController.swordAndShieldAnimations.strike1.IsPlaying or animationsForAnimationController.swordAndShieldAnimations.strike2.IsPlaying or animationsForAnimationController.swordAndShieldAnimations.strike3.IsPlaying then\n\t\t\tif isWithinDamageSequence then\n\t\t\t\tnetwork:invoke(\"performClientDamageCycle\", \"equipment\", nil, currentDamageGUID)\n\t\t\tend\n\n\t\t\twait(1 / 20)\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\n\tisDamageSequenceEnabled = false\nend\n\nlocal function onSlashAnimationTrackStopped()\n\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\n\tif slashAnimationConnection then\n\t\tslashAnimationConnection:disconnect()\n\t\tslashAnimationConnection = nil\n\tend\n\n\tif slashAnimationKeyframeConnection then\n\t\tslashAnimationKeyframeConnection:disconnect()\n\t\tslashAnimationKeyframeConnection = nil\n\tend\n\n\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\tcurrentWeaponManifest.Trail.Enabled = false\n\tend\nend\n\n-- slash1PeriodStart\n-- slash2PeriodStart\n-- startDamageSequence\n-- stopDamageSequence\nlocal function onSlashAnimationKeyframeReached(keyframeName)\n\tif keyframeName == \"slash1PeriodStart\" then\n\t\tisWithinSlash1Window = true\n\t\tdelay(3 / 10, function()\n\t\t\tisWithinSlash1Window = false\n\t\tend)\n\telseif keyframeName == \"slash2PeriodStart\" then\n\t\tisWithinSlash2Window = true\n\t\tdelay(3 / 10, function()\n\t\t\tisWithinSlash2Window = false\n\t\tend)\n\telseif keyframeName == \"startDamageSequence\" then\n\n\t\tlocal swingSound = currentWeaponManifest:FindFirstChild(\"Swing\")\n\t\tif swingSound == nil then\n\t\t\tswingSound = Instance.new(\"Sound\")\n\t\t\tswingSound.Volume = 1\n\t\t\tswingSound.MaxDistance = 50\n\t\t\tswingSound.SoundId = \"rbxassetid://2069260907\"\n\t\t\tswingSound.Name = \"Swing\"\n\t\t\tswingSound.Parent = currentWeaponManifest\n\t\tend\n\n\t\tswingSound:Play()\n\t\tisWithinDamageSequence = true\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = true\n\t\tend\n\telseif keyframeName == \"stopDamageSequence\" then\n\t\tisWithinDamageSequence = false\n\n\t\tif currentWeaponManifest and currentWeaponManifest:FindFirstChild(\"Trail\") then\n\t\t\tcurrentWeaponManifest.Trail.Enabled = false\n\t\tend\n\tend\nend\n\nfunction swordAndShield:attack()\n\t-- make sure we can't slash if these conditions are true\n\n\tif not animationsForAnimationController or not animationsForAnimationController.swordAndShieldAnimations then\n\t\treturn\n\telseif isPlayerSprinting then\n\t\treturn\n\telseif not currentWeaponManifest then\n\t\treturn\n\telseif not player.Character or not player.Character.PrimaryPart or player.Character.PrimaryPart.state.Value == \"dead\" then\n\t\treturn\n\telseif animationsForAnimationController.swordAndShieldAnimations.strike1.IsPlaying and (not isWithinSlash2Window or not canPlayerDoubleSlash)then --or not canPlayerDoubleSlash\n\n\t\treturn\n\telseif animationsForAnimationController.swordAndShieldAnimations.strike2.IsPlaying and not isWithinSlash1Window then\n\t\treturn\n\telseif animationsForAnimationController.swordAndShieldAnimations.strike3.IsPlaying then\n\t\treturn\n\tend\n\n\t-- have to do it this way for now, no reference to ability animations  in animationsForAnimationController\n\tlocal animController = myClientCharacterContainer.entity.AnimationController\n\tfor i, track in pairs(animController:GetPlayingAnimationTracks()) do\n\t\tif track.Name == \"rock_throw_upper\" or track.Name == \"rock_throw_upper_loop\" then\n\t\t\treturn\n\t\tend\n\tend\n\n\tif animationsForAnimationController.swordAndShieldAnimations.strike1.IsPlaying and isWithinSlash2Window then\n\t\tif slashAnimationConnection then\n\t\t\tslashAnimationConnection:disconnect()\n\t\t\tslashAnimationConnection = nil\n\t\tend\n\n\t\tif slashAnimationKeyframeConnection then\n\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\tslashAnimationKeyframeConnection = nil\n\t\tend\n\n\t\tanimationsForAnimationController.swordAndShieldAnimations.strike1:Stop()\n\n\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.swordAndShieldAnimations.strike2.Stopped:connect(onSlashAnimationTrackStopped)\n\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.swordAndShieldAnimations.strike2.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\tanimationInterface:replicateClientAnimationSequence(\"swordAndShieldAnimations\", \"strike2\")\n\n\t\t-- start damage sequence\n\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\tspawn(startDamageSequencePolling)\n\telseif not animationsForAnimationController.swordAndShieldAnimations.strike1.IsPlaying and (not animationsForAnimationController.swordAndShieldAnimations.strike2.IsPlaying or isWithinSlash1Window) then\n\t\tif canPlayerTripleSlash and animationsForAnimationController.swordAndShieldAnimations.strike2.IsPlaying and animationsForAnimationController.swordAndShieldAnimations.strike2.TimePosition >= animationsForAnimationController.swordAndShieldAnimations.strike2.Length * 0.3 and animationsForAnimationController.swordAndShieldAnimations.strike2.TimePosition <= animationsForAnimationController.swordAndShieldAnimations.strike2.Length * 0.7 then\n\t\t\tif slashAnimationConnection then\n\t\t\t\tslashAnimationConnection:disconnect()\n\t\t\t\tslashAnimationConnection = nil\n\t\t\tend\n\n\t\t\tif slashAnimationKeyframeConnection then\n\t\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\t\tslashAnimationKeyframeConnection = nil\n\t\t\tend\n\n\t\t\tanimationsForAnimationController.swordAndShieldAnimations.strike2:Stop()\n\n\t\t\tslashAnimationConnection \t\t\t= animationsForAnimationController.swordAndShieldAnimations.strike3.Stopped:connect(onSlashAnimationTrackStopped)\n\t\t\tslashAnimationKeyframeConnection \t= animationsForAnimationController.swordAndShieldAnimations.strike3.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\t\tanimationInterface:replicateClientAnimationSequence(\"swordAndShieldAnimations\", \"strike3\")\n\n\t\t\t-- start damage sequence\n\t\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\t\tspawn(startDamageSequencePolling)\n\t\telse\n\t\t\tif slashAnimationConnection then\n\t\t\t\tslashAnimationConnection:disconnect()\n\t\t\t\tslashAnimationConnection = nil\n\t\t\tend\n\n\t\t\tif slashAnimationKeyframeConnection then\n\t\t\t\tslashAnimationKeyframeConnection:disconnect()\n\t\t\t\tslashAnimationKeyframeConnection = nil\n\t\t\tend\n\n\t\t\tanimationsForAnimationController.swordAndShieldAnimations.strike2:Stop()\n\n\t\t\tslashAnimationConnection = animationsForAnimationController.swordAndShieldAnimations.strike1.Stopped:connect(function()\n\t\t\t\tisWithinDamageSequence = false\n\t\t\t\tisWithinSlash1Window = false\n\t\t\t\tisWithinSlash2Window = false\n\t\t\tend)\n\n\t\t\tslashAnimationKeyframeConnection = animationsForAnimationController.swordAndShieldAnimations.strike1.KeyframeReached:connect(onSlashAnimationKeyframeReached)\n\n\t\t\tanimationInterface:replicateClientAnimationSequence(\"swordAndShieldAnimations\", \"strike1\")\n\n\t\t\t-- start damage sequence\n\t\t\tcurrentDamageGUID = httpService:GenerateGUID(false)\n\t\t\tspawn(startDamageSequencePolling)\n\t\tend\n\tend\nend\n\nfunction swordAndShield:equip()\n\tisWithinSlash1Window \t= false\n\tisWithinSlash2Window \t= false\n\tisWithinDamageSequence \t= false\n\tisDamageSequenceEnabled = false\n\t--local\n\tmyClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif myClientCharacterContainer then\n\t\tcurrentWeaponManifest \t\t\t\t= network:invoke(\"getCurrentWeaponManifest\")\n\t\tanimationsForAnimationController \t= animationInterface:getAnimationsForAnimationController(myClientCharacterContainer.entity.AnimationController)\n\n\t--\tlocal grip = myClientCharacterContainer.entity:FindFirstChild(\"Grip\", true)\n\t--\tif grip then\n\t--\t\t-- force an update\n\t--\t\tonGripPropertyChanged(grip, \"Part1\")\n\t--\tend\n\tend\nend\n\nfunction swordAndShield:unequip()\n\nend\n\nlocal function onPropogationRequestToSelf(propogationNameTag, propogationValue)\n\tif propogationNameTag == \"abilities\" then\n\t\tplayerAbilitiesSlotDataCollection = propogationValue\n\n\t\tcanPlayerDoubleSlash = doesPlayerHaveAbilityUnlocked(3)\n\t\tcanPlayerTripleSlash = doesPlayerHaveAbilityUnlocked(30)\n\tend\nend\n\nlocal function main()\n\tonPropogationRequestToSelf(\"abilities\", network:invoke(\"getCacheValueByNameTag\", \"abilities\"))\n\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\tnetwork:connect(\"characterStateChanged\", \"Event\", onCharacterStateChanged)\nend\n\nmain()\n\nreturn swordAndShield"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/defaultChestProps.lua",
    "content": "return {\n\tchestModel = \"defaultChest\"\t\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/assets/init.meta.json",
    "content": "{\n  \"ignoreUnknownInstances\": true\n}"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/client.client.lua",
    "content": "-- Master local script\nlocal modules = {}\n\nlocal player = game.Players.LocalPlayer\n\nlocal PlayerScripts = script.Parent\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal directories = {ReplicatedStorage.modules, PlayerScripts.contents}\n\nlocal beginInit = false\n\nlocal function setup(directory)\n    for _, moduleScript in pairs(directory:GetDescendants()) do\n        if moduleScript:IsA(\"ModuleScript\") then\n            print(\"$ client\", \"require module\", moduleScript.Name)\n            modules[moduleScript.Name] = require(moduleScript)\n        end\n    end\nend\n\nlocal function initialize()\n    for moduleName, module in pairs(modules) do\n        if typeof(module) == \"table\" and (module.init and not module.__initialized) then\n            print(\"$ client\", \"initialize module\", moduleName)\n            module.init(modules)\n            module.__initialized = true\n        end\n    end\nend\n\nfor _, directory in pairs(directories) do\n    -- Get all static modules\n    setup(directory)\n    -- Ongoing support\n    directory.DescendantAdded:connect(function(moduleScript)\n        if moduleScript:IsA(\"ModuleScript\") then\n            print(\"$ client\", \"require module\", moduleScript.Name)\n            local module = require(moduleScript)\n            modules[moduleScript.Name] = module\n            if typeof(module) == \"table\" and module.init and beginInit then\n                print(\"$ client\", \"initialize module\", moduleScript.Name)\n                module.init(modules)\n            end\n        end\n    end)\nend\n\ntable.sort(modules, function(module1, module2)\n    return (module1.priority or 10) < (module2.priority or 10)\nend)\n\nbeginInit = true\ninitialize()\n\n-- Gui has to be done seperately unless we disable reloading (which is a bad idea)\n-- Can't just use the DescendantAdded method either because init has to run after all requires\nlocal function onCharacterAdded()\n    setup(player.PlayerGui)\n    initialize()\nend\n\nplayer.CharacterAdded:connect(onCharacterAdded)\nlocal character = player.Character\nif character then\n    onCharacterAdded()\nend\nprint(\"$ client\", \"all modules in queue initialized\")"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/ambience.lua",
    "content": "local module = {}\n\n-- Manages sounds and some aesthetics.\n-- One of the first local scripts made for Vesteria. Not clean, needs improvements\n-- Author: berezaa\n\n\nif game.ReplicatedStorage:FindFirstChild(\"overrideAmbience\") then\n\treturn\nend\n\n\nlocal network\nlocal tween\n\nlocal userSettings\n\nlocal tracks = {}\nlocal dead\nlocal step = 1/5\nlocal lastUpdate\nlocal easing = Enum.EasingStyle.Linear\n\nlocal camera = workspace.CurrentCamera\n\n\nlocal function addTrack(track)\n\ttrack.Parent = camera\n\ttable.insert(tracks,track)\n\ttrack.Volume = 0\n\ttrack.Looped = true\nend\n\nlocal function mergeColors(dayColor, nightColor, Brightness)\n\tlocal dr, dg, db = Color3.toHSV(dayColor)\n\tlocal nr, ng, nb = Color3.toHSV(nightColor)\n\n\treturn Color3.fromHSV(nr + (dr - nr) * Brightness, ng + (dg - ng) * Brightness, nb + (db - nb) * Brightness)\nend\n\nlocal function lightingUpdate()\n\tlocal light = game.ReplicatedStorage:FindFirstChild(\"lightingSettings\")\n\n\tlocal dayAmbient = light and light:FindFirstChild(\"dayAmbient\") and light.dayAmbient.Value or (Color3.fromRGB(100, 100, 100))\n\tlocal nightAmbient = light and light:FindFirstChild(\"nightAmbient\") and light.nightAmbient.Value or Color3.fromRGB(50, 50, 100)\n\n\tlocal ClockTime = game.Lighting.ClockTime\n\tlocal Brightness = 0\n\t-- Night\n\tif ClockTime < 5.0 or ClockTime > 18.5 then\n\t\tBrightness = 0\n\t-- Sunrise\n\telseif ClockTime >= 5.0 and ClockTime <= 6.5 then\n\t\tlocal Progress = (ClockTime - 5.0) / 1.5\n\t\tBrightness = Progress\n\t-- Sunset\n\telseif ClockTime >= 17.5 and ClockTime <= 18.5 then\n\t\tlocal Progress = (ClockTime - 17.5)\n\t\tBrightness = 1 - Progress\n\t-- Day\n\telse\n\t\tBrightness = 1\n\tend\n\n\tif lastUpdate then\n\t\tstep = tick() - lastUpdate\n\tend\n\n\tlocal newTime = game.ReplicatedStorage.timeOfDay.Value\n\n\tif newTime < ClockTime then\n\t\tgame.Lighting.ClockTime = newTime\n\telse\n\t\ttween(game.Lighting, {\"ClockTime\"}, newTime, step, easing)\n\tend\n\n\n\tif Brightness ~= PreviousBrightness then\n\t\tlocal dayFogColor = light and light:FindFirstChild(\"dayFogColor\") and light.dayFogColor.Value or Color3.fromRGB(151, 213, 214)\n\t\tlocal nightFogColor = light and light:FindFirstChild(\"nightFogColor\") and light.nightFogColor.Value or Color3.fromRGB(0, 66, 120)\n\t\tlocal ambientColor = mergeColors(dayAmbient, nightAmbient, Brightness)\n\t\tlocal fogColor = mergeColors(dayFogColor, nightFogColor, Brightness)\n\t\ttween(game.Lighting, {\"Ambient\", \"FogColor\", \"ExposureCompensation\"}, {ambientColor, fogColor, Brightness}, step, easing)\n\t\ttween(game.Lighting.Atmosphere, {\"Density\", \"Color\", \"Haze\", \"Glare\"}, {0.438 - 0.164 * Brightness, fogColor, 2.15 - 2.15 * Brightness, 10 * Brightness}, step, easing)\n\tend\n\n\tBrightness = PreviousBrightness\n\tlastUpdate = tick()\nend\n\n\n--game.Lighting:GetPropertyChangedSignal(\"ClockTime\"):connect(lightingUpdate)\n\n\n\ngame.SoundService:GetPropertyChangedSignal(\"AmbientReverb\"):connect(function(Value)\n\tif game.SoundService.AmbientReverb == Enum.ReverbType.UnderWater then\n\t\tif game.SoundService:FindFirstChild(\"Underwater\") then\n\t\t\tfor i, track in pairs(tracks) do\n\t\t\t\ttrack.SoundGroup = game.SoundService.Underwater\n\n\t\t\tend\n\t\tend\n\telse\n\n\t\tfor i, track in pairs(tracks) do\n\t\t\ttrack.SoundGroup = nil\n\t\tend\n\tend\nend)\n\nlocal currentTrack = \"\"\n\nlocal musicVolume = 0.5\n\n\nlocal function setMusicVolume(volume)\n\tmusicVolume = 1 * volume\n\n\tfor i,track in pairs(tracks) do\n\t\tif track.Name == currentTrack then\n\t\t\ttrack.Volume = musicVolume * 0.27\n\t\tend\n\tend\nend\n\nlocal function playTrack(trackName)\n\tif currentTrack ~= trackName then\n\t\tcurrentTrack = trackName\n\t\tfor _, track in pairs(tracks) do\n\t\t\tif track.Name == trackName then\n\n\t\t\t\t\ttrack:Play()\n\t\t\t\t\ttrack.Volume = musicVolume * 0.27\n\t\t\telseif track.Volume > 0 then\n\n\t\t\t\t\ttrack:Stop()\n\t\t\t\t\ttrack.Volume = 0\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal overriden = false\n\nlocal function overrideTrack(track)\n\tif not overriden then\n\t\toverriden = true\n\t\taddTrack(track)\n\t\tplayTrack(track.Name)\n\tend\nend\n\nif game.ReplicatedStorage:FindFirstChild(\"backgroundMusic\") then\n\toverrideTrack(game.ReplicatedStorage.backgroundMusic)\nelse\n\tplayTrack(\"Village\")\nend\ngame.ReplicatedStorage.ChildAdded:connect(function(child)\n\tif child.Name == \"backgroundMusic\" then\n\t\toverrideTrack(child)\n\tend\nend)\n\n\nlocal noise = Instance.new(\"Sound\")\nnoise.Parent = script\nnoise.Volume = 0.1\nnoise.Looped = true\nnoise.Name = \"noise\"\n\nlocal function setNoise(soundId)\n\tif noise.SoundId ~= soundId then\n\t\tnoise:Stop()\n\t\tnoise.SoundId = soundId\n\t\tnoise:Play()\n\tend\nend\n\nlocal function backgroundNoise()\n\tif game.PlaceId == 3232913902 or game.PlaceId == 2544075708 then return end -- crabby den and shiprock bottom. no crickets at these places\n\n\tif game.Lighting.ClockTime <= 6.5 or game.Lighting.ClockTime >= 18 then\n\t\tsetNoise(\"rbxassetid://\"..2049803364)\n\t\tnoise.Volume = 0.27\n\telse\n\t\tif workspace:FindFirstChild(\"forest\") then\n\t\t\tsetNoise(\"rbxassetid://\"..2050179392)\n\t\t\tnoise.Volume = 0.4\n\t\telse\n\t\t\tsetNoise(\"rbxassetid://\"..2050176819)\n\t\t\tnoise.Volume = 0.75\n\t\tend\n\tend\nend\n\n\n\n\nlocal function onDataUpdate(key, value)\n\tif key == \"userSettings\" then\n\t\tuserSettings = value\n\t\tsetMusicVolume(value.musicVolume or 0.5)\n\tend\nend\n\n\nlocal function isNight()\n\treturn game.Lighting.ClockTime < 5.9 or game.Lighting.ClockTime > 18.6\nend\n\n-- Sunrise: 5.6 - 6.6\n-- Sunset: 17.6 - 18.6\nlocal function timeOfDayPitch()\n\tif game.Lighting.ClockTime < 5.9 or game.Lighting.ClockTime > 18.6 then\n\t\treturn 1--0.8\n\telseif game.Lighting.ClockTime >= 5.6 and game.Lighting.ClockTime <= 6.6 then\n\t\treturn 1--0.8 + 0.2 * (game.Lighting.ClockTime - 5.6)\n\telseif game.Lighting.ClockTime >= 17.6 and game.Lighting.ClockTime <= 18.6 then\n\t\treturn 1-- - 0.2 * (game.Lighting.ClockTime - 17.6)\n\telse\n\t\treturn 1\n\tend\nend\n\n\nlocal function setIsDead(isDead)\n\tdead = isDead\n\tfor i,track in pairs(tracks) do\n\t\ttrack.PlaybackSpeed = (dead and 0.4 or timeOfDayPitch())\n\tend\nend\n\nlocal function main()\n\tworkspace:WaitForChild(\"Camera\")\n\tlocal assetFolder = script.Parent.Parent:WaitForChild(\"assets\")\n\tcamera = workspace.CurrentCamera\n\tfor _, child in pairs(assetFolder.tracks:GetChildren()) do\n\t\taddTrack(child)\n\tend\n\tif userSettings.musicVolume then\n\t\tsetMusicVolume(userSettings.musicVolume or 0.5)\n\tend\n\tgame.ReplicatedStorage.timeOfDay.Changed:connect(lightingUpdate)\n\tassetFolder.tracks.ChildAdded:Connect(addTrack)\n\tlightingUpdate()\n\tbackgroundNoise()\n\tgame.Lighting:GetPropertyChangedSignal(\"ClockTime\"):connect(backgroundNoise)\n\twhile wait(1) do\n\t\tfor i,track in pairs(tracks) do\n\t\t\ttrack.PlaybackSpeed = (dead and 0.4 or timeOfDayPitch())\n\t\tend\n\tend\nend\n\n\nfunction module.init(Modules)\n\tnetwork = Modules.network\n\ttween = Modules.tween\n\n\tuserSettings = network:invoke(\"getCacheValueByNameTag\", \"userSettings\")\n\n\tnetwork:create(\"musicVolumeChanged\", \"BindableEvent\", \"Event\", setMusicVolume)\n\tnetwork:create(\"ambienceSetIsDead\", \"BindableFunction\", \"OnInvoke\", setIsDead)\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onDataUpdate)\n\n\tspawn(main)\nend\n\nreturn module"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/animationInterface.lua",
    "content": "-- Author: Polymorphic\n\nlocal module = {}\n\tmodule.clientAnimations = {}\n\nlocal assetFolder = script.Parent.Parent:WaitForChild(\"assets\")\n\n-- module requires module!!!!!!! todo: maybe change this into a service?\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage:WaitForChild(\"modules\"))\n\t\tlocal network = modules.load(\"network\")\nlocal characterAnimations = replicatedStorage:WaitForChild(\"characterAnimations\")\n\nlocal animationsContainer \t\t\t= {}\nlocal availableAnimationSequences \t= {}\n\nlocal rawAnimationData = {} do\n\tfor i, animationDataCollectionModule in pairs(assetFolder.animations:GetChildren()) do\n\t\tlocal animationDataCollection = require(animationDataCollectionModule)\n\n\t\tfor animationName, animationData in pairs(animationDataCollection) do\n\t\t\tif animationData.animationId and not animationData.animationId_2 then\n\t\t\t\tlocal animation \t\t= Instance.new(\"Animation\")\n\t\t\t\tanimation.AnimationId \t= animationData.animationId\n\t\t\t\tanimation.Name \t\t\t= animationName\n\n\t\t\t\tif not characterAnimations:FindFirstChild(animationName) then\n\t\t\t\t\tanimation.Parent = characterAnimations\n\t\t\t\tend\n\n\t\t\t\tanimationData.animation = animation\n\t\t\telseif animationData.animationId and animationData.animationId_2 then\n\t\t\t\tlocal animation \t\t= Instance.new(\"Animation\")\n\t\t\t\tanimation.AnimationId \t= animationData.animationId\n\t\t\t\tanimation.Name \t\t\t= animationName\n\n\t\t\t\tlocal animation_2 \t\t= Instance.new(\"Animation\")\n\t\t\t\tanimation_2.AnimationId = animationData.animationId_2\n\t\t\t\tanimation_2.Name \t\t= animationName .. \"_2\"\n\n\t\t\t\tif not characterAnimations:FindFirstChild(animation.Name) then\n\t\t\t\t\tanimation.Parent = characterAnimations\n\t\t\t\tend\n\n\t\t\t\tif not characterAnimations:FindFirstChild(animation_2.Name) then\n\t\t\t\t\tanimation_2.Parent = characterAnimations\n\t\t\t\tend\n\n\t\t\t\tanimationData.animation = {animation; animation_2}\n\t\t\tend\n\t\tend\n\n\t\trawAnimationData[animationDataCollectionModule.Name] = animationDataCollection\n\tend\nend\n\nlocal function getSingleAnimation(animationController, animationGroup, animationName)\n\tif rawAnimationData[animationGroup] and rawAnimationData[animationGroup][animationName] then\n\t\tlocal animationData = rawAnimationData[animationGroup][animationName]\n\n\t\tif typeof(animationData.animation) == \"Instance\" then\n\t\t\tlocal animationTrack \t= animationController:LoadAnimation(animationData.animation)\n\t\t\tanimationTrack.Priority = animationData.priority or Enum.AnimationPriority.Movement\n\t\t\tanimationTrack.Looped \t= animationData.looped or false\n\t\t\tanimationTrack.Name \t= animationName\n\n\t\t\tanimationTrack:AdjustSpeed(animationData.speed or 1)\n\n\t\t\treturn animationTrack\n\t\telseif typeof(animationData.animation) == \"table\" then\n\t\t\tlocal animationTrack \t= animationController:LoadAnimation(animationData.animation[1])\n\t\t\tanimationTrack.Priority = animationData.priority or Enum.AnimationPriority.Movement\n\t\t\tanimationTrack.Looped \t= animationData.looped or false\n\t\t\tanimationTrack.Name \t= animationName\n\n\t\t\tlocal animationTrack_2 \t\t= animationController:LoadAnimation(animationData.animation[2])\n\t\t\tanimationTrack_2.Priority \t= --[[animationData.priority_2 or]] animationData.priority or Enum.AnimationPriority.Movement\n\t\t\tanimationTrack_2.Looped \t= animationData.looped or false\n\t\t\tanimationTrack_2.Name \t\t= animationName\n\n\t\t\tanimationTrack:AdjustSpeed(animationData.speed or 1)\n\n\t\t\treturn {animationTrack; animationTrack_2}\n\t\tend\n\tend\n\n\treturn nil\nend\n\nlocal function getSingleAnimationCluster(animationsName, animationController)\n\tlocal animationTable = {}\n\n\tfor animationName, animationData in pairs(rawAnimationData[animationsName]) do\n\t\tif typeof(animationData.animation) == \"Instance\" then\n\t\t\tlocal animationTrack \t= animationController:LoadAnimation(animationData.animation)\n\t\t\tanimationTrack.Priority = animationData.priority or Enum.AnimationPriority.Movement\n\t\t\tanimationTrack.Looped \t= animationData.looped or false\n\t\t\tanimationTrack.Name \t= animationName\n\n\t\t\tanimationTrack:AdjustSpeed(animationData.speed or 1)\n\n\t\t\tanimationTable[animationName] = animationTrack\n\t\telseif typeof(animationData.animation) == \"table\" then\n\t\t\tlocal animationTrack \t= animationController:LoadAnimation(animationData.animation[1])\n\t\t\tanimationTrack.Priority = animationData.priority or Enum.AnimationPriority.Movement\n\t\t\tanimationTrack.Looped \t= animationData.looped or false\n\t\t\tanimationTrack.Name \t= animationName\n\n\t\t\tlocal animationTrack_2 \t= animationController:LoadAnimation(animationData.animation[2])\n\t\t\tanimationTrack_2.Priority = --[[animationData.priority_2 or]] animationData.priority or Enum.AnimationPriority.Movement\n\t\t\tanimationTrack_2.Looped \t= animationData.looped or false\n\t\t\tanimationTrack_2.Name \t= animationName\n\n\t\t\tanimationTrack:AdjustSpeed(animationData.speed or 1)\n\t\t\tanimationTrack_2:AdjustSpeed(animationData.speed or 1)\n\n\t\t\tanimationTable[animationName] = {animationTrack; animationTrack_2}\n\t\tend\n\tend\n\n\treturn animationTable\nend\n\nfunction module:getAnimationsForAnimationController(animationController, ...)\n\tlocal animationTable \t= {}\n\tlocal animationsWanted \t= { ... }\n\n\tfor i, animationDataCollectionName in pairs(animationsWanted) do\n\t\tif rawAnimationData[animationDataCollectionName] then\n\t\t\tanimationTable[animationDataCollectionName] = getSingleAnimationCluster(animationDataCollectionName, animationController)\n\t\tend\n\tend\n\n\treturn animationTable\nend\n\nfunction module:registerAnimationsForAnimationController(animationController, ...)\n\tlocal animationTable \t= {}\n\tlocal animationsWanted \t= { ... }\n\n\tfor i, animationDataCollectionName in pairs(animationsWanted) do\n\t\tif rawAnimationData[animationDataCollectionName] then\n\t\t\tanimationTable[animationDataCollectionName] = getSingleAnimationCluster(animationDataCollectionName, animationController)\n\t\tend\n\tend\n\n\tanimationsContainer[animationController] = animationTable\n\n\treturn animationTable\nend\n\nfunction module:getAnimationsForAnimationController(animationController)\n\treturn animationsContainer[animationController]\nend\n\nfunction module:deregisterAnimationsForAnimationController(animationController)\n\tif animationController then\n\t\tanimationsContainer[animationsContainer] = nil\n\tend\nend\n\nfunction module:stopPlayingAnimationsByAnimationCollectionName(animationTable, animationCollectionName)\n\tfor i, animationTrack in pairs(animationTable[animationCollectionName]) do\n\t\tif typeof(animationTrack) == \"Instance\" then\n\t\t\tanimationTrack:Stop()\n\t\telseif typeof(animationTrack) == \"table\" then\n\t\t\tfor ii, obj in pairs(animationTrack) do\n\t\t\t\tobj:Stop()\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction module:stopPlayingAnimationsByAnimationCollectionNameWithException(animationTable, animationCollectionName, exception)\n\tif animationTable[animationCollectionName] then\n\t\tfor i, animationTrack in pairs(animationTable[animationCollectionName]) do\n\t\t\tif typeof(animationTrack) == \"Instance\" then\n\t\t\t\tif not exception or animationTrack.Name ~= exception then\n\t\t\t\t\tanimationTrack:Stop()\n\t\t\t\tend\n\t\t\telseif typeof(animationTrack) == \"table\" then\n\t\t\t\tif not exception or animationTrack[1].Name ~= exception then\n\t\t\t\t\tfor ii, obj in pairs(animationTrack) do\n\t\t\t\t\t\tobj:Stop()\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction module:replicateNonPlayerAnimationSequence(animationCollectionName, animationName, callback, extraData)\n\tif module.clientAnimations[animationCollectionName] and module.clientAnimations[animationCollectionName][animationName] then\n\t\tself:stopPlayingAnimationsByAnimationCollectionName(module.clientAnimations, \"emoteAnimations\")\n\n\t\tnetwork:fire(\"playNonPlayerAnimationSequence\", animationCollectionName, animationName)\n\n\t\tif callback and typeof(module.clientAnimations[animationCollectionName][animationName]) == \"Instance\" then\n\t\t\tlocal connection\n\n\t\t\tconnection = module.clientAnimations[animationCollectionName][animationName].Stopped:connect(function()\n\t\t\t\tconnection:disconnect()\n\n\t\t\t\tcallback()\n\t\t\tend)\n\t\tend\n\tend\n\n\tif rawAnimationData[animationCollectionName] and rawAnimationData[animationCollectionName][animationName] then\n\t\tnetwork:fireServer(\"replicateNonPlayerAnimationSequence\", animationCollectionName, animationName, extraData)\n\tend\nend\n\nfunction module:replicatePlayerAnimationSequence(animationCollectionName, animationName, callback, extraData)\n\t-- todo: remove all calls so no more redirect?\n\tmodule:replicateClientAnimationSequence(animationCollectionName, animationName, callback, extraData)\nend\n\nlocal ATTACK_SPEED_ANIMATION_COLLECTION_NAMES = {\n\t\"swordAnimations\",\n\t\"staffAnimations\",\n\t\"daggerAnimations\",\n\t\"bowAnimations\",\n\t\"greatswordAnimations\",\n\t\"dualAnimations\",\n\t\"swordAndShieldAnimations\",\n\t\"axeAnimations\",\n\t\"pickaxeAnimations\"\n}\nlocal function isAttackSpeedAnimationCollection(name)\n\tfor _, attackSpeedAnimationCollectionName in pairs(ATTACK_SPEED_ANIMATION_COLLECTION_NAMES) do\n\t\tif attackSpeedAnimationCollectionName == name then\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\nfunction module:replicateClientAnimationSequence(animationCollectionName, animationName, callback, extraData)\n\tif rawAnimationData[animationCollectionName] and rawAnimationData[animationCollectionName][animationName] then\n\t\tif isAttackSpeedAnimationCollection(animationCollectionName) then\n\t\t\textraData = extraData or {}\n\t\t\t\textraData.attackSpeed = network:invoke(\"getCacheValueByNameTag\", \"nonSerializeData\").statistics_final.attackSpeed or 0\n\n\t\tend\n\n\t\tnetwork:fire(\"playPlayerAnimationSequenceOnClientCharacter\", animationCollectionName, animationName, extraData)\n\n\t\tnetwork:fireServer(\"replicatePlayerAnimationSequence\", animationCollectionName, animationName, extraData)\n\tend\nend\n\nfunction module:getPlayingAnimationTracks()\n\tlocal myClientPlayerCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif myClientPlayerCharacterContainer then\n\t\treturn myClientPlayerCharacterContainer.entity.AnimationController:GetPlayingAnimationTracks()\n\tend\n\n\treturn {}\nend\n\nlocal function regenerateClientAnimations(myClientPlayerCharacterContainer)\n\tlocal animationController = (myClientPlayerCharacterContainer or network:invoke(\"getMyClientCharacterContainer\")).entity.AnimationController\n\n\tmodule.clientAnimations = {}\n\tfor animationDataCollectionName, animationCollection in pairs(rawAnimationData) do\n\n\t\t-- do not automatically register this animation\n\t\tif not string.match(animationDataCollectionName, \"_noChar\") then\n\t\t\tmodule.clientAnimations[animationDataCollectionName] = getSingleAnimationCluster(animationDataCollectionName, animationController)\n\t\tend\n\tend\nend\n\nlocal function main()\n\tmodule.getSingleAnimation \t\t\t= getSingleAnimation\n\tmodule.getSingleAnimationCluster \t= getSingleAnimationCluster\n\tmodule.rawAnimationData \t\t\t= rawAnimationData\n\n\t-- generate animationData for player\n\tlocal myClientPlayerCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\tif myClientPlayerCharacterContainer then\n\t\tregenerateClientAnimations(myClientPlayerCharacterContainer)\n\tend\n\n\tspawn(function()\n\t\tnetwork:connect(\"myClientCharacterContainerChanged\", \"Event\", regenerateClientAnimations)\n\tend)\nend\n\nspawn(main)\n\nreturn module"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/bolt.lua",
    "content": "-- Funny bolt ripped from magic missile\n-- I want to use this for EXP orbs when you kill a monster\nlocal bolt = {}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal runService = game:GetService(\"RunService\")\nlocal modules = require(replicatedStorage:WaitForChild(\"modules\"))\nlocal placeSetup = modules.load(\"placeSetup\")\nlocal network = modules.load(\"network\")\nlocal levels = modules.load(\"levels\")\nlocal tween = modules.load(\"tween\")\n\nlocal spawnRegionCollectionsFolder = placeSetup.getPlaceFolder(\"spawnRegionCollections\")\nlocal entityManifestCollectionFolder = placeSetup.getPlaceFolder(\"entityManifestCollection\")\nlocal entityRenderCollectionFolder = placeSetup.getPlaceFolder(\"entityRenderCollection\")\nlocal itemsFolder = placeSetup.getPlaceFolder(\"items\")\nlocal entitiesFolder = placeSetup.getPlaceFolder(\"entities\")\nlocal foilage = placeSetup.getPlaceFolder(\"foilage\")\n\nlocal RAYCAST_IGNORE_LIST = {\n\tspawnRegionCollectionsFolder,\n\tentityManifestCollectionFolder,\n\tentityRenderCollectionFolder,\n\titemsFolder,\n\tentitiesFolder,\n\tfoilage\n}\n\nlocal rand = Random.new()\n\n\nlocal assets = replicatedStorage:WaitForChild(\"assets\")\nlocal entities = assets:WaitForChild(\"entities\")\n\nfunction bolt.fire(launchCFrame, target, size)\n\t-- constants for the function\n    local contactDistanceSq = 1\n\tlocal missileTemplate = entities:FindFirstChild(\"missile\")\n    assert(missileTemplate, \"missile for exp bolts not found\")\n\t-- create a missile and keep track of it\n    local missile = missileTemplate:Clone()\n\n    missile.Size = Vector3.new(1, 1, 1) * size\n    missile.top.Position = Vector3.new(0, -size/2, 0)\n    missile.bottom.Position = Vector3.new(0, size/2, 0)\n\n\tmissile.CFrame = launchCFrame * missile.alignAttachment.CFrame:Inverse()\n\n\tlocal mover = missile.mover\n    local orientationAttachment = missile.orientationAttachment\n    local align = missile.alignOrientation\n\tlocal trail = missile.trail\n\n\torientationAttachment.CFrame = launchCFrame\n\torientationAttachment.Parent = workspace.Terrain\n\tmissile.Parent = entitiesFolder\n\n\t-- this data is used to update\n\t-- what the missile does in flight\n\tlocal boltData = {\n\t\tspeed = 15,\n\t\tmissile = missile,\n\t\ttarget = target,\n\t\tstartTime = tick(),\n\t\t-- which way the missile will drift while searching for a target\n\t\tdriftCFrame = CFrame.Angles(math.pi * 2 * rand:NextNumber(), 0, math.pi * 2 * rand:NextNumber()),\n    }\n\n    local offset = Vector3.new(rand:NextNumber() - 0.5, rand:NextNumber() - 0.5, rand:NextNumber() - 0.5) * 2\n\n    local connection\n    local finished\n\n\n    local function finish(wasSuccess)\n        if not finished then\n            finished = true\n            missile.Anchored = true\n            trail.Enabled = false\n            tween(missile, {\"Transparency\", \"Size\"}, {1, missile.Size * 5}, 0.7)\n            delay(0.7, function()\n                connection:Disconnect()\n                orientationAttachment:Destroy()\n                missile:Destroy()\n            end)\n        end\n\tend\n\n\tlocal function checkForCollision()\n\t\tlocal origin = missile.Position\n\t\tlocal direction = missile.CFrame.LookVector/2\n\t\tlocal ray = Ray.new(origin, direction)\n\t\tlocal part = workspace:FindPartOnRayWithIgnoreList(ray, RAYCAST_IGNORE_LIST, false, true)\n\t\treturn part ~= nil and part.CanCollide\n\tend\n\n\t-- run every frame while the missile is airborne\n\tlocal function update(dt)\n        -- move in the direction we're facing\n        if finished then\n            missile.CFrame = CFrame.new(boltData.target.Position + offset)\n        else\n            mover.Velocity = (missile.CFrame * missile.alignAttachment.CFrame).LookVector * boltData.speed\n            -- accelerate!\n            boltData.speed = boltData.speed + (dt or 0) * 15\n            align.MaxAngularVelocity = align.MaxAngularVelocity + (dt or 0) * 7\n\n\n            -- if we have a target, rotate towards it and run logic if we hit\n\n            local directionCFrame = CFrame.new(missile.Position, boltData.target.Position + offset)\n            orientationAttachment.CFrame = directionCFrame\n\n            local delta = (boltData.target.Position + offset) - missile.Position\n            local distanceSq =\n                delta.X * delta.X +\n                delta.Y * delta.Y +\n                delta.Z * delta.Z\n\n            local size = boltData.target.Size / 4\n            local sizeSq =\n                size.X * size.X +\n                size.Y * size.Y +\n                size.Z * size.Z\n\n            if distanceSq <= math.max(contactDistanceSq, sizeSq) then\n                finish(true)\n            end\n\n            -- if we hit something we're done\n            if checkForCollision() then\n                finish(false)\n            end\n        end\n\n\tend\n\n    connection = runService.Heartbeat:Connect(update)\n\tupdate()\n\n\t-- failsafe\n\tdelay(5, function()\n\t\tfinish(false)\n\tend)\n\n\treturn boltData\nend\n\nnetwork:connect(\"signal_exp\", \"OnClientEvent\", function(EXPTable, sourcePart)\n    local myCharacter = game.Players.LocalPlayer.Character\n\n    myCharacter = myCharacter and myCharacter.PrimaryPart\n    if myCharacter then\n\n        for playerName, EXP in pairs(EXPTable) do\n            local player = game.Players:FindFirstChild(playerName)\n            local character = player.Character and player.Character.PrimaryPart\n\n            if character and ((character == myCharacter) or (character.Position - myCharacter.Position).magnitude <= 150) then\n\n                local level = player.level.Value\n                local needed = levels.getEXPToNextLevel(level)\n                local bolts = math.ceil((EXP / needed) * (6 + level * 3))\n                for _ = 1, bolts do\n                    delay(rand:NextNumber() * 0.5, function()\n                        local origin = sourcePart.Position\n                        local offset = Vector3.new(3 * (rand:NextNumber() - 0.5), 5, 3 * (rand:NextNumber() - 0.5))\n                        local lookat = sourcePart.Position + offset\n                        bolt.fire(CFrame.new(origin, lookat), character, 0.3 * EXP ^ (1/3))\n                    end)\n                end\n            end\n        end\n    end\nend)\n\nreturn bolt"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/cameraScript.lua",
    "content": "local module = {}\n-- Vesteria custom camera to support custom characters\n-- Authors: Polymorphic, berezaa\n\nlocal RunService = game:GetService(\"RunService\")\nlocal camera = workspace.CurrentCamera\nlocal UserInputService = game:GetService(\"UserInputService\")\n\nlocal player = game.Players.LocalPlayer\nlocal character\n\nlocal zoom = 10\ngame.Players.LocalPlayer.CameraMinZoomDistance = 5\n\n\nlocal network\nlocal tween\nlocal camera_shaker\n\nlocal ddy = 0\nlocal ddx = 0\n\nlocal mobileCameraRotation\n\nlocal xRotation = -math.rad(20)\nlocal yRotation = -math.rad(20)\n\nlocal maxYRotation = math.rad(90)\nlocal maxXRotation = math.rad(80)\n\nlocal fullRotation = math.rad(720)\n\nlocal zoomIncrement = 3\n\nlocal overridden = false\nlocal IS_PLAYER_CAMERA_LOCKED = false\n\nlocal function raycastDownIgnoreCancollideFalse(ray, ignoreList)\n\tlocal hitPart, hitPosition, hitDown, hitMaterial = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList, true)\n\n\tlocal items = workspace.placeFolders:FindFirstChild(\"items\")\n\n\t-- edit: ignore water and dropped items\n\twhile hitPart and not (hitPart.CanCollide and hitMaterial ~= Enum.Material.Water and (items == nil or not hitPart:IsDescendantOf(items))) do\n\t\tignoreList[#ignoreList + 1] = hitPart\n\t\thitPart, hitPosition, hitDown, hitMaterial = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList, true)\n\tend\n\n\treturn hitPart, hitPosition, hitDown, hitMaterial\nend\n\n-------\nlocal zoomCFrame = CFrame.new(0, 0, zoom)\n\nlocal Par = workspace.CurrentCamera\n\nif game.PlaceId == 2376885433 or game.PlaceId == 2015602902 or game.PlaceId == 4623219432 then\n\tPar = game.ReplicatedStorage\nend\n\nlocal cameraCFrameVal = Instance.new(\"CFrameValue\")\ncameraCFrameVal.Name = \"CFrameValue\"\ncameraCFrameVal.Parent = Par\n\nlocal lockTarget\n\n\nlocal rotationLocked\n\nlocal function lockCameraTarget(target)\n\tlockTarget = target\n\trotationLocked = false\n\tif target then\n\t\tif workspace.CurrentCamera:FindFirstChild(\"overridden\") == nil then\n\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\ttag.Name = \"overridden\"\n\t\t\ttag.Parent = workspace.CurrentCamera\n\t\tend\n\telse\n\t\tif workspace.CurrentCamera:FindFirstChild(\"overridden\") then\n\t\t\tworkspace.CurrentCamera.overridden:Destroy()\n\t\tend\n\tend\nend\n\nlocal function lockCameraTargetWithOrientation(target, xRotate, yRotate, zoomCf)\n\tlockTarget = target\n\trotationLocked = false\n\tif target then\n\t\tif workspace.CurrentCamera:FindFirstChild(\"overridden\") == nil then\n\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\ttag.Name = \"overridden\"\n\t\t\ttag.Parent = workspace.CurrentCamera\n\t\tend\n\t\trotationLocked = true\n\t\txRotation = xRotate or xRotation\n\t\tyRotation = yRotate or yRotation\n\t\tzoomCFrame = zoomCf or zoomCFrame\n\telse\n\t\tif workspace.CurrentCamera:FindFirstChild(\"overridden\") then\n\t\t\tworkspace.CurrentCamera.overridden:Destroy()\n\t\tend\n\tend\nend\n\n\nlocal function updateCamera(step)\n\tif not character or not character.PrimaryPart then return end\n\tlocal characterCFrame do\n\t\tif IS_PLAYER_CAMERA_LOCKED then\n\t\t\tcharacterCFrame = CFrame.new(character.PrimaryPart.Position) + Vector3.new(0, 0.25 + 0.05 * zoom, 0) + (CFrame.new(Vector3.new(), camera.CFrame.RightVector) * CFrame.Angles(0, math.rad(10), 0)).lookVector * 1.75\n\t\telse\n\t\t\tcharacterCFrame = CFrame.new(character.PrimaryPart.Position) + Vector3.new(0, 0.25 + 0.05 * zoom, 0)\n\t\tend\n\tend\n\n\tif lockTarget then\n\t\tcharacterCFrame = CFrame.new(lockTarget.Position) + Vector3.new(0,1,0)\n\tend\n\n\tif not overridden then\n\t\tlocal intendedCFrame = (characterCFrame * CFrame.Angles(0, yRotation, 0) * CFrame.Angles(xRotation, 0, 0)) * zoomCFrame\n\n\t\tlocal ignoreList = {workspace.CurrentCamera}\n\t\tif workspace:FindFirstChild(\"placeFolders\") then\n\t\t\ttable.insert(ignoreList, workspace.placeFolders)\n\t\tend\n\n\n\t\tlocal direction = intendedCFrame.Position - characterCFrame.Position\n\n\t\tlocal ray = Ray.new(characterCFrame.Position, direction)\n\t\t--local hitPart, hitPosition = game.Workspace:FindPartOnRayWithIgnoreList(ray,ignoreList,false,true)\n\t\tlocal hitPart, hitPosition = raycastDownIgnoreCancollideFalse(ray, ignoreList)\n\n\t\t--tween(camera,{\"CFrame\"},intendedCFrame,0.1)\n\t\tcameraCFrameVal.Value = CFrame.new(hitPosition - direction.unit, characterCFrame.p)\n\tend\n\n\tcamera.Focus = camera.CFrame\nend\n\nlocal function lockCamera(cf, duration, easeStyle)\n\tif cf then\n\t\toverridden = true\n\t\tif duration then\n\t\t\ttween(cameraCFrameVal,{\"Value\"},cf,duration,easeStyle)\n\t\telse\n\t\t\tcameraCFrameVal.Value = cf\n\t\tend\n\t\tif workspace.CurrentCamera:FindFirstChild(\"overridden\") == nil then\n\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\ttag.Name = \"overridden\"\n\t\t\ttag.Parent = workspace.CurrentCamera\n\t\tend\n\n\telse\n\t\tif workspace.CurrentCamera:FindFirstChild(\"overridden\") then\n\t\t\tworkspace.CurrentCamera.overridden:Destroy()\n\t\tend\n\t\toverridden = false\n\tend\nend\n\nlocal primarycamera_shaker\n\nlocal function cameraShake(preset)\n\tlocal camShake = primarycamera_shaker\n\tif preset == nil then\n\t\tcamShake:Shake(camera_shaker.Presets.Explosion)\n\telseif preset == \"bump\" then\n\t\tcamShake:Shake(camera_shaker.Presets.Bump)\n\tend\nend\n\n\n\n\n-- at the moment can only be played during a cutscene\nlocal function lockCameraWithCameraShake(cf, duration, timeUntilExplosion, easeStyle, preset, explodeDuration)\n\n\tif cf then\n\t\toverridden = true\n\t\tif duration then\n\t\t\ttween(camera,{\"CFrame\"},cf,duration,easeStyle)\n\t\t\tspawn(function()\n\n\t\t\t\twait(timeUntilExplosion)\n\t\t\t\t--[[\n\t\t\t\tlocal camShake = camera_shaker.new(Enum.RenderPriority.Camera.Value, function(shakeCf)\n\t\t\t\t\tcamera.CFrame = cf * shakeCf\n\t\t\t\tend)\n\t\t\t\tcamShake:Start()\n\t\t\t\t]]\n\n\t\t\t\tlocal camShake = primarycamera_shaker\n\n\t\t\t\tif preset == nil then\n\t\t\t\t\tcamShake:Shake(camera_shaker.Presets.Explosion)\n\t\t\t\telseif preset == \"bump\" then\n\t\t\t\t\tcamShake:Shake(camera_shaker.Presets.Bump)\n\t\t\t\tend\n\t\t\t\t--[[\n\t\t\t\twait(explodeDuration or 4)\n\t\t\t\tcamShake:Stop()\n\t\t\t\t]]\n\t\t\tend)\n\t\telse\n\t\t\tcameraCFrameVal.Value = cf\n\t\tend\n\t\tif workspace.CurrentCamera:FindFirstChild(\"overridden\") == nil then\n\t\t\tlocal tag = Instance.new(\"BoolValue\")\n\t\t\ttag.Name = \"overridden\"\n\t\t\ttag.Parent = workspace.CurrentCamera\n\t\tend\n\n\telse\n\t\tif workspace.CurrentCamera:FindFirstChild(\"overridden\") then\n\t\t\tworkspace.CurrentCamera.overridden:Destroy()\n\t\tend\n\t\toverridden = false\n\tend\n\n\nend\n\n\n\nlocal lockOrigin\n\n\nlocal function onInputBegan(inputObject, Absorbed)\n\n\n\tif Absorbed then\n\t\treturn false\n\tend\n\n\tif inputObject.KeyCode == Enum.KeyCode.ButtonR3 then\n\t\tzoom = zoom - 7\n\t\tif zoom < player.CameraMinZoomDistance then\n\t\t\tzoom = player.CameraMaxZoomDistance\n\t\tend\n\t\tif not rotationLocked then\n\t\t\tzoomCFrame = CFrame.new(0, 0, zoom)\n\t\tend\n\tend\n\n\tif inputObject.UserInputType == Enum.UserInputType.MouseButton2 then\n\t\tlockOrigin = inputObject.Position\n\telseif inputObject.KeyCode == Enum.KeyCode.Left then\n\t\twhile inputObject.UserInputState ~= Enum.UserInputState.Cancel and inputObject.UserInputState ~= Enum.UserInputState.End do\n\t\t\tgame:GetService(\"RunService\").RenderStepped:wait()\n\t\t\tyRotation = (yRotation + 0.04) % fullRotation\n\t\tend\n\telseif inputObject.KeyCode == Enum.KeyCode.Right then\n\t\twhile inputObject.UserInputState ~= Enum.UserInputState.Cancel and inputObject.UserInputState ~= Enum.UserInputState.End do\n\t\t\tgame:GetService(\"RunService\").RenderStepped:wait()\n\t\t\tyRotation = (yRotation - 0.04) % fullRotation\n\t\tend\n\telseif inputObject.KeyCode == Enum.KeyCode.I then\n\t\tzoom = math.clamp(zoom - 7, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)\n\t\t-- update the zoomCFrame\n\t\tif not rotationLocked then\n\t\t\tzoomCFrame = CFrame.new(0, 0, zoom)\n\t\tend\n\telseif inputObject.KeyCode == Enum.KeyCode.O then\n\t\tzoom = math.clamp(zoom + 7, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)\n\t\t-- update the zoomCFrame\n\t\tif not rotationLocked then\n\t\t\tzoomCFrame = CFrame.new(0, 0, zoom)\n\t\tend\n\tend\nend\n\nlocal function onInputChanged(inputObject, absorbed)\n\n\tif absorbed then\n\t\treturn false\n\tend\n\n\tif inputObject.UserInputType == Enum.UserInputType.MouseMovement and (IS_PLAYER_CAMERA_LOCKED or lockOrigin) then\n\n\t\tlocal yConversion = inputObject.Delta.X / 5\n\t\tlocal xConversion = inputObject.Delta.Y\t/ 5\n\t\tif not rotationLocked then\n\t\t\txRotation = math.clamp(xRotation - math.rad(xConversion), -maxXRotation, maxXRotation)\n\t\t\tyRotation = (yRotation - math.rad(yConversion)) % fullRotation\n\t\tend\n\telseif inputObject.UserInputType == Enum.UserInputType.MouseWheel then\n\n\n\n\t\tzoom = math.clamp(zoom - ( inputObject.Position.Z)   , player.CameraMinZoomDistance, player.CameraMaxZoomDistance)\n\n\n\t\t-- update the zoomCFrame\n\t\tif not rotationLocked then\n\t\t\tzoomCFrame = CFrame.new(0, 0, zoom)\n\t\tend\n\tend\nend\n\nlocal function onInputEnded(inputObject)\n\tif inputObject.UserInputType == Enum.UserInputType.MouseButton2 then\n\t\tlockOrigin = nil\n\n\telseif inputObject.KeyCode == Enum.KeyCode.Left then\n\n\tend\nend\n\nlocal function mobileCameraRotationChanged(rotation)\n\tmobileCameraRotation = rotation\nend\n\n\nlocal function step()\n\tif mobileCameraRotation and mobileCameraRotation.magnitude > 0.1 then\n\t\tlocal dy = \tmobileCameraRotation.Y\n\t\tlocal dx =\tmobileCameraRotation.X\n\n\t\tif math.abs(dy) < 0.1 then\n\t\t\tdy = 0\n\t\t\tddy = 0\n\t\telseif math.abs(dy) > 0.5 then\n\t\t\tddy = math.clamp(ddy + math.abs(dy/10),0,4)\n\t\tend\n\n\t\tif math.abs(dx) < 0.1 then\n\t\t\tdx = 0\n\t\t\tddx = 0\n\t\telseif math.abs(dx) > 0.5 then\n\t\t\tddx = math.clamp(ddx + math.abs(dx/10),0,4)\n\t\tend\n\n\t\tlocal yConversion =  dx * (1 + ddx)\n\t\tlocal xConversion =  dy * (1 + ddy)\n\t\tif not rotationLocked then\n\t\t\txRotation = math.clamp(xRotation - math.rad(xConversion), -maxXRotation, maxXRotation)\n\t\t\tyRotation = (yRotation - math.rad(yConversion)) % fullRotation\n\t\tend\n\tend\n\n\tlocal inputs = UserInputService:GetGamepadState(Enum.UserInputType.Gamepad1)\n\n\tif inputs then\n\n\t\tfor index, inputObject in pairs(inputs) do\n\n\n\t\t\tif inputObject.KeyCode == Enum.KeyCode.Thumbstick2 then\n\n\n\t\t\t\tlocal dy = \tinputObject.Position.Y\n\t\t\t\tlocal dx =\tinputObject.Position.X\n\n\t\t\t\tif math.abs(dy) < 0.1 then\n\t\t\t\t\tdy = 0\n\t\t\t\t\tddy = 0\n\t\t\t\telseif math.abs(dy) > 0.5 then\n\t\t\t\t\tddy = math.clamp(ddy + math.abs(dy/10),0,4)\n\t\t\t\tend\n\n\t\t\t\tif math.abs(dx) < 0.1 then\n\t\t\t\t\tdx = 0\n\t\t\t\t\tddx = 0\n\t\t\t\telseif math.abs(dx) > 0.5 then\n\t\t\t\t\tddx = math.clamp(ddx + math.abs(dx/10),0,4)\n\t\t\t\tend\n\n\t\t\t\tlocal yConversion = dx * (1 + ddx)\n\t\t\t\tlocal xConversion = -dy * (1 + ddy)\n\t\t\t\tif not rotationLocked then\n\t\t\t\t\txRotation = math.clamp(xRotation - math.rad(xConversion), -maxXRotation, maxXRotation)\n\t\t\t\t\tyRotation = (yRotation - math.rad(yConversion)) % fullRotation\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\tend\nend\n\nlocal function onCharacterAdded(newCharacter)\n\tcharacter = newCharacter\n\n\tif IS_PLAYER_CAMERA_LOCKED then\n\t\tUserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter\n\telse\n\t\tUserInputService.MouseBehavior = Enum.MouseBehavior.Default\n\tend\nend\n\nlocal function isCameraLocked()\n\treturn IS_PLAYER_CAMERA_LOCKED\nend\n\nlocal function toggleCameraLock(setValue)\n\n\tif setValue ~= nil then\n\t\tIS_PLAYER_CAMERA_LOCKED = not not setValue\n\telse\n\t\tIS_PLAYER_CAMERA_LOCKED = not IS_PLAYER_CAMERA_LOCKED\n\tend\n\n\tif IS_PLAYER_CAMERA_LOCKED then\n\t\tUserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter\n\telse\n\t\tUserInputService.MouseBehavior = Enum.MouseBehavior.Default\n\tend\n\n\tif character and character.PrimaryPart then\n\t\tcharacter.PrimaryPart.hitboxGyro.D \t\t\t= 5000\n\t\tcharacter.PrimaryPart.hitboxGyro.MaxTorque \t= Vector3.new(100000000, 100000000, 100000000)\n\t\tcharacter.PrimaryPart.hitboxGyro.P \t\t\t= 3000000\n\tend\n\n\tnetwork:fire(\"toggleCameraLockChanged\", IS_PLAYER_CAMERA_LOCKED)\nend\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\tcamera_shaker = Modules.camera_shaker\n\ttween = Modules.tween\n\n\tif player.Character then\n\t\tonCharacterAdded(player.Character)\n\tend\n\n\tplayer.CharacterAdded:connect(onCharacterAdded)\n\n\tUserInputService.InputBegan:connect(onInputBegan)\n\tUserInputService.InputChanged:connect(onInputChanged)\n\tUserInputService.InputEnded:connect(onInputEnded)\n\n\tprimarycamera_shaker = camera_shaker.new(--[[Enum.RenderPriority.Camera.Value]] 2, function(shakeCF)\n\t\tcamera.CFrame = cameraCFrameVal.Value * shakeCF\n\tend)\n\tprimarycamera_shaker:Start()\n\n\t-- TODO: this can really be eliminated with a direct module reference\n\tnetwork:create(\"lockCameraTarget\", \"BindableFunction\", \"OnInvoke\", lockCameraTarget)\n\tnetwork:create(\"lockCameraTargetWithOrientation\", \"BindableFunction\", \"OnInvoke\", lockCameraTargetWithOrientation)\n\tnetwork:create(\"cameraShake\", \"BindableFunction\", \"OnInvoke\", cameraShake)\n\tnetwork:create(\"lockCameraPosition\",\"BindableFunction\",\"OnInvoke\",lockCamera)\n\tnetwork:create(\"lockCameraPositionWithCameraShake\",\"BindableFunction\",\"OnInvoke\",lockCameraWithCameraShake)\n\tnetwork:create(\"mobileCameraRotationChanged\", \"BindableEvent\", \"Event\", mobileCameraRotationChanged)\n\tnetwork:create(\"toggleCameraLockChanged\", \"BindableEvent\")\n\tnetwork:create(\"getIsPlayerCameraLocked\", \"BindableFunction\", \"OnInvoke\", isCameraLocked)\n\tnetwork:create(\"toggleCameraLock\", \"BindableFunction\", \"OnInvoke\", toggleCameraLock)\n\n\n\tRunService.RenderStepped:connect(step)\n\tRunService:BindToRenderStep(\"cameraRenderUpdate\", --[[Enum.RenderPriority.Camera.Value - 1]] 1, updateCamera)\nend\n\nreturn module"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/chatRunner.lua",
    "content": "local module = {}\n\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules \t= require(replicatedStorage.modules)\n\t\tlocal network \t\t\t= modules.load(\"network\")\n\t\tlocal utilities\t\t\t= modules.load(\"utilities\")\n\nnetwork:connect(\"signal_alertChatMessage\", \"OnClientEvent\", function(messageTable)\n\tgame.StarterGui:SetCore(\"ChatMakeSystemMessage\", messageTable)\nend)\n\nnetwork:connect(\"signal_playerKilledByPlayer\", \"OnClientEvent\", function(deadPlayer, killer, damageInfo, verb)\n\tif killer and verb then\n\t\t-- you did this!\n\t\tif killer == game.Players.LocalPlayer then\n\t\t\tutilities.playSound(\"kill\", deadPlayer.Character and deadPlayer.Character.PrimaryPart)\n\t\t\tnetwork:fire(\"alert\", {\n\t\t\t\ttext = \"You \" .. verb ..  \" \" ..deadPlayer.Name..\"!\";\n\t\t\t\ttextColor3 = Color3.fromRGB(255, 93, 61);\n\t\t\t})\n\t\tend\n\tend\nend)\n\nreturn module\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/clientServerDamageSourceInterface.client.lua",
    "content": "-- this will act as the medium between all damaging abilities/weapons\n-- and the server, everything is routed here then sent out by the server\n-- Author: Polymorphic\n\nlocal assetFolder = script.Parent.Parent:WaitForChild(\"assets\")\n\nlocal module = {}\n\nlocal player = game.Players.LocalPlayer\n\nlocal userInputService = game:GetService(\"UserInputService\")\nlocal collectionService = game:GetService(\"CollectionService\")\nlocal httpService = game:GetService(\"HttpService\")\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal modules = require(replicatedStorage.modules)\n\nlocal network = modules.load(\"network\")\nlocal utilities = modules.load(\"utilities\")\nlocal placeSetup = modules.load(\"placeSetup\")\nlocal mapping = modules.load(\"mapping\")\nlocal detection = modules.load(\"detection\")\nlocal damage = modules.load(\"damage\")\nlocal projectile = modules.load(\"projectile\")\nlocal client_utilities = modules.load(\"client_utilities\")\nlocal events = modules.load(\"events\")\nlocal tween = modules.load(\"tween\")\nlocal effects = modules.load(\"effects\")\nlocal ability_utilities\t= modules.load(\"ability_utilities\")\n\nlocal itemData = require(replicatedStorage.itemData)\nlocal abilityLookup = require(replicatedStorage.abilityLookup)\nlocal ResourceController = require(script.Parent.resources)\n\nlocal entityRenderCollectionFolder = placeSetup.awaitPlaceFolder(\"entityRenderCollection\")\nlocal entityManifestCollectionFolder = placeSetup.awaitPlaceFolder(\"entityManifestCollection\")\nlocal itemsFolder = placeSetup.awaitPlaceFolder(\"items\")\nlocal entitiesFolder = placeSetup.awaitPlaceFolder(\"entities\")\nlocal assetsFolder = replicatedStorage:WaitForChild(\"assets\")\n\nlocal currentlyEquipped\nlocal currentWeaponManifest\n\nlocal clientCharacterContainer\n\nlocal canPlayerBasicAttack = true\nlocal isPlayerHoldingDownBasicAttack = false\nlocal isPlayerCastingAbility = false\n\nlocal function onSetCanPlayerBasicAttack(value)\n\tcanPlayerBasicAttack = value\n\n\tif not canPlayerBasicAttack and isPlayerHoldingDownBasicAttack then\n\t\tif currentlyEquipped and currentlyEquipped.release then\n\t\t\tisPlayerHoldingDownBasicAttack = false\n\t\t\tcurrentlyEquipped:release()\n\t\tend\n\tend\nend\n\nlocal function getPlayerEquipmentSlotDataForWeapon()\n\tlocal equipment = network:invoke(\"getCacheValueByNameTag\", \"equipment\")\n\tif equipment then\n\t\tfor i, equipmentSlotData in pairs(equipment) do\n\t\t\tif equipmentSlotData.position == 1 then\n\t\t\t\treturn equipmentSlotData\n\t\t\tend\n\t\tend\n\tend\n\n\treturn nil\nend\n\nlocal function equipmentMeetsAbilityRequirement(equipmentData, abilityRequirement)\n\tlocal equipmentType = equipmentData.equipmentType\n\n\tif equipmentType == abilityRequirement then\n\t\treturn true\n\tend\n\n\tif abilityRequirement == \"sword\" and equipmentType == \"greatsword\" then\n\t\treturn true\n\tend\n\n\tlocal isBowDaggerAbility = (abilityRequirement == \"dagger\") or (abilityRequirement == \"bow\")\n\tlocal isBowDaggerEquipped = (equipmentType == \"dagger\") or (equipmentType == \"bow\")\n\tif isBowDaggerAbility and isBowDaggerEquipped then\n\t\tlocal renderCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\t\tif not  renderCharacterContainer then return false end\n\n\t\tlocal equipment = network:invoke(\"getCurrentlyEquippedForRenderCharacter\", renderCharacterContainer.entity)\n\t\tif not equipment then return false end\n\n\t\tlocal offhand = equipment[\"11\"]\n\t\tif (not offhand) or (not offhand.baseData) then return false end\n\n\t\tif offhand.baseData.equipmentType == abilityRequirement then\n\t\t\tnetwork:invokeServer(\"playerRequest_swapWeapons_yielding\")\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nlocal function getServerHitboxFromClientHitbox(clientHitbox)\n\tif clientHitbox.Parent:FindFirstChild(\"clientHitboxToServerHitboxReference\") then\n\t\treturn clientHitbox.Parent.clientHitboxToServerHitboxReference.Value\n\tend\nend\n\nlocal function getClientHitboxFromServerHitbox(serverHitbox)\n\tfor i, clientMonsterContainer in pairs(entityRenderCollectionFolder:GetChildren()) do\n\t\tif clientMonsterContainer:FindFirstChild(\"clientHitboxToServerHitboxReference\") and clientMonsterContainer.clientHitboxToServerHitboxReference.Value == serverHitbox then\n\t\t\treturn clientMonsterContainer.PrimaryPart\n\t\tend\n\tend\nend\n\n-- sourceType = \"ability\", \"item\"\nlocal function handleRequestEntityDamageRequest(serverHitbox, damagePosition, sourceType, sourceId, sourceTag, GUID)\n\t-- todo: do client-side sanity checks\n\t-- check if is descendant\n\tif damage.canPlayerDamageTarget(player, serverHitbox) then\n\t\tnetwork:fire(\"monsterDamagedAtPosition\", damagePosition)\n\t\tnetwork:fireServer(\"playerRequest_damageEntity\", serverHitbox, damagePosition, sourceType, sourceId, sourceTag, GUID)\n\tend\nend\n\nlocal curWeaponType\n\nlocal function int__equipWeapon(weaponData)\n\n\tlocal itemBaseData = itemData[weaponData.id]\n\tif itemBaseData and itemBaseData.isEquippable and itemBaseData.equipmentType and itemBaseData.equipmentSlot == mapping.equipmentPosition.weapon then\n\t\tif not currentlyEquipped and assetFolder.damageInterfaces:FindFirstChild(itemBaseData.equipmentType) then\n\t\t\tcurWeaponType = itemBaseData.equipmentType\n\n\t\t\t-- check for dual swords and sword and shield\n\t\t\tif clientCharacterContainer and clientCharacterContainer:FindFirstChild(\"entity\") then\n\t\t\t\tlocal currentEquipment = network:invoke(\"getCurrentlyEquippedForRenderCharacter\", clientCharacterContainer.entity)\n\n\t\t\t\tif currentEquipment[\"1\"] and currentEquipment[\"11\"] then\n\t\t\t\t\tif currentEquipment[\"1\"].baseData.equipmentType == \"sword\" and currentEquipment[\"11\"].baseData.equipmentType == \"sword\" then\n\t\t\t\t\t\tcurWeaponType = \"dual\"\n\t\t\t\t\telseif currentEquipment[\"1\"].baseData.equipmentType == \"sword\" and currentEquipment[\"11\"].baseData.equipmentType == \"shield\" then\n\t\t\t\t\t\tcurWeaponType = \"swordAndShield\"\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif curWeaponType then\n\t\t\tcurrentlyEquipped = require(assetFolder.damageInterfaces[curWeaponType])\n\t\t\tcurrentlyEquipped:equip()\n\t\tend\n\tend\n\nend\n\nlocal function int__unequipWeapon()\n\tif currentlyEquipped then\n\t\tcurrentlyEquipped:unequip()\n\t\tcurrentlyEquipped = nil\n\t\tcurWeaponType = nil\n\tend\nend\n\n\nlocal function int_checkIfEquipWeaponFromEquipment(equipment)\n\n\tif equipment then\n\t\tlocal weaponEquipmentData\n\t\tfor i, equipmentData in pairs(equipment) do\n\t\t\tif equipmentData.position == mapping.equipmentPosition.weapon then\n\t\t\t\tweaponEquipmentData = equipmentData\n\t\t\tend\n\t\tend\n\n\t\tif weaponEquipmentData then\n\t\t\tif currentlyEquipped then\n\t\t\t\t-- unequip weapon first, then equip the other weapon\n\t\t\t\tint__unequipWeapon()\n\t\t\tend\n\n\t\t\tint__equipWeapon(weaponEquipmentData)\n\t\telse\n\t\t\tif currentlyEquipped then\n\t\t\t\tint__unequipWeapon()\n\t\t\tend\n\t\tend\n\telse\n\tend\n\n\nend\n\nlocal function onPropogationRequestToSelf(propogationNameTag, propogationValue)\n\tif propogationNameTag == \"equipment\" then\n\t\tint_checkIfEquipWeaponFromEquipment(propogationValue)\n\tend\nend\n\nlocal function onMyClientCharacterWeaponChanged(weaponManifest)\n\t-- set the weapon stuff\n\tcurrentWeaponManifest = weaponManifest\n\n\tif weaponManifest:IsA(\"BasePart\") then\n\t\tweaponManifest.Touched:Connect(function()\n\t\t\t-- do nothing just have a touch interest I guess\n\t\tend)\n\tend\n\n--\tif currentWeaponManifest then\n--\t\tlocal equipmentSlotDataCollection = network:invoke(\"getCacheValueByNameTag\", \"equipment\")\n--\n--\t\tif equipmentSlotDataCollection then\n--\t\t\tint_checkIfEquipWeaponFromEquipment(equipmentSlotDataCollection)\n--\t\tend\n--\tend\nend\n\nlocal cooldownLookup = {}\nlocal displayLookup = {}\nlocal castingAnimation\n\nlocal function onMyClientCharacterContainerChanged(newMyClientCharacterContainer)\n\tclientCharacterContainer = newMyClientCharacterContainer\n\t-- wait for humanoid\n\tlocal animationController = newMyClientCharacterContainer.entity:WaitForChild(\"AnimationController\")\n\twhile not animationController:IsDescendantOf(workspace) do wait() end\n\n\tif currentlyEquipped then\n\t\tcurrentlyEquipped:equip()\n\tend\n\n\tcastingAnimation = animationController:LoadAnimation(assetsFolder.abilityAnimations.rock_throw_upper_loop)\nend\n\nlocal function onCharacterAdded(character)\n\tlocal equipment = network:invoke(\"getCacheValueByNameTag\", \"equipment\")\n\n\tint_checkIfEquipWeaponFromEquipment(equipment)\nend\n\nlocal weaponDelays = {dual = .15; dagger = .15; sword = .23; staff = .23; --[[bow handles its own attack delay]] bow = 0}\nlocal attackOnCoolDown\n\nlocal function onInputBegan(input, absorbed)\n\tif canPlayerBasicAttack and currentlyEquipped and not absorbed and not network:invoke(\"getIsCurrentlyConsuming\") then\n\t\tif input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 or input.KeyCode == Enum.KeyCode.LeftControl or input.KeyCode == Enum.KeyCode.ButtonR2 then\n\t\t\tif network:invoke(\"isCharacterStunned\") then return end\n\t\t\tif attackOnCoolDown then return end\n\n\t\t\tnetwork:invoke(\"setCharacterMovementState\", \"isSprinting\", false)\n--\t\t\tisPlayerHoldingDownBasicAttack = true\n\n\t\t\tevents:fireEventAll(\"playerWillUseBasicAttack\", player)\n\t\t\tcurrentlyEquipped:attack(input)\n\t\t\tnetwork:fire(\"stopChannels\", \"attack\")\n\t\t\tnetwork:fire(\"signalBasicAttacking\", true)\n\n--\t\t\tattackOnCoolDown = true\n\t\t\tlocal stats = network:invoke(\"getCacheValueByNameTag\", \"nonSerializeData\").statistics_final\n\t\t\tlocal attackDelay = .25\n\n\t\t\tif weaponDelays[curWeaponType] then\n\t\t\t\tattackDelay = weaponDelays[curWeaponType] / (1 + stats.attackSpeed)\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function onInputEnded(input, absorbed)\n\tif isPlayerHoldingDownBasicAttack and currentlyEquipped and not absorbed and currentlyEquipped.release then -- bows\n\t\tif input.UserInputType == Enum.UserInputType.MouseButton1 or input.KeyCode == Enum.KeyCode.LeftControl or input.KeyCode == Enum.KeyCode.ButtonR2 then\n\t\t\tcurrentlyEquipped:release()\n\n\t\t\tisPlayerHoldingDownBasicAttack = false\n\t\tend\n\telseif isPlayerHoldingDownBasicAttack and currentlyEquipped then -- swords\n\t\tif input.UserInputType == Enum.UserInputType.MouseButton1 or input.KeyCode == Enum.KeyCode.LeftControl or input.KeyCode == Enum.KeyCode.ButtonR2 then\n\t\t\tisPlayerHoldingDownBasicAttack = false\n\t\t\tnetwork:fire(\"signalBasicAttacking\", false)\n\t\tend\n\n\tend\nend\n\nlocal function onAttackInteractionSoundPlayed(position, soundName)\n\tif not position then\n\t\tposition = player.Character.PrimaryPart.Position\n\tend\n\n\tutilities.playSound(soundName, position)\nend\n\nnetwork:connect(\"attackInteractionSoundPlayed\", \"OnClientEvent\", onAttackInteractionSoundPlayed)\n\nlocal function onAttackInteractionAttackableAttacked(attackingPlayer, part, hitPosition)\n\tlocal module = part:FindFirstChild(\"attackableScript\")\n\tif not module then return end\n\n\t-- hit effects\n\tnetwork:fire(\"monsterDamagedAtPosition\", hitPosition, attackingPlayer ~= player)\n\n\tmodule = require(module)\n\tmodule.onAttackedClient(attackingPlayer)\nend\n\nnetwork:connect(\"attackInteractionAttackableAttacked\", \"OnClientEvent\", onAttackInteractionAttackableAttacked)\n\nlocal function shake(model)\n\tif model and model:IsA(\"Model\") then\n\t\tif model.PrimaryPart then\n\t\t\tlocal originalCFrameValue = model:FindFirstChild(\"originalCFrame\")\n\t\t\tif originalCFrameValue == nil then\n\t\t\t\toriginalCFrameValue = Instance.new(\"CFrameValue\")\n\t\t\t\toriginalCFrameValue.Name = \"originalCFrame\"\n\t\t\t\toriginalCFrameValue.Value = model.PrimaryPart.CFrame\n\t\t\t\toriginalCFrameValue.Parent = model\n\t\t\tend\n\t\t\tlocal originalCFrame = originalCFrameValue.Value\n\t\t\tlocal primaryPart = model.PrimaryPart\n\t\t\tlocal rootCFrame =\toriginalCFrame * CFrame.new(0, -primaryPart.Size.Y/2, 0) * CFrame.Angles(0, math.pi * 2 * math.random(), 0)\n\t\t\tlocal offset = rootCFrame:ToObjectSpace(originalCFrame)\n\n\t\t\tlocal dummyPart = Instance.new(\"Part\")\n\t\t\tdummyPart.CFrame = rootCFrame\n\n\t\t\tlocal shakeTime = 0.2\n\t\t\tlocal easingStyle = Enum.EasingStyle.Quad\n\t\t\ttween(dummyPart, {\"CFrame\"}, rootCFrame * CFrame.Angles(0.075, 0, 0), shakeTime, easingStyle, Enum.EasingDirection.Out)\n\t\t\tdelay(shakeTime, function()\n\t\t\t\ttween(dummyPart, {\"CFrame\"}, rootCFrame, shakeTime, easingStyle, Enum.EasingDirection.In)\n\t\t\tend)\n\n\t\t\teffects.onHeartbeatFor(shakeTime * 2, function()\n\t\t\t\tmodel:SetPrimaryPartCFrame(dummyPart.CFrame:ToWorldSpace(offset))\n\t\t\tend)\n\t\tend\n\tend\nend\n\nlocal hitInteractions = {}\nlocal function attackInteraction(interaction, part)\n\tlocal name = part.Name:lower()\n\n\t-- leafy boy\n\tif (name == \"grass\") or (name == \"leaf\") or (name == \"bush\") or (name == \"green\") then\n\t\tif not interaction[\"strikeFoliage\"] then\n\t\t\tlocal soundName = \"bush\" .. math.random(1,3)\n\t\t\tonAttackInteractionSoundPlayed(part.Position, soundName)\n\t\t\tnetwork:fireServer(\"attackInteractionSoundPlayed\", part, soundName)\n\n\t\t\tshake(part.Parent)\n\t\tend\n\tend\n\n\t-- stumpy dude\n\tif (name == \"stump\") or (name == \"log\") or (name == \"wood\") then\n\t\tshake(part.Parent)\n\tend\n\n\t--attackable interactable ableables!\n\tif collectionService:HasTag(part, \"attackable\") then\n\t\tlocal model = part:FindFirstAncestorWhichIsA(\"Model\")\n\t\tlocal folder = model:FindFirstAncestorWhichIsA(\"Folder\")\n\t\tif collectionService:HasTag(folder, \"resourceNodeTypeFolder\") or collectionService:HasTag(folder, \"resourceNodeGroupFolder\") then\n\t\t\tlocal nodeModel = model\n\t\t\tlocal dropPoint = network:invokeServer(\"harvestResource\", nodeModel)\n\t\t\tshake(nodeModel)\n\t\telse\n\t\t\tlocal hitPosition = detection.projection_Box(part.CFrame, part.Size, currentWeaponManifest.Position)\n\t\t\tonAttackInteractionAttackableAttacked(player, part, hitPosition)\n\t\t\tnetwork:fireServer(\"attackInteractionAttackableAttacked\", part, hitPosition)\n\t\tend\n\n\tend\nend\n\nlocal function doAttackInteractions(guid)\n\tlocal interaction = hitInteractions[guid]\n\tif not interaction then\n\t\tinteraction = {}\n\n\t\t-- save this interaction for a bit, then\n\t\t-- get rid of it to avoid memory leaks\n\t\thitInteractions[guid] = interaction\n\t\tdelay(5, function()\n\t\t\thitInteractions[guid] = nil\n\t\tend)\n\tend\n\n\tfor _, part in pairs(currentWeaponManifest:GetTouchingParts()) do\n\t\tif not interaction[part] then\n\t\t\tinteraction[part] = true\n\t\t\tattackInteraction(interaction, part)\n\t\tend\n\tend\n\nend\n\nlocal hitDebounceTable = {}\nlocal function performClientDamageCycle(sourceType, sourceId, guid)\n\tif (sourceType == \"equipment\" and currentWeaponManifest) then\n\t\tif not hitDebounceTable[guid] then\n\t\t\thitDebounceTable[guid] = {}\n\n\t\t\t-- invalidate this GUID in 5 seconds\n\t\t\t-- if an exploiter messes with this, oh well. have fun with a memory leak.\n\t\t\tdelay(5, function()\n\t\t\t\thitDebounceTable[guid] = nil\n\t\t\tend)\n\t\tend\n\n\t\tdoAttackInteractions(guid)\n\n\t\tlocal sizeIncrease = 1 + network:invoke(\"getCacheValueByNameTag\", \"nonSerializeData\").statistics_final.attackRangeIncrease\n\n\t\tfor i, entityManifest in pairs(utilities.getEntities()) do\n\t\t\tif entityManifest ~= player.Character.PrimaryPart then\n\t\t\t\tif not hitDebounceTable[guid][entityManifest] then\n\t\t\t\t\tlocal boxcastOriginCF \t= currentWeaponManifest.CFrame\n\t\t\t\t\tlocal boxProjection_serverHitbox = detection.projection_Box(entityManifest.CFrame, entityManifest.Size, boxcastOriginCF.p)\n\t\t\t\t\tif detection.boxcast_singleTarget(boxcastOriginCF, currentWeaponManifest.Size * Vector3.new(3 + sizeIncrease, 2 + sizeIncrease, 3 + sizeIncrease), boxProjection_serverHitbox) then\n\t\t\t\t\t\thitDebounceTable[guid][entityManifest] = true\n\t\t\t\t\t\tnetwork:fire(\"requestEntityDamageDealt\", entityManifest, boxProjection_serverHitbox, sourceType, sourceId, guid)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\telse\n\t\t-- do nothing\n\tend\nend\n\nlocal function int_getCurrentlyEquippedEquipmentType()\n\tlocal weaponData = getPlayerEquipmentSlotDataForWeapon()\n\n\treturn itemData[weaponData.id].equipmentType\nend\n\n-- invoke the client call on an ability\n-- and return the stuff that we might need\n-- who knows? it might come in handy\n-- returns to NOTHING if we use an event\n\nlocal function main()\n\n\tnetwork:create(\"getCurrentlyEquippedEquipmentType\", \"BindableFunction\", \"OnInvoke\", int_getCurrentlyEquippedEquipmentType)\n\tnetwork:create(\"setCanPlayerBasicAttack\", \"BindableFunction\", \"OnInvoke\", onSetCanPlayerBasicAttack)\n\tnetwork:create(\"requestEntityDamageDealt\", \"BindableEvent\", \"Event\", handleRequestEntityDamageRequest)\n\n\tnetwork:create(\"performClientDamageCycle\", \"BindableFunction\", \"OnInvoke\", performClientDamageCycle)\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\n\tcurrentWeaponManifest = network:invoke(\"getCurrentWeaponManifest\")\n\n\tnetwork:connect(\"myClientCharacterWeaponChanged\", \"Event\", onMyClientCharacterWeaponChanged)\n\n\tlocal equipment = network:invoke(\"getCacheValueByNameTag\", \"equipment\")\n\n\t-- render character listener --\n\n\tlocal newMyClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif newMyClientCharacterContainer then\n\t\tonMyClientCharacterContainerChanged(newMyClientCharacterContainer)\n\tend\n\tnetwork:connect(\"myClientCharacterContainerChanged\", \"Event\", onMyClientCharacterContainerChanged)\n\t-- get player character --\n\n\tif player.Character then\n\t\tonCharacterAdded(player.Character)\n\tend\n\n\tplayer.CharacterAdded:connect(onCharacterAdded)\n\n\tuserInputService.InputBegan:connect(onInputBegan)\n\tuserInputService.InputEnded:connect(onInputEnded)\nend\n\nmain()\n\nreturn module"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/client_events.lua",
    "content": "local module = {}\n\nfunction module.init(Modules)\n\tlocal network = Modules.network\n\tlocal events = Modules.events\n\tnetwork:connect(\"fireEvent\", \"OnClientEvent\", function(...)\n\t\tevents:fireEventLocal(...)\n\tend)\nend\n\nreturn module"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/control.lua",
    "content": "-- Authors: Polymorphic, berezaa\n\nlocal module = {}\nlocal player = game.Players.LocalPlayer\nlocal starterPlayer = game:GetService(\"StarterPlayer\")\n\nlocal DISTANCE_AWAY_THRESHOLD \t\t\t\t= 50\nlocal MANIFEST_ORIENTATION_ASSIST_TIME_BASE = 0.5\nlocal MANIFEST_ORIENTATION_ASSIST_TIME \t\t= MANIFEST_ORIENTATION_ASSIST_TIME_BASE\nlocal CAMERA_RAYCAST_LENGTH \t\t\t\t= math.floor(starterPlayer.CameraMaxZoomDistance * 1.25)\n\nlocal assetFolder = script.Parent.Parent:WaitForChild(\"assets\")\n\nlocal closestManifest\nlocal currentEquipType\n\nlocal playerMovementSpeed = 16\n\nlocal total_statistics = {}\n\n\nlocal UserInputService = game:GetService(\"UserInputService\")\n\nlocal placeSetup\nlocal network\nlocal client_utilities\nlocal tween\nlocal utilities\nlocal terrainUtil\nlocal damage\n\n\nlocal isMenuInFocus\nlocal function signal_menuFocusChanged(value)\n\tisMenuInFocus = value\nend\n\n\nlocal entityRenderCollectionFolder\nlocal entityManifestCollectionFolder\n\nlocal IGNORE_LIST\nlocal userInputService = game:GetService(\"UserInputService\")\nlocal camera = workspace.Camera\nlocal myClientCharacterContainer\n\nlocal isPlayerJumpEnabled = true\nlocal isPlayerSprintingEnabled = true\nlocal IS_PLAYER_CAMERA_LOCKED = false\nlocal playerWalkspeedMultiplier = 1\nlocal isPlayerChanneling = false\n\nlocal basicAttacking = false\n\nlocal function onPlayerStatisticsChanged(base, tot)\n\ttotal_statistics = tot\n\tplayerMovementSpeed = total_statistics.walkspeed or 14\nend\n\n-- todo: fix\nlocal animationInterface\n\nlocal itemLookup \t= require(game.ReplicatedStorage.itemData)\n\nlocal function getServerHitboxFromClientHitbox(clientHitbox)\n\tif clientHitbox.Parent:FindFirstChild(\"clientHitboxToServerHitboxReference\") then\n\t\treturn clientHitbox.Parent.clientHitboxToServerHitboxReference.Value\n\tend\nend\n\n-- my only purpose is to make you walk slower while UserInputServiceng a bow :D\nlocal function onSetIsChanneling(isChanneling)\n\tisPlayerChanneling = isChanneling\n\n\tspawn(function()\n\t\tif isChanneling then\n\t\t\tfor i = 1, 0.5, -1 / 30 do\n\t\t\t\t-- break if channeling status changes\n\t\t\t\tif not isPlayerChanneling then break end\n\t\t\t\tplayerWalkspeedMultiplier = i\n\n\t\t\t\twait()\n\t\t\tend\n\n\t\t\tplayerWalkspeedMultiplier = 0.5\n\t\telse\n\t\t\tfor i = 0.5, 1, 1 / 30 do\n\t\t\t\t-- break if channeling status changes\n\t\t\t\tif isPlayerChanneling then break end\n\t\t\t\tplayerWalkspeedMultiplier = i\n\n\t\t\t\twait()\n\t\t\tend\n\n\t\t\tplayerWalkspeedMultiplier = 1\n\t\tend\n\tend)\nend\n\nlocal tweenService \t= game:GetService(\"TweenService\")\nlocal TWEEN_INFO \t= TweenInfo.new(1 / 3, Enum.EasingStyle.Quad, Enum.EasingDirection.Out, 0, false, 0)\n\nlocal sprintFOV \t= Instance.new(\"NumberValue\")\nsprintFOV.Name \t\t= \"sprintFOV\"\nsprintFOV.Parent \t= script\n\nlocal LAST_UPDATE_TIME \t= 0\nlocal UPDATE_TIME \t\t= 0.3\n\nlocal manifestTargetLocked\n\nlocal isPlayerSprinting = false\n\nlocal isForward \t= false\nlocal isBackward \t= false\nlocal isLeftward \t= false\nlocal isRightward \t= false\n\nlocal isCastingSpell = false\n\nlocal characterArrested = false\n\nlocal states = {}\n\tstates.isSprinting \t\t= false\n\tstates.isInAir \t\t\t= false\n\tstates.isMoving \t\t= false\n\tstates.isJumping \t\t= false\n\tstates.isSitting \t\t= false\n\tstates.isExhausted \t\t= false\n\tstates.isDoubleJumping \t= false\n\tstates.isFalling \t\t= false\n\tstates.isRotating \t\t= false\n\tstates.isFishing \t\t= false\n\tstates.isGettingUp \t\t= false\n\tstates.isSwimming \t\t= false\n\n-- ask damien if you need to know how status effects work now, this aint it chief\nlocal function doesCharacterHaveStatusEffect(statusEffectType, sourceId, sourceVariant)\n\tlocal char = player.Character\n\tif not char then return false end\n\tlocal manifest = char.PrimaryPart\n\tif not manifest then return false end\n\tlocal statusEffects = manifest:FindFirstChild(\"statusEffectsV2\")\n\tif not statusEffects then return false end\n\tlocal decodeSuccessful, statuses = utilities.safeJSONDecode(statusEffects.Value)\n\tif not decodeSuccessful then return false end\n\n\tfor _, status in pairs(statuses) do\n\t\tif status.statusEffectType == statusEffectType then\n\t\t\tif (sourceId == nil or status.sourceId == sourceId) and (sourceVariant == nil or status.variant == sourceVariant) then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\n\nlocal function isCharacterStunned()\n\treturn doesCharacterHaveStatusEffect(\"stunned\")\nend\n\nlocal function begin_sprint()\n\tnetwork:invoke(\"setCharacterMovementState\", \"isSprinting\", true)\nend\n\nlocal function end_sprint()\n\tnetwork:invoke(\"setCharacterMovementState\", \"isSprinting\", false)\nend\n\n-- performs state checks and changes the sprinting state, will correct invalid state\nlocal function set_sprinting(val)\n\tif not characterArrested then\n\t\tif isPlayerSprintingEnabled and val then\n\t\t\tbegin_sprint()\n\t\telse\n\t\t\tend_sprint()\n\t\tend\n\telse\n\t\tend_sprint()\n\tend\nend\n\nlocal tau = 2 * math.pi\nlocal function convertXBoxMovementToAngle(x, y)\n    return (math.atan2(y, x) - math.pi / 2) % tau\nend\n\nlocal movementUnitVector\n\nlocal mobileMovementDirection\n\nlocal function mobileMovementDirectionChanged(direction)\n\tmobileMovementDirection = direction\nend\n\n-- youre gonna need to live with this\nlocal function getMovementAngle()\n\tlocal movementAngle\n\n\tif isForward and (((isRightward and isLeftward) and not isBackward) or not (isBackward or isRightward or isLeftward)) then\n\t\tmovementAngle = 0\n\telseif isForward and isRightward and not (isBackward or isLeftward) then\n\t\tmovementAngle = math.pi / 4\n\telseif isRightward and (((isForward and isBackward) and not isLeftward) or not (isForward or isBackward or isLeftward)) then\n\t\tmovementAngle = math.pi / 2\n\telseif isBackward and isRightward and not (isForward or isLeftward) then\n\t\tmovementAngle = 3 * math.pi / 4\n\telseif isBackward and (((isRightward and isLeftward) and not isForward) or not (isForward or isLeftward or isRightward)) then\n\t\tmovementAngle = math.pi\n\telseif isBackward and isLeftward and not (isRightward or isForward) then\n\t\tmovementAngle = 5 * math.pi / 4\n\telseif isLeftward and (((isForward and isBackward) and not isRightward) or not (isRightward or isForward or isBackward)) then\n\t\tmovementAngle = 3 * math.pi / 2\n\telseif isLeftward and isForward and not (isRightward or isBackward) then\n\t\tmovementAngle = 7 * math.pi / 4\n\tend\n\n\tif mobileMovementDirection then\n\t\tif mobileMovementDirection.magnitude > 0.1 then\n\t\t\tmovementUnitVector = mobileMovementDirection\n\t\telse\n\t\t\tmovementUnitVector = nil\n\t\t\treturn nil\n\t\tend\n\t\treturn convertXBoxMovementToAngle(mobileMovementDirection.X, -mobileMovementDirection.Y)\n\tend\n\n\tif movementAngle then\n\t\tmovementUnitVector = nil\n\t\treturn movementAngle\n\tend\n\n\tlocal state = UserInputService:GetGamepadState(Enum.UserInputType.Gamepad1)\n\tif state then\n\t\tfor index, input in pairs(state) do\n\t\t\tif input.KeyCode == Enum.KeyCode.Thumbstick1 then\n\t\t\t\tif input.Position.magnitude > 0.2 and not game.GuiService.SelectedObject then\n\t\t\t\t\tmovementUnitVector = input.Position\n\t\t\t\telse\n\t\t\t\t\tif states.isSprinting then\n\t\t\t\t\t\tset_sprinting(false)\n\t\t\t\t\tend\n\t\t\t\t\tmovementUnitVector = nil\n\t\t\t\t\treturn nil\n\t\t\t\tend\n\t\t\t\treturn convertXBoxMovementToAngle(input.Position.X, input.Position.Y)\n\t\t\tend\n\t\tend\n\tend\n\n\tmovementUnitVector = nil\n\n\treturn movementAngle\nend\n\nlocal function isOnScreen(position)\n\treturn\n\t\tposition.X >= 0 and position.X <= workspace.CurrentCamera.ViewportSize.X\n\t\tand position.Y >= 0 and position.Y <= workspace.CurrentCamera.ViewportSize.Y\n\t\tand position.Z > 0\nend\n\nlocal targetsList = {}\nlocal targetIndex = 1\nlocal manifestCurrentlyTargetted\nlocal manifestCurrentlyTargetted_distanceAway\n\nlocal function updateTargetsList()\n\tlocal characterHitbox = player.Character and player.Character.PrimaryPart\n\tif not characterHitbox then return end\n\n\tif tick() - LAST_UPDATE_TIME >= UPDATE_TIME then\n\t\tLAST_UPDATE_TIME = tick()\n\n\t\tlocal new__targetsList = {}\n\n\t\tlocal hitPart, hitPosition = client_utilities.raycastFromCurrentScreenPoint({entityRenderCollectionFolder})\n\n\t\tif hitPart then\n\t\t\tlocal hitPlayer, hitMonster do\n\t\t\t\tlocal canDoDamage, trueHitPart = damage.canPlayerDamageTarget(player, hitPart)\n\t\t\t\tif canDoDamage and trueHitPart then\n\t\t\t\t\tif trueHitPart.entityId.Value == \"character\" then\n\t\t\t\t\t\thitPlayer = game.Players:GetPlayerFromCharacter(trueHitPart.Parent)\n\t\t\t\t\telseif trueHitPart.entityId.Value == \"monster\" then\n\t\t\t\t\t\thitMonster = trueHitPart\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif\n\t\t\t\thitMonster and\n\t\t\t\tnot hitMonster:FindFirstChild(\"pet\") and\n\t\t\t\tnot hitMonster:FindFirstChild(\"isStealthed\")\n\t\t\tthen\n\t\t\t\tlocal distanceAway = (hitMonster.Position - characterHitbox.Position).magnitude\n\t\t\t\tif distanceAway <= DISTANCE_AWAY_THRESHOLD then\n\t\t\t\t\tmanifestCurrentlyTargetted \t\t\t\t= hitMonster\n\t\t\t\t\tmanifestCurrentlyTargetted_distanceAway = distanceAway\n\n\t\t\t\t\t-- short circuit\n\t\t\t\t\treturn manifestCurrentlyTargetted, manifestCurrentlyTargetted_distanceAway\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tlocal nearestMonsterManifest, distanceAway = nil, DISTANCE_AWAY_THRESHOLD\n\t\tlocal damagableTargets = damage.getDamagableTargets(game.Players.LocalPlayer)\n\t\tfor i, manifest in pairs(damagableTargets) do\n\t\t\tlocal isStealthed = manifest:FindFirstChild(\"isStealthed\") ~= nil\n\t\t\tif not isStealthed then\n\t\t\t\tlocal _distanceAway = utilities.magnitude(manifest.Position - characterHitbox.Position)\n\n\t\t\t\tif distanceAway > _distanceAway then\n\t\t\t\t\tlocal position, isInFrontNearClipping = camera:WorldToScreenPoint(manifest.Position)\n\t\t\t\t\tif position.Z > 0 and isOnScreen(position) and manifest.health.Value > 0 then\n\t\t\t\t\t\tnearestMonsterManifest \t= manifest\n\t\t\t\t\t\tdistanceAway \t\t\t= _distanceAway\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tmanifestCurrentlyTargetted \t\t\t\t= nearestMonsterManifest\n\t\tmanifestCurrentlyTargetted_distanceAway = distanceAway\n\tend\n\n\treturn manifestCurrentlyTargetted, manifestCurrentlyTargetted_distanceAway\nend\n\nlocal function cycleThroughTargetsList(cycleBackwardsNotForwards)\n\tif #targetsList > 1 then\n\t\tif cycleBackwardsNotForwards then\n\t\t\tif targetIndex == 1 then\n\t\t\t\ttargetIndex = #targetsList\n\t\t\telse\n\t\t\t\ttargetIndex = targetIndex - 1\n\t\t\tend\n\t\telse\n\t\t\ttargetIndex = ((targetIndex + 1) % #targetsList) + 1\n\t\tend\n\telse\n\t\ttargetIndex = 1\n\tend\n\n\tmanifestCurrentlyTargetted \t\t\t\t= targetsList[targetIndex] and targetsList[targetIndex].manifest\n\tmanifestCurrentlyTargetted_distanceAway = targetsList[targetIndex] and targetsList[targetIndex].distanceAway\nend\n\nlocal renderStepped_connection\nlocal function onCharacterAdded(character)\n\t-- reset states --\n\tstates.isSprinting \t\t= false\n\tstates.isInAir \t\t\t= false\n\tstates.isMoving \t\t= false\n\tstates.isJumping \t\t= false\n\tstates.isSitting \t\t= false\n\tstates.isExhausted \t\t= false\n\tstates.isDoubleJumping \t= false\n\tstates.isFishing \t\t= false\n\tstates.isRotating \t\t= false\n\tstates.isFalling \t\t= false\n\tstates.isGettingUp \t\t= false\n\tstates.isSwimming \t\t= false\n\n\tonSetIsChanneling(false)\n\n\tmyClientCharacterContainer = network:invoke(\"getMyClientCharacterContainer\")\n\n\tif character.PrimaryPart == nil and character:FindFirstChild(\"hitbox\") then\n\t\tcharacter.PrimaryPart = character.hitbox\n\tend\n\n\tlocal startTime = tick()\n\n\trepeat wait() until character.PrimaryPart or character.Parent == nil or tick() - startTime >= 10\n\n\tif not character.PrimaryPart then\n\t\treturn false\n\tend\n\n\tnetwork:fireServer(\"replicateClientStateChanged\", character.PrimaryPart.state.Value)\nend\n\n\nlocal startSprintTime = 0\nlocal isPlayerSprintingAnimationPlaying = false\n\nlocal function startSprinting_animations(onlyStopAnimation)\n\t--if states.isExhausted then return end\n\tif isPlayerSprintingAnimationPlaying then return end\n\n\tnetwork:fire(\"stopChannels\", \"sprint\")\n\n\tisPlayerSprintingAnimationPlaying = true\n\ttween(sprintFOV,{\"Value\"},15,0.5)\n\t--cameraSprinting:Play()\n\n\tif not states.isInAir and myClientCharacterContainer and myClientCharacterContainer:FindFirstChild(\"entity\") then\n\t\tmyClientCharacterContainer.entity.RightFoot.ParticleEmitter.Enabled = true\n\t\tmyClientCharacterContainer.entity.LeftFoot.ParticleEmitter.Enabled \t= true\n\tend\n\n\tstartSprintTime = tick()\nend\n\nlocal function stopSprinting_animations()\n\t--if not isPlayerSprintingAnimationPlaying then return end\n\n\tisPlayerSprintingAnimationPlaying = false\n\ttween(sprintFOV,{\"Value\"},0,0.5)\n\t--cameraWalking:Play()\n\n\tif myClientCharacterContainer and myClientCharacterContainer:FindFirstChild(\"entity\") then\n\t\tmyClientCharacterContainer.entity.RightFoot.ParticleEmitter.Enabled = false\n\t\tmyClientCharacterContainer.entity.LeftFoot.ParticleEmitter.Enabled \t= false\n\tend\nend\n\nlocal renderUpdateConnection\nlocal runService \t\t\t\t= game:GetService(\"RunService\")\nlocal mouseMovementInputObject \t= nil\n\n\n\nlocal function raycastFromScreenPosition(screenPositionX, screenPositionY)\n\tlocal characterHitbox = player.Character and player.Character.PrimaryPart\n\n\tif characterHitbox then\n\t\tlocal cameraRay = workspace.CurrentCamera:ScreenPointToRay(screenPositionX, screenPositionY)\n\n\t\tlocal ray = Ray.new(cameraRay.Origin, cameraRay.Direction.unit * CAMERA_RAYCAST_LENGTH)\n\t\tlocal hitPart, hitPosition = workspace:FindPartOnRayWithIgnoreList(ray, IGNORE_LIST)\n\n\t\treturn hitPart, hitPosition, (hitPart and utilities.magnitude(hitPart.Position - characterHitbox.Position) or nil)\n\tend\nend\n\nlocal curEmote = \"\"\nlocal isPlayerEmoting = false\n\nlocal emoteCooldown = false\n\nlocal defaultEmotes = {\n\t[\"dance\"]    = true;\n\t[\"dance2\"]   = true;\n\t[\"dance3\"]   = true;\n\t[\"oh yea\"]   = true;\n\t[\"hype\"]     = true;\n\t[\"sit\"]      = true;\n\t[\"wave\"]     = true;\n\t[\"point\"]    = true;\n\t[\"beg\"]      = true;\n\t[\"flex\"]     = true;\n\t[\"handstand\"]  = true;\n\t[\"tadaa\"]     = true;\n\t[\"jumps\"]   = true;\n\t[\"guitar\"]   = true;\n\t[\"panic\"]   = true;\n\t[\"cheer\"]   = true;\n\t[\"pushups\"]   = true;\n}\n\nlocal function performEmote(emote)\n\n\n\tif states.isSprinting or states.isInAir or\tstates.isMoving or\tstates.isJumping or\tstates.isSitting or\tstates.isExhausted or states.isDoubleJumping or states.isFalling or states.isFishing or states.isGettingUp or states.isSwimming then\n\t\treturn false\n\tend\n\n\n\n\tlocal lastEmotesReceived = network:invoke(\"getCacheValueByNameTag\", \"globalData\").emotes\n\tlocal found = false\n\temote = string.lower(emote)\n\tfor i, ownedEmote in pairs(lastEmotesReceived) do\n\t\tif emote == ownedEmote then\n\t\t\tfound = true\n\t\tend\n\tend\n\n\n\tif defaultEmotes[emote] then found = true end\n\n\tif found and not emoteCooldown then\n\n\t\tif curEmote ~= emote then\n\t\t\tcurEmote = emote\n\t\t\tisPlayerEmoting = true\n\n\t\t\tspawn(function()\n\t\t\t\temoteCooldown = true\n\t\t\t\twait(1)\n\t\t\t\temoteCooldown = false\n\t\t\tend)\n\t\t\tplayer.Character.PrimaryPart.state.Value = \"idling\"\n\t\t\tnetwork:fireServer(\"replicateClientStateChanged\", \"idling\")\n\n\t\t\tanimationInterface:replicatePlayerAnimationSequence(\"emoteAnimations\", emote, nil, {dance = true}) -- add security check on the remote, serverside when players can buy emotes\n\t\t\treturn true\n\t\tend\n\n\t\t--return false, \"Cannot perform emote while moving.\"\n\n\n\telse\n\t\tif emoteCooldown then\n\t\t\treturn false, \"Emote on cooldown.\"\n\t\tend\n\t\treturn false, \"Cannot perform invalid emote.\"\n\tend\n\n\nend\n\n\nfunction module.endEmote()\n\tif isPlayerEmoting then\n\t\tcurEmote = \"\"\n\t\tisPlayerEmoting = false\n\tend\nend\n\n\n-- this is all crappy bad code but you cant burn it all down because you need it so figure out how to\n-- use it in a way that isnt crappy bad code thank you good luck have fun we're praying for your\n-- safe return\nlocal function setCharacterMovementState(state, value, ...)\n\t--if state and state ~= \"isMoving\" then\n\t--\tisPlayerEmoting = false\n\t--\tcurEmote = \"\"\n\t--end\n\t--isPlayerEmoting = false\n\t--curEmote = \"\"\n\n\tif states[state] ~= nil and states[state] ~= value and player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.state.Value ~= \"dead\" then\n\t\tisPlayerEmoting = false\n\t\tcurEmote = \"\"\n\n\t\tif state == \"isSprinting\" and not network:invoke(\"getIsCurrentlyConsuming\") and not states.isExhausted then\n\t\t\tif states.isMoving then\n\t\t\t\tif not states[state] and value then\n\t\t\t\t\t-- turning on\n\t\t\t\t\tstartSprinting_animations()\n\n\t\t\t\telseif states[state] and not value then\n\t\t\t\t\t-- turning off\n\t\t\t\t\tstopSprinting_animations()\n\t\t\t\tend\n\t\t\telse\n\t\t\t\t-- still play exhaust if they stop moving\n\t\t\t\t-- while sprinting then stop sprinting\n\t\t\t\tif states[state] and not value then\n\t\t\t\t\tstopSprinting_animations()\n\t\t\t\tend\n\t\t\tend\n\t\telseif state == \"isSprinting\" and (network:invoke(\"getIsCurrentlyConsuming\") or states.isExhausted) then\n\t\t\treturn\n\t\telseif state == \"isMoving\" and not states.isExhausted then\n\n\t\t\tif states.isSprinting and not isPlayerSprintingAnimationPlaying then\n\t\t\t\tstartSprinting_animations(true)\n\t\t\telseif not states.isSprinting and isPlayerSprintingAnimationPlaying then\n\t\t\t\tstopSprinting_animations(true)\n\t\t\tend\n\t\telseif state == \"isGettingUp\" then\n\t\t\tif states.isSprinting then\n\t\t\t\tstates.isSprinting = false\n\n\t\t\t\tstopSprinting_animations(true)\n\t\t\tend\n\n\n\n\n\t\tend\n\n\t\tstates[state] = value\n\n\n\t\tif player.Character and player.Character.PrimaryPart then\n\t\t\tif not states.isGettingUp then\n\t\t\t\tif not states.isSwimming then\n\t\t\t\t\tif not states.isFishing then\n\n\t\t\t\t\t\t\tif not states.isSitting then\n\t\t\t\t\t\t\t\tif not states.isFalling then\n\t\t\t\t\t\t\t\t\tif not states.isJumping then\n\n\t\t\t\t\t\t\t\t\t\tif states.isMoving or states.isRotating then\n\n\n\t\t\t\t\t\t\t\t\t\t\tif states.isSprinting and not characterArrested then\n\t\t\t\t\t\t\t\t\t\t\t\t-- pray this doesnt cause race condition issues\n\t\t\t\t\t\t\t\t\t\t\t\tif player.Character.PrimaryPart.state.Value ~= \"sprinting\" then\n\t\t\t\t\t\t\t\t\t\t\t\t\tnetwork:fireServer(\"replicateClientStateChanged\", \"sprinting\")\n\t\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\t\t\tplayer.Character.PrimaryPart.state.Value = \"sprinting\"\n\n\t\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\t\tif states.isExhausted then\n\t\t\t\t\t\t\t\t\t\t\t\t\tplayer.Character.PrimaryPart.state.Value = \"walking_exhausted\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tnetwork:fireServer(\"replicateClientStateChanged\", \"walking_exhausted\")\n\t\t\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\t\t\t-- pray  here too\n\t\t\t\t\t\t\t\t\t\t\t\t\tif player.Character.PrimaryPart.state.Value ~= \"walking\" then\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tnetwork:fireServer(\"replicateClientStateChanged\", \"walking\")\n\t\t\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\t\t\t\tplayer.Character.PrimaryPart.state.Value = \"walking\"\n\t\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\tif states.isExhausted then\n\t\t\t\t\t\t\t\t\t\t\t\tplayer.Character.PrimaryPart.state.Value = \"idling_exhausted\"\n\t\t\t\t\t\t\t\t\t\t\t\tnetwork:fireServer(\"replicateClientStateChanged\", \"idling_exhausted\")\n\t\t\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\t\t\tplayer.Character.PrimaryPart.state.Value = \"idling\"\n\n\n\t\t\t\t\t\t\t\t\t\t\t\tnetwork:fireServer(\"replicateClientStateChanged\", \"idling\", \"kicking\")\n\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\tif not states.isDoubleJumping then\n\t\t\t\t\t\t\t\t\t\tplayer.Character.PrimaryPart.state.Value = \"jumping\"\n\t\t\t\t\t\t\t\t\t\tnetwork:fireServer(\"replicateClientStateChanged\", \"jumping\")\n\t\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\t\t\tplayer.Character.PrimaryPart.state.Value = \"double_jumping\"\n\t\t\t\t\t\t\t\t\t\tnetwork:fireServer(\"replicateClientStateChanged\", \"double_jumping\")\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tplayer.Character.PrimaryPart.state.Value = \"falling\"\n\t\t\t\t\t\t\t\tnetwork:fireServer(\"replicateClientStateChanged\", \"falling\")\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tplayer.Character.PrimaryPart.state.Value = \"sitting\"\n\t\t\t\t\t\t\tnetwork:fireServer(\"replicateClientStateChanged\", \"sitting\", nil, ...)\n\t\t\t\t\t\tend\n\n\t\t\t\t\telse\n\t\t\t\t\t\tplayer.Character.PrimaryPart.state.Value = \"fishing\"\n\t\t\t\t\t\tnetwork:fireServer(\"replicateClientStateChanged\", \"fishing\")\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tplayer.Character.PrimaryPart.state.Value = \"swimming\"\n\t\t\t\t\tnetwork:fireServer(\"replicateClientStateChanged\", \"swimming\")\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tplayer.Character.PrimaryPart.state.Value = \"gettingUp\"\n\t\t\t\tnetwork:fireServer(\"replicateClientStateChanged\", \"gettingUp\", nil, ...)\n\t\t\tend\n\t\tend\n\n\t\tnetwork:fire(\"characterStateChanged\", state, value, ...)\n\t\treturn true\n\tend\nend\n\nlocal MAX_CONE_ANGLE_DIFF = math.rad(20)\nlocal function isMouseInMovementCone(movementDirection, mouseDirection, movementAngle)\n\tlocal dotProduct \t= movementDirection:Dot(mouseDirection)\n\tlocal angleDiff \t= math.acos(dotProduct)\n\n\treturn angleDiff <= MAX_CONE_ANGLE_DIFF, dotProduct < 0, angleDiff, math.sign(movementDirection:Cross(mouseDirection):Dot(Vector3.new(0, 1, 0)))\nend\n\nlocal wind = assetFolder.wind\nlocal windBaseVolume = game.ReplicatedStorage:FindFirstChild(\"windVolume\") and game.ReplicatedStorage.windVolume.Value or 0.02\nwind.Volume = windBaseVolume\nwind:Play()\n\nlocal baseFriction = 500\nlocal friction = baseFriction -- change friction based on individual parts\n\nlocal externalVelocity = Vector3.new()\nlocal movementVelocity = Vector3.new()\n\nlocal overrideCharacterCFrame\n\n\n\n-- stops all movement, state changes, etc\nlocal function setCharacterArrested(arrested, arrestedCFrame)\n\tif arrested then\n\t\tcharacterArrested \t= true\n\t\tmovementVelocity \t= Vector3.new()\n\n\t\tif states.isSprinting then\n\t\t\tstopSprinting_animations(true)\n\t\tend\n\n\t\tsetCharacterMovementState(\"isMoving\", false)\n\t\tsetCharacterMovementState(\"isSprinting\", false)\n\t\tsetCharacterMovementState(\"isSitting\", false)\n\t\tsetCharacterMovementState(\"isRotating\", false)\n\n\t\tif arrestedCFrame then\n\t\t\toverrideCharacterCFrame = arrestedCFrame\n\t\t\texternalVelocity \t= Vector3.new()\n\n\t\t\tplayer.Character:SetPrimaryPartCFrame(arrestedCFrame)\n\n\n\t\t\tlocal characterHitbox = player.Character and player.Character.PrimaryPart\n\n\t\t\tif characterHitbox then\n\t\t\t\tlocal bodyPosition \t= characterHitbox.grounder\n\t\t\t\tlocal bodyVelocity \t= characterHitbox.hitboxVelocity\n\t\t\t\tlocal bodyGyro \t\t= characterHitbox.hitboxGyro\n\n\t\t\t\tbodyPosition.MaxForce = Vector3.new(1e5, 1e5, 1e5)\n\t\t\t\tbodyVelocity.MaxForce = Vector3.new(0, 0, 0)\n\n\t\t\t\tbodyGyro.CFrame \t\t= overrideCharacterCFrame\n\t\t\t\tbodyPosition.Position \t= overrideCharacterCFrame.Position\n\t\t\tend\n\t\tend\n\telse\n\t\tcharacterArrested = false\n\t\toverrideCharacterCFrame = nil\n\n\t\t-- if shift is held when we come out of arrest we should start sprinting\n\t\tlocal sprintKeyCode = Enum.KeyCode.LeftShift -- later we can change this to work with keybindings\n\t\tlocal sprintKeyDown = userInputService:IsKeyDown(sprintKeyCode)\n\n\t\tif sprintKeyDown then\n\t\t\tset_sprinting(true)\n\t\tend\n\tend\nend\n\n\n\n\nlocal lastJump = 0\n\nlocal totalStats\n\nlocal function playerStatisticsChanged(base, total)\n\ttotalStats = total\nend\n\n\n\nlocal isPlayerUnderwater\nlocal velocityHandler\n\nlocal function raycastDownIgnoreCancollideFalse(ray, ignoreList)\n\tlocal hitPart, hitPosition, hitDown, hitMaterial = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList, true)\n\n\tlocal items = workspace.placeFolders:FindFirstChild(\"items\")\n\t-- edit: ignore water and dropped items\n\twhile hitPart and not (hitPart.CanCollide and (not hitPart:IsDescendantOf(entityManifestCollectionFolder) or (not hitPart:FindFirstChild(\"entityType\") or hitPart.entityType.Value ~= \"pet\")) and hitMaterial ~= Enum.Material.Water and (items == nil or not hitPart:IsDescendantOf(items))) do\n\t\tignoreList[#ignoreList + 1] = hitPart\n\t\thitPart, hitPosition, hitDown, hitMaterial = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList, true)\n\tend\n\n\treturn hitPart, hitPosition, hitDown, hitMaterial\nend\n\nlocal yJumpVelocity\nlocal lastPositionOnGround\nlocal startPositionForFalling\n\nlocal characterOrientationUpdateConnection\n\nlocal function setupRenderSteppedConnection()\n\tlocal function updateCharacterOrientationFaceManifest(_,step)\n\t\tlocal characterHitbox = player.Character and player.Character.PrimaryPart\n\n\t\tlocal bodyPosition \t= characterHitbox and characterHitbox:FindFirstChild(\"grounder\")\n\t\tlocal bodyVelocity \t= characterHitbox and characterHitbox:FindFirstChild(\"hitboxVelocity\")\n\t\tlocal bodyGyro \t\t= characterHitbox and characterHitbox:FindFirstChild(\"hitboxGyro\")\n\n\t\tif not bodyPosition or not bodyVelocity or not bodyGyro then return end\n\n\t\tif overrideCharacterCFrame then\n\t\t\tbodyPosition.MaxForce = Vector3.new(1e5, 1e5, 1e5)\n\t\t\tbodyVelocity.MaxForce = Vector3.new(0, 0, 0)\n\n\t\t\tbodyGyro.CFrame = overrideCharacterCFrame\n\t\t\tbodyPosition.Position = overrideCharacterCFrame.Position\n\t\t\treturn\n\t\tend\n\n\t\tlocal movementAngle = getMovementAngle()\n\t\tlocal hitPosition\n\n\t\tif mouseMovementInputObject then\n\t\t\t_, hitPosition = raycastFromScreenPosition(mouseMovementInputObject.Position.X, mouseMovementInputObject.Position.Y)\n\t\tend\n\n\t\t-- todo optimize\n\t\tlocal ignoreList = {\n\t\t\tmyClientCharacterContainer,\n\t\t}\n\t\tlocal downRay = Ray.new(characterHitbox.Position, Vector3.new(0, -5, 0))\n\t\tlocal downHitPart, downHitPosition, downHitNormal, downHitMaterial = raycastDownIgnoreCancollideFalse(downRay, ignoreList)\n\n\t\tlocal height = 2.5\n\n\t\t-- check in front of the player\n\t\tif utilities.magnitude(movementVelocity) > 0.1 then\n\t\t\tlocal moving \t\t\t\t\t\t\t= movementVelocity.Unit\n\t\t\tlocal frontRay \t\t\t\t\t\t\t= Ray.new(characterHitbox.Position, Vector3.new(moving.X, -height, moving.Z))\n\t\t\tlocal frontHitPart, frontHitPosition \t= raycastDownIgnoreCancollideFalse(frontRay, ignoreList)\n\n\t\t\tif frontHitPart then\n\t\t\t\tlocal frontHitHeight = frontHitPosition.Y - (characterHitbox.Position.Y - height)\n\t\t\t\tif frontHitHeight > 0 and frontHitHeight < height then\n\t\t\t\t\theight = height + frontHitHeight * 12\n\t\t\t\tend\n\t\t\tend\n\n\t\tend\n\t\tlocal function land()\n\t\t\tbodyPosition.MaxForce = Vector3.new(0, 1e4, 0)\n\t\t\tbodyVelocity.MaxForce = Vector3.new(1e4, 0, 1e4)\n\n\t\t\tstates.isInAir = false\n\n\t\t\tlocal yVelocityOnImpact = 0\n\n\t\t\tif states.isJumping then\n\t\t\t\tsetCharacterMovementState(\"isJumping\", false)\n\t\t\t\tsetCharacterMovementState(\"isDoubleJumping\", false)\n\n\t\t\t\tif states.isFalling then\n\t\t\t\t\tyVelocityOnImpact = externalVelocity.Y\n\t\t\t\tend\n\t\t\t\tsetCharacterMovementState(\"isFalling\", false)\n\t\t\telseif states.isFalling then\n\t\t\t\tyVelocityOnImpact = externalVelocity.Y\n\n\t\t\t\tsetCharacterMovementState(\"isFalling\", false)\n\t\t\tend\n\n\t\t\tbodyPosition.Position = downHitPosition + Vector3.new(0, height, 0)\n\t\tend\n\n\t\tif downHitPart then\n\t\t\t-- to get around floating characters, make sure active parts still trigger collision events\n\t\t\tif game.CollectionService:HasTag(downHitPart, \"ActivePart\") then\n\t\t\t\tnetwork:fire(\"touchedActivePart\", downHitPart, characterHitbox)\n\t\t\tend\n\n\t\t\tif states.isSprinting and myClientCharacterContainer and myClientCharacterContainer:FindFirstChild(\"entity\") then\n\t\t\t\tmyClientCharacterContainer.entity.RightFoot.ParticleEmitter.Enabled = true\n\t\t\t\tmyClientCharacterContainer.entity.LeftFoot.ParticleEmitter.Enabled \t= true\n\n\t\t\t\tlocal color = downHitPart.Color\n\t\t\t\tif downHitPart:IsA(\"Terrain\") and (downHitMaterial ~= Enum.Material.Air) then\n\t\t\t\t\tcolor = downHitPart:GetMaterialColor(downHitMaterial)\n\t\t\t\tend\n\n\t\t\t\tmyClientCharacterContainer.entity.LeftFoot.ParticleEmitter.Color = ColorSequence.new(color)\n\t\t\t\tmyClientCharacterContainer.entity.RightFoot.ParticleEmitter.Color = ColorSequence.new(color)\n\t\t\tend\n\n\n\n\t\t\t-- make players slide off of enemies\n\t\t\tif downHitPart:isDescendantOf(entityManifestCollectionFolder) or downHitPart:isDescendantOf(entityRenderCollectionFolder) then\n\t\t\t\t-- except other players\n\t\t\t\tlocal downHitRender =\n\t\t\t\t\t(downHitPart:FindFirstChild(\"clientHitboxToServerHitboxReference\") and downHitPart) or\n\t\t\t\t\t(downHitPart.Parent:FindFirstChild(\"clientHitboxToServerHitboxReference\") and downHitPart.Parent) or\n\t\t\t\t\t(downHitPart.Parent.Parent:FindFirstChild(\"clientHitboxToServerHitboxReference\") and downHitPart.Parent.Parent) or\n\t\t\t\t\t(downHitPart.Parent.Parent.Parent:FindFirstChild(\"clientHitboxToServerHitboxReference\") and downHitPart.Parent.Parent.Parent)\n\t\t\t\tlocal downHitEntity = downHitPart.Parent\n\t\t\t\tif downHitRender then\n\t\t\t\t\tdownHitEntity = downHitRender.clientHitboxToServerHitboxReference.Value.Parent\n\t\t\t\tend\n\t\t\t\tif game.Players:GetPlayerFromCharacter(downHitEntity) then\n\t\t\t\t\tfriction = baseFriction * downHitPart.Friction\n\t\t\t\t\tland()\n\t\t\t\telse\n\n\t\t\t\t\tfriction = 0\n\t\t\t\t\tlocal dif = (characterHitbox.Position - downHitPart.Position).unit * 10\n\t\t\t\t\texternalVelocity = externalVelocity + Vector3.new(dif.X,0,dif.Z)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tfriction = baseFriction * downHitPart.Friction\n\t\t\tend\n\t\tend\n\n\t\tif not isPlayerUnderwater or not states.isSwimming then\n\t\t\tif externalVelocity.Y > 0.1 or downHitPart == nil or (characterHitbox.Position.Y - downHitPosition.Y) > height + 1 then\n\t\t\t\tbodyVelocity.MaxForce \t= Vector3.new(1e4, 1e4, 1e4)\n\t\t\t\tbodyPosition.MaxForce \t= Vector3.new()\n\t\t\t\tstates.isInAir \t\t\t= true\n\n\t\t\t\tif externalVelocity.Y < -30 and not states.isFalling then\n\t\t\t\t\tsetCharacterMovementState(\"isFalling\", true)\n\n\t\t\t\tend\n\n\t\t\t\tif not startPositionForFalling and characterHitbox.Velocity.Y < -30 and states.isFalling then\n\t\t\t\t\tstartPositionForFalling = characterHitbox.Position\n\t\t\t\tend\n\n\t\t\t\tif states.isSprinting and myClientCharacterContainer and myClientCharacterContainer:FindFirstChild(\"entity\") then\n\t\t\t\t\tmyClientCharacterContainer.entity.RightFoot.ParticleEmitter.Enabled = false\n\t\t\t\t\tmyClientCharacterContainer.entity.LeftFoot.ParticleEmitter.Enabled \t= false\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tland()\n\t\t\tend\n\t\telse\n\t\t\t-- player is underwater, and swimming!\n\t\t\tbodyVelocity.MaxForce \t= Vector3.new(1e4, 1e4, 1e4)\n\t\t\tbodyPosition.MaxForce \t= Vector3.new()\n\n\t\t\t--bodyPosition.Position = downHitPosition + Vector3.new(0, height, 0)\n\t\tend\n\n\t\tlocal mouseMovementDirection = hitPosition and CFrame.new(\n\t\t\tcharacterHitbox.Position,\n\t\t\tVector3.new(hitPosition.X, characterHitbox.Position.Y, hitPosition.Z)\n\t\t).lookVector\n\n\t\tmouseMovementDirection = mouseMovementDirection and (mouseMovementDirection - Vector3.new(0, mouseMovementDirection.Y, 0)).unit\n\n\t\tif isMenuInFocus then\n\t\t\tmouseMovementDirection = characterHitbox.CFrame.lookVector * Vector3.new(1,0,1)\n\t\tend\n\n\t\t-- process final move direction\n\t\tlocal final_direction\n\t\tif not characterArrested then\n\t\t\tif movementAngle then\n\t\t\t\tlocal movementDirection = workspace.CurrentCamera.CFrame:toWorldSpace(CFrame.Angles(0, movementAngle, 0)).lookVector\n\t\t\t\tmovementDirection \t\t= (movementDirection - Vector3.new(0, movementDirection.Y, 0)).unit\n\n\t\t\t\tlocal baseSpeed = playerMovementSpeed\n\t\t\t\tif isPlayerSprintingAnimationPlaying then\n\t\t\t\t\tbaseSpeed = baseSpeed * 2 + 1\n\t\t\t\tend\n\n\t\t\t\tlocal moveSpeed = baseSpeed * math.clamp(playerWalkspeedMultiplier, 0.5, 1)\n\n\t\t\t\tmovementUnitVector \t= movementUnitVector or Vector3.new(1,0,0)\n\t\t\t\tmovementVelocity \t= movementDirection * moveSpeed * movementUnitVector.magnitude\n\n\t\t\t\t-- update player movement\n\t\t\t\tbodyVelocity.Velocity = velocityHandler:stepCurrentVelocity(step)\n\n\t\t\t\tif currentEquipType ~= \"bow\" and not IS_PLAYER_CAMERA_LOCKED and not isCastingSpell and mouseMovementDirection then\n\t\t\t\t\tif not states.isSprinting then\n\t\t\t\t\t\tlocal isInCone, isBehind, angleDiff, sign = isMouseInMovementCone(movementDirection, mouseMovementDirection, movementAngle)\n\t\t\t\t\t\tif isInCone then\n\t\t\t\t\t\t\tfinal_direction = CFrame.new(Vector3.new(), mouseMovementDirection)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tif not isBehind then\n\t\t\t\t\t\t\t\tfinal_direction = CFrame.Angles(0, MAX_CONE_ANGLE_DIFF * sign, 0) * CFrame.new(Vector3.new(), movementDirection)\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tfinal_direction = CFrame.new(Vector3.new(), movementDirection)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\telse\n\t\t\t\t\t\tfinal_direction = CFrame.new(Vector3.new(), movementDirection)\n\t\t\t\t\tend\n\t\t\t\telseif IS_PLAYER_CAMERA_LOCKED then\n\t\t\t\t\tfinal_direction = CFrame.new(Vector3.new(), camera.CFrame.lookVector * Vector3.new(1, 0, 1))\n\t\t\t\telseif mouseMovementDirection then\n\t\t\t\t\tfinal_direction = CFrame.new(Vector3.new(), mouseMovementDirection)\n\t\t\t\telse\n\t\t\t\t\tfinal_direction = CFrame.new(Vector3.new(), movementDirection)\n\t\t\t\tend\n\n\t\t\t\tassetFolder.bRef.Value = bodyVelocity\n\n\t\t\t\t-- movement logic\n\n\t\t\t\tsetCharacterMovementState(\"isMoving\", true)\n\n\t\t\telse\n\t\t\t\tif IS_PLAYER_CAMERA_LOCKED then\n\t\t\t\t\tfinal_direction = CFrame.new(Vector3.new(), camera.CFrame.lookVector * Vector3.new(1, 0, 1))\n\t\t\t\telseif mouseMovementDirection then\n\t\t\t\t\tfinal_direction = mouseMovementDirection and CFrame.new(Vector3.new(), mouseMovementDirection)\n\t\t\t\tend\n\n\t\t\t\tmovementVelocity \t\t= Vector3.new()\n\t\t\t\tbodyVelocity.Velocity \t= velocityHandler:stepCurrentVelocity(step)\n\n\t\t\t\t-- movement logic\n\n\t\t\t\tsetCharacterMovementState(\"isMoving\", false)\n\t\t\tend\n\t\telse\n\t\t\t-- step even if arrested, incase we manually set it!\n\t\t\tbodyVelocity.Velocity = velocityHandler:stepCurrentVelocity(step)\n\t\tend\n\n\t\t-- update the target listing\n\t\tupdateTargetsList()\n\n\t\tif manifestCurrentlyTargetted and currentEquipType ~= \"bow\" and not IS_PLAYER_CAMERA_LOCKED and not states.isSprinting and not isCastingSpell and final_direction  then\n\n\t\t\tlocal frac = ((manifestCurrentlyTargetted_distanceAway - 10) / (DISTANCE_AWAY_THRESHOLD - 10)) ^ 2\n\t\t\tlocal derivativeFrac = 2 * (1 / (DISTANCE_AWAY_THRESHOLD - 10)) * (manifestCurrentlyTargetted_distanceAway - 10)\n\n\t\t\tif derivativeFrac < 0 then\n\t\t\t\tfrac = 0\n\t\t\tend\n\n\t\t\tfrac = math.clamp(1 - frac, 0, 1)\n\n\t\t\tfinal_direction = final_direction:lerp(CFrame.new(\n\t\t\t\tcharacterHitbox.Position,\n\t\t\t\tVector3.new(manifestCurrentlyTargetted.Position.X, characterHitbox.Position.Y, manifestCurrentlyTargetted.Position.Z)\n\t\t\t), frac)\n\t\tend\n\n\n\n\t\tif not characterArrested and final_direction and player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.state.Value ~= \"dead\" and not isPlayerEmoting   then\n\t\t\tif IS_PLAYER_CAMERA_LOCKED then\n\t\t\t\tbodyGyro.CFrame = final_direction\n\t\t\telse\n\t\t\t\tbodyGyro.CFrame = bodyGyro.CFrame:lerp(final_direction, 0.1)\n\t\t\tend\n\n\t\t\tlocal diffAngle = Vector3.new(final_direction.lookVector.X, 0, final_direction.lookVector.Z).unit:Dot(Vector3.new(characterHitbox.CFrame.lookVector.X, 0, characterHitbox.CFrame.lookVector.Z).unit)\n\n\t\t\tif math.abs(1 - diffAngle) > 0.05 then\n\t\t\t\tif not states.isRotating then\n\t\t\t\t\tsetCharacterMovementState(\"isRotating\", true)\n\t\t\t\tend\n\n\t\t\telse\n\t\t\t\tif states.isRotating then\n\t\t\t\t\tsetCharacterMovementState(\"isRotating\", false)\n\t\t\t\tend\n\n\t\t\tend\n\n\t\tend\n\tend\n\n\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.state.Value ~= \"dead\"  then\n\t\tif characterOrientationUpdateConnection then\n\t\t\tcharacterOrientationUpdateConnection:disconnect()\n\t\t\tcharacterOrientationUpdateConnection = nil\n\t\tend\n\t\tcharacterOrientationUpdateConnection = runService.Stepped:connect(updateCharacterOrientationFaceManifest)\n\n\tend\nend\n\nlocal function doesPlayerHaveAbilityUnlocked(abilityId)\n\tlocal playerAbilitiesSlotDataCollection = network:invoke(\"getCacheValueByNameTag\", \"abilities\")\n\tif playerAbilitiesSlotDataCollection then\n\t\tfor _, abilitySlotData in pairs(playerAbilitiesSlotDataCollection) do\n\t\t\tif abilitySlotData.id == abilityId and abilitySlotData.rank > 0 then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\n\n\treturn false\nend\n\n\nlocal isJumping = false\nlocal hasDoubleJumped = false\nlocal function perform_forceJump(inputObject)\n\tif not player.Character or not player.Character.PrimaryPart then return end\n\tif isCharacterStunned() then return end\n\n\n\tif states.isJumping then\n\t\tif player.Character.PrimaryPart.state.Value ~= \"dead\" and not states.isExhausted and doesPlayerHaveAbilityUnlocked(7) and not states.isDoubleJumping and states.isInAir then\n\n\t\t\tif externalVelocity.Y > -60 then\n\t\t\t\tplayer.Character.PrimaryPart.stamina.Value = math.max(player.Character.PrimaryPart.stamina.Value - 0.5, 0)\n\t\t\t\tsetCharacterMovementState(\"isDoubleJumping\", true)\n\t\t\t\texternalVelocity = Vector3.new(externalVelocity.X, 80, externalVelocity.Z)\n\n\t\t\t\tnetwork:fire(\"stopChannels\", \"jump\")\n\t\t\t\tlastJump = tick()\n\t\t\tend\n\t\t\t--velocityHandler:applyJoltVelocity(Vector3.new(0, 75, 0))\n\t\tend\n\telse\n\t\t-- regular jump\n\t\tif player.Character.PrimaryPart.state.Value ~= \"dead\" and not states.isExhausted then\n\t\t\tplayer.Character.PrimaryPart.stamina.Value = math.max(player.Character.PrimaryPart.stamina.Value - 0.5, 0)\n\t\t\tsetCharacterMovementState(\"isJumping\", true)\n\n\t\t\tnetwork:fire(\"stopChannels\", \"jump\")\n\n\t\t\tlocal jumpPower = totalStats.jump\n\t\t\tvelocityHandler:applyJoltVelocity(Vector3.new(0, jumpPower, 0))\n\t\t\tlastJump = tick()\n\t\tend\n\tend\n\n\tif isPlayerUnderwater and inputObject then\n\t\tdelay(0.25, function()\n\t\t\tif isPlayerUnderwater and inputObject.UserInputState == Enum.UserInputState.Begin then\n\t\t\t\t-- still holding it down\n\n\t\t\t\tsetCharacterMovementState(\"isSwimming\", true)\n\t\t\t\tsetCharacterMovementState(\"isSprinting\", false)\n\t\t\t\tisPlayerSprintingEnabled = false\n\n\t\t\t\twhile isPlayerUnderwater and inputObject.UserInputState == Enum.UserInputState.Begin do\n\t\t\t\t\twait(0.05)\n\t\t\t\tend\n\n\t\t\t\tsetCharacterMovementState(\"isSwimming\", false)\n\t\t\t\tisPlayerSprintingEnabled = true\n\t\t\tend\n\t\tend)\n\tend\nend\n\nlocal function onInputBegan(inputObject, absorbed)\n\n\tif absorbed then\n\t\treturn false\n\tend\n\n\tif inputObject.KeyCode == Enum.KeyCode.W or inputObject.KeyCode == Enum.KeyCode.Up then\n\t\tisForward = true\n\telseif inputObject.KeyCode == Enum.KeyCode.S or inputObject.KeyCode == Enum.KeyCode.Down then\n\t\tisBackward = true\n\telseif inputObject.KeyCode == Enum.KeyCode.D then\n\t\tisLeftward = true\n\telseif inputObject.KeyCode == Enum.KeyCode.A then\n\t\tisRightward = true\n\telseif inputObject.KeyCode == Enum.KeyCode.LeftShift or inputObject.KeyCode == Enum.KeyCode.ButtonL3 and states.isMoving then\n\t\tset_sprinting(true)\n\telseif inputObject.KeyCode == Enum.KeyCode.Space or inputObject.KeyCode == Enum.KeyCode.LeftAlt or inputObject.KeyCode == Enum.KeyCode.ButtonA then\n\t\tif not characterArrested then\n\t\t\tif isPlayerJumpEnabled and not states.isSwimming then\n\t\t\t\tperform_forceJump(inputObject)\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction module.doJump()\n\tif not characterArrested then\n\t\tif isPlayerJumpEnabled then\n\t\t\tperform_forceJump()\n\t\tend\n\tend\nend\n\nfunction module.doSprint(val)\n\tset_sprinting(val)\nend\n\nlocal function signalBasicAttacking(val)\n\tbasicAttacking = val\nend\n\n\nlocal function onInputEnded(inputObject)\n\t--if inputObject.UserInputType == Enum.UserInputType.Keyboard then\n\n\t\tif inputObject.KeyCode == Enum.KeyCode.W or inputObject.KeyCode == Enum.KeyCode.Up then\n\t\t\tisForward = false\n\n\t\telseif inputObject.KeyCode == Enum.KeyCode.S or inputObject.KeyCode == Enum.KeyCode.Down then\n\t\t\tisBackward = false\n\t\telseif inputObject.KeyCode == Enum.KeyCode.D then\n\t\t\tisLeftward = false\n\t\telseif inputObject.KeyCode == Enum.KeyCode.A then\n\t\t\tisRightward = false\n\t\telseif inputObject.KeyCode == Enum.KeyCode.LeftShift --[[or inputObject.KeyCode == Enum.KeyCode.ButtonL3]] then\n\t\t\tset_sprinting(false)\n\t\telseif inputObject.KeyCode == Enum.KeyCode.P then\n\t\t\t--cycleThroughTargetsList()\n\t\tend\n\t--end\nend\n\nlocal isSetup = false\nlocal function onInputChanged(inputObject)\n\tif inputObject.UserInputType == Enum.UserInputType.MouseMovement then\n\t\tmouseMovementInputObject = inputObject\n\tend\nend\n\n\nlocal function onMyClientCharacterDied()\n\tif characterOrientationUpdateConnection then\n\t\tcharacterOrientationUpdateConnection:disconnect()\n\t\tcharacterOrientationUpdateConnection = nil\n\tend\n--\trunService:UnbindFromRenderStep(\"updateCharacterOrientation\")\nend\n\nlocal function onSetCharacterMovementStateInvoke(state, value, ...)\n\tif states[state] == nil or states[state] == value then return end\n\n\treturn setCharacterMovementState(state, value, ...)\nend\n\nlocal function getCurrentlyFacingManifest(updateFirst)\n\tif updateFirst then\n\t\tupdateTargetsList()\n\tend\n\treturn manifestCurrentlyTargetted\nend\n\nlocal function setIsCastingSpell(value)\n\tisCastingSpell = value\nend\n\nlocal function getMovementVelocity()\n\treturn movementVelocity\nend\n\nlocal function getTotalVelocity()\n\tlocal bodyVelocity = player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart:FindFirstChild(\"hitboxVelocity\")\n\n\tif bodyVelocity then\n\t\treturn bodyVelocity.Velocity\n\tend\n\n\treturn Vector3.new(0,0,0)\nend\n\nlocal function getCharacterMovementStates()\n\treturn states\nend\n\nlocal function onSetMovementVelocity(newMovementVelocity)\n\tmovementVelocity = newMovementVelocity\nend\n\nlocal function onSetIsJumpEnabled(isEnabled)\n\tisPlayerJumpEnabled = isEnabled\nend\n\nlocal function onSetIsSprintingEnabled(isEnabled)\n\tisPlayerSprintingEnabled = isEnabled\nend\n\nlocal function onPropogationRequestToSelf(nameTag, value)\n\tif nameTag == \"equipment\" then\n\t\tlocal weaponData do\n\t\t\tfor i, equipmentSlotData in pairs(value) do\n\t\t\t\tif equipmentSlotData.position == 1 then\n\t\t\t\t\tweaponData = equipmentSlotData\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif weaponData then\n\t\t\tcurrentEquipType = itemLookup[weaponData.id].equipmentType\n\t\tend\n\tend\nend\n\nlocal function setStamina(value, cureExhaustion)\n\tlocal char = player.Character\n\tif not char then return end\n\n\tlocal manifest = char.PrimaryPart\n\tif not manifest then return end\n\n\tlocal stamina = manifest:FindFirstChild(\"stamina\")\n\tif not stamina then return end\n\n\tlocal maxStamina = manifest:FindFirstChild(\"maxStamina\")\n\tif not maxStamina then return end\n\n\tif value == \"max\" then\n\t\tvalue = maxStamina.Value\n\tend\n\n\tstamina.Value = value\n\n\tif cureExhaustion then\n\t\tsetCharacterMovementState(\"isExhausted\", false)\n\tend\nend\n\nfunction module.init(Modules)\n\n\tplaceSetup = Modules.placeSetup\n\tnetwork = Modules.network\n\tclient_utilities = Modules.client_utilities\n\ttween = Modules.tween\n\tutilities = Modules.utilities\n\tterrainUtil = Modules.terrainUtil\n\tdamage = Modules.damage\n\tanimationInterface = Modules.animationInterface\n\n\tIGNORE_LIST = {placeSetup.getPlaceFoldersFolder()}\n\tentityRenderCollectionFolder = placeSetup.awaitPlaceFolder(\"entityRenderCollection\")\n\tentityManifestCollectionFolder = placeSetup.awaitPlaceFolder(\"entityManifestCollection\")\n\n\ttotalStats = network:invoke(\"getCacheValueByNameTag\", \"nonSerializeData\").statistics_final\n\n\tif player.Character then\n\t\tspawn(function()\n\t\t\tonCharacterAdded(player.Character)\n\t\tend)\n\tend\n\tnetwork:create(\"doesPlayerHaveStatusEffect\", \"BindableFunction\", \"OnInvoke\", doesCharacterHaveStatusEffect)\n\tnetwork:create(\"isCharacterStunned\", \"BindableFunction\", \"OnInvoke\", isCharacterStunned)\n\tnetwork:create(\"mobileMovementDirectionChanged\", \"BindableEvent\", \"Event\", mobileMovementDirectionChanged)\n\tnetwork:create(\"playerRequest_performEmote\", \"BindableFunction\", \"OnInvoke\", performEmote)\n\tnetwork:create(\"setCharacterArrested\", \"BindableFunction\", \"OnInvoke\", setCharacterArrested)\n\tnetwork:create(\"signal_menuFocusChanged\", \"BindableEvent\", \"Event\", signal_menuFocusChanged)\n\tnetwork:connect(\"myClientCharacterContainerChanged\", \"Event\", function() onCharacterAdded(player.Character) end)\n\tnetwork:connect(\"toggleCameraLockChanged\", \"Event\", function(newValue) IS_PLAYER_CAMERA_LOCKED = newValue end)\n\tnetwork:create(\"signalBasicAttacking\", \"BindableEvent\", \"Event\", signalBasicAttacking)\n\tnetwork:connect(\"playerStatisticsChanged\", \"OnClientEvent\", playerStatisticsChanged)\n\tuserInputService.InputBegan:connect(onInputBegan)\n\tuserInputService.InputChanged:connect(onInputChanged)\n\tuserInputService.InputEnded:connect(onInputEnded)\n\n\tnetwork:connect(\"myClientCharacterDied\", \"Event\", onMyClientCharacterDied)\n\n\tnetwork:create(\"stopChannels\", \"BindableEvent\")\n\n\tnetwork:create(\"setIsChanneling\", \"BindableFunction\", \"OnInvoke\", onSetIsChanneling)\n\tnetwork:create(\"setMovementVelocity\", \"BindableFunction\", \"OnInvoke\", onSetMovementVelocity)\n\tnetwork:create(\"setIsJumpEnabled\", \"BindableFunction\", \"OnInvoke\", onSetIsJumpEnabled)\n\tnetwork:create(\"setIsSprintingEnabled\", \"BindableFunction\", \"OnInvoke\", onSetIsSprintingEnabled)\n\n\tnetwork:create(\"characterStateChanged\", \"BindableEvent\")\n\tnetwork:create(\"setCharacterMovementState\", \"BindableFunction\", \"OnInvoke\", onSetCharacterMovementStateInvoke)\n\tnetwork:create(\"forceCharacterMovementState\", \"RemoteEvent\", \"OnClientEvent\", onSetCharacterMovementStateInvoke)\n\tnetwork:create(\"getCurrentlyFacingManifest\", \"BindableFunction\", \"OnInvoke\", getCurrentlyFacingManifest)\n\tnetwork:create(\"getMovementVelocity\", \"BindableFunction\", \"OnInvoke\", getMovementVelocity)\n\tnetwork:create(\"getTotalVelocity\", \"BindableFunction\", \"OnInvoke\", getTotalVelocity)\n\tnetwork:create(\"setIsCastingSpell\", \"BindableFunction\", \"OnInvoke\", setIsCastingSpell)\n\tnetwork:create(\"getCharacterMovementStates\", \"BindableFunction\", \"OnInvoke\", getCharacterMovementStates)\n\n\tnetwork:connect(\"playerStatisticsChanged\", \"OnClientEvent\", onPlayerStatisticsChanged)\n\tnetwork:connect(\"propogationRequestToSelf\", \"Event\", onPropogationRequestToSelf)\n\n\tnetwork:connect(\"setStamina\", \"OnClientEvent\", setStamina)\n\n\ttotal_statistics = network:invoke(\"getCacheValueByNameTag\", \"nonSerializeData\").statistics_final\n\n\tvelocityHandler = {} do\n\t\tlocal airResistance = 20\n\n\n\t\tfunction velocityHandler:applyJoltVelocity(vel)\n\t\t\tif isPlayerUnderwater then\n\t\t\t\tvel = vel * 0.5\n\t\t\tend\n\n\t\t\texternalVelocity = externalVelocity + vel\n\t\tend\n\n\t\t-- this is what i think of your oop damien\n\t\tlocal function jolt(vel)\n\t\t\tvelocityHandler:applyJoltVelocity(vel)\n\t\tend\n\n\t\tnetwork:create(\"applyJoltVelocityToCharacter\",\"BindableEvent\",\"Event\",jolt)\n\t\tnetwork:connect(\"deathTrapKnockback\", \"OnClientEvent\", jolt)\n\n\t\tlocal cameraUnderwater\n\n\t\tlocal underwaterBlur = Instance.new(\"BlurEffect\")\n\t\tunderwaterBlur.Size = 4\n\t\tunderwaterBlur.Enabled = false\n\t\tunderwaterBlur.Parent = game.Lighting\n\n\t\tlocal underwaterCorrect = Instance.new(\"ColorCorrectionEffect\")\n\t\tunderwaterCorrect.Saturation = 0.35\n\t\tunderwaterCorrect.Contrast = 0.05\n\t\tunderwaterCorrect.Enabled = false\n\t\tunderwaterCorrect.Parent = game.Lighting\n\n\t\tlocal underwaterBloom = Instance.new(\"BloomEffect\")\n\t\tunderwaterBloom.Enabled = false\n\t\tunderwaterBloom.Parent = game.Lighting\n\n\t\tlocal defaultReverb = game.SoundService.AmbientReverb\n\n\n\n\t\tfunction velocityHandler:stepCurrentVelocity(step)\n\t\t\tlocal characterHitbox \t= player.Character and player.Character.PrimaryPart\n\t\t\tlocal frictionApplied \t= false\n\t\t\tlocal observedVelocity \t= characterHitbox.Velocity\n\t\t\tlocal heatExhausted = doesCharacterHaveStatusEffect(\"heat exhausted\")\n\n\t\t\tif states.isSprinting and characterHitbox.Velocity.Magnitude > 0.2 then\n\t\t\t\tcharacterHitbox.stamina.Value = math.max(characterHitbox.stamina.Value - step, 0)\n\t\t\telseif not states.isExhausted and not states.isJumping and not states.isDoubleJumping then --and not states.isFalling then\n\t\t\t\tlocal recovery = totalStats.staminaRecovery\n\t\t\t\tif (tick() - lastJump) > 1 / (recovery) then\n\t\t\t\t\tlocal scalar = recovery\n\t\t\t\t\tif heatExhausted then\n\t\t\t\t\t\tscalar = scalar - 1\n\t\t\t\t\tend\n\n\t\t\t\t\tcharacterHitbox.stamina.Value = math.min(characterHitbox.stamina.Value + (characterHitbox.maxStamina.Value * step/3 * scalar), characterHitbox.maxStamina.Value)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif heatExhausted then\n\t\t\t\tcharacterHitbox.stamina.Value = math.max(characterHitbox.stamina.Value - step / 16, 0)\n\t\t\tend\n\n\t\t\tif characterHitbox.health.Value <= 0 or characterHitbox.state.Value == \"dead\" then\n\t\t\t\tif states.isExhausted then\n\t\t\t\t\tsetCharacterMovementState(\"isExhausted\", false)\n\t\t\t\tend\n\t\t\telseif characterHitbox.stamina.Value <= 0 and not states.isExhausted then\n\t\t\t\tsetCharacterMovementState(\"isSprinting\", false)\n\t\t\t\tsetCharacterMovementState(\"isExhausted\", true)\n\t\t\t\tspawn(function()\n\t\t\t\t\twait(2)\n\t\t\t\t\tcharacterHitbox.stamina.Value = 0\n\t\t\t\t\tsetCharacterMovementState(\"isExhausted\", false)\n\t\t\t\tend)\n\t\t\t\tnetwork:fireServer(\"playerWasExhausted\")\n\t\t\tend\n\n\t\t\tlocal desiredFOV = 70 + sprintFOV.Value + math.clamp(utilities.magnitude(observedVelocity) / 5 - 10, 0, 40)\n\t\t\tlocal currentFOV = workspace.CurrentCamera.FieldOfView\n\n\t\t\tif desiredFOV ~= currentFOV then\n\t\t\t\tlocal difference = desiredFOV - currentFOV\n\t\t\t\tlocal change = math.abs(difference) * step * 3 + 0.1\n\t\t\t\tif desiredFOV > currentFOV then\n\t\t\t\t\tif desiredFOV > currentFOV + change then\n\t\t\t\t\t\tcurrentFOV = currentFOV + change\n\t\t\t\t\telse\n\t\t\t\t\t\tcurrentFOV = desiredFOV\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tif desiredFOV < currentFOV - change then\n\t\t\t\t\t\tcurrentFOV = currentFOV - change\n\t\t\t\t\telse\n\t\t\t\t\t\tcurrentFOV = desiredFOV\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tif not workspace.CurrentCamera:FindFirstChild(\"overridden\") then\n\t\t\t\t\tworkspace.CurrentCamera.FieldOfView = currentFOV\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal char = game.Players.LocalPlayer.Character and game.Players.LocalPlayer.Character.PrimaryPart\n\n\t\t\tif char then\n\t\t\t\tif (not isPlayerUnderwater) and terrainUtil.isPointUnderwater(char.Position) then\n\n\t\t\t\t\texternalVelocity = externalVelocity / 5\n\t\t\t\t\tisPlayerUnderwater = true\n\n\t\t\t\t\t-- bubbles should be tied to your head being underwater, not your torso\n\t\t\t\t\t--[[\n\t\t\t\t\tif myClientCharacterContainer and myClientCharacterContainer:FindFirstChild(\"entity\") and myClientCharacterContainer.entity:FindFirstChild(\"Head\") and myClientCharacterContainer.entity.Head:FindFirstChild(\"MouthAttachment\") then\n\t\t\t\t\t\tmyClientCharacterContainer.entity.Head.MouthAttachment.bubbleParticles.Enabled = true\n\t\t\t\t\tend\n\t\t\t\t\t]]\n\n\t\t\t\t\tlocal soundMirror = game.ReplicatedStorage.assets.sounds:FindFirstChild(\"water_in\")\n\t\t\t\t\tif soundMirror then\n\t\t\t\t\t\tlocal sound = Instance.new(\"Sound\")\n\t\t\t\t\t\tfor property, value in pairs(game.HttpService:JSONDecode(soundMirror.Value)) do\n\t\t\t\t\t\t\tsound[property] = value\n\t\t\t\t\t\tend\n\t\t\t\t\t\tsound.Parent = game.Players.LocalPlayer.Character.PrimaryPart\n\t\t\t\t\t\tsound.PlaybackSpeed = math.random(105,120)/100\n\t\t\t\t\t\tsound:Play()\n\t\t\t\t\t\tgame.Debris:AddItem(sound,5)\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal splashPart = game.ReplicatedStorage:FindFirstChild(\"fishingBob\")\n\t\t\t\t\tif splashPart then\n\t\t\t\t\t\tsplashPart = splashPart:Clone()\n\t\t\t\t\t\tsplashPart.Transparency = 1\n\t\t\t\t\t\tsplashPart.CanCollide = false\n\t\t\t\t\t\tsplashPart.CFrame = CFrame.new() + char.Position\n\t\t\t\t\t\tsplashPart.splash.Color = ColorSequence.new(workspace.Terrain.WaterColor)\n\t\t\t\t\t\tsplashPart.splash:Emit(20)\n\t\t\t\t\t\tsplashPart.Parent = workspace.CurrentCamera\n\t\t\t\t\t\tgame.Debris:AddItem(splashPart,5)\n\t\t\t\t\tend\n\n\t\t\t\t\tnetwork:fireServer(\"onPlayerEnteredWater\", char.Position)\n\n\t\t\t\telseif isPlayerUnderwater and not terrainUtil.isPointUnderwater(char.Position - Vector3.new(0, 0.5, 0)) then\n\n\t\t\t\t\t--[[\n\t\t\t\t\tif myClientCharacterContainer and myClientCharacterContainer:FindFirstChild(\"entity\") and myClientCharacterContainer.entity:FindFirstChild(\"Head\") and myClientCharacterContainer.entity.Head:FindFirstChild(\"MouthAttachment\") then\n\t\t\t\t\t\tmyClientCharacterContainer.entity.Head.MouthAttachment.bubbleParticles.Enabled = false\n\t\t\t\t\tend\n\t\t\t\t\t]]\n\n\n\t\t\t\t\tisPlayerUnderwater = false\n\n\t\t\t\t\tif states.isSwimming then\n\t\t\t\t\t\tsetCharacterMovementState(\"isSwimming\", false)\n\t\t\t\t\t\tsetCharacterMovementState(\"isJumping\", false)\n\t\t\t\t\t\tsetCharacterMovementState(\"isDoubleJumping\", false)\n\n\t\t\t\t\t\tperform_forceJump()\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal soundMirror = game.ReplicatedStorage.assets.sounds:FindFirstChild(\"water_out\")\n\t\t\t\t\tif soundMirror then\n\t\t\t\t\t\tlocal sound = Instance.new(\"Sound\")\n\t\t\t\t\t\tfor property, value in pairs(game.HttpService:JSONDecode(soundMirror.Value)) do\n\t\t\t\t\t\t\tsound[property] = value\n\t\t\t\t\t\tend\n\t\t\t\t\t\tsound.Parent = game.Players.LocalPlayer.Character.PrimaryPart\n\t\t\t\t\t\tsound.PlaybackSpeed = math.random(105,120)/100\n\t\t\t\t\t\tsound:Play()\n\t\t\t\t\t\tgame.Debris:AddItem(sound,5)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif terrainUtil.isPointUnderwater(workspace.CurrentCamera.CFrame.Position) then\n\t\t\t\tif not cameraUnderwater then\n\t\t\t\t\tcameraUnderwater = true\n\t\t\t\t\tgame.SoundService.AmbientReverb = \"UnderWater\"\n\t\t\t\t\tunderwaterBlur.Enabled = true\n\t\t\t\t\tunderwaterCorrect.Enabled = true\n\t\t\t\t\tunderwaterBloom.Enabled = true\n\t\t\t\tend\n\t\t\telseif cameraUnderwater and not terrainUtil.isPointUnderwater(workspace.CurrentCamera.CFrame.Position + Vector3.new(0, 0.25, 0)) then\n\t\t\t\tcameraUnderwater = false\n\t\t\t\tgame.SoundService.AmbientReverb = defaultReverb\n\t\t\t\tunderwaterBlur.Enabled = false\n\t\t\t\tunderwaterCorrect.Enabled = false\n\t\t\t\tunderwaterBloom.Enabled = false\n\t\t\tend\n\n\t\t\tif not states.isSwimming and (utilities.magnitude(externalVelocity) > 0 or states.isInAir) then\n\n\t\t\t\t-- Collision velocity decrement\n\t\t\t\tlocal expectedVelocity = externalVelocity + movementVelocity\n\t\t\t\tlocal observedVelocity = characterHitbox.Velocity\n\n\t\t\t\tlocal collideDecrementX = math.abs(expectedVelocity.X - observedVelocity.X) * step * 3\n\t\t\t\tlocal collideDecrementZ = math.abs(expectedVelocity.Z - observedVelocity.Z)\t* step * 3\n\n\t\t\t\tlocal externalXZ = Vector3.new(externalVelocity.X, 0, externalVelocity.Z)\n\n\t\t\t\tlocal gravityDecrement = Vector3.new(0, workspace.Gravity * step, 0)\n\n\t\t\t\tif isPlayerUnderwater then\n\t\t\t\t\tgravityDecrement = gravityDecrement / 5\n\t\t\t\tend\n\n\t\t\t\t-- Normal velocity decrement (Friction/Air resist)\n\t\t\t\tif states.isInAir then\n\t\t\t\t\tlocal volumeGoal = math.clamp((observedVelocity.magnitude - 100) / 500, windBaseVolume, 2)\n\t\t\t\t\tlocal delta = math.abs(volumeGoal - wind.Volume)\n\t\t\t\t\tif wind.Volume < volumeGoal then\n\t\t\t\t\t\twind.Volume = wind.Volume + math.clamp(delta, 0.01, 0.1)\n\t\t\t\t\telse\n\t\t\t\t\t\twind.Volume = wind.Volume - math.clamp(delta, 0.01, 0.1)\n\t\t\t\t\tend\n\n\t\t\t\t\t--local airDecrement = airResistance * step\n\n\t\t\t\t\tlocal airDecrementX = airResistance * step * (math.abs(externalVelocity.X) / utilities.magnitude(externalXZ)) + collideDecrementX\n\t\t\t\t\tlocal airDecrementZ = airResistance * step * (math.abs(externalVelocity.Z) / utilities.magnitude(externalXZ)) + collideDecrementZ\n\n\t\t\t\t\tif math.abs(externalVelocity.X) > airDecrementX then\n\t\t\t\t\t\texternalVelocity = Vector3.new(externalVelocity.X - airDecrementX * math.sign(externalVelocity.X), externalVelocity.Y, externalVelocity.Z)\n\t\t\t\t\telse\n\t\t\t\t\t\texternalVelocity = Vector3.new(0, externalVelocity.Y, externalVelocity.Z)\n\t\t\t\t\tend\n\n\t\t\t\t\tif math.abs(externalVelocity.Z) > airDecrementZ then\n\t\t\t\t\t\texternalVelocity = Vector3.new(externalVelocity.X, externalVelocity.Y, externalVelocity.Z - airDecrementZ * math.sign(externalVelocity.Z))\n\t\t\t\t\telse\n\t\t\t\t\t\texternalVelocity = Vector3.new(externalVelocity.X, externalVelocity.Y, 0)\n\t\t\t\t\tend\n\n\t\t\t\t\texternalVelocity = externalVelocity - gravityDecrement\n\t\t\t\telse\n\t\t\t\t\tif wind.Volume > windBaseVolume then\n\t\t\t\t\t\twind.Volume = wind.Volume - 0.02\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal frictionDecrementX = friction * step * (math.abs(externalVelocity.X) / utilities.magnitude(externalXZ)) + collideDecrementX\n\t\t\t\t\tlocal frictionDecrementZ = friction * step * (math.abs(externalVelocity.Z) / utilities.magnitude(externalXZ)) + collideDecrementZ\n\n\t\t\t\t\tif frictionDecrementX > 0 then\n\t\t\t\t\t\tif math.abs(externalVelocity.X) > frictionDecrementX then\n\t\t\t\t\t\t\texternalVelocity = Vector3.new(externalVelocity.X - frictionDecrementX * math.sign(externalVelocity.X), externalVelocity.Y, externalVelocity.Z)\n\t\t\t\t\t\t\tfrictionApplied = true\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\texternalVelocity = Vector3.new(0, externalVelocity.Y, externalVelocity.Z)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif frictionDecrementZ > 0 then\n\t\t\t\t\t\tif math.abs(externalVelocity.Z) > frictionDecrementZ then\n\t\t\t\t\t\t\texternalVelocity = Vector3.new(externalVelocity.X, externalVelocity.Y, externalVelocity.Z - frictionDecrementZ * math.sign(externalVelocity.Z))\n\t\t\t\t\t\t\tfrictionApplied = true\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\texternalVelocity = Vector3.new(externalVelocity.X, externalVelocity.Y, 0)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif externalVelocity.Y >= gravityDecrement.Y then\n\t\t\t\t\t\texternalVelocity = externalVelocity - gravityDecrement\n\t\t\t\t\telse\n\t\t\t\t\t\texternalVelocity = Vector3.new(externalVelocity.X, 0, externalVelocity.Z)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tif wind.Volume > windBaseVolume then\n\t\t\t\t\twind.Volume = wind.Volume - 0.02\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif myClientCharacterContainer and myClientCharacterContainer:FindFirstChild(\"entity\") then\n\t\t\t\tif frictionApplied then\n\t\t\t\t\tlocal speed = math.clamp(utilities.magnitude(externalVelocity) / 3, 5, 60)\n\n\t\t\t\t\tmyClientCharacterContainer.entity.RightFoot.smoke.Rate \t= speed\n\t\t\t\t\tmyClientCharacterContainer.entity.LeftFoot.smoke.Rate \t= speed\n\t\t\t\tend\n\n\t\t\t\tmyClientCharacterContainer.entity.RightFoot.smoke.Enabled = frictionApplied\n\t\t\t\tmyClientCharacterContainer.entity.LeftFoot.smoke.Enabled = frictionApplied\n\t\t\tend\n\n\t\t\tlocal velocityMulti = 1\n\t\t\tif isPlayerUnderwater then\n\t\t\t\tvelocityMulti = 0.7\n\t\t\tend\n\n\t\t\tif player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.state.Value == \"dead\" then\n\t\t\t\tmovementVelocity = Vector3.new()\n\t\t\tend\n\n\t\t\tif isPlayerUnderwater and states.isSwimming then\n\t\t\t\texternalVelocity = Vector3.new()\n\n\t\t\t\treturn movementVelocity * velocityMulti + Vector3.new(0, 16, 0)\n\t\t\telse\n\t\t\t\treturn (movementVelocity + externalVelocity) * velocityMulti\n\t\t\tend\n\t\tend\n\tend\n\n\t-- todo: make it so you can't exploit this, coming soon.\n\n\twhile not player.Character or not player.Character.PrimaryPart do\n\t\twait(0.5)\n\tend\n\n\t-- todo: fix\n\tlocal timeSinceLastInput = tick()\n\tuserInputService.InputBegan:connect(function()\n\t\ttimeSinceLastInput = tick()\n\tend)\n\n\tspawn(function()\n\t\twhile true do\n\t\t\tif player.Character and player.Character.PrimaryPart then\n\t\t\t\tif player.Character.PrimaryPart.state.Value == \"idling\" and (tick() - timeSinceLastInput) > 5 and not isPlayerEmoting and not basicAttacking then\n\t\t\t\t\tlocal options \t= {\"idling_kicking\"}\n\t\t\t\t\tlocal option \t= options[math.random(#options)]\n\n\t\t\t\t\tanimationInterface:replicatePlayerAnimationSequence(\"emoteAnimations\", option)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\twait(math.random(5, 10))\n\t\tend\n\tend)\n\n\tsetupRenderSteppedConnection()\nend\n\nreturn module"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/coreRenderServices/appearance_manager.lua",
    "content": "local appearence_manager = {}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal modules = require(replicatedStorage:WaitForChild(\"modules\"))\nlocal mapping = modules.load(\"mapping\")\n\nlocal function isBodyPart(obj)\n\treturn obj:IsA(\"BasePart\") and replicatedStorage.playerBaseCharacter:FindFirstChild(obj.Name) and replicatedStorage.playerBaseCharacter[obj.Name]:IsA(\"BasePart\")\nend\n\nfunction appearence_manager.ApplySkinColor(appearanceData, renderCharacter, accessoryLookup, assets)\n    if appearanceData and appearanceData.accessories.skinColorId then\n\t\tfor _, obj in pairs(renderCharacter:GetChildren()) do\n\t\t\tif obj:IsA(\"BasePart\") and isBodyPart(obj) then\n\t\t\t\tobj.Color = assets.accessories.skinColor:FindFirstChild(tostring(appearanceData.accessories.skinColorId or 1)).Value\n\t\t\tend\n\t\tend\n\telse\n\t\tfor _, obj in pairs(renderCharacter:GetChildren()) do\n\t\t\tif obj:IsA(\"BasePart\") and isBodyPart(obj) then\n\t\t\t\tobj.Color = BrickColor.new(\"Light orange\").Color\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction appearence_manager.LoadAppearence(accessoryLookup, appearanceData, hatEquipmentData, itemLookup, renderCharacter)\n\t--damien this took 5 min-=\n\t-- cry me a river\n\tlocal hairColor = accessoryLookup.hairColor:FindFirstChild(tostring(appearanceData.accessories.hairColorId or 1)).Value\n\tlocal shirtColor = accessoryLookup.shirtColor:FindFirstChild(tostring(appearanceData.accessories.shirtColorId or 1)).Value\n\n\tfor accessoryType, id in pairs(appearanceData.accessories) do\n\t\tif accessoryType == \"hair\" and hatEquipmentData then\n\t\t\tlocal itemBaseData = itemLookup[hatEquipmentData.id]\n\n\t\t\tif itemBaseData then\n\t\t\t\tlocal equipmentHairType_accessory = itemBaseData.equipmentHairType or 1\n\t\t\t\tif equipmentHairType_accessory == mapping.equipmentHairType.partial then\n\t\t\t\t\tid = id .. \"_clipped\"\n\n\t\t\t\telseif equipmentHairType_accessory == mapping.equipmentHairType.none then\n\t\t\t\t\t-- no hair\n\t\t\t\t\tid = \"\"\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif accessoryLookup:FindFirstChild(accessoryType) then\n\t\t\tlocal accessoryToLookIn = replicatedStorage.hairClipped:FindFirstChild(tostring(id)) or accessoryLookup[accessoryType]:FindFirstChild(tostring(id))\n\n\t\t\tif accessoryToLookIn then\n\t\t\t\tfor _, accessoryPartContainer in pairs(accessoryToLookIn:GetChildren()) do\n\t\t\t\t\tif renderCharacter:FindFirstChild(accessoryPartContainer.Name) then\n\n\t\t\t\t\t\tif accessoryPartContainer:FindFirstChild(\"shirtTag\") then\n\t\t\t\t\t\t\trenderCharacter[accessoryPartContainer.Name].Color = shirtColor\n\t\t\t\t\t\telseif accessoryPartContainer:FindFirstChild(\"colorOverride\") then\n\t\t\t\t\t\t\trenderCharacter[accessoryPartContainer.Name].Color = accessoryPartContainer.Color\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tfor _, accessoryPart in pairs(accessoryPartContainer:GetChildren()) do\n\t\t\t\t\t\t\tif accessoryPart:IsA(\"BasePart\") then\n\t\t\t\t\t\t\t\tlocal accessory = accessoryPart:Clone()\n\t\t\t\t\t\t\t\t\taccessory.Anchored \t\t= false\n\t\t\t\t\t\t\t\t\taccessory.CanCollide \t= false\n\n\t\t\t\t\t\t\t\tif accessory.Name == \"hair_Head\" then\n\t\t\t\t\t\t\t\t\taccessory.Color = hairColor\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif accessory.Name == \"shirt\" or accessory:FindFirstChild(\"shirtTag\") then\n\t\t\t\t\t\t\t\t\taccessory.Color = shirtColor\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tlocal projectionWeld = Instance.new(\"Motor6D\")\n\t\t\t\t\t\t\t\tprojectionWeld.Parent = accessory\n\t\t\t\t\t\t\t\tprojectionWeld.Name = \"projectionWeld\"\n\t\t\t\t\t\t\t\tprojectionWeld.Part0 = accessory\n\t\t\t\t\t\t\t\tprojectionWeld.Part1 = renderCharacter[accessoryPartContainer.Name]\n\t\t\t\t\t\t\t\tprojectionWeld.C0 = CFrame.new()\n\t\t\t\t\t\t\t\tprojectionWeld.C1 = accessoryPartContainer.CFrame:toObjectSpace(accessoryPart.CFrame)\n\n\t\t\t\t\t\t\t\taccessory.Name = \"!! ACCESSORY !!\"\n\t\t\t\t\t\t\t\taccessory.Parent = renderCharacter\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\nend\n\nreturn appearence_manager"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/coreRenderServices/bow_manager.lua",
    "content": "local bow_manager = {}\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal httpService = game:GetService(\"HttpService\")\n\nlocal modules = require(replicatedStorage:WaitForChild(\"modules\"))\nlocal network \t\t= modules.load(\"network\")\nlocal projectile \t= modules.load(\"projectile\")\n\nlocal assets = game.ReplicatedStorage:WaitForChild(\"assets\")\n\nfunction bow_manager.int__updateRenderCharacter(renderCharacter,inventoryCountLookup,equipmentSlotData,configuration,itemLookup)\n    -- arrow is funny hehe\n    -- todo: customize this per bow\n    local strap = assets.entities.ArrowUpperTorso2.strap:Clone()\n    strap.Anchored \t\t= false\n    strap.CanCollide \t= false\n\n    local strapprojectionWeld = Instance.new(\"Motor6D\", strap)\n    strapprojectionWeld.Name \t= \"projectionWeld\"\n    strapprojectionWeld.Part0 \t= strap\n    strapprojectionWeld.Part1 \t= renderCharacter.UpperTorso\n    strapprojectionWeld.C0 \t\t= CFrame.new()\n    strapprojectionWeld.C1 \t\t= assets.entities.ArrowUpperTorso2.CFrame:toObjectSpace(assets.entities.ArrowUpperTorso2.strap.CFrame)\n\n    strap.Name \t\t= \"!! ARROW !!\"\n    strap.Parent \t= renderCharacter\n\n    local quiver = assets.entities.ArrowUpperTorso2.quiver:Clone()\n    quiver.Anchored \t\t= false\n    quiver.CanCollide \t= false\n\n    local quiverprojectionWeld = Instance.new(\"Motor6D\", quiver)\n    quiverprojectionWeld.Name \t= \"projectionWeld\"\n    quiverprojectionWeld.Part0 \t= quiver\n    quiverprojectionWeld.Part1 \t= renderCharacter.UpperTorso\n    quiverprojectionWeld.C0 \t\t= CFrame.new()\n    quiverprojectionWeld.C1 \t\t= assets.entities.ArrowUpperTorso2.CFrame:toObjectSpace(assets.entities.ArrowUpperTorso2.quiver.CFrame)\n\n    quiver.Name \t\t= \"!! ARROW !!\"\n    quiver.Parent \t= renderCharacter\n\n    -- represent the arrows\n\n    local arrows = inventoryCountLookup[equipmentSlotData.id] or 0\n    local arrowParts \t= math.clamp(math.floor(arrows / configuration.getConfigurationValue(\"arrowsPerArrowPartVisualization\")) + 1, 0, configuration.getConfigurationValue(\"maxArrowPartsVisualization\"))\n\n    for ai = 1, arrowParts do\n        local arrow \t\t= itemLookup[equipmentSlotData.id].module.manifest:Clone()\n        arrow.CanCollide \t= false\n        arrow.Anchored \t\t= false\n        arrow.Parent \t\t= quiver\n\n        local xRan, yRan = math.random() * 2 - 1, math.random() * 2 - 1\n\n        local arrowWeld \t= Instance.new(\"Motor6D\", quiver)\n        arrowWeld.Name \t\t= \"projectionWeld\"\n        arrowWeld.Part0 \t= quiver\n        arrowWeld.Part1 \t= arrow\n        arrowWeld.C0 \t\t= quiver.Attachment.CFrame\n        arrowWeld.C1 \t\t= CFrame.Angles(xRan * math.rad(15), 0, yRan * math.rad(15))\n    end\nend\n\n\n\nfunction bow_manager.PlayAnimation(playerStats,animationToBePlayed,entityManifest,animationSequenceName,configuration,animationInterface,associatePlayer,characterEntityAnimationTracks,client,damage,animationName,renderEntityData,utilities,assetFolder,entitiesFolder,extraData)\n    -- bows, make sure all other bow animations stop!\n    if animationSequenceName == \"bowAnimations\" then\n\n        local currentWeaponManifest = network:invoke(\"getCurrentWeaponManifest\")\n        if currentWeaponManifest then\n            currentWeaponManifest = currentWeaponManifest:IsA(\"Model\") and currentWeaponManifest.PrimaryPart or currentWeaponManifest\n        end\n\n        animationInterface:stopPlayingAnimationsByAnimationCollectionName(characterEntityAnimationTracks, \"bowAnimations\")\n\n        if animationName == \"stretching_bow_stance\" then\n            renderEntityData.currentPlayerWeaponAnimations.stretch:Play(nil, nil, 1)\n            utilities.playSound(\"bowDraw\", currentWeaponManifest)\n\n            local arrow = assetFolder.arrow:Clone()\n            renderEntityData.stanceArrow = arrow\n            arrow.arrowWeld.Part0 = renderEntityData.currentPlayerWeapon.slackRopeRepresentation\n            arrow.arrowWeld.C0 = CFrame.Angles(-math.pi / 2, 0, 0) * CFrame.new(0, (-arrow.Size.Y/2) - 0.1, 0)\n            arrow.Parent = entitiesFolder\n\n        elseif animationName == \"firing_bow_stance\" then\n            local stanceArrowSpeed = 400\n            local stanceArrowGravity = 0\n\n            renderEntityData.currentPlayerWeaponAnimations.fire:Play()\n            utilities.playSound(\"bowFireStance\", currentWeaponManifest)\n\n            local shotCFrame = CFrame.new()\n            local visualArrow = renderEntityData.stanceArrow\n            renderEntityData.stanceArrow = nil\n            if visualArrow then\n                shotCFrame = visualArrow.CFrame\n                visualArrow:Destroy()\n            end\n\n            local function ringEffect(cframe, size)\n                size = size or 1\n\n                local ring = script:FindFirstChild(\"ring\"):Clone()\n\n                ring.CFrame = cframe * CFrame.Angles(math.pi/2,0,0)\n                ring.Size = Vector3.new(2, 0.2, 2) * size\n                ring.Parent = entitiesFolder\n\n                local duration = 0.5\n                tween(ring, {\"Size\"}, {ring.Size * 4 * size}, duration, Enum.EasingStyle.Quad)\n                tween(ring, {\"Transparency\"}, {1}, duration, Enum.EasingStyle.Linear)\n                game:GetService(\"Debris\"):AddItem(ring, duration)\n            end\n\n            local arrow = assetFolder.arrow:Clone()\n            arrow.Anchored = true\n            arrow.CFrame = shotCFrame\n            arrow.Trail.Enabled = true\n            arrow.Trail.Lifetime = 1.5\n            arrow.Trail.WidthScale = NumberSequence.new(1, 8)\n            arrow.Parent = entitiesFolder\n\n            local unitDirection = (extraData[\"mouse-target-position\"] - shotCFrame.Position).Unit\n\n            local targetsHit = {}\n\n            ringEffect((CFrame.new(Vector3.new(), unitDirection) + shotCFrame.Position) * CFrame.new(0, 0, -2))\n\n            local lifetime = 10\n            --game:GetService(\"Debris\"):AddItem(arrow, lifetime)\n\n            local ringLast = tick()\n            local ringTime = 0.05\n\n            projectile.createProjectile(\n                shotCFrame.Position,\n                unitDirection,\n                stanceArrowSpeed,\n                arrow,\n                function(hitPart, hitPosition, hitNormal, hitMaterial)\n                    local canDamageTarget, target = damage.canPlayerDamageTarget(game.Players.LocalPlayer, hitPart)\n                    if canDamageTarget and target then\n                        if not targetsHit[target] then\n                            targetsHit[target] = true\n\n                            utilities.playSound(\"bowArrowImpact\", arrow)\n                            ringEffect(arrow.CFrame * CFrame.Angles(math.pi / 2, 0, 0))\n\n                            if associatePlayer == client and canDamageTarget then\n                                network:fire(\"requestEntityDamageDealt\", target, hitPosition, \"equipment\", nil, \"ranger stance\")\n                            end\n                        end\n\n                        return true\n                    else\n                        arrow.Trail.Enabled = false\n                        game:GetService(\"Debris\"):AddItem(arrow, arrow.Trail.Lifetime)\n                    end\n                end,\n                function(t)\n                    local since = tick() - ringLast\n                    if since >= ringTime then\n                        ringLast = tick()\n                        ringEffect(arrow.CFrame * CFrame.Angles(math.pi / 2, 0, 0), 0.4)\n                    end\n\n                    return CFrame.Angles(math.pi / 2, 0, 0)\n                end,\n                projectile.makeIgnoreList{\n                    entityManifest,\n                    renderEntityData.entityContainer,\n                },\n                true,\n                stanceArrowGravity,\n                lifetime\n            )\n            print(renderEntityData.entityContainer:GetFullName())\n\n        elseif animationName == \"stretching_bow\" then\n            if renderEntityData.firingAnimationStoppedConnection then\n                renderEntityData.firingAnimationStoppedConnection:disconnect()\n                renderEntityData.firingAnimationStoppedConnection = nil\n            end\n\n            local bowPullBackTime \t= configuration.getConfigurationValue(\"bowPullBackTime\")\n            local atkspd \t\t\t= (extraData and extraData.attackSpeed) or 0\n\n            renderEntityData.bowStrechAnimationStopped = renderEntityData.currentPlayerWeaponAnimations.stretch.Stopped:connect(onBowStrechingAnimationStopped)\n            renderEntityData.currentPlayerWeaponAnimations.stretch:Play(\n                0.1,\n                1,\n                (renderEntityData.currentPlayerWeaponAnimations.stretch.Length / bowPullBackTime) * (1 + atkspd)\n            )\n\n            utilities.playSound(\"bowDraw\", currentWeaponManifest)\n\n            local drawStartTime = tick()\n\n            local numArrows = extraData.numArrows or 1\n            local firingSeed = extraData.firingSeed or 1\n\n            -- set-up the arrow\n            renderEntityData.currentArrows = {}\n            renderEntityData.firingSeed = firingSeed\n\n            -- how far should each arrow be rotated from eachother\n            local arrowAnglePadding = 3\n\n            local startingAngle = -((numArrows - 1)*arrowAnglePadding)/2\n\n            local closestAngleToZero = math.huge\n\n            for i = 1, numArrows do\n                local newArrow = assetFolder.arrow:Clone()\n\n                newArrow.Parent = workspace.CurrentCamera\n\n                local angleOffset = startingAngle + (i-1)*arrowAnglePadding\n\n                table.insert(renderEntityData.currentArrows, {\n                    arrow = newArrow,\n                    angleOffset = angleOffset,\n                    orientation = CFrame.Angles(0, math.pi, 0) * CFrame.Angles(math.pi/2,0,0) * CFrame.Angles(math.rad(angleOffset*3),0,0)\n                })\n\n                if math.abs(angleOffset) < closestAngleToZero then\n                    renderEntityData.primaryArrow = newArrow -- used for auto-targeting in bow damage interface\n                    closestAngleToZero = math.abs(angleOffset)\n                end\n            end\n\n            renderEntityData.currentDrawStartTime \t= drawStartTime\n\n            if utilities.doesPlayerHaveEquipmentPerk(associatePlayer, \"overdraw\") then\n                --renderEntityData.currentArrow.Size = renderEntityData.currentArrow.Size * 2\n                for _, arrowData in pairs(renderEntityData.currentArrows) do\n                    local arrow = arrowData.arrow\n                    arrow.Size = arrow.Size * 2\n                end\n            end\n\n            -- I have no clue what this is for so I will not touch it ~nimblz\n            delay(configuration.getConfigurationValue(\"maxBowChargeTime\"), function()\n                if renderEntityData.currentDrawStartTime == drawStartTime and renderEntityData.currentArrows then\n                    for _, arrowData in pairs(renderEntityData.currentArrows) do\n                        local arrow = arrowData.arrow\n\n                        arrow.Material \t\t= Enum.Material.Neon\n                        arrow.BrickColor \t= BrickColor.new(\"Institutional white\")\n                    end\n                end\n            end)\n\n            -- apply welds\n            for _, arrowData in pairs(renderEntityData.currentArrows) do\n                local arrow = arrowData.arrow\n\n                arrow.arrowWeld.Part0 = renderEntityData.currentPlayerWeapon.slackRopeRepresentation\n                arrow.arrowWeld.C0 = arrowData.orientation * CFrame.new(0, (-arrow.Size.Y/2) - 0.1, 0)\n            end\n\n            -- update state\n            renderEntityData.weaponState = \"streched\"\n            onEntityStateChanged(entityManifest.state.Value)\n\n            -- do this.. hehe\n            if animationToBePlayed then\n                if typeof(characterEntityAnimationTracks[animationSequenceName][animationName]) == \"Instance\" then\n                    characterEntityAnimationTracks[animationSequenceName][animationName]:Play(\n                        0.1,\n                        1,\n                        (renderEntityData.currentPlayerWeaponAnimations.stretch.Length / bowPullBackTime) * (1 + atkspd)\n                    )\n                elseif typeof(characterEntityAnimationTracks[animationSequenceName][animationName]) == \"table\" then\n                    animationToBePlayed = animationToBePlayed[1]\n\n                    for i, obj in pairs(characterEntityAnimationTracks[animationSequenceName][animationName]) do\n                        obj:Play(\n                            0.1,\n                            1,\n                            (renderEntityData.currentPlayerWeaponAnimations.stretch.Length / bowPullBackTime) * (1 + atkspd)\n                        )\n                    end\n                end\n            end\n\n            return\n        elseif animationName == \"firing_bow\" and renderEntityData.currentArrows then\n            if renderEntityData.bowStrechAnimationStopped then\n                renderEntityData.bowStrechAnimationStopped:disconnect()\n                renderEntityData.bowStrechAnimationStopped = nil\n            end\n\n            if renderEntityData.currentPlayerWeaponAnimations.stretch.IsPlaying then\n                renderEntityData.currentPlayerWeaponAnimations.stretch:Stop()\n            end\n\n            if renderEntityData.currentPlayerWeaponAnimations.stretchHold.IsPlaying then\n                renderEntityData.currentPlayerWeaponAnimations.stretchHold:Stop()\n            end\n\n            if extraData.canceled then\n                for _, arrowData in pairs(renderEntityData.currentArrows) do\n                    arrowData.arrow:Destroy()\n                end\n\n                renderEntityData.currentArrows = nil\n\n                animationToBePlayed = nil\n\n                -- force reset state\n                renderEntityData.weaponState = nil\n                onEntityStateChanged(entityManifest.state.Value)\n            else\n                local function onFiringAnimationStopped()\n                    if renderEntityData.firingAnimationStoppedConnection then\n                        renderEntityData.firingAnimationStoppedConnection:disconnect()\n                        renderEntityData.firingAnimationStoppedConnection = nil\n                    end\n\n                    -- update state\n                    renderEntityData.weaponState = nil\n                    onEntityStateChanged(entityManifest.state.Value)\n                end\n\n                renderEntityData.firingAnimationStoppedConnection = renderEntityData.currentPlayerWeaponAnimations.fire.Stopped:connect(onFiringAnimationStopped)\n                renderEntityData.currentPlayerWeaponAnimations.fire:Play()\n\n                utilities.playSound(\"bowFire\", currentWeaponManifest)\n\n                local isMagical = playerStats.int >= 30 -- Is magical, use AOE\n\n                local explodeRadius = 1.5\n                local explodeDurration = 1 / 4\n\n                if playerStats.int >= 70 then\n                    explodeRadius = 2.5\n                end\n\n                if playerStats.int >= 150 then\n                    explodeRadius = 4\n                    explodeDurration = 3 / 8\n                end\n\n                local maxPierces = utilities.calculatePierceFromStr(playerStats.str)\n                local numArrows = #renderEntityData.currentArrows\n\n                local arrowSpeed = (renderEntityData.weaponBaseData.projectileSpeed or 200) * math.clamp(extraData.bowChargeTime / configuration.getConfigurationValue(\"maxBowChargeTime\"), 0.1, 1)\n\n                local speedScalar = maxPierces - (numArrows/2)\n                speedScalar = math.max(speedScalar, -1) -- this clamps slowest possible speed to 50% of default\n                arrowSpeed = arrowSpeed + (arrowSpeed * speedScalar * 0.5) -- 50% speed buff per pierc\n\n                if utilities.doesPlayerHaveEquipmentPerk(associatePlayer, \"overdraw\") then\n                    arrowSpeed = arrowSpeed * 2\n                end\n\n\n                local unitDirection, adjusted_targetPosition = projectile.getUnitVelocityToImpact_predictiveByAbilityExecutionData(\n                    renderEntityData.currentPlayerWeapon.slackRopeRepresentation.Position,\n                    renderEntityData.weaponBaseData.projectileSpeed or 200, -- act as if you were shooting at full\n                    extraData\n                )\n\n                local shotOrigin = CFrame.new(\n                    renderEntityData.currentPlayerWeapon.slackRopeRepresentation.Position,\n                    renderEntityData.currentPlayerWeapon.slackRopeRepresentation.Position + unitDirection\n                ) * CFrame.new(0,0,-1.5)\n\n                -- do launch effect\n                if maxPierces > 0 then\n                    local durration = 0.25\n                    local newRing = script:FindFirstChild(\"ring\"):Clone()\n\n                    newRing.CFrame = shotOrigin * CFrame.new(0,0,-1) * CFrame.Angles(math.pi/2,0,0)\n                    newRing.Size = Vector3.new(2, 0.2, 2)\n                    newRing.Parent = workspace.CurrentCamera\n\n                    tween(newRing, {\"Size\"}, {Vector3.new(3 + (maxPierces*1),0.2,3 + (maxPierces*1))}, durration, Enum.EasingStyle.Quad)\n                    tween(newRing, {\"Transparency\"}, {1}, durration, Enum.EasingStyle.Linear)\n\n                    local explosionBall = Instance.new(\"Part\")\n                    local scaler = Instance.new(\"SpecialMesh\")\n\n                    explosionBall.Size = Vector3.new(3+maxPierces,3+maxPierces,2)\n                    explosionBall.Color = Color3.fromRGB(255,255,255)\n                    explosionBall.Anchored = true\n                    explosionBall.CanCollide = false\n                    explosionBall.Material = Enum.Material.Neon\n                    explosionBall.CFrame = shotOrigin * CFrame.new(0,0,-1.5)\n\n                    scaler.MeshType = Enum.MeshType.Sphere\n                    scaler.Parent = explosionBall\n\n                    explosionBall.Parent = workspace.CurrentCamera\n\n                    local finalLength = 6+(maxPierces*2)\n\n                    tween(explosionBall, {\"Transparency\"}, {1}, durration/2, Enum.EasingStyle.Linear)\n                    tween(explosionBall, {\"Size\"}, {Vector3.new(0.5,0.5,finalLength)}, durration/2, Enum.EasingStyle.Quad)\n                    tween(explosionBall, {\"CFrame\"}, {shotOrigin * CFrame.new(0,0,-(1.5 + finalLength/2))}, durration/2, Enum.EasingStyle.Quad)\n\n                    game:GetService(\"Debris\"):AddItem(newRing, durration)\n                    game:GetService(\"Debris\"):AddItem(explosionBall, durration)\n                end\n\n                local arrowRandomizer = Random.new(renderEntityData.firingSeed)\n\n                local guid = httpService:GenerateGUID(false)\n\n                -- shoot arrows\n                for _, arrowData in pairs(renderEntityData.currentArrows) do\n                    local arrow = arrowData.arrow\n                    arrow.arrowWeld:Destroy()\n                    arrow.Anchored = true\n\n\n                    local pierceCount = 0\n                    local entityPierceBlacklist = {}\n\n\n                    local shotOrientation = CFrame.new(Vector3.new(0,0,0), unitDirection)\n                    local displacedShotOrientation = shotOrientation\n\n\n                    if numArrows < 4 then\n                        displacedShotOrientation = shotOrientation *CFrame.Angles(\n                            arrowRandomizer:NextNumber(-0.025, 0.025),\n                            math.rad(arrowData.angleOffset),\n                            0\n                        )\n\n                    else\n                        displacedShotOrientation = shotOrientation * CFrame.Angles(\n                            math.rad(numArrows * 0.8) * arrowRandomizer:NextNumber(-1, 1),\n                            math.rad(arrowData.angleOffset) + (math.rad(5) * arrowRandomizer:NextNumber(-1, 1)),\n                            0\n                        )\n\n                    end\n\n                    local finalUnitDirection = displacedShotOrientation.LookVector\n\n                    if numArrows == 1 and pierceCount >= 1 then\n                        finalUnitDirection = unitDirection\n                    end\n\n                    if arrow:FindFirstChild(\"Trail\") then\n                        arrow.Trail.Enabled = true\n                    end\n\n                    renderEntityData.currentDrawStartTime \t= nil\n\n                    projectile.createProjectile(\n                        shotOrigin.Position,\n                        finalUnitDirection,\n                        arrowSpeed, --renderEntityData.weaponBaseData.projectileSpeed or 200,\n                        arrow,\n                        function(hitPart, hitPosition, hitNormal, hitMaterial)\n                            --[[\n                            if hitNormal then\n                                currentArrow.CFrame = CFrame.new(hitPosition, hitPosition + hitNormal) * CFrame.Angles(-math.rad(90), 0, 0)\n                            end\n                            ]]\n\n\n                            local function explode(needsToHit)\n                                local explosionBall = Instance.new(\"Part\")\n                                local scaler = Instance.new(\"SpecialMesh\")\n\n                                explosionBall.Size = Vector3.new(explodeRadius*2,explodeRadius*2,explodeRadius*2)\n                                explosionBall.Shape = Enum.PartType.Ball\n                                explosionBall.Color = Color3.fromRGB(255,255,255)\n                                explosionBall.Anchored = true\n                                explosionBall.CanCollide = false\n                                explosionBall.Material = Enum.Material.Neon\n                                explosionBall.CFrame = CFrame.new(hitPosition)\n\n                                scaler.Scale = Vector3.new(0,0,0)\n                                scaler.MeshType = Enum.MeshType.Sphere\n                                scaler.Parent = explosionBall\n\n                                explosionBall.Parent = workspace.CurrentCamera\n\n                                tween(explosionBall, {\"Transparency\"}, {1}, explodeDurration, Enum.EasingStyle.Linear)\n                                tween(explosionBall, {\"Color\"}, {Color3.fromRGB(0,255,100)}, explodeDurration, Enum.EasingStyle.Linear)\n                                tween(scaler, {\"Scale\"}, {Vector3.new(1,1,1) * 1.25}, explodeDurration, Enum.EasingStyle.Quint)\n                                game:GetService(\"Debris\"):AddItem(explosionBall, explodeDurration*1.15)\n\n                                -- do some AOE dmg\n                                if associatePlayer == client then\n                                    for i, v in pairs(damage.getDamagableTargets(client)) do\n                                        local vSize = (v.Size.X + v.Size.Y + v.Size.Z)/6\n                                        if (v.Position - hitPosition).magnitude <= (explodeRadius) + vSize and v ~= needsToHit then\n                                            delay(0.1, function()\n                                                network:fire(\"requestEntityDamageDealt\", v, hitPosition, \"equipment\", nil, nil, guid)\n                                            end)\n                                        end\n                                    end\n                                    if needsToHit then\n                                        delay(0.1, function()\n                                            network:fire(\"requestEntityDamageDealt\", needsToHit, hitPosition, \"equipment\", nil, nil, guid)\n                                        end)\n                                    end\n                                end\n                            end\n\n                            local function ring(initialTransparency, initialRadius, finalRadius, lifetime)\n                                initialTransparency = initialTransparency or 3/4\n                                initialRadius = initialRadius or 1\n                                finalRadius = finalRadius or 2\n                                lifetime = lifetime or 1/3\n                                local newRing = script:FindFirstChild(\"ring\"):Clone()\n\n                                newRing.CFrame = arrow.CFrame\n                                newRing.Transparency = initialTransparency\n                                newRing.Size = Vector3.new(initialRadius, 0.5, initialRadius)\n                                newRing.Parent = workspace.CurrentCamera\n\n                                tween(newRing, {\"Size\"}, {Vector3.new(finalRadius,0.2,finalRadius)}, lifetime*1.15, Enum.EasingStyle.Quint)\n                                tween(newRing, {\"Transparency\"}, {1}, lifetime, Enum.EasingStyle.Linear)\n\n                                game:GetService(\"Debris\"):AddItem(newRing, lifetime*1.15)\n                            end\n\n                            if hitPart then\n                                if (hitPart:IsDescendantOf(entityRenderCollectionFolder) or hitPart:IsDescendantOf(entityManifestCollectionFolder)) then -- entity impact\n                                    -- pierce check\n                                    local canDamageTarget, trueTarget = damage.canPlayerDamageTarget(game.Players.LocalPlayer, hitPart)\n                                    if trueTarget and not entityPierceBlacklist[trueTarget] then\n                                        entityPierceBlacklist[trueTarget] = true\n\n\n                                        if isMagical then\n                                            -- play magic sound\n                                            utilities.playSound(\"magicAttack\", arrow)\n                                        else\n                                            utilities.playSound(\"bowArrowImpact\", arrow)\n                                        end\n\n                                        if isMagical then -- do aoe dmg\n                                            explode(trueTarget)\n                                        else -- do direct damage\n                                            if associatePlayer == client and canDamageTarget then -- we shot this arrow, dmg the entity\n                                                network:fire(\"requestEntityDamageDealt\", trueTarget, hitPosition, \"equipment\", nil, nil, guid)\n                                            end\n                                        end\n\n                                        pierceCount = pierceCount + 1\n                                        if pierceCount <= maxPierces then -- did pierce an entity\n                                            local intensity = maxPierces - (pierceCount-1)\n                                            intensity = math.clamp(intensity,1,8)\n\n                                            local intensityCalls = {\n                                                function() ring(2/3, \t1, \t\t2,\t\t1/3) end, -- 1\n                                                function() ring(1/2, \t1.25, \t3,\t\t1/3) end, -- 2\n                                                function() ring(1/3, \t1.5, \t4,\t\t1/2) end, -- 3\n                                                function() ring(1/4, \t2, \t\t5,\t\t1/2) end, -- 4\n                                                function() ring(1/5, \t1.5, \t6,\t\t1/2) end, -- 5\n                                                function() ring(1/8, \t2, \t\t7,\t\t2/3) end, -- 6\n                                                function() ring(1/8, \t2.5, \t7.5,\t2/3) end, -- 7\n                                                function() ring(0, \t\t3, \t\t8,\t\t2/3) end, -- 8\n                                            }\n\n                                            (intensityCalls[intensity] or intensityCalls[3])()\n\n                                            return true\n                                        else\n                                            arrow.Anchored = false\n                                            weld(arrow, hitPart)\n                                            game:GetService(\"Debris\"):AddItem(arrow, 3)\n                                            return false\n                                        end\n                                    elseif trueTarget and entityPierceBlacklist[trueTarget] then\n                                        return true\n                                    end\n\n\n                                else -- world impact\n                                    if arrow:FindFirstChild(\"impact\") then\n                                        local hitColor = hitPart.Color\n                                        if hitPart == workspace.Terrain then\n                                            if hitMaterial ~= Enum.Material.Water then\n                                                hitColor = hitPart:GetMaterialColor(hitMaterial)\n                                            else\n                                                hitColor = BrickColor.new(\"Cyan\").Color\n                                            end\n                                        end\n\n                                        local emitPart = Instance.new(\"Part\")\n                                        emitPart.Size = Vector3.new(0.1,0.1,0.1)\n                                        emitPart.Transparency = 1\n                                        emitPart.Anchored = true\n                                        emitPart.CanCollide = false\n                                        emitPart.CFrame = (arrow.CFrame - arrow.CFrame.p) + hitPosition\n                                        local impact = arrow.impact:Clone()\n                                        impact.Parent = emitPart\n                                        emitPart.Parent = workspace.CurrentCamera\n                                        impact.Color = ColorSequence.new(hitColor)\n                                        impact:Emit(10)\n                                        game:GetService(\"Debris\"):AddItem(emitPart,3)\n                                        game:GetService(\"Debris\"):AddItem(arrow, 3)\n                                        tween(arrow, {\"Transparency\"}, {1}, 3, Enum.EasingStyle.Linear)\n                                    end\n                                    if isMagical then\n                                        explode()\n                                    end\n                                    return false\n                                end\n                            end\n                        end,\n\n                        function(t)\n                            return CFrame.Angles(math.rad(90), 0, 0)\n                        end,\n\n                        -- ignore list\n                        {entityManifest; renderEntityData.entityContainer},\n\n                        -- points to next position\n                        true\n                    )\n\n                    renderEntityData.currentArrows = nil\n                end\n\n\n            end\n        end\n    end\nend\n\n\nreturn bow_manager"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/coreRenderServices/chat_manager.lua",
    "content": "-- variables\nlocal MAX_CHAT_BUBBLE_COUNT = 3\n\n-- essentials\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal players = game:GetService(\"Players\")\n\nlocal playerScripts = players.LocalPlayer:WaitForChild(\"PlayerScripts\")\n\nlocal modules = require(replicatedStorage:WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nlocal runService = game:GetService(\"RunService\")\nlocal assetFolder = playerScripts:WaitForChild(\"assets\")\n\nlocal function getOldestChatBubble(chats)\n\tlocal oldest\n\tlocal lowestLayoutOrder = 99\n\tfor _, chat in pairs(chats) do\n\t\tif chat:IsA(\"GuiObject\") and chat.LayoutOrder < lowestLayoutOrder then\n\t\t\toldest = chat\n\t\t\tlowestLayoutOrder = chat.LayoutOrder\n\t\tend\n\tend\n\treturn oldest\nend\n\nlocal function setPrimaryChatBubble(chatBubble)\n\tif not chatBubble.titleFrame.Visible then\n\t\tif game.Players.LocalPlayer.Character and chatBubble:IsDescendantOf(game.Players.LocalPlayer.Character) then\n\t\t\treturn false\n\t\tend\n\t\tlocal size = chatBubble.Size\n\t\tif chatBubble.titleFrame.title.Text ~= \"\" then\n\t\t\tchatBubble.titleFrame.Visible = true\n\t\t\tchatBubble.Size = size + UDim2.new(0, 0, 0, 10)\n\t\t\tchatBubble.contents.Position = chatBubble.contents.Position + UDim2.new(0, 0, 0, 5)\n\t\t\tlocal dif = (chatBubble.titleFrame.AbsoluteSize.X + 20) - chatBubble.AbsoluteSize.X\n\t\t\tif dif > 0 then\n\t\t\t\tchatBubble.Size = size + UDim2.new(0, dif, 0, 0)\n\t\t\tend\n\t\tend\n\n\tend\nend\n\nlocal function getChatTagPartForEntity(entityContainer)\n\tfor _, chatTagPart in pairs(game.CollectionService:GetTagged(\"chatTag\")) do\n\t\tif chatTagPart.Parent == entityContainer then\n\t\t\treturn chatTagPart\n\t\tend\n\tend\nend\n\n\n\nlocal function createChatTagPart(entityContainer, offset, rangeMulti)\n\t--[[\n\tlocal chatTag \t= entityContainer.PrimaryPart:FindFirstChilfd(\"ChatTag\") or assetFolder.ChatTag:Clone()\n\tchatTag.Parent \t= entityContainer.PrimaryPart\n\t]]\n\n\tlocal chatTag = entityContainer:FindFirstChild(\"chatGui\") or assetFolder.misc.chatGui:Clone()\n\tchatTag.Parent = entityContainer\n\tchatTag.Adornee = entityContainer.PrimaryPart\n\tchatTag.Enabled = true\n\n\tlocal rangeMultiTag = Instance.new(\"NumberValue\")\n\trangeMultiTag.Name = \"rangeMulti\"\n\trangeMultiTag.Value = rangeMulti or 1\n\trangeMultiTag.Parent = chatTag\n\n\toffset = offset or Vector3.new()\n\tlocal offsetTag = Instance.new(\"Vector3Value\")\n\toffsetTag.Name = \"offset\"\n\toffsetTag.Value = offset\n\toffsetTag.Parent = chatTag\n\n\tchatTag.ExtentsOffsetWorldSpace = chatTag.ExtentsOffsetWorldSpace + offset\n\n\tgame.CollectionService:AddTag(chatTag,\"chatTag\")\n\treturn chatTag\nend\n\n\n\nlocal function displayChatMessageFromChatTagPart(chatTagPart, message, speakerName)\n\t--local chatTag = chatTagPart:FindFirstChild(\"SurfaceGui\")\n\tlocal chatTag = chatTagPart\n\tif chatTag then\n\t\tlocal newChatBubble = chatTag.chatTemplate:clone()\n\t\tnewChatBubble.titleFrame.title.Text = speakerName or \"\"\n\t\tlocal titleBounds = game.TextService:GetTextSize(newChatBubble.titleFrame.title.Text, newChatBubble.titleFrame.title.TextSize, newChatBubble.titleFrame.title.Font, Vector2.new()).X + 20\n\t\tnewChatBubble.titleFrame.Size = UDim2.new(0,titleBounds,0,32)\n\n\t\tnewChatBubble.titleFrame.Visible = false\n\n\t\tlocal existingChatBubbles = {}\n\t\tfor i,chatBubble in pairs(chatTag.chat:GetChildren()) do\n\t\t\tif chatBubble:IsA(\"GuiObject\") then\n\t\t\t\tchatBubble.LayoutOrder = chatBubble.LayoutOrder - 1\n\t\t\t\ttable.insert(existingChatBubbles, chatBubble)\n\t\t\tend\n\t\tend\n\n\t\tif #existingChatBubbles >= MAX_CHAT_BUBBLE_COUNT then\n\t\t\tlocal oldest = getOldestChatBubble(existingChatBubbles)\n\t\t\toldest:Destroy()\n\t\tend\n\n\t\tnewChatBubble.LayoutOrder = 10\n\t\tnewChatBubble.Parent = chatTag.chat\n\n\t\tlocal dialogueText, yOffset, xOffset = network:invoke(\"createTextFragmentLabels\",newChatBubble.contents, {{text = message, textColor3 = Color3.fromRGB(200,200,200)}} )\n\t\tif yOffset < 18 then\n\t\t\tnewChatBubble.Size = UDim2.new(0, xOffset + 20 , 0, yOffset + 26)\n\t\telse\n\t\t\tnewChatBubble.Size = UDim2.new(1, 0, 0, yOffset + 26)\n\t\tend\n\n\t\tlocal newOldest = getOldestChatBubble(chatTag.chat:GetChildren())\n\t\tif newOldest then\n\t\t\tsetPrimaryChatBubble(newOldest)\n\t\tend\n\n\t\tnewChatBubble.Visible = true\n\n\t\tspawn(function()\n\t\t\twait(15)\n\t\t\tif newChatBubble and newChatBubble.Parent then\n\t\t\t\tnewChatBubble:Destroy()\n\t\t\t\tlocal newOldest = getOldestChatBubble(chatTag.chat:GetChildren())\n\t\t\t\tif newOldest then\n\t\t\t\t\tsetPrimaryChatBubble(newOldest)\n\t\t\t\tend\n\t\t\tend\n\t\tend)\n\tend\nend\n\n\n\ngame.ReplicatedStorage:WaitForChild(\"DefaultChatSystemChatEvents\").OnMessageDoneFiltering.OnClientEvent:connect(function(messageInfo, rio)\n\t--  {\"ExtraData\":{\"Tags\":[],\"ChatColor\":null,\"NameColor\":null},\"IsFiltered\":true,\"MessageType\":\"Message\",\"IsFilterResult\":true,\n\t--\t\"Time\":1539139847,\"ID\":0,\"FromSpeaker\":\"berezaa\",\"Message\":\"## #### ##### #### #### ##\",\"OriginalChannel\":\"All\",\"SpeakerUserId\":5000861,\n\t--  \"MessageLength\":26}\n\tlocal yeet = false\n\n\tif yeet == true then\n\t-- confirm\n\t\tif messageInfo.IsFilterResult or runService:IsStudio() then\n\t\t\tlocal player = game.Players:GetPlayerByUserId(messageInfo.SpeakerUserId)\n\t\t\tlocal message = messageInfo.Message\n\n\t\t\tif player and player.Character and player.Character.PrimaryPart and message then\n\t\t\t\tlocal renderEntityData = entitiesBeingRendered[player.Character.PrimaryPart]\n\t\t\t\tif not renderEntityData or not renderEntityData.entityContainer.PrimaryPart then return false end\n\n\t\t\t\tlocal chatTag = renderEntityData.entityContainer:FindFirstChild(\"chatGui\")\n\t\t\t\tif chatTag then\n\t\t\t\t\tdisplayChatMessageFromChatTagPart(chatTag, message, player.Name)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend)\n\n\n\nnetwork:create(\"getChatTagPartForEntity\", \"BindableFunction\", \"OnInvoke\", getChatTagPartForEntity)\nnetwork:create(\"createChatTagPart\", \"BindableFunction\", \"OnInvoke\", createChatTagPart)\nnetwork:create(\"displayChatMessageFromChatTagPart\", \"BindableFunction\", \"OnInvoke\", displayChatMessageFromChatTagPart)\n\nreturn true"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/coreRenderServices/init.lua",
    "content": "local Container = {} do\n    for _, service in pairs(script:GetChildren()) do\n        local success, contents = pcall(function()\n            local module = require(service)\n\n            if type(module) == \"table\" and module.init then\n                --module:init()\n            end;\n\n            return module\n        end)\n\n        if success then\n            Container[service.Name] = contents\n        else\n            warn(\"DEBUG:\", service.Name, \"has failed to load, [Error Message]: \\n\"..contents)\n        end;\n    end;\nend;\n\nfunction Container:Hook(tab, mod)\n    return setmetatable(tab, Container[mod])\nend;\n\nreturn function(key)\n    if not Container[key] then\n        local iterations = 0\n\n        repeat\n            wait(1)\n            iterations = iterations + 1\n        until Container[key] or iterations > 10\n\n        return Container[key] \n    else\n        return Container[key]\n    end;\nend;"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/coreRenderServices/item_manager.lua",
    "content": "local item_manager = {}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal modules = require(replicatedStorage:WaitForChild(\"modules\"))\nlocal detection = modules.load(\"detection\")\nlocal network = modules.load(\"network\")\nlocal mapping = modules.load(\"mapping\")\n\nlocal itemDataLookup = require(replicatedStorage.itemData)\nlocal itemLookup = replicatedStorage.assets.items\n\nfunction item_manager.getCurrentlyEquippedForRenderCharacter(renderCharacter)\n\tlocal currentlyEquipped = {}\n\n\tfor i, obj in pairs(renderCharacter:GetChildren()) do\n\t\tif obj:IsA(\"BasePart\") or obj:IsA(\"Model\") then\n\t\t\tlocal accessoryType, accessoryId, accessorySlot = string.match(obj.Name, \"(%w+)_(%d+)_(%d+)\")\n\n\t\t\tif accessoryType and accessoryId then\n\t\t\t\taccessoryId \t= tonumber(accessoryId)\n\t\t\t\taccessorySlot \t= tonumber(accessorySlot)\n\n\t\t\t\tif accessoryType == \"EQUIPMENT\" then\n\t\t\t\t\tlocal equipmentBaseData = itemDataLookup[tonumber(accessoryId)]\n\n\t\t\t\t\tcurrentlyEquipped[tostring(accessorySlot)] = {\n\t\t\t\t\t\tbaseData \t= equipmentBaseData;\n\t\t\t\t\t\tmanifest \t= obj;\n\t\t\t\t\t}\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n    return currentlyEquipped\nend\n\nlocal function isCurrentlyEquipped(currentlyEquipped, equipmentSlotData)\n\tlocal equipmentBaseData = itemDataLookup[equipmentSlotData.id]\n\n\tif currentlyEquipped[equipmentBaseData.equipmentSlot] then\n\t\tif equipmentSlotData.id == currentlyEquipped[tostring(equipmentBaseData.equipmentSlot)].baseData.id then\n\t\t\treturn true\n\t\tend\n\tend\n\n\treturn false\nend\n\nfunction item_manager.GetWeaponStateAppendment(renderEntityData)\n    local weaponStateAppendment\n    local currentlyEquipped = item_manager.getCurrentlyEquippedForRenderCharacter(renderEntityData.entityContainer.entity)\n\n\tif currentlyEquipped[\"1\"] and currentlyEquipped[\"1\"].baseData.equipmentType then\n        -- weaponState weapons should never overlap with dual wielding (BOW IN PARTICULAR)\n        if renderEntityData.weaponState then\n            weaponStateAppendment = \"_\" .. renderEntityData.weaponState\n        elseif currentlyEquipped[\"1\"] and currentlyEquipped[\"11\"] then\n            if currentlyEquipped[\"11\"].baseData.equipmentType == \"sword\" then\n                weaponStateAppendment = \"_dual\"\n            elseif currentlyEquipped[\"11\"].baseData.equipmentType == \"shield\" then\n                weaponStateAppendment = \"AndShield\"\n            end\n        end\n    end\n\n    return weaponStateAppendment\nend\n\nfunction item_manager.GetCurrentlyPlayingAnimation(animationNameToLookFor,weaponStateAppendment,characterEntityAnimationTracks,renderCharacter)\n\tlocal currentlyEquipped = item_manager.getCurrentlyEquippedForRenderCharacter(renderCharacter)\n\tlocal currentPlayingStateAnimation\n\n\tif weaponStateAppendment then\n\t\tif currentlyEquipped[\"1\"] and currentlyEquipped[\"1\"].baseData and currentlyEquipped[\"1\"].baseData.equipmentType then\n\t\t\tlocal fullAnimationName = animationNameToLookFor..\"_\"..currentlyEquipped[\"1\"].baseData.equipmentType..weaponStateAppendment\n\n\t\t\tcurrentPlayingStateAnimation =\n\t\t\t\tcharacterEntityAnimationTracks.movementAnimations[fullAnimationName] or\n\t\t\t\tcharacterEntityAnimationTracks.movementAnimations[animationNameToLookFor]\n\t\telse\n\t\t\tcurrentPlayingStateAnimation = characterEntityAnimationTracks.movementAnimations[animationNameToLookFor]\n\t\tend\n\telse\n\t\tcurrentPlayingStateAnimation = characterEntityAnimationTracks.movementAnimations[animationNameToLookFor]\n\tend\n\n\treturn currentPlayingStateAnimation\nend\n\nfunction item_manager.RefreshStateAnimation(entityManifest,renderEntityData,weaponType)\n    local needStateChange\n\n    if entityManifest.entityType.Value == \"character\" then\n        local currentlyEquipped = item_manager.getCurrentlyEquippedForRenderCharacter(renderEntityData.entityContainer.entity)\n        if currentlyEquipped[\"1\"] then\n            if weaponType == currentlyEquipped[\"1\"].baseData.equipmentType then\n                needStateChange = true\n            end\n        end\n    end\n\n    return needStateChange\nend\n\nfunction item_manager.EquipNewItem(appearanceData,renderCharacter,_entityManifest,entitiesBeingRendered,animationInterface,associatePlayer,client,assetFolder)\n    local rightGrip, leftGrip, backMount, hipMount, neckMount =\n\trenderCharacter[\"RightHand\"]:FindFirstChild(\"Grip\"),\n\trenderCharacter[\"LeftHand\"]:FindFirstChild(\"Grip\"),\n\trenderCharacter[\"UpperTorso\"]:FindFirstChild(\"BackMount\"),\n\trenderCharacter[\"LowerTorso\"]:FindFirstChild(\"HipMount\"),\n\trenderCharacter[\"UpperTorso\"]:FindFirstChild(\"BackMount\")\n\n    local currentlyEquipped = item_manager.getCurrentlyEquippedForRenderCharacter(renderCharacter)\n\n\tfor i, equipmentData in pairs(appearanceData.equipment) do\n\n\t\tif not isCurrentlyEquipped(currentlyEquipped, equipmentData) then\n\t\t\tif equipmentData.position == mapping.equipmentPosition.weapon or equipmentData.position == mapping.equipmentPosition[\"offhand\"] then\n\t\t\t\tlocal weaponBaseData = itemDataLookup[equipmentData.id]\n\t\t\t\tlocal weaponVisualFolder = itemLookup[weaponBaseData.module.Name]\n\n\n\t\t\t\tif weaponBaseData and (weaponVisualFolder:FindFirstChild(\"manifest\") or weaponVisualFolder:FindFirstChild(\"container\")) then\n\t\t\t\t\tlocal weaponManifest\n\t\t\t\t\tlocal dye \t\t\t\t\t\t\t= equipmentData.dye\n\t\t\t\t\tlocal weaponGripType \t\t\t\t= weaponBaseData.gripType or 1\n\t\t\t\t\tlocal gripContainerOverrideCFrame \t= nil\n\n\t\t\t\t\t-- secondary weapons always left gripped\n\t\t\t\t\tif equipmentData.position == mapping.equipmentPosition[\"offhand\"] then\n\t\t\t\t\t\tweaponGripType = mapping.gripType.left\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal container = weaponVisualFolder:FindFirstChild(\"container\")\n\t\t\t\t\tif container then\n\t\t\t\t\t\tcontainer = container:FindFirstChild(\"RightHand\") or container:FindFirstChild(\"LeftHand\")\n\t\t\t\t\t\tcontainer = container:Clone()\n\n\t\t\t\t\t\tlocal weaponToCopy = container:FindFirstChild(\"manifest\") or container.PrimaryPart\n\n\t\t\t\t\t\tif weaponToCopy:IsA(\"BasePart\") then\n\t\t\t\t\t\t\tfor i,v in pairs(container:GetChildren()) do\n\t\t\t\t\t\t\t\tif v ~= weaponToCopy then\n\t\t\t\t\t\t\t\t\tv.Parent = weaponToCopy\n\t\t\t\t\t\t\t\t\tif v:IsA(\"BasePart\") then\n\t\t\t\t\t\t\t\t\t\tif dye then\n\t\t\t\t\t\t\t\t\t\t\tv.Color =  Color3.new(v.Color.r * dye.r/255, v.Color.g * dye.g/255, v.Color.b * dye.b/255)\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif dye then\n\t\t\t\t\t\t\t\t-- yes im that lazy\n\t\t\t\t\t\t\t\tlocal v = weaponToCopy\n\t\t\t\t\t\t\t\tweaponToCopy.Color =  Color3.new(v.Color.r * dye.r/255, v.Color.g * dye.g/255, v.Color.b * dye.b/255)\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tweaponManifest = weaponToCopy\n\t\t\t\t\t\t\tgripContainerOverrideCFrame = weaponToCopy.CFrame:toObjectSpace(weaponManifest.Parent.CFrame)\n\n\t\t\t\t\t\telseif weaponToCopy:IsA(\"Model\") then\n\t\t\t\t\t\t\t-- render bow\n\n\t\t\t\t\t\t\tfor i,v in pairs(weaponToCopy:GetDescendants()) do\n\t\t\t\t\t\t\t\tif v:IsA(\"BasePart\") then\n\t\t\t\t\t\t\t\t\tif dye then\n\t\t\t\t\t\t\t\t\t\tv.Color = Color3.new(v.Color.r * dye.r/255, v.Color.g * dye.g/255, v.Color.b * dye.b/255)\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tweaponManifest \t\t\t\t= weaponToCopy\n\t\t\t\t\t\t\tgripContainerOverrideCFrame = weaponToCopy.PrimaryPart.CFrame:toObjectSpace(container.CFrame)\n\t\t\t\t\t\tend\n\t\t\t\t\telseif weaponVisualFolder:FindFirstChild(\"manifest\") then\n\t\t\t\t\t\tweaponManifest = weaponVisualFolder.manifest:Clone()\n\t\t\t\t\t\tif dye then\n\t\t\t\t\t\t\t-- yes im that lazy\n\t\t\t\t\t\t\tlocal v = weaponManifest\n\t\t\t\t\t\t\tweaponManifest.Color =  Color3.new(v.Color.r * dye.r/255, v.Color.g * dye.g/255, v.Color.b * dye.b/255)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tweaponManifest.Name \t\t= \"EQUIPMENT_\" .. weaponBaseData.id .. \"_\" .. equipmentData.position\n\t\t\t\t\tweaponManifest.Parent \t\t= renderCharacter\n\n\t\t\t\t\tif weaponManifest:IsA(\"BasePart\") then\n\t\t\t\t\t\tweaponManifest.Anchored \t= false\n\t\t\t\t\t\tweaponManifest.CanCollide \t= false\n\t\t\t\t\telseif weaponManifest:IsA(\"Model\") then\n\t\t\t\t\t\tfor i, obj in pairs(weaponManifest:GetChildren()) do\n\t\t\t\t\t\t\tif obj:IsA(\"BasePart\") then\n\t\t\t\t\t\t\t\tobj.Anchored \t= false\n\t\t\t\t\t\t\t\tobj.CanCollide \t= false\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tif container then\n\t\t\t\t\t\tcontainer:Destroy()\n\t\t\t\t\t\tcontainer = nil\n\t\t\t\t\tend\n\n\t\t\t\t\t-- todo: very important\n\t\t\t\t\t-- only do this for the primary weapon\n\t\t\t\t\tlocal isMainHand = equipmentData.position == mapping.equipmentPosition.weapon\n\t\t\t\t\tif _entityManifest and isMainHand then\n\t\t\t\t\t\tlocal renderEntityData = entitiesBeingRendered[_entityManifest]\n\n\t\t\t\t\t\trenderEntityData.currentPlayerWeapon \t= weaponManifest\n\t\t\t\t\t\trenderEntityData.weaponBaseData \t\t= weaponBaseData\n\n\t\t\t\t\t\tif weaponBaseData.equipmentType == \"bow\" then\n\t\t\t\t\t\t\tif weaponManifest:FindFirstChild(\"AnimationController\") then\n\t\t\t\t\t\t\t\tlocal bowTool_Animations = animationInterface:registerAnimationsForAnimationController(weaponManifest.AnimationController, \"bowToolAnimations_noChar\").bowToolAnimations_noChar\n\n\t\t\t\t\t\t\t\trenderEntityData.currentPlayerWeaponAnimations = bowTool_Animations\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\trenderEntityData.currentPlayerWeaponAnimations = nil\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\trenderEntityData.currentPlayerWeaponAnimations = nil\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tif associatePlayer == client then\n\t\t\t\t\t\t\tnetwork:fire(\"myClientCharacterWeaponChanged\", weaponManifest)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\t-- attach weaponManifest\n\t\t\t\t\tlocal isOffhand = equipmentData.position == mapping.equipmentPosition[\"offhand\"]\n\t\t\t\t\tlocal backMounted, hipMounted, neckMounted = false, false, false\n\n\t\t\t\t\tif isOffhand then\n\t\t\t\t\t\tlocal t = weaponBaseData.equipmentType\n\n\t\t\t\t\t\tif t == \"sword\" or t == \"shield\" then\n\t\t\t\t\t\t\t-- do nothing\n\n\t\t\t\t\t\telseif t == \"dagger\" then\n\t\t\t\t\t\t\thipMounted = true\n\n\t\t\t\t\t\telseif t == \"amulet\" then\n\t\t\t\t\t\t\tneckMounted = true\n\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tbackMounted = true\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal gripCFrame = gripContainerOverrideCFrame or weaponBaseData.gripCFrame or weaponBaseData.attachmentOffset or CFrame.new()\n\t\t\t\t\tgripCFrame = gripCFrame - gripCFrame.Position\n\n\t\t\t\t\tif backMounted then\n\t\t\t\t\t\tbackMount.Part1 = weaponManifest:IsA(\"Model\") and weaponManifest.PrimaryPart or weaponManifest\n\t\t\t\t\t\tbackMount.C0 = CFrame.new(-0.25, 0.25, 0.75) * CFrame.Angles(math.pi / 2, math.pi * 0.75, math.pi / 2)\n\t\t\t\t\t\tbackMount.C1 = gripCFrame\n\t\t\t\t\telseif hipMounted then\n\t\t\t\t\t\thipMount.Part1 = weaponManifest:IsA(\"Model\") and weaponManifest.PrimaryPart or weaponManifest\n\t\t\t\t\t\thipMount.C0 = CFrame.new(-1, 0, 0) * CFrame.Angles(math.pi * 0.25, 0, 0)\n\t\t\t\t\t\thipMount.C1 = gripCFrame\n\t\t\t\t\telseif neckMounted then\n\t\t\t\t\t\tneckMount.Part1 = weaponManifest:IsA(\"Model\") and weaponManifest.PrimaryPart or weaponManifest\n\t\t\t\t\t\tneckMount.C0 = CFrame.new(0, 0.75, 0)\n\t\t\t\t\t\tneckMount.C1 = gripCFrame\n\t\t\t\t\telse\n\t\t\t\t\t\tlocal gripToAttachTo = weaponGripType == mapping.gripType.right and rightGrip or leftGrip\n\n\t\t\t\t\t\tgripToAttachTo.Part1 \t= weaponManifest:IsA(\"Model\") and weaponManifest.PrimaryPart or weaponManifest\n\t\t\t\t\t\tgripToAttachTo.C0 \t\t= CFrame.new()\n\t\t\t\t\t\tgripToAttachTo.C1 \t\t= gripContainerOverrideCFrame or weaponBaseData.gripCFrame or weaponBaseData.attachmentOffset or CFrame.new()\n\n\t\t\t\t\t\tif weaponManifest:IsA(\"BasePart\") then\n\t\t\t\t\t\t\tif weaponBaseData.equipmentType == \"dagger\" or weaponBaseData.equipmentType == \"sword\" or weaponBaseData.equipmentType == \"staff\" or weaponBaseData.equipmentType == \"greatsword\" then\n\t\t\t\t\t\t\t\tif not weaponManifest:FindFirstChild(\"topAttachment\") then\n\t\t\t\t\t\t\t\t\tlocal topAttachment \t= Instance.new(\"Attachment\", gripToAttachTo.Part1)\n\t\t\t\t\t\t\t\t\ttopAttachment.Name \t\t= \"topAttachment\"\n\n\t\t\t\t\t\t\t\t\tlocal part = gripToAttachTo.Part1\n\t\t\t\t\t\t\t\t\tlocal size = part.Size\n\t\t\t\t\t\t\t\t\tlocal points = {\n\t\t\t\t\t\t\t\t\t\tVector3.new(size.X / 2, 0, 0),\n\t\t\t\t\t\t\t\t\t\tVector3.new(0, size.Y / 2, 0),\n\t\t\t\t\t\t\t\t\t\tVector3.new(0, 0, size.Z / 2),\n\t\t\t\t\t\t\t\t\t\tVector3.new(-size.X / 2, 0, 0),\n\t\t\t\t\t\t\t\t\t\tVector3.new(0, -size.Y / 2, 0),\n\t\t\t\t\t\t\t\t\t\tVector3.new(0, 0, -size.Z / 2)\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\tlocal gripPoint = (gripToAttachTo.C1 * gripToAttachTo.C0:Inverse()).Position\n\n\t\t\t\t\t\t\t\t\tlocal bestPoint = nil\n\t\t\t\t\t\t\t\t\tlocal bestDistance = 0\n\t\t\t\t\t\t\t\t\tfor _, point in pairs(points) do\n\t\t\t\t\t\t\t\t\t\tlocal distance = (point - gripPoint).Magnitude\n\t\t\t\t\t\t\t\t\t\tif distance > bestDistance then\n\t\t\t\t\t\t\t\t\t\t\tbestPoint = point\n\t\t\t\t\t\t\t\t\t\t\tbestDistance = distance\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\ttopAttachment.Position = bestPoint\n\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif not weaponManifest:FindFirstChild(\"bottomAttachment\") then\n\t\t\t\t\t\t\t\t\tlocal projectionPosition \t= detection.projection_Box(gripToAttachTo.Part1.CFrame, gripToAttachTo.Part1.Size, gripToAttachTo.Part0.CFrame.p)\n\t\t\t\t\t\t\t\t\tlocal bottomAttachment \t\t= Instance.new(\"Attachment\", gripToAttachTo.Part1)\n\t\t\t\t\t\t\t\t\tbottomAttachment.Name \t\t= \"bottomAttachment\"\n\n\t\t\t\t\t\t\t\t\tbottomAttachment.Position \t= gripToAttachTo.Part1.CFrame:pointToObjectSpace(projectionPosition)\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif not weaponManifest:FindFirstChild(\"Trail\") then\n\t\t\t\t\t\t\t\t\tlocal trail \t\t= assetFolder.Trail:Clone()\n\t\t\t\t\t\t\t\t\ttrail.Parent \t\t= gripToAttachTo.Part1\n\t\t\t\t\t\t\t\t\ttrail.Attachment0 \t= gripToAttachTo.Part1.topAttachment\n\t\t\t\t\t\t\t\t\ttrail.Attachment1 \t= gripToAttachTo.Part1.bottomAttachment\n\t\t\t\t\t\t\t\t\ttrail.Enabled \t\t= false\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nfunction item_manager.IterateThroughItems(renderCharacter,appearanceData)\n    local rightGrip, leftGrip, backMount, hipMount, neckMount =\n\trenderCharacter[\"RightHand\"]:FindFirstChild(\"Grip\"),\n\trenderCharacter[\"LeftHand\"]:FindFirstChild(\"Grip\"),\n\trenderCharacter[\"UpperTorso\"]:FindFirstChild(\"BackMount\"),\n\trenderCharacter[\"LowerTorso\"]:FindFirstChild(\"HipMount\"),\n\trenderCharacter[\"UpperTorso\"]:FindFirstChild(\"BackMount\")\n\n    local currentlyEquipped = item_manager.getCurrentlyEquippedForRenderCharacter(renderCharacter)\n\tfor equipmentPosition, equipmentContainerData in pairs(currentlyEquipped) do\n\t\tlocal isStillEquipped = false\n\n\t\tfor i, equipmentSlotData in pairs(appearanceData.equipment) do\n\t\t\tif isCurrentlyEquipped(currentlyEquipped, equipmentSlotData) then\n\t\t\t\tisStillEquipped = true\n\t\t\tend\n\t\tend\n\n\t\tif not isStillEquipped then\n\t\t\tif rightGrip.Part1 == equipmentContainerData.manifest then\n\t\t\t\trightGrip.Part1 = nil\n\t\t\telseif leftGrip.Part1 == equipmentContainerData.manifest then\n\t\t\t\tleftGrip.Part1 = nil\n\t\t\telseif hipMount.Part1 == equipmentContainerData.manifest then\n\t\t\t\thipMount.Part1 = nil\n\t\t\telseif backMount.Part1 == equipmentContainerData.manifest then\n                backMount.Part1 = nil\n            elseif neckMount.Part1 == equipmentContainerData.manifest then\n                neckMount.Part1 = nil\n\t\t\tend\n\n\t\t\tcurrentlyEquipped[tostring(equipmentPosition)] = nil\n\t\t\tequipmentContainerData.manifest:Destroy()\n\t\tend\n\tend\nend\n\nfunction item_manager.iterateThroughappearanceData(appearanceData,renderCharacter,bow_manager,hatEquipmentData,inventoryCountLookup)\n    for _, equipmentSlotData in pairs(appearanceData.equipment) do\n        if equipmentSlotData.position == mapping.equipmentPosition.upper or equipmentSlotData.position == mapping.equipmentPosition.lower or equipmentSlotData.position == mapping.equipmentPosition.head then\n\n            local dye = equipmentSlotData.dye\n\n            if equipmentSlotData.position == mapping.equipmentPosition.head then\n                hatEquipmentData = equipmentSlotData\n            end\n\t\t\tlocal equipmentData = itemDataLookup[equipmentSlotData.id]\n\t\t\tlocal equipmentFolder = itemLookup[equipmentData.module.Name]\n            if equipmentFolder:FindFirstChild(\"container\") then\n                for _, accessoryPartContainer in pairs(equipmentFolder.container:GetChildren()) do\n                    if renderCharacter:FindFirstChild(accessoryPartContainer.Name) then\n\n                        if accessoryPartContainer:FindFirstChild(\"colorOverride\") then\n                            renderCharacter[accessoryPartContainer.Name].Color = accessoryPartContainer.Color\n                        end\n\n                        for _, accessoryPart in pairs(accessoryPartContainer:GetChildren()) do\n                            if accessoryPart:IsA(\"BasePart\") then\n                                local accessory = accessoryPart:Clone()\n                                    accessory.Anchored \t\t= false\n                                    accessory.CanCollide \t= false\n\n                                if dye then\n                                    local v = accessory\n                                    accessory.Color =  Color3.new(v.Color.r * dye.r/255, v.Color.g * dye.g/255, v.Color.b * dye.b/255)\n                                end\n\n                                local projectionWeld = Instance.new(\"Motor6D\", accessory)\n                                    projectionWeld.Name \t= \"projectionWeld\"\n                                    projectionWeld.Part0 \t= accessory\n                                    projectionWeld.Part1 \t= renderCharacter[accessoryPartContainer.Name]\n                                    projectionWeld.C0 \t\t= CFrame.new()\n                                    projectionWeld.C1 \t\t= accessoryPartContainer.CFrame:toObjectSpace(accessoryPart.CFrame)\n\n                                accessory.Name \t\t= \"!! EQUIPMENT !!\"\n                                accessory.Parent \t= renderCharacter\n                            end\n                        end\n                    end\n                end\n            end\n        elseif equipmentSlotData.position == mapping.equipmentPosition.arrow then\n            local isBowEquipped = false do\n                for i, equip in pairs(appearanceData.equipment) do\n                    if equip.position == mapping.equipmentPosition.weapon then\n                        if itemDataLookup[equip.id].equipmentType == \"bow\" then\n                            isBowEquipped = true\n                        end\n                    end\n                end\n            end\n\n            if isBowEquipped then\n                bow_manager.int__updateRenderCharacter(renderCharacter,inventoryCountLookup,equipmentSlotData,configuration,itemLookup)\n            end\n        end\n    end\nend\n\nfunction item_manager.createNetworkConnections(client,entitiesBeingRendered)\n    network:create(\"getCurrentlyEquippedForRenderCharacter\", \"BindableFunction\", \"OnInvoke\", function(renderCharacter)\n\t\treturn item_manager.getCurrentlyEquippedForRenderCharacter(renderCharacter)\n    end)\n\n    network:create(\"getCurrentWeaponManifest\", \"BindableFunction\", \"OnInvoke\", function(entityManifest)\n\t\tentityManifest = entityManifest or (client.Character and client.Character.PrimaryPart)\n\n\t\tif entityManifest and entitiesBeingRendered[entityManifest] then\n\t\t\tlocal currentlyEquipped = item_manager.getCurrentlyEquippedForRenderCharacter(entitiesBeingRendered[entityManifest].entityContainer.entity)\n\n\t\t\treturn currentlyEquipped[\"1\"] and currentlyEquipped[\"1\"].manifest\n\t\tend\n    end)\n\n    network:create(\"getRenderPlayerWeaponManifestEquipped\", \"BindableFunction\", \"OnInvoke\", function(player)\n\t\tlocal entityManifest = player.Character and player.Character.PrimaryPart\n\n\t\tif entityManifest and entitiesBeingRendered[entityManifest] then\n\t\t\tlocal currentlyEquipped = item_manager.getCurrentlyEquippedForRenderCharacter(entitiesBeingRendered[entityManifest].entityContainer.entity)\n\n\t\t\treturn currentlyEquipped[\"1\"] and currentlyEquipped[\"1\"].manifest\n\t\tend\n    end)\n\n    network:create(\"hideWeapons\", \"BindableFunction\", \"OnInvoke\", function(entity)\n\t\tlocal equipment = item_manager.getCurrentlyEquippedForRenderCharacter(entity)\n\t\tlocal partData = {}\n\n\t\tlocal checks = {\n\t\t\tequipment[\"1\"] and equipment[\"1\"].manifest,\n\t\t\tequipment[\"11\"] and equipment[\"11\"].manifest\n\t\t}\n\n\t\tlocal function hidePart(part)\n\t\t\ttable.insert(partData, {part = part, transparency = part.Transparency})\n\t\t\tpart.Transparency = 1\n\t\tend\n\n\t\tfor _, check in pairs(checks) do\n\t\t\tif check:IsA(\"BasePart\") then\n\t\t\t\thidePart(check)\n\t\t\tend\n\n\t\t\tfor _, desc in pairs(check:GetDescendants()) do\n\t\t\t\tif desc:IsA(\"BasePart\") then\n\t\t\t\t\thidePart(desc)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\treturn function()\n\t\t\tfor _, partDatum in pairs(partData) do\n\t\t\t\tpartDatum.part.Transparency = partDatum.transparency\n\t\t\tend\n\t\tend\n    end)\n\n\nend\n\nreturn item_manager"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/coreRenderServices/melee_manager.lua",
    "content": "local melee_manager = {}\n\nfunction melee_manager.PlayAnimation(animationSequenceName, characterEntityAnimationTracks, animationName, animationToBePlayed, extraData)\n\n    if\n        animationSequenceName == \"staffAnimations\" or\n        animationSequenceName == \"swordAnimations\" or\n        animationSequenceName == \"daggerAnimations\" or\n        animationSequenceName == \"greatswordAnimations\" or\n        animationSequenceName == \"dualAnimations\" or\n        animationSequenceName == \"swordAndShieldAnimations\" or\n        animationSequenceName == \"axeAnimations\" or\n        animationSequenceName == \"pickaxeAnimations\"\n        then\n            local atkspd = (extraData and extraData.attackSpeed) or 0\n            characterEntityAnimationTracks[animationSequenceName][animationName]:Play(0.1, 1, (1 + atkspd))\n        else\n            if characterEntityAnimationTracks[animationSequenceName] then\n                if typeof(characterEntityAnimationTracks[animationSequenceName][animationName]) == \"Instance\" then\n                    characterEntityAnimationTracks[animationSequenceName][animationName]:Play()\n                elseif typeof(characterEntityAnimationTracks[animationSequenceName][animationName]) == \"table\" then\n                    animationToBePlayed = animationToBePlayed[1]\n\n                for _, obj in pairs(characterEntityAnimationTracks[animationSequenceName][animationName]) do\n                    obj:Play()\n                end\n            end\n        end\n    end\nend\n\n\nreturn melee_manager"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/coreRenderServices/ragdoll_manager.lua",
    "content": "local ragdoll_manager = {}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal modules\t\t= require(replicatedStorage:WaitForChild(\"modules\"))\nlocal physics \t\t= modules.load(\"physics\")\n\nfunction ragdoll_manager.ragDollCharacter(entity, renderEntityData)\n    local ragdoll = entity:Clone()\n    ragdoll.Parent = entity.Parent\n    entity:Destroy()\n    local motorNames = {\"Root\", \"Neck\", \"RightShoulder\", \"LeftShoulder\", \"RightElbow\", \"LeftElbow\", \"Waist\", \"RightWrist\", \"LeftWrist\", \"RightHip\", \"LeftHip\", \"RightKnee\", \"LeftKnee\", \"RightAnkle\", \"LeftAnkle\"}\n    for _, motorName in pairs(motorNames) do\n        ragdoll:FindFirstChild(motorName, true):Destroy()\n    end\n\n    local rigAttachmentPairsByName = {}\n    for _, desc in pairs(ragdoll:GetDescendants()) do\n        if desc:IsA(\"Attachment\") and desc.Name:find(\"RigAttachment\") and (not desc.Name:find(\"Root\")) then\n            local name = desc.Name\n            if not rigAttachmentPairsByName[name] then\n                rigAttachmentPairsByName[name] = {}\n            end\n            table.insert(rigAttachmentPairsByName[name], desc)\n        end\n    end\n\n    physics:setWholeCollisionGroup(ragdoll, \"passthrough\")\n\n    local constraints = Instance.new(\"Folder\")\n    constraints.Name = \"constraints\"\n    constraints.Parent = ragdoll\n\n    for name, pair in pairs(rigAttachmentPairsByName) do\n        local constraint = Instance.new(\"BallSocketConstraint\")\n        constraint.LimitsEnabled = true\n        constraint.TwistLimitsEnabled = true\n        constraint.Attachment0 = pair[1]\n        constraint.Attachment1 = pair[2]\n        constraint.Parent = constraints\n\n        pair[1].Parent.CanCollide = true\n        pair[2].Parent.CanCollide = true\n    end\n\n    local hitbox = renderEntityData.entityContainer:FindFirstChild(\"hitbox\")\n    if hitbox then\n        local bp = Instance.new(\"BodyPosition\")\n        bp.MaxForce = Vector3.new(1e6, 0, 1e6)\n        bp.Parent = ragdoll.LowerTorso\n\n        local connection\n        local function onHeartbeat()\n            if not bp.Parent then\n                connection:Disconnect()\n                return\n            end\n            bp.Position = hitbox.Position\n        end\n        connection = game:GetService(\"RunService\").Heartbeat:Connect(onHeartbeat)\n    end\n\nend\n\nreturn ragdoll_manager"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/coreRenderServices/staff_manager.lua",
    "content": "local staff_manager = {}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal modules = require(replicatedStorage:WaitForChild(\"modules\"))\nlocal projectile \t= modules.load(\"projectile\")\n\n\nfunction staff_manager.PlayAnimation(animationToBePlayed,extraData,configuration)\n    if animationToBePlayed and not extraData.noRangeManaAttack and configuration.getConfigurationValue(\"doUseMageRangeAttack\", game.Players.LocalPlayer) then\n        local magicBullet \t\t= assetFolder.mageBullet:Clone()\n        magicBullet.CanCollide \t= false\n        magicBullet.Parent \t\t= workspace.CurrentCamera\n    --\t\t\t\t\t\tmagicBullet.CFrame \t\t= entityManifest.CFrame * CFrame.new(0, 0, -1.5)\n        magicBullet.CFrame\t\t= CFrame.new(renderEntityData.currentPlayerWeapon.magic.WorldPosition)\n\n        local unitDirection, adjusted_targetPosition = projectile.getUnitVelocityToImpact_predictiveByAbilityExecutionData(\n            magicBullet.Position,\n            renderEntityData.weaponBaseData.projectileSpeed or 50, -- act as if you were shooting at full\n            extraData,\n            0.05\n        )\n\n        utilities.playSound(\"magicAttack\", renderEntityData.currentPlayerWeapon)\n\n        projectile.createProjectile(\n            magicBullet.Position,\n            unitDirection,\n            renderEntityData.weaponBaseData.projectileSpeed or 40, --renderEntityData.weaponBaseData.projectileSpeed or 200,\n            magicBullet,\n            function(hitPart, hitPosition, hitNormal, hitMaterial)\n                tween(magicBullet, {\"Transparency\"},1,0.5)\n                for i,child in pairs(magicBullet:GetChildren()) do\n                    if child:IsA(\"ParticleEmitter\") or child:IsA(\"Light\") then\n                        child.Enabled = false\n                    end\n                end\n                game.Debris:AddItem(magicBullet, 0.5)\n\n                -- for damien: todo: hitPart is nil\n                if associatePlayer == client and hitPart then\n                    local canDamageTarget, trueTarget = damage.canPlayerDamageTarget(game.Players.LocalPlayer, hitPart)\n                    if canDamageTarget and trueTarget then\n                        --\t\t\t\t\t\t\t\t   (player, entityManifest, \tdamagePosition, sourceType, sourceId, \t\tguid)\n                        network:fire(\"requestEntityDamageDealt\", trueTarget, \t\thitPosition, \t\"equipment\", nil, \"magic-ball\")\n                    end\n                end\n            end,\n\n            nil,\n\n            -- ignore list\n            {entityManifest; renderEntityData.entityContainer},\n\n            -- points to next position\n            true,\n\n            0.01,\n\n            0.8\n        )\n\n        if renderEntityData.currentPlayerWeapon and renderEntityData.currentPlayerWeapon:FindFirstChild(\"magic\") and renderEntityData.currentPlayerWeapon.magic:FindFirstChild(\"castEffect\") then\n            renderEntityData.currentPlayerWeapon.magic.castEffect:Emit(1)\n        end\n\n    end\nend\n\nreturn staff_manager"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/effectsClient.client.lua",
    "content": "local debris = game:GetService(\"Debris\")\n\nlocal modules = require(game:GetService(\"ReplicatedStorage\"):WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\nlocal effects = modules.load(\"effects\")\nlocal placeSetup = modules.load(\"placeSetup\")\nlocal tween = modules.load(\"tween\")\nlocal utilities = modules.load(\"utilities\")\nlocal projectile = modules.load(\"projectile\")\n\nlocal entitiesFolder = placeSetup.awaitPlaceFolder(\"entities\")\n\nlocal function createEffectPart()\n\tlocal part = Instance.new(\"Part\")\n\tpart.Anchored = true\n\tpart.CanCollide = false\n\tpart.TopSurface = Enum.SurfaceType.Smooth\n\tpart.BottomSurface = Enum.SurfaceType.Smooth\n\tpart.CastShadow = false\n\treturn part\nend\n\nlocal function lerp(a, b, w)\n\treturn a + (b - a) * w\nend\n\nlocal effectFunctions\neffectFunctions = {\n\tacidSplash = function(args)\n\t\tlocal part = script.acidSplash:Clone()\n\t\tpart.Position = args.position\n\t\tpart.Parent = entitiesFolder\n\t\t\n\t\ttween(part, {\"Size\", \"Transparency\"}, {Vector3.new(2, 2, 2) * args.radius, 1}, args.duration)\n\t\t\n\t\tdelay(args.duration, function()\n\t\t\tpart.emitter.Enabled = false\n\t\t\tdebris:AddItem(part, part.emitter.Lifetime.Max)\n\t\tend)\n\tend,\n\t\n\tbloodHeal = function(args)\n\t\tlocal playerManifest = args.playerManifest\n\t\tlocal target = args.target\n\t\t\n\t\tif not playerManifest then return end\n\t\tif not target then return end\n\t\t\n\t\tlocal renderContainer = network:invoke(\"getPlayerRenderFromManifest\", playerManifest)\n\t\tif not renderContainer then return end\n\t\tlocal entity = renderContainer:FindFirstChild(\"entity\")\n\t\tif not entity then return end\n\t\tlocal root = renderContainer.PrimaryPart\n\t\tif not root then return end\n\t\tlocal upperTorso = entity:FindFirstChild(\"UpperTorso\")\n\t\tif not upperTorso then return end\n\t\t\n\t\tlocal function createEffectPart()\n\t\t\tlocal part = Instance.new(\"Part\")\n\t\t\tpart.Anchored = true\n\t\t\tpart.CanCollide = false\n\t\t\tpart.TopSurface = Enum.SurfaceType.Smooth\n\t\t\tpart.BottomSurface = Enum.SurfaceType.Smooth\n\t\t\treturn part\n\t\tend\n\t\t\n\t\tlocal function bloodEffect(partA, partB, spin)\n\t\t\tlocal duration = 1\n\t\t\tlocal bezierStretch = 16\n\t\t\t\n\t\t\tlocal blood = script.blood:Clone()\n\t\t\tlocal trail = blood.trail\n\t\t\tblood.CFrame = partA.CFrame\n\t\t\tblood.Parent = workspace.CurrentCamera\n\t\t\t\n\t\t\tlocal offsetB = CFrame.Angles(0, 0, spin) * CFrame.new(0, bezierStretch, 0)\n\t\t\tlocal offsetC = CFrame.Angles(0, 0, -spin) * CFrame.new(0, bezierStretch, 0)\n\t\t\t\n\t\t\teffects.onHeartbeatFor(duration, function(dt, t, w)\n\t\t\t\tlocal startToFinish = CFrame.new(partA.Position, partB.Position)\n\t\t\t\tlocal finishToStart = CFrame.new(partB.Position, partA.Position)\n\t\t\t\t\n\t\t\t\tlocal a = startToFinish.Position\n\t\t\t\tlocal b = (startToFinish * offsetB).Position\n\t\t\t\tlocal c = (finishToStart * offsetC).Position\n\t\t\t\tlocal d = finishToStart.Position\n\t\t\t\n\t\t\t\tlocal ab = a + (b - a) * w\n\t\t\t\tlocal cd = c + (d - c) * w\n\t\t\t\tlocal p = ab + (cd - ab) * w\n\t\t\t\t\n\t\t\t\tblood.CFrame = CFrame.new(p)\n\t\t\tend)\n\t\t\t\n\t\t\tdelay(duration, function()\n\t\t\t\tblood.Transparency = 1\n\t\t\t\ttrail.Enabled = false\n\t\t\t\tgame:GetService(\"Debris\"):AddItem(blood, trail.Lifetime)\n\t\t\tend)\n\t\tend\n\t\t\n\t\tlocal function lifeStealEffect(target)\n\t\t\tfor blood = 1, 3 do\n\t\t\t\tlocal spin = (blood - 2) * (math.pi / 3)\n\t\t\t\tbloodEffect(target, upperTorso, spin)\n\t\t\tend\n\t\t\t\n\t\t\tdelay(1, function()\n\t\t\t\t-- restore sound\n\t\t\t\tlocal restoreSound = script.restore:Clone()\n\t\t\t\trestoreSound.Parent = root\n\t\t\t\trestoreSound:Play()\n\t\t\t\tdebris:AddItem(restoreSound, restoreSound.TimeLength)\n\t\t\t\t\n\t\t\t\t-- blood poof\n\t\t\t\tlocal duration = 1\n\t\t\t\t\n\t\t\t\tlocal sphere = createEffectPart()\n\t\t\t\tsphere.Shape = Enum.PartType.Ball\n\t\t\t\tsphere.Color = script.blood.Color\n\t\t\t\tsphere.Material = Enum.Material.Neon\n\t\t\t\tsphere.Size = Vector3.new()\n\t\t\t\tsphere.CFrame = CFrame.new(upperTorso.Position)\n\t\t\t\tsphere.Parent = entitiesFolder\n\t\t\t\t\n\t\t\t\ttween(sphere, {\"Size\", \"Transparency\"}, {Vector3.new(8, 8, 8), 1}, duration)\n\t\t\t\teffects.onHeartbeatFor(duration, function()\n\t\t\t\t\tsphere.CFrame = CFrame.new(upperTorso.Position)\n\t\t\t\tend)\n\t\t\t\tdebris:AddItem(sphere, duration)\n\t\t\tend)\n\t\tend\n\t\t\n\t\tlifeStealEffect(target)\n\tend,\n\t\n\tlightning = function(args)\n\t\tlocal startCFrame = args.startCFrame\n\t\tlocal pointCount = args.pointCount or 14\n\t\tlocal segmentDeltaY = args.segmentDeltaY or 4\n\t\tlocal maxStutter = args.maxStutter or 4\n\t\tlocal duration = args.duration or 0.5\n\t\tlocal color = args.color or BrickColor.new(\"Electric blue\").Color\n\t\t\n\t\tlocal function lightningSegment(a, b)\n\t\t\tlocal duration = 0.5\n\t\t\t\n\t\t\tlocal part = createEffectPart()\n\t\t\tpart.Color = color\n\t\t\tpart.Material = Enum.Material.Neon\n\t\t\t\n\t\t\tlocal distance = (b - a).magnitude\n\t\t\tlocal midpoint = (a + b) / 2\n\t\t\t\n\t\t\tpart.Size = Vector3.new(0.25, 0.25, distance)\n\t\t\tpart.CFrame = CFrame.new(midpoint, b)\n\t\t\tpart.Parent = entitiesFolder\n\t\t\t\n\t\t\ttween(\n\t\t\t\tpart,\n\t\t\t\t{\"Transparency\"},\n\t\t\t\t1,\n\t\t\t\tduration\n\t\t\t)\n\t\t\tdebris:AddItem(part, duration)\n\t\tend\n\t\t\n\t\t-- create a column of points above the spot\n\t\t-- and move them randomly around a circle\n\t\t-- to create a jagged line like lightning\n\t\t-- don't bother storing the points, we don't\n\t\t-- need to do that, we only need the previous\n\t\tlocal cframe = startCFrame\n\t\t\n\t\t-- start at 2 because the target point is a point, too\n\t\tfor pointNumber = 2, pointCount do\n\t\t\tlocal theta = math.pi * 2 * math.random()\n\t\t\tlocal dx = math.cos(theta) * maxStutter * math.random()\n\t\t\tlocal dy = (pointNumber - 1) * segmentDeltaY\n\t\t\tlocal dz = math.sin(theta) * maxStutter * math.random()\n\t\t\t\n\t\t\tlocal nextCFrame = cframe * CFrame.new(dx, dy, dz)\n\t\t\t\n\t\t\tlightningSegment(cframe.Position, nextCFrame.Position)\n\t\t\tcframe = nextCFrame\n\t\tend\n\tend,\n\t\n\torbArrival = function(args)\n\t\teffectFunctions.orbAnnouncement()\n\t\t\n\t\tlocal rand = Random.new(1231996)\n\t\t\n\t\tlocal orb = args.orb\n\t\tlocal position = orb.PrimaryPart.Position\n\t\t\n\t\tlocal orbParent = orb.Parent\n\t\torb.Parent = nil\n\t\t\n\t\tlocal effect = script.orbArrival:Clone()\n\t\teffect.Position = position\n\t\teffect.Parent = entitiesFolder\n\t\t\n\t\teffect.loop:Play()\n\t\t\n\t\t-- repeated, accelerating lightning strikes\n\t\tlocal maxPause = 1\n\t\tlocal minPause = 0.05\n\t\tlocal strikeCount = 32\n\t\tfor strikeNumber = 1, strikeCount do\n\t\t\tfor _ = 1, rand:NextInteger(1, 2) do\n\t\t\t\tlocal cframe = CFrame.new(position) * CFrame.Angles(0, math.pi * 2 * rand:NextNumber(), 0) * CFrame.Angles(math.pi * 0.125 * rand:NextNumber(), 0, 0)\n\t\t\t\teffectFunctions.lightning{startCFrame = cframe}\n\t\t\tend\n\t\t\t\n\t\t\teffect[\"strike\"..rand:NextInteger(1, 6)]:Play()\n\t\t\t\n\t\t\tlocal pause = lerp(maxPause, minPause, (strikeNumber - 1) / strikeCount)\n\t\t\twait((pause * 0.5) + (pause * 0.5 * rand:NextNumber()))\n\t\tend\n\t\t\n\t\t-- expand the circle in an explosion\n\t\tlocal fadeDuration = 4\n\t\teffect.emitter.Enabled = false\n\t\teffect.BrickColor = BrickColor.new(\"Electric blue\")\n\t\teffect.explosion:Play()\n\t\twait(1)\n\t\ttween(effect, {\"Size\"}, {Vector3.new(1, 1, 1) * 128}, fadeDuration / 4, Enum.EasingStyle.Quint, Enum.EasingDirection.Out)\n\t\ttween(effect, {\"Transparency\"}, {1}, fadeDuration, Enum.EasingStyle.Linear)\n\t\teffect.loop:Stop()\n\t\tdebris:AddItem(effect, fadeDuration)\n\t\t\n\t\t-- make burning cracks on the ground\n\t\tlocal raycastIgnoreList = projectile.makeIgnoreList{\n\t\t\tplaceSetup.awaitPlaceFolder(\"entityManifestCollection\"),\n\t\t\tplaceSetup.awaitPlaceFolder(\"entityRenderCollection\")\n\t\t}\n\t\t\n\t\tlocal function crack(cframe, stepLength, stepCount, sign)\n\t\t\tif sign == nil then sign = 1 end\n\t\t\t\n\t\t\tlocal here = cframe.Position\n\t\t\tlocal wiggle = math.pi * 0.4\n\t\t\tlocal castDelta = Vector3.new(0, 6, 0)\n\t\t\t\n\t\t\tfor stepNumber = 2, stepCount do\n\t\t\t\tlocal there = here + cframe.LookVector * stepLength\n\t\t\t\tlocal ray = Ray.new(there + castDelta, -castDelta * 2)\n\t\t\t\tlocal part, point = workspace:FindPartOnRayWithIgnoreList(ray, raycastIgnoreList)\n\t\t\t\tthere = point\n\t\t\t\t\n\t\t\t\tlocal segment = createEffectPart()\n\t\t\t\tsegment.Material = Enum.Material.Neon\n\t\t\t\tsegment.BrickColor = BrickColor.new(\"Electric blue\")\n\t\t\t\tsegment.CFrame = CFrame.new((here + there) / 2, there)\n\t\t\t\tsegment.Size = Vector3.new(0.2, 0.2, (there - here).Magnitude)\n\t\t\t\t\n\t\t\t\tlocal emitter = script.orbEmitter:Clone()\n\t\t\t\temitter.Parent = segment\n\t\t\t\t\n\t\t\t\tsegment.Parent = entitiesFolder\n\t\t\t\t\n\t\t\t\tspawn(function()\n\t\t\t\t\tfor i=5,0,-1 do\n\t\t\t\t\t\temitter.Rate = math.random(0, i*2)\n\t\t\t\t\t\twait(2)\n\t\t\t\t\tend\n\t\t\t\t\tlocal segmentFadeDuration = 5\n\t\t\t\t\ttween(segment, {\"Transparency\"}, {1}, segmentFadeDuration)\n\t\t\t\t\tdebris:AddItem(segment, segmentFadeDuration)\t\t\t\t\t\n\t\t\t\tend)\n\t\t\t\t\n\t\t\t\tif (stepNumber + 1) % 2 == 0 then\n\t\t\t\t\tcrack(cframe, stepLength, stepCount - stepNumber, sign)\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\there = there\n\t\t\t\tsign = -sign\n\t\t\t\tcframe = cframe * CFrame.Angles(0, wiggle * rand:NextNumber() * sign, 0)\n\t\t\t\tcframe = cframe + (here - cframe.Position)\n\t\t\tend\n\t\tend\n\t\t\n\t\tlocal crackStartPosition do\n\t\t\tlocal ray = Ray.new(position, Vector3.new(0, -8, 0))\n\t\t\tlocal part, point = workspace:FindPartOnRayWithIgnoreList(ray, raycastIgnoreList)\n\t\t\tcrackStartPosition = point\n\t\tend\n\t\t\n\t\tlocal startTheta = math.pi * 2 * rand:NextNumber()\n\t\tlocal crackCount = 7\n\t\tlocal deltaTheta = math.pi * 2 / crackCount\n\t\tfor crackNumber = 1, crackCount do\n\t\t\tlocal theta = startTheta + deltaTheta * crackNumber\n\t\t\tcrack(CFrame.new(crackStartPosition) * CFrame.Angles(0, theta, 0), 2, 8)\n\t\tend\n\t\t\n\t\torb.Parent = orbParent\n\t\torb.PrimaryPart.loop:Play()\n\tend,\n\t\n\torbAnnouncement = function()\n\t\t--[[\n\t\tlocal adjectives = {\n\t\t\t\"a mystic\",\n\t\t\t\"an electrifying\",\n\t\t\t\"an arcane\",\n\t\t\t\"a powerful\",\n\t\t\t\"an evocative\",\n\t\t\t\"an ancient\",\n\t\t\t\"an overwhelming\",\n\t\t\t\"a great\",\n\t\t}\n\t\tlocal adjective = adjectives[math.random(1, #adjectives)]\n\t\t]]\n\t\tgame.StarterGui:SetCore(\"ChatMakeSystemMessage\", {\n\t\t\tText = \"You feel a mystic presence echo through the night...\"--[[..adjective..\" presence echo through the night...\"]],\n\t\t\tColor = BrickColor.new(\"Electric blue\").Color,\n\t\t\tFont = Enum.Font.SourceSansBold,\n\t\t})\n\tend,\n\t\n\torbDeparture = function(args)\n\t\tlocal orb = args.orb\n\t\tlocal cframe = orb:GetPrimaryPartCFrame()\n\t\t\n\t\tlocal dy = 0\n\t\tlocal vy = 0\n\t\tlocal ay = 0\n\t\tlocal aya = 16\n\t\t\n\t\teffects.onHeartbeatFor(5, function(dt, t, w)\n\t\t\tay = ay + aya * dt\n\t\t\tvy = vy + ay * dt\n\t\t\tdy = dy + vy * dt\n\t\t\torb:SetPrimaryPartCFrame(cframe + Vector3.new(0, dy, 0))\n\t\tend)\n\tend,\n}\n\nnetwork:connect(\"effects_requestEffect\", \"OnClientEvent\", function(effectName, args)\n\teffectFunctions[effectName](args)\nend)"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/entityRenderer.lua",
    "content": "-- Master script that handles entity rendering\n\nlocal module = {}\nlocal client = game.Players.LocalPlayer\n\nlocal repo = script.Parent\n\n-- render services\nlocal coreRenderServices = require(repo:WaitForChild(\"coreRenderServices\"))\n\nlocal bow_manager = coreRenderServices(\"bow_manager\")\nlocal staff_manager = coreRenderServices(\"staff_manager\")\nlocal melee_manager = coreRenderServices(\"melee_manager\")\nlocal appearance_manager = coreRenderServices(\"appearance_manager\")\nlocal ragdoll_manager = coreRenderServices(\"ragdoll_manager\")\nlocal item_manager = coreRenderServices(\"item_manager\")\n\n-- services\nlocal assetFolder = repo.Parent:WaitForChild(\"assets\")\n\nlocal RunService = game:GetService(\"RunService\")\nlocal HttpService = game:GetService(\"HttpService\")\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\n-- modules\nlocal network\nlocal tween\nlocal utilities\nlocal physics\nlocal placeSetup\nlocal projectile\nlocal configuration\nlocal events\n\n-- assets\nlocal ReplicatedStorageAssetFolder = ReplicatedStorage:WaitForChild(\"assets\")\n\nlocal defaultCharacterAppearance = require(ReplicatedStorage:WaitForChild(\"defaultCharacterAppearance\"))\nlocal defaultMonsterStateStates = require(ReplicatedStorage.defaultMonsterState).states\n\nlocal entityManifestCollectionFolder\nlocal entityRenderCollectionFolder\n\nlocal animationInterface\nlocal accessoryLookup = ReplicatedStorage.assets.accessories\nlocal itemLookup = require(ReplicatedStorage.itemData)\nlocal monsterLookup = require(ReplicatedStorage.monsterLookup)\nlocal abilityLookup = require(ReplicatedStorage.abilityLookup)\nlocal statusEffectLookup = require(ReplicatedStorage.statusEffectLookup)\nlocal entitiesBeingRendered = {}\n\n-- builds a table of whats currently equipped on a renderCharacter,\n-- id rather do this than store what every renderCharacter is wearing and\n-- keep track of it.\n\nlocal function getInventoryCountLookupTableByItemId()\n\tlocal lookupTable = {}\n\tlocal inventoryLastGot = network:invoke(\"getCacheValueByNameTag\", \"inventory\")\n\n\tif inventoryLastGot then\n\t\tfor i, inventorySlotData in pairs(inventoryLastGot) do\n\t\t\tif lookupTable[inventorySlotData.id] then\n\t\t\t\tlookupTable[inventorySlotData.id] = lookupTable[inventorySlotData.id] + (inventorySlotData.stacks or 1)\n\t\t\telse\n\t\t\t\tlookupTable[inventorySlotData.id] = inventorySlotData.stacks or 1\n\t\t\tend\n\t\tend\n\tend\n\treturn lookupTable\nend\n\n-- handles updating appearance of renderCharacter (SPECIFICALLY!!)\n-- renderEntityContainer.entity is renderCharacter (renderEntityContainer is also shortened as entityContainer)\n-- ^^ ONLY IF entityType == \"character\" ^^\nlocal function int__updateRenderCharacter(renderCharacter, appearanceData, _entityManifest)\n\tlocal associatePlayer do\n\t\tif _entityManifest then\n\t\t\tassociatePlayer = game.Players:GetPlayerFromCharacter(_entityManifest.Parent)\n\t\tend\n\tend\n\n\tappearanceData = appearanceData or defaultCharacterAppearance\n\tappearanceData.equipment = appearanceData.equipment or defaultCharacterAppearance.equipment\n\tappearanceData.accessories = appearanceData.accessories or defaultCharacterAppearance.accessories\n\n\t-- wipe all previous additions\n\tlocal accessories = {\n\t\t[\"!! ACCESSORY !!\"] = true,\n\t\t[\"!! EQUIPMENT-UPPER !!\"] = true,\n\t\t[\"!! EQUIPMENT !!\"] = true,\n\t\t[\"!! WEAPON !!\"] = true,\n\t\t[\"!! ARROW !!\"] = true,\n\t}\n\n\tfor i, obj in pairs(renderCharacter:GetChildren()) do\n\t\tif accessories[obj.Name] then\n\t\t\tobj:Destroy()\n\t\tend\n\tend\n\n\tappearance_manager.ApplySkinColor(appearanceData, renderCharacter, accessoryLookup, ReplicatedStorageAssetFolder)\n\n\tlocal hatEquipmentData\n\tlocal inventoryCountLookup = getInventoryCountLookupTableByItemId()\n\n\tif appearanceData and appearanceData.equipment then\n\t\titem_manager.iterateThroughappearanceData(appearanceData,renderCharacter,bow_manager,hatEquipmentData,inventoryCountLookup)\n\tend\n\n\t--[[\n\tplayerStoreForCurrentlyEquipped[equipmentData.position] = {\n\t\tbaseData = weaponBaseData;\n\t\tmanifest = weaponManifest;\n\t\tequipmentData = equipmentData;\n\t}\n\t--]]\n\n\t-- iterate through equipped on character and actual\n\titem_manager.IterateThroughItems(renderCharacter,appearanceData)\n\n\t-- equipping new stuff\n\titem_manager.EquipNewItem(appearanceData,renderCharacter,_entityManifest,entitiesBeingRendered,animationInterface,associatePlayer,client,assetFolder)\n\n\tif appearanceData and appearanceData.accessories then\n\t\tappearance_manager.LoadAppearence(ReplicatedStorageAssetFolder.accessories,appearanceData,hatEquipmentData,itemLookup,renderCharacter)\n\tend\n\n\tif appearanceData and appearanceData.temporaryEquipment then\n\t\tfor temporaryEquipmentName, _ in pairs(appearanceData.temporaryEquipment) do\n\t\t\tif ReplicatedStorage:FindFirstChild(\"temporaryEquipment\") and ReplicatedStorage.temporaryEquipment:FindFirstChild(temporaryEquipmentName) then\n\t\t\t\tlocal applicationFunction = require(ReplicatedStorage.temporaryEquipment[temporaryEquipmentName].application)\n\n\t\t\t\tapplicationFunction(renderCharacter)\n\t\t\tend\n\t\tend\n\tend\n\n\tif renderCharacter then\n\t\tfor i, obj in pairs(renderCharacter:GetDescendants()) do\n\t\t\tif obj:IsA(\"BasePart\") then\n\t\t\t\tobj.CanCollide = false\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function int__assembleRenderCharacter(manifest)\n\tlocal entityContainer \t= Instance.new(\"Model\")\n\n\tlocal clientPlayerHitbox = manifest:Clone()\n\tclientPlayerHitbox.BrickColor = BrickColor.new(\"Hot pink\")\n\tclientPlayerHitbox.CanCollide = false\n\tclientPlayerHitbox.Anchored = true\n\tclientPlayerHitbox.Name = \"hitbox\"\n\n\tlocal clientHitboxToServerHitboxReference = Instance.new(\"ObjectValue\")\n\tclientHitboxToServerHitboxReference.Name = \"clientHitboxToServerHitboxReference\"\n\tclientHitboxToServerHitboxReference.Value = manifest\n\tclientHitboxToServerHitboxReference.Parent = entityContainer\n\n\t-- clear all unnecessary parts within the hitbox\n\t-- we only want the part itself\n\tclientPlayerHitbox:ClearAllChildren()\n\n\tentityContainer.PrimaryPart = clientPlayerHitbox\n\tclientPlayerHitbox.Parent = entityContainer\n\n\n\n\tlocal characterBaseModel = ReplicatedStorage.playerBaseCharacter:Clone()\n\tcharacterBaseModel.Name = \"entity\"\n\tcharacterBaseModel.Parent = entityContainer\n\n\tlocal projectionWeld = Instance.new(\"Motor6D\")\n\tprojectionWeld.Name = \"projectionWeld\"\n\tprojectionWeld.Part0 = clientPlayerHitbox\n\tprojectionWeld.Part1 = characterBaseModel.PrimaryPart\n\tprojectionWeld.C0 = CFrame.new()\n\tprojectionWeld.C1 = CFrame.new(0, characterBaseModel:GetModelCFrame().Y - characterBaseModel.PrimaryPart.CFrame.Y, 0)\n\tprojectionWeld.Parent = clientPlayerHitbox\n\n\treturn entityContainer\nend\n\nlocal function dissassembleRenderEntityByManifest(entityManifest)\n\tif entitiesBeingRendered[entityManifest] then\n\t\tfor i, connection in pairs(entitiesBeingRendered[entityManifest].connections) do\n\t\t\tconnection:disconnect()\n\t\tend\n\n\t\tif entitiesBeingRendered[entityManifest].entityContainer and entitiesBeingRendered[entityManifest].entityContainer:FindFirstChild(\"entity\") and entitiesBeingRendered[entityManifest].entityContainer.entity:FindFirstChild(\"AnimationController\") then\n\t\t\tanimationInterface:deregisterAnimationsForAnimationController(entitiesBeingRendered[entityManifest].entityContainer.entity.AnimationController)\n\t\telse\n\t\t\t-- passing nil will flush all registerations whos index no longer exists\n\t\t\t-- just incase something wack happened..\n\t\t\tanimationInterface:deregisterAnimationsForAnimationController(nil)\n\t\tend\n\n\t\tentitiesBeingRendered[entityManifest].entityContainer:Destroy()\n\t\tentitiesBeingRendered[entityManifest] = nil\n\tend\nend\n\nlocal playerXpTagPairing = {}\n\nlocal function int__connectEntityEvents(entityManifest, renderEntityData)\n\tlocal associatePlayer = game.Players:GetPlayerFromCharacter(entityManifest.Parent)\n\n\t-- we're freezing whether or not its a character\n\t-- because we can't garauntee entityContainer will be the same.. i think thats why at least\n\t-- we'll test...\n\t-- local isEntityCharacter = entityManifest.entityType.Value == \"character\"\n\n\tlocal previousKeyframeReached_event\n\tlocal currentPlayingStateAnimation\n\tlocal characterEntityAnimationTracks\n\tlocal entityStatesData\n\tlocal entityBaseData\n\n\tlocal previousState = \"\"\n\tlocal isWalkingSoundPlaying = false\n\tlocal isIdleSoundPlaying = false\n\tlocal isRunningSoundPlaying = false\n\n\tlocal monsterAnimations = {}\n\n\tlocal function populateMonsterAnimationsTable()\n\t\tif entityManifest.entityType.Value == \"monster\" or entityManifest.entityType.Value == \"pet\" then\n\t\t\tmonsterAnimations = {}\n\n\t\t\t-- please dont ask me why im putting it here\n\t\t\tphysics:setWholeCollisionGroup(renderEntityData.entityContainer.entity, \"monstersLocal\")\n\n\t\t\tfor i, animation in pairs(renderEntityData.entityContainer.entity.animations:GetChildren()) do\n\t\t\t\tlocal animationTrack = renderEntityData.entityContainer.entity.AnimationController:LoadAnimation(animation)\n\t\t\t\tlocal animPriority = \"Idle\"\n\n\t\t\t\tif animation.Name == \"attacking\" or animation.Name == \"death\" then\n\t\t\t\t\tanimPriority = \"Action\"\n\t\t\t\telseif animation.Name == \"dashing\" or animation.Name == \"damaged\" then\n\t\t\t\t\tanimPriority = \"Movement\"\n\t\t\t\telseif animation.Name == \"walking\" or animation.Name == \"idling\" then\n\t\t\t\t\tanimPriority = \"Core\"\n\t\t\t\tend\n\n\t\t\t\tanimationTrack.Priority = Enum.AnimationPriority[animPriority] or Enum.AnimationPriority.Idle\n\t\t\t\tmonsterAnimations[animation.Name] \t= animationTrack\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function populateEntityData()\n\t\tif entityManifest.entityType.Value == \"character\" then\n\t\t\tcharacterEntityAnimationTracks = animationInterface:registerAnimationsForAnimationController(renderEntityData.entityContainer.entity.AnimationController, \"movementAnimations\", \"swordAndShieldAnimations\", \"dualAnimations\", \"greatswordAnimations\", \"swordAnimations\", \"daggerAnimations\", \"staffAnimations\", \"fishing-rodAnimations\", \"emoteAnimations\", \"bowAnimations\", \"axeAnimations\",\"pickaxeAnimations\")\n\t\telseif entityManifest.entityType.Value == \"monster\" or entityManifest.entityType.Value == \"pet\" then\n\t\t\tentityBaseData = (entityManifest.entityType.Value == \"monster\") and monsterLookup[entityManifest.entityId.Value] or itemLookup[tonumber(entityManifest.entityId.Value)]\n\t\t\tentityStatesData = utilities.copyTable(entityBaseData.statesData.states)\n\n\t\t\tsetmetatable(entityStatesData, {\n\t\t\t\t__index = function(_, index)\n\t\t\t\t\treturn defaultMonsterStateStates[index]\n\t\t\t\tend;\n\t\t\t})\n\t\tend\n\tend\n\n\tlocal function onCharacterAnimationTrackKeyframeReached(keyframeName)\n\t\tif keyframeName == \"footstep\" then\n\t\t\t-- do not play footstep sounds on stealthed characters\n\t\t\tif entityManifest:FindFirstChild(\"isStealthed\") then return end\n\n\t\t\tlocal footStep = \"\"\n\n\t\t\tlocal ignore = {workspace.CurrentCamera}\n\t\t\tif workspace:FindFirstChild(\"placeFolders\") then\n\t\t\t\ttable.insert(ignore, workspace.placeFolders)\n\t\t\tend\n\n\t\t\tlocal below = Ray.new(entityManifest.Position, Vector3.new(0,-5,0))\n\t\t\tlocal belowPart, belowPosition, belowNormal, belowMaterial = workspace:FindPartOnRayWithIgnoreList(below, ignore, false, false)\n\n\t\t\tif belowPart ~= nil and (belowPart:IsA(\"BasePart\") or belowPart:IsA(\"Terrain\")) then\n\t\t\t\tif belowMaterial == Enum.Material.Grass or belowMaterial == Enum.Material.LeafyGrass then\n\t\t\t\t\tfootStep = \"grass\"\n\t\t\t\telseif belowMaterial == Enum.Material.Mud or belowMaterial == Enum.Material.Ground then\n\t\t\t\t\tfootStep = \"dirt\"\n\t\t\t\telseif belowMaterial == Enum.Material.Sand or belowMaterial == Enum.Material.Sandstone then\n\t\t\t\t\tfootStep = \"sand\"\n\t\t\t\telseif belowMaterial == Enum.Material.Snow or belowMaterial == Enum.Material.Ice then\n\t\t\t\t\tfootStep = \"snow\"\n\t\t\t\telse\n\t\t\t\t\tfootStep = \"stone\"\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal footStepSound\n\n\t\t\tlocal possibleSounds = {}\n\t\t\tfor i = 1, 3 do\n\t\t\t\tlocal sound = game.ReplicatedStorage.assets.sounds:FindFirstChild(\"footstep_\"..footStep..(i>1 and tostring(i) or \"\"))\n\n\t\t\t\tif sound then\n\t\t\t\t\ttable.insert(possibleSounds, sound)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif #possibleSounds > 0 then\n\t\t\t\tfootStepSound = possibleSounds[math.random(1,#possibleSounds)]\n\t\t\tend\n\n\t\t\tif footStepSound and renderEntityData.entityContainer.PrimaryPart then\n\t\t\t\tlocal newSound \t= utilities.playSound(footStepSound.Name)\n\n\t\t\t\tnewSound.Parent = renderEntityData.entityContainer.PrimaryPart\n\t\t\t\tnewSound.Looped = false\n\t\t\t\tnewSound.Pitch \t= math.random(95,105) / 100\n\n\t\t\t\t-- make ur own volume louder\n\t\t\t\tif associatePlayer == client then\n\t\t\t\t\tnewSound.Volume \t\t= newSound.Volume * 1.5\n\t\t\t\t\tnewSound.EmitterSize \t= newSound.EmitterSize * 3\n\t\t\t\t\tnewSound.MaxDistance\t= newSound.MaxDistance * 3\n\t\t\t\tend\n\n\t\t\t\tnewSound:Play()\n\t\t\t\tgame.Debris:AddItem(newSound,1.5)\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function onEntityStateChanged(newState)\n\t\tif entityManifest.entityType.Value == \"character\" then\n\t\t\tanimationInterface:stopPlayingAnimationsByAnimationCollectionNameWithException(characterEntityAnimationTracks, \"emoteAnimations\", \"consume_consumable\")\n\n\t\t\tif currentPlayingStateAnimation then\n\t\t\t\tif typeof(currentPlayingStateAnimation) == \"Instance\" then\n\t\t\t\t\tif currentPlayingStateAnimation.Looped or newState == \"jumping\" then\n\t\t\t\t\t\tcurrentPlayingStateAnimation:Stop()\n\t\t\t\t\tend\n\t\t\t\telseif typeof(currentPlayingStateAnimation) == \"table\" then\n\t\t\t\t\tfor ii, obj in pairs(currentPlayingStateAnimation) do\n\t\t\t\t\t\tif obj.Looped or newState == \"jumping\" then\n\t\t\t\t\t\t\tobj:Stop()\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tcurrentPlayingStateAnimation = nil\n\t\t\tend\n\t\telseif entityManifest.entityType.Value == \"monster\" or entityManifest.entityType.Value == \"pet\" then\n\t\t\tif renderEntityData.entityContainer:FindFirstChild(\"entity\") and renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"walking\") then\n\t\t\t\tif newState == \"walking\" or (entityStatesData[newState] and (entityStatesData[newState].animationEquivalent == \"walking\")) or newState == \"movement\" or (entityStatesData[newState] and (entityStatesData[newState].animationEquivalent == \"movement\")) then\n\t\t\t\t\tif not isWalkingSoundPlaying then\n\t\t\t\t\t\trenderEntityData.entityContainer.entity.PrimaryPart.walking.Looped = true\n\t\t\t\t\t\trenderEntityData.entityContainer.entity.PrimaryPart.walking:Play()\n\t\t\t\t\t\tisWalkingSoundPlaying = true\n\t\t\t\t\tend\n\t\t\t\telseif isWalkingSoundPlaying then\n\t\t\t\t\trenderEntityData.entityContainer.entity.PrimaryPart.walking:Stop()\n\t\t\t\t\tisWalkingSoundPlaying = false\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif renderEntityData.entityContainer:FindFirstChild(\"entity\") and renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"running\") then\n\t\t\t\tif newState == \"running\" or (entityStatesData[newState] and (entityStatesData[newState].animationEquivalent == \"running\")) then\n\t\t\t\t\tif not isRunningSoundPlaying then\n\t\t\t\t\t\trenderEntityData.entityContainer.entity.PrimaryPart.running.Looped = true\n\t\t\t\t\t\trenderEntityData.entityContainer.entity.PrimaryPart.running:Play()\n\t\t\t\t\t\tisRunningSoundPlaying = true\n\t\t\t\t\tend\n\t\t\t\telseif isRunningSoundPlaying then\n\t\t\t\t\trenderEntityData.entityContainer.entity.PrimaryPart.running:Stop()\n\t\t\t\t\tisRunningSoundPlaying = false\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif renderEntityData.entityContainer:FindFirstChild(\"entity\") and renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"idling\") then\n\t\t\t\tif not isRunningSoundPlaying and not isWalkingSoundPlaying then\n\t\t\t\t\tif not isIdleSoundPlaying then\n\t\t\t\t\t\trenderEntityData.entityContainer.entity.PrimaryPart.idling.Looped = true\n\t\t\t\t\t\trenderEntityData.entityContainer.entity.PrimaryPart.idling:Play()\n\t\t\t\t\t\tisIdleSoundPlaying = true\n\t\t\t\t\tend\n\t\t\t\telseif isIdleSoundPlaying then\n\t\t\t\t\trenderEntityData.entityContainer.entity.PrimaryPart.idling:Stop()\n\t\t\t\t\tisIdleSoundPlaying = false\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif currentPlayingStateAnimation then\n\t\t\t\tif entityStatesData[previousState] and not entityStatesData[previousState].doNotStopAnimation then\n\t\t\t\t\tcurrentPlayingStateAnimation:Stop()\n\t\t\t\tend\n\n\t\t\t\tcurrentPlayingStateAnimation = nil\n\t\t\tend\n\t\tend\n\n\t\tif newState == \"dead\" then\n\t\t\t-- stop all animations\n\t\t\tlocal function deathEffect(multi)\n\t\t\t\tmulti = multi or 1\n\n\t\t\t\tlocal target = entityManifest.CFrame\n\t\t\t\tif renderEntityData.entityContainer and renderEntityData.entityContainer:FindFirstChild(\"entity\") then\n\t\t\t\t\tif renderEntityData.entityContainer.entity:FindFirstChild(\"Torso\") then\n\t\t\t\t\t\ttarget = renderEntityData.entityContainer.entity.Torso.CFrame\n\t\t\t\t\telseif renderEntityData.entityContainer.entity:FindFirstChild(\"UpperTorso\") then\n\t\t\t\t\t\ttarget = renderEntityData.entityContainer.entity.UpperTorso.CFrame\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tlocal deathPart = Instance.new(\"Part\")\n\t\t\t\tdeathPart.Size = entityManifest.Size * multi\n\t\t\t\tdeathPart.CFrame = target\n\t\t\t\tdeathPart.Transparency = 1\n\t\t\t\tdeathPart.CanCollide = false\n\t\t\t\tdeathPart.Anchored = true\n\n\t\t\t\tlocal deathSound = Instance.new(\"Sound\")\n\t\t\t\tdeathSound.SoundId = \"rbxassetid://2199444861\"\n\t\t\t\tdeathSound.Name = \"deathSound\"\n\t\t\t\tdeathSound.MaxDistance = 35\n\t\t\t\tdeathSound.Parent = deathPart\n\t\t\t\tdeathSound.Volume = 0.2\n\t\t\t\tdeathSound.PlaybackSpeed = 0.8 + math.random()/5\n\n\t\t\t\tif entityManifest:FindFirstChild(\"monsterScale\") and entityManifest.monsterScale.Value > 1.3 then\n\t\t\t\t\tdeathSound.Volume = deathSound.Volume * entityManifest.monsterScale.Value\n\t\t\t\t\tdeathSound.MaxDistance = deathSound.MaxDistance * (entityManifest.monsterScale.Value ^ 3)\n\t\t\t\t\tdeathSound.PlaybackSpeed = deathSound.PlaybackSpeed * (1 - entityManifest.monsterScale.Value/8)\n\t\t\t\tend\n\n\t\t\t\tdeathSound:Play()\n\n\t\t\t\tlocal effect = assetFolder.Death:Clone()\n\t\t\t\teffect.Parent = deathPart\n\n\t\t\t\tdeathPart.Parent = workspace.CurrentCamera\n\n\t\t\t\tlocal size = deathPart.Size\n\t\t\t\teffect:Emit(3 * math.sqrt(size.X * size.Y * size.Z ))\n\n\t\t\t\tgame.Debris:AddItem(deathPart,3)\n\n\t\t\tend\n\n\t\t\tfor i, animationTrack in pairs(renderEntityData.entityContainer.entity.AnimationController:GetPlayingAnimationTracks()) do\n\t\t\t\tanimationTrack:Stop()\n\t\t\tend\n\n\t\t\tif entityManifest.entityType.Value ~= \"character\" then\n\t\t\t\tentityManifest.CanCollide = false\n\t\t\tend\n\n\t\t\t-- make the render also not collidable\n\t\t\tfor i, obj in pairs(renderEntityData.entityContainer.entity:GetDescendants()) do\n\t\t\t\tif obj:IsA(\"BasePart\") then\n\t\t\t\t\tobj.CanCollide = false\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif entityManifest.entityType.Value == \"monster\" or entityManifest.entityType.Value == \"pet\" then\n\t\t\t\tlocal deathConnection\n\t\t\t\tdeathConnection = monsterAnimations.death.Stopped:connect(function()\n\n\t\t\t\t\tdeathConnection:disconnect()\n\t\t\t\t\tdeathEffect()\n\t\t\t\t\t-- bye-bye\n\t\t\t\t\tdissassembleRenderEntityByManifest(entityManifest)\n\t\t\t\tend)\n\n\t\t\t\tmonsterAnimations.death.Looped = false\n\t\t\t\tmonsterAnimations.death:Play()\n\n\t\t\t\tif renderEntityData.entityContainer.entity.PrimaryPart and renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"death\") then\n\t\t\t\t\tlocal hitsounds = {renderEntityData.entityContainer.entity.PrimaryPart.death}\n\n\t\t\t\t\tif renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"death2\") then\n\t\t\t\t\t\ttable.insert(hitsounds, renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"death2\"))\n\t\t\t\t\tend\n\n\t\t\t\t\tif renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"death3\") then\n\t\t\t\t\t\ttable.insert(hitsounds, renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"death3\"))\n\t\t\t\t\tend\n\n\t\t\t\t\tif renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"death4\") then\n\t\t\t\t\t\ttable.insert(hitsounds, renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"death4\"))\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal rand = math.random(#hitsounds)\n\t\t\t\t\tlocal hitsound = hitsounds[rand]\n\n\t\t\t\t\tif entityManifest:FindFirstChild(\"monsterScale\") and entityManifest.monsterScale.Value > 1.3 and hitsound:FindFirstChild(\"scalePitch\") == nil then\n\t\t\t\t\t\tlocal scale = entityManifest.monsterScale.Value\n\n\t\t\t\t\t\thitsound.Volume \t\t= hitsound.Volume * scale\n\t\t\t\t\t\thitsound.EmitterSize \t= hitsound.EmitterSize * (scale ^ 2)\n\t\t\t\t\t\thitsound.MaxDistance \t= hitsound.MaxDistance * (scale ^ 3)\n\t\t\t\t\t\thitsound.PlaybackSpeed \t= 1 - ((scale-1) * 0.2)\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal deathPart = Instance.new(\"Part\")\n\t\t\t\t\tdeathPart.Anchored = true\n\t\t\t\t\tdeathPart.CanCollide = false\n\t\t\t\t\tdeathPart.Parent = workspace.CurrentCamera\n\t\t\t\t\tdeathPart.Size = Vector3.new(0.1,0.1,0.1)\n\t\t\t\t\tdeathPart.Transparency = 1\n\t\t\t\t\tdeathPart.CFrame = entityManifest.CFrame\n\n\t\t\t\t\thitsound.Parent = deathPart\n\t\t\t\t\thitsound:Play()\n\n\t\t\t\t\tgame.Debris:AddItem(deathPart,hitsound.TimeLength + 0.1)\n\t\t\t\tend\n\n\t\t\t\tlocal stateData = entityStatesData[newState]\n\n\t\t\t\tspawn(function()\n\t\t\t\t\tif stateData and stateData.execute and renderEntityData.entityContainer and renderEntityData.entityContainer:FindFirstChild(\"entity\") then\n\t\t\t\t\t\tstateData.execute(client, monsterAnimations.death, entityBaseData, renderEntityData.entityContainer)\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\telseif entityManifest.entityType.Value == \"character\" then\n\t\t\t\tlocal entity = renderEntityData.entityContainer and renderEntityData.entityContainer:FindFirstChild(\"entity\")\n\t\t\t\tif entity then\n\t\t\t\t\tragdoll_manager.ragDollCharacter(renderEntityData.entityContainer,renderEntityData)\n\t\t\t\tend\n\t\t\tend\n\t\telseif newState == \"gettingUp\" and entityManifest.entityType.Value == \"character\" then\n\t\t\tlocal animation = characterEntityAnimationTracks.movementAnimations[newState]\n\n\t\t\tif animation then\n\t\t\t\tlocal connection\n\t\t\t\tlocal function onAnimationStopped()\n\t\t\t\t\tif connection then\n\t\t\t\t\t\tconnection:disconnect()\n\t\t\t\t\t\tconnection = nil\n\t\t\t\t\tend\n\n\t\t\t\t\tif associatePlayer == client then\n\t\t\t\t\t\t-- client is the one gettingUp, so after the animation finishes we want to stop\n\t\t\t\t\t\t-- the state\n\t\t\t\t\t\tnetwork:invoke(\"setCharacterArrested\", false)\n\t\t\t\t\t\tnetwork:invoke(\"setCharacterMovementState\", \"isGettingUp\", false)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tconnection = animation.Stopped:connect(onAnimationStopped)\n\n\t\t\t\tanimation.Looped = false\n\t\t\t\tanimation:Play()\n\n\t\t\t\tif associatePlayer == client then\n\t\t\t\t\tnetwork:invoke(\"setCharacterArrested\", true)\n\t\t\t\tend\n\t\t\tend\n\t\telse\n\t\t\tif entityManifest.entityType.Value == \"monster\" or entityManifest.entityType.Value == \"pet\" then\n\t\t\t\t-- todo: can we remove monsterAnimations[newState] ?\n\t\t\t\tif monsterAnimations[newState] or (entityStatesData[newState] and entityStatesData[newState].animationEquivalent and monsterAnimations[entityStatesData[newState].animationEquivalent]) then\n\t\t\t\t\tlocal targetAnimation \t= monsterAnimations[newState] or monsterAnimations[entityStatesData[newState].animationEquivalent]\n\n\t\t\t\t\t-- added support for animation variance for a single state\n\t\t\t\t\tif monsterAnimations[newState..\"2\"] then\n\t\t\t\t\t\tlocal chance = math.random(2)\n\t\t\t\t\t\tif chance == 2 then\n\t\t\t\t\t\t\ttargetAnimation = monsterAnimations[newState..\"2\"]\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal stateData = entityStatesData[newState]-- or (entityStatesData[newState].animationEquivalent and entityStatesData[entityStatesData[newState].animationEquivalent])\n\n\t\t\t\t\tif targetAnimation then\n\t\t\t\t\t\tcurrentPlayingStateAnimation = targetAnimation\n\t\t\t\t\t\tcurrentPlayingStateAnimation.Priority = (entityStatesData[newState] and entityStatesData[newState].animationPriority) or Enum.AnimationPriority.Idle\n\t\t\t\t\t\tcurrentPlayingStateAnimation.Looped = (entityStatesData[newState] and entityStatesData[newState].doNotLoopAnimation ~= true) or false\n\t\t\t\t\t\tcurrentPlayingStateAnimation:Play()\n\t\t\t\t\telse\n\t\t\t\t\t\ttargetAnimation = nil\n\t\t\t\t\tend\n\n\t\t\t\t\t-- yikesssss\n\t\t\t\t\tif stateData.additional_animation_to_play_temp then\n\t\t\t\t\t\tmonsterAnimations[stateData.additional_animation_to_play_temp]:Play()\n\t\t\t\t\tend\n\n\t\t\t\t\tif stateData and stateData.execute then\n\t\t\t\t\t\t-- client damaging thingy!\n\t\t\t\t\t\tspawn(function()\n\t\t\t\t\t\t\tstateData.execute(client, targetAnimation, entityBaseData, renderEntityData.entityContainer)\n\t\t\t\t\t\tend)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telseif entityManifest.entityType.Value == \"character\" then\n\t\t\t\tlocal weaponStateAppendment = item_manager.GetWeaponStateAppendment(renderEntityData)\n\n\t\t\t\tlocal animationNameToLookFor = newState do\n\t\t\t\t\tif entityManifest.entityId.Value ~= \"\" then\n\t\t\t\t\t\t-- classifying this as a monster masking as a player,\n\t\t\t\t\t\t-- use their states animation\n\n\t\t\t\t\t\tif monsterLookup[entityManifest.entityId.Value] then\n\t\t\t\t\t\t\tif monsterLookup[entityManifest.entityId.Value].statesData.states[newState].animationEquivalent then\n\t\t\t\t\t\t\t\tanimationNameToLookFor = monsterLookup[entityManifest.entityId.Value].statesData.states[newState].animationEquivalent\n\t\t\t\t\t\t\telseif defaultMonsterStateStates[newState].animationEquivalent then\n\t\t\t\t\t\t\t\tanimationNameToLookFor = defaultMonsterStateStates[newState].animationEquivalent\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif weaponStateAppendment then\n\t\t\t\t\tif associatePlayer and associatePlayer:FindFirstChild(\"class\") and characterEntityAnimationTracks.movementAnimations[string.lower(associatePlayer.class.Value) .. \"_\" .. animationNameToLookFor .. weaponStateAppendment] then\n\t\t\t\t\t\tanimationNameToLookFor = string.lower(associatePlayer.class.Value) .. \"_\" .. animationNameToLookFor .. weaponStateAppendment\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tlocal currentlyEquipped = item_manager.getCurrentlyEquippedForRenderCharacter(renderEntityData.entityContainer.entity)\n\n\t\t\t\tif characterEntityAnimationTracks.movementAnimations[animationNameToLookFor] or (currentlyEquipped[\"1\"] and currentlyEquipped[\"1\"].baseData.equipmentType and characterEntityAnimationTracks.movementAnimations[animationNameToLookFor .. \"_\" .. currentlyEquipped[\"1\"].baseData.equipmentType .. weaponStateAppendment]) then\n\t\t\t\t\tcurrentPlayingStateAnimation = item_manager.GetCurrentlyPlayingAnimation(animationNameToLookFor,weaponStateAppendment,characterEntityAnimationTracks,renderEntityData.entityContainer.entity)\n\t\t\t\t\tif currentPlayingStateAnimation then\n\t\t\t\t\t\tif previousKeyframeReached_event then\n\t\t\t\t\t\t\tpreviousKeyframeReached_event:disconnect()\n\n\t\t\t\t\t\t\tlocal footstep_sound = renderEntityData.entityContainer:FindFirstChild(\"footstep_sound\", true)\n\t\t\t\t\t\t\tif footstep_sound and footstep_sound.Playing then\n\t\t\t\t\t\t\t\tfootstep_sound.Looped = false\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\t-- stop all emotes\n\t\t\t\t\t\tanimationInterface:stopPlayingAnimationsByAnimationCollectionName(characterEntityAnimationTracks, \"emoteAnimations\")\n\n\t\t\t\t\t\t-- probably fix this.. i really hate that animations are two layered\n\n\t\t\t\t\t\tif typeof(currentPlayingStateAnimation) == \"Instance\" then\n\t\t\t\t\t\t\tpreviousKeyframeReached_event = currentPlayingStateAnimation.KeyframeReached:connect(onCharacterAnimationTrackKeyframeReached)\n\t\t\t\t\t\t\t-- ber edit mess with weights here\n\t\t\t\t\t\t\tif animationNameToLookFor == \"walking\" then\n\t\t\t\t\t\t\t\tcurrentPlayingStateAnimation:Play(nil, (currentPlayingStateAnimation.Priority == Enum.AnimationPriority.Movement and 0.85) or 1)\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tcurrentPlayingStateAnimation:Play(nil, 1)\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif animationNameToLookFor == \"jumping\" then\n\t\t\t\t\t\t\t\tcurrentPlayingStateAnimation:AdjustSpeed(1.5)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telseif typeof(currentPlayingStateAnimation) == \"table\" then\n\t\t\t\t\t\t\tpreviousKeyframeReached_event = currentPlayingStateAnimation[1].KeyframeReached:connect(onCharacterAnimationTrackKeyframeReached)\n\n\t\t\t\t\t\t\tfor ii, obj in pairs(currentPlayingStateAnimation) do\n\t\t\t\t\t\t\t\tobj:Play()\n\n\t\t\t\t\t\t\t\tif animationNameToLookFor == \"jumping\" then\n\t\t\t\t\t\t\t\t\tobj:AdjustSpeed(1.5)\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tpreviousState = animationNameToLookFor\n\n\t\t\t\t-- end it early\n\t\t\t\treturn\n\t\t\tend\n\t\tend\n\n\t\tpreviousState = newState\n\tend\n\n\tfunction renderEntityData:playAnimation(animationSequenceName, animationName, extraData)\n\n\t\tif characterEntityAnimationTracks[animationSequenceName] and characterEntityAnimationTracks[animationSequenceName][animationName] then\n\t\t\t-- stop all emotes\n\t\t\tanimationInterface:stopPlayingAnimationsByAnimationCollectionName(characterEntityAnimationTracks, \"emoteAnimations\")\n\n\t\t\tlocal associatePlayer = game.Players:GetPlayerFromCharacter(entityManifest.Parent)\n\n\t\t\tif animationName == \"consume_consumable\" and extraData and extraData.id then\n\t\t\t\tlocal itemBaseData = itemLookup[extraData.id]\n\t\t\t\tlocal consumableManifest = itemBaseData.module:FindFirstChild(\"manifest\")\n\n\t\t\t\tif consumableManifest and renderEntityData.entityContainer and renderEntityData.entityContainer:FindFirstChild(\"entity\") then\n\t\t\t\t\tlocal consumableGrip = renderEntityData.entityContainer.entity:FindFirstChild(\"ConsumableGrip\", true)\n\n\t\t\t\t\tif consumableGrip then\n\t\t\t\t\t\tconsumableManifest = consumableManifest:Clone()\n\t\t\t\t\t\tconsumableManifest.CanCollide = false\n\t\t\t\t\t\tconsumableManifest.Anchored\t= false\n\n\t\t\t\t\t\tfor i, Child in pairs(consumableManifest:GetChildren()) do\n\t\t\t\t\t\t\tif Child:IsA(\"BasePart\") then\n\t\t\t\t\t\t\t\tlocal motor6d = Instance.new(\"Motor6D\")\n\t\t\t\t\t\t\t\tmotor6d.Part0 = consumableManifest\n\t\t\t\t\t\t\t\tmotor6d.Part1 = Child\n\t\t\t\t\t\t\t\tmotor6d.C0 = CFrame.new()\n\t\t\t\t\t\t\t\tmotor6d.C1 = Child.CFrame:toObjectSpace(consumableManifest.CFrame)\n\t\t\t\t\t\t\t\tmotor6d.Parent = Child\n\t\t\t\t\t\t\t\tChild.CanCollide = false\n\t\t\t\t\t\t\t\tChild.Anchored = false\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tconsumableManifest.Parent \t= renderEntityData.entityContainer.entity\n\t\t\t\t\t\tconsumableGrip.Part1 \t\t= consumableManifest\n\n\t\t\t\t\t\tlocal currentEquippedManifest = network:invoke(\"getCurrentWeaponManifest\", entityManifest) --currentlyEquipped[1] and currentlyEquipped[1].manifest\n\n\t\t\t\t\t\tif currentEquippedManifest then\n\t\t\t\t\t\t\tif currentEquippedManifest:IsA(\"BasePart\") then\n\t\t\t\t\t\t\t\tcurrentEquippedManifest.Transparency = 1\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tfor i,part in pairs(currentEquippedManifest:GetDescendants()) do\n\t\t\t\t\t\t\t\tif part:isA(\"BasePart\") then\n\t\t\t\t\t\t\t\t\tpart.Transparency = part.Transparency + 1\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\t\tcharacterEntityAnimationTracks[animationSequenceName][\"consume_loop\"]:Play()\n\n\t\t\t\t\t\tlocal connection\n\t\t\t\t\t\tconnection = characterEntityAnimationTracks[animationSequenceName][\"consume_loop\"].Stopped:connect(function()\n\t\t\t\t\t\tend)\n\n\t\t\t\t\t\tif itemBaseData.useSound and game.ReplicatedStorage:FindFirstChild(\"sounds\") and consumableManifest then\n\t\t\t\t\t\t\tlocal soundMirror = game.ReplicatedStorage.assets.sounds:FindFirstChild(itemBaseData.useSound)\n\t\t\t\t\t\t\tif soundMirror then\n\t\t\t\t\t\t\t\tlocal sound = Instance.new(\"Sound\")\n\t\t\t\t\t\t\t\tfor property, value in pairs(game.HttpService:JSONDecode(soundMirror.Value)) do\n\t\t\t\t\t\t\t\t\tsound[property] = value\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tsound.Parent = consumableManifest\n\t\t\t\t\t\t\t\tsound.Volume = 0.8\n\t\t\t\t\t\t\t\tsound.MaxDistance = 150\n\t\t\t\t\t\t\t\tsound:Play()\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tdelay(extraData.ANIMATION_DESIRED_LENGTH or 2, function()\n\t\t\t\t\t\t\tif consumableManifest then\n\t\t\t\t\t\t\t\tif consumableManifest:FindFirstChild(\"consumed\") then\n\t\t\t\t\t\t\t\t\tconsumableManifest.consumed.Transparency = 1\n\t\t\t\t\t\t\t\telseif itemBaseData.useSound == \"eat_food\" then\n\t\t\t\t\t\t\t\t\tconsumableManifest.Transparency = 1\n\t\t\t\t\t\t\t\t\tfor i,part in pairs(consumableManifest:GetChildren()) do\n\t\t\t\t\t\t\t\t\t\tif part:IsA(\"BasePart\") then\n\t\t\t\t\t\t\t\t\t\t\tpart.Transparency = 1\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tdelay(0.4, function()\n\t\t\t\t\t\t\t\tif consumableGrip.Part1 == consumableManifest then\n\t\t\t\t\t\t\t\t\tconsumableGrip.Part1 = nil\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif currentEquippedManifest then\n\t\t\t\t\t\t\t\t\tif currentEquippedManifest:IsA(\"BasePart\") then\n\t\t\t\t\t\t\t\t\t\tcurrentEquippedManifest.Transparency = 0\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tfor i,part in pairs(currentEquippedManifest:GetDescendants()) do\n\t\t\t\t\t\t\t\t\t\tif part:isA(\"BasePart\") then\n\t\t\t\t\t\t\t\t\t\t\tpart.Transparency = part.Transparency - 1\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif consumableManifest then\n\t\t\t\t\t\t\t\t\tconsumableManifest:Destroy()\n\t\t\t\t\t\t\t\t\tconsumableManifest = nil\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tconnection:disconnect()\n\t\t\t\t\t\t\tend)\n\n\t\t\t\t\t\t\tcharacterEntityAnimationTracks[animationSequenceName][\"consume_loop\"]:Stop()\n\t\t\t\t\t\tend)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\telseif animationName == \"cast-line\" and extraData and extraData.targetPosition then\n\t\t\t\tlocal currentWeaponManifest = network:invoke(\"getCurrentWeaponManifest\", entityManifest)\n\n\t\t\t\tif not currentWeaponManifest or not currentWeaponManifest:FindFirstChild(\"line\") then return end\n\n\t\t\t\tcharacterEntityAnimationTracks[animationSequenceName][animationName]:Play()\n\n\t\t\t\twait(0.75)\n\n\t\t\t\tif not currentWeaponManifest or not currentWeaponManifest:FindFirstChild(\"line\") then return end\n\n\t\t\t\tlocal startPosition = (currentWeaponManifest.CFrame * CFrame.new(0, currentWeaponManifest.Size.Y / 2, 0)).p\n\t\t\t\tlocal unitDirection = ((Vector3.new(extraData.targetPosition.X, extraData.targetPosition.Y, extraData.targetPosition.Z) - startPosition).unit + Vector3.new(0, 0.08, 0)).unit--((Vector3.new(extraData.targetPosition.X, startPosition.Y, extraData.targetPosition.Z) - startPosition).unit + Vector3.new(0, 0.05, 0)).unit\n\n\t\t\t\tif renderEntityData.fishingBob then\n\t\t\t\t\trenderEntityData.fishingBob:Destroy()\n\t\t\t\t\trenderEntityData.fishingBob = nil\n\t\t\t\tend\n\n\t\t\t\trenderEntityData.fishingBob \t\t= game.ReplicatedStorage.fishingBob:Clone()\n\t\t\t\trenderEntityData.fishingBob.Parent \t= placeSetup.getPlaceFolder(\"entities\")\n\n\t\t\t\tcurrentWeaponManifest.line.Attachment1 = renderEntityData.fishingBob.Attachment\n\t\t\t\tlocal castSound = utilities.playSound(\"fishingPoleCast_Short\", currentWeaponManifest)\n\n\t\t\t\tlocal t = 2*(   math.abs(startPosition.Y -  extraData.targetPosition.Y)  ) /60\n\t\t\t\tlocal dist = (extraData.targetPosition - startPosition).magnitude\n\t\t\t\tlocal vel = math.clamp(dist/t, 1, 70)\n\n\t\t\t\tprojectile.createProjectile(\n\t\t\t\t\tstartPosition,\n\t\t\t\t\tunitDirection,\n\t\t\t\t\tvel,    --2xheight / workspace.gravity\n\t\t\t\t\trenderEntityData.fishingBob,\n\t\t\t\t\tfunction(hitPart, hitPosition, hitNormal, hitMaterial)\n\t\t\t\t\t\tif hitPart and game.CollectionService:HasTag(hitPart, \"fishingSpot\") then --if hitPart and hitPart == workspace.Terrain and hitMaterial == Enum.Material.Water then\n\n\t\t\t\t\t\t\tif associatePlayer == client then\n\t\t\t\t\t\t\t\tnetwork:fire(\"fishingBobHit\", true)\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif renderEntityData.fishingBob and renderEntityData.fishingBob:FindFirstChild(\"splash\") then\n\t\t\t\t\t\t\t\trenderEntityData.fishingBob.splash:Emit(20)\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tif castSound then\n\t\t\t\t\t\t\t\tcastSound:Stop()\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tutilities.playSound(\"fishing_BaitSplash\", renderEntityData.fishingBob)\n\t\t\t\t\t\t\tnetwork:invokeServer(\"playerRequest_startFishing\", hitPosition)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tif associatePlayer == client then\n\t\t\t\t\t\t\t\tnetwork:fire(\"fishingBobHit\", false)\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tif renderEntityData.fishingBob then\n\t\t\t\t\t\t\t\tgame:GetService(\"Debris\"):AddItem(renderEntityData.fishingBob, 1 / 30)\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\trenderEntityData.fishingBob = nil\n\t\t\t\t\t\tend\n\t\t\t\t\tend,\n\t\t\t\t\tfunction(t)\n\t\t\t\t\t\tif currentWeaponManifest:FindFirstChild(\"line\") then\n\t\t\t\t\t\t\tcurrentWeaponManifest.line.Length = 25 * t\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\t)\n\t\t\telseif animationName == \"reel-line\" then\n\t\t\t\tlocal currentWeaponManifest = network:invoke(\"getCurrentWeaponManifest\")\n\t\t\t\tif not currentWeaponManifest or not currentWeaponManifest:FindFirstChild(\"line\") then return end\n\n\t\t\t\tcharacterEntityAnimationTracks[animationSequenceName][animationName]:Play()\n\n\t\t\t\tif associatePlayer == client then\n\t\t\t\t\twait(0.75)\n\n\t\t\t\t\tnetwork:invoke(\"setCharacterMovementState\", \"isFishing\", false)\n\t\t\t\t\tnetwork:invoke(\"setCharacterArrested\", false)\n\n\t\t\t\t\tlocal fish, fishVelocity\n\t\t\t\t\tif renderEntityData.fishingBob then\n\t\t\t\t\t\tlocal _, fishModel, fishVelocityGiven = network:invokeServer(\"playerRequest_reelFishingRod\", renderEntityData.fishingBob.Position)\n\t\t\t\t\t\tfish = fishModel\n\t\t\t\t\t\tfishVelocity = fishVelocityGiven\n\t\t\t\t\tend\n\n\t\t\t\t\tif currentWeaponManifest and not fish then\n\t\t\t\t\t\tcurrentWeaponManifest.line.Attachment1 = nil\n\t\t\t\t\telseif currentWeaponManifest and fish then\n\t\t\t\t\t\tfish.Velocity = fishVelocity\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif renderEntityData.fishingBob then\n\t\t\t\t\trenderEntityData.fishingBob:Destroy()\n\t\t\t\t\trenderEntityData.fishingBob = nil\n\t\t\t\tend\n\t\t\telseif extraData and extraData.dance then\n\t\t\t\t-- dance emotes\n\t\t\t\tlocal currentEquippedManifest\n\t\t\t\tif  animationName ~= \"point\" then\n\t\t\t\t\tcurrentEquippedManifest = network:invoke(\"getCurrentWeaponManifest\", entityManifest) --currentlyEquipped[1] and currentlyEquipped[1].manifest\n\n\t\t\t\t\tif currentEquippedManifest then\n\t\t\t\t\t\tif currentEquippedManifest:IsA(\"BasePart\") then\n\t\t\t\t\t\t\tcurrentEquippedManifest.Transparency = 1\n\t\t\t\t\t\tend\n\t\t\t\t\t\tfor i,part in pairs(currentEquippedManifest:GetDescendants()) do\n\t\t\t\t\t\t\tif part:isA(\"BasePart\") then\n\t\t\t\t\t\t\t\tpart.Transparency = part.Transparency + 1\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tlocal prop\n\t\t\t\tlocal extraEmoteInfo = {\n\t\t\t\t\t[\"handstand\"]\t= {fadeTime = .3;};\n\t\t\t\t\t[\"sit\"]\t\t\t= {fadeTime = .3;};\n\t\t\t\t\t[\"panic\"]\t\t= {fadeTime = .3;};\n\t\t\t\t\t[\"pushups\"]\t\t= {fadeTime = .3;};\n\t\t\t\t\t[\"point\"] \t\t= {singleAction = true;};\n\t\t\t\t\t[\"flex\"] \t\t= {singleAction = true;};\n\t\t\t\t\t[\"guitar\"] \t\t= {singleAction = true;};\n\t\t\t\t\t[\"tadaa\"] \t\t= {singleAction = true;};\n\t\t\t\t\t[\"cheer\"] \t\t= {singleAction = true;};\n\t\t\t\t}\n\n\n\t\t\t\tif extraEmoteInfo[animationName] and  extraEmoteInfo[animationName].fadeTime then\n\t\t\t\t\tcharacterEntityAnimationTracks[animationSequenceName][animationName]:Play(extraEmoteInfo[animationName].fadeTime)\n\t\t\t\telse\n\t\t\t\t\tcharacterEntityAnimationTracks[animationSequenceName][animationName]:Play()\n\t\t\t\tend\n\n\t\t\t\t--to-do: potentially do a unified system for animations that hold\n\t\t\t\tif animationName == \"beg\" then\n\t\t\t\t\tprop = assetFolder.Plate:Clone()\n\t\t\t\t\tlocal weld = Instance.new(\"WeldConstraint\")\n\t\t\t\t\tweld.Parent = prop\n\t\t\t\t\tprop.CFrame = renderEntityData.entityContainer.entity.RightHand.CFrame *CFrame.Angles(math.pi,0,0) * CFrame.new(-.5,.16,-.2)\n\t\t\t\t\tweld.Part1 = prop\n\t\t\t\t\tweld.Part0 = renderEntityData.entityContainer.entity.RightHand\n\t\t\t\t\tprop.Parent = workspace\n\n\n\t\t\t\t\tdelay(3.1, function()\n\t\t\t\t\t\t--characterEntityAnimationTracks[animationSequenceName][\"beg_hold\"]:Play()\n\t\t\t\t\t\tcharacterEntityAnimationTracks[animationSequenceName][animationName]:AdjustSpeed(0)\n\t\t\t\t\tend)\n\t\t\t\tend\n\n\t\t\t\tlocal connection\n\t\t\t\tconnection = characterEntityAnimationTracks[animationSequenceName][animationName].Stopped:connect(function()\n\t\t\t\t\tif prop then\n\t\t\t\t\t\tprop:Destroy()\n\t\t\t\t\tend\n\n\n\t\t\t\t\tif currentEquippedManifest then\n\t\t\t\t\t\tif currentEquippedManifest:IsA(\"BasePart\") then\n\t\t\t\t\t\t\tcurrentEquippedManifest.Transparency = 0\n\t\t\t\t\t\tend\n\t\t\t\t\t\tfor i,part in pairs(currentEquippedManifest:GetDescendants()) do\n\t\t\t\t\t\t\tif part:isA(\"BasePart\") then\n\t\t\t\t\t\t\t\tpart.Transparency = part.Transparency - 1\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\t-- kind of a messy solution but this is needed for emotes to properly work with the control script\n\t\t\t\t\tif extraEmoteInfo[animationName] and  extraEmoteInfo[animationName].singleAction then\n\t\t\t\t\t\tcontrol.endEmote()\n\t\t\t\t\tend\n\n\t\t\t\t\tconnection:disconnect()\n\t\t\t\tend)\n\t\t\telse\n\t\t\t\tlocal animationToBePlayed = characterEntityAnimationTracks[animationSequenceName][animationName]\n\t\t\t\tif animationSequenceName == \"bowAnimations\" then\n\t\t\t\t\tbow_manager.PlayAnimation(animationSequenceName)\n\t\t\t\telseif animationSequenceName == \"staffAnimations\" then\n\t\t\t\t\tstaff_manager.PlayAnimation(animationToBePlayed,extraData,configuration)\n\t\t\t\tend\n\n\t\t\t\tif animationToBePlayed then\n\t\t\t\t\tmelee_manager.PlayAnimation(animationSequenceName, characterEntityAnimationTracks, animationName, animationToBePlayed, extraData)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function onEntityTypeChanged(newEntityType)\n\t\tdissassembleRenderEntityByManifest(entityManifest)\n\n\t\t-- ugly hack since the building function requires this function\n\t\t-- EPIC CIRCULAR REQUIREMENTS!\n\t\tnetwork:invoke(\"assembleEntityByManifest\", entityManifest)\n\tend\n\n\tlocal function onEntityIdChanged()\n\t\tprint(\"NateDebuggingSomeRandomFunctionWeHaveThatFiresWhenEntityIdChangesButDosentHaveAnythingInIt\")\n\tend\n\n\tlocal monsterNameUI\n\tlocal monsterNameTag\n\n\tlocal function setupMonsterDisplayUI()\n\t\t--local monsterNameUIPart = renderEntityData.entityContainer:FindFirstChild(\"MonsterHealthTag\") or assetFolder.MonsterHealthTag:Clone()\n\t\t--monsterNameUIPart.Parent = renderEntityData.entityContainer\n\t\t--monsterNameUI\t= monsterNameUIPart.SurfaceGui\n\t\tmonsterNameUI = assetFolder.monsterHealth:Clone()\n\t\tmonsterNameUI.Parent = renderEntityData.entityContainer\n\t\tmonsterNameUI.Adornee = renderEntityData.entityContainer\n\t\tmonsterNameUI.Enabled = false\n\t\t--monsterNameUIPart.Parent = renderEntityData.entityContainer\n\n\t\tlocal monsterNamePart = renderEntityData.entityContainer:FindFirstChild(\"MonsterEnemyTag\") or assetFolder.MonsterEnemyTag:Clone()\n\t\tmonsterNamePart.Parent = workspace.CurrentCamera\n\n\t\tmonsterNameTag = monsterNamePart.SurfaceGui\n\t\tmonsterNameTag.Enabled = false\n\n\t\tlocal monsterScaled = false\n\n\t\tlocal function monsterScale()\n\t\t\tif not monsterScaled then\n\t\t\t\tif entityManifest.monsterScale.Value > 1.3 then\n\t\t\t\t\tmonsterScaled = true\n\t\t\t\t\tmonsterNameTag.skull.Visible= true\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif entityManifest:FindFirstChild(\"monsterScale\") then\n\t\t\tmonsterScale()\n\t\telse\n\t\t\tlocal scaleConnection\n\t\t\tscaleConnection = entityManifest.ChildAdded:connect(function(child)\n\t\t\t\tif child.Name == \"monsterScale\" then\n\t\t\t\t\tif scaleConnection then\n\t\t\t\t\t\tscaleConnection:disconnect()\n\t\t\t\t\t\tscaleConnection = nil\n\t\t\t\t\tend\n\n\t\t\t\t\tmonsterScale()\n\t\t\t\tend\n\t\t\tend)\n\n\t\t\ttable.insert(renderEntityData.connections, scaleConnection)\n\t\tend\n\n\t\tmonsterNamePart.Parent = renderEntityData.entityContainer\n\n\t\tlocal level = entityManifest:FindFirstChild(\"level\") and entityManifest.level.Value or 1\n\t\tlocal levelText = \"Lvl \"..tostring(level)\n\t\tmonsterNameTag.level.Text = levelText\n\n\t\tif renderEntityData.disableLevelUI then\n\t\t\tmonsterNameTag.level.Visible = false\n\t\tend\n\n\t\tlocal levelBounds = game.TextService:GetTextSize(levelText, monsterNameTag.level.TextSize, monsterNameTag.level.Font, Vector2.new()).X + 6\n\t\tmonsterNameTag.level.Size = UDim2.new(0, levelBounds, 1, -6)\n\n\t\tlocal isMonsterPet = not not entityManifest:FindFirstChild(\"pet\")\n\t\tlocal monsterText = entityManifest.entityId:FindFirstChild(\"nickname\") and entityManifest.entityId.nickname.Value or entityManifest.entityId.Value\n\n\t\tlocal isNicknamed = entityManifest.entityId:FindFirstChild(\"nickname\") ~= nil\n\n\t\tif isMonsterPet and not entityManifest.entityId:FindFirstChild(\"nickname\") then\n\t\t\tmonsterText = itemLookup[tonumber(entityManifest.entityId.Value)].name\n\t\tend\n\n\t\tif entityManifest:FindFirstChild(\"specialName\") then\n\t\t\tmonsterText = entityManifest.specialName.Value\n\t\tend\n\n\t\tif entityManifest:FindFirstChild(\"monsterScale\") and (not entityManifest:FindFirstChild(\"notGiant\")) then\n\t\t\tif entityManifest.monsterScale.Value > 4.5 then\n\t\t\t\tmonsterText = \"Colossal \" .. monsterText\n\t\t\telseif entityManifest.monsterScale.Value > 2.5 then\n\t\t\t\tmonsterText = \"Super Giant \" .. monsterText\n\t\t\telseif entityManifest.monsterScale.Value > 1.3 then\n\t\t\t\tmonsterText = \"Giant \" .. monsterText\n\t\t\tend\n\t\tend\n\n\t\tif not isMonsterPet then\n\t\t\tmonsterNameTag.monster.AutoLocalize = true\n\t\t\tmonsterNameTag.monster.Text = monsterText\n\t\t\tmonsterNameTag.monster.Size = UDim2.new(0, game.TextService:GetTextSize(monsterText, monsterNameTag.monster.TextSize, monsterNameTag.monster.Font, Vector2.new()).X, 1, 0)\n\t\telse\n\t\t\tmonsterNameTag.level.Visible \t= false\n\t\t\tmonsterNameTag.monster.Visible \t= false\n\t\t\tmonsterNameTag.nickname.Visible = true\n\n\t\t\tmonsterNameTag.nickname.AutoLocalize = not isNicknamed\n\t\t\tmonsterNameTag.nickname.Text \t= monsterText\n\t\t\tmonsterNameTag.nickname.Size \t= UDim2.new(0, game.TextService:GetTextSize(monsterText, monsterNameTag.nickname.TextSize, monsterNameTag.nickname.Font, Vector2.new()).X + 10, 1, -4)\n\t\tend\n\tend\n\n\tlocal function cleanupMonsterDisplayUI()\n\n\t\tlocal monsterNamePart = renderEntityData.entityContainer:FindFirstChild(\"MonsterEnemyTag\")\n\t\tif monsterNamePart then\n\t\t\tmonsterNamePart:Destroy()\n\t\tend\n\tend\n\n\tlocal function setupCharacterDisplayUI()\n\t\tlocal nameTag \t= renderEntityData.entityContainer.PrimaryPart:FindFirstChild(\"PlayerTag\") or assetFolder.PlayerTag:Clone()\n\t\tnameTag.Parent \t= renderEntityData.entityContainer.PrimaryPart\n\n\t\tif associatePlayer then\n\t\t\tlocal xpTag = assetFolder.xpTag:Clone()\n\t\t\txpTag.Parent = renderEntityData.entityContainer.PrimaryPart\n\t\t\txpTag.Enabled = true\n\n\t\t\tplayerXpTagPairing[associatePlayer] = xpTag\n\t\tend\n\n\t\tmonsterNameUI = assetFolder.monsterHealth:Clone()\n\t\tmonsterNameUI.Parent = renderEntityData.entityContainer\n\t\tmonsterNameUI.Adornee = renderEntityData.entityContainer\n\t\tmonsterNameUI.Enabled = false\n\n\t\tif associatePlayer and nameTag then\n\t\t\tlocal function updateNameTagForCharacter()\n\t\t\t\tlocal level = associatePlayer:FindFirstChild(\"level\") and associatePlayer.level.Value or 0\n\t\t\t\tlocal levelText = \"Lvl.\" .. level\n\t\t\t\tnameTag.SurfaceGui.top.level.Text = levelText\n\n\t\t\t\tlocal class = associatePlayer:FindFirstChild(\"class\") and associatePlayer.class.Value or \"unknown\"\n\t\t\t\tif class:lower() ~= \"adventurer\" then\n\t\t\t\t\tnameTag.SurfaceGui.top.class.Image = \"rbxgameasset://Images/emblem_\"..class:lower()\n\t\t\t\t\tnameTag.SurfaceGui.top.class.Visible = true\n\t\t\t\telse\n\t\t\t\t\tnameTag.SurfaceGui.top.class.Visible = false\n\t\t\t\tend\n\n\t\t\t\tnameTag.SurfaceGui.bottom.guild.Visible = false\n\t\t\t\tif associatePlayer:FindFirstChild(\"guildId\") and associatePlayer.guildId.Value ~= \"\" then\n\t\t\t\t\tlocal guildDataFolder = game.ReplicatedStorage:FindFirstChild(\"guildDataFolder\")\n\t\t\t\t\tif guildDataFolder then\n\t\t\t\t\t\tspawn(function()\n\t\t\t\t\t\t\tlocal guildDataValue = guildDataFolder:WaitForChild(associatePlayer.guildId.Value, 10)\n\t\t\t\t\t\t\tif guildDataValue then\n\t\t\t\t\t\t\t\tlocal guildData = HttpService:JSONDecode(guildDataValue.Value)\n\t\t\t\t\t\t\t\tif guildData.name then\n\t\t\t\t\t\t\t\t\tnameTag.SurfaceGui.bottom.guild.Text = guildData.name\n\t\t\t\t\t\t\t\t\tlocal nameBounds = game.TextService:GetTextSize(guildData.name, nameTag.SurfaceGui.bottom.guild.TextSize, nameTag.SurfaceGui.bottom.guild.Font, Vector2.new()).X + 10\n\t\t\t\t\t\t\t\t\tnameTag.SurfaceGui.bottom.guild.Size = UDim2.new(0, nameBounds, 1, -4)\n\t\t\t\t\t\t\t\t\tnameTag.SurfaceGui.bottom.guild.Visible = true\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tnameTag.SurfaceGui.top.input.Visible = false\n\t\t\t\tfor i,v in pairs(nameTag.SurfaceGui.top.input:GetChildren()) do\n\t\t\t\t\tif v:IsA(\"GuiObject\") then\n\t\t\t\t\t\tv.Visible = false\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif associatePlayer:FindFirstChild(\"input\") then\n\t\t\t\t\tlocal inputFrame = nameTag.SurfaceGui.top.input:FindFirstChild(associatePlayer.input.Value)\n\t\t\t\t\tif inputFrame then\n\t\t\t\t\t\tinputFrame.Visible = true\n\t\t\t\t\t\tnameTag.SurfaceGui.top.input.Visible = true\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tnameTag.SurfaceGui.top.dev.Visible = associatePlayer:FindFirstChild(\"developer\") ~= nil\n\t\t\t\tnameTag.SurfaceGui.top.player.Text = associatePlayer.Name\n\n\t\t\t\tlocal levelBounds = game.TextService:GetTextSize(levelText, nameTag.SurfaceGui.top.level.TextSize, nameTag.SurfaceGui.top.level.Font, Vector2.new()).X + 8\n\t\t\t\tnameTag.SurfaceGui.top.level.Size \t= UDim2.new(0, levelBounds, 1, -4)\n\n\t\t\t\tlocal nameBounds = game.TextService:GetTextSize(associatePlayer.Name, nameTag.SurfaceGui.top.player.TextSize, nameTag.SurfaceGui.top.player.Font, Vector2.new()).X + 10\n\t\t\t\tnameTag.SurfaceGui.top.player.Size = UDim2.new(0, nameBounds, 1, -4)\n\n\t\t\t\tlocal totalXBound = 0\n\t\t\t\tfor i,child in pairs(nameTag.SurfaceGui.top:GetChildren()) do\n\t\t\t\t\tif child:IsA(\"GuiObject\") and child.Visible then\n\t\t\t\t\t\ttotalXBound = totalXBound + child.AbsoluteSize.X\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tnameTag.SurfaceGui.curve.Size = UDim2.new(0, totalXBound + 10, 0.5, -4)\n\t\t\tend\n\n\t\t\t-- update once\n\t\t\tupdateNameTagForCharacter()\n\n\t\t\tif associatePlayer:FindFirstChild(\"guildId\") then\n\t\t\t\ttable.insert(renderEntityData.connections, associatePlayer.guildId.Changed:connect(updateNameTagForCharacter))\n\t\t\tend\n\n\t\t\tif associatePlayer:FindFirstChild(\"level\") then\n\t\t\t\ttable.insert(renderEntityData.connections, associatePlayer.level.Changed:connect(updateNameTagForCharacter))\n\t\t\tend\n\n\t\t\tif associatePlayer:FindFirstChild(\"class\") then\n\t\t\t\ttable.insert(renderEntityData.connections, associatePlayer.class.Changed:connect(updateNameTagForCharacter))\n\t\t\tend\n\n\t\t\tif associatePlayer:FindFirstChild(\"input\") then\n\t\t\t\ttable.insert(renderEntityData.connections, associatePlayer.input.Changed:connect(updateNameTagForCharacter))\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function cleanupCharacterDisplayUI()\n\t\tlocal nameTag = renderEntityData.entityContainer.PrimaryPart:FindFirstChild(\"PlayerTag\")\n\t\tif nameTag then\n\t\t\tnameTag:Destroy()\n\t\tend\n\n\t\tlocal chatTag = renderEntityData.entityContainer:FindFirstChild(\"chatGui\")\n\t\tif chatTag then\n\t\t\tchatTag:Destroy()\n\t\tend\n\tend\n\n\tlocal currentHealth = entityManifest.health.Value\n\tlocal isShowingDamageAnimation = false\n\tlocal function onEntityHealthChanged(newHealth)\n\t\tif not monsterNameUI then\n\t\t\tif renderEntityData.entityContainer.PrimaryPart:FindFirstChild(\"monsterNameUI\") then\n\t\t\t\tmonsterNameUI = renderEntityData.entityContainer.PrimaryPart.monsterNameUI\n\t\t\tend\n\t\tend\n\n\t\tif not monsterNameTag then\n\t\t\tif renderEntityData.entityContainer.PrimaryPart then\n\t\t\t\tif renderEntityData.entityContainer.PrimaryPart:FindFirstChild(\"monsterNameTag\") then\n\t\t\t\t\tmonsterNameTag = renderEntityData.entityContainer.PrimaryPart.monsterNameTag\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif monsterNameTag and renderEntityData.entityContainer.Name ~= \"Chicken\" then\n\t\t\tmonsterNameTag.Enabled = true\n\t\tend\n\n\t\t-- should we show the health?\n\t\tif monsterNameUI then\n\t\t\tlocal healthUI = monsterNameUI\n\n\t\t\tif entityBaseData and entityBaseData.boss then\n\t\t\t\tmonsterNameUI.Enabled = false\n\n\t\t\t\thealthUI = network:invoke(\"prepareBossHealthUIForMonster\", entityBaseData) or healthUI\n\t\t\t\tif healthUI and newHealth <= 0 then\n\t\t\t\t\thealthUI.Visible = false\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tif entityManifest.maxHealth.Value > newHealth then\n\t\t\t\t\tif associatePlayer then\n\t\t\t\t\t\tmonsterNameUI.Enabled = true\n\t\t\t\t\telse\n\t\t\t\t\t\tmonsterNameUI.Enabled = true\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tmonsterNameUI.Enabled = false\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal fill = healthUI.container.backgroundFill\n\n\t\t\tlocal goal = UDim2.new(math.clamp(newHealth / entityManifest.maxHealth.Value, 0, 1), 0, 1, 0)\n\t\t\tif fill.healthLag.Size.X.Scale < fill.currentHealthFill.Size.X.Scale then\n\t\t\t\tfill.healthLag.Size = fill.currentHealthFill.Size\n\t\t\tend\n\n\t\t\tfill.healthLag.ImageColor3 = Color3.fromRGB(255, 23, 23)\n\n\t\t\tfill.currentHealthFill.Size = goal\n\n\t\t\tspawn(function()\n\t\t\t\twait(0.5)\n\t\t\t\tif fill and fill:FindFirstChild(\"healthLag\") and fill.currentHealthFill.Size == goal then\n\t\t\t\t--if fill and fill:FindFirstChild(\"healthLag\") and fill.healthLag.ImageColor3 == Color3.fromRGB(255, 43, 43) then\n\t\t\t\t\ttween(fill.healthLag, {\"Size\",\"ImageColor3\"}, {goal, Color3.fromRGB(255, 210, 38)}, 0.3)\n\t\t\t\tend\n\t\t\tend)\n\t\tend\n\n\t\t-- check if was damaged\n\t\tif newHealth < currentHealth then\n\t\t\tif not isShowingDamageAnimation then\n\t\t\t\tisShowingDamageAnimation = true\n\n\t\t\t\tlocal head = renderEntityData.entityContainer.entity:FindFirstChild(\"head\") or renderEntityData.entityContainer.entity:FindFirstChild(\"body\") or renderEntityData.entityContainer.PrimaryPart\n\t\t\t\tif head:FindFirstChild(\"damageTaken\") then\n\t\t\t\t\thead.damageTaken:Play()\n\t\t\t\tend\n\n\t\t\t\tif monsterAnimations.damaged then\n\t\t\t\t\tmonsterAnimations.damaged.Looped = false\n\t\t\t\t\tmonsterAnimations.damaged:Play()\n\t\t\t\tend\n\n\t\t\t\tlocal hitsound\n\n\t\t\t\tif renderEntityData.entityContainer.entity.PrimaryPart and renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"hit\") then\n\t\t\t\t\tlocal hitsounds = {renderEntityData.entityContainer.entity.PrimaryPart.hit}\n\t\t\t\t\tif renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"hit2\") then\n\t\t\t\t\t\ttable.insert(hitsounds, renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"hit2\"))\n\t\t\t\t\tend\n\n\t\t\t\t\tif renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"hit3\") then\n\t\t\t\t\t\ttable.insert(hitsounds, renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"hit3\"))\n\t\t\t\t\tend\n\n\t\t\t\t\tif renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"hit4\") then\n\t\t\t\t\t\ttable.insert(hitsounds, renderEntityData.entityContainer.entity.PrimaryPart:FindFirstChild(\"hit4\"))\n\t\t\t\t\tend\n\n\t\t\t\t\thitsound = hitsounds[math.random(1,#hitsounds)]\n\t\t\t\tend\n\n\t\t\t\tif hitsound then\n\t\t\t\t\tif entityManifest:FindFirstChild(\"monsterScale\") and entityManifest.monsterScale.Value > 1.3 and hitsound:FindFirstChild(\"scalePitch\") == nil then\n\t\t\t\t\t\tlocal scale = entityManifest.monsterScale.Value\n\t\t\t\t\t\thitsound.EmitterSize = hitsound.EmitterSize * scale\n\t\t\t\t\t\thitsound.MaxDistance = hitsound.MaxDistance * (scale ^ 2)\n\t\t\t\t\t\tlocal scalePitch = Instance.new(\"PitchShiftSoundEffect\")\n\t\t\t\t\t\tscalePitch.Name = \"scalePitch\"\n\t\t\t\t\t\tscalePitch.Octave = 0.9\n\t\t\t\t\t\tscalePitch.Parent = hitsound\n\t\t\t\t\tend\n\n\t\t\t\t\thitsound:Play()\n\t\t\t\tend\n\n\t\t\t\tspawn(function()\n\t\t\t\t\twait(0.5)\n\n\t\t\t\t\tif isShowingDamageAnimation then\n\t\t\t\t\t\tisShowingDamageAnimation = false\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\tend\n\t\tend\n\n\t\tcurrentHealth = newHealth\n\tend\n\n\tfunction renderEntityData:setWeaponState(weaponType, weaponState)\n\t\trenderEntityData.weaponState = weaponState\n\n\t\t-- force a refresh of the state animations\n\t\tlocal needStateChange = item_manager.RefreshStateAnimation()\n\t\tif needStateChange then\n\t\t\tonEntityStateChanged(entityManifest.state.Value)\n\t\tend\n\tend\n\n\t-- todo: refactor this to fit current statusEffect schema (does anything even use this?)\n\tfunction renderEntityData:changeStatusEffectState(sourcePlayer, sourceType, sourceId, isEnabled)\n\t\tif sourceType == \"ability\" and renderEntityData.entityContainer then\n\t\t\tlocal playerData\n\t\t\tif sourcePlayer == game.Players.LocalPlayer then\n\t\t\t\tplayerData = network:invoke(\"getLocalPlayerDataCache\")\n\t\t\tend\n\n\t\t\tlocal value = entityManifest.activeAbilityExecutionData.Value\n\t\t\tlocal success, abilityExecutionData = utilities.safeJSONDecode(value)\n\n\t\t\tlocal abilityBaseData = abilityLookup[sourceId](playerData, abilityExecutionData)\n\t\t\tif abilityBaseData then\n\t\t\t\tif isEnabled and abilityBaseData.onStatusEffectBegan then\n\t\t\t\t\tabilityBaseData:onStatusEffectBegan(renderEntityData.entityContainer)\n\t\t\t\telseif not isEnabled and abilityBaseData.onStatusEffectEnded then\n\t\t\t\t\tabilityBaseData:onStatusEffectEnded(renderEntityData.entityContainer)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal previousStatusEffects = nil\n\tlocal function onPlayerStatusEffectsChanged(currentStatusEffectsJSON)\n\t\tlocal success, currentStatusEffects = utilities.safeJSONDecode(currentStatusEffectsJSON)\n\n\t\tif success then\n\t\t\tif previousStatusEffects then\n\t\t\t\tlocal diff = {}\n\n\t\t\t\tfor i,v in pairs(currentStatusEffects) do\n\t\t\t\t\tdiff[v.id] = 1\n\t\t\t\tend\n\n\t\t\t\tfor i,v in pairs(previousStatusEffects) do\n\t\t\t\t\tdiff[v.id] = (diff[v.id] or 0) - 1\n\t\t\t\tend\n\n\t\t\t\tfor id, state in pairs(diff) do\n\t\t\t\t\t-- 1 = added, 0 = same, -1 = removed\n\t\t\t\t\tlocal statusEffectData = statusEffectLookup[id]\n\n\t\t\t\t\tif statusEffectData then\n\t\t\t\t\t\tif state == 1 and statusEffectData.__clientApplyTransitionEffectOnCharacter then\n\t\t\t\t\t\t\tstatusEffectData.__clientApplyTransitionEffectOnCharacter(renderEntityData.entityContainer)\n\t\t\t\t\t\telseif state == 1 and statusEffectData.__clientApplyStatusEffectOnCharacter then\n\t\t\t\t\t\t\tstatusEffectData.__clientApplyStatusEffectOnCharacter(renderEntityData.entityContainer)\n\t\t\t\t\t\telseif state == 0 then\n\t\t\t\t\t\t\t-- do nuffin\n\t\t\t\t\t\telseif state == -1 and statusEffectData.__clientRemoveStatusEffectOnCharacter then\n\t\t\t\t\t\t\tstatusEffectData.__clientRemoveStatusEffectOnCharacter(renderEntityData.entityContainer)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tpreviousStatusEffects = currentStatusEffects\n\t\t\telse\n\t\t\t\tfor i, activeStatusEffectData in pairs(currentStatusEffects) do\n\t\t\t\t\tlocal statusEffectData = statusEffectLookup[activeStatusEffectData.id]\n\n\t\t\t\t\tif statusEffectData and statusEffectData.__clientApplyStatusEffectOnCharacter then\n\t\t\t\t\t\tstatusEffectData.__clientApplyStatusEffectOnCharacter(renderEntityData.entityContainer)\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tpreviousStatusEffects = currentStatusEffects\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal previousCastingAbilityId = 0\n\n\tlocal function onPlayerCastingAbilityIdChanged(castingAbilityId)\n\t\t-- todo: make this not necessary\n\t\tif associatePlayer == client then return end\n\n\t\tlocal value = entityManifest.activeAbilityExecutionData.Value\n\t\tlocal success, abilityExecutionData = utilities.safeJSONDecode(value)\n\n\t\tif previousCastingAbilityId > 0 then\n\t\t\tlocal previousCastingAbilityBaseData = abilityLookup[previousCastingAbilityId](nil, abilityExecutionData)\n\n\t\t\tif previousCastingAbilityBaseData and previousCastingAbilityBaseData.onCastingEnded__client then\n\t\t\t\tpreviousCastingAbilityBaseData:onCastingEnded__client(renderEntityData.entityContainer)\n\t\t\tend\n\t\tend\n\n\t\tif castingAbilityId > 0 then\n\t\t\tlocal castingAbilityBaseData = abilityLookup[castingAbilityId](nil, abilityExecutionData)\n\n\t\t\tif castingAbilityBaseData and castingAbilityBaseData.onCastingBegan__client then\n\t\t\t\tcastingAbilityBaseData:onCastingBegan__client(renderEntityData.entityContainer)\n\t\t\tend\n\t\tend\n\n\t\tpreviousCastingAbilityId = castingAbilityId\n\tend\n\n\tlocal previousAbilityExecutionData = {id = 0}\n\n\tlocal function onActiveAbilityExecutionData_changed(value)\n\t\tlocal success, abilityExecutionData = utilities.safeJSONDecode(value)\n\n\t\tif success then\n\t\t\tif previousAbilityExecutionData[\"ability-guid\"] == abilityExecutionData[\"ability-guid\"] and previousAbilityExecutionData[\"ability-state\"] == \"end\" then\n\t\t\t\treturn false\n\t\t\tend\n\n\t\t\tlocal playerData\n\t\t\tif associatePlayer == client then\n\t\t\t\tplayerData = network:invoke(\"getLocalPlayerDataCache\")\n\t\t\tend\n\n\t\t\tif abilityExecutionData.id == 0 then\n\t\t\t\tlocal abilityBaseData = abilityLookup[previousAbilityExecutionData.id](playerData, previousAbilityExecutionData)\n\t\t\t\tif abilityBaseData and abilityBaseData.cleanup then\n\t\t\t\t\tabilityBaseData:cleanup(renderEntityData.entityContainer)\n\t\t\t\tend\n\n\t\t\t\tpreviousAbilityExecutionData[\"ability-state\"] = \"end\"\n\t\t\t\tif abilityBaseData and abilityBaseData.abilityDecidesEnd and abilityBaseData.execute then\n\t\t\t\t\tabilityBaseData:execute(renderEntityData.entityContainer, previousAbilityExecutionData, associatePlayer == client, associatePlayer == client and previousAbilityExecutionData[\"ability-guid\"])\n\t\t\t\tend\n\n\t\t\t\tif associatePlayer == client then\n\t\t\t\t\tnetwork:fire(\"setIsPlayerCastingAbility\", false)\n\t\t\t\tend\n\t\t\telse--if (associatePlayer ~= client or abilityExecutionData[\"ability-state\"] ~= \"begin\") then\n\t\t\t\t-- safeguard to prevent the client from double-firing\n\t\t\t\tif abilityExecutionData[\"ability-guid\"] == previousAbilityExecutionData[\"ability-guid\"] and previousAbilityExecutionData.step and abilityExecutionData.step <= previousAbilityExecutionData.step then\n\t\t\t\t\treturn false\n\t\t\t\tend\n\n\t\t\t\t-- do this at the start since :execute yields\n\t\t\t\tpreviousAbilityExecutionData = abilityExecutionData\n\n\t\t\t\tlocal abilityBaseData = abilityLookup[abilityExecutionData.id](playerData, abilityExecutionData)\n\t\t\t\tif abilityBaseData and abilityBaseData.execute then\n\t\t\t\t\tabilityBaseData:execute(renderEntityData.entityContainer, abilityExecutionData, associatePlayer == client, associatePlayer == client and abilityExecutionData[\"ability-guid\"])\n\t\t\t\t\tif associatePlayer == client and not abilityBaseData.abilityDecidesEnd then\n\t\t\t\t\t\twait()\n\t\t\t\t\t\tnetwork:invoke(\"client_changeAbilityState\", abilityExecutionData.id, \"end\", abilityExecutionData, abilityExecutionData.guid)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal function onCharacterAppearanceChanged(appearanceJSON)\n\t\tlocal success, appearanceData = utilities.safeJSONDecode(appearanceJSON)\n\n\t\tif success then\n\t\t\tint__updateRenderCharacter(renderEntityData.entityContainer.entity, appearanceData, entityManifest)\n\t\tend\n\tend\n\n\tlocal previousStatusEffectsV2\n\tlocal function onEntityStatusEffectChanged(newStatusEffectsV2)\n\t\tlocal success, currentStatusEffectsV2 = utilities.safeJSONDecode(newStatusEffectsV2)\n\n\t\tif success then\n\t\t\tif previousStatusEffectsV2 then\n\t\t\t\tlocal diff = {}\n\n\t\t\t\tfor i,v in pairs(currentStatusEffectsV2) do\n\t\t\t\t\tdiff[v.statusEffectType] = 1\n\t\t\t\tend\n\n\t\t\t\tfor i,v in pairs(previousStatusEffectsV2) do\n\t\t\t\t\tdiff[v.statusEffectType] = (diff[v.statusEffectType] or 0) - 1\n\t\t\t\tend\n\n\t\t\t\tfor id, state in pairs(diff) do\n\t\t\t\t\t-- 1 = added, 0 = same, -1 = removed\n\t\t\t\t\tlocal statusEffectData = statusEffectLookup[id]\n\n\t\t\t\t\tif statusEffectData then\n\t\t\t\t\t\tif state == 1 and statusEffectData.__clientApplyTransitionEffectOnCharacter then\n\t\t\t\t\t\t\tstatusEffectData.__clientApplyTransitionEffectOnCharacter(renderEntityData.entityContainer)\n\t\t\t\t\t\telseif state == 1 and statusEffectData.__clientApplyStatusEffectOnCharacter then\n\t\t\t\t\t\t\tstatusEffectData.__clientApplyStatusEffectOnCharacter(renderEntityData.entityContainer)\n\t\t\t\t\t\telseif state == -1 and statusEffectData.__clientRemoveStatusEffectOnCharacter then\n\t\t\t\t\t\t\tstatusEffectData.__clientRemoveStatusEffectOnCharacter(renderEntityData.entityContainer)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tpreviousStatusEffectsV2 = currentStatusEffectsV2\n\t\t\telse\n\t\t\t\tfor i, activeStatusEffectData in pairs(currentStatusEffectsV2) do\n\t\t\t\t\tlocal statusEffectData = statusEffectLookup[activeStatusEffectData.statusEffectType]\n\n\t\t\t\t\tif statusEffectData then\n\t\t\t\t\t\tif statusEffectData.__clientApplyStatusEffectOnCharacter then\n\t\t\t\t\t\t\tstatusEffectData.__clientApplyStatusEffectOnCharacter(renderEntityData.entityContainer)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tpreviousStatusEffectsV2 = currentStatusEffectsV2\n\t\t\tend\n\t\tend\n\tend\n\n\tif not renderEntityData.entityContainer.entity:FindFirstChild(\"AnimationController\") then\n\t\tlocal animationController \t= Instance.new(\"AnimationController\")\n\t\tanimationController.Parent \t= renderEntityData.entityContainer.entity\n\tend\n\n\t-- ALL CODE EXECUTION MUST GO AFTER THIS! --\n\n\tpopulateEntityData()\n\n\tif entityManifest.entityType.Value == \"monster\" or entityManifest.entityType.Value == \"pet\" then\n\t\tif entityManifest.entityType.Value == \"pet\" then\n\t\t\t-- make pets walkthroughable\n\t\t\tfor i, obj in pairs(renderEntityData.entityContainer.entity:GetDescendants()) do\n\t\t\t\tif obj:IsA(\"BasePart\") then\n\t\t\t\t\tobj.CanCollide = false\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tpopulateMonsterAnimationsTable()\n\t\tcleanupCharacterDisplayUI()\n\t\tsetupMonsterDisplayUI()\n\telseif entityManifest.entityType.Value == \"character\" then\n\t\tonPlayerStatusEffectsChanged(entityManifest.statusEffects.Value)\n\t\tonPlayerCastingAbilityIdChanged(entityManifest.castingAbilityId.Value)\n\n\t\tcleanupMonsterDisplayUI()\n\t\tsetupCharacterDisplayUI()\n\n\t\ttable.insert(renderEntityData.connections, entityManifest.statusEffects.Changed:connect(onPlayerStatusEffectsChanged))\n\t\ttable.insert(renderEntityData.connections, entityManifest.activeAbilityExecutionData.Changed:connect(onActiveAbilityExecutionData_changed))\n\t\ttable.insert(renderEntityData.connections, entityManifest.castingAbilityId.Changed:connect(onPlayerCastingAbilityIdChanged))\n\t\ttable.insert(renderEntityData.connections, entityManifest.appearance.Changed:connect(onCharacterAppearanceChanged))\n\tend\n\n\tonEntityStateChanged(entityManifest.state.Value)\n\n\t-- todo: fix\n\tif entityManifest:FindFirstChild(\"statusEffectsV2\") then\n\t\tonEntityStatusEffectChanged(entityManifest.statusEffectsV2.Value)\n\t\ttable.insert(renderEntityData.connections, entityManifest.statusEffectsV2.Changed:connect(onEntityStatusEffectChanged))\n\tend\n\n\ttable.insert(renderEntityData.connections, entityManifest.state.Changed:connect(onEntityStateChanged))\n\ttable.insert(renderEntityData.connections, entityManifest.entityType.Changed:connect(onEntityTypeChanged))\n\ttable.insert(renderEntityData.connections, entityManifest.entityId.Changed:connect(onEntityIdChanged))\n\ttable.insert(renderEntityData.connections, entityManifest.health.Changed:connect(onEntityHealthChanged))\n\n\tif associatePlayer == client then\n\t\tnetwork:fire(\"myClientCharacterContainerChanged\", renderEntityData.entityContainer)\n\tend\nend\n\nlocal function playersXpGained(playerXpRewards)\n\tfor playerName,xpgained in pairs(playerXpRewards) do\n\t\tlocal player = game.Players:FindFirstChild(playerName)\n\t\tif player then\n\t\t\tlocal xpTag = playerXpTagPairing[player]\n\t\t\tif xpTag and xpTag.Parent then\n\t\t\t\tspawn(function()\n\t\t\t\t\tlocal timestamp = xpTag:FindFirstChild(\"timestamp\")\n\t\t\t\t\tif timestamp == nil then\n\t\t\t\t\t\ttimestamp = Instance.new(\"NumberValue\")\n\t\t\t\t\t\ttimestamp.Name = \"timestamp\"\n\t\t\t\t\t\ttimestamp.Parent = xpTag\n\t\t\t\t\tend\n\t\t\t\t\tlocal difference = timestamp.Value - tick()\n\t\t\t\t\tif difference <= 0 then\n\t\t\t\t\t\ttimestamp.Value = tick() + 0.2\n\t\t\t\t\telse\n\t\t\t\t\t\ttimestamp.Value = timestamp.Value + 0.2\n\t\t\t\t\t\twait(difference)\n\t\t\t\t\tend\n\t\t\t\t\tlocal tag = xpTag.Frame.template:Clone()\n\t\t\t\t\ttag.Text = \"+\"..tostring(math.floor(xpgained))..\" XP\"\n\t\t\t\t\ttag.Parent = xpTag.Frame\n\t\t\t\t\ttag.TextTransparency = 1\n\t\t\t\t\ttag.TextStrokeTransparency = 1\n\t\t\t\t\ttag.Visible = true\n\t\t\t\t\ttween(tag, {\"Position\"},UDim2.new(0.5,0,0,0),2, Enum.EasingStyle.Linear)\n\t\t\t\t\ttween(tag, {\"TextTransparency\", \"TextStrokeTransparency\"}, {0, 0.5}, 0.3)\n\t\t\t\t\tdelay(0.5,function()\n\t\t\t\t\t\tif tag and tag.Parent then\n\t\t\t\t\t\t\ttween(tag, {\"TextTransparency\", \"TextStrokeTransparency\"}, {1, 1}, 1.5)\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\t\t\t\t\tgame.Debris:AddItem(tag, 2)\n\t\t\t\tend)\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function int__updateMonsterNameTag(renderData)\n\tlocal entityContainer = renderData.entityContainer\n\n\tlocal nameTagPart = entityContainer:FindFirstChild(\"MonsterEnemyTag\")\n\tif nameTagPart then\n\t\tlocal nameTag = nameTagPart:FindFirstChild(\"SurfaceGui\")\n\t\tif nameTag then\n\n\t\t\tlocal focus = client.Character and client.Character.PrimaryPart or workspace.CurrentCamera\n\t\t\tlocal distanceAway = utilities.magnitude(entityContainer.PrimaryPart.Position - focus.CFrame.p)\n\t\t\t-- bite me\n\t\t\t-- todo: options\n\t\t\tlocal displayDistance = 35\n\n\t\t\tlocal damagedByPlayer = renderData.entityManifest:FindFirstChild(\"damagedByPlayer\") ~= nil\n\t\t\tif damagedByPlayer then\n\t\t\t\tdisplayDistance = 50\n\t\t\tend\n\n\n\n\t\t\tif not renderData.disableNameTagUI and distanceAway < displayDistance and not renderData.entityManifest:FindFirstChild(\"isStealthed\") and renderData.entityManifest.entityId.Value ~= \"Chicken\" then\n\t\t\t\tnameTag.Enabled = true\n\n\t\t\t\tlocal position = Vector3.new(entityContainer.PrimaryPart.Position.X, entityContainer.PrimaryPart.Position.Y - (1 + entityContainer.PrimaryPart.Size.Y / 2), entityContainer.PrimaryPart.Position.Z)\n\t\t\t\tnameTagPart.CFrame = (workspace.CurrentCamera.CFrame - workspace.CurrentCamera.CFrame.p) + position\n\n\n\t\t\t\tlocal healthTag = entityContainer:FindFirstChild(\"monsterHealth\")\n\n\t\t\t\tif not renderData.disableHealthBarUI and healthTag then\n\n\t\t\t\t\t\thealthTag.Enabled = damagedByPlayer\n\t\t\t\telseif healthTag then\n\t\t\t\t\thealthTag.Enabled = false\n\t\t\t\tend\n\n\t\t\t\tlocal dif\n\n\t\t\t\tif entityContainer.PrimaryPart:FindFirstChild(\"monsterScale\") and entityContainer.PrimaryPart.monsterScale.Value > 1.3 then\n\t\t\t\t\tdisplayDistance = displayDistance * entityContainer.PrimaryPart.monsterScale.Value -- andrew 7/25/19 changed to be scale value instead of 2\n\t\t\t\tend\n\n\t\t\t\tlocal fullDisplayCutoff = 5\n\t\t\t\tif damagedByPlayer then\n\t\t\t\t\tfullDisplayCutoff = 10\n\t\t\t\tend\n\n\t\t\t\tif distanceAway >= fullDisplayCutoff then\n\t\t\t\t\tdif = (distanceAway - fullDisplayCutoff) / (displayDistance - fullDisplayCutoff)\n\n\t\t\t\telse\n\t\t\t\t\tdif = 0\n\t\t\t\tend\n\n\t\t\t\tif nameTag:FindFirstChild(\"level\") then\n\t\t\t\t\tnameTag.level.TextTransparency \t\t\t\t\t= dif\n\t\t\t\t\tnameTag.level.curve.ImageTransparency \t\t\t= dif\n\t\t\t\t\tnameTag.level.curve.shadow.ImageTransparency \t= dif\n\t\t\t\tend\n\n\t\t\t\tif nameTag:FindFirstChild(\"monster\") then\n\t\t\t\t\tnameTag.monster.TextTransparency \t\t\t\t= dif\n\t\t\t\t\tnameTag.monster.TextStrokeTransparency \t\t\t= dif * 1.1\n\t\t\t\t\tnameTag.monster.curve.ImageTransparency \t\t= dif\n\t\t\t\t\tnameTag.monster.curve.shadow.ImageTransparency \t= dif\n\n\t\t\t\t\tif nameTag.nickname.Text ~= \"\" then\n\t\t\t\t\t\tnameTag.nickname.TextTransparency \t\t= dif\n\t\t\t\t\t\tnameTag.nickname.BackgroundTransparency = dif\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif nameTag:FindFirstChild(\"skull\") and nameTag.skull.Visible then\n\t\t\t\t\tnameTag.skull.ImageTransparency = dif\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tnameTag.Enabled = false\n\n\t\t\t\tlocal healthTag = entityContainer:FindFirstChild(\"monsterHealth\")\n\t\t\t\tif healthTag then\n\t\t\t\t\thealthTag.Enabled = false\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal currentPartyInfo\nlocal function updatePartyInfo(partyInfo)\n\tcurrentPartyInfo = partyInfo or network:invokeServer(\"playerRequest_getMyPartyData\")\nend\n\nlocal function isPlayerInParty(player)\n\tif currentPartyInfo then\n\t\tfor i, entry in pairs(currentPartyInfo.members) do\n\t\t\tif entry.player == player then\n\t\t\t\treturn true\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function int__updateCharacterNameTag(renderEntityData)\n\tlocal entityContainer \t= renderEntityData.entityContainer\n\tlocal entityManifest \t= renderEntityData.entityManifest\n\n\tlocal displayRange = 35\n\n\tlocal nameTagPart = renderEntityData.entityContainer.PrimaryPart:FindFirstChild(\"PlayerTag\")\n\n\tif entityManifest == nil then\n\n\t\tif nameTagPart then\n\t\t\tlocal nameTag = nameTagPart:FindFirstChild(\"SurfaceGui\")\n\t\t\tif nameTag then\n\t\t\t\tnameTag.Enabled = false\n\t\t\tend\n\t\tend\n\n\t\treturn false\n\tend\n\n\tlocal focus = workspace.CurrentCamera\n\tlocal distanceAway = utilities.magnitude(entityContainer.PrimaryPart.Position - focus.CFrame.p)\n\n\tif nameTagPart then\n\t\tlocal nameTag = nameTagPart:FindFirstChild(\"SurfaceGui\")\n\n\t\tif nameTag then\n\t\t\tlocal nameTagColor = Color3.new(1,1,1)\n\t\t\tlocal associatePlayer = game.Players:GetPlayerFromCharacter(entityManifest.Parent)\n\t\t\tif not associatePlayer then return end\n\n\t\t\tlocal isPartyMember = isPlayerInParty(associatePlayer)\n\t\t\tif isPartyMember then\n\t\t\t\tdisplayRange = 150\n\t\t\tend\n\n\t\t\tif not entityManifest:FindFirstChild(\"isStealthed\") and distanceAway < displayRange and associatePlayer ~= client then\n\t\t\t\tnameTag.Enabled = true\n\n\t\t\t\tlocal player = associatePlayer\n\n\n\t\t\t\tlocal healthTag = entityContainer:FindFirstChild(\"monsterHealth\")\n\n\t\t\t\tif healthTag then\n\n\n\t\t\t\t\tif entityManifest.health.Value / entityManifest.maxHealth.Value < 1 and (not associatePlayer or (associatePlayer:FindFirstChild(\"isInPVP\") and associatePlayer.isInPVP.Value)) then\n\t\t\t\t\t\thealthTag.Enabled = true\n\t\t\t\t\t\thealthTag.container.backgroundFill.currentHealthFill.ImageColor3 = Color3.fromRGB(77, 225, 69)\n\t\t\t\t\telseif isPartyMember then\n\t\t\t\t\t\thealthTag.Enabled = true\n\t\t\t\t\t\thealthTag.container.backgroundFill.currentHealthFill.ImageColor3 = Color3.fromRGB(226, 34, 40)\n\t\t\t\t\telse\n\t\t\t\t\t\thealthTag.Enabled = false\n\t\t\t\t\tend\n\n\t\t\t\tend\n\n\t\t\t\tlocal fullDisplayCutoff = 20\n\n\n\n\t\t\t\tif isPartyMember then\n\t\t\t\t\tfullDisplayCutoff \t\t= 50\n\t\t\t\t\tdisplayRange \t\t\t= 150\n\t\t\t\t\tnameTagColor \t\t\t= Color3.fromRGB(100, 255, 255)\n\t\t\t\t\tnameTag.top.party.Visible \t= true\n\t\t\t\telseif player:FindFirstChild(\"developer\") then\n\t\t\t\t\tnameTagColor \t\t\t= Color3.fromRGB(255, 255, 128)\n\t\t\t\telse\n\t\t\t\t\tnameTag.top.party.Visible = false\n\t\t\t\tend\n\n\t\t\t\tlocal position = Vector3.new(entityManifest.Position.X, entityManifest.Position.Y - (1.9 + entityManifest.Size.Y / 2), entityManifest.Position.Z)\n\n\t\t\t\tnameTagPart.CFrame = (workspace.CurrentCamera.CFrame - workspace.CurrentCamera.CFrame.p) + position\n\n\n\n\n\t\t\t\tlocal dif\n\n\t\t\t\tif distanceAway >= fullDisplayCutoff then\n\t\t\t\t\tdif =  (distanceAway - fullDisplayCutoff) / (displayRange - fullDisplayCutoff)\n\t\t\t\telse\n\t\t\t\t\tdif = 0\n\t\t\t\tend\n\n\n\t\t\t\tnameTag.curve.ImageTransparency = dif\n\n\t\t\t\tif nameTag.top:FindFirstChild(\"level\") then\n\t\t\t\t\tnameTag.top.level.TextTransparency = dif\n\n\t\t\t\t\tnameTag.top.level.TextColor3 = nameTagColor\n\t\t\t\tend\n\n\t\t\t\tif nameTag.top:FindFirstChild(\"player\") then\n\t\t\t\t\tnameTag.top.player.TextTransparency = dif\n\n\t\t\t\t\tnameTag.top.player.TextColor3 = nameTagColor\n\t\t\t\tend\n\n\t\t\t\tif nameTag.top:FindFirstChild(\"class\") then\n\t\t\t\t\tnameTag.top.class.ImageTransparency = dif\n\n\t\t\t\t\tnameTag.top.class.ImageColor3 = nameTagColor\n\t\t\t\tend\n\n\t\t\t\tif nameTag.bottom:FindFirstChild(\"guild\") then\n\t\t\t\t\tnameTag.bottom.guild.TextTransparency = dif\n\t\t\t\t\tnameTag.bottom.guild.BackgroundTransparency = dif\n\n\t\t\t\tend\n\n\t\t\t\tif nameTag.top:FindFirstChild(\"party\") then\n\t\t\t\t\tnameTag.top.party.image.ImageTransparency = dif\n\n\t\t\t\t\tnameTag.top.party.image.ImageColor3 = nameTagColor\n\t\t\t\tend\n\n\t\t\t\tif nameTag.top:FindFirstChild(\"input\") then\n\n\t\t\t\t\tfor i,object in pairs(nameTag.top.input:GetChildren()) do\n\t\t\t\t\t\tif object:IsA(\"ImageLabel\") then\n\t\t\t\t\t\t\tobject.ImageColor3 = nameTagColor\n\t\t\t\t\t\t\tobject.ImageTransparency = dif\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\n\t\t\t\tif nameTag.top:FindFirstChild(\"dev\") then\n\t\t\t\t\tnameTag.top.dev.TextTransparency = dif\n\n\t\t\t\t\tnameTag.top.dev.TextColor3 = nameTagColor\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tnameTag.Enabled = false\n\n\n\n\t\t\t\tlocal healthTag = entityContainer:FindFirstChild(\"monsterHealth\")\n\t\t\t\tif healthTag then\n\t\t\t\t\thealthTag.Enabled = false\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\n-- this function automatically hooks players\n-- into the renderCharacter update stream\nlocal function assembleCharacterRenderEntity(entityManifest)\n\tlocal associatePlayer = game.Players:GetPlayerFromCharacter(entityManifest.Parent) do\n\t\tif associatePlayer then\n\t\t\tif not associatePlayer:FindFirstChild(\"dataLoaded\") then\n\t\t\t\treturn\n\t\t\tend\n\t\tend\n\tend\n\n\tlocal appearanceData \t= HttpService:JSONDecode(entityManifest.appearance.Value)\n\tlocal entityContainer \t= int__assembleRenderCharacter(entityManifest)\n\tentityContainer.Parent \t= entityRenderCollectionFolder\n\n\tlocal renderEntityData = {\n\t\tentityContainer = entityContainer;\n\t\tentityManifest \t= entityManifest;\n\t\tconnections \t= {};\n\t}\n\n\tint__connectEntityEvents(entityManifest, renderEntityData)\n\n\tentitiesBeingRendered[entityManifest] = renderEntityData\n\n\t-- update the appearance\n\tint__updateRenderCharacter(entityContainer.entity, appearanceData, entityManifest)\nend\n\nlocal function assembleMonsterRenderEntity(entityManifest)\n\tlocal entityContainer \t= Instance.new(\"Model\")\n\n\tlocal clientPlayerHitbox = entityManifest:Clone()\n\t\tclientPlayerHitbox.BrickColor \t= BrickColor.new(\"Hot pink\")\n\t\tclientPlayerHitbox.CanCollide \t= false\n\t\tclientPlayerHitbox.Anchored \t= true\n\t\tclientPlayerHitbox.Name \t\t= \"hitbox\"\n\n\tlocal clientHitboxToServerHitboxReference = Instance.new(\"ObjectValue\")\n\t\tclientHitboxToServerHitboxReference.Name \t= \"clientHitboxToServerHitboxReference\"\n\t\tclientHitboxToServerHitboxReference.Value \t= entityManifest\n\t\tclientHitboxToServerHitboxReference.Parent  = entityContainer\n\n\t-- clear all unnecessary parts within the hitbox\n\t-- we only want the part itself\n\tclientPlayerHitbox:ClearAllChildren()\n\n\tentityContainer.PrimaryPart = clientPlayerHitbox\n\tclientPlayerHitbox.Parent \t= entityContainer\n\n\tlocal isMonsterPet, monsterBaseStats, monsterEntityModel do\n\t\tif not entityManifest:FindFirstChild(\"pet\") then\n\t\t\tisMonsterPet \t\t= false\n\t\t\tmonsterBaseStats \t= monsterLookup[entityManifest.entityId.Value]\n\t\t\tmonsterEntityModel \t= monsterLookup[entityManifest.entityId.Value].entity:Clone()\n\t\telse\n\t\t\tisMonsterPet \t\t= true\n\t\t\tmonsterBaseStats \t= itemLookup[tonumber(entityManifest.entityId.Value)]\n\t\t\tmonsterEntityModel \t= itemLookup[tonumber(entityManifest.entityId.Value)].entity:Clone()\n\t\tend\n\n\t\tif monsterEntityModel then\n\t\t\tif entityManifest:FindFirstChild(\"colorVariant\") then\n\t\t\t\tfor i, obj in pairs(monsterEntityModel:GetDescendants()) do\n\t\t\t\t\tif obj:IsA(\"BasePart\") and obj:FindFirstChild(\"doNotDye\") == nil then\n\t\t\t\t\t\tif obj:FindFirstChild(\"colorOverride\") then\n\t\t\t\t\t\t\tlocal a = entityManifest.colorVariant.Value\n\t\t\t\t\t\t\tobj.Color = Color3.new(math.clamp(a.r,0,1),math.clamp(a.g,0,1),math.clamp(a.b,0,1))\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tlocal a = obj.Color\n\t\t\t\t\t\t\tlocal b = entityManifest.colorVariant.Value\n\t\t\t\t\t\t\tobj.Color = Color3.new(math.clamp(a.r*b.r,0,1), math.clamp(a.g*b.g,0,1), math.clamp(a.b*b.b,0,1))\n\t\t\t\t\t\tend\n\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif entityManifest:FindFirstChild(\"specialName\") then\n\t\t\t\tfor i, obj in pairs(monsterEntityModel:GetDescendants()) do\n\t\t\t\t\tif obj:IsA(\"BasePart\") and obj.Name == \"variation_\"..entityManifest:FindFirstChild(\"specialName\").Value then\n\t\t\t\t\t\tobj.Transparency = 0\n\t\t\t\t\t\tobj.CanCollide   = true\n\t\t\t\t\telseif obj:IsA(\"BasePart\") and obj.Name == \"variation_default\" then\n\t\t\t\t\t\tobj.Transparency = 1\n\t\t\t\t\t\tobj.CanCollide   = false\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\tend\n\tend\n\n\tif entityManifest:FindFirstChild(\"monsterScale\") then\n\t\tutilities.scale(monsterEntityModel, entityManifest.monsterScale.Value)\n\tend\n\n\tlocal projectionWeld = Instance.new(\"Motor6D\")\n\t\tprojectionWeld.Name \t= \"projectionWeld\"\n\t\tprojectionWeld.Part0 \t= clientPlayerHitbox\n\t\tprojectionWeld.Part1 \t= monsterEntityModel.PrimaryPart\n\t\tprojectionWeld.C0 \t\t= CFrame.new()\n\t\tprojectionWeld.C1 \t\t= CFrame.new(0, monsterEntityModel:GetModelCFrame().Y - monsterEntityModel.PrimaryPart.CFrame.Y, 0)\n\t\tprojectionWeld.Parent \t= clientPlayerHitbox\n\n\t-- set it up to render\n\tmonsterEntityModel.Parent \t= entityContainer\n\tentityContainer.Parent \t\t= entityRenderCollectionFolder\n\n\tlocal renderEntityData = {\n\t\tentityContainer \t= entityContainer;\n\t\tentityManifest \t\t= entityManifest;\n\t\tdisableHealthBarUI \t= not not entityManifest:FindFirstChild(\"isPassive\");\n\t\tdisableLevelUI \t\t= not not entityManifest:FindFirstChild(\"isPassive\") or not not entityManifest:FindFirstChild(\"hideLevel\");\n\t\tconnections \t\t= {};\n\t}\n\n\tif isMonsterPet then\n\t\trenderEntityData.disableHealthBarUI = true\n\tend\n\n\tif entitiesBeingRendered[entityManifest] then\n\t\tdissassembleRenderEntityByManifest(entityManifest)\n\tend\n\n\tint__connectEntityEvents(entityManifest, renderEntityData)\n\tentitiesBeingRendered[entityManifest] = renderEntityData\n\n\treturn entityContainer\nend\n\nlocal function showDamageAtPosition(damagePosition, isSecondary)\n\tlocal part\n\tif typeof(damagePosition) == \"Instance\" then\n\t\tpart = damagePosition\n\telse\n\t\tpart = Instance.new(\"Part\")\n\t\tpart.CFrame = CFrame.new(damagePosition)\n\t\tpart.Size = Vector3.new(0.2,0.2,0.2)\n\t\tpart.Transparency = 1\n\t\tpart.Anchored = true\n\t\tpart.CanCollide = false\n\t\tpart.Name = \"DamagePositionPart\"\n\t\tpart.Parent = workspace.CurrentCamera\n\t\tgame.Debris:AddItem(part,3)\n\tend\n\n\tlocal hitsound = Instance.new(\"Sound\")\n\thitsound.SoundId = \"rbxassetid://2065833626\"\n\thitsound.MaxDistance = isSecondary and 200 or 1000\n\thitsound.Volume = isSecondary and 0.25 or 1.5\n\thitsound.EmitterSize = isSecondary and 1 or 5\n\thitsound.Parent = part\n\thitsound:Play()\n\tgame.Debris:AddItem(hitsound, 5)\n\n\tif not isSecondary then\n\t\tlocal hit = part:FindFirstChild(\"hitParticle\") or assetFolder.hitParticle:Clone()\n\t\thit.Parent = part\n\t\thit:Emit(3)\n\tend\nend\n\nlocal function isManifestValid(manifest)\n\treturn\n\t\tmanifest.Parent\n\t\tand (manifest.Parent == workspace or manifest.Parent:IsDescendantOf(workspace))\nend\n\nlocal function updateEntitiesBeingRendered(entitiesToRender)\n\tfor i, manifest in pairs(entitiesToRender) do\n\t\tlocal renderData = entitiesBeingRendered[manifest]\n\t\tif renderData and isManifestValid(manifest) and renderData.entityContainer and renderData.entityContainer.PrimaryPart then\n\t\t\trenderData.entityContainer.PrimaryPart.CFrame = manifest.CFrame\n\n\t\t\t-- update display stuff\n\t\t\tif manifest.entityType.Value == \"character\" then\n\t\t\t\tint__updateCharacterNameTag(renderData)\n\n\t\t\telseif manifest.entityType.Value == \"monster\" or manifest.entityType.Value == \"pet\" then\n\t\t\t\tint__updateMonsterNameTag(renderData)\n\t\t\tend\n\t\telse\n\t\t\tdissassembleRenderEntityByManifest(manifest)\n\t\tend\n\tend\nend\n\nlocal function getManifestFromEntityContainer(entityContainer)\n\tfor manifest, entityData in pairs(entitiesBeingRendered) do\n\t\tif entityData.entityContainer == entityContainer then\n\t\t\treturn manifest\n\t\tend\n\tend\n\n\treturn nil\nend\n\nlocal DISTANCE_TO_RENDER_IN_ENTITY = 300\n\nlocal function int__updateNearbyEntities()\n\tanimationInterface = require(script.Parent:WaitForChild(\"animationInterface\"))\n\n\twhile true do\n\t\tif configuration.getConfigurationValue(\"doFixShadowCloneJutsu\", client) then\n\t\t\tfor i, entityContainer in pairs(entityRenderCollectionFolder:GetChildren()) do\n\t\t\t\tlocal manifest = getManifestFromEntityContainer(entityContainer)\n\n\t\t\t\tif not manifest or not manifest:IsDescendantOf(workspace) then\n\t\t\t\t\tif manifest then\n\t\t\t\t\t\tentitiesBeingRendered[manifest] = nil\n\t\t\t\t\tend\n\n\t\t\t\t\tentityContainer:Destroy()\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tfor i, player in pairs(game.Players:GetPlayers()) do\n\t\t\tif player.Character and player.Character.Parent ~= entityManifestCollectionFolder then\n\t\t\t\tplayer.Character.Parent = entityManifestCollectionFolder\n\t\t\tend\n\t\tend\n\n\t\tif client.Character and client.Character.PrimaryPart then\n\n\t\t\tlocal clientPosition = workspace.CurrentCamera.CFrame.Position\n\t\t\tlocal entities = utilities.getEntities()\n\n\t\t\tfor _, entityManifest in pairs(entities) do\n\t\t\t\tlocal distanceAway = (entityManifest.Position - clientPosition).magnitude\n\t\t\t\tlocal alwaysRendered = (entityManifest:FindFirstChild(\"alwaysRendered\") ~= nil)\n\n\t\t\t\tif alwaysRendered then\n\t\t\t\t\tif not entitiesBeingRendered[entityManifest] then\n\t\t\t\t\t\tassembleMonsterRenderEntity(entityManifest)\n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tif distanceAway <= DISTANCE_TO_RENDER_IN_ENTITY and not entitiesBeingRendered[entityManifest] then\n\t\t\t\t\t\tif entityManifest.entityType.Value == \"character\" then\n\t\t\t\t\t\t\tassembleCharacterRenderEntity(entityManifest)\n\t\t\t\t\t\telseif entityManifest.entityType.Value == \"monster\" or entityManifest.entityType.Value == \"pet\" then\n\t\t\t\t\t\t\tassembleMonsterRenderEntity(entityManifest)\n\t\t\t\t\t\tend\n\t\t\t\t\telseif distanceAway > DISTANCE_TO_RENDER_IN_ENTITY * 1.05 and entitiesBeingRendered[entityManifest] and not entitiesBeingRendered[entityManifest].isPinned then\n\t\t\t\t\t\tif entityManifest.entityType.Value == \"character\" then\n\t\t\t\t\t\t\tdissassembleRenderEntityByManifest(entityManifest)\n\t\t\t\t\t\telseif entityManifest.entityType.Value == \"monster\" or entityManifest.entityType.Value == \"pet\" then\n\t\t\t\t\t\t\tdissassembleRenderEntityByManifest(entityManifest)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\twait(1)\n\tend\nend\n\nlocal function getAlertColor(scale)\n\tlocal alertColor = Color3.fromRGB(255, 89, 92);\n\tif scale > 4.5 then\n\t\talertColor = Color3.fromRGB(255, 64, 0);\n\telseif scale >= 2.5 then\n\t\talertColor = Color3.fromRGB(214, 19, 146);\n\tend\n\treturn alertColor\nend\n\nlocal function updateMapColor()\n\tlocal colorCorrection = game.Lighting:FindFirstChild(\"giantMonsterColor\")\n\tif colorCorrection == nil then\n\t\tcolorCorrection = assetFolder.giantMonsterColor:Clone()\n\t\tcolorCorrection.Parent = game.Lighting\n\tend\n\tlocal largestScale = 0\n\tfor i, manifest in pairs(game.CollectionService:GetTagged(\"giantEnemy\")) do\n\t\tif manifest:FindFirstChild(\"health\") and manifest.health.Value > 0 then\n\t\t\tif manifest:FindFirstChild(\"monsterScale\") and manifest.monsterScale.Value > largestScale then\n\t\t\t\tlargestScale = manifest.monsterScale.Value\n\t\t\tend\n\t\tend\n\tend\n\tif largestScale >= 1.3 and game.PlaceId ~= 3303140173 then\n\t\tlocal color = getAlertColor(largestScale)\n\t\tlocal mapColor = Color3.new(math.clamp(color.r * 1.2,0,1), math.clamp(color.g * 1.2,0,1), math.clamp(color.b * 1.2,0,1))\n\t\ttween(colorCorrection,\n\t\t\t{\"Brightness\", \"Contrast\", \"Saturation\", \"TintColor\"},\n\t\t\t{0, 0.1, 0, mapColor},\n\t\t\t0.5\n\t\t)\n\telse\n\t\t-- giant enemy gone? Return to normal state\n\t\ttween(colorCorrection,\n\t\t\t{\"Brightness\", \"Contrast\", \"Saturation\", \"TintColor\"},\n\t\t\t{0, 0, 0, Color3.new(1,1,1)},\n\t\t\t1\n\t\t)\n\tend\n\nend\n\nlocal giantEnemySpawning\nlocal lastGiantEnemyAdded\n\n\nlocal function giantEnemyAdded(entityManifest)\n\tlocal scaleTag = entityManifest:WaitForChild(\"monsterScale\", 60)\n\n\tif not scaleTag then\n\t\treturn false\n\tend\n\n\tlocal scale = scaleTag.Value\n\tlocal alertColor = getAlertColor(scale)\n\n\tlocal alertText\n\tif scale > 4.5 then\n\t\talertText = \"SWEET MOTHER OF MUSHROOM! A COLOSSAL \" .. entityManifest.Name .. \" has been spotted!\";\n\telseif scale > 2.5 then\n\t\talertText = \"GET OUT OF HERE! A super giant \" .. entityManifest.Name .. \" has been spotted!\";\n\telse\n\t\talertText = \"Run for your life! A giant \" .. entityManifest.Name .. \" has been spotted!\";\n\tend\n\tnetwork:fire(\"alert\", {\n\t\ttext \t\t\t\t\t= alertText;\n\t\ttextColor3 \t\t\t\t= Color3.new(0,0,0);\n\t\tbackgroundColor3 \t\t= alertColor;\n\t\tbackgroundTransparency \t= 0;\n\t\ttextStrokeTransparency \t= 1;\n\t\tfont \t\t\t\t\t= Enum.Font.SourceSansBold;\n\t}, 6, \"giantEnemySpawned\")\n\tgame.StarterGui:SetCore(\"ChatMakeSystemMessage\", {\n\t\tText \t= alertText;\n\t\tColor \t= alertColor;\n\t\tFont \t= Enum.Font.SourceSansBold;\n\t})\n\n\tlocal smoke = assetFolder.giantEnemySmoke:Clone()\n\tsmoke.Color = ColorSequence.new(alertColor, Color3.new(0,0,0))\n\tsmoke.Rate = 80 * scale\n\tsmoke.Parent = entityManifest\n\n\tlocal beam \t\t= assetFolder.beam:Clone()\n\tbeam.CFrame \t= beam.CFrame - beam.Position + entityManifest.Position\n\tbeam.Anchored \t= true\n\tbeam.CanCollide = false\n\tbeam.Parent \t= workspace.CurrentCamera\n\tbeam.Color \t\t= alertColor\n\n\tutilities.playSound(\"giantEnemyBoom\", beam)\n\n\ttween(beam, {\"Transparency\"}, 1, 2)\n\ttween(beam.Mesh, {\"Scale\"}, Vector3.new(10000, 20, 20), 2)\n\n\tgame.Debris:AddItem(beam, 3)\n\n\tif (workspace.CurrentCamera.CFrame.Position - entityManifest.Position).magnitude < 200 then\n\t\tnetwork:invoke(\"cameraShake\")\n\tend\n\n\tlocal colorCorrection = game.Lighting:FindFirstChild(\"giantMonsterColor\")\n\tif colorCorrection == nil then\n\t\tcolorCorrection = assetFolder.giantMonsterColor:Clone()\n\t\tcolorCorrection.Parent = game.Lighting\n\tend\n\tlastGiantEnemyAdded = entityManifest\n\tgiantEnemySpawning = true\n\tlocal max = math.max(alertColor.r, alertColor.g, alertColor.b)^2\n\tlocal intenseColor = Color3.new((alertColor.r ^ 3)/max,(alertColor.g ^ 3)/max ,(alertColor.b ^ 3)/max)\n\ttween(colorCorrection,\n\t\t{\"Brightness\", \"Contrast\", \"Saturation\", \"TintColor\"},\n\t\t{0.2, 0.8, -1, intenseColor},\n\t\t0.3\n\t)\n\tdelay(0.5, function()\n\t\tif lastGiantEnemyAdded == entityManifest then\n\t\t\tgiantEnemySpawning = false\n\t\t\tupdateMapColor()\n\t\tend\n\tend)\n\nend\n\ngame.CollectionService:GetInstanceRemovedSignal(\"giantEnemy\"):connect(function(entityManifest)\n\tif not giantEnemySpawning then\n\t\tupdateMapColor()\n\tend\nend)\n\ngame.CollectionService:GetInstanceAddedSignal(\"giantEnemy\"):connect(giantEnemyAdded)\n\nspawn(function()\n\tfor i, enemy in pairs(game.CollectionService:GetTagged(\"giantEnemy\")) do\n\t\tgiantEnemyAdded(enemy)\n\tend\nend)\n\nlocal function signal_damage(entityManifest, damageInfo)\n\n\tlocal renderEntityData = entitiesBeingRendered[entityManifest]\n\tif renderEntityData then\n\t\tlocal associatePlayer\n\t\tif entityManifest.Parent and entityManifest.Parent:IsA(\"Model\") then\n\t\t\tassociatePlayer = game.Players:GetPlayerFromCharacter(entityManifest.Parent)\n\t\tend\n\n\t\tlocal isSecondary = false do\n\t\t\tif associatePlayer ~= client and (damageInfo.sourcePlayerId == nil or damageInfo.sourcePlayerId ~= client.UserId) then\n\t\t\t\tisSecondary = true\n\t\t\t\tif damageInfo.damage > 0 then\n\t\t\t\t\tnetwork:fire(\"monsterDamagedAtPosition\", entityManifest, true)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\t\tif damageInfo.sourcePlayerId == client.UserId and entityManifest:FindFirstChild(\"damagedByPlayer\") == nil then\n\t\t\tlocal damagedTag = Instance.new(\"BoolValue\")\n\t\t\tdamagedTag.Name = \"damagedByPlayer\"\n\t\t\tdamagedTag.Parent = entityManifest\n\t\tend\n\n\t\tif associatePlayer == client and damageInfo.damage > 0 then\n\t\t\tnetwork:fire(\"monsterDamagedAtPosition\", entityManifest)\n\t\tend\n\n\t\tlocal container =  renderEntityData.entityContainer\n\t\tlocal damageIndicator = container:FindFirstChild(\"damageIndicator\")\n\n\t\tif damageIndicator == nil then\n\t\t\tdamageIndicator = ReplicatedStorageAssetFolder.entities.damageIndicator:Clone()\n\t\t\tlocal thickness = math.max((container.PrimaryPart.Size.X + container.PrimaryPart.Size.Z) / 2, 3)\n\n\t\t\tdamageIndicator.Size = UDim2.new(thickness, 50, 6, 75)\n\t\t\tdamageIndicator.Parent = container\n\t\tend\n\t\tif not damageIndicator.Adornee then\n\t\t\tdamageIndicator.Adornee = container.PrimaryPart\n\t\t\tdamageIndicator.Enabled = true\n\t\tend\n\n\t\tlocal template = damageIndicator.template:Clone()\n\n\t\tlocal offset = 0.5 - (math.random() - 0.5) * 0.5\n\t\ttemplate.Text = tostring(math.floor(math.abs(damageInfo.damage) or 0))\n\t\ttemplate.TextTransparency = 1\n\t\ttemplate.TextStrokeTransparency = 1\n\t\ttemplate.Position = UDim2.new(offset,0,0.85,0)\n\t\ttemplate.Parent = damageIndicator\n\t\tgame.Debris:AddItem(template, 3)\n\n\n\t\ttemplate.Size = UDim2.new(0.7,0,0.1,0)\n\t\ttemplate.Visible = true\n\n\n\t\tif damageInfo.damage < 0 then\n\t\t\tisSecondary = false\n\t\tend\n\n\t\ttemplate.ZIndex = (isSecondary and 1) or 2\n\n\t\tlocal goalPosition = UDim2.new(offset, 0, 0, 0.3)\n\t\tlocal goalTransparency = isSecondary and 0.7 or 0\n\t\tlocal goalSize = UDim2.new(0.7, 0, 0.3, 0)\n\t\tlocal finalSize = UDim2.new(0.7,0,0.1,0)\n\n\t\tif isSecondary then\n\t\t\tlocal startTime = tick()\n\t\t\tlocal tweenConnection = RunService.Heartbeat:connect(function(step)\n\t\t\t\tlocal t = (tick()-startTime)/1.5\n\t\t\t\ttemplate.Position = UDim2.new(offset,0, 0.85 - 0.55*t ,0)\n\t\t\t\tif t > 0.5 then\n\t\t\t\t\tt = (t - 0.5) * 2\n\t\t\t\t\ttemplate.Size = UDim2.new(0.7, 0, 0.3 - 0.2*t, 0)\n\t\t\t\t\ttemplate.TextTransparency = 0.5 + t/2\n\t\t\t\t\ttemplate.TextStrokeTransparency = 0.5 + t/t\n\t\t\t\telse\n\t\t\t\t\tt = t * 2\n\t\t\t\t\ttemplate.Size = UDim2.new(0.7, 0, 0.1 + 0.2*t, 0)\n\t\t\t\t\ttemplate.TextTransparency = 1 - t/2\n\t\t\t\t\ttemplate.TextStrokeTransparency = 1 - t/2\n\t\t\t\tend\n\t\t\tend)\n\t\t\tdelay(1.5, function()\n\t\t\t\ttweenConnection:disconnect()\n\t\t\t\ttweenConnection = nil\n\t\t\tend)\n\t\telse\n\t\t\ttween(template, {\"Position\"}, goalPosition, 1.5)\n\t\t\ttween(template, {\"TextTransparency\", \"TextStrokeTransparency\", \"Size\"}, {goalTransparency, goalTransparency, goalSize}, 0.75)\n\t\t\tdelay(0.75, function()\n\t\t\t\ttween(template,{\"TextTransparency\",\"TextStrokeTransparency\",\"Size\"},{1,1,finalSize},0.75)\n\t\t\tend)\n\t\tend\n\n\t\ttemplate.TextColor3 = Color3.fromRGB(255, 251, 117)\n\t\ttemplate.Font \t\t= Enum.Font.SourceSans\n\n\t\t-- healing\n\t\tif damageInfo.damage < 0 then\n\t\t\ttemplate.TextColor3 \t= Color3.fromRGB(0, 255, 213)\n\t\t\ttemplate.Font = Enum.Font.SourceSansBold\n\t\telse\n\t\t\tif associatePlayer == client then\n\t\t\t\ttemplate.TextColor3 = Color3.fromRGB(204, 0, 255)\n\n\t\t\t\tif damageInfo.supressed then\n\t\t\t\t\ttemplate.TextColor3 = Color3.fromRGB(176, 137, 200)\n\t\t\t\tend\n\t\t\telseif associatePlayer and isSecondary then\n\t\t\t\ttemplate.TextColor3 \t\t= Color3.fromRGB(204, 0, 255)\n\t\t\t\ttemplate.TextTransparency \t= 0.5\n\n\t\t\t\tif damageInfo.supressed then\n\t\t\t\t\ttemplate.TextColor3 = Color3.fromRGB(176, 137, 200)\n\t\t\t\tend\n\t\t\telse\n\t\t\t\tif damageInfo.isCritical then\n\t\t\t\t\ttemplate.TextColor3 = Color3.fromRGB(255, 175, 83)\n\t\t\t\tend\n\n\t\t\t\tif damageInfo.supressed then\n\t\t\t\t\ttemplate.TextColor3 = Color3.fromRGB(150, 150, 150)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif damageInfo.isCritical then\n\t\t\t\ttemplate.Font = Enum.Font.SourceSansBold\n\t\t\tend\n\t\tend\n\n\tend\nend\n\n\nlocal function onEntityManifestCollectionFolderChildAdded(entityManifest)\n\t--assuming Damiens up to some mischief in here for monster rework\n\n\t--[[\n\tif entityManifest:FindFirstChild(\"monsterScale\") then\n\t\tgiantEnemyAdded(entityManifest)\n\telse\n\t\tutilities.connectEventHelper(entityManifest.ChildAdded, function(child)\n\t\t\tif child.Name == \"monsterScale\" and child.Value > 1.3 then\n\t\t\t\tgiantEnemyAdded(entityManifest)\n\t\t\t\treturn true\n\t\t\tend\n\t\tend)\n\tend\n\t]]\nend\n\nlocal function int__replicateAnimationFromPlayer(player, animationSequenceName, animationName, extraData)\n\tlocal entityManifest = player.Character and player.Character.PrimaryPart\n\n\tif entitiesBeingRendered[entityManifest] then\n\t\tentitiesBeingRendered[entityManifest]:playAnimation(animationSequenceName, animationName, extraData)\n\tend\nend\n\nlocal function onPlayerAppliedScroll(serverPlayer, scrollItemId, successfullyApplied)\n\tif not serverPlayer or not serverPlayer.Character or not serverPlayer.Character.PrimaryPart then return end\n\n\tlocal realItem = itemLookup[scrollItemId]\n\tlocal clientCharacterContainer = entitiesBeingRendered[serverPlayer.Character.PrimaryPart] and entitiesBeingRendered[serverPlayer.Character.PrimaryPart].entityContainer\n\tif clientCharacterContainer then\n\t\tif realItem and realItem.module then\n\t\t\tlocal manifest = realItem.module:FindFirstChild(\"manifest\")\n\t\t\tif manifest then\n\t\t\t\tif manifest:IsA(\"MeshPart\") then\n\t\t\t\t\tlocal representation = manifest:Clone()\n\n\t\t\t\t\trepresentation.Transparency = 1\n\t\t\t\t\trepresentation.CanCollide = false\n\t\t\t\t\trepresentation.Anchored = false\n\t\t\t\t\trepresentation.Name = \"scrollUseRepresentation\"\n\n\t\t\t\t\tlocal originalSize = representation.Size\n\n\t\t\t\t\trepresentation.Parent = workspace.CurrentCamera\n\t\t\t\t\trepresentation.Size = originalSize / 10\n\t\t\t\t\ttween(representation,{\"Size\",\"Transparency\"},{originalSize, 0},0.3)\n\n\t\t\t\t\tlocal positionOffset = Instance.new(\"Vector3Value\")\n\t\t\t\t\tpositionOffset.Value = Vector3.new(0,3,0)\n\n\t\t\t\t\tlocal function render()\n\t\t\t\t\t\tif representation then\n\t\t\t\t\t\t\tif clientCharacterContainer and clientCharacterContainer.PrimaryPart then\n\t\t\t\t\t\t\t\trepresentation.CFrame = CFrame.new(clientCharacterContainer.PrimaryPart.Position + positionOffset.Value)\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\trepresentation:Destroy()\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tlocal connection\n\t\t\t\t\tif serverPlayer == game.Players.LocalPlayer then\n\t\t\t\t\t\tconnection = RunService.RenderStepped:connect(render)\n\t\t\t\t\telse\n\t\t\t\t\t\tconnection = RunService.Heartbeat:connect(render)\n\t\t\t\t\tend\n\n\t\t\t\t\ttween(positionOffset,{\"Value\"},Vector3.new(0,5,0),0.3)\n\n\t\t\t\t\tif representation then\n\t\t\t\t\t\tif successfullyApplied then\n\t\t\t\t\t\t\tutilities.playSound(\"scrollSuccess\", representation)\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tutilities.playSound(\"scrollFail\", representation)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\n\t\t\t\t\twait(0.5)\n\n\t\t\t\t\tif successfullyApplied then\n\n\t\t\t\t\t\tlocal sparkles = assetFolder.scrollSuccess.Sparkles:Clone()\n\t\t\t\t\t\tsparkles.Enabled = false\n\t\t\t\t\t\tsparkles.Parent = representation\n\t\t\t\t\t\tsparkles:Emit(30)\n\n\t\t\t\t\t\tlocal rayHolder = assetFolder.scrollSuccess.Attachment:Clone()\n\t\t\t\t\t\trayHolder.Parent = representation\n\n\t\t\t\t\t\twait(0.1)\n\n\t\t\t\t\t\ttween(positionOffset,{\"Value\"},Vector3.new(0,5,0),0.7,nil,Enum.EasingDirection.In)\n\t\t\t\t\t\ttween(representation,{\"Transparency\"},1,0.7)\n\n\t\t\t\t\t\twait(3)\n\t\t\t\t\telse\n\n\n\t\t\t\t\t\twait(0.1)\n\n\t\t\t\t\t\tlocal explode = Instance.new(\"Explosion\")\n\t\t\t\t\t\texplode.DestroyJointRadiusPercent = 0\n\t\t\t\t\t\texplode.Parent = workspace\n\t\t\t\t\t\texplode.Position = representation.Position\n\t\t\t\t\t\tconnection:disconnect()\n\t\t\t\t\t\trepresentation.Anchored = false\n\t\t\t\t\t\trepresentation.CanCollide = true\n\t\t\t\t\t\trepresentation.Velocity = Vector3.new(math.random(-100,100),math.random(-100,100),math.random(-100,100))\n\t\t\t\t\t\twait(3)\n\t\t\t\t\tend\n\t\t\t\t\tpcall(function()\n\t\t\t\t\t\tconnection:disconnect()\n\t\t\t\t\t\tconnection = nil\n\t\t\t\t\tend)\n\t\t\t\t\tif representation then\n\t\t\t\t\t\trepresentation:Destroy()\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function getMyClientCharacterContainer()\n\twhile not client.Character or not client.Character.PrimaryPart or not entitiesBeingRendered[client.Character.PrimaryPart] do\n\t\twait(0.1)\n\tend\n\n\treturn entitiesBeingRendered[client.Character.PrimaryPart].entityContainer\nend\n\nlocal function createRenderCharacterContainerFromCharacterAppearanceData(manifestContainer, appearanceData)\n\tlocal renderCharacterContainer = int__assembleRenderCharacter(manifestContainer.PrimaryPart)\n\tint__updateRenderCharacter(renderCharacterContainer.entity, appearanceData)\n\n\treturn renderCharacterContainer\nend\n\nlocal function applyCharacterAppearanceToRenderCharacter(entity, appearanceData)\n\tint__updateRenderCharacter(entity, appearanceData)\nend\n\nlocal function assembleEntityByManifest(entityManifest)\n\tif entityManifest.entityType.Value == \"character\" then\n\t\treturn assembleCharacterRenderEntity(entityManifest)\n\telseif entityManifest.entityType.Value == \"monster\" or entityManifest.entityType.Value == \"pet\" then\n\t\treturn assembleMonsterRenderEntity(entityManifest)\n\tend\nend\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\ttween = Modules.tween\n\tutilities = Modules.utilities\n\tphysics = Modules.physics\n\tplaceSetup = Modules.placeSetup\n\tprojectile = Modules.projectile\n\tconfiguration = Modules.configuration\n\tevents = Modules.events\n\n\tevents:registerForEvent(\"playersXpGained\", playersXpGained)\n\n\tentityManifestCollectionFolder = placeSetup.awaitPlaceFolder(\"entityManifestCollection\")\n\tentityRenderCollectionFolder = placeSetup.awaitPlaceFolder(\"entityRenderCollection\")\n\n\tnetwork:connect(\"signal_damage\", \"OnClientEvent\", signal_damage)\n\tnetwork:create(\"getMyClientCharacterContainer\", \"BindableFunction\", \"OnInvoke\", getMyClientCharacterContainer)\n\n\t--run manager connections\n\titem_manager.createNetworkConnections(client,entitiesBeingRendered)\n\n\n\tnetwork:connect(\"playerAppliedScroll\", \"OnClientEvent\", onPlayerAppliedScroll)\n\tnetwork:create(\"myClientCharacterContainerChanged\", \"BindableEvent\")\n\n\t-- todo: convert all manifestContainer calls to just manifest!\n\tnetwork:create(\"createRenderCharacterContainerFromCharacterAppearanceData\", \"BindableFunction\", \"OnInvoke\", createRenderCharacterContainerFromCharacterAppearanceData)\n\n\tnetwork:create(\"createRenderMonsterContainer\", \"BindableFunction\", \"OnInvoke\", function(entityManifest)\n\t\tlocal renderMonsterContainer = assembleMonsterRenderEntity(entityManifest)\n\n\t\treturn renderMonsterContainer\n\tend)\n\n\t-- todo: convert all manifestContainer calls to just manifest!\n\tnetwork:create(\"applyCharacterAppearanceToRenderCharacter\", \"BindableFunction\", \"OnInvoke\", applyCharacterAppearanceToRenderCharacter)\n\n\tnetwork:create(\"myClientCharacterDied\", \"BindableEvent\")\n\n\tnetwork:create(\"myClientCharacterWeaponChanged\", \"BindableEvent\")\n\n\tnetwork:create(\"assembleEntityByManifest\", \"BindableFunction\", \"OnInvoke\", assembleEntityByManifest)\n\n\tnetwork:create(\"setStopRenderingPlayers\", \"BindableFunction\", \"OnInvoke\", function() end)\n\tnetwork:create(\"monsterDamagedAtPosition\", \"BindableEvent\", \"Event\", showDamageAtPosition)\n\n\tnetwork:create(\"getRenderCharacterContainerByEntityManifest\", \"BindableFunction\", \"OnInvoke\", function(entityManifest)\n\t\tif entityManifest and entitiesBeingRendered[entityManifest] then\n\t\t\treturn entitiesBeingRendered[entityManifest].entityContainer\n\t\tend\n\tend)\n\n\tnetwork:create(\"getPlayerRenderDataByNameTag\", \"BindableFunction\", \"OnInvoke\", function(player, nameTag)\n\t\tlocal entityManifest = player.Character and player.Character.PrimaryPart\n\n\t\tif entityManifest and entitiesBeingRendered[entityManifest] then\n\t\t\treturn entitiesBeingRendered[entityManifest][nameTag]\n\t\tend\n\tend)\n\n\tnetwork:create(\"getEntityManifestByRenderEntityContainer\", \"BindableFunction\", \"OnInvoke\", function(renderEntityContainer)\n\t\tfor entityManifest, v in pairs(entitiesBeingRendered) do\n\t\t\tif renderEntityContainer == v.entityContainer then\n\t\t\t\treturn entityManifest\n\t\t\tend\n\t\tend\n\n\t\treturn nil\n\tend)\n\n\tnetwork:create(\"setRenderDataByNameTag\", \"BindableFunction\", \"OnInvoke\", function(entityManifest, nameTag, value)\n\t\tif entityManifest and entitiesBeingRendered[entityManifest] then\n\t\t\tentitiesBeingRendered[entityManifest][nameTag] = value\n\t\tend\n\tend)\n\n\tnetwork:create(\"getRenderDataByNameTag\", \"BindableFunction\", \"OnInvoke\", function(entityManifest, nameTag)\n\t\tif entityManifest and entitiesBeingRendered[entityManifest] then\n\t\t\treturn entitiesBeingRendered[entityManifest][nameTag]\n\t\tend\n\n\t\treturn nil\n\tend)\n\n\tnetwork:connect(\"replicatePlayerAnimationSequence\", \"OnClientEvent\", function(player, ...)\n\t\tint__replicateAnimationFromPlayer(player, ...)\n\tend)\n\n\tnetwork:create(\"playPlayerAnimationSequenceOnClientCharacter\", \"BindableEvent\", \"Event\", function(...)\n\t\tint__replicateAnimationFromPlayer(client, ...)\n\tend)\n\n\tnetwork:create(\"getPlayerRenderStateByPlayerName\",\"BindableFunction\",\"OnInvoke\",function(playerName)\n\t\treturn \"not added\"\n\tend)\n\n\tnetwork:create(\"getPlayerRenderFromPlayerInstance\", \"BindableFunction\", \"OnInvoke\", function(playerInstance)\n\t\treturn error(\"NOT YET IMPLEMENTED\")\n\tend)\n\n\tnetwork:create(\"getPlayerRenderFromManifest\", \"BindableFunction\", \"OnInvoke\", function(playerCharacterManifest)\n\t\tlocal data = entitiesBeingRendered[playerCharacterManifest]\n\t\tif data then\n\t\t\treturn data.entityContainer\n\t\tend\n\tend)\n\n\t-- todo: replication\n\tnetwork:create(\"replicateClientCharacterWeaponStateChanged\", \"BindableEvent\", \"Event\", function(weaponType, weaponState)\n\t\tlocal entityManifest = client.Character and client.Character.PrimaryPart\n\n\t\tif entityManifest and entitiesBeingRendered[entityManifest] then\n\t\t\treturn entitiesBeingRendered[entityManifest]:setWeaponState(weaponType, weaponState)\n\t\tend\n\tend)\n\n\tnetwork:connect(\"signal_myPartyDataChanged\", \"OnClientEvent\", updatePartyInfo)\n\n\tnetwork:create(\"getMovementAnimationForCharacter\", \"BindableFunction\", \"OnInvoke\", function(animationController, state, weaponTypeEquipped, weaponState)\n\t\tlocal animation = state\n\n\t\tif not animationInterface then\n\t\t\tanimationInterface = require(script.Parent:WaitForChild(\"animationInterface\"))\n\t\tend\n\n\t\tif weaponTypeEquipped and animationInterface.rawAnimationData.movementAnimations[animation .. \"_\" .. weaponTypeEquipped] then\n\t\t\tanimation = animation .. \"_\" .. weaponTypeEquipped\n\n\t\t\tif weaponState and animationInterface.rawAnimationData.movementAnimations[animation .. \"_\" .. weaponState] then\n\t\t\t\tanimation = animation .. \"_\" .. weaponState\n\t\t\tend\n\t\tend\n\n\t\treturn animationInterface.getSingleAnimation(animationController, \"movementAnimations\", animation)\n\tend)\n\n\tlocal priorityCount = 30\n\tlocal priorityDistance = 50\n\n\tlocal deferredEntities = {}\n\tlocal priorityEntities = {}\n\n\tRunService:BindToRenderStep(\"updateEntityRendering\", 50, function()\n\t\tdeferredEntities = {}\n\t\tpriorityEntities = {}\n\t\tlocal n = 0\n\t\tlocal player = game.Players.LocalPlayer\n\t\tlocal playerPosition = player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.Position\n\t\tfor manifest, renderData in pairs(entitiesBeingRendered) do\n\t\t\tif n <= priorityCount and (manifest.Position - playerPosition).magnitude <= priorityDistance then\n\t\t\t\ttable.insert(priorityEntities, manifest)\n\t\t\telse\n\t\t\t\ttable.insert(deferredEntities, manifest)\n\t\t\tend\n\t\tend\n\t\tupdateEntitiesBeingRendered(priorityEntities)\n\tend)\n\n\tRunService.Heartbeat:connect(function()\n\t\tupdateEntitiesBeingRendered(deferredEntities)\n\tend)\n\n\n\n\n--\t-- giant message stuff\n\tentityManifestCollectionFolder.ChildAdded:connect(onEntityManifestCollectionFolderChildAdded)\n\n\t-- initialize parties\n\tupdatePartyInfo()\n\n\t-- initialize updates\n\tspawn(int__updateNearbyEntities)\nend\n\n\nreturn module"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/firefly.client.lua",
    "content": "-- Pretty fireflies\n-- Author: Polymorphic\n\nlocal runService \t= game:GetService(\"RunService\")\nlocal fireflies \t= {}\nlocal player \t\t= game.Players.LocalPlayer\n\nlocal fireflyVelocity \t= 5\nlocal fireflyCount \t\t= 30\n\nlocal fireflyOrigin\nlocal fireflyOriginOffset = Vector3.new(200, 10, 200)\n\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal placeSetup = modules.load(\"placeSetup\")\n\tlocal utilities = modules.load(\"utilities\")\n\tlocal tween = modules.load(\"tween\")\n\n--local firefliesFolder = placeSetup.getPlaceFolder(\"fireflies\")\nlocal firefliesFolder = Instance.new(\"Folder\")\nfirefliesFolder.Name = \"fireflies\"\nfirefliesFolder.Parent = workspace.CurrentCamera\n\nlocal function assignTargetCFrame()\n\treturn\n\t\tCFrame.new(workspace.CurrentCamera.CFrame.p)\n\t\t* CFrame.new(\n\t\t\tmath.random(-fireflyOriginOffset.X / 2, fireflyOriginOffset.X / 2),\n\t\t\tmath.random(-fireflyOriginOffset.Y / 2, fireflyOriginOffset.Y / 2),\n\t\t\tmath.random(-fireflyOriginOffset.Z / 2, fireflyOriginOffset.Z / 2)\n\t\t)\nend\n\nlocal function updateFireflies(t, step)\n\t\n\tlocal currentTime = tick()\n\tfor i, fireflyTable in pairs(fireflies) do\n\t\tlocal alpha \t\t\t\t= (currentTime - fireflyTable.startTime) / fireflyTable.duration\n\t\tfireflyTable.firefly.CFrame = fireflyTable.startCFrame:lerp(fireflyTable.targetCFrame, alpha) + Vector3.new(0, math.sin(currentTime - fireflyTable.offsetY), 0)\n\t\t\n\t\tif alpha >= 1 then\n\t\t\tlocal targetCFrame \t= assignTargetCFrame()\n\t\t\t\n\t\t\tlocal duration \t\t= utilities.magnitude(targetCFrame.p - fireflyTable.firefly.Position) / fireflyVelocity\n\t\t\t\n\t\t\tfireflyTable.duration \t\t= duration\n\t\t\tfireflyTable.startCFrame \t= fireflyTable.firefly.CFrame\n\t\t\tfireflyTable.targetCFrame \t= targetCFrame;\n\t\t\tfireflyTable.startTime \t\t= tick()\n\t\tend\n\tend\nend\n\nlocal function onCharacterAdded(character)\n\t--[[\n\tif game.PlaceId ~= 3323943158 then\n\t\tif not character.PrimaryPart then\n\t\t\tlocal hitbox = character:WaitForChild(\"hitbox\", 15)\n\t\t\t\n\t\t\tif hitbox then\n\t\t\t\tcharacter.PrimaryPart = hitbox\n\t\t\tend\n\t\tend\n\t\t\n\t\tfireflyOrigin = character.PrimaryPart\n\telse -- if cutscene\n\t\tfireflyOrigin = workspace:WaitForChild(\"path\"):WaitForChild(\"cutscenewagon\").PrimaryPart\n\tend\n\t]]\n\t\n\t\n\t\n\t\nend\n\nlocal fireflyConnection\nlocal function main()\n\tif player.Character then\n\t\tonCharacterAdded(player.Character)\n\tend\n\t\n\tplayer.CharacterAdded:connect(onCharacterAdded)\n\t\n\twhile wait(1) do\n\t\tlocal fireflyOrigin = workspace.CurrentCamera.CFrame\n\t\tif not fireflyOrigin then return end\n\t\t\n\t\tfor i, fireflyTable in pairs(fireflies) do\n\t\t\tif utilities.magnitude(fireflyTable.firefly.Position - fireflyOrigin.Position) >= utilities.magnitude(fireflyOriginOffset) * 1.1 then\n\t\t\t\tgame.Debris:AddItem(fireflyTable.firefly, 0.5)\n\t\t\t\ttween(fireflyTable.firefly, {\"Transparency\"}, 1, 0.5)\n\t\t\t\ttable.remove(fireflies, i)\n\t\t\tend\n\t\tend\n\t\t\n\t\tif game.Lighting.ClockTime >= 18 or game.Lighting.ClockTime <= 6.15 then\n\t\t\tif not fireflyConnection then\n\t\t\t\tfireflyConnection = runService.Stepped:connect(updateFireflies)\n\t\t\tend\n\t\t\t\n\t\t\tif fireflyOrigin and #firefliesFolder:GetChildren() < fireflyCount then\n\t\t\t\tlocal firefly \t= game.ReplicatedStorage.firefly:Clone()\n\t\t\t\t\n\t\t\t\t\n\t\t\t\tif game.ReplicatedStorage:FindFirstChild(\"fireflyColor\") then\n\t\t\t\t\tlocal col = game.ReplicatedStorage.fireflyColor.Value\n\t\t\t\t\tfirefly.Color = col\n\t\t\t\t\tif firefly:FindFirstChild(\"ParticleEmitter\") then\n\t\t\t\t\t\tfirefly.ParticleEmitter.Color = ColorSequence.new(col)\n\t\t\t\t\tend\n\t\t\t\t\tif firefly:FindFirstChild(\"PointLight\") then\n\t\t\t\t\t\tfirefly.PointLight.Color = col\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\tfirefly.Parent \t= firefliesFolder\n\t\t\t\tfirefly.CFrame \t= assignTargetCFrame()\n\t\t\t\t\n\t\t\t\tfirefly.Transparency = 1\n\t\t\t\ttween(firefly, {\"Transparency\"}, 0, 0.5)\n\t\t\t\t\n\t\t\t\tlocal targetCFrame \t= assignTargetCFrame()\n\t\t\t\tlocal duration \t\t= utilities.magnitude(targetCFrame.p - firefly.Position) / fireflyVelocity\n\t\t\t\t\n\t\t\t\ttable.insert(fireflies, {\n\t\t\t\t\tfirefly \t\t= firefly;\n\t\t\t\t\tduration \t\t= duration;\n\t\t\t\t\tstartCFrame \t= firefly.CFrame;\n\t\t\t\t\ttargetCFrame \t= targetCFrame;\n\t\t\t\t\tstartTime \t\t= tick();\n\t\t\t\t\toffsetX \t\t= math.random(9001);\n\t\t\t\t\toffsetY \t\t= math.random(9001);\n\t\t\t\t})\n\t\t\tend\n\t\telse\n\t\t\tif #firefliesFolder:GetChildren() > 0 then\n\t\t\t\tlocal target = firefliesFolder:GetChildren()[1]\n\t\t\t\tfor i, fireflyData in pairs(fireflies) do\n\t\t\t\t\tif fireflyData.firefly == target then\n\t\t\t\t\t\ttable.remove(fireflies, i)\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\ttarget:Destroy()\n\t\t\telse\n\t\t\t\tif fireflyConnection then\n\t\t\t\t\tfireflyConnection:disconnect()\n\t\t\t\t\tfireflyConnection = nil\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\nend\n\nmain()"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/guiLoader.client.lua",
    "content": "-- Annoying hack to get around Roblox's dumb gui replication changes\n-- Author: berezaa\n\nif not game:IsLoaded() then\n\trepeat wait() until game:IsLoaded()\nend\n\nlocal localPlayer = game.Players.LocalPlayer\nlocal uiSystem = game.ReplicatedStorage:WaitForChild(\"StarterGuiFolder\")\n\nfunction rebuildUiLocally(character)\n\tfor i, child in ipairs(uiSystem:GetChildren()) do\n\t\tchild:Clone().Parent = localPlayer.PlayerGui\t\t\n\tend\nend\n\nlocalPlayer.CharacterAdded:Connect(rebuildUiLocally)\n\nif localPlayer.Character then\n\trebuildUiLocally()\nend"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/inputController/characterController.lua",
    "content": "-- will move when deployed if necessary\nlocal class = {}\nclass.__index = class\n\nfunction class.new(object)\n\t--print(\"Character Object:\", object)\n\tlocal self = setmetatable({}, class)\n\tself.character = game.Players.LocalPlayer.Character\n\tself.state = \"\"\n\tself.animations = {}\n\t--print(self.character:GetFullName())\n\tself.events = {\n\t\t[\"StateHandler\"] = self.character.hitbox.state.Changed:Connect(function(value)\n\t\t\t--print(\"State: \", value)\n\t\t\tself.state = value\n\t\tend)\n\t}\n\n\t--[[for _,anim in pairs() do\n\t\tself.animations[anim] = self.character.Humanoid:LoadAnimation(anim)\n\tend]]\nend\n\nreturn class\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/inputController/init.client.lua",
    "content": "local player = game.Players.LocalPlayer\n\n\nlocal inputService = game:GetService(\"UserInputService\")\nlocal characterController = require(script.characterController)\nplayer.CharacterAdded:Connect(function()\n\twait(3) -- temporary debug wait\n\tcharacterController.new(player)\nend)\n\t\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/itemInterface.lua",
    "content": "-- Author: Polymorphic\n\nlocal module \t\t\t= {}\nlocal itemsToRenderSFX \t= {}\n\nlocal characterHitboxPart\nlocal runService \t= game:GetService(\"RunService\")\nlocal player \t\t= game.Players.LocalPlayer\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t= modules.load(\"network\")\n\t\tlocal utilities = modules.load(\"utilities\")\n\t\tlocal placeSetup = modules.load(\"placeSetup\")\n\tlocal itemLookup = require(replicatedStorage.itemData)\t\n\t\t\nlocal itemsFolder = placeSetup.awaitPlaceFolder(\"items\")\n\nlocal function onCharacterAdded(character)\n\tcharacterHitboxPart = character.PrimaryPart\nend\n-- todo: optimize items not relevant to player\nlocal function onItemAdded(item)\n\tif item:WaitForChild(\"MASK_MOTOR\", 20) then\n\t\tlocal degreeOffset = math.random(360)\n\t\tlocal offset = CFrame.Angles(math.pi * math.sin(16 * degreeOffset), degreeOffset, math.pi * math.cos(16 * degreeOffset))\n\t\ttable.insert(itemsToRenderSFX, {manifest = item; offset = offset})\n\tend\n\t\n\t-- make items that you cannot pick up transparent\n\tif item:WaitForChild(\"owners\", 20) then\n\t\twait()\n\t\tif utilities.playerCanPickUpItem(player, item) then\n\t\t\t\n\t\t\t\n\t\t\tif item:FindFirstChild(\"Legendary\") then\n\t\t\t\tutilities.playSound(\"legendaryItemDrop\", item)\n\t\t\t\titem:WaitForChild(\"Trail\",1)\n\t\t\t\titem.Trail.Color = ColorSequence.new(Color3.fromRGB(138, 11, 170))\t\n\t\t\telseif item:FindFirstChild(\"Rare\",1) then\n\t\t\t\tutilities.playSound(\"rareItemDrop\", item)\n\t\t\t\titem:WaitForChild(\"Trail\",1)\n\t\t\t\titem.Trail.Color = ColorSequence.new(Color3.fromRGB(255, 213, 0))\n\t\t\telseif item.Name == \"monster idol\" then\n\t\t\t\tutilities.playSound(\"idolDrop\", item)\n\t\t\telse\n\t\t\t\tutilities.playSound(\"itemDrop\", item)\n\t\t\tend\n\t\telse\n\t\t\tfor i,part in pairs(item:GetDescendants()) do\n\t\t\t\tif part:IsA(\"BasePart\") and part.Transparency < 1 then\n\t\t\t\t\tpart.Transparency = 1 - ((1 - part.Transparency) * 0.3)\n\t\t\t\t\tif part.Material == Enum.Material.Glass then\n\t\t\t\t\t\tpart.Material = Enum.Material.SmoothPlastic\n\t\t\t\t\tend\n\t\t\t\telseif part:IsA(\"ParticleEmitter\") or part:IsA(\"Beam\") or part:IsA(\"Trail\") or part:IsA(\"PointLight\") or part:IsA(\"Light\") then\n\t\t\t\t\tpart.Enabled = false\n\t\t\t\tend\n\t\t\tend\n\t\t\t\n\t\t\tif item:IsA(\"BasePart\") then\n\t\t\t\titem.Transparency = 1 - ((1 - item.Transparency) * 0.3)\n\t\t\t\tif item.Material == Enum.Material.Glass then\n\t\t\t\t\titem.Material = Enum.Material.SmoothPlastic\n\t\t\t\tend\n\t\t\tend\t\t\t\n\t\tend\n\tend\nend\n\n\n\nlocal tau = 2 * math.pi\nlocal function renderItemSFX()\n\tlocal degree \t\t\t= math.rad(tick() % 360)\n\tlocal applicationOffset = Vector3.new(0, 0.75 + 0.35 * math.sin(32 * degree), 0)\n\tlocal rotationOffset \t= CFrame.Angles(0, math.pi * math.cos(16 * degree), math.pi / 6)\n\t\n\tfor i, item in pairs(itemsToRenderSFX) do\n\t\tif item.manifest and item.manifest.Parent then\n\t\t\titem.manifest.MASK_MOTOR.C1 = (rotationOffset * item.offset) + applicationOffset\n\t\telse\n\t\t\ttable.remove(itemsToRenderSFX, i)\n\t\tend\n\tend\nend\n\nlocal function main()\n\t-- queue character\n\tif player.Character then\n\t\tonCharacterAdded(player.Character)\n\tend\n\n\tplayer.CharacterAdded:connect(onCharacterAdded)\n\t\n\t-- add items to queue\n\tfor i, item in pairs(itemsFolder:GetChildren()) do\n\t\tspawn(function() onItemAdded(item) end)\n\tend\n\t\n\titemsFolder.ChildAdded:connect(onItemAdded)\n\trunService.Heartbeat:connect(renderItemSFX)\nend\n\nmain()\n\nreturn module"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/loadData.lua",
    "content": "local module = {}\n\nlocal TeleportService = game:GetService(\"TeleportService\")\n\nlocal teleportData = TeleportService:GetLocalPlayerTeleportData() or {}\n\nlocal arrivingFrom = teleportData.arrivingFrom\nlocal providedTimeStamp = teleportData.dataTimestamp\nlocal accessoriesData = teleportData.playerAccessories\n\nfunction module.init(Modules)\n    local network = Modules.network\n\n    local slot, accessories\n\n    if arrivingFrom then\n        -- todo: blackout UI\n        slot = TeleportService:GetTeleportSetting(\"dataSlot\")\n        if accessoriesData and type(accessoriesData) == \"string\" then\n            accessories = game:GetService(\"HttpService\"):JSONDecode(accessoriesData)\n        end\n    else\n        slot = 1\n    end\n    network:invokeServer(\"loadPlayerData\", slot, providedTimeStamp, accessories)\nend\n\nreturn module"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/mapInteraction.lua",
    "content": "-- a collection of interactable map elements\n-- Author: berezaa\n\nlocal module = {}\n\nlocal player = game.Players.LocalPlayer\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network \t= modules.load(\"network\")\n\t\tlocal tween = modules.load(\"tween\")\n\n\nlocal bouncingParts = {}\n\nlocal function partTouched(part, hit)\n\n\t-- automatic debounce\n\tif game.CollectionService:HasTag(part, \"ActivePart\") then\n\n\t\tgame.CollectionService:RemoveTag(part, \"ActivePart\")\n\t\tspawn(function()\n\t\t\twait(0.1)\n\t\t\tgame.CollectionService:AddTag(part, \"ActivePart\")\n\t\tend)\n\n\t\tif player.Character and player.Character.PrimaryPart and part:FindFirstChild(\"HitDebounce\") == nil and hit:IsDescendantOf(player.Character) then\n\n\t\t\tfor i, p in pairs(bouncingParts) do\n\t\t\t\tif p == part then\n\t\t\t\t\treturn\n\t\t\t\tend\n\t\t\tend\n\n\n\t\t\tif game.CollectionService:HasTag(part,\"Bounce\") then\n\t\t\t\tlocal size = part.Size\n\n\n\t\t\t\tlocal difference = (player.Character.PrimaryPart.Position - part.position).unit\n\t\t\t\tif math.abs(difference.X) < 0.2 then\n\t\t\t\t\tdifference = Vector3.new(0,difference.Y,difference.Z)\n\t\t\t\tend\n\t\t\t\tif math.abs(difference.Y) < 0.2 then\n\t\t\t\t\tdifference = Vector3.new(difference.X,0,difference.Z)\n\t\t\t\tend\n\t\t\t\tif math.abs(difference.Z) < 0.2 then\n\t\t\t\t\tdifference = Vector3.new(difference.X,difference.Y,0)\n\t\t\t\tend\n\n\t\t\t\tlocal coef = 1 + ((size.X * size.Y * size.Z) ^ (1/3))\n\n\t\t\t\tlocal soundMirror = game.ReplicatedStorage.assets.sounds:FindFirstChild(\"bounce\")\n\t\t\t\tif soundMirror then\n\t\t\t\t\tlocal sound = Instance.new(\"Sound\")\n\t\t\t\t\tfor property, value in pairs(game.HttpService:JSONDecode(soundMirror.Value)) do\n\t\t\t\t\t\tsound[property] = value\n\t\t\t\t\tend\n\t\t\t\t\tsound.PlaybackSpeed = math.clamp(1.5 - coef / 50, 0.5, 1.5)\n\t\t\t\t\tsound.Volume = math.clamp(coef/25, 0.2, 2)\n\t\t\t\t\tsound.Parent = part\n\t\t\t\t\tsound:Play()\n\t\t\t\t\tgame.Debris:AddItem(sound,10)\n\t\t\t\tend\n\t\t\t\tspawn(function()\n\t\t\t\t\tlocal initSize = part.Size\n\t\t\t\t\ttable.insert(bouncingParts, part)\n\n\t\t\t\t\t--tween(part, {\"Size\"},{Vector3.new(part.Size.X*1.2, part.Size.Y*1.2, part.Size.Z*1.2)},.4)\n\t\t\t\t\t--wait(.4)\n\t\t\t\t\ttween(part, {\"Size\"},{initSize*1.5},.3, Enum.EasingStyle.Quad)\n\t\t\t\t\twait(.3)\n\t\t\t\t\ttween(part, {\"Size\"},{initSize},.7, Enum.EasingStyle.Bounce)\n\t\t\t\t\twait(.7)\n\t\t\t\t\tfor i, p in pairs(bouncingParts) do\n\t\t\t\t\t\tif p == part then\n\t\t\t\t\t\t\ttable.remove(bouncingParts,i)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\t\t\t\tend)\n\n\t\t\t\tnetwork:fire(\"applyJoltVelocityToCharacter\", difference * coef * 15)\n\t\t\telseif part.Name == \"shopPart\" then\n\t\t\t\tnetwork:invoke(\"openShop\")\n\t\t\tend\n\t\tend\n\n\tend\nend\n\nnetwork:create(\"touchedActivePart\",\"BindableEvent\",\"Event\",partTouched)\n\nlocal function check(part)\n\tif part:IsA(\"BasePart\") then\n\t\tif (part.Parent.Name == \"shroom\" or part.Parent.Name == \"Flower\") and part.Parent:FindFirstChild(\"Destroyed\") == nil then\n\t\t\tlocal size = part.Size\n\t\t\tif size.X * size.Y * size.Z <= 25 then\n\t\t\t\t-- parts that a basic attack can destroy\n\t\t\t\tgame.CollectionService:AddTag(part,\"Destroyable\")\n\t\t\t\tpart.CanCollide = false\n\t\t\telseif part.Name == \"MushPart\" then\n\t\t\t\t-- parts that you can bounce on\n\t\t\t\tgame.CollectionService:AddTag(part,\"Bounce\")\n\t\t\t\tgame.CollectionService:AddTag(part,\"ActivePart\")\n\t\t\t\tpart.CanCollide = true\n\t\t\tend\n\t\telseif part.Name == \"shopPart\" then\n\t\t\tgame.CollectionService:AddTag(part,\"ActivePart\")\n\t\tend\n\tend\n\tif game.CollectionService:HasTag(part,\"ActivePart\") then\n\t\tpart.Touched:connect(function(hit)\n\t\t\tpartTouched(part, hit)\n\t\tend)\n\tend\nend\n\nspawn(function()\n\tfor i,child in pairs(workspace:GetDescendants()) do\n\t\tcheck(child)\n\tend\n\tworkspace.DescendantAdded:connect(check)\nend)\n\nreturn module\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/npcMarkers.lua",
    "content": "-- Pretty golden punctuation\n-- Author: berezaa\n\n\nlocal module = {}\n--[[\n\nlocal assetFolder = script.Parent.Parent:WaitForChild(\"assets\")\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal collectionService = game:GetService(\"CollectionService\")\nlocal runService\t\t= game:GetService(\"RunService\")\n\nlocal questLookup \t= require(replicatedStorage.questLookup)\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal network \t\t= modules.load(\"network\")\n\tlocal tween \t\t= modules.load(\"tween\")\n\tlocal utilities \t= modules.load(\"utilities\")\n\tlocal levels\t\t= modules.load(\"levels\")\n\tlocal mapping\t\t= modules.load(\"mapping\")\n\tlocal questUtil     = modules.load(\"client_quest_util\")\n\tlocal placeSetup \t= modules.load(\"placeSetup\")\n\tlocal foilage\t\t= placeSetup.getPlaceFolder(\"foilage\")\n\t\n\t\n\tlocal utilTable = {}\n\tutilTable.network = network\t\n\tutilTable.utilities = utilities\n\tutilTable.levels = levels\t\n\tutilTable.quest_util = questUtil\n\tutilTable.mapping = mapping\t\n\nlocal function getPlayerQuestStateByQuestId(questId)\n\tlocal quests = network:invoke(\"getCacheValueByNameTag\", \"quests\")\n\t\t\n\tfor i, playerQuestData in pairs(quests.active) do\n\t\tif playerQuestData.id == questId then\n\t\t\t\n\t\t\tlocal objectiveSteps \t= 0\n\t\t\tlocal objectiveStepsDone \t    = 0\n\t\t\t\n\t\t\t-- hacky fix for now but at least it wont break\n\t\t\tif playerQuestData.currentObjective > #playerQuestData.objectives then\n\t\t\t\treturn mapping.questState.completed\n\t\t\tend\n\t\t\t\n\t\t\t\t\n\t\t\tfor ii, playerStepData in pairs(playerQuestData.objectives[playerQuestData.currentObjective].steps) do\n\t\t\t\tobjectiveSteps = objectiveSteps + 1\n\t\t\t\t\tif playerStepData.requirement.amount <= playerStepData.completion.amount then\n\t\t\t\t\t\tobjectiveStepsDone = objectiveStepsDone + 1\n\t\t\t\t\tend\n\t\t\tend\n\t\t\tif objectiveStepsDone > 0 and objectiveStepsDone == objectiveSteps and playerQuestData.objectives[playerQuestData.currentObjective].started then\n\t\t\t\t--if playerQuestData.currentObjective == #playerQuestData.objectives then\n\t\t\t\t--\treturn mapping.questState.handing \n\t\t\t\t--else\n\t\t\t\t\treturn mapping.questState.objectiveDone\n\t\t\t\t--end\n\t\t\telse\n\t\t\t\tif playerQuestData.objectives[playerQuestData.currentObjective].started then\n\t\t\t\t\treturn mapping.questState.active\n\t\t\t\telse\n\t\t\t\t\treturn mapping.questState.unassigned\n\t\t\t\tend\n\t\t\t\t\t\n\t\t\t\treturn mapping.questState.active\n\t\t\tend\n\t\t\t\t\n\t\t\t\t\n\t\tend\n\tend\n\t\t\n\tfor i, completePlayerQuestData in pairs(quests.completed) do\n\t\tif completePlayerQuestData.id == questId then\n\t\t\treturn mapping.questState.completed\n\t\tend\n\tend\n\t\t\n\treturn mapping.questState.unassigned\nend\n\n\nlocal function checkIfObjectiveStartedByQuestId(questId)\n\t\tlocal quests = network:invoke(\"getCacheValueByNameTag\", \"quests\")\n\t\t\t\n\t\tfor i, playerQuestData in pairs(quests.active) do\n\t\t\tif playerQuestData.id == questId then\n\t\t\t\t\n\t\t\t\treturn playerQuestData.objectives[playerQuestData.currentObjective].started\n\t\t\tend\n\t\tend\n\t\treturn true\n\tend\n\nlocal colors = {\n\tactive = Color3.fromRGB(255, 179, 25);\n\tinactive = Color3.fromRGB(88, 88, 88);\n}\n\nlocal questData\n\nlocal questMarkers = {}\n\nlocal updatingParts = false\n\nlocal rings = {}\n\nlocal function updateDialoguePart(dialoguePart)\n\t\n\tupdatingParts = true\n\tlocal existingPart = questMarkers[dialoguePart]\n\tif existingPart then\n\t\texistingPart:Destroy()\n\tend\n\tlocal ySize = 4\n\t\n\t\n\t--if dialoguePart.Parent and dialoguePart.Parent:IsA(\"Model\") and dialoguePart:isDescendantOf(game.Workspace) then\n\t\t--ySize = 4\n\t\t--ySize = dialoguePart.Parent:GetExtentsSize().Y / 2  !this line was lagging the client like crazy when there were enough quest/shop givers in one place!\n\t--end\n\t\n\t\n\tlocal markerPart\n\tlocal markerStyle\n\t\n\tif dialoguePart:FindFirstChild(\"inventory\") then\n\t\tmarkerPart = \"Money\"\n\t\tmarkerStyle = \"active\"\n\telseif dialoguePart:FindFirstChild(\"dialogue\") then\n\t\tlocal dialogue = require(dialoguePart.dialogue)\n\t\t\n\t\t\n\t\t\n\t\t\n\t\t\tif dialogue.flagForQuest then\n\t\t\t\t\n\t\n\t\t\t\tlocal flagForQuest = dialogue.flagForQuest\n\t\t\t\tif type(flagForQuest) == \"function\" then\n\t\t\t\t\tflagForQuest = flagForQuest(utilTable)\t\n\t\t\t\tend\n\t\t  \t\t\n\t\t\t\tlocal questData = questLookup[flagForQuest]\n\t\t\t\tlocal questState = getPlayerQuestStateByQuestId(flagForQuest)\n\t\t\t\tlocal questCanBeStarted = questUtil.masterCanStartQuest(flagForQuest) -- tests for all quest requirements\n\t\t\t\t\n\t\t\t\t\n\t\t\t\tlocal currentObjectiveAndStarted = questUtil.getQuestObjectiveAndStarted(flagForQuest)\n\t\t\t\t\n\t\t\t\tlocal canContinue = true\n\t\t\t\t\n\t\t\t\t\n\t\t\t\tlocal objectiveChoiceTable = dialogue.getObjectiveOptionsTable(utilTable)\n\t\t\t\tif currentObjectiveAndStarted < 0 then\n\t\t\t\t\tif objectiveChoiceTable[currentObjectiveAndStarted *-1] == nil then\n\t\t\t\t\t\tcanContinue = false \n\t\t\t\t\tend\n\t\t\t\telse\n\t\t\t\t\tif objectiveChoiceTable[currentObjectiveAndStarted] == nil then\n\t\t\t\t\t\tcanContinue = false \n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t-- objective unassigned logic\n\t\t\t\tif currentObjectiveAndStarted < 0 and canContinue then\n\t\t\t\t\t\n\t\t\t\t\tlocal actualObjective = currentObjectiveAndStarted * -1\n\t\t\t\t\t\n\t\t\t\t\tobjectiveChoiceTable = objectiveChoiceTable[actualObjective]\n\t\t\t\t\tfor i, choice in pairs(objectiveChoiceTable) do\n\t\t\t\t\t\tif choice.isStarterNPC ~= nil and not choice.isStarterNPC then\n\t\t\t\t\t\t\tcanContinue = false \n\t\t\t\t\t\tend\n\t\t\t\t\t\t\n\t\t\t\t\tend\n\t\t\t\telseif canContinue then\n\t\t\t\t\t-- objective handing logic\n\t\t\t\t\tobjectiveChoiceTable = objectiveChoiceTable[currentObjectiveAndStarted]\n\t\t\t\t\tfor i, choice in pairs(objectiveChoiceTable) do\n\t\t\t\t\t\tif choice.isHanderNPC ~= nil and not choice.isHanderNPC then\n\t\t\t\t\t\t\tcanContinue = false \n\t\t\t\t\t\tend\n\t\t\t\t\t\t\n\t\t\t\t\tend\n\t\t\t\t\t\n\t\t\t\tend\n\t\t\t\t\n\t\t\t\tif canContinue then\n\t\t\t\t\t\n\t\t\t\t\tif questData and questState ~= mapping.questState.completed then\n\t\t\t\t\t\t\t\n\t\t\t\t\t\tif questState == mapping.questState.handing or questState == mapping.questState.objectiveDone then\n\t\t\t\t\t\t\t\t-- quest to hand in takes max priority\n\t\t\t\t\t\t\tmarkerPart = \"Question\"\n\t\t\t\t\t\t\tmarkerStyle = \"active\"\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\telseif questState == mapping.questState.unassigned then\n\t\t\t\t\t\t\tif questCanBeStarted then\n\t\t\t\t\t\t\t\t\t-- valid quest to do\n\t\t\t\t\t\t\t\tmarkerPart = \"Exclaim\"\n\t\t\t\t\t\t\t\tmarkerStyle = \"active\"\n\t\t\t\t\t\t\telseif markerPart == nil then\n\t\t\t\t\t\t\t\t\t-- quest out of requirement\n\t\t\t\t\t\t\t\t\t\t-- ber edit remove marker from quests you cant do\n\t\t\t\t\t\t--\t\tmarkerPart = \"Exclaim\" -- if they don't have the requirements don't show the marker at all\n\t\t\t\t\t\t--\t\tmarkerStyle = \"inactive\"\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\telseif questState == mapping.questState.active and markerPart == nil then\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\tmarkerPart = \"Question\"\n\t\t\t\t\t\t\tmarkerStyle = \"inactive\"\t\n\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tend\n\t\t\t\t\n\telseif dialoguePart:FindFirstChild(\"QuestObjectiveTag\") then\n\t\t\t\tmarkerPart = \"Exclaim\"\n\t\t\t\tmarkerStyle = \"active\"\n\t\t\t\t\n\tend\n\t\t\t\t\n\n\t\n\tif markerPart then\n\t\t\n\n\n\t\t\n\t\tlocal primaryPart = dialoguePart.Parent.PrimaryPart or dialoguePart\n\t\t\n\t\tlocal radius = dialoguePart:FindFirstChild(\"range\") and dialoguePart.range.Value or 8\n\n\n\t\tlocal realMarker = assetFolder:FindFirstChild(markerPart)\n\t\tif realMarker and dialoguePart:IsDescendantOf(game.workspace) then\n\t\t\trealMarker = realMarker:Clone()\n\t\t\tquestMarkers[dialoguePart] = realMarker\n\t\t\trealMarker.Anchored = true\n\t\t\trealMarker.Color = colors[markerStyle] or colors[\"inactive\"]\n\t\t\t\n\t\t\tlocal primaryPart = dialoguePart.Parent.PrimaryPart or dialoguePart\n\t\t\t\n\t\t\trealMarker.CFrame = primaryPart.CFrame * CFrame.Angles(-math.pi/2, 0, 0) + Vector3.new(0, ySize + realMarker.Size.Y/2 + 3, 0) \n\t\t\trealMarker.Parent = foilage\n\t\t\tlocal realMarkerMask = realMarker:Clone()\n\t\t\trealMarkerMask.Parent = realMarker\n\t\t\trealMarkerMask.Material = Enum.Material.Neon\n\t\t\trealMarkerMask.Transparency = 0.7\n\t\t\t\n\t\t\tif markerStyle == \"inactive\" then\n\t\t\t\trealMarker.Transparency = 0.4\n\t\t\tend\n\t\tend\n\tend\t\n\t\t\t\nend\n\t\t\t\t\ncollectionService:GetInstanceAddedSignal(\"interact\"):connect(function(interaction)\n\tupdateDialoguePart(interaction)\nend)\t\t\t\t\n\ncollectionService:GetInstanceRemovedSignal(\"interact\"):connect(function(interaction)\n\tlocal existingPart = questMarkers[interaction]\n\tif existingPart then\n\t\texistingPart:Destroy()\n\tend\nend)\n\nlocal function updateAllParts()\n\tlocal dialogueParts = collectionService:GetTagged(\"interact\")\n\tfor i,dialoguePart in pairs(dialogueParts) do\n\t\tupdateDialoguePart(dialoguePart)\n\tend\t\nend\n\nspawn(function()\n\twait(3)\n\tupdateAllParts()\nend)\n\n\nlocal function onDataChange(key, value)\n\tif key == \"quests\" or key == \"class\" or key == \"level\" then\n\t\tupdateAllParts()\n\t\n\tend\nend\n\n\nnetwork:create(\"npcmarkers_force_update\", \"BindableEvent\", \"Event\", function(player)\n\tupdateAllParts()\nend)\n\t\nnetwork:connect(\"propogationRequestToSelf\", \"Event\", onDataChange)\n\t\nlocal angleOffset = 0\nlocal heightOffset = -0.25\n\t\n-- todo: yikes\nrunService.Heartbeat:connect(function()\n\tfor rootPart, marker in pairs(questMarkers) do\n\t\tif rootPart.Parent then\n\t\t\t\n\t\t\n\t\t\tif rootPart.Parent.PrimaryPart then\n\t\t\t\trootPart = rootPart.Parent.PrimaryPart\n\t\t\tend\n\t\t\tlocal baseCF = rootPart.CFrame * CFrame.Angles(-math.pi/2, 0, 0) + Vector3.new(0, 5.75, 0)\n\t\t\tmarker.CFrame = baseCF * CFrame.Angles(0, 0, math.rad(angleOffset)) + Vector3.new(0, heightOffset, 0) \n\t\t\tlocal markerMask = marker:FindFirstChild(marker.Name)\n\t\t\tif markerMask then\n\t\t\t\tmarkerMask.CFrame = marker.CFrame\n\t\t\tend\n\t\telse\n\t\t\tquestMarkers[rootPart] = nil\n\t\tend\n\tend\nend)\n\nspawn(function()\n\twhile runService.Heartbeat:wait() do\n\t\tangleOffset = angleOffset + 1\n\t\tif angleOffset >= 360 then\n\t\t\tangleOffset = 0\n\t\tend\n\tend\nend)\nspawn(function()\n\twhile runService.Heartbeat:wait() do\n\t\tfor i=1,100 do\n\t\t\theightOffset = heightOffset + 1/200\n\t\t\trunService.Heartbeat:wait()\n\t\tend\n\t\tfor i=1,100 do\n\t\t\theightOffset = heightOffset - 1/200\n\t\t\trunService.Heartbeat:wait()\n\t\tend\n\tend\nend)\n]]\nreturn module\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/playerDataPropogationCache.lua",
    "content": "local module = {}\n\n-- this is the service that will handle data propogation between client and server, effectively acting as the gatekeeper for data\n-- leaving the client to the server, and entering the client from the server.\n-- IMPORTANT! This can all be editted by the client, never assume the player's client cache data to be true. This will only have negative\n-- implications on players that do edit the cache (ie, a malicious player COULD edit their client cache inventory to include\n-- any items they dont own, and they could initiate a trade with the item appearing in the trade *ON THEIR SIDE*. The server will\n-- validate and reject this when it sees they're attempting to trade an item they don't own, so IMO it's not a huge issue)\n\n-- btw: I'm doing this so that we don't /need/ ten different connection listeners throughout the client, especially if, say,\n-- for a trade window we want to just get the inventory. allows us to skip requesting for the information, and as long as the\n-- player isn't being malicious then there will be no issues. players who maliciouslly edit their cache will be faced with a server\n-- rejection message, have their cache flushed, and then need to wait for the client to fetch the cache again. no fun for them!\n\n-- Author: Polymorphic\n\nlocal cache = {}\n\nlocal network\nlocal utilities\n\n-- this runs when the server sends information to the client, store it.\n-- server will not spam this.\nlocal function onPropogateCacheDataRequestToClientReceived(propogationNameTag, propogationData)\n\tcache[propogationNameTag] = propogationData\n\n\tnetwork:fire(\"propogationRequestToSelf\", propogationNameTag, propogationData)\nend\n\nlocal function onFlushPropogationCache(propogationCacheLookupTable)\n\tfor propogationNameTag, propogationData in pairs(propogationCacheLookupTable) do\n\t\tonPropogateCacheDataRequestToClientReceived(propogationNameTag, propogationData)\n\tend\nend\n\nlocal function __impFlushPropogationCache()\n\tlocal propogationCacheLookupTable = network:invokeServer(\"getPropogationCacheLookupTable\")\n\tonFlushPropogationCache(propogationCacheLookupTable)\nend\n\n-- todo: client spam limit?\nfunction module:flushPropogationCache()\n\t__impFlushPropogationCache()\nend\n\nfunction module:getByPropogationNameTag(propogationNameTag)\n\tif cache[propogationNameTag] then\n\t\tif type(cache[propogationNameTag]) == \"table\" then\n\t\t\treturn utilities.copyTable(cache[propogationNameTag])\n\t\telse\n\t\t\treturn cache[propogationNameTag]\n\t\tend\n\tend\n\n\treturn nil\nend\n\nfunction module.init(Modules)\n\n\tnetwork = Modules.network\n\tutilities = Modules.utilities\n\tnetwork:create(\"propogationRequestToSelf\", \"BindableEvent\")\n\tnetwork:create(\"propogationRequestReceived\", \"BindableEvent\")\n\tnetwork:connect(\"propogateCacheDataRequest\", \"OnClientEvent\", onPropogateCacheDataRequestToClientReceived)\n\tnetwork:connect(\"clientFlushPropogationCache\", \"OnClientEvent\", onFlushPropogationCache)\n\tspawn(function()\n\t\tmodule:flushPropogationCache()\n\tend)\n\tnetwork:create(\"getLocalPlayerDataCache\", \"BindableFunction\", \"OnInvoke\", function()\n\t\treturn cache\n\tend)\n\tnetwork:create(\"getCacheValueByNameTag\", \"BindableFunction\", \"OnInvoke\", function(nameTag)\n\t\treturn module:getByPropogationNameTag(nameTag)\n\tend)\nend\n\n\nreturn module"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/resetButtonCallback.lua",
    "content": "local module = {}\n\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\nnetwork = modules.load(\"network\")\n\nlocal resetBind = Instance.new(\"BindableEvent\")\nresetBind.Event:connect(function()\n\tlocal success = true\n\tif not game.ReplicatedStorage:FindFirstChild(\"safeZone\") then\n\t\tsuccess = network:invoke(\"promptActionFullscreen\",\"Are you sure you wish to kill your character?\")\n\tend\n\tif success then\n\t\tnetwork:invokeServer(\"playerRequest_respawnMyCharacter\")\n\tend\nend)\n\ngame.StarterGui:SetCore(\"ResetButtonCallback\",resetBind)\n\nreturn module\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/resources.lua",
    "content": "-- Resources\n-- Rocky28447\n-- June 2, 2020\n\n\n\nlocal Resources = {}\n\nlocal CollectionService = game:GetService(\"CollectionService\")\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal Modules\nlocal Thread\nlocal network\n\n\nlocal function getNodeTypeMetadataFromNode(node)\n\tlocal containingFolder = node:FindFirstAncestorWhichIsA(\"Folder\")\n\tlocal isNodeGroup = CollectionService:HasTag(containingFolder, \"resourceNodeGroupFolder\")\n\tlocal nodeTypeMetadata = isNodeGroup and containingFolder.Parent.Metadata or containingFolder.Metadata\n\n\treturn nodeTypeMetadata\nend\n\n\nfunction Resources:DoEffect(node, effect)\n\tlocal nodeMetadata = getNodeTypeMetadataFromNode(node)\n\tlocal effectFolder = nodeMetadata.EffectsStorage:FindFirstChild(effect)\n\n\tif effectFolder then\n\t\tfor _, effect in pairs (effectFolder:GetChildren()) do\n\t\t\tlocal effectClone = effect:Clone()\n\t\t\teffectClone.Parent = node.PrimaryPart\n\n\t\t\tif effectClone:IsA(\"Sound\") then\n\t\t\t\teffectClone:Play()\n\t\t\telseif effectClone:IsA(\"ParticleEmitter\") then\n\t\t\t\teffectClone:Emit(effectClone.Rate)\n\t\t\tend\n\n\t\t\tThread.Delay(10, function()\n\t\t\t\teffectClone:Destroy()\n\t\t\tend)\n\t\tend\n\tend\nend\n\n\nfunction Resources:NodeReplenished(node)\n\tlocal nodeMetadata = require(getNodeTypeMetadataFromNode(node))\n\tlocal onReplenish = nodeMetadata.Animations.OnReplenish\n\n\tif nodeMetadata.DestroyOnDeplete then\n\t\tfor _, c in pairs (node:GetDescendants()) do\n\t\t\tif c:IsA(\"BasePart\") then\n\t\t\t\tc.Transparency = 0\n\t\t\t\tc.CanCollide = true\n\t\t\tend\n\t\tend\n\telse\n\t\tif node:FindFirstChild(\"DropPoints\") then\n\t\t\tfor _, dropPoint in pairs (node.DropPoints:GetChildren()) do\n\t\t\t\tdropPoint.Value.Transparency = 0\n\t\t\tend\n\t\tend\n\tend\n\n\tif onReplenish and type(onReplenish) == \"function\" then\n\t\tonReplenish(node)\n\telse\n\t\tself:DoEffect(node, \"Replenish\")\n\tend\n\n\tCollectionService:AddTag(node.PrimaryPart, \"attackable\")\nend\n\n\nfunction Resources:NodeDepleted(node)\n\tlocal nodeMetadata = require(getNodeTypeMetadataFromNode(node))\n\tlocal onDeplete = nodeMetadata.Animations.OnDeplete\n\n\tif nodeMetadata.DestroyOnDeplete then\n\t\tfor _, c in pairs (node:GetDescendants()) do\n\t\t\tif c:IsA(\"BasePart\") then\n\t\t\t\tc.Transparency = 1\n\t\t\t\tc.CanCollide = false\n\t\t\tend\n\t\tend\n\t\tif not onDeplete or type(onDeplete) ~= \"function\" then\n\t\t\tself:DoEffect(node, \"Deplete\")\n\t\tend\n\tend\n\tif onDeplete and type(onDeplete) == \"function\" then\n\t\tonDeplete(node)\n\tend\n\tCollectionService:RemoveTag(node.PrimaryPart, \"attackable\")\nend\n\n\nfunction Resources:Start()\n\n\tnetwork:connect(\"resourceHarvested\", \"OnClientEvent\", function(node, dropPoint)\n\t\tlocal nodeMetadata = require(getNodeTypeMetadataFromNode(node))\n\t\tlocal onHarvest = nodeMetadata.Animations.OnHarvest\n\n\t\tif onHarvest and type(onHarvest) == \"function\" then\n\t\t\tonHarvest(node, dropPoint)\n\t\telse\n\t\t\tself:DoEffect(node, \"Harvest\")\n\t\tend\n\n\t\tif dropPoint then\n\t\t\tdropPoint.Transparency = 1\n\t\t\tdropPoint.CanCollide = false\n\t\tend\n\tend)\n\n\tnetwork:connect(\"resourceReplenished\", \"OnClientEvent\", function(node)\n\t\tself:NodeReplenished(node)\n\tend)\n\n\tnetwork:connect(\"resourceDepleted\", \"OnClientEvent\", function(node)\n\t\tself:NodeDepleted(node)\n\tend)\n\n\tlocal depletedNodes = network:invokeServer(\"getDepletedResourceNodes\")\n\tfor _, node in pairs(depletedNodes) do\n\t\tself:NodeDepleted(node)\n\tend\n\nend\n\n\nfunction Resources:Init()\n\tModules = require(ReplicatedStorage.modules)\n\tThread = Modules.load(\"thread\")\n\tnetwork = Modules.load(\"network\")\nend\n\n\nResources:Init()\nResources:Start()\n\nreturn Resources\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/sitting.lua",
    "content": "local module = {}\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\tlocal modules = require(replicatedStorage.modules)\n\t\tlocal network = modules.load(\"network\")\n\nlocal currentPlayerSeatPart = nil\nlocal player \t\t\t\t= game.Players.LocalPlayer\n\nlocal function seatPlayer(seatPart)\n\tif not player or not player.Character or not player.Character.PrimaryPart then return false end\n\t\n\t-- sit the player in the proper position\n\tplayer.Character.PrimaryPart.CFrame \t= script.Parent.CFrame + Vector3.new(0, 0.5, 0)\n\tplayer.Character.PrimaryPart.Anchored \t= true\n\t\n\t-- change the state to isSitting, isSitting is authoritative \n\tnetwork:invoke(\"setCharacterMovementState\", \"isSitting\", true, script.Parent)\n\t\n\tcurrentPlayerSeatPart = seatPart\n\t\n\t-- success!\n\treturn true\nend\n\nlocal function unseatPlayer()\n\t\nend\n\nlocal function isPlayerSitting()\n\t\nend\n\nlocal function getPlayerSeat()\n\t\nend\n\nlocal function main()\n\tnetwork:create(\"seatPlayer\", \"BindableFunction\", \"OnInvoke\", seatPlayer)\n\tnetwork:create(\"unseatPlayer\", \"BindableFunction\", \"OnInvoke\", unseatPlayer)\n\tnetwork:create(\"getPlayerSeat\", \"BindableFunction\", \"OnInvoke\", getPlayerSeat)\nend\n\nreturn module"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/teleportation.client.lua",
    "content": "\nlocal teleService = game:GetService(\"TeleportService\")\n\nlocal sessionId = teleService:GetTeleportSetting(\"sessionId\")\nlocal joinTime = teleService:GetTeleportSetting(\"joinTime\")\nlocal telePartyInfo = teleService:GetTeleportSetting(\"partyInfo\")\n\nlocal teleportUITemplate = game.ReplicatedStorage:WaitForChild(\"teleportUI\")\nlocal teleportUITemplate_death = game.ReplicatedStorage:WaitForChild(\"teleportUIDeath\")\n\nlocal currentTeleportUI\n\n\nlocal Player = game.Players.LocalPlayer\n\nspawn(function()\n\n\tlocal existingTeleportUI = teleService:GetArrivingTeleportGui()\n\tif existingTeleportUI then\n\t\texistingTeleportUI.Status.Text = \"Arriving...\"\n\n\n\t\t\trepeat wait() until Player:FindFirstChild(\"PlayerGui\")\n\t\t\texistingTeleportUI.Parent = Player.PlayerGui\n\n\n\t\t\tlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\n\t\t\t\tlocal modules = require(replicatedStorage.modules)\n\t\t\t\t\tlocal network \t= modules.load(\"network\")\n\t\t\t\t\tlocal tween     = modules.load(\"tween\")\n\n\t\t\tPlayer:WaitForChild(\"DataLoaded\")\n\n\t\t\ttween(existingTeleportUI.Blackout,{\"BackgroundTransparency\"},1,1)\n\t\t\ttween(existingTeleportUI.logo,{\"ImageTransparency\"},1,1)\n\t\t\ttween(existingTeleportUI.Description,{\"TextTransparency\",\"TextStrokeTransparency\"},1,1)\n\t\t\ttween(existingTeleportUI.Destination,{\"TextTransparency\",\"TextStrokeTransparency\"},1,1)\n\t\t\ttween(existingTeleportUI.Status,{\"TextTransparency\",\"TextStrokeTransparency\"},1,1)\n\t\t\twait(1)\n\t\t\texistingTeleportUI:Destroy()\n\n\tend\n\tgame.ContentProvider:PreloadAsync({teleportUITemplate:WaitForChild(\"swoosh\")})\n\tgame.ContentProvider:PreloadAsync({teleportUITemplate:WaitForChild(\"gradient\")})\nend)\n\nlocal teleporting = false\n\n\n\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal network \t= modules.load(\"network\")\n\tlocal tween = modules.load(\"tween\")\n\tlocal utilities = modules.load(\"utilities\")\n\nnetwork:create(\"clientRequestingTeleport\",\"BindableEvent\")\nnetwork:create(\"forceSpawn\",\"BindableEvent\")\n\nPlayer.CharacterAdded:Connect(function()\n\twait()\n\tnetwork:fire(\"forceSpawn\")\nend)\n\n\n\nteleService.TeleportInitFailed:connect(function(player, teleportResult, errorMessage)\n\tif player == game.Players.LocalPlayer then\n\t\tcurrentTeleportUI = teleService:GetArrivingTeleportGui() or currentTeleportUI\n\t\tif currentTeleportUI then\n\t\t\tcurrentTeleportUI.Status.Text = \"Teleport failed! (\"..teleportResult.Name..\")\"\n\t\t\twait(1.5)\n\t\t\tcurrentTeleportUI:Destroy()\n\t\t\tteleporting = false\n\t\tend\n\tend\nend)\n\nlocal preppingTeleportUI\n\nlocal function prepTeleportUI(destination, teleportType)\n\n\t-- map to non-demo version and display that info\n\tlocal placeIdMapping = utilities.placeIdMapping\n\tfor placeIdString, mappingId in pairs(placeIdMapping) do\n\t\tif destination == mappingId then\n\t\t\tdestination = tonumber(placeIdString)\n\t\t\tbreak\n\t\tend\n\tend\n\n\tif preppingTeleportUI then\n\t\treturn false\n\tend\n\n\tpreppingTeleportUI = true\n\n\tif teleportType == \"death\" then\n\t\tlocal teleportUI = teleportUITemplate_death:Clone()\n\t\tteleportUI.Parent = Player.PlayerGui\n\t\tteleportUI.Enabled = true\n\t\tteleportUI.Blackout.Visible = true\n\t\tteleportUI.Blackout.BackgroundTransparency = 1\n\t\ttween(teleportUI.Blackout, {\"BackgroundTransparency\"}, 0, 0.5)\n\t\tteleService:SetTeleportGui(teleportUITemplate_death)\n\t\twait(0.5)\n\t\tspawn(function()\n\t\t\twait(1)\n\t\t\tpreppingTeleportUI = false\n\t\tend)\n\t\treturn teleportUI\n\tend\n\n\n\n\tteleportUITemplate.Thumbnail.Image = \"https://www.roblox.com/Thumbs/Asset.ashx?width=768&height=432&assetId=\"..destination\n\n\tlocal teleportUI = nil\n\n\tif not teleportUI then\n\t\tteleportUI = teleportUITemplate:Clone()\n\t\tcurrentTeleportUI = teleportUI\n\n\t\tteleportUI.Blackout.BackgroundTransparency = 0\n\t\tteleportUI.Blackout.Position = UDim2.new(-1,0,0.5,0)\n\n\n\t\tteleportUI.Thumbnail.ImageTransparency = 1\n\t\tteleportUI.gradient.ImageTransparency = 1\n\n\n\t\tteleportUI.logo.ImageTransparency = 1\n\n\t\tteleportUI.swoosh:Play()\n\n\n\t\tteleportUI.Description.TextTransparency = 1\n\t\tteleportUI.Destination.TextTransparency = 1\n\t\tteleportUI.Status.TextTransparency = 1\n\n\n\t\ttween(teleportUI.Blackout,{\"Position\"},UDim2.new(0,0,0.5,0),0.3)\n\tend\n\n\n\tteleportUI.Parent = Player.PlayerGui\n\n\n\tteleportUI.Enabled = true\n\n\tteleService:SetTeleportGui(teleportUITemplate)\n\n\tlocal runService = game:GetService(\"RunService\")\n\n\tteleportUI.spinner.Visible = true\n\tspawn(function()\n\t\twhile teleporting do\n\t\t\tteleportUI.spinner.Rotation = teleportUI.spinner.Rotation + 2\n\t\t\trunService.RenderStepped:wait()\n\t\tend\n\tend)\n\n\tlocal preloadDone\n\tlocal infoDone\n\n\tspawn(function()\n\t\tteleportUI.Status.Text = \"Loading...\"\n\t\twait(0.3)\n\n\t\ttween(teleportUI.Status,{\"TextTransparency\"},0,0.5)\n\tend)\n\n\n\n\tspawn(function()\n\t\tgame.ContentProvider:PreloadAsync({teleportUI.logo, teleportUI.Thumbnail})\n\t\tpreloadDone = true\n\tend)\n\n\tlocal info\n\n\tspawn(function()\n\t\tinfo = game.MarketplaceService:GetProductInfo(destination,Enum.InfoType.Asset\t)\n\t\tinfoDone = true\n\tend)\n\n\tlocal start = tick()\n\trepeat wait() until preloadDone and infoDone or (tick() - start > 10)\n\n\tlocal dif = tick() - start\n\tif dif < 0.3 then\n\t\twait(0.3 - dif)\n\tend\n\n\tteleportUI.gradient.ImageTransparency = 0\n\tteleportUI.Thumbnail.UIScale.Scale = 1\n\n\n\ttween(teleportUI.logo,{\"ImageTransparency\"},0,0.7)\n\ttween(teleportUI.Thumbnail,{\"ImageTransparency\"},0,1)\n\ttween(teleportUI.Thumbnail.UIScale,{\"Scale\"}, 1.05, 1)\n\ttween(teleportUI.Description,{\"TextTransparency\"},0,0.5)\n\ttween(teleportUI.Destination,{\"TextTransparency\"},0,0.5)\n\n\n\n\tif info then\n\t\tteleportUI.Destination.Text = info.Name\n\t\tteleportUI.Description.Text = info.Description\n\t\tteleportUI.Status.Text = \"Preparing to travel...\"\n\n\t\tteleportUITemplate.Destination.Text = info.Name\n\t\tteleportUITemplate.Description.Text = info.Description\n\t\tteleportUITemplate.Status.Text = \"Traveling to location...\"\n\t\tteleService:SetTeleportGui(teleportUITemplate)\n\telse\n\t\tteleportUI.Status.Text = \"Can't find travel info\"\n\t\tteleportUITemplate.Status.Text = \"Can't find travel info\"\n\tend\n\tspawn(function()\n\t\twait(1)\n\t\tpreppingTeleportUI = false\n\tend)\n\n\n\treturn teleportUI\nend\n\nnetwork:connect(\"signal_teleport\", \"OnClientEvent\", prepTeleportUI)\n\n\n\nlocal function teleportTo(placeId)\n\n\tlocal Player = game.Players.LocalPlayer\n\tlocal teleService = game:GetService(\"TeleportService\")\n\tif Player:FindFirstChild(\"AnalyticsSessionId\") then\n\t\tteleService:SetTeleportSetting(\"sessionId\",Player.AnalyticsSessionId.Value)\n\tend\n\tif Player:FindFirstChild(\"JoinTime\") then\n\t\tteleService:SetTeleportSetting(\"joinTime\",Player.JoinTime.Value)\n\tend\n\n\tlocal teleportUI = prepTeleportUI(placeId)\n\n\twait(0.5)\n\tteleportUI.Status.Text = \"Traveling to location...\"\n\twait(0.5)\n\tteleportUI.Body.Position = UDim2.new(0,0,0,0)\n\tteleportUI.Blackout.BackgroundTransparency = 0\n\n\tgame.GuiService.SelectedObject = nil\n\n\tteleService:Teleport(placeId,nil,nil,teleportUI)\nend\n\nnetwork:create(\"teleportPlayerTo\",\"BindableFunction\",\"OnInvoke\",teleportTo)\n\nlocal currentPartyInfo\n\nlocal function updateTeleportPart(teleportPart)\n\tif currentPartyInfo then\n\t\tteleportPart.CanCollide = true\n\t\tif not game.CollectionService:HasTag(teleportPart, \"interact\") then\n\t\t\tgame.CollectionService:AddTag(teleportPart, \"interact\")\n\t\tend\n\telse\n\t\tteleportPart.CanCollide = false\n\t\tif game.CollectionService:HasTag(teleportPart, \"interact\") then\n\t\t\tgame.CollectionService:RemoveTag(teleportPart, \"interact\")\n\t\tend\n\tend\nend\n\nlocal function prepDataForTeleport(destination)\n\n\tlocal teleportUI = prepTeleportUI(destination)\n\n\tlocal starttime = os.time()\n\n\tlocal analyticsSessionId\n\tif Player:FindFirstChild(\"AnalyticsSessionId\") then\n\t\tanalyticsSessionId = Player.AnalyticsSessionId.Value\n\tend\n\n\tlocal joinTime\n\tif Player:FindFirstChild(\"JoinTime\") then\n\t\tjoinTime = Player.JoinTime.Value\n\tend\n\n\tlocal dataSlot = Player:FindFirstChild(\"dataSlot\") and Player.dataSlot.Value or 1\n\n\tlocal TimeStamp = network:invokeServer(\"saveDataForTeleportation\")\n\tif TimeStamp then\n\n\n\t\tif analyticsSessionId then\n\t\t\tteleService:SetTeleportSetting(\"sessionId\",analyticsSessionId)\n\t\tend\n\n\t\tif joinTime then\n\t\t\tteleService:SetTeleportSetting(\"joinTime\",joinTime)\n\t\tend\n\n\t\tteleService:SetTeleportSetting(\"lastTimeStamp\",TimeStamp)\n\t\tteleService:SetTeleportSetting(\"arrivingTeleportId\", game.PlaceId)\n\n\t\tteleService:SetTeleportSetting(\"dataSlot\",dataSlot)\n\n\t\tteleportUI.Status.Text = \"Traveling to location...\"\n\n\t\tlocal difference = os.time() - starttime\n\t\tif difference <= 1 then\n\t\t\twait(1-difference)\n\t\tend\n\n\t\tteleportUI.Blackout.BackgroundTransparency = 0\n\n\t\tgame.GuiService.SelectedObject = nil\n\n\t\twait()\n\tend\n\n\treturn TimeStamp, teleportUI\n\nend\n\nlocal function externalTP(destination)\n\tif Player:FindFirstChild(\"DataLoaded\") == nil or Player:FindFirstChild(\"teleporting\") or teleporting then\n\t\treturn false\n\tend\n\n\tteleporting = true\n\n\tlocal Timestamp, teleportUI = prepDataForTeleport(destination)\n\tif Timestamp then\n\t\tteleService:Teleport(destination,nil,nil,teleportUI)\n\telse\n\t\t-- failed to save data, abort teleportation\n\tend\nend\n\nnetwork:connect(\"externalTeleport\", \"OnClientEvent\", externalTP)\n\n\nnetwork:create(\"localPrepareForTeleport\", \"BindableFunction\", \"OnInvoke\", function(destination)\n\tteleporting = true\n\n\tlocal teleportUI = prepTeleportUI(destination)\nend)\n\nlocal function activate(teleportPart)\n\n\tupdateTeleportPart(teleportPart)\n\n\tlocal destination = teleportPart:WaitForChild(\"teleportDestination\").Value\n\n\tlocal db = false\n\n\tteleportPart.Touched:connect(function(hit)\n\n\n\n\t\tif Player:FindFirstChild(\"DataLoaded\") == nil or Player:FindFirstChild(\"teleporting\") then\n\t\t\treturn false\n\t\tend\n\t\tif Player.Character and hit == Player.Character.PrimaryPart and (Player.Character.PrimaryPart.Position - teleportPart.Position).magnitude < 50 then\n\n\n\t\t\tif db then\n\t\t\t\treturn false\n\t\t\tend\n\n\t\t\tlocal inParty = currentPartyInfo ~= nil\n\t\t\tlocal isForced = teleportPart:FindFirstChild(\"forced\") and teleportPart.forced.Value\n\n\t\t\tif inParty and (not isForced) then\n\t\t\t\tnetwork:fire(\"applyJoltVelocityToCharacter\", teleportPart.CFrame.lookVector * 5)\n\t\t\t\treturn\n\t\t\tend\n\n\t\t\tif not teleporting then\n\t\t\t\tteleporting = true\n\t\t\t\tlocal teleportUI = prepTeleportUI(destination)\n\t\t\t\tlocal success, fail = network:invokeServer(\"playerRequest_useTeleporter\", teleportPart)\n\t\t\t\tif success then\n\t\t\t\telse\n\t\t\t\t\tteleportUI:Destroy()\n\t\t\t\t\tteleporting = false\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend)\nend\n\nlocal parts = game.CollectionService:GetTagged(\"teleportPart\")\ngame.CollectionService:GetInstanceAddedSignal(\"teleportPart\"):connect(activate)\n\nfor i,part in pairs(parts) do\n\tspawn(function()\n\t\tactivate(part)\n\tend)\nend\n\ngame.ReplicatedStorage.ChildAdded:Connect(function(Child)\n\tif Child.Name == \"spawnPoints\" then\n\n\t\tnetwork:fire(\"forceSpawn\")\n\tend\nend)\n\nlocal function getPartyLeaderUserId(partyInfo)\n\tfor i,member in pairs(partyInfo.members) do\n\t\tlocal player = member.player\n\t\tif player and member.isLeader then\n\t\t\treturn player.userId\n\t\tend\n\tend\nend\n\nlocal function updatePartyInfo(partyInfo)\n\n\tpartyInfo = partyInfo or network:invokeServer(\"playerRequest_getMyPartyData\")\n\tcurrentPartyInfo = partyInfo\n\n\n\tfor i,teleportPart in pairs(game.CollectionService:GetTagged(\"teleportPart\")) do\n\t\tupdateTeleportPart(teleportPart)\n\tend\n\n\tif partyInfo and partyInfo.teleportState == \"teleporting\" and not teleporting then\n\t\tnetwork:invoke(\"setCharacterArrested\",true)\n\n\t\tlocal partyTeleportInfo = {party_guid = partyInfo.guid; partyLeaderUserId = getPartyLeaderUserId(partyInfo)}\n\t\tteleService:SetTeleportSetting(\"partyInfo\", partyTeleportInfo)\n\n\t\tif not currentPartyInfo.teleportDestination then\n\t\t\terror(\"AHHHHHH PANIC NO TELEPORT DESTINATION WHY HAVE YOU FORSAKEN ME LIKE THIS\")\n\t\tend\n\n\n\t\tteleporting = true\n\n\t\tlocal teleportUI = prepTeleportUI(currentPartyInfo.teleportDestination)\n\n\t\tnetwork:fireServer(\"signal_playerReadyToGroupTeleport\")\n\n\t\t--[[\n\n\t\tlocal Timestamp, teleportUI = prepDataForTeleport(currentPartyInfo.teleportDestination)\n\t\tif Timestamp then\n\n\n\n\t\telse\n\t\t\t-- failed to save data, abort teleportation\n\t\tend\n\n\t\t]]\n\n\tend\n\nend\n\nupdatePartyInfo()\nnetwork:connect(\"signal_myPartyDataChanged\", \"OnClientEvent\", updatePartyInfo)\n\n\n-- super hacky but so are humanoids so sue me\nfor i=1,3 do\n\twait()\n\tnetwork:fire(\"forceSpawn\")\nend\n\n-- wait for data to report analytics\nPlayer:WaitForChild(\"DataLoaded\", 60)\n\n-- Begin analytics setup\n\n\nif sessionId and joinTime then\n--\tnetwork:invokeServer(\"requestContinueSession\",sessionId,joinTime)\nelse\n--\tnetwork:invokeServer(\"requestNewSession\")\nend\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/contents/treasureChests.client.lua",
    "content": "-- Author: berezaa\n-- Handles chest opening and makes previously-accessed chests already open\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal modules = require(replicatedStorage:WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\nlocal tween = modules.load(\"tween\")\nlocal utilities = modules.load(\"utilities\")\n\ngame.Players.LocalPlayer:WaitForChild(\"dataLoaded\", 60)\n\nlocal treasureChests = {}\nlocal billboards = {}\n\nlocal assetsFolder = replicatedStorage:WaitForChild(\"assets\")\nlocal assetFolder = script.Parent.Parent:WaitForChild(\"assets\")\nlocal progressUi = assetFolder:WaitForChild(\"chestBillboard\")\n\nlocal function addBillboardToChest(treasureChest)\n\tif treasureChest:FindFirstChild(\"progressUi\") == nil then\n\t\tlocal ui = progressUi:Clone()\n\t\tui.ImageLabel.ImageTransparency = 1\n\t\tui.TextLabel.TextTransparency = 1\n\t\tui.TextLabel.TextStrokeTransparency = 1\n\t\tui.Adornee = treasureChest.PrimaryPart\n\t\tlocal totalChests = 0\n\t\tlocal openedChests = 0\n\t\tfor _, chestInfo in pairs(treasureChests) do\n\t\t\ttotalChests = totalChests + 1\n\t\t\tif chestInfo.open then\n\t\t\t\topenedChests = openedChests + 1\n\t\t\tend\n\t\tend\n\t\tui.TextLabel.Text = tostring(openedChests) .. \"/\" .. tostring(totalChests)\n\t\tui.Enabled = true\n\t\tui.Parent = treasureChest\n\t\ttween(ui.ImageLabel, {\"ImageTransparency\"}, 0, 1)\n\t\ttween(ui.TextLabel, {\"TextTransparency\",\"TextStrokeTransparency\"}, 0, 1)\n\t\ttable.insert(billboards, ui)\n\tend\nend\n\nlocal INTERVAL = 30 * 60 * 24\n\nlocal function getTime()\n\t-- 7AM/PM PT, 10AM/PM ET\n\treturn os.time() - INTERVAL / 12\nend\n\nlocal playerTreasureData = network:invoke(\"getCacheValueByNameTag\", \"treasure\")\n\nnetwork:connect(\"propogationRequestToSelf\", \"Event\", function(key, data)\n\tif key == \"treasure\" then\n\t\tplayerTreasureData = data\n\tend\nend)\n\n-- create model for chest and add it to world, hide bounding box\nlocal function registerTreasureChest(chestRoot)\n\tlocal isOldStyle = game.CollectionService:HasTag(chestRoot.PrimaryPart, \"interact\")\n\n\tlocal chestPropsModule = chestRoot:FindFirstChild(\"chestProps\") or assetsFolder.defaultChestProps\n\tlocal chestProps = require(chestPropsModule)\n\tlocal defaultChestProps = require(assetsFolder.defaultChestProps)\n\n\t-- wierd assignment here. It's getting the chest model from the chest props, or falling back on default if undefined\n\t-- this should probably be cleaned up but it's 12:50AM and im tired\n\t-- ~ nimblz\n\tlocal chestModel = assetsFolder.chests:FindFirstChild(\n\t\tchestProps.chestModel or defaultChestProps.chestModel\n\t) or assetsFolder.chests:FindFirstChild(\"defaultChest\")\n\n\n\tif isOldStyle then\n\t\tchestModel = chestRoot\n\telse\n\t\tchestModel = chestModel:Clone()\n\n\t\tchestModel.Parent = chestRoot\n\tend\n\n\tlocal animationController = chestModel:WaitForChild(\"AnimationController\")\n\tlocal chestOpenAnimation = chestModel:WaitForChild(\"chestOpen\")\n\tlocal chestOpenLoopAnimation = chestModel:WaitForChild(\"chestOpenLoop\")\n\n\tlocal chestLockedTrack = Instance.new(\"Animation\", chestModel)\n\tchestLockedTrack.Name = \"chestLocked\"\n\tchestLockedTrack.AnimationId = \"rbxassetid://3916391981\"\n\n\tlocal openTrack = animationController:LoadAnimation(chestOpenAnimation);\n\tlocal openLoopTrack = animationController:LoadAnimation(chestOpenLoopAnimation);\n\tlocal lockedTrack = animationController:LoadAnimation(chestLockedTrack)\n\n\topenTrack.Looped = false\n\topenTrack.Priority = Enum.AnimationPriority.Action\n\n\tlockedTrack.Looped = false\n\tlockedTrack.Priority = Enum.AnimationPriority.Action\n\n\topenLoopTrack.Looped = true\n\topenLoopTrack.Priority = Enum.AnimationPriority.Core\n\n\tlocal chestRootPart = chestRoot.PrimaryPart or chestRoot:WaitForChild(\"RootPart\")\n\n\tlocal chestModelRootPart = chestModel.PrimaryPart\n\n\tchestModel:SetPrimaryPartCFrame(chestRootPart.CFrame * CFrame.new(0, (-chestRootPart.Size.Y/2) + (chestModelRootPart.Size.Y/2), 0))\n\n\tchestRootPart.Transparency = 1\n\n\tif not isOldStyle then\n\t\tlocal attackScript = assetFolder.attackableScript:Clone()\n\t\tattackScript.Parent = chestModelRootPart\n\n\t\tgame.CollectionService:AddTag(chestModelRootPart, \"attackable\")\n\tend\n\n\n\tlocal chest = {\n\t\tchestRoot = chestRoot;\n\t\tchestModel = chestModel;\n\t\tcontroller = animationController;\n\t\topenTrack = openTrack;\n\t\tlockedTrack = lockedTrack;\n\t\topenLoopTrack = openLoopTrack;\n\t\topen = false;\n\t}\n\n\ttreasureChests[chestRoot.Name] = chest\n\n\tlocal today = math.floor(getTime() / INTERVAL)\n\tlocal chestData = playerTreasureData[\"place-\"..game.PlaceId].chests[chestModel.Name]\n\tlocal specialContents = chestModel:FindFirstChild(\"inventory\") or chestModel:FindFirstChild(\"ironChest\") or chestModel:FindFirstChild(\"goldChest\")\n\tlocal chestOpen\n\tif specialContents then\n\t\tchestOpen = chestData and chestData.open\n\telse\n\t\tchestOpen = chestData and (chestData.open >= today)\n\tend\n\tif chestOpen then\n\t\tchest.openLoopTrack:Play()\n\t\tchest.open = true\n\t\tif chestModel:FindFirstChild(\"Glow\") then\n\t\t\tchestModel.Glow.Transparency = 1\n\t\tend\n\tend\nend\n\nfor i,treasureChest in pairs(game.CollectionService:GetTagged(\"treasureChest\")) do\n\tcoroutine.wrap(function() registerTreasureChest(treasureChest) end)()\nend\ngame.CollectionService:GetInstanceAddedSignal(\"treasureChest\"):connect(registerTreasureChest)\n\n\nlocal function openTreasureChest(treasureChest)\n\tlocal chestInfo = treasureChests[treasureChest.Name]\n\tif chestInfo and not chestInfo.open then\n\t\tlocal chestModel = chestInfo.chestModel\n\t\tlocal glow = chestModel:FindFirstChild(\"Glow\")\n\n\t\tutilities.playSound(\"chest_unlock\", chestModel.PrimaryPart)\n\n\t\tlocal lock = chestModel:FindFirstChild(\"Lock\")\n\t\tif lock then\n\t\t\ttween(lock, {\"Transparency\"}, 1, 1)\n\t\tend\n\n\t\tlocal rewards, status = network:invokeServer(\"playerRequest_openTreasureChest\", treasureChest)\n\n\t\tif status then\n\t\t\treturn nil, status\n\t\tend\n\n\t\tchestInfo.open = true\n\n\t\tlocal track\n\t\tif rewards then\n\t\t\ttrack = chestInfo.openTrack\n\t\telse\n\t\t\ttrack = chestInfo.lockedTrack\n\t\tend\n\n\t\tif rewards then\n\t\t\ttrack:Play()\n\t\t\ttrack.KeyframeReached:connect(function(key)\n\t\t\t\tif key == \"opened\" then\n\t\t\t\t\tchestInfo.openLoopTrack:Play()\n\t\t\t\t\tif glow then\n\t\t\t\t\t\tglow.Transparency = 0\n\t\t\t\t\t\ttween(glow,{\"Transparency\"},1,1)\n\t\t\t\t\t\tif glow:FindFirstChild(\"ParticleEmitter\") then\n\t\t\t\t\t\t\tglow.ParticleEmitter.Color = ColorSequence.new(glow.Color)\n\t\t\t\t\t\t\tglow.ParticleEmitter:Emit(50)\n\t\t\t\t\t\tend\n\t\t\t\t\tend\n\n\t\t\t\t\tutilities.playSound(\"chest_reward\", glow)\n\t\t\t\tend\n\t\t\tend)\n\t--\t\taddBillboardToChest(chestModel)\n\t\telseif status and typeof(status) == \"number\" then\n\t\t\tglow.Transparency = 1\n\t\t\ttrack:Play()\n\n\t\t\ttrack.KeyframeReached:connect(function(key)\n\t\t\t\tif key == \"opened\" then\n\t\t\t\t\tchestInfo.openLoopTrack:Play()\n\t\t\t\tend\n\t\t\tend)\n\t\t\tif script.Parent.Parent:FindFirstChild(\"goldChest\") or script.Parent.Parent:FindFirstChild(\"ironChest\") then\n\t\t\t\tlocal alert = {\n\t\t\t\t\ttext = \"You've already opened this chest.\";\n\t\t\t\t\ttextColor3 = Color3.new(1,1,1);\n\t\t\t\t\tbackgroundColor3 = Color3.new(0.9,0.3,0.2);\n\t\t\t\t\tbackgroundTransparency = 0;\n\t\t\t\t\ttextStrokeTransparency = 1;\n\t\t\t\t\tid = \"goldchest\"..script.Parent.Parent.Name;\n\t\t\t\t}\n\t\t--\t\tModules.notifications.alert(alert, 3)\n\t\t\t\tnetwork:fire(\"alert\", {text = alert}, 2)\n\t\t\telse\n\n\t\t\t\tfor i=0,2 do\n\t\t\t\t\tlocal alert = {\n\t\t\t\t\t\ttext = \"Chest can be opened again in \" .. utilities.timeToString(status - i);\n\t\t\t\t\t\ttextColor3 = Color3.new(1,1,1);\n\t\t\t\t\t\tbackgroundColor3 = Color3.new(0.9,0.75,0.2);\n\t\t\t\t\t\tbackgroundTransparency = 0;\n\t\t\t\t\t\ttextStrokeTransparency = 1;\n\t\t\t\t\t\tid = \"chest\"..script.Parent.Parent.Name;\n\t\t\t\t\t}\n\t\t\t--\t\tModules.notifications.alert(alert, 3)\n\t\t\t\t\tnetwork:fire(\"alert\", {text = alert}, 1)\n\t\t\t\t\twait(1)\n\t\t\t\tend\n\t\t\t\tlocal alert = {\n\t\t\t\t\ttext = \"Chest can be opened again in \" .. utilities.timeToString(status - 3);\n\t\t\t\t\ttextColor3 = Color3.new(1,1,1);\n\t\t\t\t\tbackgroundColor3 = Color3.new(0.9,0.75,0.2);\n\t\t\t\t\tbackgroundTransparency = 0;\n\t\t\t\t\ttextStrokeTransparency = 1;\n\t\t\t\t\tid = \"chest\"..script.Parent.Parent.Name;\n\t\t\t\t}\n\t\t--\t\tModules.notifications.alert(alert, 3)\n\t\t\t\tnetwork:fire(\"alert\", alert, 0.5)\n\n\t\t\tend\n\t\tend\n\t\treturn rewards, status\n\tend\n\treturn nil, \"You cant open that.\"\nend\nnetwork:create(\"openTreasureChest_client\", \"BindableFunction\", \"OnInvoke\", openTreasureChest)\n\n\n\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/modules/AeroProxy.lua",
    "content": "-- Aero Proxy\n-- Rocky28447\n-- June 27, 2020\n\n\n--[[\n\n\tA proxy module to allow  main menu Aero code to interface with\n\tthe obfuscated network module. Only include functions that the\n\tAero-side will need.\n\n\tAeroProxy.getGameSaveData()\n\tAeroProxy.renderCharacter(mask, appearanceData)\n\n]]\n\n\nlocal AeroProxy = {}\n\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal Modules = require((ReplicatedStorage.modules))\nlocal network = Modules.load(\"network\")\n\n\nfunction AeroProxy.getGameSaveData(fileNum)\n\tlocal globalDataSuccess, globalDataFromServer = network:invokeServer(\"loadGame\")\n\treturn globalDataFromServer\nend\n\n\nfunction AeroProxy.renderCharacter(mask, appearanceData, player)\n\treturn network:invoke(\"createRenderCharacterContainerFromCharacterAppearanceData\", mask, appearanceData, player)\nend\n\n\nfunction AeroProxy.getMovementAnimationForCharacter(character, animation)\n\tlocal animationController \t= character.entity:WaitForChild(\"AnimationController\")\n\tlocal currentEquipment = network:invoke(\"getCurrentlyEquippedForRenderCharacter\", character.entity)\n\n\tlocal weaponType do\n\t\tif currentEquipment[1] then\n\t\t\tweaponType = currentEquipment[1].baseData.equipmentType\n\t\tend\n\tend\n\n\treturn network:invoke(\"getMovementAnimationForCharacter\", animationController, animation, weaponType, nil)\nend\n\nreturn AeroProxy"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/modules/cannonScriptLocal.lua",
    "content": "local module = {}\n\tmodule.isActive = false\n\t\nmodule.interactPrompt = \"FIRE!\" -- prompt text\n\nmodule.instant = true\n\nlocal player = game.Players.LocalPlayer\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal network = modules.load(\"network\")\n\tlocal utilities = modules.load(\"utilities\")\n\tlocal tween = modules.load(\"tween\")\n\nfunction module.init()\n\n\t\n\tnetwork:invoke(\"setCharacterArrested\", true, script.Parent.Parent.target.CFrame * CFrame.Angles(-math.pi/2, 0, 0))\n\t-- ssss sounds\n\tnetwork:fireServer(\"signal_playerHasDecidedThatTheyWantToUseTheCannon\", script.Parent.Parent)\n\n\t\nend\n\n\nnetwork:connect(\"signal_playerReadyToBeBOOMEDByTheCannon\", \"OnClientEvent\", function(cannon)\n\tif cannon == script.Parent.Parent then\n\t\tnetwork:invoke(\"setCharacterArrested\", false)\n\t\t\n\t\tlocal cannonStrength = 280\n\t\tif script.Parent:FindFirstChild(\"strength\") then\n\t\t\tcannonStrength = script.Parent.strength.Value\n\t\tend\n\t\tnetwork:fire(\"applyJoltVelocityToCharacter\", script.Parent.Parent.target.CFrame.LookVector * cannonStrength)\n\t\n\tend\nend)\n\nfunction module.close()\n\t\nend\n\nreturn module"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/modules/doorScript.lua",
    "content": "local module = {}\n\tmodule.isActive = false\n\n\nif script.Parent.Name == \"exit\" then\n\tmodule.interactPrompt = \"Exit\"\nelse\n\tmodule.interactPrompt = \"Enter\"\nend\n -- prompt text\n\nmodule.instant = true\n\nlocal player = game.Players.LocalPlayer\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal network = modules.load(\"network\")\n\tlocal utilities = modules.load(\"utilities\")\n\nlocal function getTargetDoor()\n\tfor i,door in pairs(game.CollectionService:GetTagged(\"door\")) do\n\t\tif door ~= script.Parent and door.Parent and door.Parent.Name == script.Parent.Parent.Name then\n\t\t\treturn door\n\t\tend\n\tend\nend\n\nfunction module.init()\n\n\tlocal target = getTargetDoor()\n\tif target and player.Character and player.Character.PrimaryPart then\n\t\tif game.ReplicatedStorage.assets.sounds:FindFirstChild(\"door\") then\n\t\t\tutilities.playSound(\"door\", player.Character.PrimaryPart)\n\t\tend\n\t\tplayer.Character:SetPrimaryPartCFrame(player.Character.PrimaryPart.CFrame - player.Character.PrimaryPart.Position + target.Position + target.CFrame.lookVector * 8)\n\tend\n\nend\n\n\nfunction module.close()\n\nend\n\nreturn module\n--"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/modules/escapeRope.lua",
    "content": "local module = {}\n\tmodule.isActive = false\n\t\n\n\tmodule.interactPrompt = \"ESCAPE\"\n\n -- prompt text\n\nmodule.instant = true\n\nlocal player = game.Players.LocalPlayer\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal network = modules.load(\"network\")\n\nlocal function getTargetDoor()\n\treturn script.Parent.Parent.Target\nend\n\nfunction module.init()\n\t\n\tlocal target = getTargetDoor()\n\tif target and player.Character and player.Character.PrimaryPart then\n\t\tif game.ReplicatedStorage.assets.sounds:FindFirstChild(\"ladder\") then\n\t\t\tgame.ReplicatedStorage.assets.sounds.ladder:Play()\n\t\tend\n\t\t--player.Character:SetPrimaryPartCFrame(player.Character.PrimaryPart.CFrame - player.Character.PrimaryPart.Position + target.Position)\n\t\tnetwork:fireServer(\"playerRequest_activateEscapeRope\", script.Parent)\n\tend\n\t\nend\n\n\nfunction module.close()\n\t\nend\n\nreturn module\n--"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/modules/firepitLocal.lua",
    "content": "local module = {}\n\tmodule.isActive = false\n\t\nmodule.interactPrompt = \"Ignite\" -- prompt text\n\nmodule.instant = true\n\nlocal player = game.Players.LocalPlayer\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal network = modules.load(\"network\")\n\nfunction module.init()\n\tnetwork:fireServer(\"igniteFirePit\",script.Parent)\t\nend\n\n\nfunction module.close()\n\t\nend\n\nreturn module\n--"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/modules/loadUiControl.lua",
    "content": "return function()\n\nspawn(function()\n\tscript.Parent:WaitForChild(\"blackout\")\n\tscript.Parent.blackout.Visible = true\n\tscript.Parent.blackout.BackgroundTransparency = 0\nend)\n\nlocal customizeTable\n\nlocal runService = game:GetService(\"RunService\")\n\nlocal loading = true\nlocal loadingassets = true\n--[[\nspawn(function()\n\tscript.Parent:WaitForChild(\"spinner\")\n\twhile loading do\n\t\tscript.Parent.spinner.Rotation = script.Parent.spinner.Rotation + 2\n\t\trunService.RenderStepped:wait()\n\tend\n\tscript.Parent.spinner.Visible = false\nend)\n]]\n\nlocal assets = game.ReplicatedStorage:WaitForChild(\"assets\")\n\nlocal function loadassets()\n\tlocal contentProvider \t= game:GetService(\"ContentProvider\")\n\tlocal teleService\t\t= game:GetService(\"TeleportService\")\n\n\t-- no need to load assets if you are arriving from a teleport\n\tlocal arrivingFrom = teleService:GetTeleportSetting(\"arrivingTeleportId\")\n\tif arrivingFrom and (arrivingFrom ~= 2015602902 and arrivingFrom ~= 2376885433) then\n\t\treturn false\n\tend\n\n\tlocal contentList = {}\n\n\ttable.insert(contentList, game.ReplicatedStorage:WaitForChild(\"characterAnimations\"))\n\ttable.insert(contentList, assets:WaitForChild(\"sounds\"))\n\ttable.insert(contentList, game:GetService(\"StarterGui\"))\n--\ttable.insert(contentList, game.ReplicatedStorage:WaitForChild(\"itemData\"))\n--\ttable.insert(contentList, game.ReplicatedStorage:WaitForChild(\"abilityLookup\"))\n\ttable.insert(contentList, assets:WaitForChild(\"accessories\"))\n\n\tspawn(function()\n\t\tscript.Parent.spinner.Visible = true\n\t\tlocal maxQueueSize = contentProvider.RequestQueueSize\n\t\twhile loadingassets do\n\t\t\tlocal queueSize = contentProvider.RequestQueueSize\n\t\t\tif queueSize > maxQueueSize then\n\t\t\t\tmaxQueueSize = queueSize\n\t\t\tend\n\n\t\t\tlocal loadedAssetCount = maxQueueSize - queueSize\n\n--\t\t\tcontents.value.Text = tostring(loadedAssetCount) .. \"/\" .. tostring(maxQueueSize)\n\n\t\t\tscript.Parent.spinner.Rotation = script.Parent.spinner.Rotation + 2\n\t\t\trunService.RenderStepped:wait()\n\t\tend\n\tend)\n\n\t-- make sure the loading UI is loaded in\n\tcontentProvider:PreloadAsync({script.Parent})\n\n\tcontentProvider:PreloadAsync(contentList)\n\n\n\tscript.Parent.spinner.Image = \"rbxgameasset://accept\"\n\tscript.Parent.spinner.ImageColor3 = Color3.fromRGB(132, 255, 98)\n\tscript.Parent.spinner.Rotation = 0\n\tscript.Parent.blackout.BorderColor3 = Color3.fromRGB(132, 255, 98)\n\n\tloadingassets = false\nend\n\nwait()\n\nlocal player = game.Players.LocalPlayer\nlocal rank = player:GetRankInGroup(4238824)\nlocal isAdmin = true--rank >=5 or player:IsInGroup(5018342)\nlocal isLegend = rank >= 2\n\nif not isAdmin then\n\tscript.Parent.menustatus.Text = \"The game is currently closed. Please come back another time.\"\n\treturn false\nend\n\nscript.Parent.blackout.TextLabel.Visible = false\n\nspawn(loadassets)\n\nscript.Parent.menustatus.Text = \"Loading game files...\"\n\nlocal replicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal modules = require(replicatedStorage.modules)\nlocal network = modules.load(\"network\")\nlocal levels = modules.load(\"levels\")\nlocal tween = modules.load(\"tween\")\nlocal configuration = modules.load(\"configuration\")\nlocal utilities = modules.load(\"utilities\")\nlocal money = require(script.Parent.money)\n\nlocal deathScreen = require(script.Parent.deathScreen.deathScreen)\n\ndeathScreen.init({network = network, levels = levels, tween = tween, utilities = utilities, money = money })\n\n--local isAdmin = game.Players.LocalPlayer:GetRankInGroup(4238824) >=3 or game.Players.LocalPlayer:IsInGroup(5018342)\n\nlocal isAdmin = true--game.Players.LocalPlayer:GetRankInGroup(4238824) >=5\nif not isAdmin then\n\twait(5)\n\tscript.Parent.main.Visible = false\n\tscript.Parent.landing.Visible = false\n\tscript.Parent.leftBar.Visible = false\n\twait(1)\n\ttween(script.Parent.blackout, {\"BackgroundTransparency\"},{1},3 )\n\tscript.Parent.menustatus.Text = \"The game is currently closed. Please check back later.\"\n\treturn false\nend\n\nscript.Parent.menustatus.Text = \"Loading data from server...\"\n\nlocal globalDataSuccess, globalDataFromServer = network:invokeServer(\"loadGame\")\n\nlocal globalData\nif globalDataSuccess then\n\tglobalData = globalDataFromServer\nelse\n\tscript.Parent.menustatus.Text = \"Failed to load data.\"\n\twait(3)\nend\n\nloading = false\n\nscript.Parent.Notice.Visible = false\nscript.Parent.menustatus.Visible = false\nscript.Parent.blackout.Visible = true\nscript.Parent.blackout.TextLabel.Visible = false\n--script.Parent.Enabled = true\nscript.Parent.landing.Visible = false\nscript.Parent.customize.Visible = false\nscript.Parent.main.Visible = false\nscript.Parent.onlineFriends.Visible = false\nscript.Parent.main.Frame.PlayButton.Visible = false\n\nlocal input = require(script.Parent:WaitForChild(\"input\"))\n\nif input.mode.Value == \"xbox\" or game:GetService(\"UserInputService\").GamepadEnabled then\n\tgame.GuiService.GuiNavigationEnabled = true\n\tgame.GuiService.AutoSelectGuiEnabled = true\n\tgame.GuiService.SelectedObject = script.Parent.landing.play\nend\n\nlocal blur\nlocal mainCharacter\n\n-- scan for friends every minute\nspawn(function()\n\tlocal friendsTag = {}\n\twhile true do\n\t\t--print(\"!!getting friends\")\n\t\tlocal friendInfo\n\t\tlocal success, fail = pcall(function()\n\t\t\tfriendInfo = game.Players.LocalPlayer:GetFriendsOnline()\n\t\tend)\n\t\tif success then\n\t\t\t--print(\"!!starting Handshake\")\n\t\t\tlocal friendsInfo = network:invokeServer(\"fetchPlayerFriendsInfo\", friendInfo)\n\t\t\t--print(\"!!finished handshake\",#friendsInfo)\n\t\t\tif friendsInfo then\n\t\t\t\tfor i,tag in pairs(friendsTag) do\n\t\t\t\t\tif tag then\n\t\t\t\t\t\ttag:Destroy()\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\t\tfriendsTag = {}\n\t\t\t\tfor i,friend in pairs(friendsInfo) do\n\t\t\t\t\tlocal tag = script.Parent.onlineFriends.SampleFriend:Clone()\n\t\t\t\t\ttag.username.Text = friend.UserName\n\t\t\t\t\ttag.location.Text = friend.LastLocation\n\t\t\t\t\ttag.thumbnail.Image = \"https://www.roblox.com/headshot-thumbnail/image?userId=\".. friend.VisitorId ..\"&width=100&height=100&format=png\"\n\t\t\t\t\ttag.Parent = script.Parent.onlineFriends\n\t\t\t\t\ttag.Visible = true\n\t\t\t\t\ttable.insert(friendsTag,tag)\n\t\t\t\tend\n\t\t\t\tscript.Parent.onlineFriends.title.Visible = false\n\t\t\t\tif #friendsTag > 0 then\n\t\t\t\t\tscript.Parent.onlineFriends.title.Visible = true\n\t\t\t\tend\n\t\t\tend\n\t\t\t--print(\"!!Finished logic\")\n\t\t\twait(30)\n\t\telse\n\t\t\twarn(\"GetOnlineFriends failed!\")\n\t\t\twait(10)\n\t\tend\n\tend\nend)\n\n\nfunction noticeskip()\n\tscript.Parent.Notice.Visible = false\n\tscript.Parent.main.Visible = true\n\tspawn(function()\n\t\tlocal serverMessage = require(script.Parent.main.serverMessage.serverMessage)\n\t\tserverMessage.init()\n\tend)\n\tscript.Parent.onlineFriends.Visible = true\n\n\tif input.mode.Value == \"xbox\" then\n\t\tgame.GuiService.GuiNavigationEnabled = true\n\t\tgame.GuiService.SelectedObject = input.getBestButton(script.Parent.main.Frame)\n\tend\n\n--\tnetwork:invoke(\"lockCameraPosition\",script.Parent.CameraMainPos.Value,0.5)\n--\tif blur then\n--\t\ttween(blur, {\"Size\"}, 0, 0.5)\n--\tend\nend\n\n\nfunction land()\n\tscript.Parent.landing.Visible = false\n\tgame.StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Chat, true)\n\tgame.StarterGui:SetCore(\"ChatBarDisabled\", false)\n\tgame.StarterGui:SetCore(\"ChatActive\", true)\n\tgame.StarterGui:SetCore(\"ChatWindowPosition\", UDim2.new(0.7,-10,0.7,-30))\n\tscript.Parent.Notice.Visible = true\n\tnoticeskip()\n\n\tif input.mode.Value == \"xbox\" then\n\t\tgame.GuiService.GuiNavigationEnabled = true\n\t\tgame.GuiService.SelectedObject = input.getBestButton(script.Parent.Notice)\n\tend\nend\n\nland()\n\nscript.Parent.Notice.ok.MouseButton1Click:connect(noticeskip)\ngame.Players.LocalPlayer.PlayerGui:SetTopbarTransparency(1)\n--game:GetService(\"StarterGui\"):SetCore(\"TopbarEnabled\", false)\n\nlocal function updateInputMode()\n\tif input.mode.Value == \"xbox\" then\n\t\tscript.Parent.customize.buttons.UIListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center\n\t\tscript.Parent.customize.shirtColor.UIGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center\n\t\tscript.Parent.customize.hairColor.UIGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center\n\t\tscript.Parent.customize.hairColor.Position = UDim2.new(0.5,0,0,90)\n\t\tscript.Parent.customize.hairColor.AnchorPoint = Vector2.new(0.5,0)\n\t\tscript.Parent.customize.shirtColor.Position = UDim2.new(0.5,0,0,90)\n\t\tscript.Parent.customize.shirtColor.AnchorPoint = Vector2.new(0.5,0)\n\telse\n\t\tscript.Parent.customize.buttons.UIListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Left\n\t\tscript.Parent.customize.shirtColor.UIGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Left\n\t\tscript.Parent.customize.hairColor.UIGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Left\n\t\tscript.Parent.customize.hairColor.Position = UDim2.new(0,5,0,90)\n\t\tscript.Parent.customize.hairColor.AnchorPoint = Vector2.new(0,0)\n\t\tscript.Parent.customize.shirtColor.Position = UDim2.new(0,5,0,90)\n\t\tscript.Parent.customize.shirtColor.AnchorPoint = Vector2.new(0,0)\n\tend\nend\n\ninput.mode.Changed:connect(updateInputMode)\nupdateInputMode()\n\nscript.Parent:WaitForChild(\"CameraLookat\")\nscript.Parent:WaitForChild(\"CameraOrigin\")\n\n--network:invoke(\"lockCameraPosition\",script.Parent.CameraMainPos.Value)\n\nlocal runService = game:GetService(\"RunService\")\nlocal lookup = assets:WaitForChild(\"accessories\")\nlocal coolCamera = true\n--[[\nspawn(function()\n\twhile coolCamera do\n\t\tlocal camSize = workspace.CurrentCamera.ViewportSize\n\t\tlocal ray = workspace.CurrentCamera:ScreenPointToRay(camSize.X,camSize.Y,300)\n\t\tlocal hitpart, hitpos = workspace:FindPartOnRay(ray)\n\t\tif hitpos then\n\t\t\tlocal lookat = script.Parent.CameraLookat.Value\n\t\t\tlookat = Vector3.new(hitpos.x + lookat.x, hitpos.y + lookat.y, hitpos.z + lookat.z)/2\n\t\t\tworkspace.CurrentCamera.CFrame = CFrame.new(script.Parent.CameraOrigin.Value, lookat)\n\t\tend\n\t\trunService.RenderStepped:wait()\n\tend\nend)\n]]\n\nlocal renderedItems = Instance.new(\"Folder\")\nrenderedItems.Name = \"renderedOptions\"\nrenderedItems.Parent = workspace\n\nlocal characterRender\nlocal rand = Random.new(os.time())\n\nlocal characterTable = {}\n\nrenderedItems.DescendantAdded:connect(function(object)\n\tif object.Name == \"bodyPart\" then\n\t\tlocal part = object.Parent\n\t\tpart.Color = lookup:FindFirstChild(\"skinColor\"):FindFirstChild(tostring(characterTable.accessories.skinColorId)).Value\n\telseif object.Name == \"hair_Head\" and object:IsA(\"BasePart\") then\n\t\tobject.Color = lookup:FindFirstChild(\"hairColor\"):FindFirstChild(tostring(characterTable.accessories.hairColorId)).Value\n\telseif object.Name == \"shirt\" or object.Name == \"shirtTag\" then\n\t\tif object.Name == \"shirtTag\" then\n\t\t\tobject = object.Parent\n\t\tend\n\t\tif object:IsA(\"BasePart\") then\n\t\t\tobject.Color = lookup:FindFirstChild(\"shirtColor\"):FindFirstChild(tostring(characterTable.accessories.shirtColorId)).Value\n\t\tend\n\tend\nend)\n\nrepeat wait() until not loadingassets\nwait(1)\n\ntween(script.Parent.blackout,{\"BackgroundTransparency\"},1,1)\n\nlocal pastLanding\n\nlocal function getItemFromHitpart(hitpart)\n\tfor i,renderItem in pairs(renderedItems:GetChildren()) do\n\t\tif hitpart:IsDescendantOf(renderItem) then\n\t\t\treturn renderItem\n\t\tend\n\tend\nend\n\nlocal selectedItem\n\nlocal function selectItem(item)\n\tif selectedItem and selectedItem.Parent then\n\t\tselectedItem.PrimaryPart.Transparency = 1\n\t\tselectedItem = nil\n\tend\n\tif item then\n\t\tselectedItem = item\n\t\titem.PrimaryPart.Transparency = 0.3\n\tend\nend\n\n\n\n\n\n\n\nlocal inputModule = input\n\nlocal _input\nfunction InputChanged(input, processed)\n\tif input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then\n\t\t_input = input\n\tend\nend\n\nlocal cameraTravelTime = 3\nlocal cameraTravelDistance = 15\nlocal cameraTargetGoal = CFrame.new(script.Parent.CameraOrigin.Value, script.Parent.CameraLookat.Value)\nlocal cameraTargetStart = cameraTargetGoal -cameraTargetGoal.lookVector*cameraTravelDistance\nlocal cameraTarget = cameraTargetStart\n\n\n\n\nlocal startTime = tick()\n\nrunService:BindToRenderStep(\"render\", Enum.RenderPriority.Camera.Value-1, function()\n\n\tlocal arrived\n\tif tick() - startTime < cameraTravelTime then\n\t\tlocal movementVector = cameraTargetGoal.lookVector*cameraTravelDistance\n\t\tlocal t = math.clamp((tick()-startTime)/cameraTravelTime, 0, 1)\n\t\tcameraTarget = cameraTargetStart + movementVector * t^(1/7)\n\telseif not arrived then\n\t\tarrived = true\n\t\tcameraTarget = cameraTargetGoal\n\tend\n\n\tlocal camSway = 0\n\n\tlocal hitpart, hitpos\n\n\n\n\tif --[[pastLanding and renderedItems and]] script.Parent.customize.Visible then\n\n\t\tcameraTarget = script.Parent.CameraTablePos.Value\n\t\tif cameraTarget == script.Parent.CameraTablePos.Value then\n\t\t\tcamSway = 50\n\t\tend\n\t\tif _input then\n\t\t\tworkspace.CurrentCamera.CFrame = script.Parent.CameraTablePos.Value\n\t\t\tlocal ray = workspace.CurrentCamera:ScreenPointToRay(_input.Position.X,_input.Position.Y,0)\n\t\t\tlocal renderedChildren = renderedItems:GetChildren()\n\t\t\t\tif #renderedChildren > 0 and (inputModule.mode.Value == \"pc\" or inputModule.mode.Value == \"mobile\") then\n\n\t\t\t\tlocal hitpart, hitpos = workspace:FindPartOnRayWithWhitelist(Ray.new(ray.Origin, ray.Direction * 250), renderedChildren, true)\n\t\t\t\tif hitpart then\n\n\t\t\t\t\tlocal item = getItemFromHitpart(hitpart)\n\t\t\t\t\tselectItem(item)\n\t\t\t\telse\n\n\t\t\t\t\tselectItem()\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\t\t-- add camera sway effect, to a lessor extent\n\telseif not pastLanding then\n\t\tcamSway = 100\n\tend\n\n\tif camSway > 0 then\n\t\tif _input and arrived then\n\t\t\tlocal ray = workspace.CurrentCamera:ScreenPointToRay(_input.Position.X,_input.Position.Y,camSway)\n\t\t\thitpart, hitpos = workspace:FindPartOnRay(ray)\n\t\t\tif hitpos then\n\t\t\t\tlocal lookat = cameraTarget.Position + cameraTarget.lookVector * 50\n\t\t\t\tlookat = Vector3.new(hitpos.x + lookat.x * 25, hitpos.y + lookat.y * 25, hitpos.z + lookat.z * 25)/26\n\t\t\t\t--workspace.CurrentCamera.CFrame = CFrame.new(cameraTarget.Position, lookat)\n\t\t\t\t_input = nil\n--\t\t\t\tnetwork:invoke(\"lockCameraPosition\",CFrame.new(cameraTarget.Position, lookat), 0.2)\n\t\t\tend\n\t\telseif not arrived then\n--\t\t\tnetwork:invoke(\"lockCameraPosition\",CFrame.new(cameraTarget.Position, cameraTarget.Position + cameraTarget.lookVector))\n\t\tend\n\tend\nend)\n\nlocal connection = game:GetService(\"UserInputService\").InputChanged:connect(InputChanged)\n\n\n\nlocal mouseDownTime = 0\n\n\n\n\n\ngame:GetService(\"UserInputService\").InputBegan:connect(function(input, absorbed)\n\tif not absorbed and (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) then\n\t\tmouseDownTime = tick()\n\t\tInputChanged(input, absorbed)\n\tend\nend)\n\nlocal currentCategory\n\n\ngame:GetService(\"UserInputService\").InputEnded:connect(function(input, absorbed)\n\tif not absorbed and (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) and tick() - mouseDownTime <= 2 then\n\t\tif selectedItem then\n\t\t\t--print(\"WOAH\")\n\t\t\tlocal currentDisplayCategory = currentCategory\n\t\t\tif currentDisplayCategory == \"skinColor\" then\n\t\t\t\tcurrentDisplayCategory = \"skinColorId\"\n\t\t\tend\n\t\t\tcharacterTable.accessories[currentDisplayCategory] = tonumber(selectedItem.Name)\n\n\t\t\tnetwork:invoke(\"applyCharacterAppearanceToRenderCharacter\", characterRender.entity, characterTable)\n\t\tend\n\tend\nend)\n\nlocal referralFrame = script.Parent.Notice.content.referral\n\nlocal referralPending\n\nscript.Parent.Notice.content.referral.invite.Frame.send.Activated:connect(function()\n\tif not referralPending then\n\t\treferralPending = true\n\t\treferralFrame.invite.Visible = false\n\t\treferralFrame.status.Visible = true\n\t\treferralFrame.status.wait.Visible = true\n\t\treferralFrame.status.status.Visible = false\n\t\tlocal success, status = network:invokeServer(\"sendReferral\", referralFrame.invite.Frame.code.TextBox.Text)\n\t\tif game.Players.LocalPlayer:FindFirstChild(\"acceptedReferral\") == nil then\n\t\t\treferralFrame.status.wait.Visible = false\n\t\t\treferralFrame.status.status.Visible = true\n\t\t\treferralFrame.status.status.Text = status\n\t\t\tif not success then\n\t\t\t\twait(3)\n\t\t\t\treferralFrame.status.Visible = false\n\t\t\t\treferralFrame.invite.Visible = true\n\t\t\tend\n\t\tend\n\t\treferralPending = false\n\tend\nend)\n\ngame.Players.LocalPlayer.ChildAdded:connect(function(Child)\n\tif Child.Name == \"acceptedReferral\" then\n\t\treferralFrame.status.status.Text = \"Referral accepted! Play now to recieve 200 free Ethyr.\"\n\t\treferralFrame.status.Visible = true\n\t\treferralFrame.invite.Visible = false\n\t\treferralFrame.status.status.Visible = true\n\t\treferralFrame.status.wait.Visible = false\n\tend\nend)\nlocal closing\ngame.Players.LocalPlayer.ChildRemoved:connect(function(Child)\n\tif referralFrame.status.Visible and game.Players.LocalPlayer:FindFirstChild(\"acceptedReferral\") == nil and game.Players.LocalPlayer:FindFirstChild(\"messagePending\") == nil and not closing then\n\t\tclosing = true\n\t\treferralFrame.status.status.Visible = true\n\t\treferralFrame.status.Visible = true\n\t\treferralFrame.invite.Visible = false\n\t\treferralFrame.status.wait.Visible = false\n\t\treferralFrame.status.status.Text = \"Referral timed out. Please confirm you entered the username correctly and that the player is online\"\n\t\twait(3)\n\t\tclosing = false\n\t\treferralFrame.status.Visible = false\n\t\treferralFrame.invite.Visible = true\n\tend\nend)\n\n\n\nlocal function reset()\n\tfor i,oslot in pairs(script.Parent.main.Frame.DataSlots:GetChildren()) do\n\t\tif oslot:IsA(\"ImageButton\") then\n\t\t\toslot.ImageColor3 = Color3.fromRGB(126, 126, 126)\n\t\t\toslot.PlayerData.Frame.ImageColor3 = Color3.fromRGB(36, 36, 36)\n\n\t\tend\n\tend\nend\n\nlocal playButton = script.Parent.main.play\n\nscript.Parent.DataSlot.Changed:Connect(function()\n\n\treset()\n\n\tlocal slot = script.Parent.DataSlot.Value\n\tlocal real = script.Parent.main.Frame.DataSlots:FindFirstChild(tostring(slot))\n--\tif slot and real and real.PlayerData.Visible then\n\tif slot and real then\n--\t\tscript.Parent.main.Frame.PlayButton.Slot.Text = \"Slot \" .. tostring(slot)\n\t\tplayButton.Visible = true\n\n\t\treal.ImageColor3 = Color3.fromRGB(57, 212, 255)\n\t\treal.PlayerData.Frame.ImageColor3 = Color3.fromRGB(11, 73, 111)\n\n\n\n\n\t\tplayButton.Flutter.ImageTransparency = 0.3\n\t\tplayButton.Flutter.Size = UDim2.new(1,0,1,0)\n\n\n\n\t\tplayButton.Flutter.Visible = true\n\t\ttween(playButton.Flutter,{\"Size\",\"ImageTransparency\"},{UDim2.new(3,0,3,0),1},0.3)\n\telse\n\t\tplayButton.Visible = false\n\tend\nend)\n\nlocal teleporting = false\n\n\nlocal debounce = false\n\nlocal function getModelAveragePosition(model)\n\tlocal preavg = Vector3.new()\n\tlocal totalcount = 0\n\tfor i,part in pairs(model:GetChildren()) do\n\t\tif part:IsA(\"BasePart\") then\n\t\t\tpreavg = preavg + part.Position\n\t\t\ttotalcount = totalcount + 1\n\t\tend\n\tend\n\treturn preavg / totalcount\nend\n\n\n\nlocal function updateColors()\n\tfor i,object in pairs(renderedItems:GetDescendants()) do\n\t\tif object.Name == \"bodyPart\" then\n\t\t\tlocal part = object.Parent\n\t\t\tpart.Color = lookup:FindFirstChild(\"skinColor\"):FindFirstChild(tostring(characterTable.accessories.skinColorId or 1)).Value\n\t\telseif object.Name == \"hair_Head\" and object:IsA(\"BasePart\") then\n\t\t\tobject.Color = lookup:FindFirstChild(\"hairColor\"):FindFirstChild(tostring(characterTable.accessories.hairColorId or 1)).Value\n\t\telseif object.Name == \"shirt\" or object:FindFirstChild(\"shirtTag\") then\n\t\t\tobject.Color = lookup:FindFirstChild(\"shirtColor\"):FindFirstChild(tostring(characterTable.accessories.shirtColorId or 1)).Value\n\t\tend\n\tend\n\tnetwork:invoke(\"applyCharacterAppearanceToRenderCharacter\", characterRender.entity, characterTable)\nend\n\n\nlocal function createRepresentationOfItem(item)\n\n\tlocal repre\n\n\tif item:IsA(\"Color3Value\") then\n\t\t--print(\"ello\")\n\t\trepre = assets.entities.colorRepre:Clone()\n\t\trepre.value.Color = item.Value\n\t\trepre.Name = item.Name\n\telse\n\t\trepre = item:Clone()\n\tend\n\n\n\tlocal avgpos = getModelAveragePosition(repre)\n\t-- reparent everything to root\n\tfor i,part in pairs(repre:GetDescendants()) do\n\t\tif part:IsA(\"BasePart\") then\n\t\t\tif part.Parent == repre and part:FindFirstChild(\"colorOverride\") == nil then\n\t\t\t\tlocal bodyPartTag = Instance.new(\"BoolValue\")\n\t\t\t\tbodyPartTag.Name = \"bodyPart\"\n\t\t\t\tbodyPartTag.Parent = part\n\t\t\telse\n\t\t\t\tpart.Parent = repre\n\t\t\tend\n\t\t\tpart.Anchored = true\n\t\tend\n\tend\n\t-- create a PrimaryPart using the avgPos\n\tlocal primaryPart = Instance.new(\"Part\")\n\tprimaryPart.Size = Vector3.new(2,0.5,2)\n\tprimaryPart.CFrame = CFrame.new(avgpos - Vector3.new(0,2,0))\n\tprimaryPart.Parent = repre\n\tprimaryPart.Anchored = true\n\tprimaryPart.TopSurface = Enum.SurfaceType.Smooth\n\tprimaryPart.Material = Enum.Material.Neon\n\tprimaryPart.Transparency = 1\n\n\tlocal immuneTag = Instance.new(\"BoolValue\")\n\timmuneTag.Name = \"colorOverride\"\n\timmuneTag.Parent = primaryPart\n\n\trepre.PrimaryPart = primaryPart\n\treturn repre\nend\n\nlocal displayConnection\n\nlocal lastXboxSelected\n\nlocal function generateCoolButtons()\n\tscript.Parent.customize.xboxButtons:ClearAllChildren()\n\tfor i,item in pairs(renderedItems:GetChildren()) do\n\t\tif item and item.PrimaryPart then\n\t\t\tlocal vector, onScreen = workspace.CurrentCamera:WorldToScreenPoint(item.PrimaryPart.Position)\n\t\t\tif onScreen then\n\t\t\t\tlocal button = script.Parent.customize.sampleXboxButton:Clone()\n\t\t\t\tbutton.Name = item.Name\n\t\t\t\tbutton.Parent = script.Parent.customize.xboxButtons\n\t\t\t\tbutton.Visible = input.mode.Value == \"xbox\"\n\t\t\t\tbutton.Position = UDim2.new(0, vector.X, 0, vector.Y + game.GuiService:GetGuiInset().Y)\n\t\t\t\tbutton.Activated:connect(function()\n\t\t\t\t\tif input.mode.Value == \"xbox\" then\n\t\t\t\t\t\tlocal currentDisplayCategory = currentCategory\n\t\t\t\t\t\tif currentCategory == \"skinColor\" then\n\t\t\t\t\t\t\tcurrentDisplayCategory = \"skinColorId\"\n\t\t\t\t\t\tend\n\t\t\t\t\t\tcharacterTable.accessories[currentDisplayCategory] = tonumber(button.Name)\n\t\t\t\t\t\tnetwork:invoke(\"applyCharacterAppearanceToRenderCharacter\", characterRender.entity, characterTable)\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\t\tbutton.SelectionGained:connect(function()\n\t\t\t\t\tlastXboxSelected = item\n\t\t\t\t\tselectItem(item)\n\t\t\t\tend)\n\t\t\t\tbutton.SelectionLost:connect(function()\n\t\t\t\t\tif lastXboxSelected == item then\n\t\t\t\t\t\tselectItem(nil)\n\t\t\t\t\tend\n\t\t\t\tend)\n\t\t\tend\n\t\tend\n\tend\nend\n\nlocal function inputCheck()\n\tif input.mode.Value == \"mobile\" and workspace.CurrentCamera.ViewportSize.Y <= 700 then\n\t\tscript.Parent.Notice.UIScale.Scale = 0.65\n\t\tscript.Parent.main.UIScale.Scale = 0.7\n\t\tscript.Parent.main.Size = UDim2.new(1.43, 0,1.43, 0)\n\t\tscript.Parent.customize.UIScale.Scale = 0.7\n\t\tscript.Parent.customize.Size = UDim2.new(1.43, 0,1.43, 0)\n\telse\n\t\tscript.Parent.Notice.UIScale.Scale = 1\n\t\tscript.Parent.main.UIScale.Scale = 1\n\t\tscript.Parent.main.Size = UDim2.new(1, 0,1, 0)\n\t\tscript.Parent.customize.UIScale.Scale = 1\n\t\tscript.Parent.customize.Size = UDim2.new(1, 0,1, 0)\n\tend\nend\ninputCheck()\n\ninput.mode.Changed:connect(function()\n\n\tfor i,button in pairs(script.Parent.customize.xboxButtons:GetChildren()) do\n\t\tif button:IsA(\"GuiObject\") then\n\t\t\tbutton.Visible = input.mode.Value == \"xbox\"\n\t\tend\n\tend\n\n\tinputCheck()\n\nend)\n\nlocal function displayCategory(categoryName)\n\trenderedItems:ClearAllChildren()\n\tlocal items = lookup:FindFirstChild(categoryName)\n\tif items then\n\t\tlocal x = 0\n\t\tlocal z = 0\n\n\t\tlocal target = workspace:WaitForChild(\"Tabletop\")\n\n\n\t\tfor i,item in pairs(items:GetChildren()) do\n\n\t\t\tlocal repre = createRepresentationOfItem(item)\n\t\t\tlocal cf = target.CFrame * CFrame.new((z * 6) - target.Size.X/2 + 1.1, target.Size.Y/2 + (z * 3.2), (x * 2.5) - target.Size.Z/2 + 1.1)\n\t\t\trepre:SetPrimaryPartCFrame(cf)\n\t\t\trepre.Parent = renderedItems\n\n\t\t\tx = x + 1\n\t\t\tif x > 4 then\n\t\t\t\tx = 0\n\t\t\t\tz = z + 1\n\t\t\tend\n\n\t\tend\n\tend\n\tfor i,button in pairs(script.Parent.customize.buttons:GetChildren()) do\n\t\tif button:IsA(\"ImageButton\") then\n\t\t\tbutton.ImageColor3 = Color3.fromRGB(103, 255, 212)\n\t\tend\n\tend\n\tlocal newbutton = script.Parent.customize.buttons:FindFirstChild(categoryName)\n\tif newbutton then\n\t\tnewbutton.ImageColor3 = Color3.fromRGB(255, 255, 255)\n\tend\n\tcurrentCategory = categoryName\n\n\tscript.Parent.customize.hairColor.Visible = false\n\tscript.Parent.customize.shirtColor.Visible = false\n\tif categoryName == \"hair\" then\n\t\tscript.Parent.customize.hairColor.Visible = true\n\telseif categoryName == \"undershirt\" then\n\t\tscript.Parent.customize.shirtColor.Visible = true\n\tend\n\tgenerateCoolButtons()\nend\n\nfor i,button in pairs(script.Parent.customize.buttons:GetChildren()) do\n\tif button:IsA(\"ImageButton\") then\n\t\tbutton.Activated:connect(function()\n\t\t\tdisplayCategory(button.Name)\n\t\tend)\n\tend\nend\n\n\n\nlocal function playButtonActivated()\n\tif debounce then\n\t\treturn false\n\tend\n\tdebounce = true\n\tlocal slot = script.Parent.DataSlot.Value\n\tlocal real = script.Parent.main.Frame.DataSlots:FindFirstChild(tostring(slot))\n\n\n\n--\tif slot and real and real.PlayerData.Visible and not teleporting then\n\tif slot and real and not teleporting then\n\n--\t\tif real:FindFirstChild(\"customize\") and not script.Parent.customize.Visible then\n\t\tif not (globalData and globalData.saveSlotData[\"-slot\"..slot]) and not script.Parent.customize.Visible then\n\t\t\tscript.Parent.main.Visible = false\n\t\t\tscript.Parent.onlineFriends.Visible = false\n\t\t\tscript.Parent.customize.Visible = true\n\n\t\t\tif input.mode.Value == \"xbox\" then\n\t\t\t\tgame.GuiService.GuiNavigationEnabled = true\n\t\t\t\tgame.GuiService.SelectedObject = input.getBestButton(script.Parent.customize)\n\t\t\tend\n\n\t\t\tcharacterTable.accessories = {}\n\t\t\tfor i,category in pairs(lookup:GetChildren()) do\n\t\t\t\tlocal children = category:GetChildren()\n\t\t\t\tif #children > 0 then\n\t\t\t\t\tlocal categoryName = category.Name\n\t\t\t\t\tif categoryName == \"skinColor\" or categoryName == \"shirtColor\" or categoryName == \"hairColor\" then\n\t\t\t\t\t\tcategoryName = categoryName .. \"Id\"\n\t\t\t\t\tend\n\t\t\t\t\tcharacterTable.accessories[categoryName] = 1;\n\t\t\t\tend\n\n\t\t\tend\n\t\t\tdisplayCategory(\"hair\")\n\n\t\t\tfor i,color in pairs(lookup:WaitForChild(\"hairColor\"):GetChildren()) do\n\t\t\t\tif color:IsA(\"Color3Value\") then\n\t\t\t\t\tlocal buttonRepre = script.Parent.customize.colorButtonSample:Clone()\n\t\t\t\t\tbuttonRepre.ImageColor3 = color.value\n\t\t\t\t\tbuttonRepre.Name = color.Name\n\t\t\t\t\tbuttonRepre.Parent = script.Parent.customize.hairColor\n\t\t\t\t\tbuttonRepre.Visible = true\n\t\t\t\t\t-- change hairColor\n\t\t\t\t\tbuttonRepre.Activated:connect(function()\n\t\t\t\t\t\tcharacterTable.accessories[\"hairColorId\"] = tonumber(buttonRepre.Name)\n\t\t\t\t\t\tupdateColors()\n\n\t\t\t\t\tend)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tfor i,color in pairs(lookup:WaitForChild(\"shirtColor\"):GetChildren()) do\n\t\t\t\tif color:IsA(\"Color3Value\") then\n\t\t\t\t\tlocal buttonRepre = script.Parent.customize.colorButtonSample:Clone()\n\t\t\t\t\tbuttonRepre.ImageColor3 = color.value\n\t\t\t\t\tbuttonRepre.Name = color.Name\n\t\t\t\t\tbuttonRepre.Parent = script.Parent.customize.shirtColor\n\t\t\t\t\tbuttonRepre.Visible = true\n\t\t\t\t\t-- change shirtColor\n\t\t\t\t\tbuttonRepre.Activated:connect(function()\n\t\t\t\t\t\tcharacterTable.accessories[\"shirtColorId\"] = tonumber(buttonRepre.Name)\n\t\t\t\t\t\tupdateColors()\n\n\t\t\t\t\tend)\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tcharacterRender = network:invoke(\"createRenderCharacterContainerFromCharacterAppearanceData\", workspace:WaitForChild(\"characterMask\"), characterTable)\n\t\t\tcharacterRender.Parent = workspace\n\n\t\t\tlocal animationController \t= characterRender.entity:WaitForChild(\"AnimationController\")\n\t\t\tlocal currentEquipment \t\t= network:invoke(\"getCurrentlyEquippedForRenderCharacter\", characterRender.entity)\n\n\t\t\tlocal weaponType do\n\t\t\t\tif currentEquipment[1] then\n\t\t\t\t\tweaponType = currentEquipment[1].baseData.equipmentType\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal track = network:invoke(\"getMovementAnimationForCharacter\", animationController, \"idling\", weaponType, nil)\n\n\t\t\tif track then\n\t\t\t\tif typeof(track) == \"Instance\" then\n\t\t\t\t\ttrack:Play()\n\t\t\t\telseif typeof(track) == \"table\" then\n\t\t\t\t\tfor ii, obj in pairs(track) do\n\t\t\t\t\t\tobj:Play()\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\n\t\t\tworkspace.CurrentCamera.FieldOfView = 30\n\t\t\t-- Rob if you're seeing this I want you to know im in physical pain\n\t\t\t_G.Aero.Controllers.UI.CameraCFrame = script.Parent.CameraTablePos.Value\n\t\t\t_G.Aero.Controllers.UI.FileSelect.LeftPane.AnchorPoint = Vector2.new(1, 0)\n--\t\t\tnetwork:invoke(\"lockCameraPosition\",script.Parent.CameraTablePos.Value, 0.7)\n\n\t\t\tif blur then\n\t\t\t\ttween(blur,{\"Size\"},0,0.7)\n\t\t\tend\n\n--\t\t\ttween(workspace.CurrentCamera, {\"FieldOfView\"}, 30, 0.5)\n\n\t\t\twait(0.7)\n\t\t\tcameraTarget = script.Parent.CameraTablePos.Value\n\t\t\tgenerateCoolButtons()\n\n\t\telse\n\t\t\tteleporting = true\n\n\t\t\tif script.Parent.customize.Visible and characterTable and characterTable.accessories then\n\t\t\t\tgame:GetService(\"TeleportService\"):SetTeleportSetting(\"playerAccessories\",game:GetService(\"HttpService\"):JSONEncode(characterTable.accessories))\n\t\t\t\tcustomizeTable = characterTable.accessories\n\t\t\tend\n\t\t\t--[[\n\t\t\tgame:GetService(\"TeleportService\"):SetTeleportSetting(\"arrivingTeleportId\",game.PlaceId)\n\t\t\tgame:GetService(\"TeleportService\"):SetTeleportSetting(\"lastTimeStamp\",real.Data.lastTimeStamp.Value)\n\t\t\tgame:GetService(\"TeleportService\"):SetTeleportSetting(\"dataSlot\",slot)\n\n\t\t\tnetwork:invoke(\"teleportPlayerTo\",real.Data.lastLocation.Value)\t\t\t\t]]\n\t\t\tscript.Parent.Enabled = false\n\t\t\ttween(workspace.CurrentCamera, {\"FieldOfView\"}, {120}, 7)\n\n\t\t\tnetwork:invoke(\"localPrepareForTeleport\", real.Data.lastLocation.Value)\n\n\t\t\tlocal realm\n\n\t\t\tif (slot == 13 or slot == 14 or slot == 15) and isAdmin then\n\t\t\t\trealm = \"mirror\"\n\t\t\tend\n\n\t\t\tnetwork:invokeServer(\"enterGame\", real.Data.lastLocation.Value, nil, slot, customizeTable, realm)\n\n\t\tend\n\n\n\telse\n\t\tscript.Parent.main.Frame.PlayButton.Visible = false\n\tend\n\tdebounce = false\nend\n\nscript.Parent.main.Frame.PlayButton.Activated:Connect(playButtonActivated)\nscript.Parent.main.play.Activated:Connect(playButtonActivated)\nscript.Parent.customize.play.Activated:Connect(playButtonActivated)\n\n\nif input.mode.Value:lower() == \"xbox\" or game:GetService(\"UserInputService\").GamepadEnabled then\n\tgame.GuiService.GuiNavigationEnabled = true\n\tgame.GuiService.AutoSelectGuiEnabled = true\n\tgame.GuiService.SelectedObject = script.Parent.landing.play\nend\n\n\nlocal allowedSlots = 4\n\nif isAdmin then\n\tallowedSlots = 20\nelseif isLegend then\n\tallowedSlots = 10\nend\n\n\nlocal player = game.Players.LocalPlayer\n\nscript.Parent.main.Frame.DataSlots.CanvasSize = UDim2.new(0,0,0,10 + allowedSlots * (160))\n\n\nlocal frameDataPair = {}\n\nlocal function loadDataFrameLogic(Frame, data, overrideCustomize, slot)\n\n\tif Frame.character.ViewportFrame:FindFirstChild(\"entity\") then\n\t\tFrame.character.ViewportFrame.entity:Destroy()\n\tend\n\n\tif Frame.character.ViewportFrame:FindFirstChild(\"entity2\") then\n\t\tFrame.character.ViewportFrame.entity2:Destroy()\n\tend\n\n\tif data then\n\n\t\tframeDataPair[Frame] = data\n\n\n\n\t\tif (slot == 13 or slot == 14 or slot == 15) and isAdmin then\n\t\t\tFrame.Loading.Visible = false\n\t\t\tFrame.PlayerData.Level.Visible = false\n\t\t\tFrame.PlayerData.Location.Visible = false\n\t\t\tFrame.PlayerData.Class.Text = \"Tester Slot\"\n\t\t\tFrame.Data.lastLocation.Value = 3372071669\n\t\t\tFrame.PlayerData.Visible = true\n\t\t\tFrame.BGHolder.BG.Image = \"https://www.roblox.com/Thumbs/Asset.ashx?width=768&height=432&assetId=\"..Frame.Data.lastLocation.Value\n\t\t\tFrame.BGHolder.BG.Visible = true\n\t\telseif data.newb then\n\t\t\tFrame.Loading.Visible = false\n\t\t\tFrame.PlayerData.Level.Visible = false\n\t\t\tFrame.PlayerData.Location.Visible = false\n\t\t\tFrame.PlayerData.Class.Text = \"New Adventure\"\n\t\t\tFrame.Data.lastLocation.Value = 4561988219\n\t\t\tFrame.PlayerData.Visible = true\n\t\t\tFrame.BGHolder.BG.Image = \"https://www.roblox.com/Thumbs/Asset.ashx?width=768&height=432&assetId=\"..Frame.Data.lastLocation.Value\n\t\t\tFrame.BGHolder.BG.Visible = true\n\t\t\tlocal customizeTag = Instance.new(\"BoolValue\")\n\t\t\tcustomizeTag.Name = \"customize\"\n\t\t\tcustomizeTag.Parent = Frame\n\t\telse\n\t\t\tlocal lastLocation = data.lastLocation or 2064647391\n\t\t\tFrame.Data.lastLocation.Value = lastLocation\n\t\t\tFrame.Loading.Visible = false\n\t\t\tFrame.PlayerData.Level.Text = \"lv. \" ..  data.level\n\t\t\tFrame.PlayerData.Class.Text = data.class or \"???\"\n\t\t\tFrame.PlayerData.Location.Text = \"-\"\n\t\t\tFrame.BGHolder.BG.Image = \"https://www.roblox.com/Thumbs/Asset.ashx?width=768&height=432&assetId=\"..lastLocation\n\t\t\tFrame.BGHolder.BG.Visible = true\n\t\t\tspawn(function()\n\t\t\t\tlocal info = game.MarketplaceService:GetProductInfo(lastLocation, Enum.InfoType.Asset)\n\t\t\t\tif info then\n\t\t\t\t\tFrame.PlayerData.Location.Text = info.Name\n\t\t\t\tend\n\t\t\tend)\n\t\t\tFrame.Data.lastLocation.Value = lastLocation\n\t\t\tFrame.PlayerData.Visible = true\n\t\t\tif data.accessories then\n\n\n\n\t\t\t\tlocal camera = Frame.character.ViewportFrame.CurrentCamera\n\t\t\t\tif camera == nil then\n\t\t\t\t\tcamera = Instance.new(\"Camera\")\n\t\t\t\t\tcamera.Parent = Frame.character.ViewportFrame\n\t\t\t\t\tFrame.character.ViewportFrame.CurrentCamera = camera\n\t\t\t\tend\n\n\t\t\t\tlocal client = player\n\t\t\t\tlocal character = player.Character\n\t\t\t\tlocal mask = Frame.character.ViewportFrame.characterMask\n\n\t\t\t\tlocal characterAppearanceData = {}\n\t\t\t\tcharacterAppearanceData.equipment \t= data.equipment or {}\n\t\t\t\tcharacterAppearanceData.accessories = data.accessories or {}\n\n\t\t\t\tif configuration.getConfigurationValue(\"doUseProperAnimationForLoadingPlace\", player) then\n\t\t\t\t\tspawn(function()\n\t\t\t\t\t\tlocal characterRender \t= network:invoke(\"createRenderCharacterContainerFromCharacterAppearanceData\",mask, characterAppearanceData or {}, client)\n\t\t\t\t\t\tcharacterRender.Parent \t= workspace.CurrentCamera\n\n\t\t\t\t\t\tlocal animationController \t= characterRender.entity:WaitForChild(\"AnimationController\")\n\t\t\t\t\t\tlocal currentEquipment \t\t= network:invoke(\"getCurrentlyEquippedForRenderCharacter\", characterRender.entity)\n\n\t\t\t\t\t\tlocal weaponType do\n\t\t\t\t\t\t\tif currentEquipment[1] then\n\t\t\t\t\t\t\t\tweaponType = currentEquipment[1].baseData.equipmentType\n\t\t\t\t\t\t\tend\n\t\t\t\t\t\tend\n\n\t\t\t\t\t\tlocal track = network:invoke(\"getMovementAnimationForCharacter\", animationController, \"idling\", weaponType, nil)\n\n\t\t\t\t\t\tif track then\n\t\t\t\t\t\t\tif typeof(track) == \"Instance\" then\n\t\t\t\t\t\t\t\ttrack:Play()\n\t\t\t\t\t\t\telseif typeof(track) == \"table\" then\n\t\t\t\t\t\t\t\tfor ii, obj in pairs(track) do\n\t\t\t\t\t\t\t\t\tobj:Play()\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\tspawn(function()\n\t\t\t\t\t\t\t\twhile true do\n\t\t\t\t\t\t\t\t\twait(0.1)\n\n\t\t\t\t\t\t\t\t\tif typeof(track) == \"Instance\" then\n\t\t\t\t\t\t\t\t\t\tif track.Length > 0 then\n\t\t\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\telseif typeof(track) == \"table\" then\n\t\t\t\t\t\t\t\t\t\tlocal isGood = true\n\t\t\t\t\t\t\t\t\t\tfor ii, obj in pairs(track) do\n\t\t\t\t\t\t\t\t\t\t\tif track.Length == 0 then\n\t\t\t\t\t\t\t\t\t\t\t\tisGood = false\n\t\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\t\t\tif isGood then\n\t\t\t\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\t\tend\n\n\t\t\t\t\t\t\t\tif characterRender then\n\t\t\t\t\t\t\t\t\tlocal entity \t= characterRender.entity\n\t\t\t\t\t\t\t\t\tentity.Parent \t= script.Parent.ViewportFrame\n\n\t\t\t\t\t\t\t\t\tcharacterRender:Destroy()\n\n\t\t\t\t\t\t\t\t\tlocal focus \t= CFrame.new(entity.PrimaryPart.Position + entity.PrimaryPart.CFrame.lookVector * 6.3, entity.PrimaryPart.Position) * CFrame.new(-3,0,0)\n\t\t\t\t\t\t\t\t\tcamera.CFrame \t= CFrame.new(focus.p + Vector3.new(0,1.5,0), entity.PrimaryPart.Position + Vector3.new(0,0.5,0))\n\t\t\t\t\t\t\t\tend\n\t\t\t\t\t\t\tend)\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\t\t\t\telse\n\t\t\t\t\tspawn(function()\n\t\t\t\t\t\tlocal characterRender = network:invoke(\"createRenderCharacterContainerFromCharacterAppearanceData\",mask, characterAppearanceData or {}, client)\n\t\t\t\t\t\tcharacterRender.Parent = workspace.CurrentCamera\n\n\t\t\t\t\t\tlocal animationController = characterRender.entity:WaitForChild(\"AnimationController\")\n\t\t\t\t\t\tlocal track = animationController:LoadAnimation(mask.idle)\n\t\t\t\t\t\ttrack.Looped = true\n\t\t\t\t\t\ttrack.Priority = Enum.AnimationPriority.Idle\n\t\t\t\t\t\ttrack:Play()\n\n\t\t\t\t\t\twait()\n\n\t\t\t\t\t\tif characterRender then\n\t\t\t\t\t\t\tlocal entity = characterRender.entity\n\t\t\t\t\t\t\tentity.Parent = Frame.character.ViewportFrame\n\n\t\t\t\t\t\t\tcharacterRender:destroy()\n\t\t\t\t\t\t\tlocal focus = CFrame.new(entity.PrimaryPart.Position + entity.PrimaryPart.CFrame.lookVector * 6.3, entity.PrimaryPart.Position) * CFrame.new(3,0,0)\n\t\t\t\t\t\t\tcamera.CFrame = CFrame.new(focus.p + Vector3.new(0,1.5,0), entity.PrimaryPart.Position + Vector3.new(0,0.5,0))\n\n\n\t\t\t\t\t\tend\n\t\t\t\t\tend)\n\t\t\t\tend\n\t\t\telseif not overrideCustomize then\n\t\t\t\tlocal customizeTag = Instance.new(\"BoolValue\")\n\t\t\t\tcustomizeTag.Name = \"customize\"\n\t\t\t\tcustomizeTag.Parent = Frame\n\t\t\tend\n\t\tend\n\n\n\n\telse\n\n\t\tFrame.Loading.Sample.TextLabel.Text = \"Failed!\"\n\t\twait(1)\n\t\tFrame.Loading.Sample.TextLabel.Text = \"Load\"\n\tend\nend\n\nlocal petContainer, petPreviewMaskCopy\nfunction activateFrame(Frame)\n\tlocal i = tonumber(Frame.Name)\n--\tprint(\"Click\")\n\tif debounce then\n\t\treturn false\n\tend\n\tif petContainer then\n\t\tpetContainer:Destroy()\n\t\tpetContainer = nil\n\tend\n\tdebounce = true\n--\tprint(\"Clack\")\n--\t\tif Frame.PlayerData.Visible then\n\tif frameDataPair[Frame] then\n\n\t\tlocal data = frameDataPair[Frame]\n\n\t\tscript.Parent.DataSlot.Value = i\n\t\tif mainCharacter then\n\t\t\tmainCharacter:Destroy()\n\t\t\tmainCharacter = nil\n\t\tend\n\n\t\tif data.accessories and not data.newb and not (isAdmin and (i == 13 or i == 14 or i == 15)) then\n\t\t\tlocal characterAppearanceData = {}\n\t\t\tcharacterAppearanceData.equipment \t= data.equipment\n\t\t\tcharacterAppearanceData.accessories = data.accessories\n\n\t\t\tmainCharacter \t\t\t= network:invoke(\"createRenderCharacterContainerFromCharacterAppearanceData\", workspace:WaitForChild(\"characterPreviewMask\"), characterAppearanceData)\n\t\t\tmainCharacter.Parent \t= workspace\n\n\t\t\tlocal class = string.lower(data.class)\n\t\t\tif class == \"berserker\" or class == \"paladin\" or class == \"knight\" then\n\t\t\t\tclass = \"warrior\"\n\t\t\telseif class == \"sorcerer\" or class == \"cleric\" or class == \"warlock\" then\n\t\t\t\tclass = \"mage\"\n\t\t\telseif class == \"ranger\" or class == \"trickster\" or class == \"assassin\" then\n\t\t\t\tclass = \"hunter\"\n\t\t\tend\n\n--\t\t\tlocal banner = workspace.banners:FindFirstChild(string.lower(class) .. \"Banner\")\n\n--\t\t\tif banner then\n--\t\t\t\tbanner \t\t\t= banner:Clone()\n--\t\t\t\tbanner.Parent \t= mainCharacter\n--\t\t\t\tbanner:SetPrimaryPartCFrame(\n--\t\t\t\t\tworkspace:WaitForChild(\"characterPreviewMask\").PrimaryPart.CFrame * CFrame.new(-6, -2.3, 4) * CFrame.Angles(0, math.pi / 2, 0)\n--\t\t\t\t)\n--\t\t\tend\n\n\t\t\tlocal petData do\n\t\t\t\tfor i, equipmentSlotData in pairs(data.equipment) do\n\t\t\t\t\twarn(\"scanning for pet\", equipmentSlotData.position == 10)\n\t\t\t\t\tif equipmentSlotData.position == 10 then\n\t\t\t\t\t\tpetData = equipmentSlotData\n\n\t\t\t\t\t\tbreak\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tif petData then\n\t\t\t\tif petContainer then\n\t\t\t\t\tpetContainer:Destroy()\n\t\t\t\t\tpetContainer = nil\n\t\t\t\tend\n\n\t\t\t\tworkspace.petPreviewMask.entityId.Value = petData.id\n\n\t\t\t\tpetContainer = network:invoke(\"assembleEntityByManifest\", workspace.petPreviewMask)\n\t\t\t\tnetwork:invoke(\"setRenderDataByNameTag\", workspace.petPreviewMask, \"disableNameTagUI\", true)\n\t\t\t\tnetwork:invoke(\"setRenderDataByNameTag\", workspace.petPreviewMask, \"disableHealthBarUI\", true)\n\n\t\t\tend\n\n\t\t\tlocal animationController \t= mainCharacter.entity:WaitForChild(\"AnimationController\")\n\t\t\tlocal currentEquipment \t\t= network:invoke(\"getCurrentlyEquippedForRenderCharacter\", mainCharacter.entity)\n\n\t\t\tlocal weaponType do\n\t\t\t\tif currentEquipment[1] then\n\t\t\t\t\tweaponType = currentEquipment[1].baseData.equipmentType\n\t\t\t\tend\n\t\t\tend\n\n\t\t\tlocal track = network:invoke(\"getMovementAnimationForCharacter\", animationController, \"idling\", weaponType, nil)\n\n\t\t\tif track then\n\t\t\t\tif typeof(track) == \"Instance\" then\n\t\t\t\t\ttrack:Play()\n\t\t\t\telseif typeof(track) == \"table\" then\n\t\t\t\t\tfor ii, obj in pairs(track) do\n\t\t\t\t\t\tobj:Play()\n\t\t\t\t\tend\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\telseif not loading then\n\t\t--print(\"hola\")\n\t\tloading = true\n\n\t\tspawn(function()\n\t\t\twhile loading do\n\t\t\t\tFrame.Loading.Sample.TextLabel.Text = \".\"\n\t\t\t\twait(0.3)\n\t\t\t\tif not loading then break end\n\t\t\t\tFrame.Loading.Sample.TextLabel.Text = \"..\"\n\t\t\t\twait(0.3)\n\t\t\t\tif not loading then break end\n\t\t\t\tFrame.Loading.Sample.TextLabel.Text = \"...\"\n\t\t\t\twait(0.3)\n\t\t\tend\n\t\tend)\n\n\t\tlocal data = network:invokeServer(\"loadPlayerData\", i)\n\t\tloading = false\n\n\t\tloadDataFrameLogic(Frame, data, false, i)\n\n\n\t\tfor e,otherFrame in pairs(script.Parent.main.Frame.DataSlots:GetChildren()) do\n\t\t\tif isAdmin and (i == 13 or i == 14 or i == 15) then\n\t\t\t\tloadDataFrameLogic(otherFrame, {}, true, tonumber(otherFrame.Name))\n\t\t\tend\n\t\t\tif globalData and globalData.saveSlotData then\n\t\t\t\tlocal globalData = data.globalData\n\t\t\t\tlocal quickData = globalData.saveSlotData[\"-slot\"..otherFrame.Name]\n\t\t\t\tif quickData and not frameDataPair[otherFrame] then\n\t\t\t\t\tloadDataFrameLogic(otherFrame, quickData, true, tonumber(otherFrame.Name))\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\tend\n\n\tdebounce = false\nend\n\nfor i=1,allowedSlots do\n\tlocal Frame = script.Parent.main.Frame.DataSlots.Sample:Clone()\n\tFrame.Name = tostring(i)\n\tFrame.Parent = script.Parent.main.Frame.DataSlots\n\tFrame.Slot.Text = \"slot \" .. tostring(i)\n\tFrame.PlayerData.Visible = false\n\tFrame.Loading.Visible = true\n\tFrame.LayoutOrder = i\n\n\tlocal loading = false\n\n\tif Frame.character.ViewportFrame:FindFirstChild(\"entity\") then\n\t\tFrame.character.ViewportFrame.entity:Destroy()\n\tend\n\n\tif Frame.character.ViewportFrame:FindFirstChild(\"entity2\") then\n\t\tFrame.character.ViewportFrame.entity2:Destroy()\n\tend\n\n\tFrame.MouseButton1Click:Connect(function()\n\t\tactivateFrame(Frame)\n\tend)\n\tif i == 1 then\n\t\tspawn(function()\n\t\t\t-- ugly ugly hack\n\t\t\tactivateFrame(Frame)\n\t\t\tactivateFrame(Frame)\n\t\tend)\n\tend\n\tFrame.Visible = true\nend\n\nreset()\n\nscript.Parent.main.Frame.DataSlots.Sample:Destroy()\n\n\n\n\n\n\n\nfor e,otherFrame in pairs(script.Parent.main.Frame.DataSlots:GetChildren()) do\n\tlocal i = tonumber(otherFrame.Name)\n\tif isAdmin and (i == 13 or i == 14 or i == 15) then\n\t\tloadDataFrameLogic(otherFrame, {}, true, tonumber(otherFrame.Name))\n\tend\n\tif globalData then\n\t\tif globalData.saveSlotData then\n\t\t\tlocal quickData = globalData.saveSlotData[\"-slot\"..otherFrame.Name]\n\t\t\tif quickData and not frameDataPair[otherFrame] then\n\t\t\t\tloadDataFrameLogic(otherFrame, quickData, true, tonumber(otherFrame.Name))\n\t\t\t\tif otherFrame.Name==\"1\" then\n--\t\t\t\t\t\t\tactivateFrame(otherFrame)\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\n\tend\nend\n\nif globalData then\n\n\tif globalData.ethyr then\n\t\tscript.Parent.main.ethyr.amount.Text = tostring(globalData.ethyr) .. \" Ethyr\"\n\telse\n\t\tscript.Parent.main.ethyr.amount.Text = \"0 Ethyr\"\n\n\tend\n\n\n\n\tscript.Parent.main.ethyr.buy.Activated:Connect(function()\n\t\tscript.Parent.main.products.Visible = not script.Parent.main.products.Visible\n\tend)\n\n\tfor i,product in pairs(script.Parent.main.products:GetChildren()) do\n\t\tif product:FindFirstChild(\"productId\") and product:FindFirstChild(\"buy\") then\n\t\t\tproduct.buy.Activated:connect(function()\n\t\t\t\tgame.MarketplaceService:PromptProductPurchase(game.Players.LocalPlayer, product.productId.Value)\n\t\t\tend)\n\t\tend\n\tend\n\nend\n\n\n--script.Parent.Enabled = true\n\nnetwork:connect(\"globalDataUpdated\", \"OnClientEvent\", function(globalData)\n\tif globalData.ethyr then\n\t\tscript.Parent.main.ethyr.amount.Text = tostring(globalData.ethyr) .. \" Ethyr\"\n\telse\n\t\tscript.Parent.main.ethyr.amount.Text = \"0 Ethyr\"\n\n\tend\nend)\n\ngame.Players.LocalPlayer:WaitForChild(\"DataLoaded\")\n--script.Parent.Enabled = false\n\nif input.mode.Value == \"xbox\" or game:GetService(\"UserInputService\").GamepadEnabled then\n\tgame.GuiService.GuiNavigationEnabled = true\n\tgame.GuiService.AutoSelectGuiEnabled = true\n\tgame.GuiService.SelectedObject = script.Parent.landing.play\nend\n\n\n--https://www.roblox.com/Thumbs/Asset.ashx?width=768&height=432&assetId=2015602902\n\n\nend\n"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/modules/proxy.lua",
    "content": "-- scrambler proxy for commands that need to be accessed from non-central sources\nlocal proxy = {}\n\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\nlocal network = modules.load(\"network\")\n\nfunction proxy.openShop(...)\n\tnetwork:invoke(\"openShop\", ...)\nend\n\nreturn proxy"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/modules/safeScript.lua",
    "content": "local module = {}\n\tmodule.isActive = false\n\t\nmodule.interactPrompt = \"Open\" -- prompt text\n\nlocal player = game.Players.LocalPlayer\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal network = modules.load(\"network\")\n\tlocal utilities = modules.load(\"utilities\")\n\tlocal tween = modules.load(\"tween\")\n\nlocal safe = script.Parent.Parent\n\nlocal controller = safe.AnimationController\nlocal animation = safe.Open\nlocal track = controller:LoadAnimation(animation)\n\nlocal loaded\n\nfunction module.init()\n\tif not loaded then\n\t\tgame.ContentProvider:PreloadAsync({track})\n\t\tloaded = true\n\tend\n\ttrack:Stop()\n\ttrack:Play()\n\ttrack:AdjustSpeed(1.5)\n\tdelay(2.75, function()\n\t\tif track.IsPlaying then\n\t\t\ttrack:AdjustSpeed(0)\n\t\tend\n\tend)\nend\n\nfunction module.close()\n\ttrack:AdjustSpeed(1.5)\n\tgame.CollectionService:RemoveTag(script.Parent, \"interact\")\n\tdelay(1.5, function()\n\t\tgame.CollectionService:AddTag(script.Parent, \"interact\")\n\tend)\nend\n\nreturn module"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/modules/seatScript.lua",
    "content": "local module = {}\n\tmodule.interactPrompt \t= \"SIT\" -- prompt text\n\tmodule.leavePrompt \t\t= \"GET UP\" -- end interaction text\n\tmodule.leaveMask \t\t= true -- display LEAVE as a screen UI instead of a billboard\n\tmodule.isActive \t\t= false\n\nlocal player = game.Players.LocalPlayer\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal network = modules.load(\"network\")\n\nfunction module.init()\n\tif not player or not player.Character or not player.Character.PrimaryPart then return end\n\t\n\tplayer.Character.PrimaryPart.CFrame \t= script.Parent.CFrame + Vector3.new(0, 0.5, 0)\n\tplayer.Character.PrimaryPart.Anchored \t= true\n\t\n\tnetwork:invoke(\"setCharacterMovementState\", \"isSitting\", true, script.Parent)\nend\n\nfunction module.close()\n\tif not player then return end\n\t\n\tplayer.Character.PrimaryPart.Anchored = false\n\t\n\tnetwork:invoke(\"setCharacterMovementState\", \"isSitting\", false, script.Parent)\nend\n\nreturn module\n--"
  },
  {
    "path": "src/StarterPlayer/StarterPlayerScripts/modules/warpPad.lua",
    "content": "local module = {}\n\tmodule.isActive = false\n\t\n\n\tmodule.interactPrompt = \"WARP\"\n\n -- prompt text\n\nmodule.instant = true\n\nlocal player = game.Players.LocalPlayer\nlocal modules = require(game.ReplicatedStorage:WaitForChild(\"modules\"))\n\tlocal network = modules.load(\"network\")\n\nlocal function getTargetDoor()\n\treturn script.Parent.Parent.Target\nend\n\nfunction module.init()\n\t\n\tlocal target = getTargetDoor()\n\tif target and player.Character and player.Character.PrimaryPart then\n\t\tif game.ReplicatedStorage.assets.sounds:FindFirstChild(\"blink\") then\n\t\t\tgame.ReplicatedStorage.assets.sounds.blink:Play()\n\t\tend\n\t\t--player.Character:SetPrimaryPartCFrame(player.Character.PrimaryPart.CFrame - player.Character.PrimaryPart.Position + target.Position)\n\t\tnetwork:fireServer(\"playerRequest_activateEscapeRope\", script.Parent)\n\tend\n\t\nend\n\n\nfunction module.close()\n\t\nend\n\nreturn module\n--"
  },
  {
    "path": "tools/serializer.lua",
    "content": "-- Command line code to serialize assets for rojo syncing\n\nlocal feed = \"return {\\n\"\n\nlocal defaults = {\n    [\"Volume\"] = 0.5,\n    [\"EmitterSize\"] = 10,\n    [\"MaxDistance\"] = 10000,\n    [\"Looped\"] = false,\n    [\"PlaybackSpeed\"] = 1,\n}\n\nfor _, sound in pairs(game.ReplicatedStorage.assets.sounds:GetChildren()) do\n    feed = feed .. \"\\t[\\\"\" .. sound.Name .. \"\\\"] = {\\n\"\n    for property, default in pairs(defaults) do\n        local value = sound[property]\n        if value ~= default then\n            if typeof(value) == \"number\" then\n                value = (math.floor(value * 10)) / 10\n            end\n            feed = feed .. \"\\t\\t\" .. property .. \" = \" .. tostring(value) .. \",\\n\"\n        end\n    end\n\tfeed = feed .. \"\\t\\tSoundId = \\\"\" .. sound.SoundId .. \"\\\",\\n\"\n\tfeed = feed .. \"\\t},\\n\"\nend\n\nfeed = feed .. \"}\"\nworkspace.stuff.Source = feed"
  }
]