├── .editorconfig ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── archived-but-valueable-for-reference ├── date-time │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── init.ts │ └── tsconfig.json ├── date │ ├── README.md │ ├── index.d.ts │ ├── init.lua │ ├── package-lock.json │ └── package.json ├── delay-spawn-wait │ ├── README.md │ ├── index.d.ts │ ├── init.lua │ ├── package-lock.json │ └── package.json ├── object-composer │ ├── README.md │ ├── init.d.ts │ ├── init.lua │ ├── package-lock.json │ ├── package.json │ └── tsconfig.json ├── object-stack │ ├── README.md │ ├── init.d.ts │ ├── init.lua │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── init.ts │ └── tsconfig.json └── sorted-array │ ├── index.d.ts │ └── init.lua ├── cubic-bezier ├── LICENSE ├── README.md ├── index.d.ts ├── init.lua └── package.json ├── cue ├── README.md ├── index.d.ts ├── init.lua └── package.json ├── easing-functions ├── README.md ├── index.d.ts ├── init.lua ├── package-lock.json └── package.json ├── extendable-resources ├── README.md ├── index.d.ts ├── init.lua ├── package-lock.json ├── package.json ├── src │ └── index.ts └── tsconfig.json ├── ip-data ├── README.md ├── index.d.ts ├── init.lua ├── package-lock.json ├── package.json ├── src │ └── index.ts └── tsconfig.json ├── lerp-functions ├── README.md ├── index.d.ts ├── init.lua ├── package-lock.json ├── package.json └── tsconfig.json ├── make ├── README.md ├── index.d.ts ├── init.lua ├── package-lock.json ├── package.json ├── src │ └── index.ts └── tsconfig.json ├── promise-character ├── README.md ├── index.d.ts ├── init.lua ├── package-lock.json ├── package.json ├── src │ └── index.ts └── tsconfig.json ├── promise-child ├── .eslintrc.json ├── .gitignore ├── README.md ├── index.d.ts ├── init.lua ├── package-lock.json ├── package.json ├── src │ └── index.ts └── tsconfig.json ├── schema ├── README.md ├── index.d.ts ├── init.lua ├── package-lock.json ├── package.json ├── src │ └── index.ts └── tsconfig.json ├── signal ├── README.md ├── index.d.ts ├── init.lua ├── package-lock.json ├── package.json └── tsconfig.json ├── spring ├── LICENSE ├── README.md ├── index.d.ts ├── init.lua └── package.json ├── synced-poller ├── README.md ├── index.d.ts ├── init.lua ├── package-lock.json ├── package.json ├── src │ └── index.ts └── tsconfig.json ├── tslint.json ├── tween ├── README.md ├── index.d.ts ├── init.lua ├── package-lock.json ├── package.json └── tsconfig.json ├── validate-tree ├── README.md ├── index.d.ts ├── init.lua ├── package-lock.json ├── package.json ├── src │ └── index.ts └── tsconfig.json └── weighted-random-picker ├── README.md ├── index.d.ts ├── init.lua └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | 12 | # Matches multiple files with brace expansion notation 13 | # Set default charset 14 | [*.lua] 15 | charset = utf-8 16 | indent_style = tab 17 | indent_size = 4 18 | trim_trailing_whitespace = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .history 2 | archived 3 | archived2 4 | **/node_modules 5 | **/include 6 | boilerplate.lua 7 | luac.out 8 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/node_modules/** 2 | ./node_modules/** 3 | **/.{git,svn,hg,lua}/** 4 | ./.{git,svn,hg,lua}/** 5 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "useTabs": true, 4 | "tabWidth": 4, 5 | "printWidth": 120, 6 | "parser": "typescript" 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2019 Validark 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Roblox-TS-Libraries 2 | 3 | A collection of libraries hosted on npm written by yours truly, Validark, for use with roblox-ts. 4 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/date-time/README.md: -------------------------------------------------------------------------------- 1 | # date-time 2 | 3 | A new wrapper object for dealing with dates and times. 4 | 5 | ### Goals 6 | - Accurate, feature complete date formatting across a variety of locales 7 | - Easy to use interface which abstracts all the mathemagic 8 | - Efficient, where possible 9 | - Objects should be immutable, and shouldn't be very large. 10 | - It should be clear to the library and user what a developer means by a declaration, and that should be translated over to a locale. 11 | - A developer declaration should not accidentally get declared in a locale, leading to mismatched events 12 | - Support a few different formatting options. 13 | 14 | Example: 15 | 16 | ```ts 17 | // This should create an event for the entire October of 2019. 18 | // EventStart should be the first second of October 2019, 00:00:00 in UTC time 19 | const eventStart = DateTime.fromObject({ month: 10, year: 2019 }); 20 | // Event end should be the last second of October 2019, 23:59:59 in UTC time 21 | const eventEnd = eventStart.endOf("month"); 22 | 23 | // Should print the default format of this locale, and the date should be adjusted to the locale's calendar. 24 | // This means that an October event isn't necessarily displaying October for a given locale. 25 | // (I believe) A kurdish calendar would mean that this event starts towards the end of November and ends before the end of October 26 | print(eventStart); 27 | ``` 28 | 29 | 30 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/date-time/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/date-time", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@rbxts/types": { 8 | "version": "1.0.274", 9 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.274.tgz", 10 | "integrity": "sha512-8+neFjYzQBAJYE+UbkihMC4KBnqGmqUnRXfmbOV1OEf+e+vFChabdI6WfCvuq2g16RC5v3EKtNQi2Ihx+uDfsg==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/date-time/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/date-time", 3 | "version": "0.0.0", 4 | "description": "An extendable date formatting object. In development", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/date-time" 13 | }, 14 | "keywords": [ 15 | "resources", 16 | "roblox-typescript", 17 | "time", 18 | "date", 19 | "formatting", 20 | "formatter" 21 | ], 22 | "peerDependencies": { 23 | "@rbxts/types": "^1.0.194" 24 | }, 25 | "author": "Validark", 26 | "license": "ISC", 27 | "bugs": { 28 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 29 | }, 30 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/date-time/README.md", 31 | "publishConfig": { 32 | "access": "public" 33 | }, 34 | "dependencies": { 35 | "@rbxts/types": "^1.0.194" 36 | }, 37 | "files": ["Placeholder"] 38 | } 39 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/date-time/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | // required 5 | "allowSyntheticDefaultImports": true, 6 | "downlevelIteration": true, 7 | "module": "commonjs", 8 | "noLib": true, 9 | "strict": true, 10 | "target": "es6", 11 | "typeRoots": [ "node_modules/@rbxts" ], 12 | // required, configurable 13 | "rootDir": "src", 14 | "outDir": "out", 15 | // optional 16 | "baseUrl": "src", 17 | "declaration": true, 18 | // optional, non-configurable 19 | "jsx": "react", 20 | "jsxFactory": "Roact.createElement" 21 | }, 22 | "typeAcquisition": { 23 | "enable": true 24 | }, 25 | "include": [ 26 | "./src/**/*" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/date/README.md: -------------------------------------------------------------------------------- 1 | # Date 2 | 3 | A reimplementation of the vanilla Lua os.date function built upon the one exposed by RobloxLua 4 | 5 | Demo: 6 | 7 | ```ts 8 | // ISO 8601: 9 | print(Date("%FT%T")); // 2020-01-01T01:03:05 10 | print(Date("%Y-%m-%dT%H:%M:%S")); // 2020-01-01T01:03:05 11 | print(Date("%FT%T%#z")); // 2020-01-01T01:03:05-05:00 12 | 13 | // Time: 14 | print(Date("%T")); // 08:37:43 15 | 16 | // Date: 17 | print(Date("%D")); // 01/12/20 18 | ``` 19 | 20 | `Date` functions just like the vanilla Lua `os.date` function, except padding can be toggled by inserting a '#' like so: 21 | 22 | ```ts 23 | print(Date("%#x", os.time())); 24 | ``` 25 | 26 | Note that placing a `!` at the beginning of the string will make it consider the time input to be in the UTC time zone. 27 | 28 | ## String reference: 29 | 30 | ``` 31 | The following patterns will be replaced by their tags below: 32 | %c = "%a %b %e %X %Y" 33 | %D = "%m/%d/%y" 34 | %F = "%Y-%m-%d" 35 | %n = "\n" 36 | %R = "%H:%M" 37 | %r = "%I:%M:%S %p" 38 | %T = "%H:%M:%S" 39 | %t = "\t" 40 | %v = "%e-%b-%Y" 41 | %X = "%T" 42 | %x = "%D" 43 | 44 | %#c = "%#x, %#X" 45 | %#r = "%#I:%M:%S %#p" 46 | %#T = "%#H:%M:%S" 47 | %#X = "%#T" 48 | %#x = "%A, %B %#d, %#Y" 49 | 50 | The following tags will be replaced as follows: 51 | %% = the character `%´ 52 | %a = abbreviated weekday name (e.g., Wed) 53 | %A = full weekday name (e.g., Wednesday) 54 | %b = abbreviated month name (e.g., Sep) 55 | %B = full month name (e.g., September) 56 | %C = century: (year / 100) (padded) 57 | %d = day of the month (16) [01-31] 58 | %e = day of month as decimal number [ 1, 31] 59 | %g = Same year as in %G, but as a decimal number without century [00, 99] 60 | %G = a 4-digit year as a decimal number with century 61 | %H = hour, using a 24-hour clock (23) [00-23] 62 | %I = hour, using a 12-hour clock (11) [01-12] 63 | %j = day of year [001-366] (March 1st is treated as day 0 of year) 64 | %k = Hour in 24-hour format [ 0, 23] 65 | %l = Hour in 12-hour format [ 1, 12] 66 | %m = month (09) [01, 12] 67 | %M = minute (48) [00, 59] 68 | %p = either "am" or "pm" ('#' makes it uppercase) 69 | %s = Day of year suffix: e.g. 12th, 31st, 22nd 70 | %S = Second as decimal number [00, 59] 71 | %u = ISO 8601 weekday as number with Monday as 1 [1, 7] 72 | %U = Week of year, Sunday Based [00, 53] 73 | %V = week number of year (Monday as beginning of week) [01, 53] 74 | %w = weekday (3) [0-6 = Sunday-Saturday] 75 | %W = Week of year with Monday as first day of week [0, 53] 76 | %y = two-digit year (98) [00, 99] 77 | %Y = full year (1998) 78 | %z = Time zone offset from UTC in the form [+-]%02Hours%02Minutes, e.g. +0500 79 | ``` 80 | 81 | Example: 82 | 83 | ```ts 84 | print(Date("It is currently %#r")); 85 | // > It is currently 11:41:20 am 86 | ``` 87 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/date/index.d.ts: -------------------------------------------------------------------------------- 1 | /** An object the represents a date or time. Used with `os.date` and `os.time`. */ 2 | interface DateTable { 3 | /** The year. */ 4 | year: number; 5 | /** The month. [1, 12] */ 6 | month: number; 7 | /** The day. [1, 31] */ 8 | day: number; 9 | 10 | /** The hour. [0, 23] */ 11 | hour?: number; 12 | /** The minute. [0, 59] */ 13 | min?: number; 14 | /** The second. [0, 59] */ 15 | sec?: number; 16 | /** Whether this object represents a daylight savings time. */ 17 | isdst?: boolean; 18 | /** The number of days into the year. [1, 366] */ 19 | yday?: number; 20 | /** The day of the week. [1, 7] */ 21 | wday?: number; 22 | } 23 | 24 | /** 25 | * Formats the given formatString with date/time information based on the given time. 26 | * 27 | * If the formatString is `*t`, it will use local time and return a DateTable. 28 | * 29 | * If the formatString is `!*t`, it will use UTC time and return a DateTable. 30 | * 31 | * Otherwise, it will format the string with the given specifiers (specifiers are based on the C function strftime) 32 | * 33 | * The following specifiers are supported: 34 | * 35 | * | Specifier | Meaning | Example | 36 | * | --- | --- | --- | 37 | * | %a | Abbreviated weekday name | Wed | 38 | * | %A | Full weekday name * | Wednesday | 39 | * | %b | Abbreviated month name * | Sep | 40 | * | %B | Full month name * | September | 41 | * | %c | Date and time * | | 42 | * | %d | Day of the month | 16 | 43 | * | %H | Hour, using 24-hour clock | 23 | 44 | * | %I | Hour, using 12-hour clock | 11 | 45 | * | %j | Day of year | 259 | 46 | * | %m | Month | 09 | 47 | * | %M | Minute | 48 | 48 | * | %p | Either "am" or "pm" | pm | 49 | * | %S | Second | 10 | 50 | * | %U | Week number (first Sunday as the first day of week one) | 37 | 51 | * | %w | Weekday | 3 | 52 | * | %W | Week number (first Monday as the first day of week one) | 37 | 53 | * | %x | Date * | 09/16/98 | 54 | * | %X | Time * | 23:48:10 | 55 | * | %y | Two-digit year | 98 | 56 | * | %Y | Full year | 1998 | 57 | * | %z | ISO 8601 offset from UTC in timezone (1 minute = 1, 1 hour = 100) | -0400 | 58 | * | %Z | Timezone name or abbreviation * | Eastern Daylight Time | 59 | * | %% | The % character | %% | 60 | * | %c | "%a %b %e %X %Y" | 09/16/98 23:48:10 | 61 | * | %D | "%m/%d/%y" | 10/30/20 | 62 | * | %F | "%Y-%m-%d" | 2020-30-10 | 63 | * | %R | "%H:%M" | 23:59 | 64 | * | %r | "%I:%M:%S %p" | 12:00:00 am | 65 | * | %T | "%H:%M:%S" | 13:00:00 | 66 | * | %v | "%e-%b-%Y" | 2-Sep-2020 | 67 | * | %X | "%T" | 13:00:00 | 68 | * | %x | "%D" | 10/30/20 | 69 | * | %% | "%" | % | 70 | * | %t | "\t" | \t | 71 | * | %n | "\n" | \n | 72 | * | %a | abbreviated weekday name | Wed | 73 | * | %A | full weekday name | Wednesday | 74 | * | %b | abbreviated month name | Sep | 75 | * | %B | full month name | September | 76 | * | %C | century: (year / 100) (padded) | 20 | 77 | * | %d | day of the month [01-31] | 16 | 78 | * | %e | day of month as decimal number [ 1, 31] | 1 | 79 | * | %g | year as a decimal number without century [00, 99] | 20 | 80 | * | %G | a 4-digit year as a decimal number with century | 2020 | 81 | * | %H | hour, using a 24-hour clock [00-23] | 23 | 82 | * | %I | hour, using a 12-hour clock [01-12] | 11 | 83 | * | %j | day of year [001-366] | 344 | 84 | * | %k | Hour in 24-hour format [ 0, 23] | 22 | 85 | * | %l | Hour in 12-hour format [ 1, 12] | 12 | 86 | * | %m | month [01, 12] | 09 | 87 | * | %M | minute [00, 59] | 48 | 88 | * | %p | either "am" or "pm" ('#' toggles case) | pm | 89 | * | %s | Day of year suffix | 12th, 31st, 22nd | 90 | * | %S | Second as decimal number [00, 59] | 56 | 91 | * | %u | ISO 8601 weekday as number with Monday as 1 [1, 7] | 7 | 92 | * | %U | Week of year, Sunday Based [00, 53] | 51 | 93 | * | %V | week of year (Monday as beginning of week) [01, 53] | 24 | 94 | * | %w | weekday (3) [0-6] (Sunday-Saturday) | 2 | 95 | * | %W | Week of year with Monday as first day of week [0, 53] | 25 | 96 | * | %y | two-digit year [00, 99] | 98 | 97 | * | %Y | full year | 1998 | 98 | * | %z | Time zone offset in the form [+-]%H%M | +0500 | 99 | * | --- | --- | --- | 100 | * 101 | * \* indicates the value can vary depending on the current locale. 102 | * @param formatString The string to format with either specifiers givenn, or the type of DateTable to return. 103 | * @param time The timestamp to format the formatString from. Defaults to os.time 104 | */ 105 | declare function date( 106 | formatString: T, 107 | time?: number, 108 | ): string extends T ? string | Required : T extends "*t" | "!*t" ? Required : string; 109 | 110 | export = date; 111 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/date/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/date", 3 | "version": "1.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@rbxts/compiler-types": { 8 | "version": "1.0.0-beta.11.1", 9 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.0.0-beta.11.1.tgz", 10 | "integrity": "sha512-RkWbXvePkTTrydJNXa6pKoXYbD6M5IrerFi+i2aFgUotUNizQVpaCu4PNFFJ62FH2hbkrhmwgs2IiGhOqqB2TA==" 11 | }, 12 | "@rbxts/types": { 13 | "version": "1.0.431", 14 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.431.tgz", 15 | "integrity": "sha512-bVF0kPsB8qdjR7+45giJ7M+hIGV++NmnmL9k5b2oA+p8IS+T8Elzffq1yVKI1Tq5UTzHqFXxoaybUb3/ERVn1Q==" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/date/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/date", 3 | "version": "1.0.2", 4 | "description": "A re-implementation of vanilla Lua's os.date function.", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/date" 13 | }, 14 | "keywords": [ 15 | "date", 16 | "time", 17 | "roblox", 18 | "roblox-typescript", 19 | "string", 20 | "formatter" 21 | ], 22 | "author": "Validark", 23 | "license": "ISC", 24 | "bugs": { 25 | "url": "https://github.com/Validark/Roblox-TS-libraries/issues" 26 | }, 27 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/date/README.md", 28 | "publishConfig": { 29 | "access": "public" 30 | }, 31 | "dependencies": { 32 | "@rbxts/compiler-types": "^1.0.0-beta.11.1", 33 | "@rbxts/types": "^1.0.431" 34 | }, 35 | "peerDependencies": { 36 | "@rbxts/compiler-types": "^1.0.0-beta.11.1", 37 | "@rbxts/types": "^1.0.431" 38 | }, 39 | "files": [ 40 | "init.lua", 41 | "index.d.ts", 42 | "README.md" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/delay-spawn-wait/README.md: -------------------------------------------------------------------------------- 1 | # delay-spawn-wait 2 | This library works as a (mostly) drop-in replacement for the built-in functions by the same names. The main differences are: 3 | 4 | - This library neither exposes the result of an `elapsedTime()` call as a second return value of `wait()` nor as a second argument passed into the callback given to `delay`. 5 | 6 | ```lua 7 | -- below, we compare vanilla behavior against this library's behavior: 8 | 9 | print(wait()) 10 | -- vanilla: 0.15499839999939 13074.348632812 11 | -- library: 0.15499839999939 12 | 13 | -- if you want to get that second number, instead call elapsedTime() 14 | 15 | print(wait(), elapsedTime()) 16 | -- vanilla: 0.15499839999939 13074.348632812 17 | -- library: 0.15499839999939 13074.348632812 18 | 19 | delay(0, print) 20 | -- vanilla: 0.15499839999939 13074.348632812 21 | -- library: 0.15499839999939 22 | ``` 23 | 24 | - `spawn` runs on a new thread without yielding one tick (or passing in a deltaTime parameter) 25 | - If you need to yield for one tick, try `delay(0, callback)` instead. 26 | - Keep in mind that one tick in this library is on the Heartbeat (60hz) pipeline, and NOT on the 30hz pipeline. That means 1 tick approximately equals 1/60 of a second instead of 1/30 of a second (when running at 60fps). 27 | 28 | - `spawn` and `delay` allow you to pass in extra arguments with which to call the callback. 29 | ```lua 30 | spawn(print, 0, 1, 2) -- 0 1 2 31 | 32 | -- note: delay still passes deltaTime in as the first argument to the callback! 33 | delay(0, print, 0, 1, 2) -- 0.0030354000009538 0 1 2 34 | ``` 35 | - `delay` can accept a BindableEvent to fire instead of a callback: 36 | ```lua 37 | local bindable = Instance.new("BindableEvent") 38 | delay(5, bindable) 39 | bindable.Event:Wait() -- this is what our wait() function does internally! 40 | ``` 41 | - Note that passing in extra arguments with a BindableEvent will pass your arguments directly into `BindableEvent:Fire()`. That means [these rules](https://developer.roblox.com/en-us/api-reference/function/BindableEvent/Fire) apply. 42 | ```lua 43 | local b = Instance.new("BindableEvent") 44 | delay(2, b, 1, newproxy(true)) 45 | b.Event:Connect(function(elapsedTime, num, userdata) 46 | -- elapsedTime: (number) 47 | -- num: 1 48 | -- userdata: nil 49 | end) 50 | ``` 51 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/delay-spawn-wait/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | /** 5 | * Calls a given callback or fires a given BindableEvent after a given number of seconds. 6 | * @param seconds The number of seconds to wait before resolving (minimum is 0.029). NaN/Inf/-Inf all default to 0.029 as well. 7 | * @param callback The callback to call or the BindableEvent to `Fire()` 8 | * @param args The additional arguments to pass by reference into the callback. If a BindableEvent was passed as the second argument, args will be passed in by value. 9 | */ 10 | export declare function delay< 11 | T extends 12 | | BindableEvent<(timeElapsed: number, ...args: Array) => any> 13 | | ((timeElapsed: number, ...args: Array) => any) 14 | >( 15 | seconds: number | undefined, 16 | callback: T, 17 | ...args: T extends (timeElapsed: number, ...args: infer P) => any 18 | ? P 19 | : T extends BindableEvent 20 | ? J extends (timeElapsed: number, ...args: infer P) => any 21 | ? P 22 | : never 23 | : never 24 | ): void; 25 | 26 | /** 27 | * Spawns a function on a new thread, but begins running it immediately 28 | * instead of being deferred. This is sometimes known as a "fast spawn". 29 | * @param callback The function to call. Any further arguments are passed as parameters. 30 | */ 31 | export declare function spawn(callback: T, ...args: Parameters): void; 32 | 33 | /** 34 | * Yields the current thread for a given number of seconds/ 35 | * @param seconds The duration in seconds for how long the current thread should yield before resuming. 36 | * @returns The number of seconds the thread yielded. 37 | */ 38 | export declare function wait(seconds?: number): number; 39 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/delay-spawn-wait/init.lua: -------------------------------------------------------------------------------- 1 | local RunService = game:GetService("RunService") 2 | 3 | local function spawn(callback, ...) 4 | local bindable = Instance.new("BindableEvent") 5 | local length = select("#", ...) 6 | 7 | if length == 0 then 8 | bindable.Event:Connect(callback) 9 | else 10 | local args = { ... } 11 | bindable.Event:Connect(function() 12 | callback(table.unpack(args, 1, length)) 13 | end) 14 | end 15 | 16 | bindable:Fire() 17 | bindable:Destroy() 18 | end 19 | 20 | -- uses a min-heap to achieve O(1) check operations and O(log(n)) remove/insert operations 21 | local queue = {} 22 | local len = 0 23 | 24 | local connection -- the Heartbeat `RBXScriptConnection | nil` 25 | 26 | local function heartbeatStep() 27 | local clockTick = os.clock() 28 | 29 | repeat 30 | local current = queue[1] 31 | if current == nil or current.endTime > clockTick then break end 32 | local done = len == 1 33 | 34 | if done then 35 | queue[1] = nil 36 | len = 0 37 | connection:Disconnect() 38 | connection = nil 39 | else 40 | local lastNode = queue[len] 41 | queue[len] = nil 42 | len = len - 1 43 | local targetIndex = 1 44 | 45 | while true do 46 | local childIndex = 2*targetIndex -- leftChild, but this might change to rightChild below 47 | if childIndex > len then break end 48 | local minChild = queue[childIndex] 49 | local rightChildIndex = childIndex + 1 50 | 51 | if rightChildIndex <= len then 52 | local rightChild = queue[rightChildIndex] 53 | if rightChild.endTime < minChild.endTime then 54 | childIndex = rightChildIndex 55 | minChild = rightChild 56 | end 57 | end 58 | 59 | if lastNode.endTime < minChild.endTime then break end 60 | queue[targetIndex] = minChild 61 | targetIndex = childIndex 62 | end 63 | 64 | queue[targetIndex] = lastNode 65 | end 66 | 67 | local args = current.args 68 | local callback = current.callback 69 | 70 | if typeof(callback) == "Instance" then 71 | if args then 72 | callback:Fire(os.clock() - current.startTime, table.unpack(args, 2, args[1])) 73 | else 74 | callback:Fire(os.clock() - current.startTime) 75 | end 76 | else 77 | local bindable = Instance.new("BindableEvent") 78 | 79 | if args then 80 | bindable.Event:Connect(function(elapsedTime) 81 | callback(elapsedTime, table.unpack(args, 2, args[1])) 82 | end) 83 | else 84 | bindable.Event:Connect(callback) 85 | end 86 | 87 | bindable:Fire(os.clock() - current.startTime) 88 | bindable:Destroy() 89 | end 90 | until done 91 | end 92 | 93 | local function delay(seconds, callback, ...) 94 | -- If seconds is nil, -INF, INF, NaN, or less than MINIMUM_DELAY, assume seconds is MINIMUM_DELAY. 95 | if seconds == nil or not (seconds > 0) or seconds == math.huge then 96 | seconds = 0 97 | end 98 | 99 | local startTime = os.clock() 100 | local endTime = startTime + seconds 101 | local length = select("#", ...) 102 | 103 | if connection == nil then -- first is nil when connection is nil 104 | connection = RunService.Heartbeat:Connect(heartbeatStep) 105 | end 106 | 107 | local node = { 108 | callback = callback, 109 | startTime = startTime, 110 | endTime = endTime, 111 | args = length > 0 and { length + 1, ... } 112 | } 113 | 114 | local targetIndex = len + 1 115 | len = targetIndex 116 | 117 | while true do 118 | local parentIndex = (targetIndex - targetIndex % 2) / 2 119 | if parentIndex < 1 then break end 120 | local parentNode = queue[parentIndex] 121 | if parentNode.endTime < node.endTime then break end 122 | queue[targetIndex] = parentNode 123 | targetIndex = parentIndex 124 | end 125 | 126 | queue[targetIndex] = node 127 | end 128 | 129 | local function wait(seconds) 130 | local bindable = Instance.new("BindableEvent") 131 | delay(seconds, bindable) 132 | local elapsedTime = bindable.Event:Wait() 133 | bindable:Destroy() 134 | return elapsedTime 135 | end 136 | 137 | return { 138 | spawn = spawn; 139 | delay = delay; 140 | wait = wait; 141 | } 142 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/delay-spawn-wait/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/delay-spawn-wait", 3 | "version": "2.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@rbxts/compiler-types": { 8 | "version": "1.0.0-beta.15.0", 9 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.0.0-beta.15.0.tgz", 10 | "integrity": "sha512-Tutr3dkD2bUOCeMIxHXHPYda0blPTZJ1KDRSj0TJ942QKSjxP4Pf09G/ZzySpX094uWLNndgGO9W9UmtmrvP6g==" 11 | }, 12 | "@rbxts/types": { 13 | "version": "1.0.441", 14 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.441.tgz", 15 | "integrity": "sha512-uPjojU10lXfbWeT2oh4NUbgcQfL13Tme/AO0VruRkDKSAObvspeA8rUe538awng+Ut3bpuxFVTXBXwYoMeqGtA==" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/delay-spawn-wait/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/delay-spawn-wait", 3 | "version": "2.0.0", 4 | "description": "Replacement for the default delay, spawn, and wait functions", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": {}, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/delay-spawn-wait" 11 | }, 12 | "keywords": [ 13 | "yield", 14 | "wait", 15 | "spawn", 16 | "delay" 17 | ], 18 | "bundledDependencies": [], 19 | "author": "Validark", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 23 | }, 24 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/delay-spawn-wait/README.md", 25 | "publishConfig": { 26 | "access": "public" 27 | }, 28 | "peerDependencies": { 29 | "@rbxts/compiler-types": "^1.0.0-beta.15.0", 30 | "@rbxts/types": "^1.0.441" 31 | }, 32 | "files": [ 33 | "init.lua", 34 | "index.d.ts", 35 | "README.md" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/object-composer/README.md: -------------------------------------------------------------------------------- 1 | ## object-composer 2 | 3 | object-composer ships a single `compose` function, which combines a series of callbacks which each return an object into a single function which returns the combination of all returned objects. Each callback function can optionally take a single `state` parameter which will be passed into each constructor function during object instantiation. Property collisions can occur at run-time if two constructors each attempt to write to the same property (key) with conflicting types (see bottom of readme). 4 | 5 | Here is a demo, taking inspiration from this video: https://youtu.be/wfMtDGfHWpA 6 | 7 | ```ts 8 | export {}; 9 | import compose from "@rbxts/object-composer"; 10 | 11 | const Pooper = () => ({ 12 | poops: 0, 13 | poop() { 14 | this.poops++; 15 | }, 16 | }); 17 | 18 | const Barker = ({ name }: { name: string }) => ({ 19 | bark() { 20 | print(`Woof, I am ${name}`); 21 | }, 22 | }); 23 | 24 | const Driver = ({ position: _position = 0, speed: _speed = 0 }) => ({ 25 | /** @private */ 26 | _position, 27 | /** @private */ 28 | _speed, 29 | drive() { 30 | return (this._position += this._speed); 31 | }, 32 | }); 33 | 34 | const Killer = () => ({ 35 | kills: 0, 36 | kill(this: T, target: { TakeDamage(amount: number): void }) { 37 | target.TakeDamage(1 / 0); 38 | this.kills++; 39 | return this; // returns the full `this`, not just the `Killer` this 40 | }, 41 | }); 42 | 43 | type CleaningRobot = ReturnType; 44 | const CleaningRobot = compose( 45 | Driver, 46 | () => ({ 47 | clean() {}, 48 | }), 49 | ); 50 | 51 | type MurderousRobot = ReturnType; 52 | const MurderousRobot = compose( 53 | Driver, 54 | Killer, 55 | ); 56 | 57 | type Dog = ReturnType; 58 | const Dog = compose( 59 | Pooper, 60 | Barker, 61 | ); 62 | 63 | type MurderousRobotDog = ReturnType; 64 | const MurderousRobotDog = compose( 65 | Barker, 66 | Driver, 67 | Killer, 68 | ); 69 | 70 | // We don't need a state argument for this construction, since there are defaults for every property in Driver and Killer 71 | const robot = MurderousRobot(); 72 | 73 | // Error! We need a state argument here, since `name` is required 74 | const dog = Dog(); 75 | 76 | // Good! 77 | const dog2 = Dog({ name: "pup" }); 78 | dog2.bark(); 79 | 80 | MurderousRobotDog({ 81 | position: 10, 82 | speed: 30, 83 | name: "Mr. Wolf", 84 | }); 85 | 86 | MurderousRobotDog({ 87 | name: "Hello", // We don't need speed or position arguments, since those have defaults 88 | }); 89 | 90 | // With the above pattern, you can use `Dog` as a type 91 | function f(o: Dog) { 92 | o.bark(); 93 | } 94 | ``` 95 | 96 | A cool feature of TypeScript is that you can define the `this` property to make compile-time checks on the call site. For example, if you wanted to define a method that should only work if called on an object with a particular property, you can do so! With proper object composition you shouldn't need to do this, but I thought it was cool regardless. 97 | 98 | ```ts 99 | export {}; 100 | import compose from "@rbxts/object-composer"; 101 | 102 | const ToStringHaver = () => ({ 103 | toString(): string { 104 | let result = "{\n"; 105 | for (const [prop, value] of Object.entries(this)) result += `\t${prop}: ${value},\n`; 106 | return result + "\n}"; 107 | }, 108 | }); 109 | 110 | const ThemeHaver = ({ theme: _theme = "Light" }: { theme?: "Light" | "Dark" }) => ({ 111 | /** @private */ 112 | _theme, 113 | 114 | /** Get the theme. */ 115 | getTheme() { 116 | return this._theme; 117 | }, 118 | 119 | /** Set the theme. */ 120 | setTheme }>(this: T, theme: NonNullable) { 121 | this._theme = theme; 122 | return this; 123 | }, 124 | }); 125 | 126 | const SizeHaver = ({ size: _size = new Vector3() }) => ({ 127 | /** @private */ 128 | _size, 129 | 130 | /** Get the size property. */ 131 | getSize() { 132 | return this._size; 133 | }, 134 | 135 | /** Set the size property. */ 136 | setSize(this: T, size: Vector3) { 137 | this._size = size; 138 | return this; 139 | }, 140 | 141 | /** Gets the TextSize, if it exists */ 142 | getTextSize(this: T) { 143 | const { X, Y } = this._size; 144 | return new Vector2(this._text.size() * X, Y); 145 | }, 146 | }); 147 | 148 | const TextHaver = ({ text: _text = "" }) => ({ 149 | /** @private */ 150 | _text, 151 | 152 | /** Get the text property. */ 153 | getText() { 154 | return this._text; 155 | }, 156 | 157 | /** Set the text property. */ 158 | setText(this: T, text: string) { 159 | this._text = text; 160 | return this; 161 | }, 162 | }); 163 | 164 | const TextObject = compose( 165 | ToStringHaver, 166 | ThemeHaver, 167 | SizeHaver, 168 | TextHaver, 169 | ); 170 | 171 | const text = TextObject(); 172 | 173 | // By typing `this` as the full call location on each `set` function, we can chain! 174 | text.setSize(new Vector3()) 175 | .setText("Hello") 176 | .setTheme("Dark"); 177 | 178 | text.getTextSize(); 179 | 180 | const SizedTheme = compose( 181 | SizeHaver, 182 | ThemeHaver, 183 | ); 184 | const sizedTheme = SizedTheme(); 185 | 186 | // error! sizedTheme does not have a `_text` property! 187 | sizedTheme.getTextSize(); 188 | ``` 189 | 190 | ###### Note: Index signatures are not unsupported, but they aren't "supported" either. TypeScript may only error at run-time if you use index signatures (and why would you do that? Just use a Map) 191 | ###### Note: For the purposes of this readme, "conflicting types" between `a` and `b` are defined as `typeof(a) ~= typeof(b) or typeof(a) == "table" or typeof(a) == "userdata` 192 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/object-composer/init.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | type _ = T; 3 | type Merge = _<{ [K in keyof T]: T[K] }>; 4 | declare type Maker = (state: any) => object; 5 | type UnionToIntersection = (U extends any ? (u: U) => void : never) extends (u: infer I) => void ? I : never; 6 | type ComposeReturn = {} extends S ? () => R : Partial extends S ? (state?: S) => R : (state: S) => R; 7 | 8 | export type ComposeTuple> = ComposeReturn< 9 | Merge< 10 | UnionToIntersection< 11 | { [K in keyof T]: T[K] extends (state: infer R) => void ? (unknown extends R ? never : R) : never }[Exclude< 12 | keyof T, 13 | keyof Array | "length" 14 | >] 15 | > 16 | >, 17 | UnionToIntersection< 18 | { [K in keyof T]: T[K] extends (...args: any) => infer R ? R : never }[Exclude< 19 | keyof T, 20 | keyof Array | "length" 21 | >] 22 | > 23 | >; 24 | 25 | export declare function compose>(...args: T): ComposeTuple; 26 | export default compose; 27 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/object-composer/init.lua: -------------------------------------------------------------------------------- 1 | local function compose(...) 2 | local constructors = { ... }; 3 | return function(state) 4 | if state == nil then state = {}; end; 5 | local x = {}; 6 | for _0 = 1, #constructors do 7 | for prop, value in pairs(constructors[_0](state)) do 8 | local previous = x[prop]; 9 | if previous ~= nil then 10 | local t = typeof(previous) 11 | if t ~= typeof(value) or t == "table" or t == "userdata" then 12 | error("Property collision at " .. tostring(prop) .. "!"); 13 | end 14 | else 15 | x[prop] = value; 16 | end; 17 | end; 18 | end; 19 | return x; 20 | end; 21 | end; 22 | 23 | return { 24 | default = compose; 25 | compose = compose; 26 | }; 27 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/object-composer/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/object-composer", 3 | "version": "1.0.4", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@rbxts/types": { 8 | "version": "1.0.260", 9 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.260.tgz", 10 | "integrity": "sha512-/Ou+NGVnifBWOcAKZMn4grtxEksCEwQE0pz/amUdEneCo+8GfruCnlokioNxfBD02YlGLc6ZAiXrgJJbn/qDfg==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/object-composer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/object-composer", 3 | "version": "1.0.4", 4 | "description": "A simple, light-weight multiple inheritance object composition library", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": {}, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/object-composer" 11 | }, 12 | "keywords": [ 13 | "Roblox", 14 | "TypeScript", 15 | "Stack", 16 | "Data structures", 17 | "Object", 18 | "Composition", 19 | "Multiple Inheritance", 20 | "Inheritance" 21 | ], 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 25 | }, 26 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/object-composer/README.md", 27 | "publishConfig": { 28 | "access": "public" 29 | }, 30 | "peerDependencies": { 31 | "@rbxts/types": "^1.0.194" 32 | }, 33 | "dependencies": { 34 | "@rbxts/types": "^1.0.260" 35 | }, 36 | "files": [ 37 | "init.lua", 38 | "index.d.ts", 39 | "README.md" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/object-composer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | // required 5 | "allowSyntheticDefaultImports": true, 6 | "downlevelIteration": true, 7 | "module": "commonjs", 8 | "noLib": true, 9 | "strict": true, 10 | "target": "es6", 11 | "typeRoots": [ 12 | "node_modules/@rbxts" 13 | ], 14 | // required, configurable 15 | "rootDir": "src", 16 | "outDir": "out", 17 | // optional 18 | "baseUrl": "src", 19 | "declaration": true, 20 | // optional, non-configurable 21 | "jsx": "react", 22 | "jsxFactory": "Roact.createElement" 23 | }, 24 | "typeAcquisition": { 25 | "enable": true 26 | }, 27 | "include": [ 28 | "." 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/object-stack/README.md: -------------------------------------------------------------------------------- 1 | # ObjectStack 2 | 3 | A Stack data structure which is formed by creating a singly linked-list on already existing objects. All you need to do is add a `previousNode?: T` field to your object you wish to use in the Stack. 4 | 5 | See the [index.d.ts file](https://github.com/Validark/Roblox-TS-Libraries/blob/master/object-stack/index.d.ts) for documentation on each member and method. 6 | 7 | Demo: 8 | 9 | ```ts 10 | import ObjectStack from "@rbxts/object-stack"; 11 | 12 | interface Token { 13 | type: "a" | "b" | "c"; 14 | previousNode?: Token; 15 | } 16 | 17 | const objectStack = new ObjectStack(); 18 | 19 | objectStack.push({ type: "b" }); 20 | objectStack.push({ type: "a" }); 21 | objectStack.push({ type: "c" }); 22 | 23 | objectStack.pop(); // { type: "c" } 24 | objectStack.isEmpty(); // false 25 | 26 | // iterates through the stack, from top to bottom 27 | for (let token = objectStack.top; token; token = token.previousNode) { 28 | const x = token.type; 29 | } 30 | ``` 31 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/object-stack/init.d.ts: -------------------------------------------------------------------------------- 1 | /** A Stack data structure which is formed by creating a singly linked-list on already existing objects. 2 | * 3 | * Simply allow a `previousNode` field in the Object type which can point to another object of the same type. 4 | */ 5 | declare class ObjectStack< 6 | T extends { 7 | previousNode?: T; 8 | } 9 | > { 10 | /** The number of objects in the stack */ 11 | public readonly size: number; 12 | 13 | /** The object at the top of the Stack */ 14 | public readonly top?: T; 15 | 16 | /** Pushes a given element to the top of the stack. 17 | * Writes to its `previousNode` property the object which was on top of the stack beforehand. 18 | */ 19 | public push(item: T): void; 20 | 21 | /** Removes the top element from the stack, and if it exists, returns it. */ 22 | public pop(): T | undefined; 23 | 24 | /** Returns whether the Stack is empty. */ 25 | public isEmpty(): boolean; 26 | 27 | /** Returns an iterator which iterates through the stack from top to bottom. 28 | * 29 | * For optimal use, one can copy and paste this for loop: 30 | * 31 | * ```ts 32 | for (let current = this.top; current; current = current.previousNode) 33 | ``` 34 | */ 35 | public iterate(): IterableIterator; 36 | } 37 | 38 | export = ObjectStack; 39 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/object-stack/init.lua: -------------------------------------------------------------------------------- 1 | -- Generated by https://roblox-ts.github.io v0.0.34 2 | -- Compiled May 17, 2019, 7:38 PM Central Daylight Time 3 | 4 | local _exports; 5 | local ObjectStack; 6 | do 7 | ObjectStack = {}; 8 | ObjectStack.__index = { 9 | push = function(self, item) 10 | self.size = self.size + 1; 11 | item.previousNode = self.top; 12 | self.top = item; 13 | end; 14 | pop = function(self) 15 | local top = self.top; 16 | if top then 17 | self.size = self.size - 1; 18 | self.top = top.previousNode; 19 | return top; 20 | end; 21 | end; 22 | isEmpty = function(self) 23 | if self.top then 24 | return false; 25 | else 26 | return true; 27 | end; 28 | end; 29 | iterate = function(self) 30 | return { 31 | next = coroutine.wrap(function() 32 | do 33 | local current = self.top; 34 | while current do 35 | coroutine.yield({ 36 | value = current; 37 | done = false; 38 | }); 39 | current = current.previousNode; 40 | end; 41 | end; 42 | while true do coroutine.yield({ done = true }) end; 43 | end); 44 | }; 45 | end; 46 | }; 47 | ObjectStack.new = function(...) 48 | return ObjectStack.constructor(setmetatable({}, ObjectStack), ...); 49 | end; 50 | ObjectStack.constructor = function(self, ...) 51 | self.size = 0; 52 | return self; 53 | end; 54 | end; 55 | _exports = ObjectStack; 56 | return _exports; 57 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/object-stack/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rbx-object-stack", 3 | "version": "1.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@rbxts/types": { 8 | "version": "1.0.194", 9 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.194.tgz", 10 | "integrity": "sha512-r+ROYdFeI0rY6/7T9I+gKrorOuqsLPozsFhM8lrN4RJGF9zL6NWbvRcp/TgSu/Y3BWFGhOOUrtu3ppQR5TzdgA==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/object-stack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/object-stack", 3 | "version": "1.0.1", 4 | "description": "A Stack data structure which is formed by creating a singly linked-list on already existing objects.", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/object-stack" 13 | }, 14 | "keywords": [ 15 | "Roblox", 16 | "TypeScript", 17 | "Stack", 18 | "Data structures" 19 | ], 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 23 | }, 24 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/object-stack/README.md", 25 | "publishConfig": { 26 | "access": "public" 27 | }, 28 | "peerDependencies": { 29 | "@rbxts/types": "^1.0.194" 30 | }, 31 | "dependencies": { 32 | "@rbxts/types": "^1.0.194" 33 | }, 34 | "files": [ 35 | "init.lua", 36 | "index.d.ts", 37 | "README.md" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/object-stack/src/init.ts: -------------------------------------------------------------------------------- 1 | /** A Stack data structure which is formed by creating a singly linked-list on already existing objects. 2 | * 3 | * Simply allow a `previousNode` field in the Object type which can point to another object of the same type. 4 | */ 5 | class ObjectStack< 6 | T extends { 7 | previousNode?: T; 8 | } 9 | > { 10 | /** The number of objects in the stack */ 11 | public size: number = 0; 12 | 13 | /** The object at the top of the Stack */ 14 | public top?: T; 15 | 16 | /** Pushes a given element to the top of the stack. 17 | * Writes to its `previousNode` property the object which was on top of the stack beforehand. 18 | */ 19 | public push(item: T) { 20 | this.size++; 21 | item.previousNode = this.top; 22 | this.top = item; 23 | } 24 | 25 | /** Removes the top element from the stack, and if it exists, returns it. */ 26 | public pop() { 27 | const { top } = this; 28 | 29 | if (top) { 30 | this.size--; 31 | this.top = top.previousNode; 32 | return top; 33 | } 34 | } 35 | 36 | /** Returns whether the Stack is empty. */ 37 | public isEmpty() { 38 | return this.top ? false : true; 39 | } 40 | 41 | /** Returns an iterator which iterates through the stack from top to bottom. 42 | * 43 | * For optimal use, one can copy and paste the for loop contained in this function. 44 | */ 45 | public *iterate() { 46 | for (let current = this.top; current; current = current.previousNode) yield current; 47 | } 48 | } 49 | 50 | export = ObjectStack; 51 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/object-stack/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | // required 5 | "allowSyntheticDefaultImports": true, 6 | "downlevelIteration": true, 7 | "module": "commonjs", 8 | "noLib": true, 9 | "strict": true, 10 | "target": "es6", 11 | "typeRoots": [ 12 | "node_modules/@rbxts" 13 | ], 14 | // required, configurable 15 | "rootDir": "src", 16 | "outDir": "out", 17 | // optional 18 | "baseUrl": "src", 19 | "declaration": true, 20 | // optional, non-configurable 21 | "jsx": "react", 22 | "jsxFactory": "Roact.createElement" 23 | }, 24 | "typeAcquisition": { 25 | "enable": true 26 | }, 27 | "include": [ 28 | "." 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/sorted-array/index.d.ts: -------------------------------------------------------------------------------- 1 | declare class SortedArray { 2 | /** 3 | * Removes an element from index, returning that element. 4 | * @param index The index the element is at which should be removed 5 | */ 6 | public removeIndex(index: number): T; 7 | public forEach(); 8 | public map(); 9 | public some(); 10 | public every(); 11 | public reduce(); 12 | public reduceRight(); 13 | public filter(); 14 | public slice(); 15 | 16 | /** 17 | * Inserts an element in the proper place which would preserve the array's orderedness. Returns the index the element was inserted. 18 | * @param element The element to insert 19 | */ 20 | public insert(element: T): number; 21 | 22 | /** 23 | * Finds an Element in a SortedArray and returns its position (or nil if non-existant). 24 | * @param signature The element to match to the values in the SortedArray. 25 | * @param equalsFunction An optional equality checking function which takes values from the SortedArray as the first parameter and the signature as the second. 26 | * @param lessThanFunction An optional less-than checking function which takes values from the SortedArray as the first parameter and the signature as the second. Defaults to the comparison which was passed into the constructor. 27 | * @param U_0 The first possible index the element could occur. Defaults to the first index. 28 | * @param U_n The last possible index the element could occur. Defaults to the last index. 29 | */ 30 | public indexOf( 31 | signature: R, 32 | equalsFunction?: (a: T, b: R) => boolean, 33 | lessThanFunction?: (a: T, b: R) => boolean, 34 | U_0?: number, 35 | U_n?: number 36 | ); 37 | 38 | /** 39 | * Returns a raw array with the same values (shallow-copied). 40 | */ 41 | public copy(): Array; 42 | 43 | /** 44 | * Returns a clone of the sorted array 45 | */ 46 | public clone(): SortedArray; 47 | 48 | /** 49 | * Searches the array via SortedArray:indexOf(Signature, Eq, Lt). If found, it removes the value and returns the value, otherwise returns nil. Only removes a single occurence. 50 | * @param signature The element to match to the values in the SortedArray. 51 | * @param equalsFunction An optional equality checking function which takes values from the SortedArray as the first parameter and the signature as the second. 52 | * @param lessThanFunction An optional less-than checking function which takes values from the SortedArray as the first parameter and the signature as the second. Defaults to the comparison which was passed into the constructor. 53 | */ 54 | public removeElement( 55 | signature: R, 56 | equalsFunction?: (a: T, b: R) => boolean, 57 | lessThanFunction?: (a: T, b: R) => boolean 58 | ): T | undefined; 59 | 60 | /** 61 | * Calls table.sort, if you for some reason instantiated a SortedArray an avoided sorting it 62 | */ 63 | public sort(): void; 64 | 65 | /** 66 | * Removes the value at Index and re-inserts it. This is useful for when a value may have updated in a way that could change it's position in a SortedArray. Returns Index. 67 | * @param index 68 | */ 69 | public sortIndex(index: number): number; 70 | 71 | /** 72 | * Calls RemoveElement(Signature, Eq, Lt) and re-inserts the value. This is useful for when a value may have updated in a way that could change it's position in a SortedArray. Returns Index. 73 | * @param signature The element to match to the values in the SortedArray. 74 | * @param equalsFunction An optional equality checking function which takes values from the SortedArray as the first parameter and the signature as the second. 75 | * @param lessThanFunction An optional less-than checking function which takes values from the SortedArray as the first parameter and the signature as the second. Defaults to the comparison which was passed into the constructor. 76 | */ 77 | public sortElement( 78 | signature: R, 79 | equalsFunction?: (a: T, b: R) => boolean, 80 | lessThanFunction?: (a: T, b: R) => boolean 81 | ): number; 82 | /** 83 | * Returns a SortedArray of Commonalities between self and another SortedArray. If applicable, the returned SortedArray will inherit the Comparison function from self 84 | */ 85 | public getIntersection( 86 | SortedArray2: SortedArray, 87 | equalsFunction?: (a: T, b: T) => boolean, 88 | lessThanFunction?: (a: T, b: T) => boolean 89 | ): SortedArray; 90 | 91 | /** 92 | * Returns the first element of the SortedArray 93 | */ 94 | public front(): T; 95 | 96 | /** 97 | * Returns the last element of the SortedArray 98 | */ 99 | public back(): T; 100 | 101 | /** 102 | * Returns the middle element, averaging the middle two if applicable 103 | */ 104 | public median(): T; 105 | /** 106 | * Returns the first Quartile element, averaging the two contenders if applicable 107 | */ 108 | public quartile1(): T; 109 | /** 110 | * Returns the first Quartile element, averaging the two contenders if applicable 111 | */ 112 | public quartile3(): T; 113 | } 114 | 115 | export = SortedArray; 116 | -------------------------------------------------------------------------------- /archived-but-valueable-for-reference/sorted-array/init.lua: -------------------------------------------------------------------------------- 1 | -- Class that memoizes sorting by inserting values in order. Optimized for very large arrays. 2 | -- @source https://raw.githubusercontent.com/RoStrap/DataTypes/master/SortedArray.lua 3 | -- @rostrap SortedArray 4 | -- @documentation https://rostrap.github.io/Libraries/DataTypes/SortedArray/ 5 | -- @author Validark 6 | 7 | local TS = require( 8 | game:GetService("ReplicatedStorage") 9 | :WaitForChild("RobloxTS") 10 | :WaitForChild("Include") 11 | :WaitForChild("RuntimeLib") 12 | ); 13 | 14 | local sort = table.sort 15 | local insert = table.insert 16 | 17 | local SortedArray = {} 18 | SortedArray.__index = {} 19 | 20 | SortedArray.__index.removeIndex = table.remove 21 | SortedArray.__index.forEach = TS.array_forEach 22 | SortedArray.__index.map = TS.array_map 23 | SortedArray.__index.some = TS.array_some 24 | SortedArray.__index.every = TS.array_every 25 | SortedArray.__index.reduce = TS.array_reduce 26 | SortedArray.__index.reduceRight = TS.array_reduceRight 27 | 28 | function SortedArray.__index:filter(...) 29 | local this = setmetatable(TS.array_filter(self, ...), SortedArray) 30 | this.Comparison = self.Comparison 31 | return this 32 | end 33 | 34 | function SortedArray.__index:slice(...) 35 | local this = setmetatable(TS.array_slice(self, ...), SortedArray) 36 | this.Comparison = self.Comparison 37 | return this 38 | end 39 | 40 | function SortedArray.new(self, Comparison) 41 | if self then 42 | sort(self, Comparison) 43 | else 44 | self = {} 45 | end 46 | 47 | self.Comparison = Comparison 48 | return setmetatable(self, SortedArray) 49 | end 50 | 51 | local function FindClosest(self, Value, Low, High, Eq, Lt) 52 | local Middle do 53 | local Sum = Low + High 54 | Middle = (Sum - Sum % 2) / 2 55 | end 56 | 57 | if Middle == 0 then 58 | return nil 59 | end 60 | 61 | local Compare = Lt or self.Comparison 62 | local Value2 = self[Middle] 63 | 64 | while Middle ~= High do 65 | if Eq then 66 | if Eq(Value, Value2) then 67 | return Middle 68 | end 69 | elseif Value == Value2 then 70 | return Middle 71 | end 72 | 73 | local Bool 74 | 75 | if Compare then 76 | Bool = Compare(Value, Value2) 77 | else 78 | Bool = Value < Value2 79 | end 80 | 81 | if Bool then 82 | High = Middle - 1 83 | else 84 | Low = Middle + 1 85 | end 86 | 87 | local Sum = Low + High 88 | Middle = (Sum - Sum % 2) / 2 89 | Value2 = self[Middle] 90 | end 91 | 92 | return Middle 93 | end 94 | 95 | function SortedArray.__index:insert(Value) 96 | -- Inserts a Value into the SortedArray while maintaining its sortedness 97 | 98 | local Position = FindClosest(self, Value, 1, #self) 99 | local Value2 = self[Position] 100 | 101 | if Value2 then 102 | local Compare = self.Comparison 103 | local Bool 104 | 105 | if Compare then 106 | Bool = Compare(Value, Value2) 107 | else 108 | Bool = Value < Value2 109 | end 110 | 111 | Position = Bool and Position or Position + 1 112 | else 113 | Position = 1 114 | end 115 | 116 | insert(self, Position, Value) 117 | 118 | return Position - 1 119 | end 120 | 121 | function SortedArray.__index:indexOf(Value, Eq, Lt, U_0, U_n) 122 | -- Finds a Value in a SortedArray and returns its position (or nil if non-existant) 123 | 124 | local Position = FindClosest(self, Value, U_0 or 1, U_n or #self, Eq, Lt) 125 | 126 | local Bool 127 | 128 | if Position then 129 | if Eq then 130 | Bool = Eq(Value, self[Position]) 131 | else 132 | Bool = Value == self[Position] 133 | end 134 | end 135 | 136 | return Bool and Position - 1 or nil 137 | end 138 | 139 | function SortedArray.__index:copy() 140 | local New = {} 141 | 142 | for i = 1, #self do 143 | New[i] = self[i] 144 | end 145 | 146 | return New 147 | end 148 | 149 | function SortedArray.__index:clone() 150 | local New = {} 151 | 152 | for i = 1, #self do 153 | New[i] = self[i] 154 | end 155 | 156 | New.Comparison = self.Comparison 157 | return setmetatable(New, SortedArray) 158 | end 159 | 160 | function SortedArray.__index:removeElement(Signature, Eq, Lt) 161 | local Position = self:indexOf(Signature, Eq, Lt) 162 | 163 | if Position then 164 | return self:removeIndex(Position) 165 | end 166 | end 167 | 168 | function SortedArray.__index:sort() 169 | sort(self, self.Comparison) 170 | end 171 | 172 | function SortedArray.__index:sortIndex(Index) 173 | -- Sorts a single element at number Index 174 | -- Useful for when a single element is somehow altered such that it should get a new position in the array 175 | 176 | return self:insert(self:removeIndex(Index)) 177 | end 178 | 179 | function SortedArray.__index:sortElement(Signature, Eq, Lt) 180 | -- Sorts a single element if it exists 181 | -- Useful for when a single element is somehow altered such that it should get a new position in the array 182 | 183 | return self:insert(self:removeElement(Signature, Eq, Lt)) 184 | end 185 | 186 | function SortedArray.__index:getIntersection(SortedArray2, Eq, Lt) 187 | -- Returns a SortedArray of Commonalities between self and another SortedArray 188 | -- If applicable, the returned SortedArray will inherit the Comparison function from self 189 | 190 | if SortedArray ~= getmetatable(SortedArray2) then error("bad argument #2 to GetIntersection: expected SortedArray, got " .. typeof(SortedArray2) .. " " .. tostring(SortedArray2)) end 191 | local Commonalities = SortedArray.new(nil, self.Comparison) 192 | local Count = 0 193 | local Position = 1 194 | local NumSelf = #self 195 | local NumSortedArray2 = #SortedArray2 196 | 197 | if NumSelf > NumSortedArray2 then -- Iterate through the shorter SortedArray 198 | NumSelf, NumSortedArray2 = NumSortedArray2, NumSelf 199 | self, SortedArray2 = SortedArray2, self 200 | end 201 | 202 | for i = 1, NumSelf do 203 | local Current = self[i] 204 | local CurrentPosition = SortedArray2:indexOf(Current, Eq, Lt, Position, NumSortedArray2) 205 | 206 | if CurrentPosition then 207 | Position = CurrentPosition 208 | Count = Count + 1 209 | Commonalities[Count] = Current 210 | end 211 | end 212 | 213 | return Commonalities 214 | end 215 | 216 | local function GetMedian(self, a, b) 217 | local c = a + b 218 | 219 | if c % 2 == 0 then 220 | return self[c / 2] 221 | else 222 | local d = (c - 1) / 2 223 | return (self[d] + self[d + 1]) / 2 224 | end 225 | end 226 | 227 | -- Five number summary Functions 228 | function SortedArray.__index:front() return self[1] end 229 | function SortedArray.__index:back() return self[#self] end 230 | function SortedArray.__index:median() return GetMedian(self, 1, #self) end 231 | function SortedArray.__index:quartile1() local n = #self return GetMedian(self, 1, (n - n % 2) / 2) end 232 | function SortedArray.__index:quartile3() local n = #self return GetMedian(self, 1 + (n + n % 2) / 2, n) end 233 | 234 | return SortedArray 235 | -------------------------------------------------------------------------------- /cubic-bezier/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Gaëtan Renaudeau 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /cubic-bezier/README.md: -------------------------------------------------------------------------------- 1 | # Bezier 2 | 3 | Used to create Bezier functions. 4 | 5 | ## API 6 | ```ts 7 | import Bezier from "cubic-bezier"; 8 | 9 | const EasingFunc = new Bezier(0.17, 0.67, 0.83, 0.67); 10 | 11 | EasingFunc(0) // 0 12 | EasingFunc(0.5) // something in between 13 | EasingFunc(1) // 1 14 | ``` 15 | Test and generate Bezier curves here at [cubic-bezier.com](http://cubic-bezier.com/) or at [greweb.me](http://greweb.me/bezier-easing-editor/example/) 16 | Credit: Math borrowed from [here](https://github.com/gre/bezier-easing). 17 | -------------------------------------------------------------------------------- /cubic-bezier/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns a value between [initialValue, initialValue + changeInValue] or [0, 1] that represents an in-between value 3 | * along a Bezier curve. 4 | * @param elapsedTime the time elapsed [0, d] 5 | * @param initialValue beginning value being interpolated (default = 0) 6 | * @param changeInValue change in value being interpolated (equivalent to: ending - beginning) (default = 1) 7 | * @param totalDuration duration interpolation is occurring over (default = 1) 8 | */ 9 | type Bezier = (elapsedTime: number, initialValue?: number, changeInValue?: number, totalDuration?: number) => number; 10 | declare const Bezier: new (x1: number, y1: number, x2: number, y2: number) => Bezier; 11 | export = Bezier; 12 | -------------------------------------------------------------------------------- /cubic-bezier/init.lua: -------------------------------------------------------------------------------- 1 | -- Smooth Interpolation Curve Generator 2 | -- @author Validark 3 | -- @original https://github.com/gre/bezier-easing 4 | -- Copyright (c) 2014 Gaëtan Renaudeau, MIT License (see bottom for full license) 5 | -- @testsite http://cubic-bezier.com/ 6 | -- @testsite http://greweb.me/bezier-easing-editor/example/ 7 | 8 | -- Bezier.new(x1, y1, x2, y2) 9 | -- @param numbers (x1, y1, x2, y2) The control points of your curve 10 | -- @returns function(t [b, c, d]) 11 | -- @param number t the time elapsed [0, d] 12 | -- @param number b beginning value being interpolated (default = 0) 13 | -- @param number c change in value being interpolated (equivalent to: ending - beginning) (default = 1) 14 | -- @param number d duration interpolation is occurring over (default = 1) 15 | 16 | -- These values are established by empiricism with tests (tradeoff: performance VS precision) 17 | local NEWTON_ITERATIONS = 4 18 | local NEWTON_MIN_SLOPE = 0.001 19 | local SUBDIVISION_PRECISION = 0.0000001 20 | local SUBDIVISION_MAX_ITERATIONS = 10 21 | local K_SPLINE_TABLE_SIZE = 11 22 | 23 | local K_SAMPLE_STEP_SIZE = 1 / (K_SPLINE_TABLE_SIZE - 1) 24 | 25 | local function Linear(t, b, c, d) 26 | if d ~= nil then t = t / d end 27 | return (c or 1)*math.clamp(t, 0, 1) + (b or 0) 28 | end 29 | 30 | local Bezier = {} 31 | 32 | function Bezier.new(x1, y1, x2, y2) 33 | -- TS cannot (reasonably) protect against this 34 | if not (0 <= x1 and x1 <= 1 and 0 <= x2 and x2 <= 1) then error("The x values must be within range [0, 1]") end 35 | 36 | if x1 == y1 and x2 == y2 then 37 | return Linear 38 | end 39 | 40 | -- Precompute redundant values 41 | local e, f = 3*x1, 3*x2 42 | local g, h, i = 1 - f + e, f - 2*e, 3*(1 - f + e) 43 | local j, k = 2*h, 3*y1 44 | local l, m = 1 - 3*y2 + k, 3*y2 - 2*k 45 | 46 | -- Precompute samples table 47 | local SampleValues = {} 48 | for a = 0, K_SPLINE_TABLE_SIZE - 1 do 49 | local z = a*K_SAMPLE_STEP_SIZE 50 | SampleValues[a] = ((g*z + h)*z + e)*z -- CalcBezier 51 | end 52 | 53 | return function(t, b, c, d) 54 | if d ~= nil then t = t / d end 55 | t = math.clamp(t, 0, 1) -- Make sure the endpoints are correct 56 | 57 | local CurrentSample = K_SPLINE_TABLE_SIZE - 2 58 | 59 | for a = 1, CurrentSample do 60 | if SampleValues[a] > t then 61 | CurrentSample = a - 1 62 | break 63 | end 64 | end 65 | 66 | -- Interpolate to provide an initial guess for t 67 | local IntervalStart = CurrentSample*K_SAMPLE_STEP_SIZE 68 | local GuessForT = IntervalStart + K_SAMPLE_STEP_SIZE*(t - SampleValues[CurrentSample]) / (SampleValues[CurrentSample + 1] - SampleValues[CurrentSample]) 69 | local InitialSlope = (i*GuessForT + j)*GuessForT + e 70 | 71 | if InitialSlope >= NEWTON_MIN_SLOPE then 72 | for NewtonRaphsonIterate = 1, NEWTON_ITERATIONS do 73 | local CurrentSlope = (i*GuessForT + j)*GuessForT + e 74 | if CurrentSlope == 0 then break end 75 | GuessForT = GuessForT - (((g*GuessForT + h)*GuessForT + e)*GuessForT - t) / CurrentSlope 76 | end 77 | elseif InitialSlope ~= 0 then 78 | local IntervalStep = IntervalStart + K_SAMPLE_STEP_SIZE 79 | 80 | for BinarySubdivide = 1, SUBDIVISION_MAX_ITERATIONS do 81 | GuessForT = IntervalStart + 0.5*(IntervalStep - IntervalStart) 82 | local BezierCalculation = ((g*GuessForT + h)*GuessForT + e)*GuessForT - t 83 | 84 | if BezierCalculation > 0 then 85 | IntervalStep = GuessForT 86 | else 87 | IntervalStart = GuessForT 88 | BezierCalculation = -BezierCalculation 89 | end 90 | 91 | if BezierCalculation <= SUBDIVISION_PRECISION then break end 92 | end 93 | end 94 | 95 | t = ((l*GuessForT + m)*GuessForT + k)*GuessForT 96 | return (c or 1)*t + (b or 0) 97 | end 98 | end 99 | 100 | return Bezier 101 | 102 | --[[ 103 | Copyright (c) 2014 Gaëtan Renaudeau 104 | Permission is hereby granted, free of charge, to any person 105 | obtaining a copy of this software and associated documentation 106 | files (the "Software"), to deal in the Software without 107 | restriction, including without limitation the rights to use, 108 | copy, modify, merge, publish, distribute, sublicense, and/or sell 109 | copies of the Software, and to permit persons to whom the 110 | Software is furnished to do so, subject to the following 111 | conditions: 112 | The above copyright notice and this permission notice shall be 113 | included in all copies or substantial portions of the Software. 114 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 115 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 116 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 117 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 118 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 119 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 120 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 121 | OTHER DEALINGS IN THE SOFTWARE. 122 | --]] 123 | -------------------------------------------------------------------------------- /cubic-bezier/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/cubic-bezier", 3 | "version": "1.1.0", 4 | "description": "A library for generating smooth two-dimensional interpolation curves", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/cubic-bezier" 13 | }, 14 | "keywords": [ 15 | "bezier", 16 | "cubic-bezier", 17 | "curve", 18 | "interpolation", 19 | "interpolate", 20 | "easing", 21 | "ease", 22 | "lerp", 23 | "tween", 24 | "roblox" 25 | ], 26 | "author": "Validark", 27 | "license": "ISC", 28 | "bugs": { 29 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 30 | }, 31 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/cubic-bezier/README.md", 32 | "publishConfig": { 33 | "access": "public" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /cue/README.md: -------------------------------------------------------------------------------- 1 | # Cue 2 | A lightweight custom event library, optimized for speed. The implementation is very straightforward, thus I recommend looking at [the source](https://github.com/Validark/Roblox-TS-Libraries/blob/master/cue/init.lua). 3 | 4 | View the type definitions with [documentation here](https://github.com/Validark/Roblox-TS-Libraries/blob/master/cue/index.d.ts) 5 | 6 | All functions of `Cue` could map to older `Signal` implementations. 7 | 8 | ```ts 9 | Lua -> TS 10 | Signal:Fire(args) -> Cue.go(args) 11 | Signal:Connect(func) -> Cue.bind(func) 12 | SignalConnection:Disconnect() -> Cue.unbind(func) 13 | Signal:Destroy() -> Cue.unbindAll() 14 | ``` 15 | 16 | This implementation cuts out separate objects being returned for the purpose of disconnecting functions, and instead expects the programmer to pass in the callback they wish to `unbind`. 17 | 18 | # Demo 19 | ```ts 20 | // Imports the module cue as `Cue` 21 | import Cue from "@rbxts/cue"; 22 | 23 | // Instantiates a new Cue: 24 | const cue = new Cue<(bool: boolean, count: number) => void>(); 25 | 26 | // Makes a new function we are going to bind to the cue 27 | const printArgs = (bool: boolean) => print(bool); 28 | 29 | // Binds the printArgs function to this cue 30 | cue.bind(printArgs); 31 | 32 | // Runs all bound functions for this cue 33 | cue.go(true, 5); // --> prints true 34 | 35 | // Unbinds the printArgs function from this cue 36 | cue.unbind(printArgs); 37 | ``` 38 | Corresponding Lua equivalent: 39 | ```lua 40 | -- Imports the module rbx-cue as `Cue` 41 | local Cue = require(TS.getModule("cue", script.Parent)); 42 | 43 | -- Instantiates a new Cue: 44 | local cue = Cue.new(); 45 | 46 | -- Makes a new function we are going to bind to the cue 47 | local printArgs = function(bool) 48 | return print(bool); 49 | end; 50 | 51 | -- Binds the printArgs function to this cue 52 | cue:bind(printArgs); 53 | 54 | -- Runs all bound functions for this cue 55 | cue:go(true, 5); --> prints true 56 | 57 | -- Unbinds the printArgs function from this cue 58 | cue:unbind(printArgs); 59 | ``` 60 | 61 | ## Rationale 62 | 63 | This library prioritizes being extraordinarily light-weight, and reflects that. 64 | 65 | ##### Due to limitations of using coroutine.yield, this library removes any kind of `:Wait()` method which could lead to unexpected problems for the developer. Here is my source on that, from evaera: 66 | > ###### Any C-side code that invokes user code then "waits" for the user code to yield back to the C-side code will be broken, because of the way Roblox models this idea: continuations. Continuations are essentially a set of instructions that travels along with the thread and instructs the C-functions what to do when they're done running. C-side yield functions have special machinery to pass along these continuations when they are invoked. However, `coroutine.resume` has no concept of continuations, so when you use it to resume a thread, the continuations are effectively discarded. This means that any pending jobs that were meant to run when the user thread finishes running will never actually run, thus potentially leaving those dependent threads stuck in a yielded state forever. 67 | > ###### For an example of this behavior in action, consider the `require` function. It is a yield function and when it invokes the module thread it passes along a continuation task that asks that thread to resume the requiring script when it's done. This all happens behind the scenes and you probably wouldn't realize any of this happens, because if you have a `wait(2); return nil` in that module, it waits for two seconds and then you get `nil` back from `require` in the requiring module. 68 | > ###### But if you instead use `coroutine.yield()` to yield the module thread and then resume it later with `coroutine.resume`, since that function has effectively discarded any pending continuations that the thread had, the `require` call from the requiring script will never return a value and that script will be stuck forever in a yielded state. 69 | > ###### This behavior isn't just isolated to `require`, though. Any time any C-side code invokes user code and waits for it, these caveats will apply. The advantage of using `BindableEvent:Wait` is that it supports continuations, whereas the coroutine library does not. 70 | -------------------------------------------------------------------------------- /cue/index.d.ts: -------------------------------------------------------------------------------- 1 | /** A lightweight custom event library */ 2 | interface Cue void, Generic = false> { 3 | /** 4 | * Gives a cue to fire all bound functions. 5 | * @param args The arguments to pass into bound functions 6 | */ 7 | go(...args: FunctionArguments): void; 8 | 9 | /** 10 | * Binds a function to be run on cue. 11 | * @param callback The function which should be called when the event fires 12 | */ 13 | bind = FunctionArguments>( 14 | callback?: Generic extends true 15 | ? (FunctionArguments extends Array 16 | ? (...args: O) => void 17 | : BoundFunctionSignature) 18 | : BoundFunctionSignature, 19 | ): void; 20 | 21 | /** 22 | * Unbinds a function from being run on cue. 23 | * @param callback The function which, if bound, will be unbound from the event 24 | */ 25 | unbind = FunctionArguments>( 26 | callback?: Generic extends true 27 | ? (FunctionArguments extends Array 28 | ? (...args: O) => void 29 | : BoundFunctionSignature) 30 | : BoundFunctionSignature, 31 | ): void; 32 | 33 | /** 34 | * Unbinds all functions from this event, especially in preparation for garbage-collection. 35 | */ 36 | unbindAll(): void; 37 | } 38 | 39 | declare const Cue: new void, Generic = false>() => Cue; 40 | export = Cue; 41 | -------------------------------------------------------------------------------- /cue/init.lua: -------------------------------------------------------------------------------- 1 | -- The lightest Event library ever 2 | -- Pretty straightforward 3 | -- @author Validark 4 | 5 | local Cue = {} 6 | Cue.__index = Cue 7 | 8 | function Cue.new() 9 | return setmetatable({}, Cue) 10 | end 11 | 12 | function Cue:go(...) 13 | for i = 1, #self do 14 | coroutine.resume(coroutine.create(self[i]), ...) 15 | end 16 | end 17 | 18 | function Cue:bind(Function) 19 | self[#self + 1] = Function 20 | end 21 | 22 | function Cue:unbind(Function) 23 | local n = #self 24 | 25 | for i = 1, n do 26 | if Function == self[i] then 27 | self[i] = self[n] 28 | self[n] = nil 29 | break 30 | end 31 | end 32 | end 33 | 34 | function Cue:unbindAll() 35 | for i = 1, #self do 36 | self[i] = nil 37 | end 38 | end 39 | 40 | return Cue 41 | -------------------------------------------------------------------------------- /cue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/cue", 3 | "version": "1.0.1", 4 | "description": "The fastest and lightest Event object ever made", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/cue" 13 | }, 14 | "keywords": [ 15 | "Roblox", 16 | "TypeScript", 17 | "roblox-TS", 18 | "event", 19 | "signal", 20 | "connection" 21 | ], 22 | "author": "Validark", 23 | "license": "ISC", 24 | "bugs": { 25 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 26 | }, 27 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/cue/README.md", 28 | "publishConfig": { 29 | "access": "public" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /easing-functions/README.md: -------------------------------------------------------------------------------- 1 | # EasingFunctions 2 | 3 | A bunch of reuseable Easing Functions, including those from the Material Design specification ([Standard, Acceleration, and Deceleration](https://material.io/design/motion/speed.html#easing)) 4 | 5 | These are the available EasingFunctions: 6 | 7 | | Directionless | In | Out | InOut | OutIn | 8 | |:----------------:|:---------:|:----------:|:------------:|:------------:| 9 | | Linear | InQuad | OutQuad | InOutQuad | OutInQuad | 10 | | Spring | InCubic | OutCubic | InOutCubic | OutInCubic | 11 | | SoftSpring | InQuart | OutQuart | InOutQuart | OutInQuart | 12 | | RevBack | InQuint | OutQuint | InOutQuint | OutInQuint | 13 | | RidiculousWiggle | InSine | OutSine | InOutSine | OutInSine | 14 | | Smooth | InExpo | OutExpo | InOutExpo | OutInExpo | 15 | | Smoother | InCirc | OutCirc | InOutCirc | OutInCirc | 16 | | Acceleration | InElastic | OutElastic | InOutElastic | OutInElastic | 17 | | Deceleration | InBack | OutBack | InOutBack | OutInBack | 18 | | Sharp | InBounce | OutBounce | InOutBounce | OutInBounce | 19 | | Standard | 20 | 21 | Signature: 22 | 23 | ```ts 24 | /** 25 | * Returns a value between [initialValue, initialValue + changeInValue] or [0, 1] that represents an in-between value 26 | * along a Bezier curve. 27 | * @param elapsedTime the time elapsed [0, d] 28 | * @param initialValue beginning value being interpolated (default = 0) 29 | * @param changeInValue change in value being interpolated (equivalent to: ending - beginning) (default = 1) 30 | * @param totalDuration duration interpolation is occurring over (default = 1) 31 | */ 32 | type BasicEasingFunction = ( 33 | elapsedTime: number, 34 | initialValue: number, 35 | changeInValue: number, 36 | totalDuration: number, 37 | ) => number; 38 | ``` 39 | 40 | Some functions have an extra parameter or two. These will either be the amplitude and period or the overshoot. 41 | -------------------------------------------------------------------------------- /easing-functions/index.d.ts: -------------------------------------------------------------------------------- 1 | import Bezier from "@rbxts/cubic-bezier"; 2 | 3 | /** 4 | * Returns a value between [initialValue, initialValue + changeInValue] or [0, 1] that represents an in-between value 5 | * along a Bezier curve. 6 | * @param elapsedTime the time elapsed [0, d] 7 | * @param initialValue beginning value being interpolated (default = 0) 8 | * @param changeInValue change in value being interpolated (equivalent to: ending - beginning) (default = 1) 9 | * @param totalDuration duration interpolation is occurring over (default = 1) 10 | */ 11 | type BasicEasingFunction = ( 12 | elapsedTime: number, 13 | initialValue: number, 14 | changeInValue: number, 15 | totalDuration: number, 16 | ) => number; 17 | 18 | /** 19 | * Returns a value between [initialValue, initialValue + changeInValue] or [0, 1] that represents an in-between value 20 | * along a Bezier curve. 21 | * @param elapsedTime the time elapsed [0, d] 22 | * @param initialValue beginning value being interpolated (default = 0) 23 | * @param changeInValue change in value being interpolated (equivalent to: ending - beginning) (default = 1) 24 | * @param totalDuration duration interpolation is occurring over (default = 1) 25 | * @param amplitude The amplitude of the curve 26 | * @param period The period of the curve 27 | */ 28 | type PeriodicEasingFunction = ( 29 | elapsedTime: number, 30 | initialValue: number, 31 | changeInValue: number, 32 | totalDuration: number, 33 | amplitude: number, 34 | period: number, 35 | ) => number; 36 | 37 | /** 38 | * Returns a value between [initialValue, initialValue + changeInValue] or [0, 1] that represents an in-between value 39 | * along a Bezier curve. 40 | * @param elapsedTime the time elapsed [0, d] 41 | * @param initialValue beginning value being interpolated (default = 0) 42 | * @param changeInValue change in value being interpolated (equivalent to: ending - beginning) (default = 1) 43 | * @param totalDuration duration interpolation is occurring over (default = 1) 44 | * @param overshoot The overshoot of the curve 45 | */ 46 | type OvershootEasingFunction = ( 47 | elapsedTime: number, 48 | initialValue: number, 49 | changeInValue: number, 50 | totalDuration: number, 51 | overshoot: number, 52 | ) => number; 53 | 54 | export const Sharp: Bezier; 55 | export const Standard: Bezier; 56 | export const Acceleration: Bezier; 57 | export const Deceleration: Bezier; 58 | 59 | export const Linear: BasicEasingFunction; 60 | export const Smooth: BasicEasingFunction; 61 | export const Smoother: BasicEasingFunction; 62 | export const RevBack: BasicEasingFunction; 63 | export const RidiculousWiggle: BasicEasingFunction; 64 | export const Spring: BasicEasingFunction; 65 | export const SoftSpring: BasicEasingFunction; 66 | export const InQuad: BasicEasingFunction; 67 | export const OutQuad: BasicEasingFunction; 68 | export const InOutQuad: BasicEasingFunction; 69 | export const OutInQuad: BasicEasingFunction; 70 | export const InCubic: BasicEasingFunction; 71 | export const OutCubic: BasicEasingFunction; 72 | export const InOutCubic: BasicEasingFunction; 73 | export const OutInCubic: BasicEasingFunction; 74 | export const InQuart: BasicEasingFunction; 75 | export const OutQuart: BasicEasingFunction; 76 | export const InOutQuart: BasicEasingFunction; 77 | export const OutInQuart: BasicEasingFunction; 78 | export const InQuint: BasicEasingFunction; 79 | export const OutQuint: BasicEasingFunction; 80 | export const InOutQuint: BasicEasingFunction; 81 | export const OutInQuint: BasicEasingFunction; 82 | export const InSine: BasicEasingFunction; 83 | export const OutSine: BasicEasingFunction; 84 | export const InOutSine: BasicEasingFunction; 85 | export const OutInSine: BasicEasingFunction; 86 | export const InExpo: BasicEasingFunction; 87 | export const OutExpo: BasicEasingFunction; 88 | export const InOutExpo: BasicEasingFunction; 89 | export const OutInExpo: BasicEasingFunction; 90 | export const InCirc: BasicEasingFunction; 91 | export const OutCirc: BasicEasingFunction; 92 | export const InOutCirc: BasicEasingFunction; 93 | export const OutInCirc: BasicEasingFunction; 94 | export const OutBounce: BasicEasingFunction; 95 | export const InBounce: BasicEasingFunction; 96 | export const InOutBounce: BasicEasingFunction; 97 | export const OutInBounce: BasicEasingFunction; 98 | 99 | export const OutElastic: PeriodicEasingFunction; 100 | export const InElastic: PeriodicEasingFunction; 101 | export const InOutElastic: PeriodicEasingFunction; 102 | export const OutInElastic: PeriodicEasingFunction; 103 | 104 | export const InBack: OvershootEasingFunction; 105 | export const OutBack: OvershootEasingFunction; 106 | export const InOutBack: OvershootEasingFunction; 107 | export const OutInBack: OvershootEasingFunction; 108 | -------------------------------------------------------------------------------- /easing-functions/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/easing-functions", 3 | "version": "1.1.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@rbxts/cubic-bezier": { 8 | "version": "1.0.2", 9 | "resolved": "https://registry.npmjs.org/@rbxts/cubic-bezier/-/cubic-bezier-1.0.2.tgz", 10 | "integrity": "sha512-g2z5pqUN1f8BO8nqjTbQyfIwVj9K2SoCLNPKmlez2iYqOpMn4VyNPPgac7f1y6MJrenqAa4BWMmTWwuI07gmLg==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /easing-functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/easing-functions", 3 | "version": "1.1.0", 4 | "description": "A bunch of reuseable Easing Functions, including those from the Material Design specification and Robert Penner.", 5 | "main": "init.lua", 6 | "typings": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/easing-functions" 13 | }, 14 | "keywords": [ 15 | "bezier", 16 | "cubic-bezier", 17 | "curve", 18 | "interpolation", 19 | "interpolate", 20 | "easing", 21 | "ease", 22 | "lerp", 23 | "tween", 24 | "roblox" 25 | ], 26 | "bundledDependencies": [], 27 | "peerDependencies": { 28 | "@rbxts/cubic-bezier": "^1.0.2" 29 | }, 30 | "author": "Validark", 31 | "license": "ISC", 32 | "bugs": { 33 | "url": "https://github.com/Validark/Roblox-TS-libraries/issues" 34 | }, 35 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/easing-functions/README.md", 36 | "publishConfig": { 37 | "access": "public" 38 | }, 39 | "dependencies": { 40 | "@rbxts/cubic-bezier": "^1.0.2" 41 | }, 42 | "files": [ 43 | "init.lua", 44 | "index.d.ts", 45 | "README.md" 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /extendable-resources/README.md: -------------------------------------------------------------------------------- 1 | # Extendable Resources 2 | 3 | A library for defining resources so it can be easily imported by other modules. 4 | 5 | The main function is called `makeFolderManager`. Its documentation is as follows: 6 | 7 | ##### Finds a folder called folderName in folderParent, and returns a function which searches this folder for an instance with a given name. If this instance does not exist on the client, the function will yield. If it does not exist on the server, it will generate an instance of type optionalInstanceType or error. 8 | 9 | ###### @param folderParent — The parent to search for the folder in 10 | 11 | ###### @param folderName — The name of the folder to search for 12 | 13 | ###### @param optionalInstanceType - The instance type which can be generated if the instance does not exist and is on the server 14 | 15 | Usage: 16 | 17 | ```ts 18 | import { ReplicatedStorage } from "@rbxts/services"; 19 | import makeFolderManager from "@rbxts/extendable-resources"; 20 | 21 | // creates/finds a folder named "RemoteEvents" in ReplicatedStorage 22 | // if it can't find a particular object, it will instantiate a RemoteEvent with the proper name 23 | const getRemoteEvent = makeFolderManager(ReplicatedStorage, "RemoteEvents", "RemoteEvent"); 24 | 25 | export const Chatted = getRemoteEvent("Chatted"); 26 | export const Cleanup = getRemoteEvent("Cleanup"); 27 | 28 | // or in another library 29 | 30 | // Not necessary to specify "Model", but it will force returned values to be a Model type 31 | const getMap = makeFolderManager<"Model">(ReplicatedStorage, "Maps"); 32 | 33 | export const SFOTH = getMap("SFOTH"); 34 | export const City = getMap("City"); 35 | ``` 36 | 37 | Then you can easily import the needed resources into another library: 38 | 39 | ```ts 40 | import { Chatted, Cleanup } from "ReplicatedStorage/RemoteEventDefinitions"; 41 | ``` 42 | -------------------------------------------------------------------------------- /extendable-resources/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /** 3 | * Finds a folder called folderName in folderParent, 4 | * and returns a function which searches this folder for an instance with a given name. 5 | * If this instance does not exist on the client, the function will yield. 6 | * If it does not exist on the server, it will generate an instance of type optionalInstanceType or error. 7 | * 8 | * @param folderParent The parent to search for the folder in 9 | * @param folderName The name of the folder to search for 10 | * @param optionalInstanceType The instance type which can be generated if the instance does not 11 | * exist and is on the server 12 | */ 13 | declare function makeFolderManager(folderParent: Instance, folderName: string, optionalInstanceType?: T): (instanceName: string) => CreatableInstances[T]; 14 | export = makeFolderManager; 15 | -------------------------------------------------------------------------------- /extendable-resources/init.lua: -------------------------------------------------------------------------------- 1 | -- Compiled with roblox-ts v1.2.3 2 | -- @author Validark 3 | local isServer = game:GetService("RunService"):IsServer() 4 | --[[ 5 | * 6 | * Wraps a function which takes a single argument as a parameter, and makes it idempotent. 7 | * The new function will return the value in the cache, else it will call the wrapped function and cache the result 8 | * @param func The function to wrap 9 | ]] 10 | local function cacheSingleArgFunc(func) 11 | local cache = {} 12 | return function(arg) 13 | local value = cache[arg] 14 | if value == nil then 15 | value = func(arg) 16 | local _value = value 17 | -- ▼ Map.set ▼ 18 | cache[arg] = _value 19 | -- ▲ Map.set ▲ 20 | end 21 | return value 22 | end 23 | end 24 | local function constructManager(folder, optionalInstanceType) 25 | if isServer then 26 | return function(instanceName) 27 | local target = folder:FindFirstChild(instanceName) 28 | if target == nil then 29 | if optionalInstanceType then 30 | target = Instance.new(optionalInstanceType) 31 | target.Name = instanceName 32 | target.Parent = folder 33 | else 34 | return error("Failed to find " .. instanceName .. " in " .. folder.Name) 35 | end 36 | end 37 | return target 38 | end 39 | else 40 | return function(instanceName) 41 | return folder:WaitForChild(instanceName) 42 | end 43 | end 44 | end 45 | local getFolderGetter = cacheSingleArgFunc(function(folderParent) 46 | return constructManager(folderParent, "Folder") 47 | end) 48 | --[[ 49 | * 50 | * Finds a folder called folderName in folderParent, 51 | * and returns a function which searches this folder for an instance with a given name. 52 | * If this instance does not exist on the client, the function will yield. 53 | * If it does not exist on the server, it will generate an instance of type optionalInstanceType or error. 54 | * 55 | * @param folderParent The parent to search for the folder in 56 | * @param folderName The name of the folder to search for 57 | * @param optionalInstanceType The instance type which can be generated if the instance does not 58 | * exist and is on the server 59 | ]] 60 | local function makeFolderManager(folderParent, folderName, optionalInstanceType) 61 | return constructManager(getFolderGetter(folderParent)(folderName), optionalInstanceType) 62 | end 63 | return makeFolderManager 64 | -------------------------------------------------------------------------------- /extendable-resources/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/extendable-resources", 3 | "version": "1.0.2", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@rbxts/extendable-resources", 9 | "version": "1.0.2", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@rbxts/compiler-types": "^1.2.3-types.1", 13 | "@rbxts/types": "^1.0.537" 14 | } 15 | }, 16 | "node_modules/@rbxts/compiler-types": { 17 | "version": "1.2.3-types.1", 18 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.2.3-types.1.tgz", 19 | "integrity": "sha512-SXXIBazyJ7N6d2xcy471/kqZZpCv7EDOWrRJ45jcv3g00VQaZwYl4Elr10woqRloIblQanwJ7yUqGXAsWv7iuQ==" 20 | }, 21 | "node_modules/@rbxts/types": { 22 | "version": "1.0.537", 23 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.537.tgz", 24 | "integrity": "sha512-9orfH1YFf2ewZe3rxuPhRlPXLshUj4Ewx+F7Kn73i/Iu4C7InclLFSw2YaYz326MsjKycS7CvIu+ZDWoo7kq8Q==" 25 | } 26 | }, 27 | "dependencies": { 28 | "@rbxts/compiler-types": { 29 | "version": "1.2.3-types.1", 30 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.2.3-types.1.tgz", 31 | "integrity": "sha512-SXXIBazyJ7N6d2xcy471/kqZZpCv7EDOWrRJ45jcv3g00VQaZwYl4Elr10woqRloIblQanwJ7yUqGXAsWv7iuQ==" 32 | }, 33 | "@rbxts/types": { 34 | "version": "1.0.537", 35 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.537.tgz", 36 | "integrity": "sha512-9orfH1YFf2ewZe3rxuPhRlPXLshUj4Ewx+F7Kn73i/Iu4C7InclLFSw2YaYz326MsjKycS7CvIu+ZDWoo7kq8Q==" 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /extendable-resources/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/extendable-resources", 3 | "version": "1.0.3", 4 | "description": "An extendable object retrieval system for defining folders.", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/extendable-resources" 13 | }, 14 | "keywords": [ 15 | "resources", 16 | "roblox-typescript", 17 | "RemoteEvents", 18 | "RemoteFunctions", 19 | "Remotes", 20 | "Folder", 21 | "Manager" 22 | ], 23 | "author": "Validark", 24 | "license": "ISC", 25 | "bugs": { 26 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 27 | }, 28 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/extendable-resources/README.md", 29 | "publishConfig": { 30 | "access": "public" 31 | }, 32 | "devDependencies": { 33 | "@rbxts/compiler-types": "^1.2.3-types.1", 34 | "@rbxts/types": "^1.0.537" 35 | }, 36 | "files": [ 37 | "init.lua", 38 | "index.d.ts", 39 | "README.md" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /extendable-resources/src/index.ts: -------------------------------------------------------------------------------- 1 | // @author Validark 2 | 3 | const isServer = game.GetService("RunService").IsServer(); 4 | 5 | /** 6 | * Wraps a function which takes a single argument as a parameter, and makes it idempotent. 7 | * The new function will return the value in the cache, else it will call the wrapped function and cache the result 8 | * @param func The function to wrap 9 | */ 10 | function cacheSingleArgFunc(func: (arg: T) => K) { 11 | const cache = new Map(); 12 | 13 | return (arg: T) => { 14 | let value = cache.get(arg); 15 | 16 | if (value === undefined) { 17 | value = func(arg); 18 | cache.set(arg, value); 19 | } 20 | 21 | return value; 22 | }; 23 | } 24 | 25 | function constructManager( 26 | folder: Instance, 27 | optionalInstanceType?: T, 28 | ): (instanceName: string) => CreatableInstances[T] { 29 | if (isServer) { 30 | return instanceName => { 31 | let target = folder.FindFirstChild(instanceName) as CreatableInstances[T] | undefined; 32 | 33 | if (target === undefined) { 34 | if (optionalInstanceType) { 35 | target = new Instance(optionalInstanceType); 36 | target.Name = instanceName; 37 | target.Parent = folder; 38 | } else { 39 | return error(`Failed to find ${instanceName} in ${folder.Name}`); 40 | } 41 | } 42 | 43 | return target; 44 | }; 45 | } else { 46 | return instanceName => folder.WaitForChild(instanceName) as CreatableInstances[T]; 47 | } 48 | } 49 | 50 | const getFolderGetter = cacheSingleArgFunc((folderParent: Instance) => constructManager(folderParent, "Folder")); 51 | 52 | /** 53 | * Finds a folder called folderName in folderParent, 54 | * and returns a function which searches this folder for an instance with a given name. 55 | * If this instance does not exist on the client, the function will yield. 56 | * If it does not exist on the server, it will generate an instance of type optionalInstanceType or error. 57 | * 58 | * @param folderParent The parent to search for the folder in 59 | * @param folderName The name of the folder to search for 60 | * @param optionalInstanceType The instance type which can be generated if the instance does not 61 | * exist and is on the server 62 | */ 63 | function makeFolderManager( 64 | folderParent: Instance, 65 | folderName: string, 66 | optionalInstanceType?: T, 67 | ) { 68 | return constructManager(getFolderGetter(folderParent)(folderName), optionalInstanceType); 69 | } 70 | 71 | export = makeFolderManager; 72 | -------------------------------------------------------------------------------- /extendable-resources/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | // required 5 | "allowSyntheticDefaultImports": true, 6 | "downlevelIteration": true, 7 | "module": "commonjs", 8 | "noLib": true, 9 | "strict": true, 10 | "target": "es6", 11 | "typeRoots": [ "node_modules/@rbxts" ], 12 | // required, configurable 13 | "rootDir": "src", 14 | "outDir": "out", 15 | // optional 16 | "baseUrl": "src", 17 | "declaration": true, 18 | // optional, non-configurable 19 | "jsx": "react", 20 | "jsxFactory": "Roact.createElement" 21 | }, 22 | "typeAcquisition": { 23 | "enable": true 24 | }, 25 | "include": [ 26 | "./src/**/*" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /ip-data/README.md: -------------------------------------------------------------------------------- 1 | # ip-data 2 | 3 | Typings for the following API: http://ip-api.com/docs/api:json 4 | 5 | Demo usage: 6 | 7 | ```ts 8 | import getIPData from "@rbxts/ip-data"; 9 | import { ServerLocation } from "MyRemoteEvents" 10 | const UnknownLocationStr = "Server Location: Unknown"; 11 | 12 | getIPData() 13 | .then( 14 | myData => { 15 | return myData.status === "success" 16 | ? "Server Location: %s, %s".format(myData.regionName, myData.country) 17 | : UnknownLocationStr; 18 | }, 19 | () => { 20 | return UnknownLocationStr; 21 | }, 22 | ) 23 | .then(locationString => { 24 | ServerLocation.FireAllClients(locationString); 25 | Players.PlayerAdded.Connect(plr => ServerLocation.FireClient(plr, locationString)); 26 | }); 27 | ``` 28 | -------------------------------------------------------------------------------- /ip-data/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | interface IPDataSuccess { 3 | /** 4 | * Whether the request was successful 5 | */ 6 | status: "success"; 7 | /** 8 | * Continent name 9 | * 10 | * Example: North America 11 | */ 12 | continent: string; 13 | /** 14 | * Two-letter continent code 15 | * 16 | * Example: NA 17 | */ 18 | continentCode: string; 19 | /** 20 | * Country name 21 | * 22 | * Example: "United States" 23 | */ 24 | country: string; 25 | /** 26 | * Two-letter country code ISO 3166-1 alpha-2 27 | * 28 | * Example: "US" 29 | * 30 | * https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 31 | */ 32 | countryCode: string; 33 | /** 34 | * Region/state short code (FIPS or ISO) 35 | * 36 | * Examples: "CA", "10" 37 | */ 38 | region: string; 39 | /** 40 | * Region/State name 41 | * 42 | * Example: "California" 43 | */ 44 | regionName: string; 45 | /** 46 | * City name 47 | * 48 | * Example: "Mountain View" 49 | */ 50 | city: string; 51 | /** 52 | * District (subdivision of city) 53 | * 54 | * Example: "Old Farm District" 55 | */ 56 | district: string; 57 | /** 58 | * Zip code 59 | * 60 | * Example: 94043 61 | */ 62 | zip: string; 63 | /** 64 | * Latitude 65 | * 66 | * Example: 37.4192 67 | */ 68 | lat: number; 69 | /** 70 | * Longitude 71 | * 72 | * Example: -122.0574 73 | */ 74 | lon: number; 75 | /** 76 | * City timezone 77 | * 78 | * Example: "America/Los_Angeles" 79 | */ 80 | timezone: string; 81 | /** 82 | * National currency 83 | * 84 | * Example: USD 85 | */ 86 | currency: string; 87 | /** 88 | * Internet Service Provider Name 89 | * 90 | * Example: "Google" 91 | */ 92 | isp: string; 93 | /** 94 | * Organization name 95 | * 96 | * Example: "Google" 97 | */ 98 | org: string; 99 | /** 100 | * AS number and name, separated by space 101 | * 102 | * Example: "AS15169 Google Inc." 103 | */ 104 | as: string; 105 | /** 106 | * AS name (RIR). Empty for IP blocks not being announced in BGP tables. 107 | * 108 | * Example: GOOGLE 109 | */ 110 | asname: string; 111 | /** 112 | * Reverse DNS of the IP 113 | * 114 | * Example: "wi-in-f94.1e100.net" 115 | */ 116 | reverse: string; 117 | /** 118 | * Mobile (cellular) connection 119 | */ 120 | mobile: boolean; 121 | /** 122 | * Proxy (anonymous) 123 | */ 124 | proxy: boolean; 125 | /** 126 | * IPv4 used for the query 127 | */ 128 | query: string; 129 | /** 130 | * A rating of how accurate these values are, presumably out of 10 131 | * 132 | * Example: 10 133 | */ 134 | accuracy: 10; 135 | /** Hosting, colocated or data center */ 136 | hosting: boolean; 137 | /** Timezone UTC DST offset in seconds */ 138 | offset: number; 139 | } 140 | interface IPDataFail { 141 | status: "fail"; 142 | /** 143 | * Describes why the request failed 144 | */ 145 | message: "private range" | "reserved range" | "invalid query"; 146 | } 147 | declare const _default: () => Promise; 148 | export = _default; 149 | -------------------------------------------------------------------------------- /ip-data/init.lua: -------------------------------------------------------------------------------- 1 | -- Compiled with roblox-ts v1.2.3 2 | local TS = _G[script] 3 | local HttpService = game:GetService("HttpService") 4 | local ipData 5 | return TS.async(function() 6 | local _condition = ipData 7 | if _condition == nil then 8 | ipData = HttpService:JSONDecode(HttpService:GetAsync("http://ip-api.com/json/?fields=" .. tostring(2 ^ 26 - 1))) 9 | _condition = ipData 10 | end 11 | return _condition 12 | end) 13 | -------------------------------------------------------------------------------- /ip-data/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/ip-data", 3 | "version": "1.0.8", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@rbxts/ip-data", 9 | "version": "1.0.8", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@rbxts/compiler-types": "^1.2.3-types.1", 13 | "@rbxts/types": "^1.0.537" 14 | } 15 | }, 16 | "node_modules/@rbxts/compiler-types": { 17 | "version": "1.2.3-types.1", 18 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.2.3-types.1.tgz", 19 | "integrity": "sha512-SXXIBazyJ7N6d2xcy471/kqZZpCv7EDOWrRJ45jcv3g00VQaZwYl4Elr10woqRloIblQanwJ7yUqGXAsWv7iuQ==" 20 | }, 21 | "node_modules/@rbxts/types": { 22 | "version": "1.0.537", 23 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.537.tgz", 24 | "integrity": "sha512-9orfH1YFf2ewZe3rxuPhRlPXLshUj4Ewx+F7Kn73i/Iu4C7InclLFSw2YaYz326MsjKycS7CvIu+ZDWoo7kq8Q==" 25 | } 26 | }, 27 | "dependencies": { 28 | "@rbxts/compiler-types": { 29 | "version": "1.2.3-types.1", 30 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.2.3-types.1.tgz", 31 | "integrity": "sha512-SXXIBazyJ7N6d2xcy471/kqZZpCv7EDOWrRJ45jcv3g00VQaZwYl4Elr10woqRloIblQanwJ7yUqGXAsWv7iuQ==" 32 | }, 33 | "@rbxts/types": { 34 | "version": "1.0.537", 35 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.537.tgz", 36 | "integrity": "sha512-9orfH1YFf2ewZe3rxuPhRlPXLshUj4Ewx+F7Kn73i/Iu4C7InclLFSw2YaYz326MsjKycS7CvIu+ZDWoo7kq8Q==" 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ip-data/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/ip-data", 3 | "version": "1.0.9", 4 | "description": "https://ip-api.com API Wrapper", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": {}, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/ip-data" 11 | }, 12 | "keywords": [ 13 | "ip" 14 | ], 15 | "author": "Validark", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 19 | }, 20 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/ip-data/README.md", 21 | "publishConfig": { 22 | "access": "public" 23 | }, 24 | "dependencies": { 25 | "@rbxts/compiler-types": "^1.2.3-types.1", 26 | "@rbxts/types": "^1.0.537" 27 | }, 28 | "files": [ 29 | "init.lua", 30 | "index.d.ts", 31 | "README.md" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /ip-data/src/index.ts: -------------------------------------------------------------------------------- 1 | interface IPDataSuccess { 2 | /** 3 | * Whether the request was successful 4 | */ 5 | status: "success"; 6 | 7 | /** 8 | * Continent name 9 | * 10 | * Example: North America 11 | */ 12 | continent: string; 13 | 14 | /** 15 | * Two-letter continent code 16 | * 17 | * Example: NA 18 | */ 19 | continentCode: string; 20 | 21 | /** 22 | * Country name 23 | * 24 | * Example: "United States" 25 | */ 26 | country: string; 27 | 28 | /** 29 | * Two-letter country code ISO 3166-1 alpha-2 30 | * 31 | * Example: "US" 32 | * 33 | * https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 34 | */ 35 | countryCode: string; 36 | 37 | /** 38 | * Region/state short code (FIPS or ISO) 39 | * 40 | * Examples: "CA", "10" 41 | */ 42 | region: string; 43 | 44 | /** 45 | * Region/State name 46 | * 47 | * Example: "California" 48 | */ 49 | regionName: string; 50 | 51 | /** 52 | * City name 53 | * 54 | * Example: "Mountain View" 55 | */ 56 | city: string; 57 | 58 | /** 59 | * District (subdivision of city) 60 | * 61 | * Example: "Old Farm District" 62 | */ 63 | district: string; 64 | 65 | /** 66 | * Zip code 67 | * 68 | * Example: 94043 69 | */ 70 | zip: string; 71 | 72 | /** 73 | * Latitude 74 | * 75 | * Example: 37.4192 76 | */ 77 | lat: number; 78 | 79 | /** 80 | * Longitude 81 | * 82 | * Example: -122.0574 83 | */ 84 | lon: number; 85 | 86 | /** 87 | * City timezone 88 | * 89 | * Example: "America/Los_Angeles" 90 | */ 91 | timezone: string; 92 | 93 | /** 94 | * National currency 95 | * 96 | * Example: USD 97 | */ 98 | currency: string; 99 | 100 | /** 101 | * Internet Service Provider Name 102 | * 103 | * Example: "Google" 104 | */ 105 | isp: string; 106 | 107 | /** 108 | * Organization name 109 | * 110 | * Example: "Google" 111 | */ 112 | org: string; 113 | 114 | /** 115 | * AS number and name, separated by space 116 | * 117 | * Example: "AS15169 Google Inc." 118 | */ 119 | as: string; 120 | 121 | /** 122 | * AS name (RIR). Empty for IP blocks not being announced in BGP tables. 123 | * 124 | * Example: GOOGLE 125 | */ 126 | asname: string; 127 | 128 | /** 129 | * Reverse DNS of the IP 130 | * 131 | * Example: "wi-in-f94.1e100.net" 132 | */ 133 | reverse: string; 134 | 135 | /** 136 | * Mobile (cellular) connection 137 | */ 138 | mobile: boolean; 139 | 140 | /** 141 | * Proxy (anonymous) 142 | */ 143 | proxy: boolean; 144 | 145 | /** 146 | * IPv4 used for the query 147 | */ 148 | query: string; 149 | 150 | /** 151 | * A rating of how accurate these values are, presumably out of 10 152 | * 153 | * Example: 10 154 | */ 155 | accuracy: 10; 156 | 157 | /** Hosting, colocated or data center */ 158 | hosting: boolean; 159 | 160 | /** Timezone UTC DST offset in seconds */ 161 | offset: number; 162 | } 163 | 164 | interface IPDataFail { 165 | status: "fail"; 166 | 167 | /** 168 | * Describes why the request failed 169 | */ 170 | message: "private range" | "reserved range" | "invalid query"; 171 | } 172 | 173 | const HttpService = game.GetService("HttpService"); 174 | let ipData: IPDataSuccess | IPDataFail | undefined; 175 | 176 | export = async () => 177 | ipData ?? 178 | (ipData = HttpService.JSONDecode(HttpService.GetAsync(`http://ip-api.com/json/?fields=${2 ** 26 - 1}`)) as 179 | | IPDataSuccess 180 | | IPDataFail); 181 | -------------------------------------------------------------------------------- /ip-data/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | // required 5 | "allowSyntheticDefaultImports": true, 6 | "downlevelIteration": true, 7 | "module": "commonjs", 8 | "noLib": true, 9 | "strict": true, 10 | "target": "es6", 11 | "typeRoots": [ 12 | "node_modules/@rbxts" 13 | ], 14 | // required, configurable 15 | "rootDir": "src", 16 | "outDir": "out", 17 | // optional 18 | "baseUrl": "src", 19 | "declaration": true, 20 | // optional, non-configurable 21 | "jsx": "react", 22 | "jsxFactory": "Roact.createElement" 23 | }, 24 | "typeAcquisition": { 25 | "enable": true 26 | }, 27 | "include": [ 28 | "." 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /lerp-functions/README.md: -------------------------------------------------------------------------------- 1 | # Lerps 2 | 3 | Has some lerp functions that look like this: 4 | 5 | ```ts 6 | /** 7 | * Given a startValue and endValue, returns a function that, 8 | * when passed an alpha value, returns an interpolated value between the two. 9 | */ 10 | type LerpFunction = (startValue: T, endValue: T) => (alpha: number) => T; 11 | ``` 12 | 13 | These are the types: 14 | 15 | ``` 16 | string 17 | number 18 | boolean 19 | Region3 20 | CFrame 21 | UDim2 22 | UDim 23 | Ray 24 | Rect 25 | Color3 26 | Vector2 27 | Vector3 28 | NumberRange 29 | ColorSequence 30 | NumberSequence 31 | PhysicalProperties 32 | NumberSequenceKeypoint 33 | ``` 34 | 35 | The string lerp function is only for Lighting's TimeOfDay property, although probably shouldn't be. 36 | -------------------------------------------------------------------------------- /lerp-functions/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Given a startValue and endValue, returns a function that, 3 | * when passed an alpha value, returns an interpolated value between the two. 4 | */ 5 | type LerpFunction = (startValue: T, endValue: T) => (alpha: number) => T; 6 | 7 | declare interface LerpFunctions { 8 | string: LerpFunction; 9 | number: LerpFunction; 10 | boolean: LerpFunction; 11 | Region3: LerpFunction; 12 | CFrame: LerpFunction; 13 | UDim2: LerpFunction; 14 | UDim: LerpFunction; 15 | Ray: LerpFunction; 16 | Rect: LerpFunction; 17 | Color3: LerpFunction; 18 | Vector2: LerpFunction; 19 | Vector3: LerpFunction; 20 | NumberRange: LerpFunction; 21 | ColorSequence: LerpFunction; 22 | NumberSequence: LerpFunction; 23 | PhysicalProperties: LerpFunction; 24 | NumberSequenceKeypoint: LerpFunction; 25 | } 26 | 27 | declare const Lerps: LerpFunctions; 28 | export = Lerps; 29 | -------------------------------------------------------------------------------- /lerp-functions/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/lerp-functions", 3 | "version": "1.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@rbxts/types": { 8 | "version": "1.0.227", 9 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.227.tgz", 10 | "integrity": "sha512-jcAEpzljzGHwI0pNax5GD2rz8EHYFeJ7uhtJWv7PxLwcrA8ZEWLo5Y7MlxbFRgM455QH5OWh5f/0L3T6I+1/Rg==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lerp-functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/lerp-functions", 3 | "version": "1.0.2", 4 | "description": "Interpolation functions", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "bezier", 12 | "cubic-bezier", 13 | "curve", 14 | "interpolation", 15 | "interpolate", 16 | "easing", 17 | "ease", 18 | "lerp", 19 | "tween", 20 | "roblox" 21 | ], 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/lerp-functions" 25 | }, 26 | "author": "Validark", 27 | "license": "ISC", 28 | "bugs": { 29 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 30 | }, 31 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/lerp-functions/README.md", 32 | "dependencies": { 33 | "@rbxts/types": "^1.0.227" 34 | }, 35 | "publishConfig": { 36 | "access": "public" 37 | }, 38 | "files": [ 39 | "init.lua", 40 | "index.d.ts", 41 | "README.md" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /lerp-functions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | // required 5 | "allowSyntheticDefaultImports": true, 6 | "downlevelIteration": true, 7 | "module": "commonjs", 8 | "noLib": true, 9 | "strict": true, 10 | "target": "es6", 11 | "typeRoots": [ 12 | "node_modules/@rbxts" 13 | ], 14 | // required, configurable 15 | "rootDir": "src", 16 | "outDir": "out", 17 | // optional 18 | "baseUrl": "src", 19 | "declaration": true, 20 | // optional, non-configurable 21 | "jsx": "react", 22 | "jsxFactory": "Roact.createElement", 23 | }, 24 | "include": ["."], 25 | "typeAcquisition": { 26 | "enable": true 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /make/README.md: -------------------------------------------------------------------------------- 1 | # Make 2 | 3 | A library providing sugar for declaring Instances. Mostly self-explanatory. 4 | 5 | Usage: 6 | 7 | Just pass in the ClassName of the Instance you want `Make` to generate with an object containing the properties it should have. 8 | 9 | ```ts 10 | import Make from "make"; 11 | 12 | Make("Frame", { 13 | Active: false, 14 | Children: [ 15 | Make("ImageLabel", { 16 | Image: "", 17 | BackgroundTransparency: 0, 18 | ImageTransparency: 0.5, 19 | Children: [Make("ImageButton", {})], 20 | }), 21 | Make("ImageButton", { 22 | MouseButton1Down: (x: number, y: number) => { 23 | print(x, y); 24 | }, 25 | }), 26 | ], 27 | }); 28 | ``` 29 | 30 | Additional Implementation details: 31 | 32 | - `Children` is a whitelisted member. It expects an array of Instances which will be parented to the generated instance. 33 | - Setting an event member, like `MouseButton1Down` in the example above, will `Connect` the expected callback to the event. 34 | 35 | ###### Note: The `Parent` property is always set last. This avoids ordering bugs/inefficiency 36 | -------------------------------------------------------------------------------- /make/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | declare type WritablePropertyNames = { 4 | readonly [K in keyof T]-?: T[K] extends Callback ? never : (() => F extends { 5 | [Q in K]: T[K]; 6 | } ? 1 : 2) extends () => F extends { 7 | -readonly [Q in K]: T[K]; 8 | } ? 1 : 2 ? K : never; 9 | }[keyof T]; 10 | declare type GetBindableToRBXScriptSignal = { 11 | [key in ({ 12 | [K in keyof T]-?: T[K] extends RBXScriptSignal ? K : never; 13 | })[keyof T]]: (T[key] extends RBXScriptSignal ? R : never); 14 | }; 15 | /** 16 | * Returns a table wherein an object's writable properties can be specified, 17 | * while also allowing functions to be passed in which can be bound to a RBXScriptSignal. 18 | */ 19 | declare type GetPartialObjectWithBindableConnectSlots = Partial> & GetBindableToRBXScriptSignal>; 20 | /** 21 | * Instantiates a new Instance of `className` with given `settings`, 22 | * where `settings` is an object of the form { [K: propertyName]: value }. 23 | * 24 | * `settings.Children` is an array of child objects to be parented to the generated Instance. 25 | * 26 | * Events can be set to a callback function, which will be connected. 27 | * 28 | * `settings.Parent` is always set last. 29 | */ 30 | declare function Make & { 31 | /** The Children to place inside of this Instance. */ 32 | Children?: ReadonlyArray; 33 | Parent?: Instance | undefined; 34 | }>(className: T, settings: Q): CreatableInstances[T] & { [K_1 in keyof ({ [O in Extract<"Name", keyof Q>]: Q[O]; } & { 35 | ClassName: T; 36 | } & (Q["Children"] extends never ? never : { [K in Exclude]: Q["Children"][K] extends infer A ? A extends { 37 | Name: string; 38 | } ? string extends A["Name"] ? never : (k: { [P in A["Name"]]: A; }) => void : never : never; }[Exclude] extends (k: infer U) => void ? U : never))]: ({ [O in Extract<"Name", keyof Q>]: Q[O]; } & { 39 | ClassName: T; 40 | } & (Q["Children"] extends never ? never : { [K in Exclude]: Q["Children"][K] extends infer A ? A extends { 41 | Name: string; 42 | } ? string extends A["Name"] ? never : (k: { [P in A["Name"]]: A; }) => void : never : never; }[Exclude] extends (k: infer U) => void ? U : never))[K_1]; }; 43 | export = Make; 44 | -------------------------------------------------------------------------------- /make/init.lua: -------------------------------------------------------------------------------- 1 | -- Compiled with roblox-ts v1.2.3 2 | --[[ 3 | * 4 | * Returns a table wherein an object's writable properties can be specified, 5 | * while also allowing functions to be passed in which can be bound to a RBXScriptSignal. 6 | ]] 7 | --[[ 8 | * 9 | * Instantiates a new Instance of `className` with given `settings`, 10 | * where `settings` is an object of the form { [K: propertyName]: value }. 11 | * 12 | * `settings.Children` is an array of child objects to be parented to the generated Instance. 13 | * 14 | * Events can be set to a callback function, which will be connected. 15 | * 16 | * `settings.Parent` is always set last. 17 | ]] 18 | local function Make(className, settings) 19 | local _binding = settings 20 | local children = _binding.Children 21 | local parent = _binding.Parent 22 | local instance = Instance.new(className) 23 | for setting, value in pairs(settings) do 24 | if setting ~= "Children" and setting ~= "Parent" then 25 | local _binding_1 = instance 26 | local prop = _binding_1[setting] 27 | if typeof(prop) == "RBXScriptSignal" then 28 | prop:Connect(value) 29 | else 30 | instance[setting] = value 31 | end 32 | end 33 | end 34 | if children then 35 | for _, child in ipairs(children) do 36 | child.Parent = instance 37 | end 38 | end 39 | instance.Parent = parent 40 | return instance 41 | end 42 | return Make 43 | -------------------------------------------------------------------------------- /make/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/make", 3 | "version": "1.0.5", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@rbxts/make", 9 | "version": "1.0.5", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@rbxts/compiler-types": "^1.2.3-types.1", 13 | "@rbxts/types": "^1.0.537" 14 | } 15 | }, 16 | "node_modules/@rbxts/compiler-types": { 17 | "version": "1.2.3-types.1", 18 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.2.3-types.1.tgz", 19 | "integrity": "sha512-SXXIBazyJ7N6d2xcy471/kqZZpCv7EDOWrRJ45jcv3g00VQaZwYl4Elr10woqRloIblQanwJ7yUqGXAsWv7iuQ==" 20 | }, 21 | "node_modules/@rbxts/types": { 22 | "version": "1.0.537", 23 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.537.tgz", 24 | "integrity": "sha512-9orfH1YFf2ewZe3rxuPhRlPXLshUj4Ewx+F7Kn73i/Iu4C7InclLFSw2YaYz326MsjKycS7CvIu+ZDWoo7kq8Q==" 25 | } 26 | }, 27 | "dependencies": { 28 | "@rbxts/compiler-types": { 29 | "version": "1.2.3-types.1", 30 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.2.3-types.1.tgz", 31 | "integrity": "sha512-SXXIBazyJ7N6d2xcy471/kqZZpCv7EDOWrRJ45jcv3g00VQaZwYl4Elr10woqRloIblQanwJ7yUqGXAsWv7iuQ==" 32 | }, 33 | "@rbxts/types": { 34 | "version": "1.0.537", 35 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.537.tgz", 36 | "integrity": "sha512-9orfH1YFf2ewZe3rxuPhRlPXLshUj4Ewx+F7Kn73i/Iu4C7InclLFSw2YaYz326MsjKycS7CvIu+ZDWoo7kq8Q==" 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /make/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/make", 3 | "version": "1.0.6", 4 | "description": "Shorthand for declaring Instances with properties.", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/make" 13 | }, 14 | "keywords": [ 15 | "resources", 16 | "roblox-typescript", 17 | "RemoteEvents", 18 | "RemoteFunctions", 19 | "Remotes", 20 | "Folder", 21 | "Manager" 22 | ], 23 | "author": "Validark", 24 | "license": "ISC", 25 | "bugs": { 26 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 27 | }, 28 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/make/README.md", 29 | "publishConfig": { 30 | "access": "public" 31 | }, 32 | "dependencies": { 33 | "@rbxts/compiler-types": "^1.2.3-types.1", 34 | "@rbxts/types": "^1.0.537" 35 | }, 36 | "files": [ 37 | "init.lua", 38 | "index.d.ts", 39 | "README.md" 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /make/src/index.ts: -------------------------------------------------------------------------------- 1 | type WritablePropertyNames = { 2 | readonly [K in keyof T]-?: T[K] extends Callback 3 | ? never 4 | : (() => F extends { [Q in K]: T[K] } ? 1 : 2) extends () => F extends { 5 | -readonly [Q in K]: T[K]; 6 | } 7 | ? 1 8 | : 2 9 | ? K 10 | : never; 11 | }[keyof T]; 12 | 13 | type GetBindableToRBXScriptSignal = { 14 | [key in ({ [K in keyof T]-?: T[K] extends RBXScriptSignal ? K : never })[keyof T]]: (T[key] extends RBXScriptSignal< 15 | infer R 16 | > 17 | ? R 18 | : never) 19 | }; 20 | 21 | /** 22 | * Returns a table wherein an object's writable properties can be specified, 23 | * while also allowing functions to be passed in which can be bound to a RBXScriptSignal. 24 | */ 25 | type GetPartialObjectWithBindableConnectSlots = Partial< 26 | Pick> & GetBindableToRBXScriptSignal 27 | >; 28 | 29 | /** 30 | * Instantiates a new Instance of `className` with given `settings`, 31 | * where `settings` is an object of the form { [K: propertyName]: value }. 32 | * 33 | * `settings.Children` is an array of child objects to be parented to the generated Instance. 34 | * 35 | * Events can be set to a callback function, which will be connected. 36 | * 37 | * `settings.Parent` is always set last. 38 | */ 39 | function Make & { 40 | /** The Children to place inside of this Instance. */ 41 | Children?: ReadonlyArray; 42 | Parent?: Instance | undefined; 43 | }>( 44 | className: T, 45 | settings: Q, 46 | ) { 47 | const { Children: children, Parent: parent } = settings; 48 | const instance = new Instance(className); 49 | 50 | for (const [setting, value] of pairs(settings as unknown as Map)) { 51 | if (setting !== "Children" && setting !== "Parent") { 52 | const { [setting]: prop } = instance; 53 | 54 | if (typeIs(prop, "RBXScriptSignal")) { 55 | (prop as RBXScriptSignal).Connect(value); 56 | } else { 57 | instance[setting] = value; 58 | } 59 | } 60 | } 61 | 62 | if (children) { 63 | for (const child of children) { 64 | child.Parent = instance; 65 | } 66 | } 67 | instance.Parent = parent; 68 | return instance as CreatableInstances[T] & 69 | Reconstruct<{ [O in Extract<"Name", keyof Q>]: Q[O] } & { [G in "ClassName"]: T } & 70 | (Q["Children"] extends never ? never : 71 | { 72 | [K in Exclude | "length">]: 73 | Q["Children"][K] extends infer A ? A extends { Name: string } ? string extends A["Name"] ? never : (k: { [P in A["Name"]]: A }) => void : never : never 74 | }[Exclude | "length">] extends (k: infer U) => void ? U : never)> 75 | } 76 | 77 | export = Make; 78 | -------------------------------------------------------------------------------- /make/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | // required 5 | "allowSyntheticDefaultImports": true, 6 | "downlevelIteration": true, 7 | "module": "commonjs", 8 | "noLib": true, 9 | "strict": true, 10 | "target": "es6", 11 | "typeRoots": [ "node_modules/@rbxts" ], 12 | // required, configurable 13 | "rootDir": "src", 14 | "outDir": "out", 15 | // optional 16 | "baseUrl": "src", 17 | "declaration": true, 18 | // optional, non-configurable 19 | "jsx": "react", 20 | "jsxFactory": "Roact.createElement" 21 | }, 22 | "typeAcquisition": { 23 | "enable": true 24 | }, 25 | "include": [ 26 | "./src/**/*" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /promise-character/README.md: -------------------------------------------------------------------------------- 1 | # promise-character 2 | 3 | Usage: 4 | 5 | Pass a Player's Character `Model` into `promiseR15` or `promiseR6`, which will promise via `promiseTree` from `validate-tree` that all the members defined in the R6/R15 trees exist (all members except those generated by Sound/Animate scripts). 6 | 7 | ```ts 8 | promiseR15(character).then(char => { 9 | // All members are properly typed and defined! 10 | char.Head.face.Texture = "" 11 | }); 12 | 13 | // alternatively, one may use `await` 14 | async function f() { 15 | const char = await promiseR15(character); 16 | char.Head.face.Texture = "" 17 | } 18 | ``` 19 | 20 | Here is what I envision as a practical use for this library: 21 | 22 | ```ts 23 | import { 24 | promiseR6, 25 | promiseR15, 26 | CharacterRigR15, 27 | CharacterRigR6, 28 | } from "@rbxts/promise-character"; 29 | 30 | import { Players } from "@rbxts/services"; 31 | import { promiseChildOfClass } from "@rbxts/promise-child"; 32 | 33 | function doStuffWithR15(rig: CharacterRigR15) { 34 | rig.RightFoot.Destroy(); 35 | } 36 | 37 | function doStuffWithR6(rig: CharacterRigR6) { 38 | rig["Left Arm"].Destroy(); 39 | } 40 | 41 | async function handleCharacterModel(model: Model) { 42 | const rigType = (await promiseChildOfClass(model, "Humanoid")).RigType.Name; 43 | 44 | if (rigType === "R15") { 45 | const rig15 = await promiseR15(model); 46 | rig15.Head.Neck.Destroy(); // R15 specific logic :) 47 | doStuffWithR15(rig15); 48 | } else if (rigType === "R6") { 49 | const rig6 = await promiseR6(model); 50 | rig6.Torso.Destroy(); // R6 specific logic :) 51 | doStuffWithR6(rig6); 52 | } else { 53 | throw `${model.Name} has an unknown rig type! ${rigType}`; 54 | } 55 | } 56 | 57 | Players.PlayerAdded.Connect(({ Name, Character, CharacterAdded }) => { 58 | print(`Welcome, ${Name}`); 59 | if (Character) handleCharacterModel(Character); 60 | CharacterAdded.Connect(handleCharacterModel); 61 | }); 62 | ``` 63 | -------------------------------------------------------------------------------- /promise-character/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/promise-character", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@rbxts/promise-character", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@rbxts/compiler-types": "^1.2.3-types.1", 13 | "@rbxts/types": "^1.0.537", 14 | "@rbxts/validate-tree": "^2.0.0" 15 | } 16 | }, 17 | "node_modules/@rbxts/compiler-types": { 18 | "version": "1.2.3-types.1", 19 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.2.3-types.1.tgz", 20 | "integrity": "sha512-SXXIBazyJ7N6d2xcy471/kqZZpCv7EDOWrRJ45jcv3g00VQaZwYl4Elr10woqRloIblQanwJ7yUqGXAsWv7iuQ==" 21 | }, 22 | "node_modules/@rbxts/types": { 23 | "version": "1.0.537", 24 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.537.tgz", 25 | "integrity": "sha512-9orfH1YFf2ewZe3rxuPhRlPXLshUj4Ewx+F7Kn73i/Iu4C7InclLFSw2YaYz326MsjKycS7CvIu+ZDWoo7kq8Q==" 26 | }, 27 | "node_modules/@rbxts/validate-tree": { 28 | "version": "2.0.0", 29 | "resolved": "https://registry.npmjs.org/@rbxts/validate-tree/-/validate-tree-2.0.0.tgz", 30 | "integrity": "sha512-MjB7VHT8u++jwOdWkhsfBvIueONw9rAcku42SzmeCc+pxv0+uFgFdAEtdbwmjmaI9VwzzsS9qKE4na5T+udWbg==", 31 | "dependencies": { 32 | "@rbxts/compiler-types": "^1.2.3-types.1", 33 | "@rbxts/types": "^1.0.537" 34 | } 35 | } 36 | }, 37 | "dependencies": { 38 | "@rbxts/compiler-types": { 39 | "version": "1.2.3-types.1", 40 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.2.3-types.1.tgz", 41 | "integrity": "sha512-SXXIBazyJ7N6d2xcy471/kqZZpCv7EDOWrRJ45jcv3g00VQaZwYl4Elr10woqRloIblQanwJ7yUqGXAsWv7iuQ==" 42 | }, 43 | "@rbxts/types": { 44 | "version": "1.0.537", 45 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.537.tgz", 46 | "integrity": "sha512-9orfH1YFf2ewZe3rxuPhRlPXLshUj4Ewx+F7Kn73i/Iu4C7InclLFSw2YaYz326MsjKycS7CvIu+ZDWoo7kq8Q==" 47 | }, 48 | "@rbxts/validate-tree": { 49 | "version": "2.0.0", 50 | "resolved": "https://registry.npmjs.org/@rbxts/validate-tree/-/validate-tree-2.0.0.tgz", 51 | "integrity": "sha512-MjB7VHT8u++jwOdWkhsfBvIueONw9rAcku42SzmeCc+pxv0+uFgFdAEtdbwmjmaI9VwzzsS9qKE4na5T+udWbg==", 52 | "requires": { 53 | "@rbxts/compiler-types": "^1.2.3-types.1", 54 | "@rbxts/types": "^1.0.537" 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /promise-character/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/promise-character", 3 | "version": "1.0.3", 4 | "description": "Promise all members of a character rig exist, then dot access!", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "build": "rbxtsc & mv out/* . & rm -r out" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/promise-character" 13 | }, 14 | "keywords": [ 15 | "character", 16 | "dot", 17 | "Roblox", 18 | "Player", 19 | "yield", 20 | "wait", 21 | "members", 22 | "parts", 23 | "promise" 24 | ], 25 | "author": "Validark", 26 | "license": "ISC", 27 | "bugs": { 28 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 29 | }, 30 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/promise-character/README.md", 31 | "publishConfig": { 32 | "access": "public" 33 | }, 34 | "dependencies": { 35 | "@rbxts/compiler-types": "^1.2.3-types.1", 36 | "@rbxts/types": "^1.0.537", 37 | "@rbxts/validate-tree": "^2.0.1" 38 | }, 39 | "files": [ 40 | "init.lua", 41 | "index.d.ts", 42 | "README.md" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /promise-character/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | // required 5 | "allowSyntheticDefaultImports": true, 6 | "downlevelIteration": true, 7 | "module": "commonjs", 8 | "noLib": true, 9 | "strict": true, 10 | "target": "es6", 11 | "typeRoots": [ 12 | "node_modules/@rbxts" 13 | ], 14 | // required, configurable 15 | "rootDir": "src", 16 | "outDir": "out", 17 | // optional 18 | "baseUrl": "src", 19 | "declaration": true, 20 | // optional, non-configurable 21 | "jsx": "react", 22 | "jsxFactory": "Roact.createElement" 23 | }, 24 | "typeAcquisition": { 25 | "enable": true 26 | }, 27 | "include": [ 28 | "." 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /promise-child/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "jsx": true, 5 | "useJSXTextNode": true, 6 | "ecmaVersion": 2018, 7 | "sourceType": "module", 8 | "project": "./tsconfig.json" 9 | }, 10 | "plugins": [ 11 | "@typescript-eslint", 12 | "roblox-ts", 13 | "prettier" 14 | ], 15 | "extends": [ 16 | "plugin:@typescript-eslint/recommended", 17 | "plugin:roblox-ts/recommended", 18 | "prettier/@typescript-eslint", 19 | "plugin:prettier/recommended" 20 | ], 21 | "rules": { 22 | "prettier/prettier": [ 23 | "warn", 24 | { 25 | "semi": true, 26 | "trailingComma": "all", 27 | "singleQuote": false, 28 | "printWidth": 120, 29 | "tabWidth": 4, 30 | "useTabs": true 31 | } 32 | ] 33 | } 34 | } -------------------------------------------------------------------------------- /promise-child/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /out 3 | /include 4 | *.tsbuildinfo 5 | -------------------------------------------------------------------------------- /promise-child/README.md: -------------------------------------------------------------------------------- 1 | # promise-child 2 | 3 | Promises a child appears within an Instance. Comes with 3 functions: 4 | 5 | ```ts 6 | export declare function promiseChild(instance: Instance, childName: string | number): Promise; 7 | 8 | export declare function promiseChildOfClass(instance: Instance, className: T): Promise; 9 | 10 | export declare function promiseChildWhichIsA(instance: Instance, className: T): Promise; 11 | ``` 12 | 13 | Pretty straightforward. The `promiseChild` function returns a promise that resolves when a given `childName` appears inside an instance. `promiseChildOfClass` looks for a given ClassName, and `promiseChildWhichIsA` looks for an instance which `IsA(class)` 14 | 15 | Example: 16 | 17 | ```ts 18 | import { promiseChildOfClass } from "@rbxts/promise-child"; 19 | 20 | game.GetService("Players").PlayerAdded.Connect((plr) => { 21 | plr.CharacterAdded.Connect(async (char) => { 22 | const humanoid = await promiseChildOfClass(char, "Humanoid"); 23 | switch (humanoid.RigType.Name) { 24 | case "R6": { 25 | makeR6Ragdoll(char); 26 | break; 27 | } 28 | case "R15": { 29 | makeR15Ragdoll(char); 30 | break; 31 | } 32 | default: 33 | error("A player spawned with an unsupported RigType"); 34 | } 35 | }); 36 | }); 37 | 38 | ``` 39 | 40 | It will error (and will need to be caught) if its parent or one of its ancestors has its `Parent` property set to `nil` (the error will be a string). 41 | 42 | Since it is implemented using Promises, these are cancellable requests! 43 | 44 | ```ts 45 | const Lighting = game.GetService("Lighting"); 46 | const skyGetter = promiseChildOfClass(Lighting, "Sky"); 47 | // if skyGetter didn't already resolve at the end of 5 seconds, cancel it 48 | const skyTimeout = Promise.delay(5).then(() => skyGetter.cancel()); 49 | 50 | const sunRaysGetter = promiseChildOfClass(Lighting, "SunRaysEffect"); 51 | sunRaysGetter.now() 52 | .then(() => print("We got our SunRaysEffect synchronously!")); 53 | .catch(() => print("We're going to have to wait for it to show up!")) 54 | ``` 55 | -------------------------------------------------------------------------------- /promise-child/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | export declare function promiseChildWhichIsA(parent: Instance, className: T): Promise; 4 | export declare function promiseChildOfClass(parent: Instance, className: T): Promise; 5 | export declare function promiseChild(parent: Instance, childName: string | number): Promise; 6 | -------------------------------------------------------------------------------- /promise-child/init.lua: -------------------------------------------------------------------------------- 1 | -- Compiled with roblox-ts v1.2.3 2 | local TS = _G[script] 3 | local function promiseChildWhichIsA(parent, className) 4 | local child = parent:FindFirstChildWhichIsA(className) 5 | if child then 6 | return TS.Promise.resolve(child) 7 | end 8 | local warner = TS.Promise.delay(5) 9 | local _arg0 = function() 10 | return warn('[promiseChildWhichIsA] Infinite wait possible for a "' .. className .. '" to appear under ' .. parent:GetFullName()) 11 | end 12 | warner:andThen(_arg0) 13 | local connection1 14 | local connection2 15 | local promise = TS.Promise.new(function(resolve, reject) 16 | connection1 = parent.ChildAdded:Connect(function(child) 17 | return child:IsA(className) and resolve(child) 18 | end) 19 | connection2 = parent.AncestryChanged:Connect(function(_, newParent) 20 | return newParent or reject(parent:GetFullName() .. " had its root parent set to nil") 21 | end) 22 | end) 23 | promise:finally(function() 24 | warner:cancel() 25 | connection1:Disconnect() 26 | connection2:Disconnect() 27 | end) 28 | return promise 29 | end 30 | local function promiseChildOfClass(parent, className) 31 | local child = parent:FindFirstChildOfClass(className) 32 | if child then 33 | return TS.Promise.resolve(child) 34 | end 35 | local warner = TS.Promise.delay(5) 36 | local _arg0 = function() 37 | return warn('[promiseChildOfClass] Infinite wait possible for a "' .. className .. '" to appear under ' .. parent:GetFullName()) 38 | end 39 | warner:andThen(_arg0) 40 | local connection1 41 | local connection2 42 | local promise = TS.Promise.new(function(resolve, reject) 43 | connection1 = parent.ChildAdded:Connect(function(child) 44 | return child.ClassName == className and resolve(child) 45 | end) 46 | connection2 = parent.AncestryChanged:Connect(function(_, newParent) 47 | return newParent or reject(parent:GetFullName() .. " had its root parent set to nil") 48 | end) 49 | end) 50 | promise:finally(function() 51 | warner:cancel() 52 | connection1:Disconnect() 53 | connection2:Disconnect() 54 | end) 55 | return promise 56 | end 57 | local function promiseChild(parent, childName) 58 | local child = parent:FindFirstChild(childName) 59 | if child then 60 | return TS.Promise.resolve(child) 61 | end 62 | local connections = {} 63 | local warner = TS.Promise.delay(5) 64 | local _arg0 = function() 65 | return warn('[promiseChild] Infinite wait possible for "' .. tostring(childName) .. '" to appear under ' .. parent:GetFullName()) 66 | end 67 | warner:andThen(_arg0) 68 | local promise = TS.Promise.new(function(resolve, reject) 69 | local _arg0_1 = parent.ChildAdded:Connect(function(child) 70 | if child.Name == childName then 71 | resolve(child) 72 | else 73 | local _arg0_2 = child:GetPropertyChangedSignal("Name"):Connect(function() 74 | return child.Name == childName and child.Parent == parent and resolve(child) 75 | end) 76 | -- ▼ Array.push ▼ 77 | connections[#connections + 1] = _arg0_2 78 | -- ▲ Array.push ▲ 79 | end 80 | end) 81 | -- ▼ Array.push ▼ 82 | connections[#connections + 1] = _arg0_1 83 | -- ▲ Array.push ▲ 84 | local _arg0_2 = parent.AncestryChanged:Connect(function(_, newParent) 85 | return newParent or reject(parent:GetFullName() .. " had its root parent set to nil") 86 | end) 87 | -- ▼ Array.push ▼ 88 | connections[#connections + 1] = _arg0_2 89 | -- ▲ Array.push ▲ 90 | end) 91 | promise:finally(function() 92 | warner:cancel() 93 | for _, connection in ipairs(connections) do 94 | connection:Disconnect() 95 | end 96 | end) 97 | return promise 98 | end 99 | return { 100 | promiseChildWhichIsA = promiseChildWhichIsA, 101 | promiseChildOfClass = promiseChildOfClass, 102 | promiseChild = promiseChild, 103 | } 104 | -------------------------------------------------------------------------------- /promise-child/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/promise-child", 3 | "version": "1.2.1", 4 | "description": "Helper functions that promise a child appears within an Instance.", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": {}, 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/promise-child" 11 | }, 12 | "keywords": [ 13 | "yield", 14 | "wait", 15 | "instance", 16 | "part", 17 | "humanoid", 18 | "promise" 19 | ], 20 | "author": "Validark", 21 | "license": "ISC", 22 | "bugs": { 23 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 24 | }, 25 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/promise-child/README.md", 26 | "publishConfig": { 27 | "access": "public" 28 | }, 29 | "devDependencies": { 30 | "@rbxts/compiler-types": "^1.3.3", 31 | "@rbxts/types": "^1.0.637", 32 | "@typescript-eslint/eslint-plugin": "^4.33.0", 33 | "@typescript-eslint/parser": "^4.33.0", 34 | "eslint": "^7.32.0", 35 | "eslint-config-prettier": "^8.3.0", 36 | "eslint-plugin-prettier": "^4.0.0", 37 | "eslint-plugin-roblox-ts": "^0.0.31", 38 | "prettier": "^2.4.1", 39 | "typescript": "^4.4.3" 40 | }, 41 | "files": [ 42 | "index.d.ts", 43 | "init.lua", 44 | "README.md" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /promise-child/src/index.ts: -------------------------------------------------------------------------------- 1 | export function promiseChildWhichIsA(parent: Instance, className: T): Promise { 2 | const child = parent.FindFirstChildWhichIsA(className); 3 | if (child) return Promise.resolve(child); 4 | 5 | const warner = Promise.delay(5); 6 | 7 | warner.then(() => 8 | warn( 9 | `[promiseChildWhichIsA] Infinite wait possible for a "${className}" to appear under ${parent.GetFullName()}`, 10 | ), 11 | ); 12 | 13 | let connection1: RBXScriptConnection; 14 | let connection2: RBXScriptConnection; 15 | 16 | const promise = new Promise((resolve, reject) => { 17 | connection1 = parent.ChildAdded.Connect((child) => child.IsA(className) && resolve(child)); 18 | connection2 = parent.AncestryChanged.Connect( 19 | (_, newParent) => newParent || reject(`${parent.GetFullName()} had its root parent set to nil`), 20 | ); 21 | }); 22 | 23 | promise.finally(() => { 24 | warner.cancel(); 25 | connection1.Disconnect(); 26 | connection2.Disconnect(); 27 | }); 28 | 29 | return promise; 30 | } 31 | 32 | export function promiseChildOfClass(parent: Instance, className: T): Promise { 33 | const child = parent.FindFirstChildOfClass(className); 34 | if (child) return Promise.resolve(child); 35 | 36 | const warner = Promise.delay(5); 37 | 38 | warner.then(() => 39 | warn( 40 | `[promiseChildOfClass] Infinite wait possible for a "${className}" to appear under ${parent.GetFullName()}`, 41 | ), 42 | ); 43 | 44 | let connection1: RBXScriptConnection; 45 | let connection2: RBXScriptConnection; 46 | 47 | const promise = new Promise((resolve, reject) => { 48 | connection1 = parent.ChildAdded.Connect((child) => classIs(child, className) && resolve(child)); 49 | connection2 = parent.AncestryChanged.Connect( 50 | (_, newParent) => newParent || reject(`${parent.GetFullName()} had its root parent set to nil`), 51 | ); 52 | }); 53 | 54 | promise.finally(() => { 55 | warner.cancel(); 56 | connection1.Disconnect(); 57 | connection2.Disconnect(); 58 | }); 59 | 60 | return promise; 61 | } 62 | 63 | export function promiseChild(parent: Instance, childName: string | number): Promise { 64 | const child = parent.FindFirstChild(childName); 65 | if (child) return Promise.resolve(child); 66 | 67 | const connections = new Array(); 68 | const warner = Promise.delay(5); 69 | 70 | warner.then(() => 71 | warn(`[promiseChild] Infinite wait possible for "${childName}" to appear under ${parent.GetFullName()}`), 72 | ); 73 | 74 | const promise = new Promise((resolve, reject) => { 75 | connections.push( 76 | parent.ChildAdded.Connect((child) => { 77 | if (child.Name === childName) resolve(child); 78 | else 79 | connections.push( 80 | child 81 | .GetPropertyChangedSignal("Name") 82 | .Connect(() => child.Name === childName && child.Parent === parent && resolve(child)), 83 | ); 84 | }), 85 | ); 86 | 87 | connections.push( 88 | parent.AncestryChanged.Connect( 89 | (_, newParent) => newParent || reject(`${parent.GetFullName()} had its root parent set to nil`), 90 | ), 91 | ); 92 | }); 93 | 94 | promise.finally(() => { 95 | warner.cancel(); 96 | for (const connection of connections) connection.Disconnect(); 97 | }); 98 | 99 | return promise; 100 | } 101 | -------------------------------------------------------------------------------- /promise-child/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // required 4 | "allowSyntheticDefaultImports": true, 5 | "downlevelIteration": true, 6 | "jsx": "react", 7 | "jsxFactory": "Roact.createElement", 8 | "jsxFragmentFactory": "Roact.Fragment", 9 | "module": "commonjs", 10 | "moduleResolution": "Node", 11 | "noLib": true, 12 | "resolveJsonModule": true, 13 | "strict": true, 14 | "target": "ESNext", 15 | "typeRoots": ["node_modules/@rbxts"], 16 | 17 | // configurable 18 | "rootDir": "src", 19 | "outDir": "out", 20 | "baseUrl": "src", 21 | "incremental": true, 22 | "tsBuildInfoFile": "out/tsconfig.tsbuildinfo", 23 | "declaration": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /schema/README.md: -------------------------------------------------------------------------------- 1 | # schema 2 | 3 | A powerful tree reconciler. 4 | 5 | ## usage 6 | ```ts 7 | import { reconcileSchema } from "@rbxts/schema"; 8 | 9 | const Workspace = game.GetService("Workspace"); 10 | 11 | reconcileSchema( 12 | { 13 | // The ClassName we expect to match (doesn't use IsA) 14 | $className: "Folder", 15 | 16 | // A check to perform on this instance to determine validity 17 | $check: (f) => f.Name === "myFolder", 18 | 19 | // A callback to be called when all candidate instances are deemed invalid, 20 | // destroyed (but children preserved), and a new instance is instantiated. 21 | // The argument passed to this callback is the newly instantiated instance 22 | $instantiate: (f) => { 23 | f.Name = "myFolder"; 24 | f.Parent = Workspace; 25 | }, 26 | 27 | // A table with the valid children of this object indexed by their name 28 | $children: { 29 | isAwesome: "BoolValue", // shorthand for { $className: "BoolValue" } 30 | 31 | color: { 32 | $className: "Color3Value", 33 | 34 | // If we need to create a new Color3Value for this schema, set Value to white 35 | // If we don't need to create a new Color3Value, this won't be called 36 | $instantiate: (c3) => { 37 | c3.Value = Color3.fromRGB(255, 255, 255); 38 | }, 39 | }, 40 | }, 41 | }, 42 | // If `reconcileSchema` is passed `undefined` here, it will instantiate the entire tree 43 | // Otherwise, it will try to reconcile the above definition with the instance passed in. 44 | Workspace.FindFirstChild("myFolder"), 45 | ); 46 | ``` 47 | 48 | `reconcileSchema` reconciles an InstanceSchema against an Instance, while trying to preserve child instances where possible (to save instance properties). Instances are validated by their `ClassName` property and the optional `$check` callback. Child instances are made into candidates for the aforementioned check by their `Name` being a key in the `$children` table. If multiple candidates exist, this will take the first one that matches (otherwise instantiate a new object) and re-parent the children of the other candidates before continuing to the next reconcile. 49 | -------------------------------------------------------------------------------- /schema/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | export declare type InstanceSchema = { 3 | [K in keyof CreatableInstances]: { 4 | /** The name of an instances ClassName. Matched via IsA() */ 5 | $className: K; 6 | /** An optional condition which instances must pass in order to validate. No yielding or errors (unless you want the error to cancel the check) */ 7 | $check?: (instance: CreatableInstances[K]) => boolean | undefined; 8 | /** All the children inside this object. */ 9 | $children?: { 10 | [K: string]: keyof CreatableInstances | InstanceSchema | undefined; 11 | }; 12 | /** Called when reconciling a schema and instantiating this instance */ 13 | $instantiate?: (instance: CreatableInstances[K]) => void; 14 | }; 15 | }[keyof CreatableInstances]; 16 | /** Reconciles an InstanceSchema against an Instance. 17 | * This implementation tries to preserve instances where possible. 18 | * Instances are validated by their `ClassName` property and the optional `$check` callback. 19 | * Child Instances are made into candidates for the aforementioned check by their `Name`. 20 | * If multiple candidates exist, this will take the first one that matches (else instantiate a new object) and 21 | * re-parent the children of the other candidates before continuing to the next reconcile. 22 | * @returns Instance guaranteed to match the InstanceSchema. 23 | */ 24 | export declare function reconcileSchema(schema: T, instance?: Instance): EvaluateSchema; 25 | export declare type EvaluateSchema = Instances[T["$className"]] extends infer B ? (T["$check"] extends (a: unknown) => a is infer A ? (A extends B ? A : A & B) : B) & (T["$children"] extends object ? { 26 | [K in keyof T["$children"]]: T["$children"][K] extends infer U ? U extends InstanceSchema ? EvaluateSchema : U extends keyof Instances ? Instances[U] : never : never; 27 | } : unknown) : never; 28 | -------------------------------------------------------------------------------- /schema/init.lua: -------------------------------------------------------------------------------- 1 | -- Compiled with https://roblox-ts.github.io v0.3.2 2 | -- July 20, 2020 3 | -- Modified slightly 4 | 5 | local exports = {}; 6 | local instantiateChild, reconcileChildren; 7 | function instantiateChild(parent, name, schema, instanceChildren) 8 | if type(schema) == "string" then 9 | local newInstance = Instance.new(schema); 10 | newInstance.Name = name; 11 | newInstance.Parent = parent; 12 | return newInstance; 13 | else 14 | local className = schema["$className"]; 15 | local instantiate = schema["$instantiate"]; 16 | local children = schema["$children"]; 17 | local newInstance = instantiateChild(parent, name, className, nil); 18 | if instanceChildren then 19 | for _0 = 1, #instanceChildren do 20 | local child = instanceChildren[_0]; 21 | child.Parent = newInstance; 22 | end; 23 | end; 24 | if instantiate then 25 | instantiate(newInstance); 26 | instanceChildren = newInstance:GetChildren(); 27 | end; 28 | reconcileChildren(newInstance, children, instanceChildren); 29 | return newInstance; 30 | end; 31 | end; 32 | local function checkChildInMap(schema, instance, childrenToBeReparented) 33 | if type(schema) == "string" then 34 | if instance.ClassName == schema then 35 | reconcileChildren(instance, nil, childrenToBeReparented); 36 | return true; 37 | end; 38 | else 39 | local className = schema["$className"]; 40 | local check = schema["$check"]; 41 | local children = schema["$children"]; 42 | if (className == instance.ClassName) and ((check == nil) or (check(instance))) then 43 | if children and childrenToBeReparented then 44 | for _0 = 1, #childrenToBeReparented do 45 | local grandchild = childrenToBeReparented[_0]; 46 | grandchild.Parent = instance; 47 | end; 48 | end; 49 | local _1 = instance; 50 | local _0 = childrenToBeReparented; 51 | reconcileChildren(_1, children, _0 or instance:GetChildren()); 52 | return true; 53 | end; 54 | end; 55 | return false; 56 | end; 57 | local function aggregateChildren(instances) 58 | local children = {}; 59 | for _0 = 1, #instances do 60 | local instance = instances[_0]; 61 | local _1 = instance:GetChildren(); 62 | for _2 = 1, #_1 do 63 | local grandchild = _1[_2]; 64 | children[#children + 1] = grandchild; 65 | end; 66 | end; 67 | return children; 68 | end; 69 | function reconcileChildren(parent, children, instanceChildren) 70 | if children then 71 | if instanceChildren then 72 | local childMap = {}; 73 | for _0 = 1, #instanceChildren do 74 | local child = instanceChildren[_0]; 75 | local Name = child.Name; 76 | local previous = childMap[Name]; 77 | if previous then 78 | if typeof(previous) == "Instance" then 79 | childMap[Name] = { previous, child }; 80 | else 81 | previous[#previous + 1] = child; 82 | end; 83 | else 84 | childMap[Name] = child; 85 | end; 86 | end; 87 | for name, schema in pairs(children) do 88 | local child; 89 | if type(name) == "number" then 90 | local strName = tostring(name); 91 | child = childMap[strName]; 92 | if child then 93 | childMap[name] = child; 94 | childMap[strName] = nil; 95 | end; 96 | else 97 | child = childMap[name]; 98 | end; 99 | if child == nil then 100 | instantiateChild(parent, name, schema, nil); 101 | else 102 | if typeof(child) == "Instance" then 103 | if checkChildInMap(schema, child, nil) then 104 | childMap[name] = nil; 105 | end; 106 | else 107 | local allChildren = aggregateChildren(child); 108 | local validated = false; 109 | for _3 = 1, #child do 110 | local individual = child[_3]; 111 | if (not (validated)) and (checkChildInMap(schema, individual, allChildren)) then 112 | childMap[name] = nil; 113 | validated = true; 114 | else 115 | local _4 = individual:GetChildren(); 116 | for _5 = 1, #_4 do 117 | local holy = _4[_5]; 118 | holy.Parent = nil; 119 | end; 120 | individual:Destroy(); 121 | end; 122 | end; 123 | if not (validated) then 124 | for _4 = 1, #allChildren do 125 | local evil = allChildren[_4]; 126 | evil:Destroy(); 127 | end; 128 | end; 129 | end; 130 | end; 131 | end; 132 | for name, child in pairs(childMap) do 133 | local schema = children[name]; 134 | if schema ~= nil then 135 | local _3 = not (type(schema) == "string"); 136 | if _3 then 137 | if typeof(child) == "Instance" then 138 | _3 = child:GetChildren(); 139 | else 140 | _3 = aggregateChildren(child); 141 | end; 142 | end; 143 | instantiateChild(parent, name, schema, _3); 144 | end; 145 | if typeof(child) == "Instance" then 146 | child:Destroy(); 147 | else 148 | for _3 = 1, #child do 149 | local evil = child[_3]; 150 | evil:Destroy(); 151 | end; 152 | end; 153 | end; 154 | else 155 | for name, childSchema in pairs(children) do 156 | instantiateChild(parent, name, childSchema, nil); 157 | end; 158 | end; 159 | else 160 | if instanceChildren then 161 | for _0 = 1, #instanceChildren do 162 | local child = instanceChildren[_0]; 163 | child:Destroy(); 164 | end; 165 | end; 166 | if parent then 167 | parent:ClearAllChildren(); 168 | end; 169 | end; 170 | end; 171 | local function reconcileSchema(schema, instance) 172 | local className = schema["$className"]; 173 | local check = schema["$check"]; 174 | local children = schema["$children"]; 175 | if instance then 176 | if (className == instance.ClassName) and ((check == nil) or (check(instance))) then 177 | reconcileChildren(instance, children, instance:GetChildren()); 178 | return instance; 179 | else 180 | local newInstance = instantiateChild(instance.Parent, instance.Name, schema, instance:GetChildren()); 181 | instance:Destroy(); 182 | return newInstance; 183 | end; 184 | else 185 | return instantiateChild(nil, className, schema, nil); 186 | end; 187 | end; 188 | exports.reconcileSchema = reconcileSchema; 189 | return exports; 190 | -------------------------------------------------------------------------------- /schema/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/schema", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@rbxts/compiler-types": { 8 | "version": "1.0.0-beta.11.1", 9 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.0.0-beta.11.1.tgz", 10 | "integrity": "sha512-RkWbXvePkTTrydJNXa6pKoXYbD6M5IrerFi+i2aFgUotUNizQVpaCu4PNFFJ62FH2hbkrhmwgs2IiGhOqqB2TA==" 11 | }, 12 | "@rbxts/types": { 13 | "version": "1.0.431", 14 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.431.tgz", 15 | "integrity": "sha512-bVF0kPsB8qdjR7+45giJ7M+hIGV++NmnmL9k5b2oA+p8IS+T8Elzffq1yVKI1Tq5UTzHqFXxoaybUb3/ERVn1Q==" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /schema/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/schema", 3 | "version": "0.0.1", 4 | "description": "A powerful tree reconciler with support for user-defined checks and instantiate callbacks.", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "build": "npx roblox-ts && mv out/* . && rm -r out && echo \"\tMake sure to swap out Symbol.iterator for pairs()!\"" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/schema" 13 | }, 14 | "keywords": [ 15 | "Roblox", 16 | "TypeScript", 17 | "Schema", 18 | "Tree", 19 | "Validator", 20 | "Validate", 21 | "reconcile", 22 | "reconciler", 23 | "instance", 24 | "hierarchy" 25 | ], 26 | "license": "ISC", 27 | "bugs": { 28 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 29 | }, 30 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/schema/README.md", 31 | "peerDependencies": { 32 | "@rbxts/compiler-types": "^1.0.0-beta.11.1", 33 | "@rbxts/types": "^1.0.431" 34 | }, 35 | "publishConfig": { 36 | "access": "public" 37 | }, 38 | "files": [ 39 | "init.lua", 40 | "index.d.ts", 41 | "README.md" 42 | ], 43 | "dependencies": { 44 | "@rbxts/compiler-types": "^1.0.0-beta.11.1", 45 | "@rbxts/types": "^1.0.431" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /schema/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | // required 5 | "allowSyntheticDefaultImports": true, 6 | "downlevelIteration": true, 7 | "module": "commonjs", 8 | "noLib": true, 9 | "strict": true, 10 | "target": "es6", 11 | "typeRoots": [ 12 | "node_modules/@rbxts" 13 | ], 14 | // required, configurable 15 | "rootDir": "src", 16 | "outDir": "out", 17 | // optional 18 | "baseUrl": "src", 19 | "declaration": true, 20 | // optional, non-configurable 21 | "jsx": "react", 22 | "jsxFactory": "Roact.createElement" 23 | }, 24 | "typeAcquisition": { 25 | "enable": true 26 | }, 27 | "include": [ 28 | "." 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /signal/README.md: -------------------------------------------------------------------------------- 1 | # Signal 2 | BindableEvent wrapper. Passes arguments by reference rather than value. 3 | 4 | ```ts 5 | import Signal from "@rbxts/signal"; 6 | 7 | const signal = new Signal<(tab: Array) => void>(); 8 | const strings = new Array(); 9 | 10 | signal.Connect(a => print(a)) 11 | 12 | print(strings) // table: 2BC04578 13 | signal.Fire(strings) // table: 2BC04578 14 | ``` 15 | 16 | Corresponding Lua equivalent: 17 | 18 | ```lua 19 | local Signal = require(TS.getModule("signal", script.Parent)); 20 | local signal = Signal.new(); 21 | local strings = {}; 22 | signal:Connect(function(a) 23 | return print(a); 24 | end); 25 | print(strings); 26 | signal:Fire(strings); 27 | ``` 28 | -------------------------------------------------------------------------------- /signal/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * BindableEvent wrapper which passes variables by reference instead of by value 3 | */ 4 | interface Signal any = () => void, Generic extends boolean = false> { 5 | /** 6 | * Fires the BindableEvent with any number of arguments 7 | * @param args The arguments to pass into the connected functions 8 | */ 9 | Fire(...args: Parameters): void; 10 | 11 | /** 12 | * Establishes a function to be called when the event fires. 13 | * Returns a `RBXScriptConnection` object associated with the connection. 14 | * @param callback The function to connect to `BindableEvent.Event` 15 | */ 16 | Connect = Parameters>( 17 | callback: Generic extends true 18 | ? (Parameters extends Array 19 | ? (...args: O) => void 20 | : ConnectedFunctionSignature) 21 | : ConnectedFunctionSignature, 22 | ): RBXScriptConnection; 23 | 24 | /** 25 | * Establishes a function to be called when the event fires. 26 | * Returns a `RBXScriptConnection` object associated with the connection. 27 | * When the event fires, the signal callback is executed in a desynchronized state. 28 | * Using `ConnectParallel` is similar to, but more efficient than, using `Connect` followed by a call to `task.desynchronize()` in the signal handler. 29 | * Note: Scripts that connect in parallel must be rooted under an Actor. 30 | * @param callback The function to connect to `BindableEvent.Event` 31 | */ 32 | ConnectParallel = Parameters>( 33 | callback: Generic extends true 34 | ? (Parameters extends Array 35 | ? (...args: O) => void 36 | : ConnectedFunctionSignature) 37 | : ConnectedFunctionSignature, 38 | ): RBXScriptConnection; 39 | 40 | /** 41 | * Establishes a function to be called when the event fires. 42 | * Returns a `RBXScriptConnection` object associated with the connection. 43 | * The behavior of `Once` is similar to `Connect`. 44 | * However, instead of allowing multiple events to be received by the specified function, only the first event will be delivered. 45 | * Using `Once` also ensures that the connection to the function will be automatically disconnected prior the function being called. 46 | * @param callback The function to connect to `BindableEvent.Event` 47 | */ 48 | Once = Parameters>( 49 | callback: Generic extends true 50 | ? (Parameters extends Array 51 | ? (...args: O) => void 52 | : ConnectedFunctionSignature) 53 | : ConnectedFunctionSignature, 54 | ): RBXScriptConnection; 55 | 56 | /** 57 | * Yields the current thread until the thread is fired. 58 | */ 59 | Wait(): LuaTuple>; 60 | 61 | /** 62 | * Destroys the Signal 63 | */ 64 | Destroy(): void; 65 | } 66 | 67 | declare const Signal: new any = () => void, Generic extends boolean = false>() => Signal< 68 | ConnectedFunctionSignature, 69 | Generic 70 | >; 71 | 72 | export = Signal; 73 | -------------------------------------------------------------------------------- /signal/init.lua: -------------------------------------------------------------------------------- 1 | local Signal = {} 2 | Signal.__index = Signal 3 | 4 | function Signal.new() 5 | return setmetatable({ Bindable = Instance.new("BindableEvent") }, Signal) 6 | end 7 | 8 | function Signal:Connect(Callback) 9 | return self.Bindable.Event:Connect(function(GetArguments) 10 | Callback(GetArguments()) 11 | end) 12 | end 13 | 14 | function Signal:Once(Callback) 15 | return self.Bindable.Event:Once(function(GetArguments) 16 | Callback(GetArguments()) 17 | end) 18 | end 19 | 20 | function Signal:ConnectParallel(Callback) 21 | return self.Bindable.Event:ConnectParallel(function(GetArguments) 22 | Callback(GetArguments()) 23 | end) 24 | end 25 | 26 | function Signal:Fire(...) 27 | local Arguments = { ... } 28 | local n = select("#", ...) 29 | 30 | self.Bindable:Fire(function() 31 | return table.unpack(Arguments, 1, n) 32 | end) 33 | end 34 | 35 | function Signal:Wait() 36 | return self.Bindable.Event:Wait()() 37 | end 38 | 39 | function Signal:Destroy() 40 | self.Bindable:Destroy() 41 | end 42 | 43 | return Signal 44 | -------------------------------------------------------------------------------- /signal/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/signal", 3 | "version": "1.1.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@rbxts/signal", 9 | "version": "1.1.0", 10 | "license": "ISC", 11 | "peerDependencies": { 12 | "@rbxts/compiler-types": "^1.3.3-types.1", 13 | "@rbxts/types": "^1.0.640" 14 | } 15 | }, 16 | "node_modules/@rbxts/compiler-types": { 17 | "version": "1.3.3-types.1", 18 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.3.3-types.1.tgz", 19 | "integrity": "sha512-iWeioe5WziBTnY+FIT7aQ5bimlw81PYZ5d9WO0h1kX3joEigXPQpn2yHGRxzrqIssqQr47Y6fwknklaaQ6IPMQ==", 20 | "peer": true 21 | }, 22 | "node_modules/@rbxts/types": { 23 | "version": "1.0.640", 24 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.640.tgz", 25 | "integrity": "sha512-jsW+EUHQb/fKp3aFKaF2d1vs3DRlTIl0PzBf8EcAPCXB6CBpPe4eCg+G6yHSX9bQcACwZlG0Y7S4wB9C2QcFqg==", 26 | "peer": true 27 | } 28 | }, 29 | "dependencies": { 30 | "@rbxts/compiler-types": { 31 | "version": "1.3.3-types.1", 32 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.3.3-types.1.tgz", 33 | "integrity": "sha512-iWeioe5WziBTnY+FIT7aQ5bimlw81PYZ5d9WO0h1kX3joEigXPQpn2yHGRxzrqIssqQr47Y6fwknklaaQ6IPMQ==", 34 | "peer": true 35 | }, 36 | "@rbxts/types": { 37 | "version": "1.0.640", 38 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.640.tgz", 39 | "integrity": "sha512-jsW+EUHQb/fKp3aFKaF2d1vs3DRlTIl0PzBf8EcAPCXB6CBpPe4eCg+G6yHSX9bQcACwZlG0Y7S4wB9C2QcFqg==", 40 | "peer": true 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /signal/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/signal", 3 | "version": "1.1.1", 4 | "description": "BindableEvent wrapper which passes values by reference", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/signal" 13 | }, 14 | "keywords": [ 15 | "Roblox", 16 | "TypeScript", 17 | "Signal", 18 | "BindableEvent", 19 | "Bindable", 20 | "Bind" 21 | ], 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 25 | }, 26 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/signal/README.md", 27 | "devDependencies": { 28 | "@rbxts/compiler-types": "^1.3.3-types.1", 29 | "@rbxts/types": "^1.0.640" 30 | }, 31 | "publishConfig": { 32 | "access": "public" 33 | }, 34 | "files": [ 35 | "init.lua", 36 | "index.d.ts", 37 | "README.md" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /signal/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | // required 5 | "allowSyntheticDefaultImports": true, 6 | "downlevelIteration": true, 7 | "module": "commonjs", 8 | "noLib": true, 9 | "strict": true, 10 | "target": "es6", 11 | "typeRoots": [ 12 | "node_modules/@rbxts" 13 | ], 14 | // required, configurable 15 | "rootDir": "src", 16 | "outDir": "out", 17 | // optional 18 | "baseUrl": "src", 19 | "declaration": true, 20 | // optional, non-configurable 21 | "jsx": "react", 22 | "jsxFactory": "Roact.createElement" 23 | }, 24 | "typeAcquisition": { 25 | "enable": true 26 | }, 27 | "include": [ 28 | "." 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /spring/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Parker Stebbins 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /spring/README.md: -------------------------------------------------------------------------------- 1 | # Spring 2 | 3 | Simulates the motion of a critically damped spring. 4 | -------------------------------------------------------------------------------- /spring/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Simulates the motion of a critically damped spring 3 | * @original fractality 4 | * @editor Validark 5 | */ 6 | 7 | interface Spring { 8 | // spring methods/properties 9 | dampingRatio: number; 10 | angularFrequency: number; 11 | 12 | /** The value the spring makes its way towards */ 13 | goal: T; 14 | 15 | /** 16 | * Use resetToPosition to set this value 17 | */ 18 | readonly position: T; 19 | readonly velocity: T; 20 | 21 | /** 22 | * Sets the Spring position. Sets the Velocity to position * 0 to match the type 23 | * @param position The position to set the Spring to. Sets the Velocity to position * 0 to match the type 24 | */ 25 | resetToPosition(position: T): this; 26 | 27 | /** 28 | * The tick or stepping function for this spring. 29 | * @param deltaTime the change in time since the last call 30 | * @returns position 31 | */ 32 | update(deltaTime: number): T; 33 | } 34 | 35 | /** 36 | * Creates a new critically damped spring. 37 | * @param position The position to set the spring to 38 | * @param angularFrequency The angular frequency of the spring 39 | * @param goal The target of the Spring 40 | * @param dampingRatio The damping ratio of the spring 41 | */ 42 | declare const Spring: new (position: T, angularFrequency?: number, goal?: T, dampingRatio?: number) => Spring; 43 | 44 | export = Spring; 45 | -------------------------------------------------------------------------------- /spring/init.lua: -------------------------------------------------------------------------------- 1 | -- Simulates the motion of a critically damped spring 2 | -- @original fractality 3 | -- @editor Validark 4 | 5 | local Spring = {} 6 | Spring.__index = Spring 7 | 8 | local tau = math.pi * 2 9 | local exp = math.exp 10 | local sin = math.sin 11 | local cos = math.cos 12 | local sqrt = math.sqrt 13 | 14 | local EPSILON = 1e-4 15 | 16 | function Spring.new(position, angularFrequency, goal, dampingRatio) 17 | if angularFrequency == nil then angularFrequency = 10 end 18 | if dampingRatio == nil then dampingRatio = 1 end 19 | 20 | if dampingRatio * angularFrequency < 0 then 21 | error("Spring does not converge", 2) 22 | end 23 | 24 | return setmetatable({ 25 | goal = goal or position, 26 | dampingRatio = dampingRatio, 27 | angularFrequency = angularFrequency, 28 | }, Spring):resetToPosition(position) 29 | end 30 | 31 | function Spring:resetToPosition(position) 32 | self.position = position 33 | self.velocity = position * 0 -- Match the original vector type 34 | return self 35 | end 36 | 37 | function Spring:update(deltaTime) 38 | local dampingRatio = self.dampingRatio 39 | local angularFrequency = self.angularFrequency * tau 40 | local goal = self.goal 41 | local p0 = self.position 42 | local v0 = self.velocity 43 | 44 | local offset = p0 - goal 45 | local decay = exp(-dampingRatio * angularFrequency * deltaTime) 46 | local position 47 | 48 | if dampingRatio == 1 then -- Critically damped 49 | position = (offset * (1 + angularFrequency * deltaTime) + v0 * deltaTime) * decay + goal 50 | self.velocity = (v0 * (1 - angularFrequency * deltaTime) - offset * (angularFrequency * angularFrequency * deltaTime)) * decay 51 | elseif dampingRatio < 1 then -- Underdamped 52 | local e = 1 - dampingRatio * dampingRatio 53 | local c = sqrt(e) 54 | local y = angularFrequency * c 55 | local i = cos(y * deltaTime) 56 | local j = sin(y * deltaTime) 57 | 58 | -- Damping ratios approaching 1 can cause division by small numbers. 59 | -- To fix that, group terms around z=j/c and find an approximation for z. 60 | -- Start with the definition of z: 61 | -- z = sin(deltaTime*angularFrequency*c)/c 62 | -- Substitute a=deltaTime*angularFrequency: 63 | -- z = sin(a*c)/c 64 | -- Take the Maclaurin expansion of z with respect to c: 65 | -- z = a - (a^3*c^2)/6 + (a^5*c^4)/120 + O(c^6) 66 | -- z ≈ a - (a^3*c^2)/6 + (a^5*c^4)/120 67 | -- Rewrite in Horner form: 68 | -- z ≈ a + ((a*a)*(c*c)*(c*c)/20 - c*c)*(a*a*a)/6 69 | 70 | local z 71 | if c > EPSILON then 72 | z = j / c 73 | else 74 | local a = deltaTime * angularFrequency 75 | local a_2 = a * a 76 | z = a * (((e*e * a_2 - 20*e) / 120) * a_2 + 1) 77 | end 78 | 79 | -- Frequencies approaching 0 present a similar problem. 80 | -- We want an approximation for y as angularFrequency approaches 0, where: 81 | -- y = sin(deltaTime*angularFrequency*c)/(angularFrequency*c) 82 | -- Substitute b=deltaTime*c: 83 | -- y = sin(b*c)/b 84 | -- Now reapply the process from z. 85 | 86 | if y > EPSILON then 87 | y = j / y 88 | else 89 | local b = y * y 90 | local dd = deltaTime * deltaTime 91 | y = deltaTime * (dd * (b*b*dd / 20 - b) / 6 + 1) 92 | end 93 | 94 | local ze = z * dampingRatio 95 | position = (offset * (i + ze) + v0 * y) * decay + goal 96 | self.velocity = (v0 * (i - ze) - offset * (z * angularFrequency)) * decay 97 | else -- Overdamped 98 | local x = -angularFrequency * dampingRatio 99 | local y = angularFrequency * sqrt(dampingRatio * dampingRatio - 1) 100 | local r1 = x + y 101 | local r2 = x - y 102 | 103 | local co2 = (v0 - offset * r1) / (2 * y) 104 | local co1 = offset - co2 105 | 106 | local e1 = co1 * exp(r1 * deltaTime) 107 | local e2 = co2 * exp(r2 * deltaTime) 108 | 109 | position = e1 + e2 + goal 110 | self.velocity = e1 * r1 + e2 * r2 111 | end 112 | 113 | self.position = position 114 | return position 115 | end 116 | 117 | return Spring 118 | -------------------------------------------------------------------------------- /spring/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/spring", 3 | "version": "1.0.2", 4 | "description": "Simulates the motion of a critically damped spring", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/spring" 13 | }, 14 | "keywords": [ 15 | "spring", 16 | "roblox-TypeScript" 17 | ], 18 | "author": "fractality", 19 | "license": "ISC", 20 | "bugs": { 21 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 22 | }, 23 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/spring/README.md", 24 | "publishConfig": { 25 | "access": "public" 26 | }, 27 | "files": [ 28 | "init.lua", 29 | "index.d.ts", 30 | "README.md" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /synced-poller/README.md: -------------------------------------------------------------------------------- 1 | # Synced poller 2 | 3 | Calls functions on an interval along os.time (for cross-server simultaneous calls) 4 | 5 | Calls a function every `interval` seconds, whenever `(os.time() % interval == 0)`. Functions are called along `os.time()` (with `tick()` precision). 6 | 7 | If a condition is provided, it will call the callback on each poll, and cancel if it returns false. 8 | -------------------------------------------------------------------------------- /synced-poller/index.d.ts: -------------------------------------------------------------------------------- 1 | /** A polling class that continually calls a callback in sync with os.time. 2 | * If a condition is provided, it will call the callback on each poll, and cancel if it returns false 3 | */ 4 | declare class SyncedPoller { 5 | isRunning: boolean; 6 | /** 7 | * @param interval How often to call the callback. 8 | * @param callback The callback to call. 9 | * @param condition If provided, will call this on every poll, and will cancel the synced-poller if it returns false. 10 | */ 11 | constructor(interval: number, callback: () => void, condition?: () => boolean); 12 | cancel(): void; 13 | } 14 | export = SyncedPoller; 15 | -------------------------------------------------------------------------------- /synced-poller/init.lua: -------------------------------------------------------------------------------- 1 | -- Compiled with roblox-ts v1.2.3 2 | --[[ 3 | * The difference in seconds between os.time() and os.clock(). 4 | * With this, we can use `os.clock() + timeDifferential` as a more precise os.time() 5 | ]] 6 | local timeDifferential 7 | do 8 | local targetTime = os.time() + 1 9 | while os.time() ~= targetTime do 10 | end 11 | timeDifferential = targetTime - os.clock() 12 | end 13 | --[[ 14 | * A polling class that continually calls a callback in sync with os.time. 15 | * If a condition is provided, it will call the callback on each poll, and cancel if it returns false 16 | ]] 17 | local SyncedPoller 18 | do 19 | SyncedPoller = setmetatable({}, { 20 | __tostring = function() 21 | return "SyncedPoller" 22 | end, 23 | }) 24 | SyncedPoller.__index = SyncedPoller 25 | function SyncedPoller.new(...) 26 | local self = setmetatable({}, SyncedPoller) 27 | return self:constructor(...) or self 28 | end 29 | function SyncedPoller:constructor(interval, callback, condition) 30 | self.isRunning = true 31 | local recall 32 | recall = function() 33 | if self.isRunning then 34 | if condition == nil or condition() then 35 | callback() 36 | task.delay(interval - ((os.clock() + timeDifferential) % interval), recall) 37 | else 38 | self.isRunning = false 39 | end 40 | end 41 | end 42 | task.delay(interval - ((os.clock() + timeDifferential) % interval), recall) 43 | end 44 | function SyncedPoller:cancel() 45 | self.isRunning = false 46 | end 47 | end 48 | return SyncedPoller 49 | -------------------------------------------------------------------------------- /synced-poller/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/synced-poller", 3 | "version": "2.0.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@rbxts/synced-poller", 9 | "version": "2.0.1", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "@rbxts/compiler-types": "^2.0.2-types.0", 13 | "@rbxts/types": "^1.0.641" 14 | } 15 | }, 16 | "node_modules/@rbxts/compiler-types": { 17 | "version": "2.0.2-types.0", 18 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-2.0.2-types.0.tgz", 19 | "integrity": "sha512-m947QoUoYnRIZygwaEzuBqJIYdOg0t1qCi1ZShXA5M9elUi4XrGebytuC1qGFoWHvtu4N7vqIrma5LzpaOT/Jw==", 20 | "dev": true 21 | }, 22 | "node_modules/@rbxts/types": { 23 | "version": "1.0.641", 24 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.641.tgz", 25 | "integrity": "sha512-yjS3NQyOglkchaV8FcqTRJSB6hSVLFsCcSem6BLLjLGlazRuvrbTO+ss0H/jYbA8FMumpEXzGrBsNTHmfdgbPQ==", 26 | "dev": true 27 | } 28 | }, 29 | "dependencies": { 30 | "@rbxts/compiler-types": { 31 | "version": "2.0.2-types.0", 32 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-2.0.2-types.0.tgz", 33 | "integrity": "sha512-m947QoUoYnRIZygwaEzuBqJIYdOg0t1qCi1ZShXA5M9elUi4XrGebytuC1qGFoWHvtu4N7vqIrma5LzpaOT/Jw==", 34 | "dev": true 35 | }, 36 | "@rbxts/types": { 37 | "version": "1.0.641", 38 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.641.tgz", 39 | "integrity": "sha512-yjS3NQyOglkchaV8FcqTRJSB6hSVLFsCcSem6BLLjLGlazRuvrbTO+ss0H/jYbA8FMumpEXzGrBsNTHmfdgbPQ==", 40 | "dev": true 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /synced-poller/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/synced-poller", 3 | "version": "2.0.1", 4 | "description": "Calls functions on an interval along os.time (for cross-server simultaneous calls)", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "build": "rbxtsc & mv out/* . & rm -r out" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/synced-poller" 13 | }, 14 | "keywords": [ 15 | "yield", 16 | "wait", 17 | "poll", 18 | "poller", 19 | "synced" 20 | ], 21 | "author": "Validark", 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 25 | }, 26 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/synced-poller/README.md", 27 | "publishConfig": { 28 | "access": "public" 29 | }, 30 | "devDependencies": { 31 | "@rbxts/compiler-types": "^2.0.2-types.0", 32 | "@rbxts/types": "^1.0.641" 33 | }, 34 | "files": [ 35 | "init.lua", 36 | "index.d.ts", 37 | "README.md" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /synced-poller/src/index.ts: -------------------------------------------------------------------------------- 1 | /** The difference in seconds between os.time() and os.clock(). 2 | * With this, we can use `os.clock() + timeDifferential` as a more precise os.time() 3 | */ 4 | let timeDifferential: number; 5 | 6 | { 7 | const targetTime = os.time() + 1; 8 | while (os.time() !== targetTime); 9 | timeDifferential = targetTime - os.clock(); 10 | } 11 | 12 | /** A polling class that continually calls a callback in sync with os.time. 13 | * If a condition is provided, it will call the callback on each poll, and cancel if it returns false 14 | */ 15 | class SyncedPoller { 16 | public isRunning = true; 17 | 18 | /** 19 | * @param interval How often to call the callback. 20 | * @param callback The callback to call. 21 | * @param condition If provided, will call this on every poll, and will cancel the synced-poller if it returns false. 22 | */ 23 | constructor(interval: number, callback: () => void, condition?: () => boolean) { 24 | const recall = () => { 25 | if (this.isRunning) { 26 | if (condition === undefined || condition()) { 27 | callback(); 28 | task.delay(interval - ((os.clock() + timeDifferential) % interval), recall); 29 | } else { 30 | this.isRunning = false; 31 | } 32 | } 33 | }; 34 | 35 | task.delay(interval - ((os.clock() + timeDifferential) % interval), recall); 36 | } 37 | 38 | public cancel() { 39 | this.isRunning = false; 40 | } 41 | } 42 | 43 | export = SyncedPoller; 44 | -------------------------------------------------------------------------------- /synced-poller/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | // required 5 | "allowSyntheticDefaultImports": true, 6 | "downlevelIteration": true, 7 | "module": "commonjs", 8 | "noLib": true, 9 | "strict": true, 10 | "target": "es6", 11 | "typeRoots": [ 12 | "node_modules/@rbxts" 13 | ], 14 | // required, configurable 15 | "rootDir": "src", 16 | "outDir": "out", 17 | // optional 18 | "baseUrl": "src", 19 | "declaration": true, 20 | // optional, non-configurable 21 | "jsx": "react", 22 | "jsxFactory": "Roact.createElement" 23 | }, 24 | "typeAcquisition": { 25 | "enable": true 26 | }, 27 | "include": [ 28 | "." 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "array-type": [true, "generic"], 5 | "arrow-parens": false, 6 | "class-name": true, 7 | "curly": false, 8 | "forin": false, 9 | "indent": [true, "tabs", 4], 10 | "interface-name": false, 11 | "max-classes-per-file": false, 12 | "no-bitwise": true, 13 | "no-console": false, 14 | "no-empty-interface": false, 15 | "no-empty": true, 16 | "no-string-literal": true, 17 | "no-unnecessary-qualifier": true, 18 | "no-unused-variable": true, 19 | "no-var-keyword": true, 20 | "no-namespace": false, 21 | "prefer-const": true, 22 | "quotemark": [true, "double", "avoid-escape" ], 23 | "semicolon": [true, "always", "ignore-bound-class-methods"], 24 | "triple-equals": true, 25 | "no-string-throw":false, 26 | "object-literal-sort-keys":false, 27 | "member-ordering": [true, { 28 | "order": [ 29 | "private-static-field", 30 | "protected-static-field", 31 | "public-static-field", 32 | 33 | "private-static-method", 34 | "protected-static-method", 35 | "public-static-method", 36 | 37 | "private-instance-field", 38 | "protected-instance-field", 39 | "public-instance-field", 40 | 41 | "private-constructor", 42 | "protected-constructor", 43 | "public-constructor", 44 | 45 | "private-instance-method", 46 | "protected-instance-method", 47 | "public-instance-method" 48 | ] 49 | }] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tween/README.md: -------------------------------------------------------------------------------- 1 | # Tween 2 | A library for writing efficient, simple interpolation code. 3 | 4 | Setup: 5 | ```ts 6 | import { Standard, Deceleration, OutElastic } from "@rbxts/easing-functions"; 7 | import Tween from "@rbxts/tween"; 8 | 9 | const Workspace = game.GetService("Workspace"); 10 | ``` 11 | 12 | Here is the generalized form: 13 | 14 | ```ts 15 | /** 16 | * Creates a Tween along a curve, with a callback to call each tick. 17 | * Connects an interpolation function to RunService.RenderStepped if initialValue is a CFrame, 18 | * else RunService.Heartbeat. 19 | * @param totalDuration The total duration of the Tween 20 | * @param easingFunction The easingFunction to call each tick 21 | * @param callback The function to call each tick 22 | * @param initialValue The starting value to interpolate to push into the callback function each tick. default=0 23 | * @param endValue The target value the initialValue should reach. default=1 24 | * @param extraValue1 An extra value to be passed into the easingFunction 25 | * @param extraValue2 An extra value to be passed into the easingFunction 26 | */ 27 | declare function Tween( 28 | totalDuration: number, 29 | easingFunction: (delta: T) => void, 30 | callback: (delta: T) => void, 31 | initialValue?: T, 32 | endValue?: T, 33 | extraValue1?: any, 34 | extraValue2?: any, 35 | ): PseudoTween; 36 | ``` 37 | 38 | Here is the simplest form: 39 | 40 | This will return a Tween object, and connect this interpolation to RunService.Heartbeat. x will be a number along the Standard curve [0, 1]. The interpolation will last 1 second. Standard is the EasingFunction, which is the curve along which this interpolation will animate. 41 | ```ts 42 | Tween(1, Standard, x => print(x)); 43 | ``` 44 | 45 | Functions that interpolate CFrames will instead connect to RunService.RenderStepped. 46 | ```ts 47 | const Camera = Workspace.CurrentCamera; 48 | const StartPosition = Camera.CFrame; 49 | const EndPosition = Camera.CFrame.add(new CFrame(5, 5, 5)); 50 | 51 | // binds to RenderStepped 52 | Tween(2, Deceleration, Position => Camera.CFrame = Position, StartPosition, EndPosition); 53 | ``` 54 | 55 | StartPosition and EndPosition can be basically any Roblox type (default = number), and this will be the type that gets passed into the callback. 56 | ```ts 57 | const red = Color3.fromRGB(255, 0, 0); 58 | const blue = Color3.fromRGB(0, 0, 255); 59 | const myObj = new Instance("Part"); 60 | myObj.Parent = Workspace 61 | 62 | // The last two 0.5 arguments will be passed into OutElastic, as parameters representing amplitude and period 63 | Tween(1, OutElastic, x => print(x), myObj.Color, blue, 0.5, 0.5) 64 | ``` 65 | 66 | When lerping Color3 values, it will automatically lerp across the CIELUV Color space: 67 | 68 | ![](https://i.gyazo.com/7b20b827543f913594edc95646486204.gif) 69 | ![](https://i.gyazo.com/43b29d3dd432dc5ce94185bd13730190.gif) 70 | 71 | ## PseudoTween Object 72 | 73 | Calling the Tween function will return a PseudoTween, which has pretty much the same API as Roblox's TweenBase. 74 | 75 | ```ts 76 | /** 77 | * A Tween Object 78 | */ 79 | interface PseudoTween { 80 | /** 81 | * The Play function starts the playback of its Tween. 82 | * Note: if a tween has already begun calling Play, 83 | * this will have no effect unless the tween has finished or has been stopped 84 | * (either by this.Cancel() or this.Pause()). 85 | */ 86 | Resume(): this; 87 | 88 | /** 89 | * The Pause function halts playback of its Tween. 90 | * If TweenBase/Resume is called again the tween will resume playback from the moment it was paused. 91 | */ 92 | Pause(): this; 93 | 94 | /** 95 | * The Cancel function halts playback of its Tween and resets the tween variables. 96 | * If TweenBase:Play is called again the Tween's properties will resume interpolating towards their destination but, 97 | * as the tween variables have been reset, take the full length of the animation to do so. 98 | */ 99 | Cancel(): this; 100 | 101 | /** 102 | * Yields the current thread until the Tween has completed. 103 | */ 104 | Wait(): this; 105 | } 106 | ``` 107 | -------------------------------------------------------------------------------- /tween/index.d.ts: -------------------------------------------------------------------------------- 1 | import Bezier from "@rbxts/cubic-bezier"; 2 | import { BasicEasingFunction, OvershootEasingFunction, PeriodicEasingFunction } from "@rbxts/easing-functions"; 3 | 4 | type LerpFunctions = 5 | | string 6 | | number 7 | | boolean 8 | | Region3 9 | | CFrame 10 | | UDim2 11 | | UDim 12 | | Ray 13 | | Rect 14 | | Color3 15 | | Vector2 16 | | Vector3 17 | | NumberRange 18 | | ColorSequence 19 | | NumberSequence 20 | | PhysicalProperties 21 | | NumberSequenceKeypoint; 22 | 23 | /** 24 | * A Tween Object 25 | */ 26 | export interface PseudoTween { 27 | /** 28 | * Whether the Tween is currently interpolating 29 | */ 30 | readonly Running: boolean; 31 | 32 | /** 33 | * How much time has elapsed on the Tween 34 | */ 35 | readonly ElapsedTime: number; 36 | 37 | /** 38 | * The Play function starts the playback of its Tween. 39 | * Note: if a tween has already begun calling Play, 40 | * this will have no effect unless the tween has finished or has been stopped 41 | * (either by this.Cancel() or this.Pause()). 42 | */ 43 | Play(): this; 44 | 45 | /** 46 | * The Pause function halts playback of its Tween. 47 | * If TweenBase/Play is called again the tween will resume playback from the moment it was paused. 48 | */ 49 | Pause(): this; 50 | 51 | /** 52 | * The Cancel function halts playback of its Tween and resets the tween variables. 53 | * If TweenBase:Play is called again the Tween's properties will resume interpolating towards their destination but, 54 | * as the tween variables have been reset, take the full length of the animation to do so. 55 | */ 56 | Cancel(): this; 57 | 58 | /** 59 | * Yields the current thread until the Tween has completed. 60 | */ 61 | Wait(): this; 62 | } 63 | 64 | /** 65 | * Creates a Tween along a curve, with a callback to call each tick. 66 | * Connects an interpolation function to RunService.Heartbeat. 67 | * @param easingFunction The easingFunction to call each tick 68 | * @param callback The function to call each tick 69 | */ 70 | export declare function Tween( 71 | totalDuration: number, 72 | easingFunction: BasicEasingFunction | Bezier | PeriodicEasingFunction, 73 | callback: (delta: number) => void, 74 | ): PseudoTween; 75 | 76 | /** 77 | * Creates a Tween along a curve, with a callback to call each tick. 78 | * Connects an interpolation function to RunService.RenderStepped if initialValue is a CFrame, 79 | * else RunService.Heartbeat. 80 | * @param totalDuration The total duration of the Tween 81 | * @param easingFunction The easingFunction to call each tick 82 | * @param callback The function to call each tick 83 | * @param initialValue The starting value to interpolate to push into the callback function each tick. 84 | * @param endValue The target value the initialValue should reach. 85 | */ 86 | export declare function Tween( 87 | totalDuration: number, 88 | easingFunction: BasicEasingFunction | Bezier | PeriodicEasingFunction, 89 | callback: (delta: T) => void, 90 | initialValue: T, 91 | endValue: T, 92 | ): PseudoTween; 93 | 94 | /** 95 | * Creates a Tween along a curve, with a callback to call each tick. 96 | * Connects an interpolation function to RunService.RenderStepped if initialValue is a CFrame, 97 | * else RunService.Heartbeat. 98 | * @param totalDuration The total duration of the Tween 99 | * @param easingFunction The easingFunction to call each tick 100 | * @param callback The function to call each tick 101 | * @param initialValue The starting value to interpolate to push into the callback function each tick. 102 | * @param endValue The target value the initialValue should reach. 103 | * @param amplitude The amplitude of the curve 104 | * @param period The period of the curve 105 | */ 106 | export declare function Tween( 107 | totalDuration: number, 108 | easingFunction: PeriodicEasingFunction, 109 | callback: (delta: T) => void, 110 | initialValue: T, 111 | endValue: T, 112 | amplitude: number, 113 | period: number, 114 | ): PseudoTween; 115 | 116 | /** 117 | * Creates a Tween along a curve, with a callback to call each tick. 118 | * Connects an interpolation function to RunService.RenderStepped if initialValue is a CFrame, 119 | * else RunService.Heartbeat. 120 | * @param totalDuration The total duration of the Tween 121 | * @param easingFunction The easingFunction to call each tick 122 | * @param callback The function to call each tick 123 | * @param initialValue The starting value to interpolate to push into the callback function each tick. 124 | * @param endValue The target value the initialValue should reach. 125 | * @param overstep The overstep of the curve 126 | */ 127 | export declare function Tween( 128 | totalDuration: number, 129 | easingFunction: OvershootEasingFunction, 130 | callback: (delta: T) => void, 131 | initialValue: T, 132 | endValue: T, 133 | overstep: number, 134 | ): PseudoTween; 135 | 136 | /** 137 | * Creates a Tween along a curve, with a callback to call each tick. 138 | * Connects an interpolation function to RunService.RenderStepped if initialValue is a CFrame, 139 | * else RunService.Heartbeat. 140 | * @param totalDuration The total duration of the Tween 141 | * @param easingFunction The easingFunction to call each tick 142 | * @param callback The function to call each tick 143 | * @param initialValue The starting value to interpolate to push into the callback function each tick. default=0 144 | * @param endValue The target value the initialValue should reach. default=1 145 | * @param extraValue1 An extra value to be passed into the easingFunction 146 | * @param extraValue2 An extra value to be passed into the easingFunction 147 | */ 148 | export declare function Tween( 149 | totalDuration: number, 150 | easingFunction: (delta: T) => void, 151 | callback: (delta: T) => void, 152 | initialValue?: T, 153 | endValue?: T, 154 | extraValue1?: any, 155 | extraValue2?: any, 156 | ): PseudoTween; 157 | 158 | export default Tween; 159 | -------------------------------------------------------------------------------- /tween/init.lua: -------------------------------------------------------------------------------- 1 | local Lerps = require(assert(script.Parent:FindFirstChild("lerp-functions"), "[@rbxts/tween] Please install @rbxts/lerp-functions to use this library.")) 2 | 3 | local Tween = { 4 | Running = false; 5 | ElapsedTime = 0; 6 | } 7 | Tween.__index = Tween 8 | 9 | local RunService = game:GetService("RunService") 10 | local Heartbeat = RunService.Heartbeat 11 | local RenderStepped = RunService.RenderStepped 12 | 13 | function Tween:Play() 14 | if not self.Running then 15 | local RenderEvent 16 | 17 | if self.ValueType == "CFrame" then 18 | RenderEvent = RenderStepped 19 | else 20 | RenderEvent = Heartbeat 21 | end 22 | 23 | self.Connection = RenderEvent:Connect(self.Interpolator) 24 | self.Running = true 25 | end 26 | 27 | return self 28 | end 29 | 30 | function Tween:Pause() 31 | if self.Running then 32 | self.Connection:Disconnect() 33 | self.Running = false 34 | end 35 | 36 | return self 37 | end 38 | 39 | function Tween:Cancel() 40 | self.ElapsedTime = 0 41 | return self:Pause() 42 | end 43 | 44 | function Tween:Wait() 45 | local RenderEvent 46 | 47 | if self.ValueType == "CFrame" then 48 | RenderEvent = RenderStepped 49 | else 50 | RenderEvent = Heartbeat 51 | end 52 | 53 | while self.Running do RenderEvent:Wait() end 54 | return self 55 | end 56 | 57 | local function MakeTween(Duration, EasingFunction, Callback, InitialValue, EndValue, v1, v2) 58 | if InitialValue == nil then 59 | InitialValue = 0 60 | EndValue = 1 61 | end 62 | 63 | local ValueType = typeof(InitialValue) 64 | local LerpFunction = Lerps[ValueType](InitialValue, EndValue) 65 | 66 | local self = setmetatable({ 67 | Duration = Duration; 68 | ValueType = ValueType; 69 | }, Tween) 70 | 71 | function self.Interpolator(Step) 72 | local ElapsedTime = self.ElapsedTime + Step 73 | self.ElapsedTime = ElapsedTime 74 | 75 | if Duration > ElapsedTime then 76 | -- Because of the way doubles are implemented, 77 | -- they can hold more unique values between 0 and 1 than any other numbers. 78 | -- To take advantage of this precision, we shouldn't try to get smart about 79 | -- changing the beginning/change parameters here 80 | Callback(LerpFunction(EasingFunction(ElapsedTime, 0, 1, Duration, v1, v2))) 81 | else 82 | Callback(EndValue) 83 | self:Pause() 84 | end 85 | end 86 | 87 | return self:Play() 88 | end 89 | 90 | return { 91 | Tween = MakeTween; 92 | default = MakeTween; 93 | } 94 | -------------------------------------------------------------------------------- /tween/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/tween", 3 | "version": "1.0.4", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@rbxts/tween", 9 | "version": "1.0.4", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@rbxts/cubic-bezier": "^1.0.2", 13 | "@rbxts/easing-functions": "^1.0.0", 14 | "@rbxts/lerp-functions": "^1.0.0" 15 | }, 16 | "devDependencies": { 17 | "@rbxts/compiler-types": "^2.0.2-types.0", 18 | "@rbxts/types": "^1.0.641" 19 | } 20 | }, 21 | "node_modules/@rbxts/compiler-types": { 22 | "version": "2.0.2-types.0", 23 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-2.0.2-types.0.tgz", 24 | "integrity": "sha512-m947QoUoYnRIZygwaEzuBqJIYdOg0t1qCi1ZShXA5M9elUi4XrGebytuC1qGFoWHvtu4N7vqIrma5LzpaOT/Jw==", 25 | "dev": true 26 | }, 27 | "node_modules/@rbxts/cubic-bezier": { 28 | "version": "1.0.2", 29 | "resolved": "https://registry.npmjs.org/@rbxts/cubic-bezier/-/cubic-bezier-1.0.2.tgz", 30 | "integrity": "sha512-g2z5pqUN1f8BO8nqjTbQyfIwVj9K2SoCLNPKmlez2iYqOpMn4VyNPPgac7f1y6MJrenqAa4BWMmTWwuI07gmLg==" 31 | }, 32 | "node_modules/@rbxts/easing-functions": { 33 | "version": "1.0.0", 34 | "resolved": "https://registry.npmjs.org/@rbxts/easing-functions/-/easing-functions-1.0.0.tgz", 35 | "integrity": "sha512-10n1S3B4HDCMDQFkyVOfNgY5Rf0+6xWt5dqJdWosKFIsGy35hMI0UswrRvGtgzTFoMZKvVHg0zBRT+0JXcp+9A==", 36 | "dependencies": { 37 | "@rbxts/cubic-bezier": "^1.0.2" 38 | }, 39 | "peerDependencies": { 40 | "@rbxts/cubic-bezier": "^1.0.2" 41 | } 42 | }, 43 | "node_modules/@rbxts/lerp-functions": { 44 | "version": "1.0.0", 45 | "resolved": "https://registry.npmjs.org/@rbxts/lerp-functions/-/lerp-functions-1.0.0.tgz", 46 | "integrity": "sha512-CCsICLGs6GJDIfIhcRh8j20Ux/qPmKn6Po0VlFpwBkEtVtpln+7z3LDqOY035OTVPbRi3SciXTB9QctYVe2AOA==", 47 | "dependencies": { 48 | "@rbxts/types": "^1.0.194" 49 | } 50 | }, 51 | "node_modules/@rbxts/types": { 52 | "version": "1.0.641", 53 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.641.tgz", 54 | "integrity": "sha512-yjS3NQyOglkchaV8FcqTRJSB6hSVLFsCcSem6BLLjLGlazRuvrbTO+ss0H/jYbA8FMumpEXzGrBsNTHmfdgbPQ==" 55 | } 56 | }, 57 | "dependencies": { 58 | "@rbxts/compiler-types": { 59 | "version": "2.0.2-types.0", 60 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-2.0.2-types.0.tgz", 61 | "integrity": "sha512-m947QoUoYnRIZygwaEzuBqJIYdOg0t1qCi1ZShXA5M9elUi4XrGebytuC1qGFoWHvtu4N7vqIrma5LzpaOT/Jw==", 62 | "dev": true 63 | }, 64 | "@rbxts/cubic-bezier": { 65 | "version": "1.0.2", 66 | "resolved": "https://registry.npmjs.org/@rbxts/cubic-bezier/-/cubic-bezier-1.0.2.tgz", 67 | "integrity": "sha512-g2z5pqUN1f8BO8nqjTbQyfIwVj9K2SoCLNPKmlez2iYqOpMn4VyNPPgac7f1y6MJrenqAa4BWMmTWwuI07gmLg==" 68 | }, 69 | "@rbxts/easing-functions": { 70 | "version": "1.0.0", 71 | "resolved": "https://registry.npmjs.org/@rbxts/easing-functions/-/easing-functions-1.0.0.tgz", 72 | "integrity": "sha512-10n1S3B4HDCMDQFkyVOfNgY5Rf0+6xWt5dqJdWosKFIsGy35hMI0UswrRvGtgzTFoMZKvVHg0zBRT+0JXcp+9A==", 73 | "requires": { 74 | "@rbxts/cubic-bezier": "^1.0.2" 75 | } 76 | }, 77 | "@rbxts/lerp-functions": { 78 | "version": "1.0.0", 79 | "resolved": "https://registry.npmjs.org/@rbxts/lerp-functions/-/lerp-functions-1.0.0.tgz", 80 | "integrity": "sha512-CCsICLGs6GJDIfIhcRh8j20Ux/qPmKn6Po0VlFpwBkEtVtpln+7z3LDqOY035OTVPbRi3SciXTB9QctYVe2AOA==", 81 | "requires": { 82 | "@rbxts/types": "^1.0.194" 83 | } 84 | }, 85 | "@rbxts/types": { 86 | "version": "1.0.641", 87 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.641.tgz", 88 | "integrity": "sha512-yjS3NQyOglkchaV8FcqTRJSB6hSVLFsCcSem6BLLjLGlazRuvrbTO+ss0H/jYbA8FMumpEXzGrBsNTHmfdgbPQ==" 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tween/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/tween", 3 | "version": "1.0.5", 4 | "description": "A library for writing efficient, simple interpolation code", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/tween" 13 | }, 14 | "keywords": [ 15 | "bezier", 16 | "cubic-bezier", 17 | "curve", 18 | "interpolation", 19 | "interpolate", 20 | "easing", 21 | "ease", 22 | "lerp", 23 | "tween", 24 | "roblox" 25 | ], 26 | "bundledDependencies": [], 27 | "dependencies": { 28 | "@rbxts/easing-functions": "^1.0.0", 29 | "@rbxts/lerp-functions": "^1.0.0", 30 | "@rbxts/cubic-bezier": "^1.0.2" 31 | }, 32 | "devDependencies": { 33 | "@rbxts/types": "^1.0.641", 34 | "@rbxts/compiler-types": "^2.0.2-types.0" 35 | }, 36 | "author": "Validark", 37 | "license": "ISC", 38 | "bugs": { 39 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 40 | }, 41 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/tween/README.md", 42 | "publishConfig": { 43 | "access": "public" 44 | }, 45 | "files": [ 46 | "init.lua", 47 | "index.d.ts", 48 | "README.md" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /tween/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | // required 5 | "allowSyntheticDefaultImports": true, 6 | "downlevelIteration": true, 7 | "module": "commonjs", 8 | "noLib": true, 9 | "strict": true, 10 | "target": "es6", 11 | "typeRoots": [ 12 | "node_modules/@rbxts" 13 | ], 14 | // required, configurable 15 | "rootDir": "src", 16 | "outDir": "out", 17 | // optional 18 | "baseUrl": "src", 19 | "declaration": true, 20 | // optional, non-configurable 21 | "jsx": "react", 22 | "jsxFactory": "Roact.createElement" 23 | }, 24 | "typeAcquisition": { 25 | "enable": true 26 | }, 27 | "include": [ 28 | "." 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /validate-tree/README.md: -------------------------------------------------------------------------------- 1 | # validate-tree 2 | 3 | Validates a rojo-esque tree definition, like so: 4 | 5 | ## Demo 6 | 7 | ```ts 8 | const projectTree = { 9 | $className: "Folder", 10 | NumItems: { 11 | $className: "IntValue", 12 | Data: "Folder", // This is a shorthand for { $className: "Folder" } 13 | }, 14 | } as const; 15 | 16 | function g(o: EvaluateInstanceTree) { 17 | return o.NumItems.Value++; 18 | } 19 | 20 | function f(o: Instance) { 21 | if (validateTree(o, projectTree)) { 22 | print(o.NumItems.Data.GetFullName()); // good 23 | g(o); // good! 24 | } 25 | o.NumItems.Data.GetFullName(); // error! 26 | g(o); // error! 27 | 28 | promiseTree(o, projectTree).then(project => { 29 | print(project.NumItems.Value) 30 | }) 31 | } 32 | ``` 33 | 34 | The first parameter must be an Instance (or extend from it). The second parameter must be an object tree similar to ones considered valid by Rojo. Every tree must have a `$className` member, and can have any number of keys which represent the name of a child instance, which should have a corresponding value which is this same kind of tree. There is also a shorthand syntax, seen above with `Folder: "Folder"`, which is equivalent to `Folder: { $className: "Folder" }`. 35 | 36 | ###### Note: Currently, the `as const` may be necessary to preserve the true type of the object tree. Your types will not work if you do not use the tree object in-line or declare it with `as const` after it. 37 | 38 | This library also exports `EvaluateInstanceTree` if you want to use it for your own nefarious purposes. 39 | -------------------------------------------------------------------------------- /validate-tree/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /** Defines a Rojo-esque tree type which defines an abstract object tree. */ 4 | export interface InstanceTree { 5 | $className?: keyof Instances; 6 | [Key: string]: keyof Instances | undefined | InstanceTree; 7 | } 8 | declare type MoreSpecificType = U extends D ? U : D extends U ? D : U & D; 9 | declare type AllKeys = T extends any ? keyof T : never; 10 | /** Evaluates a Rojo-esque tree and transforms it into an indexable type. */ 11 | export declare type EvaluateInstanceTree = (T extends { 12 | $className: keyof Instances; 13 | } ? Instances[T["$className"]] : D) extends infer U ? MoreSpecificType & { 14 | [K in Exclude>>]: T[K] extends keyof Instances ? Instances[T[K]] : EvaluateInstanceTree; 15 | } : never; 16 | /** Returns whether a given Instance matches a particular Rojo-esque InstanceTree. 17 | * @param object The object which needs validation 18 | * @param tree The tree to validate 19 | * @param violators 20 | */ 21 | export declare function validateTree(object: I, tree: T, violators?: Array): object is EvaluateInstanceTree; 22 | /** Promises a given tree of objects exists within an object. 23 | * @param tree Must be an object tree similar to ones considered valid by Rojo. 24 | * Every tree must have a `$className` member, and can have any number of keys which represent 25 | * the name of a child instance, which should have a corresponding value which is this same kind of tree. 26 | * There is also a shorthand syntax available, where setting a key equal to a className is equivalent 27 | * to an object with `$className` defined. Hence `Things: "Folder"` is equivalent to `Things: { $className: "Folder" }` 28 | */ 29 | export declare function promiseTree(object: I, tree: T): Promise>; 30 | export {}; 31 | -------------------------------------------------------------------------------- /validate-tree/init.lua: -------------------------------------------------------------------------------- 1 | -- Compiled with roblox-ts v1.3.3 2 | local TS = _G[script] 3 | -- * Defines a Rojo-esque tree type which defines an abstract object tree. 4 | -- * Evaluates a Rojo-esque tree and transforms it into an indexable type. 5 | local function getService(serviceName) 6 | return game:GetService(serviceName) 7 | end 8 | --[[ 9 | * Returns whether a given Instance matches a particular Rojo-esque InstanceTree. 10 | * @param object The object which needs validation 11 | * @param tree The tree to validate 12 | * @param violators 13 | ]] 14 | local function validateTree(object, tree, violators) 15 | if tree["$className"] ~= nil and not object:IsA(tree["$className"]) then 16 | return false 17 | end 18 | local matches = true 19 | if object.ClassName == "DataModel" then 20 | for serviceName, classOrTree in pairs(tree) do 21 | if serviceName ~= "$className" then 22 | local success, value = pcall(getService, serviceName) 23 | if not success then 24 | if violators ~= nil then 25 | local _arg0 = 'game.GetService("' .. (serviceName .. '")') 26 | table.insert(violators, _arg0) 27 | end 28 | return false 29 | end 30 | if value and (type(classOrTree) == "string" or validateTree(value, classOrTree, violators)) then 31 | if value.Name ~= serviceName then 32 | value.Name = serviceName 33 | end 34 | else 35 | if violators == nil then 36 | return false 37 | end 38 | matches = false 39 | local _arg0 = 'game.GetService("' .. (serviceName .. '")') 40 | table.insert(violators, _arg0) 41 | end 42 | end 43 | end 44 | else 45 | local whitelistedKeys = { 46 | ["$className"] = true, 47 | } 48 | for _, child in ipairs(object:GetChildren()) do 49 | local childName = child.Name 50 | if childName ~= "$className" then 51 | local classOrTree = tree[childName] 52 | if if type(classOrTree) == "string" then child:IsA(classOrTree) else classOrTree and validateTree(child, classOrTree, violators) then 53 | whitelistedKeys[childName] = true 54 | end 55 | end 56 | end 57 | for key in pairs(tree) do 58 | if not (whitelistedKeys[key] ~= nil) then 59 | if violators == nil then 60 | return false 61 | end 62 | matches = false 63 | local _arg0 = object:GetFullName() .. "." .. key 64 | table.insert(violators, _arg0) 65 | end 66 | end 67 | end 68 | return matches 69 | end 70 | --[[ 71 | * Promises a given tree of objects exists within an object. 72 | * @param tree Must be an object tree similar to ones considered valid by Rojo. 73 | * Every tree must have a `$className` member, and can have any number of keys which represent 74 | * the name of a child instance, which should have a corresponding value which is this same kind of tree. 75 | * There is also a shorthand syntax available, where setting a key equal to a className is equivalent 76 | * to an object with `$className` defined. Hence `Things: "Folder"` is equivalent to `Things: { $className: "Folder" }` 77 | ]] 78 | local function promiseTree(object, tree) 79 | if validateTree(object, tree) then 80 | return TS.Promise.resolve(object) 81 | end 82 | local connections = {} 83 | local warner = TS.Promise.delay(5) 84 | local _arg0 = function() 85 | local violators = {} 86 | if not validateTree(object, tree, violators) then 87 | warn("[promiseTree] Infinite wait possible. Waiting for: " .. table.concat(violators, ", ")) 88 | end 89 | end 90 | warner:andThen(_arg0) 91 | local promise = TS.Promise.new(function(resolve) 92 | local function updateTree(violators) 93 | if validateTree(object, tree, violators) then 94 | resolve(object) 95 | end 96 | end 97 | for _, d in ipairs(object:GetDescendants()) do 98 | local _arg0_1 = d:GetPropertyChangedSignal("Name"):Connect(updateTree) 99 | table.insert(connections, _arg0_1) 100 | end 101 | local _arg0_1 = object.DescendantAdded:Connect(function(descendant) 102 | local _arg0_2 = descendant:GetPropertyChangedSignal("Name"):Connect(updateTree) 103 | table.insert(connections, _arg0_2) 104 | updateTree() 105 | end) 106 | table.insert(connections, _arg0_1) 107 | end) 108 | promise:finally(function() 109 | for _, connection in ipairs(connections) do 110 | connection:Disconnect() 111 | end 112 | warner:cancel() 113 | end) 114 | return promise 115 | end 116 | return { 117 | validateTree = validateTree, 118 | promiseTree = promiseTree, 119 | } 120 | -------------------------------------------------------------------------------- /validate-tree/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/validate-tree", 3 | "version": "1.1.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@rbxts/validate-tree", 9 | "version": "1.1.1", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@rbxts/compiler-types": "^1.2.3-types.1", 13 | "@rbxts/types": "^1.0.537" 14 | }, 15 | "devDependencies": {} 16 | }, 17 | "node_modules/@rbxts/compiler-types": { 18 | "version": "1.2.3-types.1", 19 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.2.3-types.1.tgz", 20 | "integrity": "sha512-SXXIBazyJ7N6d2xcy471/kqZZpCv7EDOWrRJ45jcv3g00VQaZwYl4Elr10woqRloIblQanwJ7yUqGXAsWv7iuQ==" 21 | }, 22 | "node_modules/@rbxts/types": { 23 | "version": "1.0.537", 24 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.537.tgz", 25 | "integrity": "sha512-9orfH1YFf2ewZe3rxuPhRlPXLshUj4Ewx+F7Kn73i/Iu4C7InclLFSw2YaYz326MsjKycS7CvIu+ZDWoo7kq8Q==" 26 | } 27 | }, 28 | "dependencies": { 29 | "@rbxts/compiler-types": { 30 | "version": "1.2.3-types.1", 31 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-1.2.3-types.1.tgz", 32 | "integrity": "sha512-SXXIBazyJ7N6d2xcy471/kqZZpCv7EDOWrRJ45jcv3g00VQaZwYl4Elr10woqRloIblQanwJ7yUqGXAsWv7iuQ==" 33 | }, 34 | "@rbxts/types": { 35 | "version": "1.0.537", 36 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.537.tgz", 37 | "integrity": "sha512-9orfH1YFf2ewZe3rxuPhRlPXLshUj4Ewx+F7Kn73i/Iu4C7InclLFSw2YaYz326MsjKycS7CvIu+ZDWoo7kq8Q==" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /validate-tree/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/validate-tree", 3 | "version": "2.0.2", 4 | "description": "Validates whether a given instance matches a given instance tree", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "build": "rbxtsc & mv out/* . & rm -r out" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/validate-tree" 13 | }, 14 | "keywords": [ 15 | "dot", 16 | "Roblox", 17 | "Player", 18 | "tree", 19 | "validate", 20 | "object", 21 | "members", 22 | "parts" 23 | ], 24 | "author": "Validark", 25 | "license": "ISC", 26 | "bugs": { 27 | "url": "https://github.com/Validark/Roblox-TS-Libraries/issues" 28 | }, 29 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/validate-tree/README.md", 30 | "publishConfig": { 31 | "access": "public" 32 | }, 33 | "devDependencies": { 34 | "@rbxts/compiler-types": "^1.2.3", 35 | "@rbxts/types": "^1.0.537" 36 | }, 37 | "files": [ 38 | "init.lua", 39 | "index.d.ts", 40 | "README.md" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /validate-tree/src/index.ts: -------------------------------------------------------------------------------- 1 | /** Defines a Rojo-esque tree type which defines an abstract object tree. */ 2 | export interface InstanceTree { 3 | $className?: keyof Instances; 4 | [Key: string]: keyof Instances | undefined | InstanceTree; 5 | } 6 | 7 | type MoreSpecificType = U extends D ? U : D extends U ? D : U & D; 8 | type AllKeys = T extends any ? keyof T : never; 9 | 10 | /** Evaluates a Rojo-esque tree and transforms it into an indexable type. */ 11 | export declare type EvaluateInstanceTree = 12 | (T extends { $className: keyof Instances } ? Instances[T["$className"]] : D) extends infer U 13 | ? MoreSpecificType & { 14 | [K in Exclude>>]: 15 | T[K] extends keyof Instances ? Instances[T[K]] : EvaluateInstanceTree 16 | } 17 | : never; 18 | 19 | function getService(serviceName: string) { 20 | return game.GetService(serviceName as keyof Services); 21 | } 22 | 23 | /** Returns whether a given Instance matches a particular Rojo-esque InstanceTree. 24 | * @param object The object which needs validation 25 | * @param tree The tree to validate 26 | * @param violators 27 | */ 28 | export function validateTree( 29 | object: I, 30 | tree: T, 31 | violators?: Array, 32 | ): object is EvaluateInstanceTree { 33 | if ("$className" in tree && !object.IsA(tree.$className!)) return false; 34 | let matches = true; 35 | 36 | if (classIs(object, "DataModel")) { 37 | for (const [serviceName, classOrTree] of (tree as unknown as Map)) { 38 | if (serviceName !== "$className") { 39 | const [success, value] = pcall(getService, serviceName); 40 | 41 | if (!success) { 42 | if (violators !== undefined) violators.push(`game.GetService("${serviceName}")`); 43 | return false; 44 | } 45 | 46 | if (value && (typeIs(classOrTree, "string") || validateTree(value, classOrTree, violators))) { 47 | if (value.Name !== serviceName) value.Name = serviceName; 48 | } else { 49 | if (violators === undefined) return false; 50 | matches = false; 51 | violators.push(`game.GetService("${serviceName}")`); 52 | } 53 | } 54 | } 55 | } else { 56 | const whitelistedKeys = new Set(["$className"]); 57 | 58 | for (const child of object.GetChildren()) { 59 | const childName = child.Name; 60 | if (childName !== "$className") { 61 | const classOrTree = tree[childName]; 62 | 63 | if (typeIs(classOrTree, "string") ? child.IsA(classOrTree) : classOrTree && validateTree(child, classOrTree, violators)) 64 | whitelistedKeys.add(childName); 65 | } 66 | } 67 | 68 | for (const [key] of (tree as unknown as Map)) { 69 | if (!whitelistedKeys.has(key)) { 70 | if (violators === undefined) return false; 71 | matches = false; 72 | violators.push(object.GetFullName() + "." + key); 73 | } 74 | } 75 | } 76 | 77 | return matches; 78 | } 79 | 80 | /** Promises a given tree of objects exists within an object. 81 | * @param tree Must be an object tree similar to ones considered valid by Rojo. 82 | * Every tree must have a `$className` member, and can have any number of keys which represent 83 | * the name of a child instance, which should have a corresponding value which is this same kind of tree. 84 | * There is also a shorthand syntax available, where setting a key equal to a className is equivalent 85 | * to an object with `$className` defined. Hence `Things: "Folder"` is equivalent to `Things: { $className: "Folder" }` 86 | */ 87 | export function promiseTree( 88 | object: I, 89 | tree: T, 90 | ): Promise> { 91 | if (validateTree(object, tree)) { 92 | return Promise.resolve(object as I & EvaluateInstanceTree); 93 | } 94 | 95 | const connections = new Array() 96 | const warner = Promise.delay(5) 97 | 98 | warner.then(() => { 99 | const violators = new Array() 100 | if (!validateTree(object, tree, violators)) 101 | warn(`[promiseTree] Infinite wait possible. Waiting for: ${violators.join(", ")}`) 102 | }) 103 | 104 | const promise = new Promise>((resolve) => { 105 | function updateTree(violators?: Array) { 106 | if (validateTree(object, tree, violators)) 107 | resolve(object as I & EvaluateInstanceTree) 108 | } 109 | 110 | for (const d of object.GetDescendants()) 111 | connections.push(d.GetPropertyChangedSignal("Name").Connect(updateTree)) 112 | 113 | connections.push( 114 | object.DescendantAdded.Connect(descendant => { 115 | connections.push(descendant.GetPropertyChangedSignal("Name").Connect(updateTree)) 116 | updateTree() 117 | }), 118 | ) 119 | }) 120 | 121 | promise.finally(() => { 122 | for (const connection of connections) connection.Disconnect() 123 | warner.cancel() 124 | }) 125 | 126 | return promise 127 | } 128 | -------------------------------------------------------------------------------- /validate-tree/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node", 4 | // required 5 | "allowSyntheticDefaultImports": true, 6 | "downlevelIteration": true, 7 | "module": "commonjs", 8 | "noLib": true, 9 | "strict": true, 10 | "target": "es6", 11 | "typeRoots": [ 12 | "node_modules/@rbxts" 13 | ], 14 | // required, configurable 15 | "rootDir": "src", 16 | "outDir": "out", 17 | // optional 18 | "baseUrl": "src", 19 | "declaration": true, 20 | // optional, non-configurable 21 | "jsx": "react", 22 | "jsxFactory": "Roact.createElement" 23 | }, 24 | "typeAcquisition": { 25 | "enable": true 26 | }, 27 | "include": [ 28 | "." 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /weighted-random-picker/README.md: -------------------------------------------------------------------------------- 1 | # Weighted Random Picker 2 | 3 | Generates option-picker functions from relative probabilities: 4 | 5 | ```ts 6 | // When relative probabilities are not provided, it gives equal weight to each option 7 | const CoinFlip = new RandomPicker(["Heads", "Tails"]) 8 | 9 | print(CoinFlip()) // "Heads" 10 | print(CoinFlip()) // "Tails" 11 | 12 | const DiceRoll = new RandomPicker([1, 2, 3, 4, 5, 6]) 13 | 14 | const CoinFlipOrDiceRoll = new RandomPicker( 15 | // calls CoinFlip over DiceRoll at a 3:1 ratio 16 | // in other words, CoinFlip gets set to 0.75, DiceRoll becomes 0.25 17 | [CoinFlip, DiceRoll], 18 | [ 3, 1] 19 | ) 20 | 21 | CoinFlipOrDiceRoll()() // 1 22 | CoinFlipOrDiceRoll()() // Heads 23 | CoinFlipOrDiceRoll()() // 5 24 | ``` 25 | -------------------------------------------------------------------------------- /weighted-random-picker/index.d.ts: -------------------------------------------------------------------------------- 1 | declare type RandomPicker = () => T; 2 | 3 | /** 4 | * Creates a function which returns a random element from options when called. 5 | * @param options The elements which can be selected by the function 6 | * @param relativeWeights An array which corresponds to each element in options, with each element being the relative probability of each corresponding option. 7 | */ 8 | declare const RandomPicker: new (options: Array, relativeWeights?: Array) => RandomPicker; 9 | 10 | export = RandomPicker; 11 | -------------------------------------------------------------------------------- /weighted-random-picker/init.lua: -------------------------------------------------------------------------------- 1 | -- Generates option-picker functions from relative probabilities 2 | -- @author Validark 3 | 4 | math.randomseed(tick()) 5 | 6 | return { 7 | new = function(MyOptions, MyRelativeWeights) 8 | local Options = {} 9 | local RelativeWeights = {} 10 | local n = #MyOptions 11 | 12 | if n == 0 then 13 | error("[WeightedProbabilityFunction] must supply at least one option", 2) 14 | end 15 | 16 | if MyRelativeWeights then 17 | if n ~= #MyRelativeWeights then 18 | error("[WeightedProbabilityFunction] options and relativeWeights arrays must be of the same length", 2) 19 | end 20 | 21 | local TotalWeight = 0 22 | 23 | for i = 1, n do 24 | local Weight = MyRelativeWeights[i] 25 | if Weight < 0 then 26 | error("[WeightedProbabilityFunction] relativeWeights cannot be negative", 2) 27 | end 28 | TotalWeight = TotalWeight + Weight 29 | Options[i] = MyOptions[i] 30 | end 31 | 32 | if TotalWeight <= 0 then 33 | error("[WeightedProbabilityFunction] Please give a relativeWeight greater than 0", 2) 34 | end 35 | 36 | for i = 1, n do 37 | RelativeWeights[i] = MyRelativeWeights[i] / TotalWeight 38 | end 39 | else 40 | local Weight = 1 / n 41 | for i = 1, n do 42 | Options[i] = MyOptions[i] 43 | RelativeWeights[i] = Weight 44 | end 45 | end 46 | 47 | return function() 48 | local Picked = math.random() 49 | 50 | for i = 1, n do 51 | Picked = Picked - RelativeWeights[i] 52 | if Picked < 0 then 53 | return Options[i] 54 | end 55 | end 56 | 57 | return Options[n] 58 | end 59 | end; 60 | } 61 | -------------------------------------------------------------------------------- /weighted-random-picker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/weighted-random-picker", 3 | "version": "1.0.1", 4 | "description": "Generates functions which pick a random element of an array based upon their assigned relative probabilities.", 5 | "main": "init.lua", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/Validark/Roblox-TS-Libraries/tree/master/weighted-random-picker" 13 | }, 14 | "keywords": [ 15 | "Roblox-TypeScript", 16 | "Roblox", 17 | "probability", 18 | "pick", 19 | "choose", 20 | "random", 21 | "decide" 22 | ], 23 | "author": "Validark", 24 | "license": "ISC", 25 | "bugs": { 26 | "url": "https://github.com/Validark/Roblox-TS-libraries/issues" 27 | }, 28 | "homepage": "https://github.com/Validark/Roblox-TS-Libraries/blob/master/rbx-weighted-random-picker/README.md", 29 | "publishConfig": { 30 | "access": "public" 31 | }, 32 | "files": [ 33 | "init.lua", 34 | "index.d.ts", 35 | "README.md" 36 | ] 37 | } 38 | --------------------------------------------------------------------------------