commit fcf92829e86c0fef56051a43462f9191efdb400e
Author: VirtuBrick <139835327+VirtuBrick@users.noreply.github.com>
Date: Thu Feb 22 11:59:13 2024 -0500
Initial commit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5c3dafe
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+config.py
+__pycache__/
+assets/
+proxies.txt
+*.pem
+logs/*.log*
+download_cache/*
+*.key
+*.crt
+!download_cache/KEEPME
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f56b37b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,100 @@
+# Syntax Backend
+**Last Updated: 20/2/2024**
+
+## What you need
+### Requirements
+ - Linux Server ( For Production environment )
+ - PostgreSQL Server
+ - Redis Server
+ - NGINX
+ - Cloudflare Account
+ - Domain with Cloudflare protection
+ - Python 3.12+
+ - FFmpeg
+ - Gunicorn
+
+> Note: These are the bare minimum needed for Syntax Backend to run, please do not attempt to host a publicly accessible version of Syntax if you do not know what you are doing
+
+### Optional Services
+ - Syntax Gameserver running on Windows Server ( Needed for rendering and games )
+ - Syntax Discord Bot
+ - Ko-Fi ( Please modify code if you are not going to use this )
+ - Cryptomus
+ - MailJet ( Email Vericiation, modification to the code is needed as email templates are not included )
+ - HTTP Proxies for faster asset migration ( [webshare.io](https://webshare.io/) is recommended )
+ - Amazon S3 Bucket ( **USE_LOCAL_STORAGE** must be enabled if you are not planning to use a S3 Bucket )
+
+## Configuration
+Copy `config.example.py` and name it as `config.py` then place it in the same directory as this readme file
+
+1. **FLASK_SESSION_KEY** - Used for salting passwords and 2FA Secret Generation, please change to a random long string and never change it ever again!
+~~2. **AuthorizationKey** - Added for debugging and bypassing ratelimits, please also change to a random long string~~ Removed from codebase
+3. **SQLALCHEMY_DATABASE_URI** - URI for connecting to the postgres database, refer to [Documentation](https://flask-sqlalchemy.palletsprojects.com/en/2.x/config/) for creating a database URI
+4. **FLASK_LIMITED_STORAGE_URI** - Redis Server URI, can leave as default if your redis server is hosted locally and does not require authorization
+5. **BaseDomain** - Change to your domain *(eg. roblox.com)*, please do not host on a subdomain as it is not supported!
+6. **CloudflareTurnstileSiteKey** - Please setup turnstile on the domain you are hosting and then grab the turnstile site key from there
+7. **CloudflareTurnstileSecretKey** - Read above
+8. **DISCORD_CLIENT_ID** - Go to the Discord Developer Portal and go to the Discord Application you are going to use, then place its ClientID here
+9. **DiscordBotToken** - Use your Discord Bot Token
+10. **DISCORD_CLIENT_SECRET** - Discord Application Client Secret
+11. **DISCORD_BOT_AUTHTOKEN** - Authorization Token for Syntax Discord Bot, use random long string
+12. **DISCORD_BOT_AUTHORISED_IPS** - List of IPs which are allowed to access Discord Bot internal APIs
+13. **DISCORD_ADMIN_LOGS_WEBHOOK** - Discord Webhook for logging Moderation Actions
+14. **MAILJET** - You will have to modify the code and the config for this as your email template will be different
+15. **KOFI_VERIFICATION_TOKEN** - Used for verifying requests from Ko-Fi to automate donations processing, please change to a random long string if you do not plan on using this. If you do you can find the verification token in your Ko-Fi API Panel.
+16. **VERIFIED_EMAIL_REWARD_ASSET** - The AssetId the user is rewarded with once they verify their email, you can change this after setting up everything
+17. **ASSETMIGRATOR_ROBLOSECURITY** - Used for private audio migration
+18. **ASSETMIGRATOR_USE_PROXIES** - If you want to use proxies for Asset Migration ( Which you should as it speeds up everything )
+19. **ASSETMIGRATOR_PROXY_LIST_LOCATION** - The path to the file which contains the proxies
+20. **RSA_PRIVATE_KEY_PATH** - The path to the private key, expects a 1024 Bit RSA private key used for signing JoinScripts and everyting else **This is required!!**
+21. **RSA_PRIVATE_KEY_PATH2** - Same thing for above but expects a 2048 Bit RSA private key
+22. **USE_LOCAL_STORAGE** - Uses local storage for storing and reading files, bypasses S3 and uses **AWS_S3_DOWNLOAD_CACHE_DIR** as its storage directory ( SHOULD ONLY BE USED IN A DEVELOPMENT ENVIRONMENT )
+23. **AWS_ACCESS_KEY** - Your AWS Access Key, please create one in your AWS IAM Manager
+24. **AWS_SECRET_KEY** - The Secret Key for the access key
+25. **AWS_S3_BUCKET_NAME** - The bucket name assets and images will be uploaded to
+26. **AWS_S3_DOWNLOAD_CACHE_DIR** - Where files downloaded from S3 will be cached
+27. **AWS_REGION_NAME** - The region of the bucket
+28. **CDN_URL** - Change to where the CDN is
+29. **DISCOURSE_SSO_ENABLED** - Allows authentication with Syntax for [Discourse](https://www.discourse.org/) Forums
+30. **DISCOURSE_FORUM_BASEURL** - The location of the forum
+31. **DISCOURSE_SECRET_KEY** - The secret key for signing
+32. **ADMIN_GROUP_ID** - The GroupId where admins are in, used for showing the admin badges ingame
+33. **ITEMRELEASER_DISCORD_WEBHOOK** - The Discord Webhook to use for announcing an item release
+34. **ITEMRELEASER_ITEM_PING_ROLE_ID** - The Discord Role ID to ping for announcing an item release
+35. **PROMETHEUS_ENABLED** - If the Prometheus endpoint is enabled
+36. **PROMETHEUS_ALLOWED_IPS** - IPs which are allowed to query the Prometheus endpoint
+37. **CHEATER_REPORTS_DISCORD_WEBHOOK** - The Discord webhook to use for cheater reports from RCCService
+38. **ROLIMONS_API_ENABLED** - Used for Synmons
+39. **ROLIMONS_API_KEY** - Used for Synmons
+40. **GAMESERVER_COMM_PRIVATE_KEY_LOCATION** - The Private key location used for signing requests sent to gameservers
+41. **CRYPTOMUS_PAYMENT_ENABLED** - If the cryptomus payment system is enabled
+42. **CRYPTOMUS_MERCHANT_ID** - Your Cryptomus merchant ID
+43. **CRYPTOMUS_API_KEY** - Your Cryptoumus API Key
+44. **IPAPI_AUTH_KEY** - API Key for [IPAPI](https://ipapi.co/) used for VPN and proxy detection on signup
+
+## KeyPair Generation
+The SYNTAX Backend requires some keys for it to sign and communicate with gameservers, run the script below to generate those keys.
+```
+python tools/generate_new_keys.py
+```
+
+In the `tools` directory 6 new files should have been created, 2 key pairs are for joinscript signing which is needed by the Client and RCCService to authenticate and verify properly.
+Another keypair is for signing requests to communicate with all gameservers, take the public key and place it in your gameserver directory.
+
+## First Time Setup
+
+First install all required dependencies by running `pip install -r requirements.txt` in this directory
+
+Next run the command `flask shell` in the same directory as this README.md file, then in the shell run `db.create_all()`.
+This will automatically create all the tables needed in your PostgreSQL database.
+
+Next use type in the following command in your flask shell
+```
+from app.shell_commands import create_admin_user
+create_admin_user()
+```
+This will create an admin user with all existing admin privileges
+
+Now we can finally start the website, please make sure you have [gunicorn](https://gunicorn.org/) installed on your Linux Machine, gunicorn does not support Windows Machines. To start run the shell script `./start.sh` which will start a webserver on port `3003`. Please make sure you have NGINX configured as a reverse proxy to proxy the website and also have configured Cloudflare to serve your website on the main and all subdomains.
+
+If you are running a Windows Machine and want to run in debug mode run `flask run --port 3006 --debug`, this will open the website on port 3006
diff --git a/app/__init__.py b/app/__init__.py
new file mode 100644
index 0000000..c675ecc
--- /dev/null
+++ b/app/__init__.py
@@ -0,0 +1,415 @@
+import base64
+import redis
+import sys
+import os
+import hashlib
+import traceback
+import string
+import random
+import re
+import logging
+from config import Config
+from logging.handlers import TimedRotatingFileHandler
+from flask import Flask, jsonify, render_template, session, redirect, url_for, request, make_response, Response
+from datetime import datetime, timedelta
+from urllib.parse import urlparse
+
+from app.models.user import User
+from app.models.messages import Message
+from app.models.user_trades import UserTrade
+from app.models.friend_request import FriendRequest
+from app.models.asset import Asset
+from app.models.asset_version import AssetVersion
+from app.models.game_session_log import GameSessionLog
+from app.enums.TradeStatus import TradeStatus
+from app.enums.AssetType import AssetType
+from app.enums.TransactionType import TransactionType
+from app.util import auth, assetversion, s3helper, signscript, transactions
+from app.services.economy import IncrementTargetBalance, GetUserBalance
+from app.extensions import db, limiter, scheduler, CORS, redis_controller, csrf, get_remote_address
+import app.shell_commands as cmd
+
+logging.basicConfig(
+ level = logging.INFO,
+ format = "%(asctime)s [%(levelname)s] %(message)s"
+)
+logger = logging.getLogger(__name__)
+logname = "./logs/syntaxweb.log"
+handler = TimedRotatingFileHandler(logname, when="midnight", backupCount=30)
+handler.suffix = "%Y%m%d"
+
+logging.getLogger().addHandler(handler)
+
+TwelveClientAssets = [37801173, 46295864, 48488236, 53870848, 53870858, 60595696, 89449009, 89449094, 97188757]
+def create_app(config_class=Config):
+ app = Flask(__name__, template_folder="pages")
+ app.config.from_object(config_class)
+ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
+ app.config["SECRET_KEY"] = config_class.FLASK_SESSION_KEY
+ app.config['CORS_HEADERS'] = 'Content-Type'
+ app.config['SESSION_TYPE'] = 'redis'
+ app.config['SESSION_REDIS'] = redis.from_url(Config.FLASK_LIMITED_STORAGE_URI)
+ app.config['MAX_CONTENT_LENGTH'] = 32 * 1024 * 1024
+ app.config["SQUEEZE_MIN_SIZE"] = 0
+ #if app.debug is False:
+ #app.config["SERVER_NAME"] = config_class.BaseDomain
+
+ db.init_app(app)
+ limiter.init_app(app)
+ csrf.init_app(app)
+ clean_domain = config_class.BaseDomain.replace('.', r'\.')
+ CORS.init_app(app, supports_credentials=True, resources={r"/*": {"origins": [f"https://{clean_domain}", f"http://{clean_domain}", f"https://.+{clean_domain}", f"http://.+{clean_domain}"]}})
+ scheduler.init_app(app)
+ scheduler.start()
+
+ from app.pages.login.login import login
+ from app.pages.signup.signup import signup
+ from app.pages.static import static
+ from app.pages.settings.settings import settings
+ from app.pages.home.home import home
+ from app.pages.admin.admin import AdminRoute, GetAmountOfPendingAssets, IsUserAnAdministrator
+ from app.routes.asset import AssetRoute
+ from app.routes.authentication import AuthenticationRoute
+ from app.routes.jobreporthandler import JobReportHandler
+ from app.routes.clientinfo import ClientInfo
+ from app.routes.thumbnailer import Thumbnailer
+ from app.routes.image import ImageRoute
+ from app.routes.fflagssettings import FFlagRoute
+ from app.pages.profiles.profile import Profile
+ from app.routes.gamejoin import GameJoinRoute
+ from app.routes.marketplace import MarketPlaceRoute, EconomyV1Route
+ from app.routes.presence import PresenceRoute
+ from app.pages.messages.messages import MessageRoute
+ #from app.pages.clothingmigrator.migrator import ClothingMigratorRoute
+ from app.pages.catalog.catalog import CatalogRoute
+ from app.pages.avatar.avatar import AvatarRoute
+ from app.routes.pointsservice import PointsServiceRoute
+ from app.routes.datastoreservice import DataStoreRoute
+ from app.pages.clientpages.clientpages import ClientPages
+ from app.routes.luawebservice import LuaWebServiceRoute
+ from app.pages.develop.develop import DevelopPagesRoute
+ from app.routes.bootstrapper import BootstrapperRoute
+ from app.pages.studio.studiopages import StudioPagesRoute
+ from app.pages.games.games import GamePagesRoute
+ from app.pages.membership.membership import MembershipPages
+ from app.routes.rate import AssetRateRoute
+ from app.pages.trades.trades import TradesPageRoute
+ from app.routes.sets import SetsRoute
+ from app.pages.notapproved.notapproved import NotApprovedRoute
+ from app.pages.groups.groupspage import groups_page
+ from app.pages.giftcardredeem.redeem import GiftcardRedeemRoute
+ from app.pages.currencyexchange.controller import CurrencyExchangeRoute
+ from app.routes.kofihandler import KofiHandlerRoute
+ #from app.pages.invitekeys.handler import inviteKeyRoute
+ from app.routes.discord_internal import DiscordInternal
+ from app.routes.publicapi import PublicAPIRoute
+ from app.pages.catalog.catalog import LibraryRoute
+ from app.routes.discourse_sso import discourse_sso
+ from app.pages.transactions.transactions import TransactionsRoute
+ from app.routes.gametransactions import GameTransactionsRoute
+ from app.pages.audiomigrator.audiomigrator import AudioMigratorRoute
+ from app.routes.rbxapi import RBXAPIRoute
+ from app.routes.legacydatapersistence import LegacyDataPersistenceRoute
+ from app.routes.friendapi import FriendsAPIRoute
+ from app.routes.inventoryapi import InventoryAPI
+ from app.routes.usersapi import UsersAPI
+ from app.routes.mobile import MobileAPIRoute
+ from app.routes.gamesapi import GamesAPIRoute
+ from app.routes.accountsettingsapi import AccountSettingsAPIRoute
+ from app.routes.presenceapi import PresenceAPIRoute
+ from app.routes.avatarapi import AvatarAPIRoute
+ from app.routes.badgesapi import BadgesAPIRoute
+ from app.pages.catalog.catalog import BadgesPageRoute
+ from app.pages.users.users_page import users_page
+ from app.routes.teleportservice import TeleportServiceRoute
+ from app.routes.prometheus import PrometheusRoute
+ from app.routes.rolimons import RolimonsAPI
+ from app.routes.cryptomus_handler import CryptomusHandler
+ app.register_blueprint(login, url_prefix="/")
+ app.register_blueprint(signup, url_prefix="/")
+ app.register_blueprint(static, url_prefix="/")
+ app.register_blueprint(settings, url_prefix="/")
+ app.register_blueprint(home, url_prefix="/")
+ app.register_blueprint(AssetRoute, url_prefix="/")
+ app.register_blueprint(AuthenticationRoute, url_prefix="/")
+ app.register_blueprint(AdminRoute, url_prefix="/admin")
+ app.register_blueprint(JobReportHandler, url_prefix="/")
+ app.register_blueprint(ClientInfo, url_prefix="/")
+ app.register_blueprint(Thumbnailer, url_prefix="/internal")
+ app.register_blueprint(ImageRoute, url_prefix="/")
+ app.register_blueprint(FFlagRoute, url_prefix="/")
+ app.register_blueprint(Profile, url_prefix="/")
+ app.register_blueprint(GameJoinRoute, url_prefix="/")
+ app.register_blueprint(MarketPlaceRoute, url_prefix="/marketplace")
+ app.register_blueprint(EconomyV1Route, url_prefix="/")
+ app.register_blueprint(PresenceRoute, url_prefix="/presence")
+ app.register_blueprint(MessageRoute, url_prefix="/messages")
+ #app.register_blueprint(ClothingMigratorRoute, url_prefix="/")
+ app.register_blueprint(CatalogRoute, url_prefix="/catalog")
+ app.register_blueprint(AvatarRoute, url_prefix="/")
+ app.register_blueprint(PointsServiceRoute, url_prefix="/")
+ app.register_blueprint(DataStoreRoute, url_prefix="/")
+ app.register_blueprint(ClientPages, url_prefix="/")
+ app.register_blueprint(LuaWebServiceRoute, url_prefix="/")
+ app.register_blueprint(DevelopPagesRoute, url_prefix="/")
+ app.register_blueprint(BootstrapperRoute, url_prefix="/")
+ app.register_blueprint(StudioPagesRoute, url_prefix="/")
+ app.register_blueprint(GamePagesRoute, url_prefix="/")
+ app.register_blueprint(MembershipPages, url_prefix="/")
+ app.register_blueprint(AssetRateRoute, url_prefix="/")
+ app.register_blueprint(TradesPageRoute, url_prefix="/")
+ app.register_blueprint(SetsRoute, url_prefix="/")
+ app.register_blueprint(NotApprovedRoute, url_prefix="/")
+ app.register_blueprint(groups_page, url_prefix="/")
+ app.register_blueprint(GiftcardRedeemRoute, url_prefix="/")
+ app.register_blueprint(CurrencyExchangeRoute, url_prefix="/currency-exchange")
+ app.register_blueprint(KofiHandlerRoute, url_prefix="/")
+ #app.register_blueprint(inviteKeyRoute, url_prefix="/")
+ app.register_blueprint(DiscordInternal, url_prefix="/internal/discord_bot")
+ app.register_blueprint(PublicAPIRoute, url_prefix="/public-api")
+ app.register_blueprint(LibraryRoute, url_prefix="/library")
+ app.register_blueprint(discourse_sso, url_prefix="/discourse")
+ app.register_blueprint(TransactionsRoute, url_prefix="/transactions")
+ app.register_blueprint(GameTransactionsRoute, url_prefix="/")
+ app.register_blueprint(AudioMigratorRoute, url_prefix="/")
+ app.register_blueprint(RBXAPIRoute, url_prefix="/")
+ app.register_blueprint(LegacyDataPersistenceRoute, url_prefix="/persistence/legacy")
+ app.register_blueprint(FriendsAPIRoute, url_prefix="/")
+ app.register_blueprint(InventoryAPI, url_prefix="/")
+ app.register_blueprint(UsersAPI, url_prefix="/")
+ app.register_blueprint(MobileAPIRoute, url_prefix="/")
+ app.register_blueprint(GamesAPIRoute, url_prefix="/")
+ app.register_blueprint(AccountSettingsAPIRoute, url_prefix="/")
+ app.register_blueprint(PresenceAPIRoute, url_prefix="/")
+ app.register_blueprint(AvatarAPIRoute, url_prefix="/")
+ app.register_blueprint(BadgesAPIRoute, url_prefix="/")
+ app.register_blueprint(BadgesPageRoute, url_prefix="/badges")
+ app.register_blueprint(users_page, url_prefix="/")
+ app.register_blueprint(TeleportServiceRoute, url_prefix="/reservedservers")
+ app.register_blueprint(PrometheusRoute, url_prefix="/")
+ app.register_blueprint(RolimonsAPI, url_prefix="/api/internal_rolimons")
+ app.register_blueprint(CryptomusHandler, url_prefix="/cryptomus_service")
+
+ def ConvertDatetimeToDayMonthYear(date):
+ return date.strftime("%d/%m/%Y")
+
+ app.jinja_env.globals.update(round=round, b64decode=base64.b64decode, len=len, ConvertDatetimeToDayMonthYear=ConvertDatetimeToDayMonthYear, datetime_utcnow = datetime.utcnow)
+
+ @app.before_request
+ def before_request():
+ BrowserUserAgent = request.headers.get('User-Agent', default="Unknown")
+ if "Roblox" not in BrowserUserAgent:
+ if request.method == "GET":
+ CloudFlareScheme = request.headers.get('CF-Visitor')
+ if CloudFlareScheme is not None:
+ if "https" not in CloudFlareScheme:
+ return redirect(request.url.replace("http://", "https://", 1), code=301)
+ elif BrowserUserAgent == "Roblox/WinInet":
+ requestReferer = request.headers.get( key = "Referer", default = None )
+ if requestReferer is not None:
+ try:
+ if urlparse( requestReferer ).hostname[ -len( config_class.BaseDomain ): ] != config_class.BaseDomain:
+ logging.warn(f"Bad Referer - ref : {requestReferer} - target : {request.url}")
+ return "Bad Referer", 403
+ except:
+ pass
+
+ @app.after_request
+ def after_request( response : Response ):
+ if get_remote_address() in config_class.DEBUG_IPS:
+ logging.info(f"Debug - {response.status_code} - {request.host} - {request.path} - {request.args}")
+ if hasattr(response, 'direct_passthrough') and not response.direct_passthrough:
+ UserObj : User = auth.GetCurrentUser()
+ if UserObj is not None:
+ if UserObj.accountstatus != 1:
+ auth.invalidateToken(request.cookies.get(".ROBLOSECURITY"))
+ session["not-approved-viewer"] = UserObj.id
+ resp = make_response(redirect("/not-approved"))
+ resp.set_cookie(".ROBLOSECURITY", "", expires=0)
+ return resp
+ if request.cookies.get(key="t", default=None, type=str) is None:
+ NewToken = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(128))
+ response.set_cookie("t", NewToken, expires=datetime.utcnow() + timedelta(days=365), domain=f".{config_class.BaseDomain}")
+ if "not-approved-viewer" in session:
+ UserObj : User = auth.GetCurrentUser()
+ if UserObj is not None:
+ session.pop("not-approved-viewer")
+ return response
+
+ @app.context_processor
+ def inject_user():
+ if ".ROBLOSECURITY" in request.cookies:
+ AuthenticatedUser : User = auth.GetCurrentUser()
+ if AuthenticatedUser is None:
+ return {}
+
+ def award_daily_login_bonus():
+ if redis_controller.get(f"daily_login_bonus:{str(AuthenticatedUser.id)}") is not None:
+ return
+ if AuthenticatedUser.created > datetime.utcnow() - timedelta( days = 1 ):
+ return
+ if GameSessionLog.query.filter_by(user_id=AuthenticatedUser.id).filter( GameSessionLog.joined_at > datetime.utcnow() - timedelta( days = 3 ) ).first() is None:
+ return
+
+ redis_controller.setex(f"daily_login_bonus:{str(AuthenticatedUser.id)}", 60 * 60 * 24 ,"1")
+ IncrementTargetBalance(AuthenticatedUser, 10, 1)
+ transactions.CreateTransaction(
+ Reciever = AuthenticatedUser,
+ Sender = User.query.filter_by(id=1).first(),
+ CurrencyAmount = 10,
+ CurrencyType = 1,
+ TransactionType = TransactionType.BuildersClubStipend,
+ CustomText = "Daily Login Bonus"
+ )
+
+ if redis_controller.exists(f"award_daily_login_bonus_attempt:{str(AuthenticatedUser.id)}") is None:
+ redis_controller.setex(f"award_daily_login_bonus_attempt:{str(AuthenticatedUser.id)}", 60, "1")
+ award_daily_login_bonus()
+
+ unreadMessages = Message.query.filter_by(recipient_id=AuthenticatedUser.id, read=False).count()
+ inboundTrades = UserTrade.query.filter_by(recipient_userid=AuthenticatedUser.id, status=TradeStatus.Pending).count()
+ friendRequests = FriendRequest.query.filter_by(requestee_id=AuthenticatedUser.id).count()
+ AuthenticatedUser.lastonline = datetime.utcnow()
+ db.session.commit()
+ userRobux, userTix = GetUserBalance(AuthenticatedUser)
+ isAdministrator = IsUserAnAdministrator( AuthenticatedUser )
+ PendingAssetsCount = 0
+ if isAdministrator:
+ PendingAssetsCount = GetAmountOfPendingAssets()
+
+ return {
+ "currentuser": {
+ "id": AuthenticatedUser.id,
+ "username": AuthenticatedUser.username,
+ "robux": userRobux,
+ "tix": userTix,
+ "unread_messages": unreadMessages,
+ "inbound_trades": inboundTrades,
+ "friend_requests": friendRequests,
+ "is_admin": isAdministrator,
+ "pending_asset_count": PendingAssetsCount
+ },
+ }
+ return {}
+
+ @app.context_processor
+ def inject_website_wide_message():
+ if redis_controller.exists("website_wide_message"):
+ url_pattern = re.compile(r'(https?://\S+)')
+ website_message = website_wide_message=redis_controller.get("website_wide_message")
+ website_message = url_pattern.sub(r'\1', website_message)
+ return dict(website_wide_message=website_message)
+ return {}
+
+ @app.context_processor
+ def injecthcaptcha_sitekey():
+ return dict(turnstilekey=Config.CloudflareTurnstileSiteKey)
+
+ @app.route('/')
+ def main():
+ if "user" in session:
+ return redirect("/home")
+ else:
+ return redirect("/login")
+
+ @app.errorhandler(404)
+ def page_not_found(e):
+ BrowserUserAgent = request.headers.get('User-Agent')
+ if BrowserUserAgent is not None:
+ if "RobloxStudio" in BrowserUserAgent:
+ return "
404 - Page not found
Return to homepage"
+ #logging.error(f"404 - {request.path}")
+ return render_template("404.html"), 404
+
+ @app.errorhandler(403)
+ def page_forbidden(e):
+ BrowserUserAgent = request.headers.get('User-Agent')
+ if BrowserUserAgent is not None:
+ if "RobloxStudio" in BrowserUserAgent:
+ return "
403 - Forbidden
Return to homepage"
+ return render_template("403.html"), 403
+
+ @app.errorhandler(405)
+ def page_forbidden(e):
+ BrowserUserAgent = request.headers.get('User-Agent')
+ if BrowserUserAgent is not None:
+ if "RobloxStudio" in BrowserUserAgent:
+ return "
405 - Method Not Allowed
Return to homepage"
+ return render_template("405.html"), 405
+
+ @app.errorhandler(500)
+ def page_internal_server_error(e):
+ exc_type, exc_value, exc_traceback = sys.exc_info()
+ PageRoute = request.path
+ return render_template("500.html", error={
+ "type": exc_type,
+ "value": exc_value,
+ "traceback": str(traceback.format_exc())
+ }, page=PageRoute), 500
+
+
+ @app.errorhandler(429)
+ def ratelimit_handler(e):
+ return jsonify({"error": "You are being rate limited.", "message": "You are being rate limited.", "success": False}), 429
+
+ if config_class.ASSETMIGRATOR_USE_PROXIES:
+ redis_controller.delete("assetmigrator_proxies")
+ with open(config_class.ASSETMIGRATOR_PROXY_LIST_LOCATION, "r") as f:
+ LoadedProxies = 0
+ for line in f:
+ if line.strip() != "":
+ LoadedProxies += 1
+ redis_controller.sadd("assetmigrator_proxies", line.strip())
+ logging.info(f"Loaded {LoadedProxies} proxies")
+
+ try:
+ if not redis_controller.exists("coregui_ids_cooldown"):
+ with app.app_context():
+ redis_controller.setex("coregui_ids_cooldown", 60 * 60, "1")
+ AllCoreGui = os.listdir("./app/files/CoreGui")
+ redis_controller.delete("coregui_ids")
+ for CoreGui in AllCoreGui:
+ try:
+ redis_controller.sadd("coregui_ids", int(CoreGui))
+ except:
+ logging.error(f"Failed to load CoreGui file {CoreGui}")
+
+ AssetObj : Asset = Asset.query.filter_by(id=int(CoreGui)).first()
+ if AssetObj is None:
+ AssetObj = Asset(
+ name = "CoreGui",
+ created_at = datetime.utcnow(),
+ updated_at = datetime.utcnow(),
+ asset_type = AssetType.Lua,
+ creator_id = 1,
+ creator_type = 0,
+ moderation_status = 0
+ )
+ AssetObj.id = int(CoreGui)
+ db.session.add(AssetObj)
+ db.session.commit()
+ logging.info(f"Created CoreGui asset {CoreGui}")
+ CoreGuiContent = open(f"./app/files/CoreGui/{CoreGui}", "r").read()
+ if int(CoreGui) in TwelveClientAssets:
+ CoreGuiContent = signscript.signUTF8(f"%{CoreGui}%\r\n{CoreGuiContent}", addNewLine=False, twelveclient=True)
+ else:
+ CoreGuiContent = signscript.signUTF8(f"--rbxassetid%{CoreGui}%\r\n{CoreGuiContent}", addNewLine=False)
+
+ CoreGuiHash = hashlib.sha512(CoreGuiContent.encode("utf-8")).hexdigest()
+ AssetVersionObj : AssetVersion = assetversion.GetLatestAssetVersion( AssetObj )
+ if AssetVersionObj is None or AssetVersionObj.content_hash != CoreGuiHash:
+ s3helper.UploadBytesToS3(
+ CoreGuiContent.encode("utf-8"),
+ CoreGuiHash
+ )
+ assetversion.CreateNewAssetVersion(
+ AssetObj,
+ CoreGuiHash,
+ CoreGuiContent,
+ )
+ logging.info(f"Loaded {len(AllCoreGui)} CoreGui files")
+ except Exception as e:
+ logging.error(f"Failed to load CoreGui files: {e}")
+
+ logging.info("App created")
+ return app
\ No newline at end of file
diff --git a/app/enums/AssetType.py b/app/enums/AssetType.py
new file mode 100644
index 0000000..9f2c8fc
--- /dev/null
+++ b/app/enums/AssetType.py
@@ -0,0 +1,70 @@
+from enum import Enum
+
+class AssetType( Enum ):
+ Image = 1
+ TShirt = 2
+ Audio = 3
+ Mesh = 4
+ Lua = 5
+ HTML = 6
+ Text = 7
+ Hat = 8
+ Place = 9
+ Model = 10
+ Shirt = 11
+ Pants = 12
+ Decal = 13
+ Avatar = 16
+ Head = 17
+ Face = 18
+ Gear = 19
+ Badge = 21
+ GroupEmblem = 22
+ Animation = 24
+ Torso = 27
+ RightArm = 28
+ LeftArm = 29
+ LeftLeg = 30
+ RightLeg = 31
+ Package = 32
+ YoutubeVideo = 33
+ GamePass = 34
+ App = 35
+ Code = 37
+ Plugin = 38
+ SolidModel = 39
+ MeshPart = 40
+ HairAccessory = 41
+ FaceAccessory = 42
+ NeckAccessory = 43
+ ShoulderAccessory = 44
+ FrontAccessory = 45
+ BackAccessory = 46
+ WaistAccessory = 47
+ ClimbAnimation = 48
+ DeathAnimation = 49
+ FallAnimation = 50
+ IdleAnimation = 51
+ JumpAnimation = 52
+ RunAnimation = 53
+ SwimAnimation = 54
+ WalkAnimation = 55
+ PoseAnimation = 56
+ EarAccessory = 57
+ EyeAccessory = 58
+ EmoteAnimation = 61
+ Video = 62
+ TShirtAccessory = 64
+ ShirtAccessory = 65
+ PantsAccessory = 66
+ JacketAccessory = 67
+ SweaterAccessory = 68
+ ShortsAccessory = 69
+ LeftShoeAccessory = 70
+ RightShoeAccessory = 71
+ DressSkirtAccessory = 72
+ FontFamily = 73
+ EyebrowAccessory = 76
+ EyelashAccessory = 77
+ MoodAnimation = 78
+ DynamicHead = 79
\ No newline at end of file
diff --git a/app/enums/BanType.py b/app/enums/BanType.py
new file mode 100644
index 0000000..ea9f94c
--- /dev/null
+++ b/app/enums/BanType.py
@@ -0,0 +1,10 @@
+from enum import Enum
+
+class BanType(Enum):
+ Warning = 0
+ Day1Ban = 1
+ Day3Ban = 2
+ Day7Ban = 3
+ Day14Ban = 4
+ Day30Ban = 5
+ Deleted = 6
\ No newline at end of file
diff --git a/app/enums/ChatStyle.py b/app/enums/ChatStyle.py
new file mode 100644
index 0000000..28f774d
--- /dev/null
+++ b/app/enums/ChatStyle.py
@@ -0,0 +1,6 @@
+from enum import Enum
+
+class ChatStyle(Enum):
+ Classic = 0
+ Bubble = 1
+ ClassicAndBubble = 2
\ No newline at end of file
diff --git a/app/enums/CryptomusPaymentStatus.py b/app/enums/CryptomusPaymentStatus.py
new file mode 100644
index 0000000..b048a3a
--- /dev/null
+++ b/app/enums/CryptomusPaymentStatus.py
@@ -0,0 +1,17 @@
+from enum import Enum
+
+class CryptomusPaymentStatus(Enum):
+ Paid = 0
+ PaidOver = 1
+ WrongAmount = 2
+ Process = 3
+ ConfirmCheck = 4
+ WrongAmountWaiting = 5
+ Check = 6
+ Fail = 7
+ Cancel = 8
+ SystemFail = 9
+ RefundProcess = 10
+ RefundFail = 11
+ RefundPaid = 12
+ Locked = 13
\ No newline at end of file
diff --git a/app/enums/GiftcardType.py b/app/enums/GiftcardType.py
new file mode 100644
index 0000000..0b902ee
--- /dev/null
+++ b/app/enums/GiftcardType.py
@@ -0,0 +1,8 @@
+from enum import Enum
+
+class GiftcardType(Enum):
+ Outrageous_BuildersClub = 0
+ Turbo_BuildersClub = 1
+ RobuxCurrency = 2
+ TixCurrency = 3
+ Item = 4
\ No newline at end of file
diff --git a/app/enums/LimitedItemTransferMethod.py b/app/enums/LimitedItemTransferMethod.py
new file mode 100644
index 0000000..5ef5f5e
--- /dev/null
+++ b/app/enums/LimitedItemTransferMethod.py
@@ -0,0 +1,7 @@
+from enum import Enum
+
+class LimitedItemTransferMethod( Enum ):
+ Purchase = 0
+ Trade = 1
+ WonByLottery = 2
+ ForcedTransfer = 3
\ No newline at end of file
diff --git a/app/enums/MembershipType.py b/app/enums/MembershipType.py
new file mode 100644
index 0000000..32ad6a5
--- /dev/null
+++ b/app/enums/MembershipType.py
@@ -0,0 +1,7 @@
+from enum import Enum
+
+class MembershipType( Enum ):
+ NonBuildersClub = 0
+ BuildersClub = 1
+ TurboBuildersClub = 2
+ OutrageousBuildersClub = 3
\ No newline at end of file
diff --git a/app/enums/PlaceRigChoice.py b/app/enums/PlaceRigChoice.py
new file mode 100644
index 0000000..eb085b3
--- /dev/null
+++ b/app/enums/PlaceRigChoice.py
@@ -0,0 +1,6 @@
+from enum import Enum
+
+class PlaceRigChoice( Enum ):
+ UserChoice = 0
+ ForceR6 = 1
+ ForceR15 = 2
\ No newline at end of file
diff --git a/app/enums/PlaceYear.py b/app/enums/PlaceYear.py
new file mode 100644
index 0000000..2374767
--- /dev/null
+++ b/app/enums/PlaceYear.py
@@ -0,0 +1,17 @@
+from enum import Enum
+
+class PlaceYear( Enum ):
+ Nine = 2009
+ Ten = 2010
+ Eleven = 2011
+ Twelve = 2012
+ Thirteen = 2013
+ Fourteen = 2014
+ Fifteen = 2015
+ Sixteen = 2016
+ Seventeen = 2017
+ Eighteen = 2018
+ Nineteen = 2019
+ Twenty = 2020
+ TwentyOne = 2021
+ TwentyTwo = 2022
diff --git a/app/enums/TradeStatus.py b/app/enums/TradeStatus.py
new file mode 100644
index 0000000..f6be642
--- /dev/null
+++ b/app/enums/TradeStatus.py
@@ -0,0 +1,8 @@
+from enum import Enum
+
+class TradeStatus( Enum ):
+ Pending = 0
+ Accepted = 1
+ Declined = 2
+ Expired = 3
+ Cancelled = 4
\ No newline at end of file
diff --git a/app/enums/TransactionType.py b/app/enums/TransactionType.py
new file mode 100644
index 0000000..9aaa6f9
--- /dev/null
+++ b/app/enums/TransactionType.py
@@ -0,0 +1,9 @@
+from enum import Enum
+
+class TransactionType( Enum ):
+ Purchase = 0
+ Sale = 1
+ GroupPayout = 2
+ BuildersClubStipend = 3
+ Commisions = 4
+ Trade = 5
\ No newline at end of file
diff --git a/app/extensions.py b/app/extensions.py
new file mode 100644
index 0000000..631f6fa
--- /dev/null
+++ b/app/extensions.py
@@ -0,0 +1,708 @@
+from flask_sqlalchemy import SQLAlchemy
+from sqlalchemy import or_, and_, text
+from flask_limiter import Limiter, HEADERS
+from flask_apscheduler import APScheduler
+from flask import request
+from flask_cors import CORS
+import redis
+import datetime
+from config import Config
+import logging
+import time
+from flask_wtf.csrf import CSRFProtect
+from app.enums.AssetType import AssetType
+csrf = CSRFProtect()
+db = SQLAlchemy()
+Config = Config()
+CORS = CORS()
+
+def get_remote_address() -> str:
+ cloudflare = request.headers.get("CF-Connecting-IP")
+ if cloudflare is not None:
+ return cloudflare
+ return request.remote_addr or "127.0.0.1"
+
+def get_user_id() -> str:
+ """
+ Gets the UserId for the current request
+
+ :return: UserId or -1 if not logged in
+ """
+ if ".ROBLOSECURITY" in request.cookies:
+ return "user_id:-1"
+
+ from app.util.auth import GetTokenInfo
+ UserTokenInfo = GetTokenInfo( request.cookies.get(".ROBLOSECURITY", default = "None", type = str) )
+ if UserTokenInfo is None:
+ return "user_id:-1"
+
+ return f"user_id:{ UserTokenInfo[0] }"
+
+limiter = Limiter(
+ get_remote_address,
+ storage_uri=Config.FLASK_LIMITED_STORAGE_URI,
+ strategy="fixed-window-elastic-expiry",
+ headers_enabled=True,
+ key_prefix = "address_limiter"
+)
+
+user_limiter = Limiter(
+ key_func = get_user_id,
+ storage_uri=Config.FLASK_LIMITED_STORAGE_URI,
+ strategy="fixed-window-elastic-expiry",
+ headers_enabled=True,
+ key_prefix = "user_limiter"
+)
+
+redis_controller = Config.REDIS_CLIENT
+
+scheduler = APScheduler()
+logging.getLogger('apscheduler').setLevel(logging.ERROR)
+
+@scheduler.task('interval', id='item_release_pool_releaser', seconds=60, misfire_grace_time=60)
+def item_release_pool_releaser():
+ with scheduler.app.app_context():
+ import time
+ import json
+ import random
+ import uuid
+ import requests
+ from app.models.asset import Asset
+ from app.pages.admin.admin import GetNextItemDropDateTime, SetAssetOffsaleJob
+
+ if redis_controller.get("item_release_pool_releaser") is not None:
+ return
+ redis_controller.set("item_release_pool_releaser", "busy", ex=120)
+ if redis_controller.llen("ItemReleasePool:Items") <= 0:
+ redis_controller.set("ItemReleasePool:LastDropTimestamp", value = str(round(time.time())))
+ return
+
+ if datetime.datetime.utcnow() < GetNextItemDropDateTime():
+ return
+ random.seed( time.time() )
+ RandomIndex = random.randint( 0, redis_controller.llen("ItemReleasePool:Items") - 1 )
+ SelectedAssetId = redis_controller.lindex( "ItemReleasePool:Items", RandomIndex )
+ if SelectedAssetId is None:
+ logging.warning("item_release_pool_releaser failed to selected random asset, lindex returned None")
+ return
+ redis_controller.lrem("ItemReleasePool:Items", count = 0, value = SelectedAssetId)
+ try:
+ SelectedAssetId = int(SelectedAssetId)
+ except Exception as e:
+ logging.warn(f"item_release_pool_releaser failed to cast string to integer, value: {SelectedAssetId}")
+ return
+
+ AssetMetadata = redis_controller.get(f"ItemReleasePool:Item_Metadata:{SelectedAssetId}")
+ if AssetMetadata is None:
+ logging.warn(f"item_release_pool_releaser failed to get asset metadata for {SelectedAssetId}")
+ return
+ try:
+ AssetMetadata = json.loads(AssetMetadata)
+ except Exception as e:
+ logging.warn(f"item_release_pool_releaser failed to parse json from item metadata {SelectedAssetId}, {str(e)}")
+ return
+
+ AssetObj : Asset = Asset.query.filter_by( id = SelectedAssetId ).first()
+ if AssetObj is None:
+ logging.warn(f"item_release_pool_releaser asset id {SelectedAssetId} does not exist")
+ return
+
+ AssetObj.name = AssetMetadata["Name"]
+ AssetObj.description = AssetMetadata["Description"]
+ AssetObj.price_robux = AssetMetadata["RobuxPrice"]
+ AssetObj.price_tix = AssetMetadata["TicketsPrice"]
+ AssetObj.is_limited = AssetMetadata["IsLimited"]
+ AssetObj.is_limited_unique = AssetMetadata["IsLimitedUnique"]
+ AssetObj.serial_count = AssetMetadata["SerialCount"]
+ AssetObj.moderation_status = 0
+ AssetObj.updated_at = datetime.datetime.utcnow()
+ AssetObj.is_for_sale = True
+
+ if AssetMetadata["OffsaleAfter"] is not None:
+ OffsaleAfter = datetime.timedelta( seconds = AssetMetadata["OffsaleAfter"] )
+ AssetOffsaleAt = datetime.datetime.utcnow() + OffsaleAfter
+
+ if redis_controller.exists(f"APSchedulerTaskJobUUID:{str(AssetObj.id)}"):
+ try:
+ scheduler.remove_job(redis_controller.get(f"APSchedulerTaskJobUUID:{str(AssetObj.id)}"))
+ except:
+ logging.warning(f"Failed to remove job {redis_controller.get(f'APSchedulerTaskJobUUID:{str(AssetObj.id)}')}")
+
+ APSchedulerTaskJobUUID = str(uuid.uuid4())
+ scheduler.add_job(id=APSchedulerTaskJobUUID, func=SetAssetOffsaleJob, trigger='date', run_date=AssetOffsaleAt, args=[AssetObj.id])
+ redis_controller.set(f"APSchedulerTaskJobUUID:{str(AssetObj.id)}", APSchedulerTaskJobUUID)
+ logging.info(f"Asset {str(AssetObj.id)} has been set to go offsale at {str(AssetOffsaleAt)}, job UUID: {APSchedulerTaskJobUUID}")
+
+ AssetObj.offsale_at = AssetOffsaleAt
+
+ db.session.commit()
+ logging.info(f"Released Item {AssetObj.id}")
+ redis_controller.set("ItemReleasePool:LastDropTimestamp", value = str(round(time.time())))
+
+ try:
+ requests.post(
+ Config.ITEMRELEASER_DISCORD_WEBHOOK,
+ json = {
+ "content": f"<@&{Config.ITEMRELEASER_ITEM_PING_ROLE_ID}> New Item Drop!",
+ "allowed_mentions": {
+ "replied_user": False,
+ "parse": [],
+ "roles": [
+ Config.ITEMRELEASER_ITEM_PING_ROLE_ID
+ ]
+ },
+ "username": "Automatic Item Release Bot",
+ "avatar_url": f"{Config.BaseURL}/Thumbs/Head.ashx?x=48&y=48&userId=1",
+ "embeds": [{
+ "type": "rich",
+ "title": AssetObj.name,
+ "description": AssetObj.description,
+ "color": 0x00ff62,
+ "fields": [
+ {
+ "name": "Robux Price",
+ "value": f"R${AssetObj.price_robux}",
+ "inline": True
+ },
+ {
+ "name": "Tickets Price",
+ "value": f"T${AssetObj.price_tix}",
+ "inline": True
+ },
+ {
+ "name": "Limited Unique",
+ "value": str(AssetObj.is_limited_unique),
+ "inline": True
+ },
+ {
+ "name": "Serial Count",
+ "value": "None" if AssetObj.serial_count == 0 else str(AssetObj.serial_count),
+ "inline": True
+ },
+ {
+ "name": "Offsale in",
+ "value": "Never" if AssetObj.offsale_at is None else f"",
+ "inline": True
+ }
+ ],
+ "thumbnail": {
+ "url": f"{Config.BaseURL}/Thumbs/Asset.ashx?x=180&y=180&assetId={AssetObj.id}",
+ "height": 120,
+ "width": 120
+ },
+ "url": f"{Config.BaseURL}/catalog/{AssetObj.id}/--",
+ "footer": {
+ "text": f"Syntax Item Release Bot"
+ },
+ "timestamp": datetime.datetime.utcnow().isoformat()
+ }]
+ }
+ )
+ except Exception as e:
+ logging.warn(f"item_release_pool_releaser failed to send Discord Webhook message, {e}")
+
+@scheduler.task('interval', id='builders_club_stipend', seconds=120, misfire_grace_time=60)
+def builders_club_stipend():
+ with scheduler.app.app_context():
+ if redis_controller.get("builders_club_stipend") is not None:
+ return
+ redis_controller.set("builders_club_stipend", "busy", ex=120)
+ from app.models.user_membership import UserMembership
+ from app.models.user import User
+ from app.models.game_session_log import GameSessionLog
+ from app.enums.MembershipType import MembershipType
+ from app.enums.TransactionType import TransactionType
+ from app.services.economy import IncrementTargetBalance
+ from app.util.transactions import CreateTransaction
+ from app.util.membership import GetUserMembership, RemoveUserMembership, GiveUserMembership
+ from app.pages.messages.messages import CreateSystemMessage
+ from app.models.linked_discord import LinkedDiscord
+
+ # Get all users who membership has expired
+ ExpiredMemberships : list[UserMembership] = UserMembership.query.filter(
+ UserMembership.expiration < datetime.datetime.utcnow()
+ )
+ for MembershipObj in ExpiredMemberships:
+ try:
+ if MembershipObj.membership_type == MembershipType.BuildersClub:
+ # Check if their discord is still linked
+ LinkedDiscordObj : LinkedDiscord = LinkedDiscord.query.filter_by(user_id=MembershipObj.user_id).first()
+ if LinkedDiscordObj is not None:
+ # Give them a free month of BC
+ MembershipObj.membership_type = MembershipType.BuildersClub
+ MembershipObj.expiration = datetime.datetime.utcnow() + datetime.timedelta(days=31)
+ db.session.commit()
+ continue
+ elif MembershipObj.membership_type == MembershipType.TurboBuildersClub or MembershipObj.membership_type == MembershipType.OutrageousBuildersClub:
+ UserObj : User = User.query.filter_by(id=MembershipObj.user_id).first()
+ CreateSystemMessage(
+ subject = "Builders Club membership expired",
+ message = f"""Hello {UserObj.username},
+This is an automated message to inform you that your Builders Club membership has expired, if you wish to renew your membership you can do so by following the instructions below
+ - For Turbo Builders Club members:
+ - You can renew your membership in the Discord Server by running the '/claim-turbo' command in the #bot-commands channel, this does require you to still be boosting our Discord Server
+ - For Outrageous Builders Club members:
+ - You can renew your membership by donating $5 to our Ko-Fi page https://ko-fi.com/syntaxeco
+
+If you have any questions or concerns, please contact our support in our Discord Server
+
+Sincerely,
+The SYNTAX Team""",
+ userid = MembershipObj.user_id
+ )
+ # Remove the membership
+ RemoveUserMembership(MembershipObj.user_id)
+ LinkedDiscordObj : LinkedDiscord = LinkedDiscord.query.filter_by(user_id=MembershipObj.user_id).first()
+ if LinkedDiscordObj is not None:
+ GiveUserMembership(MembershipObj.user_id, MembershipType.BuildersClub, expiration=datetime.timedelta(days=31))
+ except Exception as e:
+ logging.info(f"Error while removing expired membership, Exception: {str(e)}")
+ continue
+
+ WaitingMemberships : list[UserMembership] = UserMembership.query.filter(
+ or_(
+ UserMembership.membership_type == MembershipType.TurboBuildersClub,
+ UserMembership.membership_type == MembershipType.OutrageousBuildersClub
+ )
+ ).filter(
+ UserMembership.next_stipend < datetime.datetime.utcnow()
+ ).all()
+ if len(WaitingMemberships) > 0:
+ logging.info(f"Found {len(WaitingMemberships)} users waiting for stipend")
+ for MembershipObj in WaitingMemberships:
+ UserObj : User = User.query.filter_by(id=MembershipObj.user_id).first()
+ if UserObj is None:
+ continue
+ if MembershipObj.membership_type == MembershipType.TurboBuildersClub:
+ IncrementTargetBalance(UserObj, 45, 0)
+ MembershipObj.next_stipend = MembershipObj.next_stipend + datetime.timedelta(hours=24)
+ CreateTransaction(
+ Reciever = UserObj,
+ Sender = User.query.filter_by(id=1).first(),
+ CurrencyAmount = 45,
+ CurrencyType = 0,
+ TransactionType = TransactionType.BuildersClubStipend,
+ AssetId = None,
+ CustomText = "Builders Club Stipend"
+ )
+ logging.info(f"Sent stipend to {UserObj.username} ({UserObj.id})")
+ elif MembershipObj.membership_type == MembershipType.OutrageousBuildersClub:
+ IncrementTargetBalance(UserObj, 80, 0)
+ MembershipObj.next_stipend = MembershipObj.next_stipend + datetime.timedelta(hours=24)
+ CreateTransaction(
+ Reciever = UserObj,
+ Sender = User.query.filter_by(id=1).first(),
+ CurrencyAmount = 80,
+ CurrencyType = 0,
+ TransactionType = TransactionType.BuildersClubStipend,
+ AssetId = None,
+ CustomText = "Builders Club Stipend"
+ )
+ logging.info(f"Sent stipend to {UserObj.username} ({UserObj.id})")
+ db.session.commit()
+ WaitingMemberships : list[UserMembership] = UserMembership.query.filter(
+ UserMembership.membership_type == MembershipType.BuildersClub
+ ).filter(
+ UserMembership.next_stipend < datetime.datetime.utcnow()
+ ).join(User).filter(
+ User.lastonline > datetime.datetime.utcnow() - datetime.timedelta(hours=24)
+ ).all()
+ for MembershipObj in WaitingMemberships:
+ UserObj : User = User.query.filter_by(id=MembershipObj.user_id).first()
+ if UserObj is None:
+ continue
+ #if GameSessionLog.query.filter_by(user_id=UserObj.id).filter( GameSessionLog.joined_at > datetime.datetime.utcnow() - datetime.timedelta( days = 2 ) ).first() is None:
+ # MembershipObj.next_stipend = datetime.datetime.utcnow() + datetime.timedelta(hours=6)
+ # db.session.commit()
+ # return
+ IncrementTargetBalance(UserObj, 10, 0)
+ CreateTransaction(
+ Reciever = UserObj,
+ Sender = User.query.filter_by(id=1).first(),
+ CurrencyAmount = 10,
+ CurrencyType = 0,
+ TransactionType = TransactionType.BuildersClubStipend,
+ AssetId = None,
+ CustomText = "Builders Club Stipend"
+ )
+ MembershipObj.next_stipend = datetime.datetime.utcnow() + datetime.timedelta(hours=24)
+ logging.info(f"Sent stipend to {UserObj.username} ({UserObj.id})")
+ db.session.commit()
+
+ redis_controller.delete("builders_club_stipend")
+
+@scheduler.task('interval', id='refresh_discord_token', seconds=300, misfire_grace_time=900)
+def refresh_discord_token():
+ with scheduler.app.app_context():
+ if redis_controller.get("refresh_discord_token") is not None:
+ return
+ redis_controller.set("refresh_discord_token", "busy", ex=120)
+ from app.models.linked_discord import LinkedDiscord
+ from app.models.user import User
+ from app.models.user_membership import UserMembership
+ from app.enums.MembershipType import MembershipType
+ from app.util.membership import GetUserMembership, RemoveUserMembership
+ from app.pages.messages.messages import CreateSystemMessage
+ from app.util.discord import RefreshAccessToken, GetUserInfoFromToken, DiscordUserInfo, UnexpectedStatusCode, MissingScope
+
+ def SendUnlinkedNotification( UserId : int , Reason : str ):
+ UserObj : User = User.query.filter_by(id=UserId).first()
+ if UserObj is None:
+ return
+ CreateSystemMessage(
+ subject = "Discord Account unlinked",
+ message = f"""Hello {UserObj.username},
+Your discord account was unlinked from your account because \"{Reason}\", if you currently have a Builders Club membership it will be automatically removed from your account until you link your discord account again.
+
+If you have any questions or concerns, please contact our support in our Discord Server
+
+Sincerely,
+The SYNTAX Team""",
+ userid = UserObj.id
+ )
+ CurrentUserMembership : MembershipType = GetUserMembership(UserObj)
+ if CurrentUserMembership == MembershipType.BuildersClub:
+ # Remove the membership
+ RemoveUserMembership(UserObj)
+
+ # Get all users who has a linked discord account and has a discord_expiry that is less than the current time
+ WaitingDiscordLinks : list[LinkedDiscord] = LinkedDiscord.query.filter(
+ LinkedDiscord.discord_expiry < datetime.datetime.utcnow()
+ )
+ for LinkedDiscordObj in WaitingDiscordLinks:
+ try:
+ try:
+ DiscordOAuth2TokenExchangeResponseJSON = RefreshAccessToken(LinkedDiscordObj.discord_refresh_token)
+ except UnexpectedStatusCode as e:
+ db.session.delete(LinkedDiscordObj)
+ SendUnlinkedNotification(LinkedDiscordObj.user_id, f"UnexpectedStatusCodeException_RefreshAccessToken: {str(e)}")
+ continue
+ except MissingScope as e:
+ db.session.delete(LinkedDiscordObj)
+ SendUnlinkedNotification(LinkedDiscordObj.user_id, f"MissingScopeException_RefreshAccessToken: {str(e)}")
+ continue
+ # Get user info
+ try:
+ DiscordUserInfoObj : DiscordUserInfo = GetUserInfoFromToken(DiscordOAuth2TokenExchangeResponseJSON["access_token"])
+ except UnexpectedStatusCode as e:
+ db.session.delete(LinkedDiscordObj)
+ SendUnlinkedNotification(LinkedDiscordObj.user_id, f"UnexpectedStatusCodeException_RefreshUserInfo: {str(e)}")
+ continue
+ if DiscordUserInfoObj is None:
+ continue
+ LinkedDiscordObj.discord_access_token = DiscordOAuth2TokenExchangeResponseJSON["access_token"]
+ LinkedDiscordObj.discord_refresh_token = DiscordOAuth2TokenExchangeResponseJSON["refresh_token"]
+ LinkedDiscordObj.discord_expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=DiscordOAuth2TokenExchangeResponseJSON["expires_in"])
+ LinkedDiscordObj.discord_username = DiscordUserInfoObj.Username
+ LinkedDiscordObj.discord_discriminator = DiscordUserInfoObj.Discriminator
+ LinkedDiscordObj.discord_avatar = DiscordUserInfoObj.AvatarHash
+ LinkedDiscordObj.last_updated = datetime.datetime.utcnow()
+ db.session.commit()
+ except Exception as e:
+ logging.info(f"Error while refreshing discord token, Exception: {str(e)}")
+ continue
+
+
+@scheduler.task('interval', id='migrate_assets', seconds=120, misfire_grace_time=60)
+def migrate_assets():
+ with scheduler.app.app_context():
+ if redis_controller.get("migrate_assets_lock") is not None:
+ return
+ redis_controller.set("migrate_assets_lock", "busy", ex=120)
+ from app.routes.asset import migrateAsset, AddAssetToMigrationQueue, AddAudioAssetToAudioMigrationQueue
+ from app.routes.asset import NoPermissionException, AssetDeliveryAPIFailedException, AssetOnCooldownException, EconomyAPIFailedException, RatelimittedReachedException, AssetNotFoundException, AssetNotAllowedException
+ from app.models.asset import Asset
+
+ EconomyFailedCount : int = 0
+
+ while True:
+ if redis_controller.llen("migrate_assets_queue") == 0:
+ break
+ # Get an asset from the queue
+ AssetId : int = int(redis_controller.lpop("migrate_assets_queue"))
+ if AssetId <= 1:
+ continue
+ try:
+ logging.info(f"AutoAssetMigrator: Auto migrating asset {AssetId}, {redis_controller.llen('migrate_assets_queue')} assets left in queue")
+ migrateAsset(AssetId, throwException=True)
+ except RatelimittedReachedException:
+ logging.info("AutoAssetMigrator: Ratelimit reached while auto migrating assets, stopping")
+ AddAssetToMigrationQueue(AssetId, bypassQueueLimit=False)
+ break
+ except EconomyAPIFailedException:
+ EconomyFailedCount += 1
+ if EconomyFailedCount >= 4:
+ logging.info("AutoAssetMigrator: Economy API failed while auto migrating assets, stopping")
+ #AddAssetToMigrationQueue(AssetId, bypassQueueLimit=False)
+ break
+ else:
+ logging.info("AutoAssetMigrator: Economy API failed while auto migrating assets, retrying")
+ continue
+ except NoPermissionException:
+ logging.info(f"AutoAssetMigrator: No permission to migrate asset from Roblox, assetId: {AssetId}")
+ continue
+ except AssetDeliveryAPIFailedException:
+ logging.info(f"AutoAssetMigrator: AssetDelivery API failed while migrating asset, assetId: {AssetId}")
+ continue
+ except AssetOnCooldownException:
+ logging.info(f"AutoAssetMigrator: Asset is on cooldown, assetId: {AssetId}")
+ continue
+ except AssetNotAllowedException:
+ logging.info(f"AutoAssetMigrator: Asset is not allowed, assetId: {AssetId}")
+ continue
+ except AssetNotFoundException:
+ logging.info(f"AutoAssetMigrator: Asset not found, assetId: {AssetId}")
+ continue
+ except Exception as e:
+ logging.info(f"AutoAssetMigrator: Unknown error while migrating asset, Exception: {str(e)} ,assetId: {AssetId}")
+ continue
+ except:
+ logging.info(f"AutoAssetMigrator: Unknown error while migrating asset, assetId: {AssetId}")
+ continue
+
+ while True:
+ # Migrating audios have a different queue
+ if redis_controller.llen("migrate_audio_assets_queue") == 0:
+ break
+ AssetId : int = int(redis_controller.lpop("migrate_audio_assets_queue"))
+ if AssetId <= 1:
+ continue
+ try:
+ AssociatedPlaceId : int = int(redis_controller.get(f"audio_asset:{AssetId}:placeid"))
+ if AssociatedPlaceId <= 1:
+ continue
+ except:
+ continue
+
+ logging.info(f"AutoAssetMigrator: Auto migrating audio asset {AssetId}, {redis_controller.llen('migrate_audio_assets_queue')} assets left in queue")
+ try:
+ migrateAsset(AssetId, allowedTypes = [3], throwException=True, bypassCooldown = True, attemptSoundWithPlaceId = AssociatedPlaceId)
+ except RatelimittedReachedException:
+ logging.info("AutoAssetMigrator: Ratelimit reached while auto migrating assets, stopping")
+ AddAudioAssetToAudioMigrationQueue(AssetId, bypassQueueLimit=False, placeId = AssociatedPlaceId)
+ break
+ except EconomyAPIFailedException:
+ EconomyFailedCount += 1
+ if EconomyFailedCount >= 4:
+ logging.info("AutoAssetMigrator: Economy API failed while auto migrating assets, stopping")
+ #AddAssetToMigrationQueue(AssetId, bypassQueueLimit=False)
+ break
+ else:
+ logging.info("AutoAssetMigrator: Economy API failed while auto migrating assets, retrying")
+ continue
+ except NoPermissionException:
+ logging.info(f"AutoAssetMigrator: No permission to migrate asset from Roblox, assetId: {AssetId}")
+ continue
+ except AssetDeliveryAPIFailedException:
+ logging.info(f"AutoAssetMigrator: AssetDelivery API failed while migrating asset, assetId: {AssetId}")
+ continue
+ except AssetOnCooldownException:
+ logging.info(f"AutoAssetMigrator: Asset is on cooldown, assetId: {AssetId}")
+ continue
+ except AssetNotAllowedException:
+ logging.info(f"AutoAssetMigrator: Asset is not allowed, assetId: {AssetId}")
+ continue
+ except AssetNotFoundException:
+ logging.info(f"AutoAssetMigrator: Asset not found, assetId: {AssetId}")
+ continue
+ except Exception as e:
+ logging.info(f"AutoAssetMigrator: Unknown error while migrating asset, Exception: {str(e)} ,assetId: {AssetId}")
+ continue
+ except:
+ logging.info(f"AutoAssetMigrator: Unknown error while migrating asset, assetId: {AssetId}")
+ continue
+
+ redis_controller.delete("migrate_assets_lock")
+
+
+
+@scheduler.task('interval', id='fix_thumbnails', seconds = 600, misfire_grace_time=60)
+def fix_thumbnails():
+ with scheduler.app.app_context():
+ if redis_controller.get("fix_thumbnails") is not None:
+ return
+ redis_controller.set("fix_thumbnails", "busy", ex=60)
+ from app.models.gameservers import GameServer
+ from app.models.asset_thumbnail import AssetThumbnail
+ from app.models.asset_version import AssetVersion
+ from app.models.asset import Asset
+ from app.models.user import User
+ from app.models.user_thumbnail import UserThumbnail
+ from app.models.place_icon import PlaceIcon
+ from app.util.assetversion import GetLatestAssetVersion
+
+ from app.routes.thumbnailer import TakeThumbnail, TakeUserThumbnail
+ assetVersions = AssetVersion.query.filter(
+ ~db.session.query(AssetThumbnail.asset_id).filter(
+ AssetThumbnail.asset_id == AssetVersion.asset_id,
+ ).filter(
+ AssetThumbnail.asset_version_id == AssetVersion.version
+ ).exists()
+ ).join(Asset, Asset.id == AssetVersion.asset_id).filter(
+ and_(
+ Asset.moderation_status == 0,
+ Asset.asset_type != AssetType.Place
+ )
+ ).distinct(AssetVersion.asset_id).order_by(AssetVersion.asset_id, AssetVersion.version.desc()).all()
+
+ if len(assetVersions) > 0:
+ AssetTypeBrokenCounter = {}
+ logging.info(f"Found {len(assetVersions)} broken thumbnails")
+ SuccessCount = 0
+ for assetVersion in assetVersions:
+ AssetObj : Asset = Asset.query.filter_by(id=assetVersion.asset_id).first()
+ if AssetObj is None:
+ continue
+ if AssetObj.asset_type == AssetType.Place:
+ continue
+ if AssetObj.moderation_status == 0:
+ LatestVersion : AssetVersion = GetLatestAssetVersion(AssetObj)
+ if LatestVersion is not assetVersion:
+ continue
+ Result = TakeThumbnail(assetVersion.asset_id)
+ if Result == "Thumbnail request sent":
+ SuccessCount += 1
+ else:
+ if AssetObj.asset_type.name in AssetTypeBrokenCounter:
+ AssetTypeBrokenCounter[AssetObj.asset_type.name] += 1
+ else:
+ AssetTypeBrokenCounter[AssetObj.asset_type.name] = 1
+ if SuccessCount >= 30:
+ logging.info(f"Stopping thumbnail fixer, reached 30 thumbnails fixed")
+ break
+
+ for key, value in AssetTypeBrokenCounter.items():
+ logging.info(f"Thumbnail fixer: {value} {key} assets failed to fix")
+ users = User.query.filter(User.id.notin_(db.session.query(UserThumbnail.userid))).all()
+ for user in users:
+ TakeUserThumbnail(user.id)
+ users = UserThumbnail.query.filter(or_(UserThumbnail.full_contenthash == None, UserThumbnail.headshot_contenthash == None)).all()
+ for user in users:
+ TakeUserThumbnail(user.userid)
+
+ Places : list[Asset] = Asset.query.filter(Asset.asset_type == AssetType.Place).filter(Asset.id.notin_(db.session.query(PlaceIcon.placeid))).all()
+ for PlaceObj in Places:
+ TakeThumbnail(PlaceObj.id, isIcon=True)
+
+
+@scheduler.task('interval', id='heartbeat', seconds=15, misfire_grace_time=10)
+def heartbeat():
+ with scheduler.app.app_context():
+ if redis_controller.get("heartbeat") is not None:
+ return # Another instance is already running
+ redis_controller.set("heartbeat", "busy", ex=14)
+ from app.models.gameservers import GameServer
+ from app.models.placeserver_players import PlaceServerPlayer
+ from app.models.placeservers import PlaceServer
+ from app.models.user import User
+ from app.services.gameserver_comm import perform_get, perform_post
+ from app.routes.jobreporthandler import HandleUserTimePlayed
+ import requests
+ import time
+ import threading
+
+ def HandlePlayerDeletion( playerObj : PlaceServerPlayer, placeId : int = None):
+ if placeId is not None:
+ try:
+ TotalTimePlayed = (datetime.datetime.utcnow() - playerObj.joinTime).total_seconds()
+ userObj : User = User.query.filter_by(id=playerObj.userid).first()
+ HandleUserTimePlayed(userObj, TotalTimePlayed, serverUUID = str(playerObj.serveruuid), placeId = placeId)
+ except Exception as e:
+ logging.warn(f"Failed to handle player deletion, Exception: {str(e)}")
+ db.session.delete(playerObj)
+ db.session.commit()
+
+ def RefreshServerInfo( server : GameServer ):
+ with scheduler.app.app_context():
+ server : GameServer = server
+ startTime = time.time()
+ statsRequest = None
+ try:
+ statsRequest = perform_get(
+ TargetGameserver = server,
+ Endpoint = "stats",
+ RequestTimeout = 6
+ )
+ except Exception as e:
+ # Mark the server as offline
+ server.isRCCOnline = False
+ server.thumbnailQueueSize = 0
+ server.RCCmemoryUsage = 0
+ server.heartbeatResponseTime = 0
+ if server.lastHeartbeat < datetime.datetime.utcnow() - datetime.timedelta(seconds=90):
+ # Delete all placeservers
+ GhostServers = PlaceServer.query.filter_by(originServerId=server.serverId).all()
+ for GhostServer in GhostServers:
+ GhostPlayers = PlaceServerPlayer.query.filter_by(serveruuid=GhostServer.serveruuid).all()
+ for GhostPlayer in GhostPlayers:
+ HandlePlayerDeletion(GhostPlayer, placeId = GhostServer.serverPlaceId)
+ db.session.delete(GhostServer)
+ db.session.commit()
+ return
+ if statsRequest is not None and statsRequest.status_code != 200:
+ # Mark the server as offline
+ server.isRCCOnline = False
+ server.thumbnailQueueSize = 0
+ server.RCCmemoryUsage = 0
+ server.heartbeatResponseTime = 0
+ if server.lastHeartbeat < datetime.datetime.utcnow() - datetime.timedelta(seconds=90):
+ # Delete all placeservers
+ GhostServers = PlaceServer.query.filter_by(originServerId=server.serverId).all()
+ for GhostServer in GhostServers:
+ GhostPlayers = PlaceServerPlayer.query.filter_by(serveruuid=GhostServer.serveruuid).all()
+ for GhostPlayer in GhostPlayers:
+ HandlePlayerDeletion(GhostPlayer, placeId = GhostServer.serverPlaceId)
+ db.session.delete(GhostServer)
+ db.session.commit()
+ return
+ if statsRequest is not None and statsRequest.status_code == 200:
+ endTime = time.time()
+ server.lastHeartbeat = datetime.datetime.utcnow()
+ server.heartbeatResponseTime = endTime - startTime
+ stats = statsRequest.json()
+ server.isRCCOnline = stats["RCCOnline"]
+ server.thumbnailQueueSize = stats["ThumbnailQueueSize"]
+ server.RCCmemoryUsage = stats["RCCMemoryUsage"]
+ db.session.commit()
+
+ if "RunningJobs" in stats:
+ for RunningJob in stats["RunningJobs"]:
+ PlaceServerObj : PlaceServer = PlaceServer.query.filter_by(serveruuid = RunningJob).first()
+ if PlaceServerObj is None:
+ logging.debug(f"CloseJob : Closing {RunningJob} because PlaceServer does not exist in database, Owner: {server.serverId} / {server.serverName}")
+ try:
+ CloseJobRequest = perform_post(
+ TargetGameserver = server,
+ Endpoint = "CloseJob",
+ JSONData = {
+ "jobid": RunningJob
+ }
+ )
+ except Exception as e:
+ continue
+
+ refresh_server_thread_list : list[threading.Thread] = []
+ servers : list[GameServer] = GameServer.query.all()
+ for server in servers:
+ refresh_server_thread_list.append(threading.Thread(target=RefreshServerInfo, args=(server,)))
+ for thread in refresh_server_thread_list:
+ thread.start()
+ for thread in refresh_server_thread_list:
+ thread.join()
+
+ GhostServers : list[PlaceServer] = PlaceServer.query.filter(PlaceServer.lastping < datetime.datetime.utcnow() - datetime.timedelta(seconds=60)).all()
+ for GhostServer in GhostServers:
+ GhostPlayers = PlaceServerPlayer.query.filter_by(serveruuid=GhostServer.serveruuid).all()
+ for GhostPlayer in GhostPlayers:
+ HandlePlayerDeletion(GhostPlayer, placeId = GhostServer.serverPlaceId)
+ db.session.delete(GhostServer)
+
+ GhostPlayers : list[PlaceServerPlayer] = PlaceServerPlayer.query.filter(PlaceServerPlayer.lastHeartbeat < datetime.datetime.utcnow() - datetime.timedelta(seconds=120)).all()
+ for GhostPlayer in GhostPlayers:
+ db.session.delete(GhostPlayer)
+
+ db.session.commit()
+
+ redis_controller.delete("heartbeat")
+
+
diff --git a/app/files/2012Studio.lua b/app/files/2012Studio.lua
new file mode 100644
index 0000000..e69de29
diff --git a/app/files/2014Gameserver.lua b/app/files/2014Gameserver.lua
new file mode 100644
index 0000000..bbd0677
--- /dev/null
+++ b/app/files/2014Gameserver.lua
@@ -0,0 +1,367 @@
+
+print("Config Load")
+
+local placeId = {PlaceId}
+local port = {NetworkPort}
+local gameId = {PlaceId}
+local CreatorId = {CreatorId}
+local CreatorType = {CreatorType}
+local TempPlaceAccessKey = "{TempPlaceAccessKey}"
+local sleeptime = 1
+local access = "{AuthToken}"
+local JobId = "{JobId}"
+local BaseURL = "http://www.syntax.eco"
+local BaseDomain = "syntax.eco"
+local timeout = 15
+
+local HttpService = game:GetService("HttpService")
+local Players = game:GetService("Players")
+local ScriptContext = game:GetService("ScriptContext")
+
+print("Starting server for place "..tostring(placeId).." on port "..tostring(port).." and job id "..JobId)
+
+------------------- UTILITY FUNCTIONS --------------------------
+
+function waitForChild(parent, childName)
+ while true do
+ local child = parent:findFirstChild(childName)
+ if child then
+ return child
+ end
+ parent.ChildAdded:wait()
+ end
+end
+
+function onDied(victim, humanoid)
+ return
+end
+
+-----------------------------------END UTILITY FUNCTIONS -------------------------
+
+-----------------------------------"CUSTOM" SHARED CODE----------------------------------
+
+pcall(function() settings().Network.UseInstancePacketCache = true end)
+pcall(function() settings().Network.UsePhysicsPacketCache = true end)
+pcall(function() settings()["Task Scheduler"].PriorityMethod = Enum.PriorityMethod.AccumulatedError end)
+
+
+settings().Network.PhysicsSend = Enum.PhysicsSendMethod.TopNErrors
+settings().Network.ExperimentalPhysicsEnabled = true
+settings().Network.WaitingForCharacterLogRate = 100
+pcall(function() settings().Diagnostics:LegacyScriptMode() end)
+
+-----------------------------------START GAME SHARED SCRIPT------------------------------
+
+local RobloxPlacesList = {{
+ 4378
+}}
+
+function isPlaceOwnedByRoblox( place_id )
+ for _, id in pairs( RobloxPlacesList ) do
+ if id == place_id then
+ return true
+ end
+ end
+ return false
+end
+
+local assetId = placeId -- might be able to remove this now
+
+local scriptContext = game:GetService('ScriptContext')
+scriptContext.ScriptsDisabled = true
+
+game:SetPlaceID(assetId, isPlaceOwnedByRoblox(assetId))
+pcall(function () if universeId ~= nil then game:SetUniverseId(universeId) end end)
+pcall(function() game.JobId = JobId end)
+game:GetService("ChangeHistoryService"):SetEnabled(false)
+
+if CreatorType == 1 then
+ CreatorType = Enum.CreatorType.User
+elseif CreatorType == 2 then
+ CreatorType = Enum.CreatorType.Group
+else
+ CreatorType = Enum.CreatorType.User
+end
+
+pcall(function() game:SetCreatorID(CreatorId, CreatorType) end)
+
+-- establish this peer as the Server
+local ns = game:GetService("NetworkServer")
+
+local badgeUrlFlagExists, badgeUrlFlagValue = pcall(function () return settings():GetFFlag("NewBadgeServiceUrlEnabled") end)
+local newBadgeUrlEnabled = badgeUrlFlagExists and badgeUrlFlagValue
+if BaseURL~=nil then
+ local apiProxyUrl = string.gsub(BaseURL, "http://www", "https://api") -- hack - passing domain (ie "sitetest1.robloxlabs.com") and appending "https://api." to it would be better
+
+ pcall(function() game:GetService("Players"):SetAbuseReportUrl(BaseURL .. "/AbuseReport/InGameChatHandler.ashx") end)
+ pcall(function() game:GetService("ScriptInformationProvider"):SetAssetUrl(BaseURL .. "/Asset/") end)
+ pcall(function() game:GetService("ContentProvider"):SetBaseUrl(BaseURL .. "/") end)
+ pcall(function() game:GetService("Players"):SetChatFilterUrl(BaseURL .. "/Game/ChatFilter.ashx") end)
+
+ if gameCode then
+ game:SetVIPServerId(tostring(gameCode))
+ end
+
+ game:GetService("BadgeService"):SetPlaceId(placeId)
+
+ if access~=nil then
+ game:GetService("BadgeService"):SetAwardBadgeUrl(BaseURL .. "/Game/Badge/AwardBadge.ashx?UserID=%d&BadgeID=%d&PlaceID=%d")
+ game:GetService("BadgeService"):SetHasBadgeUrl(BaseURL .. "/Game/Badge/HasBadge.ashx?UserID=%d&BadgeID=%d")
+ game:GetService("BadgeService"):SetIsBadgeDisabledUrl(BaseURL .. "/Game/Badge/IsBadgeDisabled.ashx?BadgeID=%d&PlaceID=%d")
+
+ game:GetService("FriendService"):SetMakeFriendUrl(BaseURL .. "/Friend/CreateFriend?firstUserId=%d&secondUserId=%d")
+ game:GetService("FriendService"):SetBreakFriendUrl(BaseURL .. "/Friend/BreakFriend?firstUserId=%d&secondUserId=%d")
+ game:GetService("FriendService"):SetGetFriendsUrl(BaseURL .. "/Friend/AreFriends?userId=%d")
+ game:GetService("FriendService"):SetCreateFriendRequestUrl(BaseURL .. "/Friend/CreateFriendRequest?requesterUserId=%d&requestedUserId=%d")
+ game:GetService("FriendService"):SetDeleteFriendRequestUrl(BaseURL .. "/Friend/DeleteFriendRequest?requesterUserId=%d&requestedUserId=%d")
+ end
+ game:GetService("BadgeService"):SetIsBadgeLegalUrl("")
+ game:GetService("InsertService"):SetBaseSetsUrl(BaseURL .. "/Game/Tools/InsertAsset.ashx?nsets=10&type=base")
+ game:GetService("InsertService"):SetUserSetsUrl(BaseURL .. "/Game/Tools/InsertAsset.ashx?nsets=20&type=user&userid=%d")
+ game:GetService("InsertService"):SetCollectionUrl(BaseURL .. "/Game/Tools/InsertAsset.ashx?sid=%d")
+ game:GetService("InsertService"):SetAssetUrl(BaseURL .. "/Asset/?id=%d")
+ game:GetService("InsertService"):SetAssetVersionUrl(BaseURL .. "/Asset/?assetversionid=%d")
+
+ game:GetService("Players"):SetSaveDataUrl(BaseURL .. "/persistence/legacy/save?placeId=" .. tostring(placeId) .. "&userId=%d")
+ game:GetService("Players"):SetLoadDataUrl(BaseURL .. "/persistence/legacy/load?placeId=" .. tostring(placeId) .. "&userId=%d")
+
+ --pcall(function() loadfile(BaseURL .. "/Game/LoadPlaceInfo.ashx?PlaceId=" .. placeId)() end)
+
+ --pcall(function()
+ -- if access then
+ -- loadfile(BaseURL .. "/Game/PlaceSpecificScript.ashx?PlaceId=" .. placeId .. "&" .. access)()
+ -- end
+ -- end)
+end
+
+--pcall(function() game:GetService("NetworkServer"):SetIsPlayerAuthenticationRequired(true) end)
+settings().Diagnostics.LuaRamLimit = 0
+print("Configured Server")
+local StartTime = tick()
+local StoppingServer = false
+
+local function GetPlayerByUserId( userId )
+ for _, player in pairs( Players:GetPlayers() ) do
+ if player.userId == userId then
+ return player
+ end
+ end
+ return nil
+end
+
+local function ReportServerPlayers(IgnoreThisPlayer)
+ --if StoppingServer then return end
+ local success, message = pcall(function()
+ local PlayerList = {{}}
+ for _, player in pairs(Players:GetChildren()) do
+ if player:IsA("Player") and player ~= IgnoreThisPlayer then
+ table.insert(PlayerList, {{
+ ["UserId"] = player.userId,
+ ["Name"] = player.Name
+ }})
+ end
+ end
+ local MessagePayload = HttpService:JSONEncode({{
+ ["AuthToken"] = access,
+ ["JobId"] = JobId,
+ ["Players"] = PlayerList
+ }})
+ local ResponseData = game:HttpPost(BaseURL.."/internal/gameserver/reportplayers", MessagePayload, true, "application/json")
+ local ResponseJSON = HttpService:JSONDecode(ResponseData)
+ for _, player in pairs(ResponseJSON["bad"]) do -- This is a list of players that need to be kicked from the server
+ local TargetPlayer = GetPlayerByUserId(player)
+ if TargetPlayer ~= nil then
+ print("Kicking Player", tostring(player), "because was requested by backend")
+ TargetPlayer:Kick("There was an issue authenticating you, please contact support.")
+ TargetPlayer:Destroy()
+ end
+ end
+ end)
+ if not success then
+ print("ReportServerPlayers failed:", message)
+ end
+end
+
+local function ReportServerStats()
+ if StoppingServer then return end
+ local success, message = pcall(function()
+ local MessagePayload = HttpService:JSONEncode({{
+ ["AuthToken"] = access,
+ ["JobId"] = JobId,
+ ["PlaceId"] = placeId,
+ ["ServerAliveTime"] = (tick() - StartTime) + 1
+ }})
+ game:HttpPost(BaseURL.."/internal/gameserver/reportstats", MessagePayload, false, "application/json")
+ end)
+ if not success then
+ print("ReportServerStats failed:", message)
+ end
+end
+
+local function ReportServerShutdown()
+ local success, message = pcall(function()
+ local MessagePayload = HttpService:JSONEncode({{
+ ["AuthToken"] = access,
+ ["JobId"] = JobId,
+ ["PlaceId"] = placeId,
+ ["ServerAliveTime"] = tick() - StartTime
+ }})
+ game:HttpPost(BaseURL.."/internal/gameserver/reportshutdown", MessagePayload, false, "application/json")
+ end)
+ if not success then
+ print("ReportServerShutdown failed:", message)
+ end
+end
+
+local function AuthenticatePlayer( player )
+ local success, message = pcall(function()
+ local VerificationTicket = string.match( player.CharacterAppearance, BaseDomain.."/Asset/CharacterFetch.ashx%?userId=%d+%&t=(%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x)%&legacy=1$")
+ if VerificationTicket == nil then
+ print("Failed to get VerificationTicket for player", player.Name)
+ return false
+ end
+
+ local MessagePayload = HttpService:JSONEncode({{
+ ["AuthToken"] = access,
+ ["JobId"] = JobId,
+ ["PlaceId"] = placeId,
+ ["ServerAliveTime"] = tick() - StartTime,
+ ["UserId"] = player.userId,
+ ["VerificationTicket"] = VerificationTicket,
+ ["CharacterAppearance"] = player.CharacterAppearance,
+ ["Username"] = player.Name
+ }})
+ local ResponseData = game:HttpPost(BaseURL.."/internal/gameserver/verifyplayer", MessagePayload, true, "application/json")
+ local ResponseJSON = HttpService:JSONDecode(ResponseData)
+ return ResponseJSON["authenticated"]
+ end)
+ if not success then
+ print("AuthenticatePlayer failed:", message)
+ return false
+ end
+ return message
+end
+
+local function ShutdownServer()
+ StoppingServer = true
+ ReportServerShutdown()
+ ScriptContext.ScriptsDisabled = true
+ ns:Stop(1000)
+ game:Shutdown()
+end
+
+local TotalPlayersJoined = 0
+game:GetService("Players").PlayerAdded:connect(function(player)
+ local StartTime = tick()
+ local CharacterURL
+ repeat
+ if string.find(player.CharacterAppearance, BaseDomain.."/Asset/CharacterFetch.ashx%?userId=%d+") then
+ CharacterURL = player.CharacterAppearance
+ end
+ wait(0.1)
+ until CharacterURL ~= nil or tick() - StartTime > 1
+ if CharacterURL == nil then
+ player:Kick("There was an issue authenticating you, please contact support.")
+ print("Failed to get UserId for player", player.Name, "because CharacterURL was nil")
+ return
+ end
+
+ local UserId = tonumber(string.match(CharacterURL, BaseDomain.."/Asset/CharacterFetch.ashx%?userId=(%d+)%&t=%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x%&legacy=1$"))
+
+ if UserId ~= nil then
+ player.userId = UserId
+ else
+ player:Kick("There was an issue authenticating you, please contact support.")
+ print("Failed to get UserId for player", player.Name, CharacterURL)
+ return
+ end
+
+ local IsPlayerAuthenticated = AuthenticatePlayer(player)
+ if IsPlayerAuthenticated then
+ player.DataComplexityLimit = 1024 * 1024 * 1
+ player.CharacterAppearance = BaseURL.."/Asset/CharacterFetch.ashx?userId="..tostring(player.userId).."&legacy=1"
+ ReportServerPlayers()
+ player:LoadData()
+ TotalPlayersJoined = TotalPlayersJoined + 1
+
+ local PlayerChangedConnection
+ PlayerChangedConnection = player.Changed:connect(function(property)
+ if property == "Name" then
+ ReportServerPlayers()
+ end
+ end)
+
+ coroutine.wrap(function()
+ while true do
+ wait(120)
+ if StoppingServer then break end
+ if player.Parent == nil then break end
+ pcall(function() player:SaveData() end)
+ end
+ end)()
+ else
+ player:Kick("There was an issue authenticating you, please contact support.")
+ print("Failed to authenticate player", player.Name)
+ return
+ end
+end)
+
+
+game:GetService("Players").PlayerRemoving:connect(function(player)
+ ReportServerPlayers(player)
+ pcall(function() player:SaveData() end)
+ local PlayerCount = #Players:GetPlayers()
+ if PlayerCount == 0 then
+ wait(10) -- Wait 10 seconds to see if anyone rejoins
+ PlayerCount = #Players:GetPlayers()
+ if PlayerCount == 0 then
+ ShutdownServer()
+ end
+ end
+end)
+
+local onlyCallGameLoadWhenInRccWithAccessKey = newBadgeUrlEnabled
+wait()
+-- load the game
+print("Loading game")
+
+local success, result = pcall(function()
+ game:Load(BaseURL .. "/asset/?id=" .. placeId.."&access=".. TempPlaceAccessKey)
+end)
+if not success then
+ print("Failed to Load Place File, unsupported file format")
+ local ErrorMessage = Instance.new("Message", workspace)
+ ErrorMessage.Text = "Failed to Load Place File, unsupported file format"
+end
+
+--Players:SetChatStyle(Enum.ChatStyle.ClassicAndBubble)
+-- Now start the connection
+ns:Start(port, sleeptime)
+
+if timeout then
+ scriptContext:SetTimeout(timeout)
+end
+scriptContext.ScriptsDisabled = false
+
+-- StartGame --
+Game:GetService("RunService"):Run()
+ReportServerStats()
+
+coroutine.wrap(function()
+ while true do
+ wait(10)
+ if StoppingServer then break end
+ ReportServerStats()
+ ReportServerPlayers()
+ end
+end)()
+
+coroutine.wrap(function()
+ wait(120) -- Wait 2 minutes to check if anyone has joined
+ if TotalPlayersJoined == 0 then
+ print("Stopping server, no players joined past 2 minutes.")
+ ShutdownServer()
+ end
+end)()
+
+pcall(function() Game:GetService("ScriptContext"):AddStarterScript(37801172) end)
diff --git a/app/files/2014Join.lua b/app/files/2014Join.lua
new file mode 100644
index 0000000..57b875e
--- /dev/null
+++ b/app/files/2014Join.lua
@@ -0,0 +1,331 @@
+
+pcall(function() game:SetPlaceID(-1, false) end)
+
+local startTime = tick()
+local connectResolved = false
+local loadResolved = false
+local joinResolved = false
+local playResolved = true
+local playStartTime = 0
+local player = nil
+local BaseURL = "http://www.syntax.eco"
+local PlaceId = {PlaceId}
+
+settings()["Game Options"].CollisionSoundEnabled = true
+pcall(function() settings().Rendering.EnableFRM = true end)
+pcall(function() settings().Physics.Is30FpsThrottleEnabled = true end)
+pcall(function() settings()["Task Scheduler"].PriorityMethod = Enum.PriorityMethod.AccumulatedError end)
+pcall(function() settings().Physics.PhysicsEnvironmentalThrottle = Enum.EnviromentalPhysicsThrottle.DefaultAuto end)
+
+
+-- arguments ---------------------------------------
+local threadSleepTime = 15
+local test = false
+
+local closeConnection = game.Close:connect(function()
+ if 0 then
+ if not connectResolved then
+ local duration = tick() - startTime;
+ elseif (not loadResolved) or (not joinResolved) then
+ local duration = tick() - startTime;
+ if not loadResolved then
+ loadResolved = true
+ end
+ if not joinResolved then
+ joinResolved = true
+ end
+ elseif not playResolved then
+ local duration = tick() - playStartTime;
+ playResolved = true
+ end
+ end
+end)
+
+game:GetService("ChangeHistoryService"):SetEnabled(false)
+game:GetService("ContentProvider"):SetThreadPool(16)
+game:GetService("InsertService"):SetBaseSetsUrl(BaseURL.."/Game/Tools/InsertAsset.ashx?nsets=10&type=base")
+game:GetService("InsertService"):SetUserSetsUrl(BaseURL.."/Game/Tools/InsertAsset.ashx?nsets=20&type=user&userid=%d")
+game:GetService("InsertService"):SetCollectionUrl(BaseURL.."/Game/Tools/InsertAsset.ashx?sid=%d")
+game:GetService("InsertService"):SetAssetUrl(BaseURL.."/Asset/?id=%d")
+game:GetService("InsertService"):SetAssetVersionUrl(BaseURL.."/Asset/?assetversionid=%d")
+
+pcall(function() game:GetService("SocialService"):SetFriendUrl(BaseURL.."/Game/LuaWebService/HandleSocialRequest.ashx?method=IsFriendsWith&playerid=%d&userid=%d") end)
+pcall(function() game:GetService("SocialService"):SetBestFriendUrl(BaseURL.."/Game/LuaWebService/HandleSocialRequest.ashx?method=IsBestFriendsWith&playerid=%d&userid=%d") end)
+pcall(function() game:GetService("SocialService"):SetGroupUrl(BaseURL.."/Game/LuaWebService/HandleSocialRequest.ashx?method=IsInGroup&playerid=%d&groupid=%d") end)
+pcall(function() game:GetService("SocialService"):SetGroupRankUrl(BaseURL.."/Game/LuaWebService/HandleSocialRequest.ashx?method=GetGroupRank&playerid=%d&groupid=%d") end)
+pcall(function() game:GetService("SocialService"):SetGroupRoleUrl(BaseURL.."/Game/LuaWebService/HandleSocialRequest.ashx?method=GetGroupRole&playerid=%d&groupid=%d") end)
+pcall(function() game:GetService("GamePassService"):SetPlayerHasPassUrl(BaseURL.."/Game/GamePass/GamePassHandler.ashx?Action=HasPass&UserID=%d&PassID=%d") end)
+pcall(function() game:GetService("MarketplaceService"):SetProductInfoUrl(BaseURL.."/marketplace/productinfo?assetId=%d") end)
+pcall(function() game:GetService("MarketplaceService"):SetPlayerOwnsAssetUrl(BaseURL.."/ownership/hasasset?userId=%d&assetId=%d") end)
+pcall(function() game:SetCreatorID(0, Enum.CreatorType.User) end)
+
+-- Bubble chat. This is all-encapsulated to allow us to turn it off with a config setting
+pcall(function() game:GetService("Players"):SetChatStyle(Enum.ChatStyle.Classic) end)
+pcall( function() if settings().Network.MtuOverride == 0 then settings().Network.MtuOverride = 1400 end end)
+
+local waitingForCharacter = false;
+local waitingForCharacterGuid = "26c3de03-3381-4ab6-8e60-e415fa757eba";
+
+
+-- globals -----------------------------------------
+
+client = game:GetService("NetworkClient")
+visit = game:GetService("Visit")
+
+-- functions ---------------------------------------
+function ifSeleniumThenSetCookie(key, value)
+ game:GetService("CookiesService"):SetCookieValue(key, value)
+end
+
+function setMessage(message)
+ game:SetMessage(message)
+end
+setMessage("Connecting to SYNTAX...")
+function showErrorWindow(message, errorType, errorCategory)
+ if (not loadResolved) or (not joinResolved) then
+ local duration = tick() - startTime;
+ if not loadResolved then
+ loadResolved = true
+ end
+ if not joinResolved then
+ joinResolved = true
+ end
+ elseif not playResolved then
+ local duration = tick() - playStartTime;
+ playResolved = true
+ end
+
+ game:SetMessage(message)
+end
+
+function reportError(err, message)
+ print("***ERROR*** " .. err)
+ client:Disconnect()
+ wait(1)
+ showErrorWindow("Error: " .. err, message, "Other")
+end
+
+-- called when the client connection closes
+function onDisconnection(peer, lostConnection)
+ if lostConnection then
+ showErrorWindow("You have lost the connection to the game", "LostConnection", "LostConnection")
+ else
+ showErrorWindow("This game has shut down", "Kick", "Kick")
+ end
+end
+
+function requestCharacter(replicator)
+
+ -- prepare code for when the Character appears
+ local connection
+ connection = player.Changed:connect(function (property)
+ if property=="Character" then
+ game:ClearMessage()
+ waitingForCharacter = false
+
+ connection:disconnect()
+
+ if 0 then
+ if not joinResolved then
+ local duration = tick() - startTime;
+ joinResolved = true
+
+ playStartTime = tick()
+ playResolved = false
+ end
+ end
+ end
+ end)
+
+ setMessage("Requesting character")
+
+ if 0 and not loadResolved then
+ local duration = tick() - startTime;
+ loadResolved = true
+ end
+
+ local success, err = pcall(function()
+ replicator:RequestCharacter()
+ setMessage("Waiting for character")
+ waitingForCharacter = true
+ end)
+end
+
+-- called when the client connection is established
+function onConnectionAccepted(url, replicator)
+ connectResolved = true
+ --reportDuration("GameConnect", "Success", tick() - startTime, false)
+
+ local waitingForMarker = true
+
+ local success, err = pcall(function()
+ if not test then
+ visit:SetPing("", 300)
+ end
+ game:SetMessageBrickCount()
+ replicator.Disconnection:connect(onDisconnection)
+
+ -- Wait for a marker to return before creating the Player
+ local marker = replicator:SendMarker()
+
+ marker.Received:connect(function()
+ waitingForMarker = false
+ requestCharacter(replicator)
+ end)
+ end)
+
+ if not success then
+ reportError(err,"ConnectionAccepted")
+ return
+ end
+
+ -- TODO: report marker progress
+
+ while waitingForMarker do
+ workspace:ZoomToExtents()
+ wait(0.5)
+ end
+end
+
+-- called when the client connection fails
+function onConnectionFailed(_, error)
+ showErrorWindow("Failed to connect to the Game. (ID=" .. error .. ")", "ID" .. error, "Other")
+end
+
+-- called when the client connection is rejected
+function onConnectionRejected()
+ connectionFailed:disconnect()
+ showErrorWindow("This game is not available. Please try another", "WrongVersion", "WrongVersion")
+end
+
+idled = false
+function onPlayerIdled(time)
+ if time > 20*60 then
+ showErrorWindow(string.format("You were disconnected for being idle %d minutes", time/60), "Idle", "Idle")
+ client:Disconnect()
+ if not idled then
+ idled = true
+ end
+ end
+end
+
+pcall(function() settings().Diagnostics:LegacyScriptMode() end)
+coroutine.wrap(function()
+ game:SetRemoteBuildMode(true)
+
+ setMessage("Fetching Place Info from SYNTAX")
+ --print("Fetching Place Info from Server")
+ local joinScriptUrl = nil
+ local AttemptCount = 0
+ local success, result = nil, nil
+ while true do
+ success, result = pcall(function()
+ return game:HttpPost( BaseURL.."/Game/placelauncher.ashx?placeId="..tostring(PlaceId).."&rand="..tostring(math.random(1,9999999)), "{{}}", true, "application/json")
+ end)
+ --print("Placelauncher ["..tostring(AttemptCount).."]: "..tostring(result))
+
+ if success then
+ local JSONResponse = game:GetService("HttpService"):JSONDecode(result)
+ --print("Fetch Place Info Success, ["..tostring(AttemptCount).."]")
+ if JSONResponse["status"] == 1 then
+ setMessage("Waiting for Server to start... ( This may take a while ) [ "..tostring(AttemptCount).." ]")
+ --print("Placelauncher returned status 1")
+ elseif JSONResponse["status"] == 2 then -- Server Started
+ --print("Placelauncher returned status 2")
+ setMessage("Server Found! Connecting...")
+ joinScriptUrl = JSONResponse["joinScriptUrl"]
+
+ break
+ else
+ setMessage("RequestFailed, message: "..JSONResponse["message"])
+ error("RequestFailed, message: "..JSONResponse["message"])
+ end
+ if AttemptCount > 15 then
+ setMessage("Placelauncher request timed out, please try again later")
+ error("Placelauncher request timed out, please try again later")
+ end
+ --print("Waiting 3 seconds before next fetch [ "..tostring(AttemptCount).." ]")
+ wait(3)
+ AttemptCount = AttemptCount + 1
+ else
+ setMessage("Failed to get place launcher info: "..result)
+ error("Failed to get place launcher info: "..result)
+ end
+ end
+
+ if not joinScriptUrl then
+ setMessage("Failed to get join script, please try again later")
+ error("Failed to get join script")
+ end
+ --print("Fetch JoinScriptUrl Success")
+
+ local success, result = pcall(function()
+ return game:HttpGet(joinScriptUrl, true)
+ end)
+ if not success then
+ setMessage("Failed to get join script: "..result)
+ error("Failed to get join script: "..result)
+ end
+
+ local JSONResponse = game:GetService("HttpService"):JSONDecode(result:sub(result:find("\n", 1, true)+1))
+
+ local MachineAddress = JSONResponse["MachineAddress"]
+ local ServerPort = JSONResponse["ServerPort"]
+ local PlayerUsername = JSONResponse["UserName"]
+ local PlayerId = JSONResponse["UserId"]
+ local AccountAge = JSONResponse["AccountAge"]
+ local GameSessionId = JSONResponse["SessionId"]
+ local CharacterAppearance = JSONResponse["CharacterAppearance"]
+
+ setMessage("Welcome, "..PlayerUsername.."! Connecting to SYNTAX...")
+ --print("Connecting to "..MachineAddress..":"..tostring(ServerPort).." as "..PlayerUsername.." ("..tostring(PlayerId)..")")
+ wait(1.5)
+
+ client.ConnectionAccepted:connect(onConnectionAccepted)
+ client.ConnectionRejected:connect(onConnectionRejected)
+ connectionFailed = client.ConnectionFailed:connect(onConnectionFailed)
+ client.Ticket = ""
+
+ local ConnectionAttempt = 0
+ while true do
+ setMessage("Connecting to Gameserver... [ "..tostring(ConnectionAttempt).." ]")
+
+ local isConnectionSuccessful, player = pcall(function()
+ playerConnectSucces, player = pcall(function() return client:PlayerConnect(PlayerId, MachineAddress, ServerPort, 0, threadSleepTime) end)
+ if not playerConnectSucces then
+ --print("PlayerConnect function failed, fallback to legacy connect")
+ player = game:GetService("Players"):CreateLocalPlayer(0)
+ client:Connect(MachineAddress, ServerPort, 0, threadSleepTime)
+ end
+
+ return player
+ end)
+ if isConnectionSuccessful then
+ break
+ else
+ if ConnectionAttempt > 5 then
+ error("Failed to connect to server: "..player)
+ end
+ ConnectionAttempt = ConnectionAttempt + 1
+ wait(2)
+ end
+ end
+
+ player:SetSuperSafeChat(false)
+
+ pcall(function() player:SetUnder13(false) end)
+ pcall(function() player:SetMembershipType(Enum.MembershipType[JSONResponse["MembershipType"]]) end)
+ pcall(function() player:SetAccountAge(AccountAge) end)
+ pcall(function() player.Name = PlayerUsername end)
+ pcall(function() player.UserId = PlayerId end)
+ pcall(function() client:SetGameSessionID(GameSessionId) end)
+ pcall(function() game:SetPlaceID(PlaceId, false) end)
+ pcall(function() player.ChatMode = Enum.ChatMode.TextAndMenu end)
+
+ player.Idled:connect(onPlayerIdled)
+ player.CharacterAppearance = CharacterAppearance
+ game:GetService("Players"):SetChatStyle(Enum.ChatStyle[JSONResponse["ChatStyle"]])
+
+ pcall(function() game:SetScreenshotInfo("") end)
+ pcall(function() game:SetVideoInfo('GamesROBLOX, video, free game, online virtual world') end)
+end)()
\ No newline at end of file
diff --git a/app/files/2016Gameserver.lua b/app/files/2016Gameserver.lua
new file mode 100644
index 0000000..1259220
--- /dev/null
+++ b/app/files/2016Gameserver.lua
@@ -0,0 +1,213 @@
+local PlaceId, Port, BaseURL, AuthToken, CreatorId, CreatorType, DownloadAuthorizationToken = ...
+
+local RunService = game:GetService("RunService")
+local ContentProvider = game:GetService("ContentProvider")
+local Players = game:GetService("Players")
+local HttpService = game:GetService("HttpService")
+local ScriptContext = game:GetService('ScriptContext')
+local RobloxReplicatedStorage = game:GetService('RobloxReplicatedStorage')
+
+HttpService.HttpEnabled = false -- Disable HttpService for security reasons
+
+if CreatorType == 1 then
+ CreatorType = Enum.CreatorType.User
+elseif CreatorType == 2 then
+ CreatorType = Enum.CreatorType.Group
+else
+ CreatorType = Enum.CreatorType.User
+end
+pcall(function() game:SetCreatorID(CreatorId, CreatorType) end)
+
+pcall(function() settings().Network.UseInstancePacketCache = true end)
+pcall(function() settings().Network.UsePhysicsPacketCache = true end)
+--pcall(function() settings()["Task Scheduler"].PriorityMethod = Enum.PriorityMethod.FIFO end)
+pcall(function() settings()["Task Scheduler"].PriorityMethod = Enum.PriorityMethod.AccumulatedError end)
+
+--settings().Network.PhysicsSend = 1 -- 1==RoundRobin
+--settings().Network.PhysicsSend = Enum.PhysicsSendMethod.ErrorComputation2
+settings().Network.PhysicsSend = Enum.PhysicsSendMethod.TopNErrors
+settings().Network.ExperimentalPhysicsEnabled = true
+settings().Network.WaitingForCharacterLogRate = 100
+pcall(function() settings().Diagnostics:LegacyScriptMode() end)
+
+pcall(function() ScriptContext:AddStarterScript(37801172) end)
+ScriptContext.ScriptsDisabled = true
+
+game:SetPlaceID(PlaceId, false)
+game:GetService("ChangeHistoryService"):SetEnabled(false)
+local NetworkServer = game:GetService("NetworkServer")
+
+if BaseURL~=nil then
+ pcall(function() game:GetService("Players"):SetAbuseReportUrl(BaseURL .. "/AbuseReport/InGameChatHandler.ashx") end)
+ pcall(function() game:GetService("ScriptInformationProvider"):SetAssetUrl(BaseURL .. "/Asset/") end)
+ pcall(function() game:GetService("ContentProvider"):SetBaseUrl(BaseURL .. "/") end)
+ pcall(function() game:GetService("Players"):SetChatFilterUrl(BaseURL .. "/Game/ChatFilter.ashx") end)
+
+ game:GetService("BadgeService"):SetPlaceId(PlaceId)
+ game:GetService("BadgeService"):SetIsBadgeLegalUrl("")
+ game:GetService("BadgeService"):SetAwardBadgeUrl(BaseURL .. "/Game/Badge/AwardBadge.ashx?UserID=%d&BadgeID=%d&PlaceID=%d")
+ game:GetService("BadgeService"):SetHasBadgeUrl(BaseURL .. "/Game/Badge/HasBadge.ashx?UserID=%d&BadgeID=%d")
+ game:GetService("BadgeService"):SetIsBadgeDisabledUrl(BaseURL .. "/Game/Badge/IsBadgeDisabled.ashx?BadgeID=%d&PlaceID=%d")
+
+ game:GetService("InsertService"):SetBaseSetsUrl(BaseURL .. "/Game/Tools/InsertAsset.ashx?nsets=10&type=base")
+ game:GetService("InsertService"):SetUserSetsUrl(BaseURL .. "/Game/Tools/InsertAsset.ashx?nsets=20&type=user&userid=%d")
+ game:GetService("InsertService"):SetCollectionUrl(BaseURL .. "/Game/Tools/InsertAsset.ashx?sid=%d")
+ game:GetService("InsertService"):SetAssetUrl(BaseURL .. "/Asset/?id=%d")
+ game:GetService("InsertService"):SetAssetVersionUrl(BaseURL .. "/Asset/?assetversionid=%d")
+
+ game:GetService("Players"):SetSaveDataUrl(BaseURL .. "/persistence/legacy/save?userId=%d")
+ game:GetService("Players"):SetLoadDataUrl(BaseURL .. "/persistence/legacy/load?userId=%d")
+
+ game:GetService("FriendService"):SetMakeFriendUrl(BaseURL .. "/Friend/CreateFriend?firstUserId=%d&secondUserId=%d")
+ game:GetService("FriendService"):SetBreakFriendUrl(BaseURL .. "/Friend/BreakFriend?firstUserId=%d&secondUserId=%d")
+ game:GetService("FriendService"):SetGetFriendsUrl(BaseURL .. "/Friend/AreFriends?userId=%d")
+ game:GetService("FriendService"):SetCreateFriendRequestUrl(BaseURL .. "/Friend/CreateFriendRequest?requesterUserId=%d&requestedUserId=%d")
+ game:GetService("FriendService"):SetDeleteFriendRequestUrl(BaseURL .. "/Friend/DeleteFriendRequest?requesterUserId=%d&requestedUserId=%d")
+
+ --pcall(function() loadfile(BaseURL .. "/Game/LoadPlaceInfo.ashx?PlaceId=" .. PlaceId)() end) idk what this is suppose to return
+end
+
+pcall(function()
+ game:GetService("NetworkServer"):SetIsPlayerAuthenticationRequired(true)
+end)
+settings().Diagnostics.LuaRamLimit = 0
+
+local StartTime = tick()
+local StoppingServer = false
+local function ReportServerPlayers(IgnoreThisPlayer)
+ --if StoppingServer then return end
+ local success, message = pcall(function()
+ local PlayerList = {}
+ for _, player in pairs(Players:GetChildren()) do
+ if player:IsA("Player") and player ~= IgnoreThisPlayer then
+ table.insert(PlayerList, {
+ ["UserId"] = player.UserId,
+ ["Name"] = player.Name
+ })
+ end
+ end
+ local MessagePayload = HttpService:JSONEncode({
+ ["AuthToken"] = AuthToken,
+ ["JobId"] = game.JobId,
+ ["Players"] = PlayerList
+ })
+ local ResponseData = game:HttpPost(BaseURL.."/internal/gameserver/reportplayers", MessagePayload, true, "application/json")
+ local ResponseJSON = HttpService:JSONDecode(ResponseData)
+ for _, player in pairs(ResponseJSON["bad"]) do -- This is a list of players that need to be kicked from the server
+ local TargetPlayer = Players:GetPlayerByUserId(player)
+ if TargetPlayer ~= nil then
+ TargetPlayer:Kick("There was an issue authenticating you, please contact support.")
+ TargetPlayer:Destroy()
+ end
+ end
+
+ end)
+end
+
+local function ReportServerStats()
+ if StoppingServer then return end
+ local success, message = pcall(function()
+ local MessagePayload = HttpService:JSONEncode({
+ ["AuthToken"] = AuthToken,
+ ["JobId"] = game.JobId,
+ ["PlaceId"] = PlaceId,
+ ["ServerAliveTime"] = (tick() - StartTime) + 1
+ })
+ game:HttpPost(BaseURL.."/internal/gameserver/reportstats", MessagePayload, false, "application/json")
+ end)
+end
+
+local function ReportServerShutdown()
+ local success, message = pcall(function()
+ local MessagePayload = HttpService:JSONEncode({
+ ["AuthToken"] = AuthToken,
+ ["JobId"] = game.JobId,
+ ["PlaceId"] = PlaceId,
+ ["ServerAliveTime"] = tick() - StartTime
+ })
+ game:HttpPost(BaseURL.."/internal/gameserver/reportshutdown", MessagePayload, false, "application/json")
+ end)
+end
+
+local function ShutdownServer()
+ StoppingServer = true
+ ReportServerShutdown()
+ ScriptContext.ScriptsDisabled = true
+ game:HttpPost("http://127.0.0.1:3000/CloseJob?RCCReturnAuth="..AuthToken, HttpService:JSONEncode({
+ ["jobid"] = game.JobId
+ }), false, "application/json")
+end
+
+if PlaceId ~= nil and BaseURL ~= nil then
+ wait()
+ local success, message = pcall(function()
+ game:Load(BaseURL.."/asset/?id="..tostring(PlaceId).."&access="..DownloadAuthorizationToken)
+ end)
+ if not success then
+ -- Report error
+ local MessagePayload = HttpService:JSONEncode({
+ ["AuthToken"] = AuthToken,
+ ["JobId"] = game.JobId,
+ ["PlaceId"] = PlaceId,
+ ["Error"] = message
+ })
+ game:HttpPost(BaseURL.."/internal/gameserver/reportfailure", MessagePayload, false, "application/json")
+ -- Lets start the server but with an empty place and a error message
+ local NewMessage = Instance.new("Message", workspace)
+ NewMessage.Text = "There was an error loading this place file, Error Message: "..message..", PlaceId: "..tostring(PlaceId)..", JobId: "..tostring(game.JobId)
+ end
+end
+
+NetworkServer:Start(Port)
+ScriptContext:SetTimeout(10)
+ScriptContext.ScriptsDisabled = false
+
+local TotalPlayersJoined = 0
+
+Players.PlayerAdded:Connect(function(player)
+ ReportServerPlayers()
+ TotalPlayersJoined = TotalPlayersJoined + 1
+ player.DataComplexityLimit = 1024 * 1024 * 1
+ player:LoadData()
+
+ coroutine.wrap(function()
+ while true do
+ wait(120)
+ if StoppingServer then break end
+ if player.Parent == nil then break end
+ player:SaveData()
+ end
+ end)()
+end)
+Players.PlayerRemoving:Connect(function(player)
+ ReportServerPlayers(player)
+ player:SaveData()
+ local PlayerCount = #Players:GetPlayers()
+ if PlayerCount == 0 then
+ wait(10) -- Wait 10 seconds to see if anyone rejoins
+ PlayerCount = #Players:GetPlayers()
+ if PlayerCount == 0 then
+ ShutdownServer()
+ end
+ end
+end)
+
+game:GetService("RunService"):Run()
+ReportServerStats()
+
+coroutine.wrap(function()
+ while true do
+ wait(20)
+ if StoppingServer then break end
+ ReportServerStats()
+ ReportServerPlayers()
+ end
+end)()
+
+coroutine.wrap(function()
+ wait(120) -- Wait 2 minutes to check if anyone has joined
+ if TotalPlayersJoined == 0 then
+ warn("Stopping server, no players joined past 2 minutes.")
+ ShutdownServer()
+ end
+end)()
\ No newline at end of file
diff --git a/app/files/AnimationThumbnail.png b/app/files/AnimationThumbnail.png
new file mode 100644
index 0000000..36aa431
Binary files /dev/null and b/app/files/AnimationThumbnail.png differ
diff --git a/app/files/AudioThumbnail.png b/app/files/AudioThumbnail.png
new file mode 100644
index 0000000..1f72a79
Binary files /dev/null and b/app/files/AudioThumbnail.png differ
diff --git a/app/files/Baseplate.rbxlx b/app/files/Baseplate.rbxlx
new file mode 100644
index 0000000..c760294
--- /dev/null
+++ b/app/files/Baseplate.rbxlx
@@ -0,0 +1,415 @@
+
+ null
+ nil
+
+
+ false
+ RBX1512699E848543EC96B3B41C9AAF63F2
+ 0
+ false
+ -500
+ true
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ Workspace
+ false
+ 1
+ null
+ false
+
+
+
+
+ 0
+ 18.7408524
+ 23.4260654
+ 1
+ 0
+ -0
+ -0
+ 0.707106829
+ 0.707106829
+ 0
+ -0.707106829
+ 0.707106829
+
+ null
+ 0
+ 70
+
+ 0
+ 17.3266392
+ 22.0118523
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ true
+ Camera
+
+
+
+
+ true
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 4
+ 0
+ 194
+
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ true
+
+ 4288914085
+
+ false
+
+ 0.300000012
+ 0.5
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ true
+ 256
+ Terrain
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+ AQU=
+ -0.5
+ 0.5
+ 3
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+ 4278998108
+ 0.300000012
+ 0.150000006
+ 10
+
+ 2044
+ 252
+ 2044
+
+
+
+
+
+ true
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 4
+ 0
+ 199
+
+ 0
+ -10
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+ 0
+ 0
+ 0
+ 1
+
+ true
+ 4284702562
+
+ false
+
+ 0.5
+ 0.300000012
+ -0.5
+ 0.5
+ 0
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+ true
+ 256
+ BasePlate
+ 0
+ -0.5
+ 0.5
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+ -0.5
+ 0.5
+ 3
+ 0
+ 0
+
+ 0
+ 0
+ 0
+
+ 0
+ 1
+
+ 512
+ 20
+ 512
+
+
+
+
+
+
+ NonReplicatedCSGDictionaryService
+
+
+
+
+ CSGDictionaryService
+
+
+
+
+ true
+ 12
+ Players
+ 7536745
+
+
+
+
+ ReplicatedFirst
+
+
+
+
+ true
+ 400
+ 0.5
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ true
+ 100
+ true
+ StarterPlayer
+ 100
+
+
+
+ StarterPlayerScripts
+
+
+
+
+
+ StarterPack
+
+
+
+
+ StarterGui
+ true
+ true
+
+
+
+
+ Teleport Service
+
+
+
+
+ 0
+ 10
+ 1
+ SoundService
+ 1
+
+
+
+
+ CollectionService
+
+
+
+
+ PhysicsService
+
+
+
+
+ Geometry
+
+
+
+
+ RenderHooksService
+
+
+
+
+ false
+ InsertService
+
+
+
+
+ SocialService
+
+
+
+
+ GamePassService
+
+
+
+
+ 1000
+ Debris
+
+
+
+
+ Instance
+
+
+
+
+ Instance
+
+
+
+
+ CookiesService
+
+
+
+
+ ContextActionService
+
+
+
+
+ Instance
+
+
+
+
+ AssetService
+
+
+
+
+ Selection
+
+
+
+
+ false
+ ServerScriptService
+
+
+
+
+ ServerStorage
+
+
+
+
+ ReplicatedStorage
+
+
+
+
+ Instance
+
+
+
+
+ 4278190080
+ 1
+ 14
+ 4278190080
+ 4278190080
+ 4290822336
+ 100000
+ 0
+ 41.7332993
+ true
+ Lighting
+ 4286611584
+ true
+ 4289967032
+ 14:00:00
+
+
+
+
+ false
+ HttpService
+
+
+
\ No newline at end of file
diff --git a/app/files/CoreGui/107893730 b/app/files/CoreGui/107893730
new file mode 100644
index 0000000..1773568
--- /dev/null
+++ b/app/files/CoreGui/107893730
@@ -0,0 +1,1107 @@
+-- this script creates the gui and sends the web requests for in game purchase prompts
+
+-- wait for important items to appear
+while not Game do
+ wait(0.1)
+end
+while not game:GetService("MarketplaceService") do
+ wait(0.1)
+end
+while not game:FindFirstChild("CoreGui") do
+ wait(0.1)
+end
+while not game.CoreGui:FindFirstChild("RobloxGui") do
+ wait(0.1)
+end
+
+-------------------------------- Global Variables ----------------------------------------
+-- utility variables
+local RbxUtility = nil
+local baseUrl = game:GetService("ContentProvider").BaseUrl:lower()
+baseUrl = string.gsub(baseUrl,"/m.","/www.") --mobile site does not work for this stuff!
+
+-- data variables
+local currentProductInfo, currentAssetId, currentCurrencyType, currentCurrencyAmount, currentEquipOnPurchase, currentProductId, currentServerResponseTable
+local checkingPlayerFunds = false
+local purchasingConsumable = false
+
+-- gui variables
+local currentlyPrompting = false
+local currentlyPurchasing = false
+local purchaseDialog, errorDialog = nil
+local tweenTime = 0.3
+local showPosition = UDim2.new(0.5,-217,0.5,-146)
+local hidePosition = UDim2.new(0.5,-217,1,25)
+local isSmallScreen = nil
+local spinning = false
+local spinnerIcons = nil
+local smallScreenThreshold = 450
+local renderSteppedConnection = nil
+
+local modalEnabledFix = false
+local success = pcall(function() modalEnabledFix = settings():GetFFlag("FixModalEnabledOniOS") end)
+if not success then
+ modalEnabledFix = false
+end
+
+-- user facing images
+local assetUrls = {}
+local assetUrl = "http://www.syntax.eco/Asset/?id="
+local errorImageUrl = assetUrl .. "42557901" table.insert(assetUrls, errorImageUrl)
+local buyImageUrl = assetUrl .. "142494143" table.insert(assetUrls,buyImageUrl)
+local cancelButtonImageUrl = assetUrl .. "142494219" table.insert(assetUrls, cancelButtonImageUrl)
+local freeButtonImageDownUrl = assetUrl .. "104651761" table.insert(assetUrls, freeButtonImageDownUrl)
+local loadingImage = assetUrl .. "143116791" table.insert(assetUrls,loadingImage)
+
+-- user facing string
+local buyHeaderText = "Buy Item"
+local takeHeaderText = "Take Item"
+local buyFailedHeaderText = "An Error Occurred"
+
+local errorPurchasesDisabledText = "In-game purchases are disabled"
+local errorPurchasesUnknownText = "Roblox is performing maintenance"
+
+local purchaseSucceededText = "Your purchase of itemName succeeded!"
+local purchaseFailedText = "Your purchase of itemName failed because errorReason. Your account has not been charged. Please try again soon."
+local productPurchaseText = "Would you like to buy the itemName assetType for currencyTypecurrencyAmount?"--"Would you like to buy the itemName assetType from assetOwner for currencyTypecurrencyAmount?"
+local productPurchaseTixOnlyText = "Would you like to buy the itemName assetType for currencyAmount currencyType?" --"Would you like to buy the itemName assetType from assetOwner for currencyAmount currencyType?"
+local freeItemPurchaseText = "Would you like to take the assetType itemName for FREE?"
+local freeItemBalanceText = "Your balance of Robux or Tix will not be affected by this transaction."
+local upgradeBCText = "You require an upgrade to your Builders Club membership to purchase this item. Click 'Buy Builders Club' to upgrade."
+-------------------------------- End Global Variables ----------------------------------------
+
+
+----------------------------- Util Functions ---------------------------------------------
+function getSecureApiBaseUrl()
+ local secureApiUrl = baseUrl
+ secureApiUrl = string.gsub(secureApiUrl,"http","https")
+ secureApiUrl = string.gsub(secureApiUrl,"www","api")
+ return secureApiUrl
+end
+
+function getRbxUtility()
+ if not RbxUtility then
+ RbxUtility = LoadLibrary("RbxUtility")
+ end
+ return RbxUtility
+end
+
+function preloadAssets()
+ for i = 1, #assetUrls do
+ game:GetService("ContentProvider"):Preload(assetUrls[i])
+ end
+end
+----------------------------- End Util Functions ---------------------------------------------
+
+
+-------------------------------- Accept/Decline Functions --------------------------------------
+function removeCurrentPurchaseInfo()
+ currentAssetId = nil
+ currentCurrencyType = nil
+ currentCurrencyAmount = nil
+ currentEquipOnPurchase = nil
+ currentProductId = nil
+ currentProductInfo = nil
+ currentServerResponseTable = nil
+
+ checkingPlayerFunds = false
+end
+
+function userPurchaseActionsEnded(isSuccess)
+ checkingPlayerFunds = false
+
+ purchaseDialog.BodyFrame.AfterBalanceText.Visible = false
+
+ if isSuccess then -- show the user we bought the item successfully, when they close this dialog we will call signalPromptEnded
+ local newPurchasedSucceededText = string.gsub( purchaseSucceededText,"itemName", tostring(currentProductInfo["Name"]))
+ purchaseDialog.BodyFrame.ItemDescription.Text = newPurchasedSucceededText
+ setButtonsVisible(purchaseDialog.BodyFrame.OkPurchasedButton)
+ hidePurchasing()
+ else -- otherwise we didn't purchase, no need to show anything, just signal and close dialog
+ signalPromptEnded(isSuccess)
+ end
+end
+
+function signalPromptEnded(isSuccess)
+ closePurchasePrompt()
+ if purchasingConsumable then
+ game:GetService("MarketplaceService"):SignalPromptProductPurchaseFinished(game.Players.LocalPlayer.userId, currentProductId, isSuccess)
+ else
+ game:GetService("MarketplaceService"):SignalPromptPurchaseFinished(game.Players.LocalPlayer, currentAssetId, isSuccess)
+ end
+ removeCurrentPurchaseInfo()
+end
+
+-- make sure our gui displays the proper purchase data, and set the productid we will try and buy if use specifies a buy action
+function updatePurchasePromptData(toggleColoredText)
+ local newItemDescription = ""
+
+ -- id to use when we request a purchase
+ if not currentProductId then
+ currentProductId = currentProductInfo["ProductId"]
+ end
+
+ if isFreeItem() then
+ newItemDescription = string.gsub( freeItemPurchaseText,"itemName", tostring(currentProductInfo["Name"]))
+ newItemDescription = string.gsub( newItemDescription,"assetType", tostring(assetTypeToString(currentProductInfo["AssetTypeId"])) )
+ setHeaderText(takeHeaderText)
+ else -- otherwise item costs something, so different prompt
+ local purchaseText = productPurchaseText
+ if currentProductIsTixOnly() then
+ purchaseText = productPurchaseTixOnlyText
+ end
+
+ newItemDescription = string.gsub( purchaseText,"itemName", tostring(currentProductInfo["Name"]))
+ newItemDescription = string.gsub( newItemDescription,"assetType", tostring(assetTypeToString(currentProductInfo["AssetTypeId"])) )
+ --newItemDescription = string.gsub( newItemDescription,"assetOwner", tostring(currentProductInfo["Creator"]["Name"]) )
+ newItemDescription = string.gsub( newItemDescription,"currencyType", tostring(currencyTypeToString(currentCurrencyType)) )
+ newItemDescription = string.gsub( newItemDescription,"currencyAmount", tostring(currentCurrencyAmount))
+ setHeaderText(buyHeaderText)
+ end
+
+ purchaseDialog.BodyFrame.ItemDescription.Text = newItemDescription
+
+ if purchasingConsumable then
+ purchaseDialog.BodyFrame.ItemPreview.Image = baseUrl .. "thumbs/asset.ashx?assetid=" .. tostring(currentProductInfo["IconImageAssetId"]) .. '&x=100&y=100&format=png'
+ else
+ purchaseDialog.BodyFrame.ItemPreview.Image = baseUrl .. "thumbs/asset.ashx?assetid=" .. tostring(currentAssetId) .. '&x=100&y=100&format=png'
+ end
+end
+
+function checkIfCanPurchase()
+ if checkingPlayerFunds then
+ local canPurchase, insufficientFunds, notRightBC = canPurchaseItem() -- check again to see if we can buy item
+ if not canPurchase or (insufficientFunds or notRightBC) then -- wait a bit and try a few more times
+ local retries = 1000
+ while retries > 0 and (insufficientFunds or notRightBC) and checkingPlayerFunds and canPurchase do
+ wait(1/10)
+ canPurchase, insufficientFunds, notRightBC = canPurchaseItem()
+ retries = retries - 1
+ end
+ end
+ if canPurchase and not insufficientFunds then
+ -- we can buy item! set our buttons up and we will exit this loop
+ setButtonsVisible(purchaseDialog.BodyFrame.BuyButton,purchaseDialog.BodyFrame.CancelButton, purchaseDialog.BodyFrame.AfterBalanceText)
+ end
+ end
+end
+
+function closePurchasePrompt()
+ purchaseDialog:TweenPosition(hidePosition, Enum.EasingDirection.Out, Enum.EasingStyle.Quad, tweenTime, true, function()
+ game.GuiService:RemoveCenterDialog(purchaseDialog)
+ hidePurchasing()
+
+ purchaseDialog.Visible = false
+ currentlyPrompting = false
+ currentlyPurchasing = false
+
+ if modalEnabledFix then
+ Game:GetService("UserInputService").ModalEnabled = false
+ end
+ end)
+end
+
+function showPurchasePrompt()
+ local canPurchase, insufficientFunds, notRightBC, override, descText = canPurchaseItem()
+
+ if canPurchase then
+ updatePurchasePromptData()
+
+ if override and descText then
+ purchaseDialog.BodyFrame.ItemDescription.Text = descText
+ purchaseDialog.BodyFrame.AfterBalanceText.Visible = false
+ end
+ game.GuiService:AddCenterDialog(purchaseDialog, Enum.CenterDialogType.ModalDialog,
+ --ShowFunction
+ function()
+ -- set the state for our buttons
+ purchaseDialog.Visible = true
+
+ if not currentlyPurchasing then
+ if isFreeItem() then
+ setButtonsVisible(purchaseDialog.BodyFrame.FreeButton, purchaseDialog.BodyFrame.CancelButton)
+ elseif notRightBC then
+ setButtonsVisible(purchaseDialog.BodyFrame.BuyBCButton, purchaseDialog.BodyFrame.CancelButton)
+ elseif insufficientFunds then
+ setButtonsVisible(purchaseDialog.BodyFrame.BuyRobuxButton, purchaseDialog.BodyFrame.CancelButton)
+ elseif override then
+ if currentProductIsTixOnly() then
+ purchaseDialog.BodyFrame.AfterBalanceText.Visible = true
+ end
+ setButtonsVisible(purchaseDialog.BodyFrame.BuyDisabledButton, purchaseDialog.BodyFrame.CancelButton)
+ else
+ setButtonsVisible(purchaseDialog.BodyFrame.BuyButton, purchaseDialog.BodyFrame.CancelButton)
+ end
+ end
+
+ if modalEnabledFix then
+ Game:GetService("UserInputService").ModalEnabled = true
+ end
+ purchaseDialog:TweenPosition(showPosition, Enum.EasingDirection.Out, Enum.EasingStyle.Quad, tweenTime, true)
+ end,
+ --HideFunction
+ function()
+ if modalEnabledFix then
+ Game:GetService("UserInputService").ModalEnabled = false
+ end
+ purchaseDialog.Visible = false
+ end)
+ else -- we failed in prompting a purchase, do a decline
+ doDeclinePurchase()
+ end
+end
+
+-- given an asset id, this function will grab that asset from the website, and return the first "Tool" object found inside it
+function getToolAssetID(assetID)
+ local newTool = game:GetService("InsertService"):LoadAsset(assetID)
+ if not newTool then return nil end
+
+ if newTool:IsA("Tool") then
+ return newTool
+ end
+
+ local toolChildren = newTool:GetChildren()
+ for i = 1, #toolChildren do
+ if toolChildren[i]:IsA("Tool") then
+ return toolChildren[i]
+ end
+ end
+ return nil
+end
+
+-- the user tried to purchase by clicking the purchase button, but something went wrong.
+-- let them know their account was not charged, and that they do not own the item yet.
+function purchaseFailed(inGamePurchasesDisabled)
+ local name = "Item"
+ if currentProductInfo then name = currentProductInfo["Name"] end
+
+ local newPurchasedFailedText = string.gsub( purchaseFailedText,"itemName", tostring(name))
+ if inGamePurchasesDisabled then
+ newPurchasedFailedText = string.gsub( newPurchasedFailedText,"errorReason", tostring(errorPurchasesDisabledText) )
+ else
+ newPurchasedFailedText = string.gsub( newPurchasedFailedText,"errorReason", tostring(errorPurchasesUnknownText) )
+ end
+
+ purchaseDialog.BodyFrame.ItemDescription.Text = newPurchasedFailedText
+ purchaseDialog.BodyFrame.ItemPreview.Image = errorImageUrl
+
+ setButtonsVisible(purchaseDialog.BodyFrame.OkButton)
+
+ setHeaderText(buyFailedHeaderText)
+
+ hidePurchasing()
+end
+
+-- user has specified they want to buy an item, now try to attempt to buy it for them
+function doAcceptPurchase(currencyPreferredByUser)
+ if currentlyPurchasing then return end
+ currentlyPurchasing = true
+
+ showPurchasing() -- shows a purchasing ui (shows spinner)
+
+ local startTime = tick()
+
+ -- http call to do the purchase
+ local response = "none"
+ local url = nil
+
+ -- consumables need to use a different url
+ if purchasingConsumable then
+ url = getSecureApiBaseUrl() .. "marketplace/submitpurchase?productId=" .. tostring(currentProductId) ..
+ "¤cyTypeId=" .. tostring(currencyEnumToInt(currentCurrencyType)) ..
+ "&expectedUnitPrice=" .. tostring(currentCurrencyAmount) ..
+ "&placeId=" .. tostring(Game.PlaceId)
+ local flagExists, flagValue = pcall(function() settings():GetFFlag("AddRequestIdToDeveloperProductPurchases") end)
+ if flagExists and flagValue then
+ local h = game:GetService("HttpService")
+ url = url .. "&requestId=" .. h:UrlEncode(h:GenerateGUID())
+ end
+ else
+ url = getSecureApiBaseUrl() .. "marketplace/purchase?productId=" .. tostring(currentProductId) ..
+ "¤cyTypeId=" .. tostring(currencyEnumToInt(currentCurrencyType)) ..
+ "&purchasePrice=" .. tostring(currentCurrencyAmount) ..
+ "&locationType=Game" .. "&locationId=" .. Game.PlaceId
+ end
+
+ local success, reason = ypcall(function()
+ response = game:HttpPostAsync(url, "RobloxPurchaseRequest")
+ end)
+
+ -- debug output for us (found in the logs from local)
+ print("doAcceptPurchase success from ypcall is ",success,"reason is",reason)
+
+ if (tick() - startTime) < 1 then
+ wait(1) -- allow the purchasing waiting dialog to at least be readable (otherwise it might flash, looks bad)...
+ end
+
+ -- check to make sure purchase actually happened on the web end
+ if response == "none" or response == nil or response == '' then
+ print("did not get a proper response from web on purchase of",currentAssetId,currentProductId)
+ purchaseFailed()
+ return
+ end
+
+ -- parse our response, decide how to react
+ response = getRbxUtility().DecodeJSON(response)
+
+ if response then
+ if response["success"] == false then
+ if response["status"] ~= "AlreadyOwned" then
+ print("web return response of fail on purchase of",currentAssetId,currentProductId)
+ purchaseFailed( (response["status"] == "EconomyDisabled") )
+ return
+ end
+ end
+ else
+ print("web return response of non parsable JSON on purchase of",currentAssetId)
+ purchaseFailed()
+ return
+ end
+
+ -- check to see if this item was bought, and if we want to equip it (also need to make sure the asset type was gear)
+ if currentEquipOnPurchase and success and currentAssetId and tonumber(currentProductInfo["AssetTypeId"]) == 19 then
+ local tool = getToolAssetID(tonumber(currentAssetId))
+ if tool then
+ tool.Parent = game.Players.LocalPlayer.Backpack
+ end
+ end
+
+ if purchasingConsumable then
+ if not response["receipt"] then
+ print("tried to buy productId, but no receipt returned. productId was",currentProductId)
+ purchaseFailed()
+ return
+ end
+ Game:GetService("MarketplaceService"):SignalClientPurchaseSuccess( tostring(response["receipt"]), game.Players.LocalPlayer.userId, currentProductId )
+ else
+ userPurchaseActionsEnded(success)
+ end
+end
+
+-- user pressed the cancel button, just remove all purchasing prompts
+function doDeclinePurchase()
+ if currentlyPurchasing then return end
+ userPurchaseActionsEnded(false)
+end
+-------------------------------- End Accept/Decline Functions --------------------------------------
+
+
+---------------------------------------------- Currency Functions ---------------------------------------------
+-- enums have no implicit conversion to numbers in lua, has to have a function to do this
+function currencyEnumToInt(currencyEnum)
+ if currencyEnum == Enum.CurrencyType.Robux or currencyEnum == Enum.CurrencyType.Default then
+ return 1
+ elseif currencyEnum == Enum.CurrencyType.Tix then
+ return 2
+ end
+end
+
+-- oi, this is ugly
+function assetTypeToString(assetType)
+ if assetType == 1 then return "Image"
+ elseif assetType == 2 then return "T-Shirt"
+ elseif assetType == 3 then return "Audio"
+ elseif assetType == 4 then return "Mesh"
+ elseif assetType == 5 then return "Lua"
+ elseif assetType == 6 then return "HTML"
+ elseif assetType == 7 then return "Text"
+ elseif assetType == 8 then return "Hat"
+ elseif assetType == 9 then return "Place"
+ elseif assetType == 10 then return "Model"
+ elseif assetType == 11 then return "Shirt"
+ elseif assetType == 12 then return "Pants"
+ elseif assetType == 13 then return "Decal"
+ elseif assetType == 16 then return "Avatar"
+ elseif assetType == 17 then return "Head"
+ elseif assetType == 18 then return "Face"
+ elseif assetType == 19 then return "Gear"
+ elseif assetType == 21 then return "Badge"
+ elseif assetType == 22 then return "Group Emblem"
+ elseif assetType == 24 then return "Animation"
+ elseif assetType == 25 then return "Arms"
+ elseif assetType == 26 then return "Legs"
+ elseif assetType == 27 then return "Torso"
+ elseif assetType == 28 then return "Right Arm"
+ elseif assetType == 29 then return "Left Arm"
+ elseif assetType == 30 then return "Left Leg"
+ elseif assetType == 31 then return "Right Leg"
+ elseif assetType == 32 then return "Package"
+ elseif assetType == 33 then return "YouTube Video"
+ elseif assetType == 34 then return "Game Pass"
+ elseif assetType == 0 then return "Product"
+ end
+
+ return ""
+end
+
+function currencyTypeToString(currencyType)
+ if currencyType == Enum.CurrencyType.Tix then
+ return "Tix"
+ else
+ return "R$"
+ end
+end
+
+-- figure out what currency to use based on the currency you can actually sell the item in and what the script specified
+function setCurrencyAmountAndType(priceInRobux, priceInTix)
+ if currentCurrencyType == Enum.CurrencyType.Default or currentCurrencyType == Enum.CurrencyType.Robux then -- sell for default (user doesn't care) or robux
+ if priceInRobux ~= nil and priceInRobux ~= 0 then -- we can sell for robux
+ currentCurrencyAmount = priceInRobux
+ currentCurrencyType = Enum.CurrencyType.Robux
+ else -- have to use tix
+ currentCurrencyAmount = priceInTix
+ currentCurrencyType = Enum.CurrencyType.Tix
+ end
+ elseif currentCurrencyType == Enum.CurrencyType.Tix then -- we want to sell for tix
+ if priceInTix ~= nil and priceInTix ~= 0 then -- we can sell for tix
+ currentCurrencyAmount = priceInTix
+ currentCurrencyType = Enum.CurrencyType.Tix
+ else -- have to use robux
+ currentCurrencyAmount = priceInRobux
+ currentCurrencyType = Enum.CurrencyType.Robux
+ end
+ else
+ return false
+ end
+
+ if currentCurrencyAmount == nil then
+ return false
+ end
+
+ return true
+end
+
+-- will get the player's balance of robux and tix, return in a table
+function getPlayerBalance()
+ local playerBalance = nil
+ local success, errorCode = ypcall(function() playerBalance = game:HttpGetAsync(getSecureApiBaseUrl() .. "currency/balance") end)
+ if not success then
+ print("Get player balance failed because",errorCode)
+ return nil
+ end
+
+ if playerBalance == '' then
+ return nil
+ end
+
+ playerBalance = getRbxUtility().DecodeJSON(playerBalance)
+
+ return playerBalance
+end
+
+-- should open an external default browser window to this url
+function openBuyCurrencyWindow()
+ checkingPlayerFunds = true
+ game:GetService("GuiService"):OpenBrowserWindow(baseUrl .. "Upgrades/Robux.aspx")
+end
+
+function openBCUpSellWindow()
+ checkingPlayerFunds = true
+ Game:GetService('GuiService'):OpenBrowserWindow(baseUrl .. "Upgrades/BuildersClubMemberships.aspx")
+end
+
+-- set up the gui text at the bottom of the prompt (alerts user to how much money they will have left, or if they need to buy more to buy the item)
+function updateAfterBalanceText(playerBalance, notRightBc)
+ if isFreeItem() then
+ purchaseDialog.BodyFrame.AfterBalanceText.Text = freeItemBalanceText
+ return true, false
+ end
+
+ local keyWord = nil
+ if currentCurrencyType == Enum.CurrencyType.Robux then
+ keyWord = "robux"
+ elseif currentCurrencyType == Enum.CurrencyType.Tix then
+ keyWord = "tickets"
+ end
+
+ if not keyWord then
+ return false
+ end
+
+ local playerBalanceNumber = tonumber(playerBalance[keyWord])
+ if not playerBalanceNumber then
+ return false
+ end
+
+ local afterBalanceNumber = playerBalanceNumber - currentCurrencyAmount
+
+ -- check to see if we have enough of the desired currency to allow a purchase, if not we need to prompt user to buy robux
+ if not notRightBc then
+ if afterBalanceNumber < 0 and keyWord == "robux" then
+ purchaseDialog.BodyFrame.AfterBalanceText.Text = "You need " .. currencyTypeToString(currentCurrencyType) .. tostring(-afterBalanceNumber) .. " more to buy this, click 'Buy R$' to purchase more."
+ return true, true
+ elseif afterBalanceNumber < 0 and keyWord == "tickets" then
+ purchaseDialog.BodyFrame.AfterBalanceText.Text = "You need " .. tostring(-afterBalanceNumber) .. " " .. currencyTypeToString(currentCurrencyType) .. " more to buy this item."
+ return true, true -- user can't buy more tickets, so we say fail the transaction (maybe instead we can prompt them to trade currency???)
+ end
+ else
+ purchaseDialog.BodyFrame.AfterBalanceText.Text = upgradeBCText
+ return true, false
+ end
+
+ if currentProductIsTixOnly() then
+ purchaseDialog.BodyFrame.AfterBalanceText.Text = "Your balance after this transaction will be " .. tostring(afterBalanceNumber) .. " " .. currencyTypeToString(currentCurrencyType) .. "."
+ else
+ purchaseDialog.BodyFrame.AfterBalanceText.Text = "Your balance after this transaction will be " .. currencyTypeToString(currentCurrencyType) .. tostring(afterBalanceNumber) .. "."
+ end
+ purchaseDialog.BodyFrame.AfterBalanceText.Visible = true
+ return true, false
+end
+
+function isFreeItem()
+ -- if both of these are true, then the item is free, just prompt user if they want to take one
+ return currentProductInfo and currentProductInfo["IsForSale"] == true and currentProductInfo["IsPublicDomain"] == true
+end
+---------------------------------------------- End Currency Functions ---------------------------------------------
+
+
+---------------------------------------------- Data Functions -----------------------------------------------------
+
+-- more enum to int fun!
+function membershipTypeToNumber(membership)
+ if membership == Enum.MembershipType.None then
+ return 0
+ elseif membership == Enum.MembershipType.BuildersClub then
+ return 1
+ elseif membership == Enum.MembershipType.TurboBuildersClub then
+ return 2
+ elseif membership == Enum.MembershipType.OutrageousBuildersClub then
+ return 3
+ end
+
+ return -1
+end
+
+function currentProductIsTixOnly()
+ local priceInRobux = currentProductInfo["PriceInRobux"]
+ local priceInTix = currentProductInfo["PriceInTickets"]
+
+ if priceInRobux == nil then return true end
+ priceInRobux = tonumber(priceInRobux)
+ if priceInRobux == nil then return true end
+
+ if priceInTix == nil then return false end
+ priceInTix = tonumber(priceInTix)
+ if priceInTix == nil then return false end
+
+ return (priceInRobux <= 0 and priceInTix > 0)
+end
+
+-- This functions checks to make sure the purchase is even possible, if not it returns false and we don't prompt user (some situations require user feedback when we won't prompt)
+function canPurchaseItem()
+
+ -- first we see if player already owns the asset/get the productinfo
+ local playerOwnsAsset = false
+ local notRightBc = false
+ local descText = nil
+
+ if purchasingConsumable then
+ currentProductInfo = game:GetService("MarketplaceService"):GetProductInfo(currentProductId, Enum.InfoType.Product)
+ else
+ currentProductInfo = game:GetService("MarketplaceService"):GetProductInfo(currentAssetId)
+ end
+
+ if currentProductInfo == nil then
+ descText = "In-game sales are temporarily disabled. Please try again later."
+ return true, nil, nil, true, descText
+ end
+
+ if not purchasingConsumable then
+ if not currentAssetId then
+ return false
+ end
+ if currentAssetId <= 0 then
+ return false
+ end
+
+ local success, errorCode = ypcall(function() playerOwnsAsset = game:HttpGetAsync(getSecureApiBaseUrl()
+ .. "ownership/hasAsset?userId="
+ .. tostring(game.Players.LocalPlayer.userId)
+ .. "&assetId=" .. tostring(currentAssetId))
+ end)
+
+ if not success then
+ return false
+ end
+
+ if playerOwnsAsset == true or playerOwnsAsset == "true" then
+ descText = "You already own this item."
+ return true, nil, nil, true, descText
+ end
+ end
+
+ purchaseDialog.BodyFrame.AfterBalanceText.Visible = true
+
+ -- next we parse through product info and see if we can purchase
+
+ if type(currentProductInfo) ~= "table" then
+ currentProductInfo = getRbxUtility().DecodeJSON(currentProductInfo)
+ end
+
+ if not currentProductInfo then
+ descText = "Could not get product info. Please try again later."
+ return true, nil, nil, true, descText
+ end
+
+ if currentProductInfo["IsForSale"] == false and currentProductInfo["IsPublicDomain"] == false then
+ descText = "This item is no longer for sale."
+ return true, nil, nil, true, descText
+ end
+
+ -- now we start talking money, making sure we are going to be able to purchase this
+ if not setCurrencyAmountAndType(tonumber(currentProductInfo["PriceInRobux"]), tonumber(currentProductInfo["PriceInTickets"])) then
+ descText = "We could retrieve the price of the item correctly. Please try again later."
+ return true, nil, nil, true, descText
+ end
+
+ local playerBalance = getPlayerBalance()
+ if not playerBalance then
+ descText = "Could not retrieve your balance. Please try again later."
+ return true, nil, nil, true, descText
+ end
+
+ if tonumber(currentProductInfo["MinimumMembershipLevel"]) > membershipTypeToNumber(game.Players.LocalPlayer.MembershipType) then
+ notRightBc = true
+ end
+
+ local updatedBalance, insufficientFunds = updateAfterBalanceText(playerBalance, notRightBc)
+
+ if notRightBc then
+ purchaseDialog.BodyFrame.AfterBalanceText.Active = true
+ return true, insufficientFunds, notRightBc, false
+ end
+
+ if currentProductInfo["ContentRatingTypeId"] == 1 then
+ if game.Players.LocalPlayer:GetUnder13() then
+ descText = "Your account is under 13 so purchase of this item is not allowed."
+ return true, nil, nil, true, descText
+ end
+ end
+
+ if (currentProductInfo["IsLimited"] == true or currentProductInfo["IsLimitedUnique"] == true) and
+ (currentProductInfo["Remaining"] == "" or currentProductInfo["Remaining"] == 0 or currentProductInfo["Remaining"] == nil) then
+ descText = "All copies of this item have been sold out! Try buying from other users on www.roblox.com."
+ return true, nil, nil, true, descText
+ end
+
+ if not updatedBalance then
+ descText = 'Could not update your balance. Please check back after some time.'
+ return true, nil, nil, true, descText
+ end
+
+ if insufficientFunds then
+ -- if this is a ticket only time and we don't have enough, tell the user to get more tix
+ if currentProductIsTixOnly() then
+ descText = "This item costs more tickets than you currently have! Try trading currency on www.roblox.com to get more tickets."
+ return true, nil, nil, true, descText
+ end
+ end
+
+ -- we use insufficient funds to display a prompt to buy more robux
+ return true, insufficientFunds
+end
+
+---------------------------------------------- End Data Functions -----------------------------------------------------
+
+
+---------------------------------------------- Gui Functions ----------------------------------------------
+function startSpinner()
+ renderSteppedConnection = Game:GetService("RunService").RenderStepped:connect(function()
+ purchaseDialog.PurchasingFrame.PurchasingSpinnerOuter.Rotation = purchaseDialog.PurchasingFrame.PurchasingSpinnerOuter.Rotation + 7
+ purchaseDialog.PurchasingFrame.PurchasingSpinnerInner.Rotation = purchaseDialog.PurchasingFrame.PurchasingSpinnerInner.Rotation - 9
+ end)
+end
+
+function stopSpinner()
+ if renderSteppedConnection then
+ renderSteppedConnection:disconnect()
+ renderSteppedConnection = nil
+ end
+end
+
+-- next two functions control the "Purchasing..." overlay
+function showPurchasing()
+ purchaseDialog.PurchasingFrame.Visible = true
+ startSpinner()
+end
+
+function hidePurchasing()
+ purchaseDialog.PurchasingFrame.Visible = false
+ stopSpinner()
+end
+
+-- convenience method to say exactly what buttons should be visible (all others are not!)
+function setButtonsVisible(...)
+ local args = {...}
+ local argCount = select('#', ...)
+
+ local bodyFrameChildren = purchaseDialog.BodyFrame:GetChildren()
+ for i = 1, #bodyFrameChildren do
+ if bodyFrameChildren[i]:IsA("GuiButton") then
+ bodyFrameChildren[i].Visible = false
+ for j = 1, argCount do
+ if bodyFrameChildren[i] == args[j] then
+ bodyFrameChildren[i].Visible = true
+ break
+ end
+ end
+ end
+ end
+end
+
+-- all the gui init. Would be nice if this didn't have to be a script
+function createPurchasePromptGui()
+ purchaseDialog = Instance.new("Frame")
+ purchaseDialog.Name = "PurchaseFrame"
+ purchaseDialog.Size = UDim2.new(0,435,0,292)
+ purchaseDialog.Position = hidePosition
+ purchaseDialog.Active = true
+ purchaseDialog.Visible = false
+ purchaseDialog.BackgroundColor3 = Color3.new(225/255,225/255,225/255)
+ purchaseDialog.BorderSizePixel = 0
+ purchaseDialog.Parent = game.CoreGui.RobloxGui
+
+ local bodyFrame = Instance.new("Frame")
+ bodyFrame.Name = "BodyFrame"
+ bodyFrame.Active = true
+ bodyFrame.Size = UDim2.new(1,-10,1,-55)
+ bodyFrame.Position = UDim2.new(0,5,0,50)
+ bodyFrame.BackgroundColor3 = Color3.new(1, 1, 1)
+ bodyFrame.BorderSizePixel = 0
+ bodyFrame.ZIndex = 8
+ bodyFrame.Parent = purchaseDialog
+
+ local titleLabel = createTextObject("TitleLabel", "Buy Item", "TextLabel", Enum.FontSize.Size36)
+ titleLabel.Active = true
+ titleLabel.Font = Enum.Font.SourceSansBold
+ titleLabel.TextColor3 = Color3.new(54/255,54/255,54/255)
+ titleLabel.ZIndex = 8
+ titleLabel.Size = UDim2.new(1,0,0,50)
+ titleLabel.Parent = purchaseDialog
+
+ local distanceBetweenButtons = 20
+
+ local cancelButton = createImageButton("CancelButton")
+ cancelButton.Position = UDim2.new(0.5,(distanceBetweenButtons/2),1,-100)
+ cancelButton.BorderColor3 = Color3.new(86/255,86/255,86/255)
+ cancelButton.Parent = bodyFrame
+ cancelButton.Modal = true
+ cancelButton.ZIndex = 8
+ cancelButton.Image = cancelButtonImageUrl
+ cancelButton.MouseButton1Up:connect(function( )
+ cancelButton.Image = cancelButtonImageUrl
+ end)
+ cancelButton.MouseLeave:connect(function( )
+ cancelButton.Image = cancelButtonImageUrl
+ end)
+ cancelButton.MouseButton1Click:connect(doDeclinePurchase)
+
+ local cancelText = createTextObject("CancelText","Cancel","TextLabel",Enum.FontSize.Size24)
+ cancelText.TextColor3 = Color3.new(1,1,1)
+ cancelText.Size = UDim2.new(1,0,1,0)
+ cancelText.ZIndex = 8
+ cancelText.Parent = cancelButton
+
+ local cancelHoverFrame = Instance.new("Frame")
+ cancelHoverFrame.Name = "HoverFrame"
+ cancelHoverFrame.Size = UDim2.new(1,0,1,0)
+ cancelHoverFrame.BackgroundColor3 = Color3.new(1,1,1)
+ cancelHoverFrame.BackgroundTransparency = 0.7
+ cancelHoverFrame.BorderSizePixel = 0
+ cancelHoverFrame.Visible = false
+ cancelHoverFrame.ZIndex = 8
+ cancelHoverFrame.Parent = cancelButton
+ cancelButton.MouseEnter:connect(function()
+ cancelHoverFrame.Visible = true
+ end)
+ cancelButton.MouseLeave:connect(function( )
+ cancelHoverFrame.Visible = false
+ end)
+ cancelButton.MouseButton1Click:connect(function( )
+ cancelHoverFrame.Visible = false
+ end)
+
+ local buyButton = createImageButton("BuyButton")
+ buyButton.Position = UDim2.new(0.5,-117-(distanceBetweenButtons/2),1,-100)
+ buyButton.BorderColor3 = Color3.new(0,112/255,1/255)
+ buyButton.Image = buyImageUrl
+ buyButton.ZIndex = 8
+ buyButton.Parent = bodyFrame
+
+ local buyText = createTextObject("BuyText","Buy Now","TextLabel",Enum.FontSize.Size24)
+ buyText.ZIndex = 8
+ buyText.TextColor3 = Color3.new(1,1,1)
+ buyText.Size = UDim2.new(1,0,1,0)
+ buyText.Parent = buyButton
+
+ local buyHoverFrame = cancelHoverFrame:Clone()
+ buyButton.MouseEnter:connect(function()
+ buyHoverFrame.Visible = true
+ end)
+ buyButton.MouseLeave:connect(function( )
+ buyHoverFrame.Visible = false
+ end)
+ buyButton.MouseButton1Click:connect(function( )
+ buyHoverFrame.Visible = false
+ end)
+ buyHoverFrame.Parent = buyButton
+
+ local buyDisabledButton = buyButton:Clone()
+ buyDisabledButton.Name = "BuyDisabledButton"
+ buyDisabledButton.AutoButtonColor = false
+ buyDisabledButton.Visible = false
+ buyDisabledButton.Active = false
+ buyDisabledButton.Parent = bodyFrame
+
+ local buyRobux = buyButton:Clone()
+ buyRobux.Name = "BuyRobuxButton"
+ buyRobux.AutoButtonColor = false
+ buyRobux.Visible = false
+ buyRobux.ZIndex = 8
+ buyRobux.BuyText.Text = "Buy R$"
+ buyRobux.MouseEnter:connect(function()
+ buyRobux.HoverFrame.Visible = true
+ end)
+ buyRobux.MouseLeave:connect(function( )
+ buyRobux.HoverFrame.Visible = false
+ end)
+ buyRobux.MouseButton1Click:connect(function( )
+ buyRobux.HoverFrame.Visible = false
+ openBuyCurrencyWindow()
+ end)
+ buyRobux.Parent = bodyFrame
+
+ local buyBC = buyRobux:Clone()
+ buyBC.Name = "BuyBCButton"
+ buyBC.BuyText.Text = "Buy Builders Club"
+ buyBC.MouseEnter:connect(function()
+ buyBC.HoverFrame.Visible = true
+ end)
+ buyBC.MouseLeave:connect(function( )
+ buyBC.HoverFrame.Visible = false
+ end)
+ buyBC.MouseButton1Click:connect(function( )
+ buyBC.HoverFrame.Visible = false
+ openBCUpSellWindow()
+ end)
+ buyBC.Parent = bodyFrame
+
+ local freeButton = buyButton:Clone()
+ freeButton.BuyText.Text = "Take Free"
+ freeButton.BackgroundTransparency = 1
+ freeButton.Name = "FreeButton"
+ freeButton.Visible = false
+ freeButton.MouseEnter:connect(function()
+ freeButton.HoverFrame.Visible = true
+ end)
+ freeButton.MouseButton1Click:connect(function( )
+ freeButton.HoverFrame.Visible = false
+ end)
+ freeButton.MouseLeave:connect(function( )
+ freeButton.HoverFrame.Visible = false
+ end)
+ freeButton.Parent = bodyFrame
+
+ local okButton = buyButton:Clone()
+ okButton.BuyText.Text = "Ok"
+ okButton.Name = "OkButton"
+ okButton.Visible = false
+ okButton.Position = UDim2.new(0.5,-okButton.Size.X.Offset/2,1,-100)
+ okButton.Modal = true
+ okButton.MouseEnter:connect(function()
+ okButton.HoverFrame.Visible = true
+ end)
+ okButton.MouseButton1Click:connect(function( )
+ okButton.HoverFrame.Visible = false
+ signalPromptEnded(false)
+ end)
+ okButton.MouseLeave:connect(function( )
+ okButton.HoverFrame.Visible = false
+ end)
+ okButton.Parent = bodyFrame
+
+ local okPurchasedButton = okButton:Clone()
+ okPurchasedButton.Name = "OkPurchasedButton"
+ okPurchasedButton.MouseEnter:connect(function()
+ okPurchasedButton.HoverFrame.Visible = true
+ end)
+ okPurchasedButton.MouseLeave:connect(function( )
+ okPurchasedButton.HoverFrame.Visible = false
+ end)
+ okPurchasedButton.MouseButton1Click:connect(function()
+ okPurchasedButton.HoverFrame.Visible = false
+ if purchasingConsumable then
+ userPurchaseProductActionsEnded(true)
+ else
+ signalPromptEnded(true)
+ end
+ end)
+ okPurchasedButton.Parent = bodyFrame
+
+ buyButton.MouseButton1Click:connect(function() doAcceptPurchase(Enum.CurrencyType.Robux) end)
+ freeButton.MouseButton1Click:connect(function() doAcceptPurchase(false) end)
+
+ local itemPreview = Instance.new("ImageLabel")
+ itemPreview.Name = "ItemPreview"
+ itemPreview.BackgroundTransparency = 1
+ itemPreview.BorderSizePixel = 0
+ itemPreview.Position = UDim2.new(0,20,0,20)
+ itemPreview.Size = UDim2.new(0,100,0,100)
+ itemPreview.ZIndex = 9
+ itemPreview.Parent = bodyFrame
+
+ local itemDescription = createTextObject("ItemDescription","","TextLabel",Enum.FontSize.Size18)
+ itemDescription.TextXAlignment = Enum.TextXAlignment.Left
+ itemDescription.Position = UDim2.new(0.5, -70, 0, 10)
+ itemDescription.Size = UDim2.new(0,245,0,115)
+ itemDescription.TextColor3 = Color3.new(54/255,54/255,54/255)
+ itemDescription.ZIndex = 8
+ itemDescription.Parent = bodyFrame
+
+ local afterBalanceText = createTextObject("AfterBalanceText","","TextLabel",Enum.FontSize.Size14)
+ afterBalanceText.BackgroundTransparency = 1
+ afterBalanceText.TextColor3 = Color3.new(102/255,102/255,102/255)
+ afterBalanceText.Position = UDim2.new(0,5,1,-33)
+ afterBalanceText.Size = UDim2.new(1,-10,0,28)
+ afterBalanceText.ZIndex = 8
+ afterBalanceText.Parent = bodyFrame
+
+ local purchasingFrame = Instance.new("Frame")
+ purchasingFrame.Name = "PurchasingFrame"
+ purchasingFrame.Size = UDim2.new(1,0,1,0)
+ purchasingFrame.BackgroundColor3 = Color3.new(0,0,0)
+ purchasingFrame.BackgroundTransparency = 0.05
+ purchasingFrame.BorderSizePixel = 0
+ purchasingFrame.ZIndex = 9
+ purchasingFrame.Visible = false
+ purchasingFrame.Active = true
+ purchasingFrame.Parent = purchaseDialog
+
+ local purchasingLabel = createTextObject("PurchasingLabel","Purchasing","TextLabel",Enum.FontSize.Size48)
+ purchasingLabel.Size = UDim2.new(1,0,1,0)
+ purchasingLabel.Position = UDim2.new(0,0,0,-24)
+ purchasingLabel.ZIndex = 10
+ purchasingLabel.Parent = purchasingFrame
+
+ local purchasingSpinner = Instance.new("ImageLabel")
+ purchasingSpinner.Name = "PurchasingSpinnerOuter"
+ purchasingSpinner.Image = loadingImage
+ purchasingSpinner.BackgroundTransparency = 1
+ purchasingSpinner.BorderSizePixel = 0
+ purchasingSpinner.Size = UDim2.new(0,64,0,64)
+ purchasingSpinner.Position = UDim2.new(0.5,-32,0.5,32)
+ purchasingSpinner.ZIndex = 10
+ purchasingSpinner.Parent = purchasingFrame
+
+ local purchasingSpinnerInner = purchasingSpinner:Clone()
+ purchasingSpinnerInner.BackgroundTransparency = 1
+ purchasingSpinnerInner.Name = "PurchasingSpinnerInner"
+ purchasingSpinnerInner.Size = UDim2.new(0,32,0,32)
+ purchasingSpinnerInner.Position = UDim2.new(0.5,-16,0.5,48)
+ purchasingSpinnerInner.Parent = purchasingFrame
+end
+
+-- next 2 functions are convenienvce creation functions for guis
+function createTextObject(name, text, type, size)
+ local textLabel = Instance.new(type)
+ textLabel.Font = Enum.Font.SourceSans
+ textLabel.TextColor3 = Color3.new(217/255, 217/255, 217/255)
+ textLabel.TextWrapped = true
+ textLabel.Name = name
+ textLabel.Text = text
+ textLabel.BackgroundTransparency = 1
+ textLabel.BorderSizePixel = 0
+ textLabel.FontSize = size
+
+ return textLabel
+end
+
+function createImageButton(name)
+ local imageButton = Instance.new("ImageButton")
+ imageButton.Size = UDim2.new(0,117,0,60)
+ imageButton.Name = name
+ return imageButton
+end
+
+function setHeaderText(text)
+ purchaseDialog.TitleLabel.Text = text
+end
+
+function doPurchasePrompt(player, assetId, equipIfPurchased, currencyType, productId)
+ if not purchaseDialog then
+ createPurchasePromptGui()
+ end
+
+ if player == game.Players.LocalPlayer then
+ if currentlyPrompting then return end
+
+ currentlyPrompting = true
+
+ currentAssetId = assetId
+ currentProductId = productId
+ currentCurrencyType = currencyType
+ currentEquipOnPurchase = equipIfPurchased
+
+ purchasingConsumable = (currentProductId ~= nil)
+
+ showPurchasePrompt()
+ end
+end
+
+function userPurchaseProductActionsEnded(userIsClosingDialog)
+ checkingPlayerFunds = false
+
+ if userIsClosingDialog then
+ closePurchasePrompt()
+ if currentServerResponseTable then
+ local isPurchased = false
+ if tostring(currentServerResponseTable["isValid"]):lower() == "true" then
+ isPurchased = true
+ end
+
+ Game:GetService("MarketplaceService"):SignalPromptProductPurchaseFinished(tonumber(currentServerResponseTable["playerId"]), tonumber(currentServerResponseTable["productId"]), isPurchased)
+ end
+ removeCurrentPurchaseInfo()
+ else
+ if tostring(currentServerResponseTable["isValid"]):lower() == "true" then
+ local newPurchasedSucceededText = string.gsub( purchaseSucceededText,"itemName", tostring(currentProductInfo["Name"]))
+ purchaseDialog.BodyFrame.ItemDescription.Text = newPurchasedSucceededText
+ setButtonsVisible(purchaseDialog.BodyFrame.OkPurchasedButton)
+ hidePurchasing()
+ else
+ purchaseFailed()
+ end
+ end
+end
+
+function doProcessServerPurchaseResponse(serverResponseTable)
+ if not serverResponseTable then
+ purchaseFailed()
+ return
+ end
+
+ if serverResponseTable["playerId"] and tonumber(serverResponseTable["playerId"]) == game.Players.LocalPlayer.userId then
+ currentServerResponseTable = serverResponseTable
+ userPurchaseProductActionsEnded(false)
+ end
+end
+
+---------------------------------------------- End Gui Functions ----------------------------------------------
+
+
+---------------------------------------------- Script Event start/initialization ----------------------------------------------
+preloadAssets()
+
+game:GetService("MarketplaceService").PromptProductPurchaseRequested:connect(function(player, productId, equipIfPurchased, currencyType)
+ doPurchasePrompt(player, nil, equipIfPurchased, currencyType, productId)
+end)
+
+Game:GetService("MarketplaceService").PromptPurchaseRequested:connect(function(player, assetId, equipIfPurchased, currencyType)
+ doPurchasePrompt(player, assetId, equipIfPurchased, currencyType, nil)
+end)
+
+Game:GetService("MarketplaceService").ServerPurchaseVerification:connect(function(serverResponseTable)
+ doProcessServerPurchaseResponse(serverResponseTable)
+end)
+
+Game:GetService("GuiService").BrowserWindowClosed:connect(checkIfCanPurchase)
diff --git a/app/files/CoreGui/152908679 b/app/files/CoreGui/152908679
new file mode 100644
index 0000000..f574e4c
--- /dev/null
+++ b/app/files/CoreGui/152908679
@@ -0,0 +1,264 @@
+-- ContextActionTouch.lua
+-- Copyright ROBLOX 2014, created by Ben Tkacheff
+-- this script controls ui and firing of lua functions that are bound in ContextActionService for touch inputs
+-- Essentially a user can bind a lua function to a key code, input type (mousebutton1 etc.) and this
+
+-- Variables
+local contextActionService = Game:GetService("ContextActionService")
+local isTouchDevice = Game:GetService("UserInputService").TouchEnabled
+local functionTable = {}
+local buttonVector = {}
+local buttonScreenGui = nil
+local buttonFrame = nil
+
+local ContextDownImage = "http://www.syntax.eco/asset/?id=97166756"
+local ContextUpImage = "http://www.syntax.eco/asset/?id=97166444"
+
+local oldTouches = {}
+
+local buttonPositionTable = {
+ [1] = UDim2.new(0,123,0,70),
+ [2] = UDim2.new(0,30,0,60),
+ [3] = UDim2.new(0,180,0,160),
+ [4] = UDim2.new(0,85,0,-25),
+ [5] = UDim2.new(0,185,0,-25),
+ [6] = UDim2.new(0,185,0,260),
+ [7] = UDim2.new(0,216,0,65)
+ }
+local maxButtons = #buttonPositionTable
+
+-- Preload images
+Game:GetService("ContentProvider"):Preload(ContextDownImage)
+Game:GetService("ContentProvider"):Preload(ContextUpImage)
+
+while not Game.Players do
+ wait()
+end
+
+while not Game.Players.LocalPlayer do
+ wait()
+end
+
+function createContextActionGui()
+ if not buttonScreenGui and isTouchDevice then
+ buttonScreenGui = Instance.new("ScreenGui")
+ buttonScreenGui.Name = "ContextActionGui"
+
+ buttonFrame = Instance.new("Frame")
+ buttonFrame.BackgroundTransparency = 1
+ buttonFrame.Size = UDim2.new(0.3,0,0.5,0)
+ buttonFrame.Position = UDim2.new(0.7,0,0.5,0)
+ buttonFrame.Name = "ContextButtonFrame"
+ buttonFrame.Parent = buttonScreenGui
+ end
+end
+
+-- functions
+function setButtonSizeAndPosition(object)
+ local buttonSize = 55
+ local xOffset = 10
+ local yOffset = 95
+
+ -- todo: better way to determine mobile sized screens
+ local onSmallScreen = (game.CoreGui.RobloxGui.AbsoluteSize.X < 600)
+ if not onSmallScreen then
+ buttonSize = 85
+ xOffset = 40
+ end
+
+ object.Size = UDim2.new(0,buttonSize,0,buttonSize)
+end
+
+function contextButtonDown(button, inputObject, actionName)
+ if inputObject.UserInputType == Enum.UserInputType.Touch then
+ button.Image = ContextDownImage
+ contextActionService:CallFunction(actionName, Enum.UserInputState.Begin, inputObject)
+ end
+end
+
+function contextButtonMoved(button, inputObject, actionName)
+ if inputObject.UserInputType == Enum.UserInputType.Touch then
+ button.Image = ContextDownImage
+ contextActionService:CallFunction(actionName, Enum.UserInputState.Change, inputObject)
+ end
+end
+
+function contextButtonUp(button, inputObject, actionName)
+ button.Image = ContextUpImage
+ if inputObject.UserInputType == Enum.UserInputType.Touch and inputObject.UserInputState == Enum.UserInputState.End then
+ contextActionService:CallFunction(actionName, Enum.UserInputState.End, inputObject)
+ end
+end
+
+function isSmallScreenDevice()
+ return Game:GetService("GuiService"):GetScreenResolution().y <= 320
+end
+
+
+function createNewButton(actionName, functionInfoTable)
+ local contextButton = Instance.new("ImageButton")
+ contextButton.Name = "ContextActionButton"
+ contextButton.BackgroundTransparency = 1
+ contextButton.Size = UDim2.new(0,90,0,90)
+ contextButton.Active = true
+ if isSmallScreenDevice() then
+ contextButton.Size = UDim2.new(0,70,0,70)
+ end
+ contextButton.Image = ContextUpImage
+ contextButton.Parent = buttonFrame
+
+ local currentButtonTouch = nil
+
+ Game:GetService("UserInputService").InputEnded:connect(function ( inputObject )
+ oldTouches[inputObject] = nil
+ end)
+ contextButton.InputBegan:connect(function(inputObject)
+ if oldTouches[inputObject] then return end
+
+ if inputObject.UserInputState == Enum.UserInputState.Begin and currentButtonTouch == nil then
+ currentButtonTouch = inputObject
+ contextButtonDown(contextButton, inputObject, actionName)
+ end
+ end)
+ contextButton.InputChanged:connect(function(inputObject)
+ if oldTouches[inputObject] then return end
+ if currentButtonTouch ~= inputObject then return end
+
+ contextButtonMoved(contextButton, inputObject, actionName)
+ end)
+ contextButton.InputEnded:connect(function(inputObject)
+ if oldTouches[inputObject] then return end
+ if currentButtonTouch ~= inputObject then return end
+
+ currentButtonTouch = nil
+ oldTouches[inputObject] = true
+ contextButtonUp(contextButton, inputObject, actionName)
+ end)
+
+ local actionIcon = Instance.new("ImageLabel")
+ actionIcon.Name = "ActionIcon"
+ actionIcon.Position = UDim2.new(0.175, 0, 0.175, 0)
+ actionIcon.Size = UDim2.new(0.65, 0, 0.65, 0)
+ actionIcon.BackgroundTransparency = 1
+ if functionInfoTable["image"] and type(functionInfoTable["image"]) == "string" then
+ actionIcon.Image = functionInfoTable["image"]
+ end
+ actionIcon.Parent = contextButton
+
+ local actionTitle = Instance.new("TextLabel")
+ actionTitle.Name = "ActionTitle"
+ actionTitle.Size = UDim2.new(1,0,1,0)
+ actionTitle.BackgroundTransparency = 1
+ actionTitle.Font = Enum.Font.SourceSansBold
+ actionTitle.TextColor3 = Color3.new(1,1,1)
+ actionTitle.TextStrokeTransparency = 0
+ actionTitle.FontSize = Enum.FontSize.Size18
+ actionTitle.TextWrapped = true
+ actionTitle.Text = ""
+ if functionInfoTable["title"] and type(functionInfoTable["title"]) == "string" then
+ actionTitle.Text = functionInfoTable["title"]
+ end
+ actionTitle.Parent = contextButton
+
+ return contextButton
+end
+
+function createButton( actionName, functionInfoTable )
+ local button = createNewButton(actionName, functionInfoTable)
+
+ local position = nil
+ for i = 1,#buttonVector do
+ if buttonVector[i] == "empty" then
+ position = i
+ break
+ end
+ end
+
+ if not position then
+ position = #buttonVector + 1
+ end
+
+ if position > maxButtons then
+ return -- todo: let user know we have too many buttons already?
+ end
+
+ buttonVector[position] = button
+ functionTable[actionName]["button"] = button
+
+ button.Position = buttonPositionTable[position]
+ button.Parent = buttonFrame
+
+ if buttonScreenGui and buttonScreenGui.Parent == nil then
+ buttonScreenGui.Parent = Game.Players.LocalPlayer.PlayerGui
+ end
+end
+
+function removeAction(actionName)
+ if not functionTable[actionName] then return end
+
+ local actionButton = functionTable[actionName]["button"]
+
+ if actionButton then
+ actionButton.Parent = nil
+
+ for i = 1,#buttonVector do
+ if buttonVector[i] == actionButton then
+ buttonVector[i] = "empty"
+ break
+ end
+ end
+
+ actionButton:Destroy()
+ end
+
+ functionTable[actionName] = nil
+end
+
+function addAction(actionName,createTouchButton,functionInfoTable)
+ if functionTable[actionName] then
+ removeAction(actionName)
+ end
+ functionTable[actionName] = {functionInfoTable}
+ if createTouchButton and isTouchDevice then
+ createContextActionGui()
+ createButton(actionName, functionInfoTable)
+ end
+end
+
+-- Connections
+contextActionService.BoundActionChanged:connect( function(actionName, changeName, changeTable)
+ if functionTable[actionName] and changeTable then
+ local button = functionTable[actionName]["button"]
+ if button then
+ if changeName == "image" then
+ button.ActionIcon.Image = changeTable[changeName]
+ elseif changeName == "title" then
+ button.ActionTitle.Text = changeTable[changeName]
+ elseif changeName == "description" then
+ -- todo: add description to menu
+ elseif changeName == "position" then
+ button.Position = changeTable[changeName]
+ end
+ end
+ end
+end)
+
+contextActionService.BoundActionAdded:connect( function(actionName, createTouchButton, functionInfoTable)
+ addAction(actionName, createTouchButton, functionInfoTable)
+end)
+
+contextActionService.BoundActionRemoved:connect( function(actionName, functionInfoTable)
+ removeAction(actionName)
+end)
+
+contextActionService.GetActionButtonEvent:connect( function(actionName)
+ if functionTable[actionName] then
+ contextActionService:FireActionButtonFoundSignal(actionName, functionTable[actionName]["button"])
+ end
+end)
+
+-- make sure any bound data before we setup connections is handled
+local boundActions = contextActionService:GetAllBoundActionInfo()
+for actionName, actionData in pairs(boundActions) do
+ addAction(actionName,actionData["createTouchButton"],actionData)
+end
diff --git a/app/files/CoreGui/153556783 b/app/files/CoreGui/153556783
new file mode 100644
index 0000000..03b2218
--- /dev/null
+++ b/app/files/CoreGui/153556783
@@ -0,0 +1,564 @@
+-- This is responsible for all touch controls we show (as of this writing, only on iOS)
+-- this includes character move thumbsticks, and buttons for jump, use of items, camera, etc.
+-- Written by Ben Tkacheff, Copyright Roblox 2013
+
+-- obligatory stuff to make sure we don't access nil data
+while not Game do
+ wait()
+end
+while not Game:FindFirstChild("Players") do
+ wait()
+end
+while not Game.Players.LocalPlayer do
+ wait()
+end
+while not Game:FindFirstChild("CoreGui") do
+ wait()
+end
+while not Game.CoreGui:FindFirstChild("RobloxGui") do
+ wait()
+end
+
+local userInputService = Game:GetService("UserInputService")
+local success = pcall(function() userInputService:IsLuaTouchControls() end)
+if not success then
+ script:Destroy()
+end
+
+----------------------------------------------------------------------------
+----------------------------------------------------------------------------
+-- Variables
+local screenResolution = Game:GetService("GuiService"):GetScreenResolution()
+function isSmallScreenDevice()
+ return screenResolution.y <= 500
+end
+
+local localPlayer = Game.Players.LocalPlayer
+local thumbstickInactiveAlpha = 0.3
+local thumbstickSize = 120
+if isSmallScreenDevice() then
+ thumbstickSize = 70
+end
+
+local touchControlsSheet = "rbxasset://textures/ui/TouchControlsSheet.png"
+local ThumbstickDeadZone = 5
+local ThumbstickMaxPercentGive = 0.92
+local thumbstickTouches = {}
+
+local jumpButtonSize = 90
+if isSmallScreenDevice() then
+ jumpButtonSize = 70
+end
+local oldJumpTouches = {}
+local currentJumpTouch = nil
+
+local CameraRotateSensitivity = 0.007
+local CameraRotateDeadZone = CameraRotateSensitivity * 16
+local CameraZoomSensitivity = 0.03
+local PinchZoomDelay = 0.2
+local cameraTouch = nil
+
+
+-- make sure all of our images are good to go
+Game:GetService("ContentProvider"):Preload(touchControlsSheet)
+
+----------------------------------------------------------------------------
+----------------------------------------------------------------------------
+-- Functions
+
+function DistanceBetweenTwoPoints(point1, point2)
+ local dx = point2.x - point1.x
+ local dy = point2.y - point1.y
+ return math.sqrt( (dx*dx) + (dy*dy) )
+end
+
+function transformFromCenterToTopLeft(pointToTranslate, guiObject)
+ return UDim2.new(0,pointToTranslate.x - guiObject.AbsoluteSize.x/2,0,pointToTranslate.y - guiObject.AbsoluteSize.y/2)
+end
+
+function rotatePointAboutLocation(pointToRotate, pointToRotateAbout, radians)
+ local sinAnglePercent = math.sin(radians)
+ local cosAnglePercent = math.cos(radians)
+
+ local transformedPoint = pointToRotate
+
+ -- translate point back to origin:
+ transformedPoint = Vector2.new(transformedPoint.x - pointToRotateAbout.x, transformedPoint.y - pointToRotateAbout.y)
+
+ -- rotate point
+ local xNew = transformedPoint.x * cosAnglePercent - transformedPoint.y * sinAnglePercent
+ local yNew = transformedPoint.x * sinAnglePercent + transformedPoint.y * cosAnglePercent
+
+ -- translate point back:
+ transformedPoint = Vector2.new(xNew + pointToRotateAbout.x, yNew + pointToRotateAbout.y)
+
+ return transformedPoint
+end
+
+function dotProduct(v1,v2)
+ return ((v1.x*v2.x) + (v1.y*v2.y))
+end
+
+function stationaryThumbstickTouchMove(thumbstickFrame, thumbstickOuter, touchLocation)
+ local thumbstickOuterCenterPosition = Vector2.new(thumbstickOuter.Position.X.Offset + thumbstickOuter.AbsoluteSize.x/2, thumbstickOuter.Position.Y.Offset + thumbstickOuter.AbsoluteSize.y/2)
+ local centerDiff = DistanceBetweenTwoPoints(touchLocation, thumbstickOuterCenterPosition)
+
+ -- thumbstick is moving outside our region, need to cap its distance
+ if centerDiff > (thumbstickSize/2) then
+ local thumbVector = Vector2.new(touchLocation.x - thumbstickOuterCenterPosition.x,touchLocation.y - thumbstickOuterCenterPosition.y);
+ local normal = thumbVector.unit
+ if normal.x == math.nan or normal.x == math.inf then
+ normal = Vector2.new(0,normal.y)
+ end
+ if normal.y == math.nan or normal.y == math.inf then
+ normal = Vector2.new(normal.x,0)
+ end
+
+ local newThumbstickInnerPosition = thumbstickOuterCenterPosition + (normal * (thumbstickSize/2))
+ thumbstickFrame.Position = transformFromCenterToTopLeft(newThumbstickInnerPosition, thumbstickFrame)
+ else
+ thumbstickFrame.Position = transformFromCenterToTopLeft(touchLocation,thumbstickFrame)
+ end
+
+ return Vector2.new(thumbstickFrame.Position.X.Offset - thumbstickOuter.Position.X.Offset,thumbstickFrame.Position.Y.Offset - thumbstickOuter.Position.Y.Offset)
+end
+
+function followThumbstickTouchMove(thumbstickFrame, thumbstickOuter, touchLocation)
+ local thumbstickOuterCenter = Vector2.new(thumbstickOuter.Position.X.Offset + thumbstickOuter.AbsoluteSize.x/2, thumbstickOuter.Position.Y.Offset + thumbstickOuter.AbsoluteSize.y/2)
+
+ -- thumbstick is moving outside our region, need to position outer thumbstick texture carefully (to make look and feel like actual joystick controller)
+ if DistanceBetweenTwoPoints(touchLocation, thumbstickOuterCenter) > thumbstickSize/2 then
+ local thumbstickInnerCenter = Vector2.new(thumbstickFrame.Position.X.Offset + thumbstickFrame.AbsoluteSize.x/2, thumbstickFrame.Position.Y.Offset + thumbstickFrame.AbsoluteSize.y/2)
+ local movementVectorUnit = Vector2.new(touchLocation.x - thumbstickInnerCenter.x, touchLocation.y - thumbstickInnerCenter.y).unit
+
+ local outerToInnerVectorCurrent = Vector2.new(thumbstickInnerCenter.x - thumbstickOuterCenter.x, thumbstickInnerCenter.y - thumbstickOuterCenter.y)
+ local outerToInnerVectorCurrentUnit = outerToInnerVectorCurrent.unit
+ local movementVector = Vector2.new(touchLocation.x - thumbstickInnerCenter.x, touchLocation.y - thumbstickInnerCenter.y)
+
+ -- First, find the angle between the new thumbstick movement vector,
+ -- and the vector between thumbstick inner and thumbstick outer.
+ -- We will use this to pivot thumbstick outer around thumbstick inner, gives a nice joystick feel
+ local crossOuterToInnerWithMovement = (outerToInnerVectorCurrentUnit.x * movementVectorUnit.y) - (outerToInnerVectorCurrentUnit.y * movementVectorUnit.x)
+ local angle = math.atan2(crossOuterToInnerWithMovement, dotProduct(outerToInnerVectorCurrentUnit, movementVectorUnit))
+ local anglePercent = angle * math.min( (movementVector.magnitude)/(outerToInnerVectorCurrent.magnitude), 1.0);
+
+ -- If angle is significant, rotate about the inner thumbsticks current center
+ if math.abs(anglePercent) > 0.00001 then
+ local outerThumbCenter = rotatePointAboutLocation(thumbstickOuterCenter, thumbstickInnerCenter, anglePercent)
+ thumbstickOuter.Position = transformFromCenterToTopLeft(Vector2.new(outerThumbCenter.x,outerThumbCenter.y), thumbstickOuter)
+ end
+
+ -- now just translate outer thumbstick to make sure it stays nears inner thumbstick
+ thumbstickOuter.Position = UDim2.new(0,thumbstickOuter.Position.X.Offset+movementVector.x,0,thumbstickOuter.Position.Y.Offset+movementVector.y)
+ end
+
+ thumbstickFrame.Position = transformFromCenterToTopLeft(touchLocation,thumbstickFrame)
+
+ -- a bit of error checking to make sure thumbsticks stay close to eachother
+ thumbstickFramePosition = Vector2.new(thumbstickFrame.Position.X.Offset,thumbstickFrame.Position.Y.Offset)
+ thumbstickOuterPosition = Vector2.new(thumbstickOuter.Position.X.Offset,thumbstickOuter.Position.Y.Offset)
+ if DistanceBetweenTwoPoints(thumbstickFramePosition, thumbstickOuterPosition) > thumbstickSize/2 then
+ local vectorWithLength = (thumbstickOuterPosition - thumbstickFramePosition).unit * thumbstickSize/2
+ thumbstickOuter.Position = UDim2.new(0,thumbstickFramePosition.x + vectorWithLength.x,0,thumbstickFramePosition.y + vectorWithLength.y)
+ end
+
+ return Vector2.new(thumbstickFrame.Position.X.Offset - thumbstickOuter.Position.X.Offset,thumbstickFrame.Position.Y.Offset - thumbstickOuter.Position.Y.Offset)
+end
+
+function movementOutsideDeadZone(movementVector)
+ return ( (math.abs(movementVector.x) > ThumbstickDeadZone) or (math.abs(movementVector.y) > ThumbstickDeadZone) )
+end
+
+function constructThumbstick(defaultThumbstickPos, updateFunction, stationaryThumbstick)
+ local thumbstickFrame = Instance.new("Frame")
+ thumbstickFrame.Name = "ThumbstickFrame"
+ thumbstickFrame.Active = true
+ thumbstickFrame.Size = UDim2.new(0,thumbstickSize,0,thumbstickSize)
+ thumbstickFrame.Position = defaultThumbstickPos
+ thumbstickFrame.BackgroundTransparency = 1
+
+ local outerThumbstick = Instance.new("ImageLabel")
+ outerThumbstick.Name = "OuterThumbstick"
+ outerThumbstick.Image = touchControlsSheet
+ outerThumbstick.ImageRectOffset = Vector2.new(0,0)
+ outerThumbstick.ImageRectSize = Vector2.new(220,220)
+ outerThumbstick.BackgroundTransparency = 1
+ outerThumbstick.Size = UDim2.new(0,thumbstickSize,0,thumbstickSize)
+ outerThumbstick.Position = defaultThumbstickPos
+ outerThumbstick.Parent = Game.CoreGui.RobloxGui
+
+ local innerThumbstick = Instance.new("ImageLabel")
+ innerThumbstick.Name = "InnerThumbstick"
+ innerThumbstick.Image = touchControlsSheet
+ innerThumbstick.ImageRectOffset = Vector2.new(220,0)
+ innerThumbstick.ImageRectSize = Vector2.new(111,111)
+ innerThumbstick.BackgroundTransparency = 1
+ innerThumbstick.Size = UDim2.new(0,thumbstickSize/2,0,thumbstickSize/2)
+ innerThumbstick.Position = UDim2.new(0, thumbstickFrame.Size.X.Offset/2 - thumbstickSize/4, 0, thumbstickFrame.Size.Y.Offset/2 - thumbstickSize/4)
+ innerThumbstick.Parent = thumbstickFrame
+ innerThumbstick.ZIndex = 2
+
+ local thumbstickTouch = nil
+ local userInputServiceTouchMovedCon = nil
+ local userInputSeviceTouchEndedCon = nil
+
+ local startInputTracking = function(inputObject)
+ if thumbstickTouch then return end
+ if inputObject == cameraTouch then return end
+ if inputObject == currentJumpTouch then return end
+ if inputObject.UserInputType ~= Enum.UserInputType.Touch then return end
+
+ thumbstickTouch = inputObject
+ table.insert(thumbstickTouches,thumbstickTouch)
+
+ thumbstickFrame.Position = transformFromCenterToTopLeft(thumbstickTouch.Position,thumbstickFrame)
+ outerThumbstick.Position = thumbstickFrame.Position
+
+ userInputServiceTouchMovedCon = userInputService.TouchMoved:connect(function(movedInput)
+ if movedInput == thumbstickTouch then
+ local movementVector = nil
+ if stationaryThumbstick then
+ movementVector = stationaryThumbstickTouchMove(thumbstickFrame,outerThumbstick,Vector2.new(movedInput.Position.x,movedInput.Position.y))
+ else
+ movementVector = followThumbstickTouchMove(thumbstickFrame,outerThumbstick,Vector2.new(movedInput.Position.x,movedInput.Position.y))
+ end
+
+ if updateFunction then
+ updateFunction(movementVector,outerThumbstick.Size.X.Offset/2)
+ end
+ end
+ end)
+ userInputSeviceTouchEndedCon = userInputService.TouchEnded:connect(function(endedInput)
+ if endedInput == thumbstickTouch then
+ if updateFunction then
+ updateFunction(Vector2.new(0,0),1)
+ end
+
+ userInputSeviceTouchEndedCon:disconnect()
+ userInputServiceTouchMovedCon:disconnect()
+
+ thumbstickFrame.Position = defaultThumbstickPos
+ outerThumbstick.Position = defaultThumbstickPos
+
+ for i, object in pairs(thumbstickTouches) do
+ if object == thumbstickTouch then
+ table.remove(thumbstickTouches,i)
+ break
+ end
+ end
+ thumbstickTouch = nil
+ end
+ end)
+ end
+
+ userInputService.Changed:connect(function(prop)
+ if prop == "ModalEnabled" then
+ thumbstickFrame.Visible = not userInputService.ModalEnabled
+ outerThumbstick.Visible = not userInputService.ModalEnabled
+ end
+ end)
+
+ thumbstickFrame.InputBegan:connect(startInputTracking)
+ return thumbstickFrame
+end
+
+function setupCharacterMovement( parentFrame )
+ local lastMovementVector, lastMaxMovement = nil
+ local moveCharacterFunc = localPlayer.MoveCharacter
+ local moveCharacterFunction = function ( movementVector, maxMovement )
+ if localPlayer then
+ if movementOutsideDeadZone(movementVector) then
+ lastMovementVector = movementVector
+ lastMaxMovement = maxMovement
+ -- sometimes rounding error will not allow us to go max speed at some
+ -- thumbstick angles, fix this with a bit of fudging near 100% throttle
+ if movementVector.magnitude/maxMovement > ThumbstickMaxPercentGive then
+ maxMovement = movementVector.magnitude - 1
+ end
+ moveCharacterFunc(localPlayer, movementVector, maxMovement)
+ else
+ lastMovementVector = Vector2.new(0,0)
+ lastMaxMovement = 1
+ moveCharacterFunc(localPlayer, lastMovementVector, lastMaxMovement)
+ end
+ end
+ end
+
+ local thumbstickPos = UDim2.new(0,thumbstickSize/2,1,-thumbstickSize*1.75)
+ if isSmallScreenDevice() then
+ thumbstickPos = UDim2.new(0,(thumbstickSize/2) - 10,1,-thumbstickSize - 20)
+ end
+ local characterThumbstick = constructThumbstick(thumbstickPos, moveCharacterFunction, false)
+ characterThumbstick.Name = "CharacterThumbstick"
+ characterThumbstick.Parent = parentFrame
+
+ local refreshCharacterMovement = function()
+ if localPlayer and moveCharacterFunc and lastMovementVector and lastMaxMovement then
+ moveCharacterFunc(localPlayer, lastMovementVector, lastMaxMovement)
+ end
+ end
+ return refreshCharacterMovement
+end
+
+
+function setupJumpButton( parentFrame )
+ local jumpButton = Instance.new("ImageButton")
+ jumpButton.Name = "JumpButton"
+ jumpButton.BackgroundTransparency = 1
+ jumpButton.Image = touchControlsSheet
+ jumpButton.ImageRectOffset = Vector2.new(176,222)
+ jumpButton.ImageRectSize = Vector2.new(174,174)
+ jumpButton.Size = UDim2.new(0,jumpButtonSize,0,jumpButtonSize)
+ if isSmallScreenDevice() then
+ jumpButton.Position = UDim2.new(1, -(jumpButtonSize*2.25), 1, -jumpButtonSize - 20)
+ else
+ jumpButton.Position = UDim2.new(1, -(jumpButtonSize*2.75), 1, -jumpButtonSize - 120)
+ end
+
+ local playerJumpFunc = localPlayer.JumpCharacter
+
+ local doJumpLoop = function ()
+ while currentJumpTouch do
+ if localPlayer then
+ playerJumpFunc(localPlayer)
+ end
+ wait(1/60)
+ end
+ end
+
+ jumpButton.InputBegan:connect(function(inputObject)
+ if inputObject.UserInputType ~= Enum.UserInputType.Touch then return end
+ if currentJumpTouch then return end
+ if inputObject == cameraTouch then return end
+ for i, touch in pairs(oldJumpTouches) do
+ if touch == inputObject then
+ return
+ end
+ end
+
+ currentJumpTouch = inputObject
+ jumpButton.ImageRectOffset = Vector2.new(0,222)
+ jumpButton.ImageRectSize = Vector2.new(174,174)
+ doJumpLoop()
+ end)
+ jumpButton.InputEnded:connect(function (inputObject)
+ if inputObject.UserInputType ~= Enum.UserInputType.Touch then return end
+
+ jumpButton.ImageRectOffset = Vector2.new(176,222)
+ jumpButton.ImageRectSize = Vector2.new(174,174)
+
+ if inputObject == currentJumpTouch then
+ table.insert(oldJumpTouches,currentJumpTouch)
+ currentJumpTouch = nil
+ end
+ end)
+ userInputService.InputEnded:connect(function ( globalInputObject )
+ for i, touch in pairs(oldJumpTouches) do
+ if touch == globalInputObject then
+ table.remove(oldJumpTouches,i)
+ break
+ end
+ end
+ end)
+
+ userInputService.Changed:connect(function(prop)
+ if prop == "ModalEnabled" then
+ jumpButton.Visible = not userInputService.ModalEnabled
+ end
+ end)
+
+ jumpButton.Parent = parentFrame
+end
+
+function isTouchUsedByJumpButton( touch )
+ if touch == currentJumpTouch then return true end
+ for i, touchToCompare in pairs(oldJumpTouches) do
+ if touch == touchToCompare then
+ return true
+ end
+ end
+
+ return false
+end
+
+function isTouchUsedByThumbstick(touch)
+ for i, touchToCompare in pairs(thumbstickTouches) do
+ if touch == touchToCompare then
+ return true
+ end
+ end
+
+ return false
+end
+
+function setupCameraControl(parentFrame, refreshCharacterMoveFunc)
+ local lastPos = nil
+ local hasRotatedCamera = false
+ local rotateCameraFunc = userInputService.RotateCamera
+
+ local pinchTime = -1
+ local shouldPinch = false
+ local lastPinchScale = nil
+ local zoomCameraFunc = userInputService.ZoomCamera
+ local pinchTouches = {}
+ local pinchFrame = nil
+
+ local resetCameraRotateState = function()
+ cameraTouch = nil
+ hasRotatedCamera = false
+ lastPos = nil
+ end
+
+ local resetPinchState = function ()
+ pinchTouches = {}
+ lastPinchScale = nil
+ shouldPinch = false
+ pinchFrame:Destroy()
+ pinchFrame = nil
+ end
+
+ local startPinch = function(firstTouch, secondTouch)
+ -- track pinching in new frame
+ if pinchFrame then pinchFrame:Destroy() end -- make sure we didn't track in any mud
+ pinchFrame = Instance.new("Frame")
+ pinchFrame.Name = "PinchFrame"
+ pinchFrame.BackgroundTransparency = 1
+ pinchFrame.Parent = parentFrame
+ pinchFrame.Size = UDim2.new(1,0,1,0)
+
+ pinchFrame.InputChanged:connect(function(inputObject)
+ if not shouldPinch then
+ resetPinchState()
+ return
+ end
+ resetCameraRotateState()
+
+ if lastPinchScale == nil then -- first pinch move, just set up scale
+ if inputObject == firstTouch then
+ lastPinchScale = (inputObject.Position - secondTouch.Position).magnitude
+ firstTouch = inputObject
+ elseif inputObject == secondTouch then
+ lastPinchScale = (inputObject.Position - firstTouch.Position).magnitude
+ secondTouch = inputObject
+ end
+ else -- we are now actually pinching, do comparison to last pinch size
+ local newPinchDistance = 0
+ if inputObject == firstTouch then
+ newPinchDistance = (inputObject.Position - secondTouch.Position).magnitude
+ firstTouch = inputObject
+ elseif inputObject == secondTouch then
+ newPinchDistance = (inputObject.Position - firstTouch.Position).magnitude
+ secondTouch = inputObject
+ end
+ if newPinchDistance ~= 0 then
+ local pinchDiff = newPinchDistance - lastPinchScale
+ if pinchDiff ~= 0 then
+ zoomCameraFunc(userInputService, (pinchDiff * CameraZoomSensitivity))
+ end
+ lastPinchScale = newPinchDistance
+ end
+ end
+ end)
+ pinchFrame.InputEnded:connect(function(inputObject) -- pinch is over, destroy all
+ if inputObject == firstTouch or inputObject == secondTouch then
+ resetPinchState()
+ end
+ end)
+ end
+
+ local pinchGestureReceivedTouch = function(inputObject)
+ if #pinchTouches < 1 then
+ table.insert(pinchTouches,inputObject)
+ pinchTime = tick()
+ shouldPinch = false
+ elseif #pinchTouches == 1 then
+ shouldPinch = ( (tick() - pinchTime) <= PinchZoomDelay )
+
+ if shouldPinch then
+ table.insert(pinchTouches,inputObject)
+ startPinch(pinchTouches[1], pinchTouches[2])
+ else -- shouldn't ever get here, but just in case
+ pinchTouches = {}
+ end
+ end
+ end
+
+ parentFrame.InputBegan:connect(function (inputObject)
+ if inputObject.UserInputType ~= Enum.UserInputType.Touch then return end
+ if isTouchUsedByJumpButton(inputObject) then return end
+
+ local usedByThumbstick = isTouchUsedByThumbstick(inputObject)
+ if not usedByThumbstick then
+ pinchGestureReceivedTouch(inputObject)
+ end
+
+ if cameraTouch == nil and not usedByThumbstick then
+ cameraTouch = inputObject
+ lastPos = Vector2.new(cameraTouch.Position.x,cameraTouch.Position.y)
+ lastTick = tick()
+ end
+ end)
+ userInputService.InputChanged:connect(function (inputObject)
+ if inputObject.UserInputType ~= Enum.UserInputType.Touch then return end
+ if cameraTouch ~= inputObject then return end
+
+ local newPos = Vector2.new(cameraTouch.Position.x,cameraTouch.Position.y)
+ local touchDiff = (lastPos - newPos) * CameraRotateSensitivity
+
+ -- first time rotating outside deadzone, just setup for next changed event
+ if not hasRotatedCamera and (touchDiff.magnitude > CameraRotateDeadZone) then
+ hasRotatedCamera = true
+ lastPos = newPos
+ end
+
+ -- fire everytime after we have rotated out of deadzone
+ if hasRotatedCamera and (lastPos ~= newPos) then
+ rotateCameraFunc(userInputService, touchDiff)
+ refreshCharacterMoveFunc()
+ lastPos = newPos
+ end
+ end)
+ userInputService.InputEnded:connect(function (inputObject)
+ if cameraTouch == inputObject or cameraTouch == nil then
+ resetCameraRotateState()
+ end
+
+ for i, touch in pairs(pinchTouches) do
+ if touch == inputObject then
+ table.remove(pinchTouches,i)
+ end
+ end
+ end)
+end
+
+function setupTouchControls()
+ local touchControlFrame = Instance.new("Frame")
+ touchControlFrame.Name = "TouchControlFrame"
+ touchControlFrame.Size = UDim2.new(1,0,1,0)
+ touchControlFrame.BackgroundTransparency = 1
+ touchControlFrame.Parent = Game.CoreGui.RobloxGui
+
+ local refreshCharacterMoveFunc = setupCharacterMovement(touchControlFrame)
+ setupJumpButton(touchControlFrame)
+ setupCameraControl(touchControlFrame, refreshCharacterMoveFunc)
+
+ userInputService.ProcessedEvent:connect(function(inputObject, processed)
+ if not processed then return end
+
+ -- kill camera pan if the touch is used by some user controls
+ if inputObject == cameraTouch and inputObject.UserInputState == Enum.UserInputState.Begin then
+ cameraTouch = nil
+ end
+ end)
+end
+
+
+----------------------------------------------------------------------------
+----------------------------------------------------------------------------
+-- Start of Script
+
+if userInputService:IsLuaTouchControls() then
+ setupTouchControls()
+else
+ script:Destroy()
+end
\ No newline at end of file
diff --git a/app/files/CoreGui/153556822 b/app/files/CoreGui/153556822
new file mode 100644
index 0000000..df1eb7c
--- /dev/null
+++ b/app/files/CoreGui/153556822
@@ -0,0 +1,247 @@
+--[[
+ This script controls the gui the player sees in regards to his or her health.
+ Can be turned with Game.StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.Health,false)
+ Copyright ROBLOX 2014. Written by Ben Tkacheff.
+--]]
+
+---------------------------------------------------------------------
+-- Initialize/Variables
+while not Game do
+ wait(1/60)
+end
+while not Game.Players do
+ wait(1/60)
+end
+
+local useCoreHealthBar = false
+local success = pcall(function() useCoreHealthBar = Game.Players:GetUseCoreScriptHealthBar() end)
+if not success or not useCoreHealthBar then
+ return
+end
+
+local currentHumanoid = nil
+
+local HealthGui = nil
+local lastHealth = 100
+local HealthPercentageForOverlay = 5
+local maxBarTweenTime = 0.3
+
+local guiEnabled = false
+local healthChangedConnection = nil
+local humanoidDiedConnection = nil
+local characterAddedConnection = nil
+
+local greenBarImage = "http://www.syntax.eco/asset/?id=35238053"
+local redBarImage = "http://www.syntax.eco/asset/?id=35238036"
+local goldBarImage = "http://www.syntax.eco/asset/?id=154646431" -- for god mode
+local hurtOverlayImage = "http://www.syntax.eco/asset/?id=34854607"
+
+Game:GetService("ContentProvider"):Preload(greenBarImage)
+Game:GetService("ContentProvider"):Preload(redBarImage)
+Game:GetService("ContentProvider"):Preload(goldBarImage)
+Game:GetService("ContentProvider"):Preload(hurtOverlayImage)
+
+while not Game.Players.LocalPlayer do
+ wait(1/60)
+end
+
+---------------------------------------------------------------------
+-- Functions
+
+function CreateGui()
+ if HealthGui and #HealthGui:GetChildren() > 0 then
+ HealthGui.Parent = Game.CoreGui.RobloxGui
+ return
+ end
+
+ local hurtOverlay = Instance.new("ImageLabel")
+ hurtOverlay.Name = "HurtOverlay"
+ hurtOverlay.BackgroundTransparency = 1
+ hurtOverlay.Image = hurtOverlayImage
+ hurtOverlay.Position = UDim2.new(-10,0,-10,0)
+ hurtOverlay.Size = UDim2.new(20,0,20,0)
+ hurtOverlay.Visible = false
+ hurtOverlay.Parent = HealthGui
+
+ local healthFrame = Instance.new("Frame")
+ healthFrame.Name = "HealthFrame"
+ healthFrame.BackgroundColor3 = Color3.new(0,0,0)
+ healthFrame.BorderColor3 = Color3.new(0,0,0)
+ healthFrame.Position = UDim2.new(0.5,-85,1,-22)
+ healthFrame.Size = UDim2.new(0,170,0,18)
+ healthFrame.Parent = HealthGui
+
+ local healthBar = Instance.new("ImageLabel")
+ healthBar.Name = "HealthBar"
+ healthBar.BackgroundTransparency = 1
+ healthBar.Image = greenBarImage
+ healthBar.Size = UDim2.new(1,0,1,0)
+ healthBar.Parent = healthFrame
+
+ local healthLabel = Instance.new("TextLabel")
+ healthLabel.Name = "HealthLabel"
+
+ healthLabel.Text = "Health " -- gives room at end of health bar
+ healthLabel.Font = Enum.Font.SourceSansBold
+ healthLabel.FontSize = Enum.FontSize.Size14
+ healthLabel.TextColor3 = Color3.new(1,1,1)
+ healthLabel.TextStrokeTransparency = 0
+ healthLabel.TextXAlignment = Enum.TextXAlignment.Right
+
+ healthLabel.BackgroundTransparency = 1
+ healthLabel.Size = UDim2.new(1,0,1,0)
+ healthLabel.Parent = healthFrame
+
+ HealthGui.Parent = Game.CoreGui.RobloxGui
+end
+
+function UpdateGui(health)
+ if not HealthGui then return end
+
+ local healthFrame = HealthGui:FindFirstChild("HealthFrame")
+ if not healthFrame then return end
+
+ local healthBar = healthFrame:FindFirstChild("HealthBar")
+ if not healthBar then return end
+
+ -- If more than 1/4 health, bar = green. Else, bar = red.
+ local percentHealth = (health/currentHumanoid.MaxHealth)
+
+ if percentHealth ~= percentHealth then
+ percentHealth = 1
+ healthBar.Image = goldBarImage
+ elseif percentHealth > 0.25 then
+ healthBar.Image = greenBarImage
+ else
+ healthBar.Image = redBarImage
+ end
+
+ local width = (health / currentHumanoid.MaxHealth)
+ width = math.max(math.min(width,1),0) -- make sure width is between 0 and 1
+ if width ~= width then width = 1 end
+
+ local healthDelta = lastHealth - health
+ lastHealth = health
+
+ local percentOfTotalHealth = math.abs(healthDelta/currentHumanoid.MaxHealth)
+ percentOfTotalHealth = math.max(math.min(percentOfTotalHealth,1),0) -- make sure percentOfTotalHealth is between 0 and 1
+ if percentOfTotalHealth ~= percentOfTotalHealth then percentOfTotalHealth = 1 end
+
+ local newHealthSize = UDim2.new(width,0,1,0)
+
+ if healthBar:IsDescendantOf(Game) then
+ healthBar:TweenSize(newHealthSize, Enum.EasingDirection.InOut, Enum.EasingStyle.Linear, percentOfTotalHealth * maxBarTweenTime, true)
+ else
+ healthBar.Size = newHealthSize
+ end
+
+ local thresholdForHurtOverlay = currentHumanoid.MaxHealth * (HealthPercentageForOverlay/100)
+
+ if healthDelta >= thresholdForHurtOverlay then
+ AnimateHurtOverlay()
+ end
+end
+
+function AnimateHurtOverlay()
+ if not HealthGui then return end
+
+ local overlay = HealthGui:FindFirstChild("HurtOverlay")
+ if not overlay then return end
+
+ local newSize = UDim2.new(20, 0, 20, 0)
+ local newPos = UDim2.new(-10, 0, -10, 0)
+
+ if overlay:IsDescendantOf(Game) then
+ -- stop any tweens on overlay
+ overlay:TweenSizeAndPosition(newSize,newPos,Enum.EasingDirection.Out,Enum.EasingStyle.Linear,0,true,function()
+
+ -- show the gui
+ overlay.Size = UDim2.new(1,0,1,0)
+ overlay.Position = UDim2.new(0,0,0,0)
+ overlay.Visible = true
+
+ -- now tween the hide
+ if overlay:IsDescendantOf(Game) then
+ overlay:TweenSizeAndPosition(newSize,newPos,Enum.EasingDirection.Out,Enum.EasingStyle.Quad,10,false,function()
+ overlay.Visible = false
+ end)
+ else
+ overlay.Size = newSize
+ overlay.Position = newPos
+ end
+ end)
+ else
+ overlay.Size = newSize
+ overlay.Position = newPos
+ end
+
+end
+
+function humanoidDied()
+ UpdateGui(0)
+end
+
+function disconnectPlayerConnections()
+ if characterAddedConnection then characterAddedConnection:disconnect() end
+ if humanoidDiedConnection then humanoidDiedConnection:disconnect() end
+ if healthChangedConnection then healthChangedConnection:disconnect() end
+end
+
+function newPlayerCharacter()
+ disconnectPlayerConnections()
+ startGui()
+end
+
+function startGui()
+ characterAddedConnection = Game.Players.LocalPlayer.CharacterAdded:connect(newPlayerCharacter)
+
+ local character = Game.Players.LocalPlayer.Character
+ if not character then
+ return
+ end
+
+ currentHumanoid = character:WaitForChild("Humanoid")
+ if not currentHumanoid then
+ return
+ end
+
+ if not Game.StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Health) then
+ return
+ end
+
+ healthChangedConnection = currentHumanoid.HealthChanged:connect(UpdateGui)
+ humanoidDiedConnection = currentHumanoid.Died:connect(humanoidDied)
+ UpdateGui(currentHumanoid.Health)
+
+ CreateGui()
+end
+
+
+
+---------------------------------------------------------------------
+-- Start Script
+
+HealthGui = Instance.new("Frame")
+HealthGui.Name = "HealthGui"
+HealthGui.BackgroundTransparency = 1
+HealthGui.Size = UDim2.new(1,0,1,0)
+
+Game.StarterGui.CoreGuiChangedSignal:connect(function(coreGuiType,enabled)
+ if coreGuiType == Enum.CoreGuiType.Health or coreGuiType == Enum.CoreGuiType.All then
+ if guiEnabled and not enabled then
+ if HealthGui then
+ HealthGui.Parent = nil
+ end
+ disconnectPlayerConnections()
+ elseif not guiEnabled and enabled then
+ startGui()
+ end
+
+ guiEnabled = enabled
+ end
+end)
+
+if Game.StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Health) then
+ guiEnabled = true
+ startGui()
+end
\ No newline at end of file
diff --git a/app/files/CoreGui/157877000 b/app/files/CoreGui/157877000
new file mode 100644
index 0000000..7465408
--- /dev/null
+++ b/app/files/CoreGui/157877000
@@ -0,0 +1,1038 @@
+--Include
+local Create = assert(LoadLibrary("RbxUtility")).Create
+
+-- A Few Script Globals
+local gui
+if script.Parent:FindFirstChild("ControlFrame") then
+ gui = script.Parent:FindFirstChild("ControlFrame")
+else
+ gui = script.Parent
+end
+
+-- Dev-Console Root
+
+local Dev_Container = Create'Frame'{
+ Name = 'DevConsoleContainer';
+ Parent = gui;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 0.9;
+ Position = UDim2.new(0, 100, 0, 10);
+ Size = UDim2.new(0.5, 20, 0.5, 20);
+ Visible = false;
+ BackgroundTransparency = 0.9;
+}
+
+local ToggleConsole = Create'BindableFunction'{
+ Name = 'ToggleDevConsole';
+ Parent = gui
+}
+
+
+local devConsoleInitialized = false
+function initializeDeveloperConsole()
+ if devConsoleInitialized then
+ return
+ end
+ devConsoleInitialized = true
+
+ ---Dev-Console Variables
+ local LOCAL_CONSOLE = 1
+ local SERVER_CONSOLE = 2
+
+ local MAX_LIST_SIZE = 1000
+
+ local minimumSize = Vector2.new(245, 180)
+ local currentConsole = LOCAL_CONSOLE
+
+ local localMessageList = {}
+ local serverMessageList = {}
+
+ local localOffset = 0
+ local serverOffset = 0
+
+ local errorToggleOn = true
+ local warningToggleOn = true
+ local infoToggleOn = true
+ local outputToggleOn = true
+ local wordWrapToggleOn = false
+
+ local textHolderSize = 0
+
+ local frameNumber = 0
+
+ --Create Dev-Console
+
+ local Dev_Body = Create'Frame'{
+ Name = 'Body';
+ Parent = Dev_Container;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 0.5;
+ Position = UDim2.new(0, 0, 0, 21);
+ Size = UDim2.new(1, 0, 1, -25);
+ }
+
+ local Dev_OptionsHolder = Create'Frame'{
+ Name = 'OptionsHolder';
+ Parent = Dev_Body;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 1.0;
+ Position = UDim2.new(0, 220, 0, 0);
+ Size = UDim2.new(1, -255, 0, 24);
+ ClipsDescendants = true
+ }
+
+ local Dev_OptionsBar = Create'Frame'{
+ Name = 'OptionsBar';
+ Parent = Dev_OptionsHolder;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 1.0;
+ Position = UDim2.new(0.0, -250, 0, 4);
+ Size = UDim2.new(0, 234, 0, 18);
+ }
+
+ local Dev_ErrorToggleFilter = Create'TextButton'{
+ Name = 'ErrorToggleButton';
+ Parent = Dev_OptionsBar;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BorderColor3 = Color3.new(1.0, 0, 0);
+ Position = UDim2.new(0, 115, 0, 0);
+ Size = UDim2.new(0, 18, 0, 18);
+ Font = "SourceSansBold";
+ FontSize = Enum.FontSize.Size14;
+ Text = "";
+ TextColor3 = Color3.new(1.0, 0, 0);
+ }
+
+ Create'Frame'{
+ Name = 'CheckFrame';
+ Parent = Dev_ErrorToggleFilter;
+ BackgroundColor3 = Color3.new(1.0,0,0);
+ BorderColor3 = Color3.new(1.0, 0, 0);
+ Position = UDim2.new(0, 4, 0, 4);
+ Size = UDim2.new(0, 10, 0, 10);
+ }
+
+ local Dev_InfoToggleFilter = Create'TextButton'{
+ Name = 'InfoToggleButton';
+ Parent = Dev_OptionsBar;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BorderColor3 = Color3.new(0.4, 0.5, 1.0);
+ Position = UDim2.new(0, 65, 0, 0);
+ Size = UDim2.new(0, 18, 0, 18);
+ Font = "SourceSansBold";
+ FontSize = Enum.FontSize.Size14;
+ Text = "";
+ TextColor3 = Color3.new(0.4, 0.5, 1.0);
+ }
+
+ Create'Frame'{
+ Name = 'CheckFrame';
+ Parent = Dev_InfoToggleFilter;
+ BackgroundColor3 = Color3.new(0.4, 0.5, 1.0);
+ BorderColor3 = Color3.new(0.4, 0.5, 1.0);
+ Position = UDim2.new(0, 4, 0, 4);
+ Size = UDim2.new(0, 10, 0, 10);
+ }
+
+ local Dev_OutputToggleFilter = Create'TextButton'{
+ Name = 'OutputToggleButton';
+ Parent = Dev_OptionsBar;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BorderColor3 = Color3.new(1.0, 1.0, 1.0);
+ Position = UDim2.new(0, 40, 0, 0);
+ Size = UDim2.new(0, 18, 0, 18);
+ Font = "SourceSansBold";
+ FontSize = Enum.FontSize.Size14;
+ Text = "";
+ TextColor3 = Color3.new(1.0, 1.0, 1.0);
+ }
+
+ Create'Frame'{
+ Name = 'CheckFrame';
+ Parent = Dev_OutputToggleFilter;
+ BackgroundColor3 = Color3.new(1.0, 1.0, 1.0);
+ BorderColor3 = Color3.new(1.0, 1.0, 1.0);
+ Position = UDim2.new(0, 4, 0, 4);
+ Size = UDim2.new(0, 10, 0, 10);
+ }
+
+ local Dev_WarningToggleFilter = Create'TextButton'{
+ Name = 'WarningToggleButton';
+ Parent = Dev_OptionsBar;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BorderColor3 = Color3.new(1.0, 0.6, 0.4);
+ Position = UDim2.new(0, 90, 0, 0);
+ Size = UDim2.new(0, 18, 0, 18);
+ Font = "SourceSansBold";
+ FontSize = Enum.FontSize.Size14;
+ Text = "";
+ TextColor3 = Color3.new(1.0, 0.6, 0.4);
+ }
+
+ Create'Frame'{
+ Name = 'CheckFrame';
+ Parent = Dev_WarningToggleFilter;
+ BackgroundColor3 = Color3.new(1.0, 0.6, 0.4);
+ BorderColor3 = Color3.new(1.0, 0.6, 0.4);
+ Position = UDim2.new(0, 4, 0, 4);
+ Size = UDim2.new(0, 10, 0, 10);
+ }
+
+ local Dev_WordWrapToggle = Create'TextButton'{
+ Name = 'WordWrapToggleButton';
+ Parent = Dev_OptionsBar;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BorderColor3 = Color3.new(0.8, 0.8, 0.8);
+ Position = UDim2.new(0, 215, 0, 0);
+ Size = UDim2.new(0, 18, 0, 18);
+ Font = "SourceSansBold";
+ FontSize = Enum.FontSize.Size14;
+ Text = "";
+ TextColor3 = Color3.new(0.8, 0.8, 0.8);
+ }
+
+ Create'Frame'{
+ Name = 'CheckFrame';
+ Parent = Dev_WordWrapToggle;
+ BackgroundColor3 = Color3.new(0.8, 0.8, 0.8);
+ BorderColor3 = Color3.new(0.8, 0.8, 0.8);
+ Position = UDim2.new(0, 4, 0, 4);
+ Size = UDim2.new(0, 10, 0, 10);
+ Visible = false
+ }
+
+ Create'TextLabel'{
+ Name = 'Filter';
+ Parent = Dev_OptionsBar;
+ BackgroundTransparency = 1.0;
+ Position = UDim2.new(0, 0, 0, 0);
+ Size = UDim2.new(0, 40, 0, 18);
+ Font = "SourceSansBold";
+ FontSize = Enum.FontSize.Size14;
+ Text = "Filter";
+ TextColor3 = Color3.new(1, 1, 1);
+ }
+
+ Create'TextLabel'{
+ Name = 'WordWrap';
+ Parent = Dev_OptionsBar;
+ BackgroundTransparency = 1;
+ Position = UDim2.new(0, 150, 0, 0);
+ Size = UDim2.new(0, 50, 0, 18);
+ Font = "SourceSansBold";
+ FontSize = Enum.FontSize.Size14;
+ Text = "Word Wrap";
+ TextColor3 = Color3.new(1, 1, 1);
+ }
+
+ local Dev_ScrollBar = Create'Frame'{
+ Name = 'ScrollBar';
+ Parent = Dev_Body;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 0.9;
+ Position = UDim2.new(1, -20, 0, 26);
+ Size = UDim2.new(0, 20, 1, -50);
+ Visible = false;
+ BackgroundTransparency = 0.9;
+ }
+
+ local Dev_ScrollArea = Create'Frame'{
+ Name = 'ScrollArea';
+ Parent = Dev_ScrollBar;
+ BackgroundTransparency = 1;
+ Position = UDim2.new(0, 0, 0, 23);
+ Size = UDim2.new(1, 0, 1, -46);
+ BackgroundTransparency = 1;
+ }
+
+ local Dev_Handle = Create'ImageButton'{
+ Name = 'Handle';
+ Parent = Dev_ScrollArea;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 0.5;
+ Position = UDim2.new(0, 0, .2, 0);
+ Size = UDim2.new(0, 20, 0, 40);
+ BackgroundTransparency = 0.5;
+ }
+
+ Create'ImageLabel'{
+ Name = 'ImageLabel';
+ Parent = Dev_Handle;
+ BackgroundTransparency = 1;
+ Position = UDim2.new(0, 0, 0.5, -8);
+ Rotation = 180;
+ Size = UDim2.new(1, 0, 0, 16);
+ Image = "http://www.syntax.eco/asset/?id=151205881";
+ }
+
+ local Dev_DownButton = Create'ImageButton'{
+ Name = 'Down';
+ Parent = Dev_ScrollBar;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 0.5;
+ Position = UDim2.new(0, 0, 1, -20);
+ Size = UDim2.new(0, 20, 0, 20);
+ BackgroundTransparency = 0.5;
+ }
+
+ Create'ImageLabel'{
+ Name = 'ImageLabel';
+ Parent = Dev_DownButton;
+ BackgroundTransparency = 1;
+ Position = UDim2.new(0, 3, 0, 3);
+ Size = UDim2.new(0, 14, 0, 14);
+ Rotation = 180;
+ Image = "http://www.syntax.eco/asset/?id=151205813";
+ }
+
+ local Dev_UpButton = Create'ImageButton'{
+ Name = 'Up';
+ Parent = Dev_ScrollBar;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 0.5;
+ Position = UDim2.new(0, 0, 0, 0);
+ Size = UDim2.new(0, 20, 0, 20);
+ }
+
+ Create'ImageLabel'{
+ Name = 'ImageLabel';
+ Parent = Dev_UpButton;
+ BackgroundTransparency = 1;
+ Position = UDim2.new(0, 3, 0, 3);
+ Size = UDim2.new(0, 14, 0, 14);
+ Image = "http://www.syntax.eco/asset/?id=151205813";
+ }
+
+ local Dev_TextBox = Create'Frame'{
+ Name = 'TextBox';
+ Parent = Dev_Body;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 0.6;
+ Position = UDim2.new(0, 2, 0, 26);
+ Size = UDim2.new(1, -4, 1, -28);
+ ClipsDescendants = true;
+ }
+
+ local Dev_TextHolder = Create'Frame'{
+ Name = 'TextHolder';
+ Parent = Dev_TextBox;
+ BackgroundTransparency = 1;
+ Position = UDim2.new(0, 0, 0, 0);
+ Size = UDim2.new(1, 0, 1, 0);
+ }
+
+ local Dev_OptionsButton = Create'ImageButton'{
+ Name = 'OptionsButton';
+ Parent = Dev_Body;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 1.0;
+ Position = UDim2.new(0, 200, 0, 2);
+ Size = UDim2.new(0, 20, 0, 20);
+ }
+
+ Create'ImageLabel'{
+ Name = 'ImageLabel';
+ Parent = Dev_OptionsButton;
+ BackgroundTransparency = 1.0;
+ Position = UDim2.new(0, 0, 0, 0);
+ Size = UDim2.new(1, 0, 1, 0);
+ Rotation = 0;
+ Image = "http://www.syntax.eco/asset/?id=152093917";
+ }
+
+ local Dev_ResizeButton = Create'ImageButton'{
+ Name = 'ResizeButton';
+ Parent = Dev_Body;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 0.5;
+ Position = UDim2.new(1, -20, 1, -20);
+ Size = UDim2.new(0, 20, 0, 20);
+ }
+
+ Create'ImageLabel'{
+ Name = 'ImageLabel';
+ Parent = Dev_ResizeButton;
+ BackgroundTransparency = 1;
+ Position = UDim2.new(0, 6, 0, 6);
+ Size = UDim2.new(0.8, 0, 0.8, 0);
+ Rotation = 135;
+ Image = "http://www.syntax.eco/asset/?id=151205813";
+ }
+
+ Create'TextButton'{
+ Name = 'LocalConsole';
+ Parent = Dev_Body;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 0.6;
+ Position = UDim2.new(0, 7, 0, 5);
+ Size = UDim2.new(0, 90, 0, 20);
+ Font = "SourceSansBold";
+ FontSize = Enum.FontSize.Size14;
+ Text = "Local Console";
+ TextColor3 = Color3.new(1, 1, 1);
+ TextYAlignment = Enum.TextYAlignment.Center;
+ }
+
+ Create'TextButton'{
+ Name = 'ServerConsole';
+ Parent = Dev_Body;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 0.8;
+ Position = UDim2.new(0, 102, 0, 5);
+ Size = UDim2.new(0, 90, 0, 17);
+ Font = "SourceSansBold";
+ FontSize = Enum.FontSize.Size14;
+ Text = "Server Console";
+ TextColor3 = Color3.new(1, 1, 1);
+ TextYAlignment = Enum.TextYAlignment.Center;
+ }
+
+ local Dev_TitleBar = Create'Frame'{
+ Name = 'TitleBar';
+ Parent = Dev_Container;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 0.5;
+ Position = UDim2.new(0, 0, 0, 0);
+ Size = UDim2.new(1, 0, 0, 20);
+ }
+
+ local Dev_CloseButton = Create'ImageButton'{
+ Name = 'CloseButton';
+ Parent = Dev_TitleBar;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 0.5;
+ Position = UDim2.new(1, -20, 0, 0);
+ Size = UDim2.new(0, 20, 0, 20);
+ }
+
+ Create'ImageLabel'{
+ Parent = Dev_CloseButton;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 1;
+ Position = UDim2.new(0, 3, 0, 3);
+ Size = UDim2.new(0, 14, 0, 14);
+ Image = "http://www.syntax.eco/asset/?id=151205852";
+ }
+
+ Create'TextButton'{
+ Name = 'TextButton';
+ Parent = Dev_TitleBar;
+ BackgroundColor3 = Color3.new(0,0,0);
+ BackgroundTransparency = 0.5;
+ Position = UDim2.new(0, 0, 0, 0);
+ Size = UDim2.new(1, -23, 1, 0);
+ Text = "";
+ }
+
+ Create'TextLabel'{
+ Name = 'TitleText';
+ Parent = Dev_TitleBar;
+ BackgroundTransparency = 1;
+ Position = UDim2.new(0, 0, 0, 0);
+ Size = UDim2.new(0, 185, 0, 20);
+ Font = "SourceSansBold";
+ FontSize = Enum.FontSize.Size18;
+ Text = "Server Console";
+ TextColor3 = Color3.new(1, 1, 1);
+ Text = "Roblox Developer Console";
+ TextYAlignment = Enum.TextYAlignment.Top;
+ }
+
+ ---Saved Mouse Information
+ local previousMousePos = nil
+ local pPos = nil
+
+ local previousMousePosResize = nil
+ local pSize = nil
+
+ local previousMousePosScroll = nil
+ local pScrollHandle = nil
+ local pOffset = nil
+
+ local scrollUpIsDown = false
+ local scrollDownIsDown = false
+
+ function clean()
+ previousMousePos = nil
+ pPos = nil
+ previousMousePosResize = nil
+ pSize = nil
+ previousMousePosScroll = nil
+ pScrollHandle = nil
+ pOffset = nil
+ scrollUpIsDown = false
+ scrollDownIsDown = false
+ end
+
+
+ ---Handle Dev-Console Position
+ function refreshConsolePosition(x, y)
+ if not previousMousePos then
+ return
+ end
+
+ local delta = Vector2.new(x, y) - previousMousePos
+ Dev_Container.Position = UDim2.new(0, pPos.X + delta.X, 0, pPos.Y + delta.Y)
+ end
+
+ Dev_TitleBar.TextButton.MouseButton1Down:connect(function(x, y)
+ previousMousePos = Vector2.new(x, y)
+ pPos = Dev_Container.AbsolutePosition
+ end)
+
+ Dev_TitleBar.TextButton.MouseButton1Up:connect(function(x, y)
+ clean()
+ end)
+
+ ---Handle Dev-Console Size
+ function refreshConsoleSize(x, y)
+ if not previousMousePosResize then
+ return
+ end
+
+ local delta = Vector2.new(x, y) - previousMousePosResize
+ Dev_Container.Size = UDim2.new(0, math.max(pSize.X + delta.X, minimumSize.X), 0, math.max(pSize.Y + delta.Y, minimumSize.Y))
+ end
+ Dev_Container.Body.ResizeButton.MouseButton1Down:connect(function(x, y)
+ previousMousePosResize = Vector2.new(x, y)
+ pSize = Dev_Container.AbsoluteSize
+ end)
+
+ Dev_Container.Body.ResizeButton.MouseButton1Up:connect(function(x, y)
+ clean()
+ end)
+
+
+ ---Handle Dev-Console Close Button
+ Dev_TitleBar.CloseButton.MouseButton1Down:connect(function(x, y)
+ Dev_Container.Visible = false
+ end)
+
+ Dev_Container.TitleBar.CloseButton.MouseButton1Up:connect(function()
+ clean()
+ end)
+
+ local optionsHidden = true
+ local animating = false
+ --Options
+ function startAnimation()
+ if animating then return end
+ animating = true
+
+ repeat
+ if optionsHidden then
+ frameNumber = frameNumber - 1
+ else
+ frameNumber = frameNumber + 1
+ end
+
+ local x = frameNumber / 5
+ local smoothStep = x * x * (3 - (2 * x))
+ Dev_OptionsButton.ImageLabel.Rotation = smoothStep * 5 * 9
+ Dev_OptionsBar.Position = UDim2.new(0, (smoothStep * 5 * 50) - 250, 0, 4)
+
+ wait()
+ if (frameNumber <= 0 and optionsHidden) or (frameNumber >= 5 and not optionsHidden) then
+ animating = false
+ end
+ until not animating
+ end
+
+ Dev_OptionsButton.MouseButton1Down:connect(function(x, y)
+ optionsHidden = not optionsHidden
+ startAnimation()
+ end)
+
+ --Scroll Position
+
+ function changeOffset(value)
+ if (currentConsole == LOCAL_CONSOLE) then
+ localOffset = localOffset + value
+ elseif (currentConsole == SERVER_CONSOLE) then
+ serverOffset = serverOffset + value
+ end
+
+ repositionList()
+ end
+
+ --Refresh Dev-Console Text
+ function refreshTextHolderForReal()
+ local childMessages = Dev_TextHolder:GetChildren()
+
+ local messageList
+
+ if (currentConsole == LOCAL_CONSOLE) then
+ messageList = localMessageList
+ elseif (currentConsole == SERVER_CONSOLE) then
+ messageList = serverMessageList
+ end
+
+ local posOffset = 0
+
+ for i = 1, #childMessages do
+ childMessages[i].Visible = false
+ end
+
+ for i = 1, #messageList do
+ local message
+
+ local movePosition = false
+
+ if i > #childMessages then
+ message = Create'TextLabel'{
+ Name = 'Message';
+ Parent = Dev_TextHolder;
+ BackgroundTransparency = 1;
+ TextXAlignment = 'Left';
+ Size = UDim2.new(1, 0, 0, 14);
+ FontSize = 'Size10';
+ ZIndex = 1;
+ }
+ movePosition = true
+ else
+ message = childMessages[i]
+ end
+
+ if (outputToggleOn or messageList[i].Type ~= Enum.MessageType.MessageOutput) and
+ (infoToggleOn or messageList[i].Type ~= Enum.MessageType.MessageInfo) and
+ (warningToggleOn or messageList[i].Type ~= Enum.MessageType.MessageWarning) and
+ (errorToggleOn or messageList[i].Type ~= Enum.MessageType.MessageError) then
+ message.TextWrapped = wordWrapToggleOn
+ message.Size = UDim2.new(0.98, 0, 0, 2000)
+ message.Parent = Dev_Container
+ message.Text = messageList[i].Time.." -- "..messageList[i].Message
+
+ message.Size = UDim2.new(0.98, 0, 0, message.TextBounds.Y)
+ message.Position = UDim2.new(0, 5, 0, posOffset)
+ message.Parent = Dev_TextHolder
+ posOffset = posOffset + message.TextBounds.Y
+
+ if movePosition then
+ if (currentConsole == LOCAL_CONSOLE and localOffset > 0) or (currentConsole == SERVER_CONSOLE and serverOffset > 0) then
+ changeOffset(message.TextBounds.Y)
+ end
+ end
+
+ message.Visible = true
+
+ if messageList[i].Type == Enum.MessageType.MessageError then
+ message.TextColor3 = Color3.new(1, 0, 0)
+ elseif messageList[i].Type == Enum.MessageType.MessageInfo then
+ message.TextColor3 = Color3.new(0.4, 0.5, 1)
+ elseif messageList[i].Type == Enum.MessageType.MessageWarning then
+ message.TextColor3 = Color3.new(1, 0.6, 0.4)
+ else
+ message.TextColor3 = Color3.new(1, 1, 1)
+ end
+ end
+
+
+ end
+
+ textHolderSize = posOffset
+
+ end
+
+ -- Refreshing the textholder every 0.1 (if needed) is good enough, surely fast enough
+ -- We don't want it to update 50x in a tick because there are 50 messages in that tick
+ -- (Whenever for one reason or another a lot of output comes in, it can lag
+ -- This will make it behave better in a situation of a lot of output comming in)
+ local refreshQueued = false
+ function refreshTextHolder()
+ if refreshQueued then return end
+ Delay(0.1,function()
+ refreshQueued = false
+ refreshTextHolderForReal()
+ end) refreshQueued = true
+ end
+
+ --Handle Dev-Console Scrollbar
+
+ local inside = 0
+ function holdingUpButton()
+ if scrollUpIsDown then
+ return
+ end
+ scrollUpIsDown = true
+ wait(.6)
+ inside = inside + 1
+ while scrollUpIsDown and inside < 2 do
+ wait()
+ changeOffset(12)
+ end
+ inside = inside - 1
+ end
+
+ function holdingDownButton()
+ if scrollDownIsDown then
+ return
+ end
+ scrollDownIsDown = true
+ wait(.6)
+ inside = inside + 1
+ while scrollDownIsDown and inside < 2 do
+ wait()
+ changeOffset(-12)
+ end
+ inside = inside - 1
+ end
+
+ Dev_Container.Body.ScrollBar.Up.MouseButton1Click:connect(function()
+ changeOffset(10)
+ end)
+
+ Dev_Container.Body.ScrollBar.Up.MouseButton1Down:connect(function()
+ changeOffset(10)
+ holdingUpButton()
+ end)
+
+ Dev_Container.Body.ScrollBar.Up.MouseButton1Up:connect(function()
+ clean()
+ end)
+
+ Dev_Container.Body.ScrollBar.Down.MouseButton1Down:connect(function()
+ changeOffset(-10)
+ holdingDownButton()
+ end)
+
+ Dev_Container.Body.ScrollBar.Down.MouseButton1Up:connect(function()
+ clean()
+ end)
+
+ function handleScroll(x, y)
+ if not previousMousePosScroll then
+ return
+ end
+
+ local delta = (Vector2.new(x, y) - previousMousePosScroll).Y
+
+ local backRatio = 1 - (Dev_Container.Body.TextBox.AbsoluteSize.Y / Dev_TextHolder.AbsoluteSize.Y)
+
+ local movementSize = Dev_ScrollArea.AbsoluteSize.Y - Dev_ScrollArea.Handle.AbsoluteSize.Y
+ local normalDelta = math.max(math.min(delta, movementSize), 0 - movementSize)
+ local normalRatio = normalDelta / movementSize
+
+ local textMovementSize = (backRatio * Dev_TextHolder.AbsoluteSize.Y)
+ local offsetChange = textMovementSize * normalRatio
+
+ if (currentConsole == LOCAL_CONSOLE) then
+ localOffset = pOffset - offsetChange
+ elseif (currentConsole == SERVER_CONSOLE) then
+ serverOffset = pOffset - offsetChange
+ end
+ end
+
+ Dev_ScrollArea.Handle.MouseButton1Down:connect(function(x, y)
+ previousMousePosScroll = Vector2.new(x, y)
+ pScrollHandle = Dev_ScrollArea.Handle.AbsolutePosition
+ if (currentConsole == LOCAL_CONSOLE) then
+ pOffset = localOffset
+ elseif (currentConsole == SERVER_CONSOLE) then
+ pOffset = serverOffset
+ end
+
+ end)
+
+ Dev_ScrollArea.Handle.MouseButton1Up:connect(function(x, y)
+ clean()
+ end)
+
+ local function existsInsideContainer(container, x, y)
+ local pos = container.AbsolutePosition
+ local size = container.AbsoluteSize
+ if x < pos.X or x > pos.X + size.X or y < pos.y or y > pos.y + size.y then
+ return false
+ end
+ return true
+ end
+
+
+
+ --Refresh Dev-Console Message Positions
+ function repositionList()
+
+ if (currentConsole == LOCAL_CONSOLE) then
+ localOffset = math.min(math.max(localOffset, 0), textHolderSize - Dev_Container.Body.TextBox.AbsoluteSize.Y)
+ Dev_TextHolder.Size = UDim2.new(1, 0, 0, textHolderSize)
+
+ elseif (currentConsole == SERVER_CONSOLE) then
+ serverOffset = math.min(math.max(serverOffset, 0), textHolderSize - Dev_Container.Body.TextBox.AbsoluteSize.Y)
+ Dev_TextHolder.Size = UDim2.new(1, 0, 0, textHolderSize)
+ end
+
+ local ratio = Dev_Container.Body.TextBox.AbsoluteSize.Y / Dev_TextHolder.AbsoluteSize.Y
+
+ if ratio >= 1 then
+ Dev_Container.Body.ScrollBar.Visible = false
+ Dev_Container.Body.TextBox.Size = UDim2.new(1, -4, 1, -28)
+
+ if (currentConsole == LOCAL_CONSOLE) then
+ Dev_TextHolder.Position = UDim2.new(0, 0, 1, 0 - textHolderSize)
+ elseif (currentConsole == SERVER_CONSOLE) then
+ Dev_TextHolder.Position = UDim2.new(0, 0, 1, 0 - textHolderSize)
+ end
+
+
+ else
+ Dev_Container.Body.ScrollBar.Visible = true
+ Dev_Container.Body.TextBox.Size = UDim2.new(1, -25, 1, -28)
+
+ local backRatio = 1 - ratio
+ local offsetRatio
+
+ if (currentConsole == LOCAL_CONSOLE) then
+ offsetRatio = localOffset / Dev_TextHolder.AbsoluteSize.Y
+ elseif (currentConsole == SERVER_CONSOLE) then
+ offsetRatio = serverOffset / Dev_TextHolder.AbsoluteSize.Y
+ end
+
+ local topRatio = math.max(0, backRatio - offsetRatio)
+
+ local scrollHandleSize = math.max((Dev_ScrollArea.AbsoluteSize.Y) * ratio, 21)
+
+ local scrollRatio = scrollHandleSize / Dev_ScrollArea.AbsoluteSize.Y
+ local ratioConversion = (1 - scrollRatio) / (1 - ratio)
+
+ local topScrollRatio = topRatio * ratioConversion
+
+ local sPos = math.min((Dev_ScrollArea.AbsoluteSize.Y) * topScrollRatio, Dev_ScrollArea.AbsoluteSize.Y - scrollHandleSize)
+
+ Dev_ScrollArea.Handle.Size = UDim2.new(1, 0, 0, scrollHandleSize)
+ Dev_ScrollArea.Handle.Position = UDim2.new(0, 0, 0, sPos)
+
+ if (currentConsole == LOCAL_CONSOLE) then
+ Dev_TextHolder.Position = UDim2.new(0, 0, 1, 0 - textHolderSize + localOffset)
+ elseif (currentConsole == SERVER_CONSOLE) then
+ Dev_TextHolder.Position = UDim2.new(0, 0, 1, 0 - textHolderSize + serverOffset)
+ end
+
+ end
+ end
+
+ -- Easy, fast, and working nicely
+ local function numberWithZero(num)
+ return (num < 10 and "0" or "")..num
+ end
+
+ local str = "%s:%s:%s"
+
+ function ConvertTimeStamp(timeStamp)
+ local localTime = timeStamp - os.time() + math.floor(tick())
+ local dayTime = localTime % 86400
+
+ local hour = math.floor(dayTime/3600)
+
+ dayTime = dayTime - (hour * 3600)
+ local minute = math.floor(dayTime/60)
+
+ dayTime = dayTime - (minute * 60)
+ local second = dayTime
+
+ local h = numberWithZero(hour)
+ local m = numberWithZero(minute)
+ local s = numberWithZero(dayTime)
+
+ return str:format(h,m,s)
+ end
+
+ --Filter
+
+ Dev_OptionsBar.ErrorToggleButton.MouseButton1Down:connect(function(x, y)
+ errorToggleOn = not errorToggleOn
+ Dev_OptionsBar.ErrorToggleButton.CheckFrame.Visible = errorToggleOn
+ refreshTextHolder()
+ repositionList()
+ end)
+
+ Dev_OptionsBar.WarningToggleButton.MouseButton1Down:connect(function(x, y)
+ warningToggleOn = not warningToggleOn
+ Dev_OptionsBar.WarningToggleButton.CheckFrame.Visible = warningToggleOn
+ refreshTextHolder()
+ repositionList()
+ end)
+
+ Dev_OptionsBar.InfoToggleButton.MouseButton1Down:connect(function(x, y)
+ infoToggleOn = not infoToggleOn
+ Dev_OptionsBar.InfoToggleButton.CheckFrame.Visible = infoToggleOn
+ refreshTextHolder()
+ repositionList()
+ end)
+
+ Dev_OptionsBar.OutputToggleButton.MouseButton1Down:connect(function(x, y)
+ outputToggleOn = not outputToggleOn
+ Dev_OptionsBar.OutputToggleButton.CheckFrame.Visible = outputToggleOn
+ refreshTextHolder()
+ repositionList()
+ end)
+
+ Dev_OptionsBar.WordWrapToggleButton.MouseButton1Down:connect(function(x, y)
+ wordWrapToggleOn = not wordWrapToggleOn
+ Dev_OptionsBar.WordWrapToggleButton.CheckFrame.Visible = wordWrapToggleOn
+ refreshTextHolder()
+ repositionList()
+ end)
+
+ ---Dev-Console Message Functionality
+ function AddLocalMessage(str, messageType, timeStamp)
+ localMessageList[#localMessageList+1] = {Message = str, Time = ConvertTimeStamp(timeStamp), Type = messageType}
+ while #localMessageList > MAX_LIST_SIZE do
+ table.remove(localMessageList, 1)
+ end
+
+ refreshTextHolder()
+
+ repositionList()
+ end
+
+ function AddServerMessage(str, messageType, timeStamp)
+ serverMessageList[#serverMessageList+1] = {Message = str, Time = ConvertTimeStamp(timeStamp), Type = messageType}
+ while #serverMessageList > MAX_LIST_SIZE do
+ table.remove(serverMessageList, 1)
+ end
+
+ refreshTextHolder()
+
+ repositionList()
+ end
+
+
+
+ --Handle Dev-Console Local/Server Buttons
+ Dev_Container.Body.LocalConsole.MouseButton1Click:connect(function(x, y)
+ if (currentConsole == SERVER_CONSOLE) then
+ currentConsole = LOCAL_CONSOLE
+ local localConsole = Dev_Container.Body.LocalConsole
+ local serverConsole = Dev_Container.Body.ServerConsole
+
+ localConsole.Size = UDim2.new(0, 90, 0, 20)
+ serverConsole.Size = UDim2.new(0, 90, 0, 17)
+ localConsole.BackgroundTransparency = 0.6
+ serverConsole.BackgroundTransparency = 0.8
+
+ if game:FindFirstChild("Players") and game.Players["LocalPlayer"] then
+ local mouse = game.Players.LocalPlayer:GetMouse()
+ local mousePos = Vector2.new(mouse.X, mouse.Y)
+ refreshConsolePosition(mouse.X, mouse.Y)
+ refreshConsoleSize(mouse.X, mouse.Y)
+ handleScroll(mouse.X, mouse.Y)
+ end
+
+ refreshTextHolder()
+ repositionList()
+
+ end
+ end)
+
+ Dev_Container.Body.LocalConsole.MouseButton1Up:connect(function()
+ clean()
+ end)
+
+ local serverHistoryRequested = false;
+
+ Dev_Container.Body.ServerConsole.MouseButton1Click:connect(function(x, y)
+
+ if not serverHistoryRequested then
+ serverHistoryRequested = true
+ game:GetService("LogService"):RequestServerOutput()
+ end
+
+ if (currentConsole == LOCAL_CONSOLE) then
+ currentConsole = SERVER_CONSOLE
+ local localConsole = Dev_Container.Body.LocalConsole
+ local serverConsole = Dev_Container.Body.ServerConsole
+
+ serverConsole.Size = UDim2.new(0, 90, 0, 20)
+ localConsole.Size = UDim2.new(0, 90, 0, 17)
+ serverConsole.BackgroundTransparency = 0.6
+ localConsole.BackgroundTransparency = 0.8
+
+ if game:FindFirstChild("Players") and game.Players["LocalPlayer"] then
+ local mouse = game.Players.LocalPlayer:GetMouse()
+ local mousePos = Vector2.new(mouse.X, mouse.Y)
+ refreshConsolePosition(mouse.X, mouse.Y)
+ refreshConsoleSize(mouse.X, mouse.Y)
+ handleScroll(mouse.X, mouse.Y)
+ end
+
+ refreshTextHolder()
+ repositionList()
+ end
+ end)
+
+ ---Extra Mouse Handlers for Dev-Console
+ Dev_Container.Body.ServerConsole.MouseButton1Up:connect(function()
+ clean()
+ end)
+
+ if game:FindFirstChild("Players") and game.Players["LocalPlayer"] then
+ local LocalMouse = game.Players.LocalPlayer:GetMouse()
+ LocalMouse.Move:connect(function()
+ if not Dev_Container.Visible then
+ return
+ end
+ local mouse = game.Players.LocalPlayer:GetMouse()
+ local mousePos = Vector2.new(mouse.X, mouse.Y)
+ refreshConsolePosition(mouse.X, mouse.Y)
+ refreshConsoleSize(mouse.X, mouse.Y)
+ handleScroll(mouse.X, mouse.Y)
+
+ refreshTextHolder()
+ repositionList()
+ end)
+
+ LocalMouse.Button1Up:connect(function()
+ clean()
+ end)
+
+ LocalMouse.WheelForward:connect(function()
+ if not Dev_Container.Visible then
+ return
+ end
+ if existsInsideContainer(Dev_Container, LocalMouse.X, LocalMouse.Y) then
+ changeOffset(10)
+ end
+ end)
+
+ LocalMouse.WheelBackward:connect(function()
+ if not Dev_Container.Visible then
+ return
+ end
+ if existsInsideContainer(Dev_Container, LocalMouse.X, LocalMouse.Y) then
+ changeOffset(-10)
+ end
+ end)
+
+ end
+
+ Dev_ScrollArea.Handle.MouseButton1Down:connect(function()
+ repositionList()
+ end)
+
+
+ ---Populate Dev-Console with dummy messages
+
+ local history = game:GetService("LogService"):GetLogHistory()
+
+ for i = 1, #history do
+ AddLocalMessage(history[i].message, history[i].messageType, history[i].timestamp)
+ end
+
+ game:GetService("LogService").MessageOut:connect(function(message, messageType)
+ AddLocalMessage(message, messageType, os.time())
+ end)
+
+ game:GetService("LogService").ServerMessageOut:connect(AddServerMessage)
+
+end
+
+local currentlyToggling = false
+function ToggleConsole.OnInvoke()
+ if currentlyToggling then
+ return
+ end
+
+ currentlyToggling = true
+ initializeDeveloperConsole()
+ Dev_Container.Visible = not Dev_Container.Visible
+ currentlyToggling = false
+end
diff --git a/app/files/CoreGui/158948138 b/app/files/CoreGui/158948138
new file mode 100644
index 0000000..6cfbae1
--- /dev/null
+++ b/app/files/CoreGui/158948138
@@ -0,0 +1,175 @@
+-- Creates the generic "ROBLOX" loading screen on startup
+-- Written by Ben Tkacheff, 2014
+
+local frame
+local forceRemovalTime = 5
+local destroyed = false
+
+Game:GetService("ContentProvider"):Preload("rbxasset://textures/roblox-logo.png")
+
+-- get control functions set up immediately
+function removeLoadingScreen()
+ if frame then frame:Destroy() end
+ if script then script:Destroy() end
+ destroyed = true
+end
+
+function startForceLoadingDoneTimer()
+ wait(forceRemovalTime)
+ removeLoadingScreen()
+end
+
+function gameIsLoaded()
+ if Game.ReplicatedFirst:IsDefaultLoadingGuiRemoved() then
+ removeLoadingScreen()
+ else
+ startForceLoadingDoneTimer()
+ end
+end
+
+function makeDefaultLoadingScreen()
+ if not settings():GetFFlag("NewLoadingScreen") then return end
+ if destroyed then return end
+
+ frame = Instance.new("Frame")
+ frame.ZIndex = 10
+ frame.Active = true
+ frame.Size = UDim2.new(1,0,1,0)
+ frame.BackgroundColor3 = Color3.new(48/255,90/255,206/255)
+
+ local robloxLogo = Instance.new("ImageLabel")
+ robloxLogo.BackgroundTransparency = 1
+ robloxLogo.ZIndex = 10
+ robloxLogo.Image = "rbxasset://textures/roblox-logo.png"
+ robloxLogo.Size = UDim2.new(0,1031,0,265)
+ robloxLogo.Position = UDim2.new(0.5,-515,0.5,-132)
+ robloxLogo.Name = "RobloxLogo"
+ robloxLogo.Parent = frame
+
+ local poweredByText = Instance.new("TextLabel")
+ poweredByText.Font = Enum.Font.SourceSansBold
+ poweredByText.FontSize = Enum.FontSize.Size24
+ poweredByText.TextWrap = true
+ poweredByText.TextColor3 = Color3.new(1,1,1)
+ poweredByText.BackgroundTransparency = 1
+ poweredByText.ZIndex = 10
+ poweredByText.Text = "This Game Powered By"
+ poweredByText.TextXAlignment = Enum.TextXAlignment.Left
+ poweredByText.Size = UDim2.new(1,0,0,40)
+ poweredByText.Position = UDim2.new(0,0,0,-50)
+ poweredByText.Name = "PoweredByText"
+ poweredByText.Parent = robloxLogo
+
+ local exitButton = Instance.new("ImageButton")
+ exitButton.ZIndex = 10
+ exitButton.BackgroundTransparency = 1
+ exitButton.Image = "rbxasset://textures/ui/CloseButton.png"
+ exitButton.Size = UDim2.new(0,22,0,22)
+ exitButton.Position = UDim2.new(1,-23,0,1)
+ exitButton.Name = "ExitButton"
+ exitButton:SetVerb("Exit")
+
+ UserSettings().GameSettings.FullscreenChanged:connect(function ( isFullScreen )
+ if isFullScreen then
+ exitButton.Parent = frame
+ else
+ exitButton.Parent = nil
+ end
+ end)
+ if UserSettings().GameSettings:InFullScreen()then
+ exitButton.Parent = frame
+ end
+
+ -- put something visible up asap
+ frame.Parent = Game.CoreGui.RobloxGui
+
+ local instanceText = Instance.new("TextLabel")
+ instanceText.Font = Enum.Font.SourceSansBold
+ instanceText.FontSize = Enum.FontSize.Size18
+ instanceText.TextWrap = true
+ instanceText.TextColor3 = Color3.new(1,1,1)
+ instanceText.BackgroundTransparency = 1
+ instanceText.ZIndex = 10
+ instanceText.Text = ""
+ instanceText.Size = UDim2.new(1,0,0,40)
+ instanceText.Position = UDim2.new(0,0,1,-60)
+ instanceText.Name = "InstanceText"
+ instanceText.Parent = frame
+
+ local loadingText = Instance.new("TextLabel")
+ loadingText.Font = Enum.Font.SourceSansBold
+ loadingText.FontSize = Enum.FontSize.Size36
+ loadingText.TextWrap = true
+ loadingText.TextColor3 = Color3.new(1,1,1)
+ loadingText.BackgroundTransparency = 1
+ loadingText.ZIndex = 10
+ loadingText.Text = "Loading"
+ loadingText.Size = UDim2.new(1,0,0,40)
+ loadingText.Position = UDim2.new(0,0,1,20)
+ loadingText.Name = "LoadingText"
+ loadingText.Parent = robloxLogo
+
+ local howManyDots = 0
+ local lastUpdateTime = tick()
+ local minUpdateTime = 0.3
+ local aspectRatio = 1031/265
+
+ function ResolutionChanged( prop )
+ if prop == "AbsoluteSize" then
+ local size = Game.CoreGui.RobloxGui.AbsoluteSize
+ if size.X >= 1031 then
+ robloxLogo.Size = UDim2.new(0,1031,0,265)
+ robloxLogo.Position = UDim2.new(0.5,-515,0.5,-132)
+ else
+ local sizeReducer = -0.05
+ while size.X < robloxLogo.AbsoluteSize.X do
+
+ robloxLogo.Size = UDim2.new(sizeReducer,1031,0,265)
+ local newY = robloxLogo.AbsoluteSize.X * 265/1031
+ robloxLogo.Size = UDim2.new(sizeReducer,1031,0,newY)
+ robloxLogo.Position = UDim2.new(0.5 - (sizeReducer/2),-515,0.5,-132)
+
+ sizeReducer = sizeReducer - 0.1
+ end
+ end
+ end
+ end
+
+ ResolutionChanged("AbsoluteSize")
+ Game.CoreGui.RobloxGui.Changed:connect(ResolutionChanged)
+
+ Game:GetService("RunService").RenderStepped:connect(function()
+ instanceText.Text = Game:GetMessage()
+
+ if tick() - lastUpdateTime >= minUpdateTime then
+ howManyDots = howManyDots + 1
+ if howManyDots > 5 then
+ howManyDots = 0
+ end
+
+ loadingText.Text = "Loading"
+ for i = 1, howManyDots do
+ loadingText.Text = loadingText.Text .. "."
+ end
+ lastUpdateTime = tick()
+ end
+ end)
+end
+
+makeDefaultLoadingScreen()
+
+Game.ReplicatedFirst.RemoveDefaultLoadingGuiSignal:connect(function()
+ removeLoadingScreen()
+end)
+if Game.ReplicatedFirst:IsDefaultLoadingGuiRemoved() then
+ removeLoadingScreen()
+ return
+end
+
+Game.Loaded:connect(function()
+ gameIsLoaded()
+end)
+
+if Game:IsLoaded() then
+ gameIsLoaded()
+end
\ No newline at end of file
diff --git a/app/files/CoreGui/35914081 b/app/files/CoreGui/35914081
new file mode 100644
index 0000000..9d7b873
--- /dev/null
+++ b/app/files/CoreGui/35914081
@@ -0,0 +1,290 @@
+-- creates the in-game gui sub menus for property tools
+-- written 9/27/2010 by Ben (jeditkacheff)
+
+local gui = script.Parent
+if gui:FindFirstChild("ControlFrame") then
+ gui = gui:FindFirstChild("ControlFrame")
+end
+
+local currentlySelectedButton = nil
+
+local localAssetBase = "rbxasset://textures/ui/"
+
+local selectedButton = Instance.new("ObjectValue")
+selectedButton.RobloxLocked = true
+selectedButton.Name = "SelectedButton"
+selectedButton.Parent = gui.BuildTools
+
+local closeButton = Instance.new("ImageButton")
+closeButton.Name = "CloseButton"
+closeButton.RobloxLocked = true
+closeButton.BackgroundTransparency = 1
+closeButton.Image = localAssetBase .. "CloseButton.png"
+closeButton.ZIndex = 2
+closeButton.Size = UDim2.new(0.2,0,0.05,0)
+closeButton.AutoButtonColor = false
+closeButton.Position = UDim2.new(0.75,0,0.01,0)
+
+
+
+function setUpCloseButtonState(button)
+
+ button.MouseEnter:connect(function()
+ button.Image = localAssetBase .. "CloseButton_dn.png"
+ end)
+ button.MouseLeave:connect(function()
+ button.Image = localAssetBase .. "CloseButton.png"
+ end)
+ button.MouseButton1Click:connect(function()
+ button.ClosedState.Value = true
+ button.Image = localAssetBase .. "CloseButton.png"
+ end)
+
+end
+
+-- nice selection animation
+function fadeInButton(button)
+
+ if currentlySelectedButton ~= nil then
+ currentlySelectedButton.Selected = false
+ currentlySelectedButton.ZIndex = 2
+ currentlySelectedButton.Frame.BackgroundTransparency = 1
+ end
+
+ local speed = 0.1
+ button.ZIndex = 3
+ while button.Frame.BackgroundTransparency > 0 do
+ button.Frame.BackgroundTransparency = button.Frame.BackgroundTransparency - speed
+ wait()
+ end
+ button.Selected = true
+
+ currentlySelectedButton = button
+ selectedButton.Value = currentlySelectedButton
+end
+
+------------------------------- create the color selection sub menu -----------------------------------
+
+local paintMenu = Instance.new("ImageLabel")
+local paintTool = gui.BuildTools.Frame.PropertyTools.PaintTool
+paintMenu.Name = "PaintMenu"
+paintMenu.RobloxLocked = true
+paintMenu.Parent = paintTool
+paintMenu.Position = UDim2.new(-2.7,0,-3,0)
+paintMenu.Size = UDim2.new(2.5,0,10,0)
+paintMenu.BackgroundTransparency = 1
+paintMenu.ZIndex = 2
+paintMenu.Image = localAssetBase .. "PaintMenu.png"
+
+local paintColorButton = Instance.new("ImageButton")
+paintColorButton.RobloxLocked = true
+paintColorButton.BorderSizePixel = 0
+paintColorButton.ZIndex = 2
+paintColorButton.Size = UDim2.new(0.200000003, 0,0.0500000007, 0)
+
+local selection = Instance.new("Frame")
+selection.RobloxLocked = true
+selection.BorderSizePixel = 0
+selection.BackgroundColor3 = Color3.new(1,1,1)
+selection.BackgroundTransparency = 1
+selection.ZIndex = 2
+selection.Size = UDim2.new(1.1,0,1.1,0)
+selection.Position = UDim2.new(-0.05,0,-0.05,0)
+selection.Parent = paintColorButton
+
+local header = 0.08
+local spacing = 18
+
+local count = 1
+
+function findNextColor()
+ colorName = tostring(BrickColor.new(count))
+ while colorName == "Medium stone grey" do
+ count = count + 1
+ colorName = tostring(BrickColor.new(count))
+ end
+ return count
+end
+
+for i = 0,15 do
+ for j = 1, 4 do
+ newButton = paintColorButton:clone()
+ newButton.RobloxLocked = true
+ newButton.BackgroundColor3 = BrickColor.new(findNextColor()).Color
+ newButton.Name = tostring(BrickColor.new(count))
+ count = count + 1
+ if j == 1 then newButton.Position = UDim2.new(0.08,0,i/spacing + header,0)
+ elseif j == 2 then newButton.Position = UDim2.new(0.29,0,i/spacing + header,0)
+ elseif j == 3 then newButton.Position = UDim2.new(0.5,0,i/spacing + header,0)
+ elseif j == 4 then newButton.Position = UDim2.new(0.71,0,i/spacing + header,0) end
+ newButton.Parent = paintMenu
+ end
+end
+
+local paintButtons = paintMenu:GetChildren()
+for i = 1, #paintButtons do
+ paintButtons[i].MouseButton1Click:connect(function()
+ fadeInButton(paintButtons[i])
+ end)
+end
+
+local paintCloseButton = closeButton:clone()
+paintCloseButton.RobloxLocked = true
+paintCloseButton.Parent = paintMenu
+
+local closedState = Instance.new("BoolValue")
+closedState.RobloxLocked = true
+closedState.Name = "ClosedState"
+closedState.Parent = paintCloseButton
+
+setUpCloseButtonState(paintCloseButton)
+
+------------------------------- create the material selection sub menu -----------------------------------
+
+local materialMenu = Instance.new("ImageLabel")
+local materialTool = gui.BuildTools.Frame.PropertyTools.MaterialSelector
+materialMenu.RobloxLocked = true
+materialMenu.Name = "MaterialMenu"
+materialMenu.Position = UDim2.new(-4,0,-3,0)
+materialMenu.Size = UDim2.new(2.5,0,6.5,0)
+materialMenu.BackgroundTransparency = 1
+materialMenu.ZIndex = 2
+materialMenu.Image = localAssetBase .. "MaterialMenu.png"
+materialMenu.Parent = materialTool
+
+local textures = {"Plastic","Wood","Slate","CorrodedMetal","Ice","Grass","Foil","DiamondPlate","Concrete"}
+
+local materialButtons = {}
+
+local materialButton = Instance.new("ImageButton")
+materialButton.RobloxLocked = true
+materialButton.BackgroundTransparency = 1
+materialButton.Size = UDim2.new(0.400000003, 0,0.16, 0)
+materialButton.ZIndex = 2
+
+selection.Parent = materialButton
+
+local current = 1
+function getTextureAndName(button)
+
+ if current > #textures then
+ button:remove()
+ return false
+ end
+ button.Image = localAssetBase .. textures[current] .. ".png"
+ button.Name = textures[current]
+ current = current + 1
+ return true
+
+end
+
+local ySpacing = 0.10
+local xSpacing = 0.07
+for i = 1,5 do
+ for j = 1,2 do
+ local button = materialButton:clone()
+ button.RobloxLocked = true
+ button.Position = UDim2.new((j -1)/2.2 + xSpacing,0,ySpacing + (i - 1)/5.5,0)
+ if getTextureAndName(button) then button.Parent = materialMenu else button:remove() end
+ table.insert(materialButtons,button)
+ end
+end
+
+
+for i = 1, #materialButtons do
+ materialButtons[i].MouseButton1Click:connect(function()
+ fadeInButton(materialButtons[i])
+ end)
+end
+
+local materialCloseButton = closeButton:clone()
+materialCloseButton.RobloxLocked = true
+materialCloseButton.Size = UDim2.new(0.2,0,0.08,0)
+materialCloseButton.Parent = materialMenu
+
+local closedState = Instance.new("BoolValue")
+closedState.RobloxLocked = true
+closedState.Name = "ClosedState"
+closedState.Parent = materialCloseButton
+
+setUpCloseButtonState(materialCloseButton)
+
+
+------------------------------- create the surface selection sub menu -----------------------------------
+
+local surfaceMenu = Instance.new("ImageLabel")
+local surfaceTool = gui.BuildTools.Frame.PropertyTools.InputSelector
+surfaceMenu.RobloxLocked = true
+surfaceMenu.Name = "SurfaceMenu"
+surfaceMenu.Position = UDim2.new(-2.6,0,-4,0)
+surfaceMenu.Size = UDim2.new(2.5,0,5.5,0)
+surfaceMenu.BackgroundTransparency = 1
+surfaceMenu.ZIndex = 2
+surfaceMenu.Image = localAssetBase .. "SurfaceMenu.png"
+surfaceMenu.Parent = surfaceTool
+
+textures = {"Smooth", "Studs", "Inlets", "Universal", "Glue", "Weld", "Hinge", "Motor"}
+current = 1
+
+local surfaceButtons = {}
+
+local surfaceButton = Instance.new("ImageButton")
+surfaceButton.RobloxLocked = true
+surfaceButton.BackgroundTransparency = 1
+surfaceButton.Size = UDim2.new(0.400000003, 0,0.19, 0)
+surfaceButton.ZIndex = 2
+
+selection.Parent = surfaceButton
+
+local ySpacing = 0.14
+local xSpacing = 0.07
+for i = 1,4 do
+ for j = 1,2 do
+ local button = surfaceButton:clone()
+ button.RobloxLocked = true
+ button.Position = UDim2.new((j -1)/2.2 + xSpacing,0,ySpacing + (i - 1)/4.6,0)
+ getTextureAndName(button)
+ button.Parent = surfaceMenu
+ table.insert(surfaceButtons,button)
+ end
+end
+
+for i = 1, #surfaceButtons do
+ surfaceButtons[i].MouseButton1Click:connect(function()
+ fadeInButton(surfaceButtons[i])
+ end)
+end
+
+local surfaceMenuCloseButton = closeButton:clone()
+surfaceMenuCloseButton.RobloxLocked = true
+surfaceMenuCloseButton.Size = UDim2.new(0.2,0,0.09,0)
+surfaceMenuCloseButton.Parent = surfaceMenu
+
+local closedState = Instance.new("BoolValue")
+closedState.RobloxLocked = true
+closedState.Name = "ClosedState"
+closedState.Parent = surfaceMenuCloseButton
+
+setUpCloseButtonState(surfaceMenuCloseButton)
+
+if game.CoreGui.Version >= 2 then
+ local function setupTweenTransition(button, menu, outXScale, inXScale)
+ button.Changed:connect(
+ function(property)
+ if property ~= "Selected" then
+ return
+ end
+ if button.Selected then
+ menu:TweenPosition(UDim2.new(inXScale, menu.Position.X.Offset, menu.Position.Y.Scale, menu.Position.Y.Offset),
+ Enum.EasingDirection.Out, Enum.EasingStyle.Quart, 1, true)
+ else
+ menu:TweenPosition(UDim2.new(outXScale, menu.Position.X.Offset, menu.Position.Y.Scale, menu.Position.Y.Offset),
+ Enum.EasingDirection.In, Enum.EasingStyle.Quart, 0.5, true)
+ end
+ end)
+ end
+
+ setupTweenTransition(paintTool, paintMenu, -2.7, 2.6)
+ setupTweenTransition(surfaceTool, surfaceMenu, -2.6, 2.6)
+ setupTweenTransition(materialTool, materialMenu, -4, 1.4)
+end
diff --git a/app/files/CoreGui/35914620 b/app/files/CoreGui/35914620
new file mode 100644
index 0000000..4612587
--- /dev/null
+++ b/app/files/CoreGui/35914620
@@ -0,0 +1,70 @@
+-- this script is responsible for keeping the gui proportions under control
+
+local screen = script.Parent
+
+local BottomLeftControl
+local BottomRightControl
+local TopLeftControl
+local BuildTools
+
+local controlFrame = script.Parent:FindFirstChild("ControlFrame")
+local loadoutPadding = 43
+local currentLoadout
+
+BottomLeftControl = controlFrame:FindFirstChild("BottomLeftControl")
+BottomRightControl = controlFrame:FindFirstChild("BottomRightControl")
+TopLeftControl = controlFrame:FindFirstChild("TopLeftControl")
+currentLoadout = script.Parent:FindFirstChild("CurrentLoadout")
+BuildTools = controlFrame:FindFirstChild("BuildTools")
+
+function makeYRelative()
+
+ BottomLeftControl.SizeConstraint = 2
+ BottomRightControl.SizeConstraint = 2
+ if TopLeftControl then TopLeftControl.SizeConstraint = 2 end
+ if currentLoadout then currentLoadout.SizeConstraint = 2 end
+ if BuildTools then BuildTools.Frame.SizeConstraint = 2 end
+
+ BottomLeftControl.Position = UDim2.new(0,0,1,-BottomLeftControl.AbsoluteSize.Y)
+ BottomRightControl.Position = UDim2.new(1,-BottomRightControl.AbsoluteSize.X,1,-BottomRightControl.AbsoluteSize.Y)
+
+end
+
+
+
+function makeXRelative()
+
+ BottomLeftControl.SizeConstraint = 1
+ BottomRightControl.SizeConstraint = 1
+ if TopLeftControl then TopLeftControl.SizeConstraint = 1 end
+ if currentLoadout then currentLoadout.SizeConstraint = 1 end
+ if BuildTools then BuildTools.Frame.SizeConstraint = 1 end
+
+ BottomLeftControl.Position = UDim2.new(0,0,1,-BottomLeftControl.AbsoluteSize.Y)
+ BottomRightControl.Position = UDim2.new(1,-BottomRightControl.AbsoluteSize.X,1,-BottomRightControl.AbsoluteSize.Y)
+
+end
+
+local function resize()
+ if screen.AbsoluteSize.x > screen.AbsoluteSize.y then
+ makeYRelative()
+ else
+ makeXRelative()
+ end
+ if currentLoadout then
+ currentLoadout.Position =
+ UDim2.new(0,screen.AbsoluteSize.X/2 -currentLoadout.AbsoluteSize.X/2,currentLoadout.Position.Y.Scale,-currentLoadout.AbsoluteSize.Y - loadoutPadding)
+ end
+end
+screen.Changed:connect(function(property)
+
+ if property == "AbsoluteSize" then
+ wait()
+ resize()
+ end
+
+end)
+
+wait()
+resize()
+
diff --git a/app/files/CoreGui/36040464 b/app/files/CoreGui/36040464
new file mode 100644
index 0000000..1060c5c
--- /dev/null
+++ b/app/files/CoreGui/36040464
@@ -0,0 +1,35 @@
+-- this script is responsible for moving the material menu in and out when selected/deselected
+
+local button = script.Parent
+local activated = false
+
+function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+
+waitForChild(script.Parent,"PaintMenu")
+local menu = script.Parent:FindFirstChild("PaintMenu")
+
+local moving = false
+local speed = 0.35
+
+button.Changed:connect(function(property)
+
+ if property ~= "Selected" then return end
+ if moving then return end
+ moving = true activated = button.Selected
+ if activated then
+ while menu.Position.X.Scale < 2.6 do
+ menu.Position = UDim2.new(menu.Position.X.Scale + speed,menu.Position.X.Offset,menu.Position.Y.Scale,menu.Position.Y.Offset)
+ wait()
+ end
+ else
+ while menu.Position.X.Scale > -2.7 do
+ menu.Position = UDim2.new(menu.Position.X.Scale - speed,menu.Position.X.Offset,menu.Position.Y.Scale,menu.Position.Y.Offset)
+ wait()
+ end
+ end
+ moving = false
+end)
\ No newline at end of file
diff --git a/app/files/CoreGui/36040483 b/app/files/CoreGui/36040483
new file mode 100644
index 0000000..d28a1aa
--- /dev/null
+++ b/app/files/CoreGui/36040483
@@ -0,0 +1,36 @@
+-- this script is responsible for moving the surface menu in and out when selected/deselected
+
+local button = script.Parent
+local activated = false
+
+function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+
+waitForChild(script.Parent,"SurfaceMenu")
+local menu = script.Parent:FindFirstChild("SurfaceMenu")
+
+local speed = 0.35
+local moving = false
+
+button.Changed:connect(function(property)
+
+ if property ~= "Selected" then return end
+ if moving then return end
+ moving = true
+ activated = button.Selected
+ if activated then
+ while menu.Position.X.Scale < 2.6 do
+ menu.Position = UDim2.new(menu.Position.X.Scale + speed,menu.Position.X.Offset,menu.Position.Y.Scale,menu.Position.Y.Offset)
+ wait()
+ end
+ else
+ while menu.Position.X.Scale > -2.6 do
+ menu.Position = UDim2.new(menu.Position.X.Scale - speed,menu.Position.X.Offset,menu.Position.Y.Scale,menu.Position.Y.Offset)
+ wait()
+ end
+ end
+
+moving = false end)
\ No newline at end of file
diff --git a/app/files/CoreGui/36040495 b/app/files/CoreGui/36040495
new file mode 100644
index 0000000..5b54f7c
--- /dev/null
+++ b/app/files/CoreGui/36040495
@@ -0,0 +1,36 @@
+-- this script is responsible for moving the material menu in and out when selected/deselected
+
+local button = script.Parent
+local activated = false
+
+function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+
+waitForChild(script.Parent,"MaterialMenu")
+local menu = script.Parent:FindFirstChild("MaterialMenu")
+
+local moving = false
+local speed = 0.35
+
+button.Changed:connect(function(property)
+ if property ~= "Selected" then return end
+ if moving then return end
+
+ moving = true
+ activated = button.Selected
+ if activated then
+ while menu.Position.X.Scale < 1.4 do
+ menu.Position = UDim2.new(menu.Position.X.Scale + speed,menu.Position.X.Offset,menu.Position.Y.Scale,menu.Position.Y.Offset)
+ wait()
+ end
+ else
+ while menu.Position.X.Scale > -4 do
+ menu.Position = UDim2.new(menu.Position.X.Scale - speed,menu.Position.X.Offset,menu.Position.Y.Scale,menu.Position.Y.Offset)
+ wait()
+ end
+ end
+
+moving = false end)
\ No newline at end of file
diff --git a/app/files/CoreGui/36051740 b/app/files/CoreGui/36051740
new file mode 100644
index 0000000..5fc93c3
--- /dev/null
+++ b/app/files/CoreGui/36051740
@@ -0,0 +1,707 @@
+--[[if game.Players.LocalPlayer.Name == "Player" then
+ return
+end]]
+
+-- Script Globals
+local buildDeleteID = 36738185
+local buildStamperID = 87198196
+local buildGroupDraggerID = 36334760
+local buildConfigID = 87240571
+local buildRotateID = 0
+
+local buildTools = {}
+
+local player = nil
+local backpack = nil
+
+
+
+
+-- Functions
+local function waitForProperty(instance, name)
+ while not instance[name] do
+ instance.Changed:wait()
+ end
+end
+
+local function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+
+function getLatestPlayer()
+ waitForProperty(game.Players,"LocalPlayer")
+ player = game.Players.LocalPlayer
+ waitForChild(player,"Backpack")
+ backpack = player.Backpack
+end
+
+function backpackHasTool(tool)
+ local backpackChildren = backpack:GetChildren()
+ for i = 1, #backpackChildren do
+ if backpackChildren[i] == tool then
+ return true
+ end
+ end
+ return false
+end
+
+function getToolAssetID(assetID,toolName)
+ local newTool = game:GetService("InsertService"):LoadAsset(assetID)
+ newTool = newTool:FindFirstChild(toolName)
+ return newTool
+end
+
+function giveAssetId(assetID,toolName)
+ local theTool = getToolAssetID(assetID,toolName)
+ if theTool and not backpackHasTool(theTool) then
+ theTool.Parent = backpack
+ table.insert(buildTools,theTool)
+ end
+end
+
+function givePlayerBuildTools()
+ getLatestPlayer()
+
+ giveAssetId(buildStamperID,"StamperTool")
+ giveAssetId(buildDeleteID,"BuildDelete")
+ giveAssetId(buildGroupDraggerID,"BuildGroupDragger")
+ giveAssetId(buildRotateID,"BuildRotate")
+ giveAssetId(buildConfigID,"BuildConfiguration")
+end
+
+function takePlayerBuildTools()
+ for k,v in ipairs(buildTools) do
+ v.Parent = nil
+ end
+ buildTools = {}
+end
+
+
+
+print("start script")
+-- Start Script
+if game.CoreGui.RobloxGui.ControlFrame.BottomLeftControl:FindFirstChild("ToolButton") then -- we are still using old build tools (TODO: remove this when new tools are good enough)
+ print("old tools")
+ local localAssetBase = "rbxasset://textures/ui/"
+
+ local control = script.Parent:FindFirstChild("ControlFrame")
+ local bottomLeftControls
+ local buildTools
+
+ if control then
+ bottomLeftControls = control.BottomLeftControl
+ buildTools = control.BuildTools
+ else
+ bottomLeftControls = script.Parent.BottomLeftControl
+ buildTools = script.Parent.BuildTools
+ end
+
+ buildToolsVisible = false
+
+ -- used right now to push build tools down a bit (should change in client)
+ buildTools.Frame.Position = UDim2.new(-0.15,0,0.3,0)
+
+ bottomLeftControls.ToolButton.Image = localAssetBase .. "ToolButton.png"
+
+ local prevObject = nil
+
+ local tryCount = 0
+
+ local location = 0
+
+ local name
+
+ local buttons = {}
+ local value
+
+ local player
+ local backpack
+
+ local resetCon
+
+ local equippedTool
+ local selectedButton
+
+ -- the build tools
+ local buildResize
+ local buildClone
+ local buildInsert
+ local buildDragger
+ local buildColor
+ local buildGroupDragger
+ local buildSurface
+ local buildMaterial
+ local buildConfiguration
+ local buildDelete
+
+ -- the build tools asset ids
+ local buildResizeID = 36738142
+ local buildCloneID = 36017373
+ local buildInsertID = 36431591
+ local buildDraggerID = 36068233
+ local buildColorID = 35205409
+ local buildGroupDraggerID = 36334760
+ local buildSurfaceID = 35226945
+ local buildMaterialID = 35223828
+ local buildConfigurationID = 36270159
+ local buildDeleteID = 36738185
+
+
+ function waitForProperty(instance, name)
+ while not instance[name] do
+ instance.Changed:wait()
+ end
+ end
+
+ function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+ end
+
+ function setPlayerAndBackpack()
+ waitForProperty(game.Players,"LocalPlayer")
+ player = game.Players.LocalPlayer
+ backpack = player.Backpack
+ end
+
+ setPlayerAndBackpack()
+
+ function playerReset()
+
+ loadTools()
+ local prevObject = nil
+ local equippedTool = nil
+ setPlayerAndBackpack()
+ resetCon:disconnect()
+ resetCon = game.Players.LocalPlayer.CharacterAdded:connect(playerReset)
+
+ end
+
+ resetCon = game.Players.LocalPlayer.CharacterAdded:connect(playerReset)
+
+ function giveSelectedValue(tool)
+
+ if tool:FindFirstChild("SelectedButton") == nil then
+ local selected = Instance.new("ObjectValue")
+ selected.Name = "SelectedButton"
+ selected.RobloxLocked = true
+
+ if tool.Name == "BuildConfiguration" then
+ selected.Value = buildTools.Frame.MiscTools.PropertyTool
+ elseif tool.Name == "BuildInsert" then
+ selected.Value = buildTools.Frame
+ end
+ selected.Parent = tool
+ end
+
+ end
+
+ function loadTools()
+ -- load in all tools to decrease issues with loading, also don't have to keep reloading assets
+ buildResize = game:GetService("InsertService"):LoadAsset(buildResizeID)
+ buildResize = buildResize:FindFirstChild("BuildResize")
+ giveSelectedValue(buildResize)
+ buildResize.Parent = backpack
+
+ buildClone = game:GetService("InsertService"):LoadAsset(buildCloneID)
+ buildClone = buildClone:FindFirstChild("BuildClone")
+ giveSelectedValue(buildClone)
+ buildClone.Parent = backpack
+
+ buildInsert = game:GetService("InsertService"):LoadAsset(buildInsertID)
+ buildInsert = buildInsert:FindFirstChild("BuildInsert")
+ giveSelectedValue(buildInsert)
+ buildInsert.Parent = backpack
+
+ buildDragger = game:GetService("InsertService"):LoadAsset(buildDraggerID)
+ buildDragger = buildDragger:FindFirstChild("BuildDragger")
+ giveSelectedValue(buildDragger)
+ buildDragger.Parent = backpack
+
+ buildColor = game:GetService("InsertService"):LoadAsset(buildColorID)
+ buildColor = buildColor:FindFirstChild("BuildColorTester")
+ giveSelectedValue(buildColor)
+ buildColor.Parent = backpack
+
+ buildGroupDragger = game:GetService("InsertService"):LoadAsset(buildGroupDraggerID)
+ buildGroupDragger = buildGroupDragger:FindFirstChild("BuildGroupDragger")
+ giveSelectedValue(buildGroupDragger)
+ buildGroupDragger.Parent = backpack
+
+ buildSurface = game:GetService("InsertService"):LoadAsset(buildSurfaceID)
+ buildSurface = buildSurface:FindFirstChild("BuildSurfaceTest")
+ giveSelectedValue(buildSurface)
+ buildSurface.Parent = backpack
+
+ buildMaterial = game:GetService("InsertService"):LoadAsset(buildMaterialID)
+ buildMaterial = buildMaterial:FindFirstChild("BuildMaterialTest")
+ giveSelectedValue(buildMaterial)
+ buildMaterial.Parent = backpack
+
+ buildConfiguration = game:GetService("InsertService"):LoadAsset(buildConfigurationID)
+ buildConfiguration = buildConfiguration:FindFirstChild("BuildConfiguration")
+ giveSelectedValue(buildConfiguration)
+ buildConfiguration.Parent = backpack
+
+ buildDelete = game:GetService("InsertService"):LoadAsset(buildDeleteID)
+ buildDelete = buildDelete:FindFirstChild("BuildDelete")
+ giveSelectedValue(buildDelete)
+ buildDelete.Parent = backpack
+ end
+ loadTools()
+
+ waitForChild(buildTools,"SelectedButton")
+
+ buildTools.SelectedButton.Changed:connect(function(property)
+
+ if value == buildTools.SelectedButton.Value then return end
+ value = buildTools.SelectedButton.Value
+
+ selectedButton = buildTools.SelectedButton.Value
+
+ if equippedTool then
+ equippedTool.SelectedButton.Value = selectedButton
+ end
+ end)
+
+
+
+ local frames = buildTools.Frame:GetChildren()
+ for i = 1, #frames do
+ if frames[i]:IsA("Frame") and frames[i].Name ~= "Divs" then
+ local buttonSubSet = frames[i]:GetChildren()
+ for j = 1, #buttonSubSet do
+ table.insert(buttons,buttonSubSet[j])
+ end
+ end
+ end
+
+ function unEquipAnyItems()
+
+ playerItems = player.Character:GetChildren()
+ for i = 1, #playerItems do
+ if playerItems[i]:isA("Tool") or playerItems[i]:isA("HopperBin") then
+ playerItems[i].Parent = backpack
+ return
+ end
+ end
+
+ end
+ unEquipAnyItems()
+
+ --------------------- Build Bar Tool Tip Code -----------------------------
+
+ function setUpText(toolTip)
+
+ local name = toolTip.Parent.Name
+ if name == "CloneObject" then toolTip.Text = "Copy Part"
+ elseif name == "DeleteObject" then toolTip.Text = "Delete Part"
+ elseif name == "InsertObject" then toolTip.Text = "Insert Part"
+ elseif name == "PropertyTool" then toolTip.Text = "Edit Part"
+ elseif name == "GroupMove" then toolTip.Text = "Move Models and Parts"
+ elseif name == "PartMove" then toolTip.Text = "Move Parts"
+ elseif name == "ScaleObject" then toolTip.Text = "Resize a part"
+ elseif name == "InputSelector" then toolTip.Text = "Change Surface"
+ elseif name == "MaterialSelector" then toolTip.Text = "Change Material"
+ elseif name == "PaintTool" then toolTip.Text = "Change Color" end
+
+ end
+
+ local fadeSpeed = 0.1
+ function buildToolsTips()
+
+ local frame = Instance.new("TextLabel")
+ frame.Name = "ToolTip"
+ frame.Text = "Hi! I'm a ToolTip!"
+ frame.ZIndex = 10
+ frame.Size = UDim2.new(2,0,1,0)
+ frame.Position = UDim2.new(1,0,0,0)
+ frame.BackgroundColor3 = Color3.new(1,1,153/255)
+ frame.BackgroundTransparency = 1
+ frame.TextTransparency = 1
+ frame.TextWrap = true
+
+ for i = 1, #buttons do
+ local tip = frame:Clone()
+ tip.RobloxLocked = true
+ tip.Parent = buttons[i]
+ setUpText(tip)
+ local inside = Instance.new("BoolValue")
+ inside.Value = false
+ inside.RobloxLocked = true
+ tip.Parent.MouseEnter:connect(function()
+ inside.Value = true
+ wait(1.2)
+ if inside.Value then
+ while inside.Value and tip.BackgroundTransparency > 0 do
+ tip.BackgroundTransparency = tip.BackgroundTransparency - fadeSpeed
+ tip.TextTransparency = tip.TextTransparency - fadeSpeed
+ wait()
+ end
+ end
+ end)
+ tip.Parent.MouseLeave:connect(function()
+ inside.Value = false
+ tip.BackgroundTransparency = 1
+ tip.TextTransparency = 1
+ end)
+ tip.Parent.MouseButton1Click:connect(function()
+ inside.Value = false
+ tip.BackgroundTransparency = 1
+ tip.TextTransparency = 1
+ end)
+ end
+ end
+
+ --------------------- End Build Bar Tool Tip Code --------------------------
+
+
+
+ ----------------------------- Reset Button Code ----------------------------
+ function reset(subset)
+
+ local buttons = subset:GetChildren()
+ if subset.Name == "AddDeleteTools" then
+ for i = 1, #buttons do
+ buttons[i].Selected = false
+ if buttons[i].Name == "CloneObject" then buttons[i].Image = localAssetBase .. "CloneButton.png"
+ elseif buttons[i].Name == "DeleteObject" then buttons[i].Image = localAssetBase .. "DeleteButton.png"
+ elseif buttons[i].Name == "InsertObject" then buttons[i].Image = localAssetBase .. "InsertButton.png" end
+ end
+
+
+ elseif subset.Name == "MiscTools" then
+ for i = 1, #buttons do
+ buttons[i].Selected = false
+ if buttons[i].Name == "PropertyTool" then buttons[i].Image = localAssetBase .. "PropertyButton.png" end
+ end
+
+
+ elseif subset.Name == "PhysicalTools" then
+ for i = 1, #buttons do
+ buttons[i].Selected = false
+ if buttons[i].Name == "GroupMove" then buttons[i].Image = localAssetBase .. "GroupMoveButton.png"
+ elseif buttons[i].Name == "PartMove" then buttons[i].Image = localAssetBase .."PartMoveButton.png"
+ elseif buttons[i].Name == "ScaleObject" then buttons[i].Image = localAssetBase .. "ScaleButton.png" end
+ end
+
+
+ elseif subset.Name == "PropertyTools" then
+ for i = 1, #buttons do
+ buttons[i].Selected = false
+ if buttons[i].Name == "InputSelector" then buttons[i].Image = localAssetBase .. "SurfaceButton.png"
+ elseif buttons[i].Name == "MaterialSelector" then buttons[i].Image = localAssetBase .. "MaterialButton.png"
+ elseif buttons[i].Name == "PaintTool" then buttons[i].Image = localAssetBase .. "PaintButton.png" end
+ end
+ end
+
+ end
+
+ function resetAllButtons()
+
+ local categories = buildTools.Frame:GetChildren()
+ for i = 1, #categories do
+ reset(categories[i])
+ end
+
+ end
+
+ ----------------------------- End Reset Button Code -------------------------
+ resetAllButtons()
+
+
+
+ -- sets button to active image
+ function setButtonStateActive(Object, Name)
+
+ if Name == "BuildInsert" then Object.Image = localAssetBase .. "InsertButton_dn.png" Object.Selected = true
+ elseif Name == "BuildDelete" then Object.Image = localAssetBase .. "DeleteButton_dn.png" Object.Selected = true
+ elseif Name == "BuildClone" then Object.Image = localAssetBase .. "CloneButton_dn.png" Object.Selected = true
+ elseif Name == "BuildConfiguration" then Object.Image = localAssetBase .. "PropertyButton_dn.png" Object.Selected = true
+ elseif Name == "BuildDragger" then Object.Image = localAssetBase .. "PartMoveButton_dn.png" Object.Selected = true
+ elseif Name == "BuildGroupDragger" then Object.Image = localAssetBase .. "GroupMoveButton_dn.png" Object.Selected = true
+ elseif Name == "BuildResize" then Object.Image = localAssetBase .. "ScaleButton_dn.png" Object.Selected = true
+ elseif Name == "BuildSurfaceTest" then Object.Image = localAssetBase .. "SurfaceButton_dn.png" Object.Selected = true
+ elseif Name == "BuildMaterialTest" then Object.Image = localAssetBase .. "MaterialButton_dn.png" Object.Selected = true
+ elseif Name == "BuildColorTester" then Object.Image = localAssetBase .. "PaintButton_dn.png" Object.Selected = true
+ end
+
+ end
+
+
+ function removeTool(tool, Object)
+
+ playerItems = player.Characer:GetChildren()
+ for i = 1, #playerItems do
+ if playerItems[i].Name == tool and playerItems[i]:isA("Tool") then
+ playerItems[i].Parent = backpack
+ playerItems[i]:remove()
+ return
+ end
+ end
+
+ backpackStuff = backpack:GetChildren()
+ for i = 1, #backpackStuff do
+ if backpackStuff[i].Name == tool then
+ backpackStuff[i].Parent = nil
+ backpackStuff[i]:remove()
+ return
+ end
+ end
+
+ end
+
+
+
+ function getTool(tool)
+ playerItems = player.Character:GetChildren()
+ for i = 1, #playerItems do
+ if playerItems[i].Name == tool then
+ if playerItems[i]:isA("Tool") then return playerItems[i], true end
+ end
+ end
+
+ backpackStuff = backpack:GetChildren()
+ for i = 1, #backpackStuff do
+ if backpackStuff[i].Name == tool then
+ return backpackStuff[i], false
+ end
+ end
+ end
+
+ function toolDeselection(Name,Object,id)
+
+ local hasTool, equipped = getTool(Name)
+ if equipped then
+ resetAllButtons()
+ unEquipAnyItems()
+ hasTool.Parent = nil
+ hasTool:remove()
+
+ if equippedTool then
+ equippedTool.Parent = nil
+ equippedTool:remove()
+ equippedTool = nil
+ end
+ end
+
+ end
+
+
+ -- Used to find allocated tool that we got at beginning of script
+ function getToolLocal(Name)
+
+ if Name == "BuildInsert" then return buildInsert
+ elseif Name == "BuildDelete" then return buildDelete
+ elseif Name == "BuildClone" then return buildClone
+ elseif Name == "BuildConfiguration" then return buildConfiguration
+ elseif Name == "BuildDragger" then return buildDragger
+ elseif Name == "BuildGroupDragger" then return buildGroupDragger
+ elseif Name == "BuildResize" then return buildResize
+ elseif Name == "BuildSurfaceTest" then return buildSurface
+ elseif Name == "BuildMaterialTest" then return buildMaterial
+ elseif Name == "BuildColorTester" then return buildColor
+ end
+
+ end
+
+ function assureTool(tool, id, Name)
+
+ if tool == nil or type(tool) ~= "userdata" or tool:FindFirstChild("Handle") == nil then
+ tool = game:GetService("InsertService"):LoadAsset(id)
+
+ local instances = tool:GetChildren()
+ if #instances == 0 then
+ tool:Remove()
+ return nil
+ end
+
+ tool = tool:FindFirstChild(Name)
+ tool.Parent = backpack
+ end
+
+ end
+
+ -- general function to get tool when button is clicked
+ function toolSelection(Name,Object,id)
+ local tool = nil
+ local hasTool, equipped = getTool(Name)
+ if equipped then
+
+ if hasTool:FindFirstChild("SelectedButton") == false then
+ giveSelectedValue(hasTool)
+ end
+
+ resetAllButtons()
+ unEquipAnyItems()
+ hasTool.Parent = nil
+
+ if equippedTool ~= nil then
+ equippedTool.Parent = backpack
+ end
+
+ elseif hasTool then
+
+ -- failsafe to make sure we load in tool, no matter what
+ assureTool(hasTool,id, Name)
+ if hasTool == nil then return nil end
+
+ unEquipAnyItems()
+ Object.Selected = true
+ hasTool.Parent = player.Character
+ equippedTool = hasTool
+ hasTool.Unequipped:connect(function() resetAllButtons() end)
+ setButtonStateActive(Object, Name)
+
+ else
+
+ -- first try to find tool we already loaded in
+ tool = getToolLocal(Name)
+
+ -- failsafe to make sure we load in tool, no matter what
+ assureTool(tool,id, Name)
+ if tool == nil then return nil end
+
+ if tool then
+
+ if tool:FindFirstChild("SelectedButton") == nil then
+ local selected = Instance.new("ObjectValue")
+ selected.Name = "SelectedButton"
+ selected.RobloxLocked = true
+
+ if Name == "BuildConfiguration" then
+ selected.Value = buildTools.Frame.MiscTools.PropertyTool
+ elseif Name == "BuildInsert" then
+ selected.Value = buildTools.Frame
+ end
+
+ selected.Parent = tool
+ end
+
+ unEquipAnyItems()
+ if equippedTool ~= nil then
+ if prevObject then resetAllButtons() end
+ equippedTool.Parent = backpack
+ end
+
+ equippedTool = tool
+ equippedTool.Parent = player.Character
+
+ Object.Selected = true
+ equippedTool.Unequipped:connect(function() resetAllButtons() end)
+ setButtonStateActive(Object, Name)
+ end
+ end
+ prevObject = Object
+ end
+
+
+ -- used to animate the tool bar open/close
+ local scrollSpeed = 0.01
+ function openCloseTools()
+ buildToolsVisible = not buildToolsVisible
+ if buildToolsVisible then
+ bottomLeftControls.ToolButton.Image = localAssetBase .. "ToolButton_dn.png"
+ buildTools.Frame.CloseButton.Image = localAssetBase .. "CloseButton.png"
+ buildTools.Frame.Size = UDim2.new(0.15,0,0.57,0)
+ buildTools.Frame.Position = UDim2.new(-0.1, 0,buildTools.Frame.Position.Y.Scale,0)
+ while buildTools.Frame.Position.X.Scale < -0.01 and buildToolsVisible do
+ buildTools.Frame.Position = UDim2.new(buildTools.Frame.Position.X.Scale + scrollSpeed, 0,buildTools.Frame.Position.Y.Scale,0)
+ wait()
+ end
+ buildTools.Frame.Position = UDim2.new(0, 0,buildTools.Frame.Position.Y.Scale,0)
+ else
+ bottomLeftControls.ToolButton.Image = localAssetBase .. "ToolButton.png"
+ while buildTools.Frame.AbsolutePosition.X + buildTools.Frame.AbsoluteSize.X > 0 and not buildToolsVisible do
+ buildTools.Frame.Position = UDim2.new(buildTools.Frame.Position.X.Scale - scrollSpeed, 0,buildTools.Frame.Position.Y.Scale,0)
+ wait()
+ end
+ buildTools.Frame.Size = UDim2.new(0,buildTools.Frame.AbsoluteSize.X,buildTools.Frame.Size.Y.Scale,buildTools.Frame.Size.Y.Offset)
+ buildTools.Frame.Position = UDim2.new(0,-buildTools.Frame.AbsoluteSize.X,buildTools.Frame.Position.Y.Scale,buildTools.Frame.Position.Y.Offset)
+ end
+ end
+
+ -- setup tool button listeners
+ bottomLeftControls.ToolButton.MouseButton1Click:connect(openCloseTools)
+ bottomLeftControls.ToolButton.MouseEnter:connect(function() if not buildToolsVisible then bottomLeftControls.ToolButton.Image = localAssetBase .. "ToolButton_dn.png" end end)
+ bottomLeftControls.ToolButton.MouseLeave:connect(function() if not buildToolsVisible then bottomLeftControls.ToolButton.Image = localAssetBase .. "ToolButton.png" end end)
+
+ -- close button on build tools
+ buildTools.Frame.CloseButton.MouseButton1Click:connect(openCloseTools)
+ buildTools.Frame.CloseButton.MouseEnter:connect(function() buildTools.Frame.CloseButton.Image = localAssetBase .. "CloseButton_dn.png" end)
+ buildTools.Frame.CloseButton.MouseLeave:connect(function() buildTools.Frame.CloseButton.Image = localAssetBase .. "CloseButton.png" end)
+
+ buildToolsTips()
+
+ -- Add/Delete Tools
+ buildTools.Frame.AddDeleteTools.DeleteObject.MouseButton1Click:connect(function()
+ toolSelection("BuildDelete", buildTools.Frame.AddDeleteTools.DeleteObject,buildDeleteID) end)
+ buildTools.Frame.AddDeleteTools.CloneObject.MouseButton1Click:connect(function()
+ toolSelection("BuildClone",buildTools.Frame.AddDeleteTools.CloneObject,buildCloneID) end)
+ buildTools.Frame.AddDeleteTools.InsertObject.MouseButton1Click:connect(function()
+ toolSelection("BuildInsert",buildTools.Frame.AddDeleteTools.InsertObject,buildInsertID)
+ if(buildTools.Frame:FindFirstChild("InsertToolboxMain")) then
+ buildTools.Frame.InsertToolboxMain.InsertMainDialog.InsertTab.CloseButton.ClosedState.Changed:connect(function(value)
+ if value == true then
+ toolSelection("BuildInsert",buildTools.Frame.AddDeleteTools.InsertObject,buildInsertID)
+ if(buildTools.Frame:FindFirstChild("InsertToolboxMain")) then
+ buildTools.Frame.InsertToolboxMain.InsertMainDialog.InsertTab.CloseButton.ClosedState.Value = false
+ end
+ end
+ end)
+ end
+
+ end)
+
+ -- Physical Tools
+ buildTools.Frame.PhysicalTools.ScaleObject.MouseButton1Click:connect(function()
+ toolSelection("BuildResize",buildTools.Frame.PhysicalTools.ScaleObject, buildResizeID) end)
+ --[[buildTools.Frame.PhysicalTools.PartMove.MouseButton1Click:connect(function()
+ toolSelection("BuildDragger",buildTools.Frame.PhysicalTools.PartMove,buildDraggerID) end)]]
+ buildTools.Frame.PhysicalTools.GroupMove.MouseButton1Click:connect(function()
+ toolSelection("BuildGroupDragger",buildTools.Frame.PhysicalTools.GroupMove,buildGroupDraggerID) end)
+
+ -- Property Tools
+ buildTools.Frame.PropertyTools.MaterialSelector.MouseButton1Click:connect(function()
+ toolSelection("BuildMaterialTest",buildTools.Frame.PropertyTools.MaterialSelector,buildMaterialID) end)
+ buildTools.Frame.PropertyTools.MaterialSelector.MaterialMenu.CloseButton.ClosedState.Changed:connect(function(value)
+ if value == true then
+ toolSelection("BuildMaterialTest",buildTools.Frame.PropertyTools.MaterialSelector,buildMaterialID)
+ buildTools.Frame.PropertyTools.MaterialSelector.MaterialMenu.CloseButton.ClosedState.Value = false
+ end
+ end)
+ buildTools.Frame.PropertyTools.PaintTool.MouseButton1Click:connect(function()
+ toolSelection("BuildColorTester",buildTools.Frame.PropertyTools.PaintTool, buildColorID) end)
+ buildTools.Frame.PropertyTools.PaintTool.PaintMenu.CloseButton.ClosedState.Changed:connect(function(value)
+ if value == true then
+ toolSelection("BuildColorTester",buildTools.Frame.PropertyTools.PaintTool,buildColorID)
+ buildTools.Frame.PropertyTools.PaintTool.PaintMenu.CloseButton.ClosedState.Value = false
+ end
+ end)
+ buildTools.Frame.PropertyTools.InputSelector.MouseButton1Click:connect(function()
+ toolSelection("BuildSurfaceTest",buildTools.Frame.PropertyTools.InputSelector, buildSurfaceID) end)
+ buildTools.Frame.PropertyTools.InputSelector.SurfaceMenu.CloseButton.ClosedState.Changed:connect(function(value)
+ if value == true then
+ toolSelection("BuildSurfaceTest",buildTools.Frame.PropertyTools.InputSelector,buildSurfaceID)
+ buildTools.Frame.PropertyTools.InputSelector.SurfaceMenu.CloseButton.ClosedState.Value = false
+ end
+ end)
+
+ -- Misc Tools
+ buildTools.Frame.MiscTools.PropertyTool.MouseButton1Click:connect(function()
+ toolSelection("BuildConfiguration",buildTools.Frame.MiscTools.PropertyTool, buildConfigurationID) end)
+
+else -- we are using new build tools
+ getLatestPlayer()
+
+ givePlayerBuildTools()
+
+ -- If player dies, we make sure to give them build tools again
+ player.CharacterAdded:connect(function()
+ takePlayerBuildTools()
+ givePlayerBuildTools()
+ end)
+end
\ No newline at end of file
diff --git a/app/files/CoreGui/36868950 b/app/files/CoreGui/36868950
new file mode 100644
index 0000000..f0fee74
--- /dev/null
+++ b/app/files/CoreGui/36868950
@@ -0,0 +1,109 @@
+local controlFrame = script.Parent:FindFirstChild("ControlFrame")
+
+if not controlFrame then return end
+
+local topLeftControl = controlFrame:FindFirstChild("TopLeftControl")
+local bottomLeftControl = controlFrame:FindFirstChild("BottomLeftControl")
+local bottomRightControl = controlFrame:FindFirstChild("BottomRightControl")
+
+
+local frameTip = Instance.new("TextLabel")
+frameTip.Name = "ToolTip"
+frameTip.Text = ""
+frameTip.Font = Enum.Font.ArialBold
+frameTip.FontSize = Enum.FontSize.Size12
+frameTip.TextColor3 = Color3.new(1,1,1)
+frameTip.BorderSizePixel = 0
+frameTip.ZIndex = 10
+frameTip.Size = UDim2.new(2,0,1,0)
+frameTip.Position = UDim2.new(1,0,0,0)
+frameTip.BackgroundColor3 = Color3.new(0,0,0)
+frameTip.BackgroundTransparency = 1
+frameTip.TextTransparency = 1
+frameTip.TextWrap = true
+
+local inside = Instance.new("BoolValue")
+inside.Name = "inside"
+inside.Value = false
+inside.Parent = frameTip
+
+function setUpListeners(frameToListen)
+ local fadeSpeed = 0.1
+ frameToListen.Parent.MouseEnter:connect(function()
+ if frameToListen:FindFirstChild("inside") then
+ frameToListen.inside.Value = true
+ wait(1.2)
+ if frameToListen.inside.Value then
+ while frameToListen.inside.Value and frameToListen.BackgroundTransparency > 0 do
+ frameToListen.BackgroundTransparency = frameToListen.BackgroundTransparency - fadeSpeed
+ frameToListen.TextTransparency = frameToListen.TextTransparency - fadeSpeed
+ wait()
+ end
+ end
+ end
+ end)
+ function killTip(killFrame)
+ killFrame.inside.Value = false
+ killFrame.BackgroundTransparency = 1
+ killFrame.TextTransparency = 1
+ end
+ frameToListen.Parent.MouseLeave:connect(function() killTip(frameToListen) end)
+ frameToListen.Parent.MouseButton1Click:connect(function() killTip(frameToListen) end)
+end
+
+function createSettingsButtonTip(parent)
+ if parent == nil then
+ parent = bottomLeftControl:FindFirstChild("SettingsButton")
+ end
+
+ local toolTip = frameTip:clone()
+ toolTip.RobloxLocked = true
+ toolTip.Text = "Settings/Leave Game"
+ toolTip.Position = UDim2.new(0,0,0,-18)
+ toolTip.Size = UDim2.new(0,120,0,20)
+ toolTip.Parent = parent
+ setUpListeners(toolTip)
+end
+
+wait(5) -- make sure we are loaded in, won't need tool tips for first 5 seconds anyway
+
+---------------- set up Bottom Left Tool Tips -------------------------
+
+local bottomLeftChildren = bottomLeftControl:GetChildren()
+local hasSettingsTip = false
+
+for i = 1, #bottomLeftChildren do
+
+ if bottomLeftChildren[i].Name == "Exit" then
+ local exitTip = frameTip:clone()
+ exitTip.RobloxLocked = true
+ exitTip.Text = "Leave Place"
+ exitTip.Position = UDim2.new(0,0,-1,0)
+ exitTip.Size = UDim2.new(1,0,1,0)
+ exitTip.Parent = bottomLeftChildren[i]
+ setUpListeners(exitTip)
+ elseif bottomLeftChildren[i].Name == "SettingsButton" then
+ hasSettingsTip = true
+ createSettingsButtonTip(bottomLeftChildren[i])
+ end
+end
+
+---------------- set up Bottom Right Tool Tips -------------------------
+
+local bottomRightChildren = bottomRightControl:GetChildren()
+
+for i = 1, #bottomRightChildren do
+ if bottomRightChildren[i].Name:find("Camera") ~= nil then
+ local cameraTip = frameTip:clone()
+ cameraTip.RobloxLocked = true
+ cameraTip.Text = "Camera View"
+ if bottomRightChildren[i].Name:find("Zoom") then
+ cameraTip.Position = UDim2.new(-1,0,-1.5)
+ else
+ cameraTip.Position = UDim2.new(0,0,-1.5,0)
+ end
+ cameraTip.Size = UDim2.new(2,0,1.25,0)
+ cameraTip.Parent = bottomRightChildren[i]
+ setUpListeners(cameraTip)
+ end
+end
diff --git a/app/files/CoreGui/37801172 b/app/files/CoreGui/37801172
new file mode 100644
index 0000000..edbda6c
--- /dev/null
+++ b/app/files/CoreGui/37801172
@@ -0,0 +1,115 @@
+-- Creates all neccessary scripts for the gui on initial load, everything except build tools
+-- Created by Ben T. 10/29/10
+-- Please note that these are loaded in a specific order to diminish errors/perceived load time by user
+
+local scriptContext = game:GetService("ScriptContext")
+local touchEnabled = false
+pcall(function() touchEnabled = game:GetService("UserInputService").TouchEnabled end)
+
+-- library registration
+scriptContext:AddCoreScript(60595695, scriptContext,"/Libraries/LibraryRegistration/LibraryRegistration")
+
+local function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+local function waitForProperty(instance, property)
+ while not instance[property] do
+ instance.Changed:wait()
+ end
+end
+
+-- Responsible for tracking logging items
+local scriptContext = game:GetService("ScriptContext")
+scriptContext:AddCoreScript(59002209, scriptContext, "CoreScripts/Sections")
+
+waitForChild(game:GetService("CoreGui"),"RobloxGui")
+local screenGui = game:GetService("CoreGui"):FindFirstChild("RobloxGui")
+
+if not touchEnabled then
+ -- ToolTipper (creates tool tips for gui)
+ scriptContext:AddCoreScript(36868950,screenGui,"CoreScripts/ToolTip")
+ -- SettingsScript
+ scriptContext:AddCoreScript(46295863,screenGui,"CoreScripts/Settings")
+else
+ scriptContext:AddCoreScript(153556783,screenGui,"CoreScripts/TouchControls")
+end
+
+-- MainBotChatScript
+scriptContext:AddCoreScript(39250920,screenGui,"CoreScripts/MainBotChatScript")
+
+-- Developer Console Script
+scriptContext:AddCoreScript(157877000,screenGui,"CoreScripts/DeveloperConsole")
+
+-- Popup Script
+scriptContext:AddCoreScript(48488451,screenGui,"CoreScripts/PopupScript")
+-- Friend Notification Script (probably can use this script to expand out to other notifications)
+scriptContext:AddCoreScript(48488398,screenGui,"CoreScripts/NotificationScript")
+-- Chat script
+scriptContext:AddCoreScript(97188756, screenGui, "CoreScripts/ChatScript")
+-- Purchase Prompt Script
+scriptContext:AddCoreScript(107893730, screenGui, "CoreScripts/PurchasePromptScript")
+-- Health Script
+scriptContext:AddCoreScript(153556822, screenGui, "CoreScripts/HealthScript")
+
+if not touchEnabled then
+ -- New Player List
+ scriptContext:AddCoreScript(48488235,screenGui,"CoreScripts/PlayerListScript")
+elseif screenGui.AbsoluteSize.Y > 600 then
+ -- New Player List
+ scriptContext:AddCoreScript(48488235,screenGui,"CoreScripts/PlayerListScript")
+else
+ delay(5, function()
+ if screenGui.AbsoluteSize.Y >= 600 then
+ -- New Player List
+ scriptContext:AddCoreScript(48488235,screenGui,"CoreScripts/PlayerListScript")
+ end
+ end)
+end
+
+if game.CoreGui.Version >= 3 then
+ -- Backpack Builder, creates most of the backpack gui
+ scriptContext:AddCoreScript(53878047,screenGui,"CoreScripts/BackpackScripts/BackpackBuilder")
+
+ waitForChild(screenGui,"CurrentLoadout")
+ waitForChild(screenGui,"Backpack")
+ local Backpack = screenGui.Backpack
+
+ -- Manager handles all big backpack state changes, other scripts subscribe to this and do things accordingly
+ if game.CoreGui.Version >= 7 then
+ scriptContext:AddCoreScript(89449093,Backpack,"CoreScripts/BackpackScripts/BackpackManager")
+ end
+
+ -- Backpack Gear (handles all backpack gear tab stuff)
+ game:GetService("ScriptContext"):AddCoreScript(89449008,Backpack,"CoreScripts/BackpackScripts/BackpackGear")
+ -- Loadout Script, used for gear hotkeys
+ scriptContext:AddCoreScript(53878057,screenGui.CurrentLoadout,"CoreScripts/BackpackScripts/LoadoutScript")
+ if game.CoreGui.Version >= 8 then
+ -- Wardrobe script handles all character dressing operations
+ scriptContext:AddCoreScript(-1,Backpack,"CoreScripts/BackpackScripts/BackpackWardrobe")
+ end
+end
+
+local IsPersonalServer = not not game.Workspace:FindFirstChild("PSVariable")
+if IsPersonalServer then
+ game:GetService("ScriptContext"):AddCoreScript(64164692,game.Players.LocalPlayer,"BuildToolManager")
+end
+game.Workspace.ChildAdded:connect(function(nchild)
+ if nchild.Name=='PSVariable' and nchild:IsA('BoolValue') then
+ IsPersonalServer = true
+ game:GetService("ScriptContext"):AddCoreScript(64164692,game.Players.LocalPlayer,"BuildToolManager")
+ end
+end)
+
+if touchEnabled then -- touch devices don't use same control frame
+ -- only used for touch device button generation
+ scriptContext:AddCoreScript(152908679,screenGui,"CoreScripts/ContextActionTouch")
+
+ waitForChild(screenGui, 'ControlFrame')
+ waitForChild(screenGui.ControlFrame, 'BottomLeftControl')
+ screenGui.ControlFrame.BottomLeftControl.Visible = false
+
+ waitForChild(screenGui.ControlFrame, 'TopLeftControl')
+ screenGui.ControlFrame.TopLeftControl.Visible = false
+end
\ No newline at end of file
diff --git a/app/files/CoreGui/37801173 b/app/files/CoreGui/37801173
new file mode 100644
index 0000000..9750d26
--- /dev/null
+++ b/app/files/CoreGui/37801173
@@ -0,0 +1,74 @@
+-- Creates all neccessary scripts for the gui on initial load, everything except build tools
+-- Created by Ben T. 10/29/10
+-- Please note that these are loaded in a specific order to diminish errors/perceived load time by user
+local scriptContext = game:GetService("ScriptContext")
+local touchEnabled = false
+pcall(function() touchEnabled = game:GetService("UserInputService").TouchEnabled end)
+
+-- library registration
+scriptContext:AddCoreScript(60595696, scriptContext,"/Libraries/LibraryRegistration/LibraryRegistration")
+
+local function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+local function waitForProperty(instance, property)
+ while not instance[property] do
+ instance.Changed:wait()
+ end
+end
+
+-- Responsible for tracking logging items
+local scriptContext = game:GetService("ScriptContext")
+scriptContext:AddCoreScript(59002209, scriptContext, "CoreScripts/Sections")
+
+waitForChild(game:GetService("CoreGui"),"RobloxGui")
+local screenGui = game:GetService("CoreGui"):FindFirstChild("RobloxGui")
+
+if not touchEnabled then
+ -- ToolTipper (creates tool tips for gui)
+ scriptContext:AddCoreScript(36868950,screenGui,"CoreScripts/ToolTip")
+ -- SettingsScript
+ scriptContext:AddCoreScript(46295864,screenGui,"CoreScripts/Settings")
+end
+
+-- MainBotChatScript
+scriptContext:AddCoreScript(39250920,screenGui,"CoreScripts/MainBotChatScript")
+-- New Player List
+scriptContext:AddCoreScript(48488236,screenGui,"CoreScripts/PlayerListScript")
+-- Popup Script
+scriptContext:AddCoreScript(48488451,screenGui,"CoreScripts/PopupScript")
+-- Friend Notification Script (probably can use this script to expand out to other notifications)
+scriptContext:AddCoreScript(48488398,screenGui,"CoreScripts/NotificationScript")
+-- Chat script
+scriptContext:AddCoreScript(97188757, screenGui, "CoreScripts/ChatScript")
+
+
+if game.CoreGui.Version >= 3 then
+ -- Backpack Builder, creates most of the backpack gui
+ scriptContext:AddCoreScript(53878048,screenGui,"CoreScripts/BackpackScripts/BackpackBuilder")
+
+ waitForChild(screenGui,"CurrentLoadout")
+ waitForChild(screenGui,"Backpack")
+ local Backpack = screenGui.Backpack
+
+ -- Manager handles all big backpack state changes, other scripts subscribe to this and do things accordingly
+ if game.CoreGui.Version >= 7 then
+ scriptContext:AddCoreScript(89449094,Backpack,"CoreScripts/BackpackScripts/BackpackManager")
+ end
+
+ -- Backpack Gear (handles all backpack gear tab stuff)
+ game:GetService("ScriptContext"):AddCoreScript(89449009,Backpack,"CoreScripts/BackpackScripts/BackpackGear")
+ -- Loadout Script, used for gear hotkeys
+ scriptContext:AddCoreScript(53878058,screenGui.CurrentLoadout,"CoreScripts/BackpackScripts/LoadoutScript")
+ if game.CoreGui.Version >= 8 then
+ -- Wardrobe script handles all character dressing operations
+ scriptContext:AddCoreScript(-1,Backpack,"CoreScripts/BackpackScripts/BackpackWardrobe")
+ end
+end
+
+if touchEnabled then -- touch devices don't use same control frame
+ waitForChild(screenGui, 'ControlFrame')
+ screenGui.ControlFrame.Visible = false
+end
diff --git a/app/files/CoreGui/39250920 b/app/files/CoreGui/39250920
new file mode 100644
index 0000000..5356cdf
--- /dev/null
+++ b/app/files/CoreGui/39250920
@@ -0,0 +1,560 @@
+function waitForProperty(instance, name)
+ while not instance[name] do
+ instance.Changed:wait()
+ end
+end
+
+function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+
+
+local mainFrame
+local choices = {}
+local lastChoice
+local choiceMap = {}
+local currentConversationDialog
+local currentConversationPartner
+local currentAbortDialogScript
+
+local tooFarAwayMessage = "You are too far away to chat!"
+local tooFarAwaySize = 300
+local characterWanderedOffMessage = "Chat ended because you walked away"
+local characterWanderedOffSize = 350
+local conversationTimedOut = "Chat ended because you didn't reply"
+local conversationTimedOutSize = 350
+
+local player
+local screenGui
+local chatNotificationGui
+local messageDialog
+local timeoutScript
+local reenableDialogScript
+local dialogMap = {}
+local dialogConnections = {}
+
+local gui = nil
+waitForChild(game,"CoreGui")
+waitForChild(game.CoreGui,"RobloxGui")
+if game.CoreGui.RobloxGui:FindFirstChild("ControlFrame") then
+ gui = game.CoreGui.RobloxGui.ControlFrame
+else
+ gui = game.CoreGui.RobloxGui
+end
+
+function currentTone()
+ if currentConversationDialog then
+ return currentConversationDialog.Tone
+ else
+ return Enum.DialogTone.Neutral
+ end
+end
+
+
+function createChatNotificationGui()
+ chatNotificationGui = Instance.new("BillboardGui")
+ chatNotificationGui.Name = "ChatNotificationGui"
+ chatNotificationGui.ExtentsOffset = Vector3.new(0,1,0)
+ chatNotificationGui.Size = UDim2.new(4, 0, 5.42857122, 0)
+ chatNotificationGui.SizeOffset = Vector2.new(0,0)
+ chatNotificationGui.StudsOffset = Vector3.new(0.4, 4.3, 0)
+ chatNotificationGui.Enabled = true
+ chatNotificationGui.RobloxLocked = true
+ chatNotificationGui.Active = true
+
+ local image = Instance.new("ImageLabel")
+ image.Name = "Image"
+ image.Active = false
+ image.BackgroundTransparency = 1
+ image.Position = UDim2.new(0,0,0,0)
+ image.Size = UDim2.new(1.0,0,1.0,0)
+ image.Image = ""
+ image.RobloxLocked = true
+ image.Parent = chatNotificationGui
+
+
+ local button = Instance.new("ImageButton")
+ button.Name = "Button"
+ button.AutoButtonColor = false
+ button.Position = UDim2.new(0.0879999995, 0, 0.0529999994, 0)
+ button.Size = UDim2.new(0.829999983, 0, 0.460000008, 0)
+ button.Image = ""
+ button.BackgroundTransparency = 1
+ button.RobloxLocked = true
+ button.Parent = image
+end
+
+function getChatColor(tone)
+ if tone == Enum.DialogTone.Neutral then
+ return Enum.ChatColor.Blue
+ elseif tone == Enum.DialogTone.Friendly then
+ return Enum.ChatColor.Green
+ elseif tone == Enum.DialogTone.Enemy then
+ return Enum.ChatColor.Red
+ end
+end
+
+function styleChoices(tone)
+ for i, obj in pairs(choices) do
+ resetColor(obj, tone)
+ end
+ resetColor(lastChoice, tone)
+end
+
+function styleMainFrame(tone)
+ if tone == Enum.DialogTone.Neutral then
+ mainFrame.Style = Enum.FrameStyle.ChatBlue
+ mainFrame.Tail.Image = "rbxasset://textures/chatBubble_botBlue_tailRight.png"
+ elseif tone == Enum.DialogTone.Friendly then
+ mainFrame.Style = Enum.FrameStyle.ChatGreen
+ mainFrame.Tail.Image = "rbxasset://textures/chatBubble_botGreen_tailRight.png"
+ elseif tone == Enum.DialogTone.Enemy then
+ mainFrame.Style = Enum.FrameStyle.ChatRed
+ mainFrame.Tail.Image = "rbxasset://textures/chatBubble_botRed_tailRight.png"
+ end
+
+ styleChoices(tone)
+end
+function setChatNotificationTone(gui, purpose, tone)
+ if tone == Enum.DialogTone.Neutral then
+ gui.Image.Image = "rbxasset://textures/chatBubble_botBlue_notify_bkg.png"
+ elseif tone == Enum.DialogTone.Friendly then
+ gui.Image.Image = "rbxasset://textures/chatBubble_botGreen_notify_bkg.png"
+ elseif tone == Enum.DialogTone.Enemy then
+ gui.Image.Image = "rbxasset://textures/chatBubble_botRed_notify_bkg.png"
+ end
+ if purpose == Enum.DialogPurpose.Quest then
+ gui.Image.Button.Image = "rbxasset://textures/chatBubble_bot_notify_bang.png"
+ elseif purpose == Enum.DialogPurpose.Help then
+ gui.Image.Button.Image = "rbxasset://textures/chatBubble_bot_notify_question.png"
+ elseif purpose == Enum.DialogPurpose.Shop then
+ gui.Image.Button.Image = "rbxasset://textures/chatBubble_bot_notify_money.png"
+ end
+end
+
+function createMessageDialog()
+ messageDialog = Instance.new("Frame");
+ messageDialog.Name = "DialogScriptMessage"
+ messageDialog.Style = Enum.FrameStyle.RobloxRound
+ messageDialog.Visible = false
+
+ local text = Instance.new("TextLabel")
+ text.Name = "Text"
+ text.Position = UDim2.new(0,0,0,-1)
+ text.Size = UDim2.new(1,0,1,0)
+ text.FontSize = Enum.FontSize.Size14
+ text.BackgroundTransparency = 1
+ text.TextColor3 = Color3.new(1,1,1)
+ text.RobloxLocked = true
+ text.Parent = messageDialog
+end
+
+function showMessage(msg, size)
+ messageDialog.Text.Text = msg
+ messageDialog.Size = UDim2.new(0,size,0,40)
+ messageDialog.Position = UDim2.new(0.5, -size/2, 0.5, -40)
+ messageDialog.Visible = true
+ wait(2)
+ messageDialog.Visible = false
+end
+
+function variableDelay(str)
+ local length = math.min(string.len(str), 100)
+ wait(0.75 + ((length/75) * 1.5))
+end
+
+function resetColor(frame, tone)
+ if tone == Enum.DialogTone.Neutral then
+ frame.BackgroundColor3 = Color3.new(0/255, 0/255, 179/255)
+ frame.Number.TextColor3 = Color3.new(45/255, 142/255, 245/255)
+ elseif tone == Enum.DialogTone.Friendly then
+ frame.BackgroundColor3 = Color3.new(0/255, 77/255, 0/255)
+ frame.Number.TextColor3 = Color3.new(0/255, 190/255, 0/255)
+ elseif tone == Enum.DialogTone.Enemy then
+ frame.BackgroundColor3 = Color3.new(140/255, 0/255, 0/255)
+ frame.Number.TextColor3 = Color3.new(255/255,88/255, 79/255)
+ end
+end
+
+function highlightColor(frame, tone)
+ if tone == Enum.DialogTone.Neutral then
+ frame.BackgroundColor3 = Color3.new(2/255, 108/255, 255/255)
+ frame.Number.TextColor3 = Color3.new(1, 1, 1)
+ elseif tone == Enum.DialogTone.Friendly then
+ frame.BackgroundColor3 = Color3.new(0/255, 128/255, 0/255)
+ frame.Number.TextColor3 = Color3.new(1, 1, 1)
+ elseif tone == Enum.DialogTone.Enemy then
+ frame.BackgroundColor3 = Color3.new(204/255, 0/255, 0/255)
+ frame.Number.TextColor3 = Color3.new(1, 1, 1)
+ end
+end
+
+function wanderDialog()
+ print("Wander")
+ mainFrame.Visible = false
+ endDialog()
+ showMessage(characterWanderedOffMessage, characterWanderedOffSize)
+end
+
+function timeoutDialog()
+ print("Timeout")
+ mainFrame.Visible = false
+ endDialog()
+ showMessage(conversationTimedOut, conversationTimedOutSize)
+end
+function normalEndDialog()
+ print("Done")
+ endDialog()
+end
+
+function endDialog()
+ if currentAbortDialogScript then
+ currentAbortDialogScript:Remove()
+ currentAbortDialogScript = nil
+ end
+
+ local dialog = currentConversationDialog
+ currentConversationDialog = nil
+ if dialog and dialog.InUse then
+ local reenableScript = reenableDialogScript:Clone()
+ reenableScript.archivable = false
+ reenableScript.Disabled = false
+ reenableScript.Parent = dialog
+ end
+
+ for dialog, gui in pairs(dialogMap) do
+ if dialog and gui then
+ gui.Enabled = not dialog.InUse
+ end
+ end
+
+ currentConversationPartner = nil
+end
+
+function sanitizeMessage(msg)
+ if string.len(msg) == 0 then
+ return "..."
+ else
+ return msg
+ end
+end
+
+function selectChoice(choice)
+ renewKillswitch(currentConversationDialog)
+
+ --First hide the Gui
+ mainFrame.Visible = false
+ if choice == lastChoice then
+ game.Chat:Chat(game.Players.LocalPlayer.Character, "Goodbye!", getChatColor(currentTone()))
+
+ normalEndDialog()
+ else
+ local dialogChoice = choiceMap[choice]
+
+ game.Chat:Chat(game.Players.LocalPlayer.Character, sanitizeMessage(dialogChoice.UserDialog), getChatColor(currentTone()))
+ wait(1)
+ currentConversationDialog:SignalDialogChoiceSelected(player, dialogChoice)
+ game.Chat:Chat(currentConversationPartner, sanitizeMessage(dialogChoice.ResponseDialog), getChatColor(currentTone()))
+
+ variableDelay(dialogChoice.ResponseDialog)
+ presentDialogChoices(currentConversationPartner, dialogChoice:GetChildren())
+ end
+end
+
+function newChoice(numberText)
+ local frame = Instance.new("TextButton")
+ frame.BackgroundColor3 = Color3.new(0/255, 0/255, 179/255)
+ frame.AutoButtonColor = false
+ frame.BorderSizePixel = 0
+ frame.Text = ""
+ frame.MouseEnter:connect(function() highlightColor(frame, currentTone()) end)
+ frame.MouseLeave:connect(function() resetColor(frame, currentTone()) end)
+ frame.MouseButton1Click:connect(function() selectChoice(frame) end)
+ frame.RobloxLocked = true
+
+ local number = Instance.new("TextLabel")
+ number.Name = "Number"
+ number.TextColor3 = Color3.new(127/255, 212/255, 255/255)
+ number.Text = numberText
+ number.FontSize = Enum.FontSize.Size14
+ number.BackgroundTransparency = 1
+ number.Position = UDim2.new(0,4,0,2)
+ number.Size = UDim2.new(0,20,0,24)
+ number.TextXAlignment = Enum.TextXAlignment.Left
+ number.TextYAlignment = Enum.TextYAlignment.Top
+ number.RobloxLocked = true
+ number.Parent = frame
+
+ local prompt = Instance.new("TextLabel")
+ prompt.Name = "UserPrompt"
+ prompt.BackgroundTransparency = 1
+ prompt.TextColor3 = Color3.new(1,1,1)
+ prompt.FontSize = Enum.FontSize.Size14
+ prompt.Position = UDim2.new(0,28, 0, 2)
+ prompt.Size = UDim2.new(1,-32, 1, -4)
+ prompt.TextXAlignment = Enum.TextXAlignment.Left
+ prompt.TextYAlignment = Enum.TextYAlignment.Top
+ prompt.TextWrap = true
+ prompt.RobloxLocked = true
+ prompt.Parent = frame
+
+ return frame
+end
+function initialize(parent)
+ choices[1] = newChoice("1)")
+ choices[2] = newChoice("2)")
+ choices[3] = newChoice("3)")
+ choices[4] = newChoice("4)")
+
+ lastChoice = newChoice("5)")
+ lastChoice.UserPrompt.Text = "Goodbye!"
+ lastChoice.Size = UDim2.new(1,0,0,28)
+
+ mainFrame = Instance.new("Frame")
+ mainFrame.Name = "UserDialogArea"
+ mainFrame.Size = UDim2.new(0, 350, 0, 200)
+ mainFrame.Style = Enum.FrameStyle.ChatBlue
+ mainFrame.Visible = false
+
+ imageLabel = Instance.new("ImageLabel")
+ imageLabel.Name = "Tail"
+ imageLabel.Size = UDim2.new(0,62,0,53)
+ imageLabel.Position = UDim2.new(1,8,0.25)
+ imageLabel.Image = "rbxasset://textures/chatBubble_botBlue_tailRight.png"
+ imageLabel.BackgroundTransparency = 1
+ imageLabel.RobloxLocked = true
+ imageLabel.Parent = mainFrame
+
+ for n, obj in pairs(choices) do
+ obj.RobloxLocked = true
+ obj.Parent = mainFrame
+ end
+ lastChoice.RobloxLocked = true
+ lastChoice.Parent = mainFrame
+
+ mainFrame.RobloxLocked = true
+ mainFrame.Parent = parent
+end
+
+function presentDialogChoices(talkingPart, dialogChoices)
+ if not currentConversationDialog then
+ return
+ end
+
+ currentConversationPartner = talkingPart
+ sortedDialogChoices = {}
+ for n, obj in pairs(dialogChoices) do
+ if obj:IsA("DialogChoice") then
+ table.insert(sortedDialogChoices, obj)
+ end
+ end
+ table.sort(sortedDialogChoices, function(a,b) return a.Name < b.Name end)
+
+ if #sortedDialogChoices == 0 then
+ normalEndDialog()
+ return
+ end
+
+ local pos = 1
+ local yPosition = 0
+ choiceMap = {}
+ for n, obj in pairs(choices) do
+ obj.Visible = false
+ end
+
+ for n, obj in pairs(sortedDialogChoices) do
+ if pos <= #choices then
+ --3 lines is the maximum, set it to that temporarily
+ choices[pos].Size = UDim2.new(1, 0, 0, 24*3)
+ choices[pos].UserPrompt.Text = obj.UserDialog
+ local height = math.ceil(choices[pos].UserPrompt.TextBounds.Y/24)*24
+
+ choices[pos].Position = UDim2.new(0, 0, 0, yPosition)
+ choices[pos].Size = UDim2.new(1, 0, 0, height)
+ choices[pos].Visible = true
+
+ choiceMap[choices[pos]] = obj
+
+ yPosition = yPosition + height
+ pos = pos + 1
+ end
+ end
+
+ lastChoice.Position = UDim2.new(0,0,0,yPosition)
+ lastChoice.Number.Text = pos .. ")"
+
+ mainFrame.Size = UDim2.new(0, 350, 0, yPosition+24+32)
+ mainFrame.Position = UDim2.new(0,20,0.0, -mainFrame.Size.Y.Offset-20)
+ styleMainFrame(currentTone())
+ mainFrame.Visible = true
+end
+
+function doDialog(dialog)
+ while not Instance.Lock(dialog, player) do
+ wait()
+ end
+
+ if dialog.InUse then
+ Instance.Unlock(dialog)
+ return
+ else
+ dialog.InUse = true
+ Instance.Unlock(dialog)
+ end
+
+ currentConversationDialog = dialog
+ game.Chat:Chat(dialog.Parent, dialog.InitialPrompt, getChatColor(dialog.Tone))
+ variableDelay(dialog.InitialPrompt)
+
+ presentDialogChoices(dialog.Parent, dialog:GetChildren())
+end
+
+function renewKillswitch(dialog)
+ if currentAbortDialogScript then
+ currentAbortDialogScript:Remove()
+ currentAbortDialogScript = nil
+ end
+
+ currentAbortDialogScript = timeoutScript:Clone()
+ currentAbortDialogScript.archivable = false
+ currentAbortDialogScript.Disabled = false
+ currentAbortDialogScript.Parent = dialog
+end
+
+function checkForLeaveArea()
+ while currentConversationDialog do
+ if currentConversationDialog.Parent and (player:DistanceFromCharacter(currentConversationDialog.Parent.Position) >= currentConversationDialog.ConversationDistance) then
+ wanderDialog()
+ end
+ wait(1)
+ end
+end
+
+function startDialog(dialog)
+ if dialog.Parent and dialog.Parent:IsA("BasePart") then
+ if player:DistanceFromCharacter(dialog.Parent.Position) >= dialog.ConversationDistance then
+ showMessage(tooFarAwayMessage, tooFarAwaySize)
+ return
+ end
+
+ for dialog, gui in pairs(dialogMap) do
+ if dialog and gui then
+ gui.Enabled = false
+ end
+ end
+
+ renewKillswitch(dialog)
+
+ delay(1, checkForLeaveArea)
+ doDialog(dialog)
+ end
+end
+
+function removeDialog(dialog)
+ if dialogMap[dialog] then
+ dialogMap[dialog]:Remove()
+ dialogMap[dialog] = nil
+ end
+ if dialogConnections[dialog] then
+ dialogConnections[dialog]:disconnect()
+ dialogConnections[dialog] = nil
+ end
+end
+
+function addDialog(dialog)
+ if dialog.Parent then
+ if dialog.Parent:IsA("BasePart") then
+ local chatGui = chatNotificationGui:clone()
+ chatGui.Enabled = not dialog.InUse
+ chatGui.Adornee = dialog.Parent
+ chatGui.RobloxLocked = true
+ chatGui.Parent = game.CoreGui
+ chatGui.Image.Button.MouseButton1Click:connect(function() startDialog(dialog) end)
+ setChatNotificationTone(chatGui, dialog.Purpose, dialog.Tone)
+
+ dialogMap[dialog] = chatGui
+
+ dialogConnections[dialog] = dialog.Changed:connect(function(prop)
+ if prop == "Parent" and dialog.Parent then
+ --This handles the reparenting case, seperate from removal case
+ removeDialog(dialog)
+ addDialog(dialog)
+ elseif prop == "InUse" then
+ chatGui.Enabled = not currentConversationDialog and not dialog.InUse
+ if dialog == currentConversationDialog then
+ timeoutDialog()
+ end
+ elseif prop == "Tone" or prop == "Purpose" then
+ setChatNotificationTone(chatGui, dialog.Purpose, dialog.Tone)
+ end
+ end)
+ else -- still need to listen to parent changes even if current parent is not a BasePart
+ dialogConnections[dialog] = dialog.Changed:connect(function(prop)
+ if prop == "Parent" and dialog.Parent then
+ --This handles the reparenting case, seperate from removal case
+ removeDialog(dialog)
+ addDialog(dialog)
+ end
+ end)
+ end
+ end
+end
+
+function fetchScripts()
+ local model = game:GetService("InsertService"):LoadAsset(39226062)
+ if type(model) == "string" then -- load failed, lets try again
+ wait(0.1)
+ model = game:GetService("InsertService"):LoadAsset(39226062)
+ end
+ if type(model) == "string" then -- not going to work, lets bail
+ return
+ end
+
+ waitForChild(model,"TimeoutScript")
+ timeoutScript = model.TimeoutScript
+ waitForChild(model,"ReenableDialogScript")
+ reenableDialogScript = model.ReenableDialogScript
+end
+
+function onLoad()
+ waitForProperty(game.Players, "LocalPlayer")
+ player = game.Players.LocalPlayer
+ waitForProperty(player, "Character")
+
+ --print("Fetching Scripts")
+ fetchScripts()
+
+ --print("Creating Guis")
+ createChatNotificationGui()
+
+ --print("Creating MessageDialog")
+ createMessageDialog()
+ messageDialog.RobloxLocked = true
+ messageDialog.Parent = gui
+
+ --print("Waiting for BottomLeftControl")
+ waitForChild(gui, "BottomLeftControl")
+
+ --print("Initializing Frame")
+ local frame = Instance.new("Frame")
+ frame.Name = "DialogFrame"
+ frame.Position = UDim2.new(0,0,0,0)
+ frame.Size = UDim2.new(0,0,0,0)
+ frame.BackgroundTransparency = 1
+ frame.RobloxLocked = true
+ frame.Parent = gui.BottomLeftControl
+ initialize(frame)
+
+ --print("Adding Dialogs")
+ game.CollectionService.ItemAdded:connect(function(obj) if obj:IsA("Dialog") then addDialog(obj) end end)
+ game.CollectionService.ItemRemoved:connect(function(obj) if obj:IsA("Dialog") then removeDialog(obj) end end)
+ for i, obj in pairs(game.CollectionService:GetCollection("Dialog")) do
+ if obj:IsA("Dialog") then
+ addDialog(obj)
+ end
+ end
+end
+
+onLoad()
\ No newline at end of file
diff --git a/app/files/CoreGui/45284430 b/app/files/CoreGui/45284430
new file mode 100644
index 0000000..54226e1
--- /dev/null
+++ b/app/files/CoreGui/45284430
@@ -0,0 +1,3844 @@
+local t = {}
+
+local function ScopedConnect(parentInstance, instance, event, signalFunc, syncFunc, removeFunc)
+ local eventConnection = nil
+
+ --Connection on parentInstance is scoped by parentInstance (when destroyed, it goes away)
+ local tryConnect = function()
+ if game:IsAncestorOf(parentInstance) then
+ --Entering the world, make sure we are connected/synced
+ if not eventConnection then
+ eventConnection = instance[event]:connect(signalFunc)
+ if syncFunc then syncFunc() end
+ end
+ else
+ --Probably leaving the world, so disconnect for now
+ if eventConnection then
+ eventConnection:disconnect()
+ if removeFunc then removeFunc() end
+ end
+ end
+ end
+
+ --Hook it up to ancestryChanged signal
+ local connection = parentInstance.AncestryChanged:connect(tryConnect)
+
+ --Now connect us if we're already in the world
+ tryConnect()
+
+ return connection
+end
+
+local function getScreenGuiAncestor(instance)
+ local localInstance = instance
+ while localInstance and not localInstance:IsA("ScreenGui") do
+ localInstance = localInstance.Parent
+ end
+ return localInstance
+end
+
+local function CreateButtons(frame, buttons, yPos, ySize)
+ local buttonNum = 1
+ local buttonObjs = {}
+ for i, obj in ipairs(buttons) do
+ local button = Instance.new("TextButton")
+ button.Name = "Button" .. buttonNum
+ button.Font = Enum.Font.Arial
+ button.FontSize = Enum.FontSize.Size18
+ button.AutoButtonColor = true
+ button.Modal = true
+ if obj["Style"] then
+ button.Style = obj.Style
+ else
+ button.Style = Enum.ButtonStyle.RobloxButton
+ end
+ button.Text = obj.Text
+ button.TextColor3 = Color3.new(1,1,1)
+ button.MouseButton1Click:connect(obj.Function)
+ button.Parent = frame
+ buttonObjs[buttonNum] = button
+
+ buttonNum = buttonNum + 1
+ end
+ local numButtons = buttonNum-1
+
+ if numButtons == 1 then
+ frame.Button1.Position = UDim2.new(0.35, 0, yPos.Scale, yPos.Offset)
+ frame.Button1.Size = UDim2.new(.4,0,ySize.Scale, ySize.Offset)
+ elseif numButtons == 2 then
+ frame.Button1.Position = UDim2.new(0.1, 0, yPos.Scale, yPos.Offset)
+ frame.Button1.Size = UDim2.new(.8/3,0, ySize.Scale, ySize.Offset)
+
+ frame.Button2.Position = UDim2.new(0.55, 0, yPos.Scale, yPos.Offset)
+ frame.Button2.Size = UDim2.new(.35,0, ySize.Scale, ySize.Offset)
+ elseif numButtons >= 3 then
+ local spacing = .1 / numButtons
+ local buttonSize = .9 / numButtons
+
+ buttonNum = 1
+ while buttonNum <= numButtons do
+ buttonObjs[buttonNum].Position = UDim2.new(spacing*buttonNum + (buttonNum-1) * buttonSize, 0, yPos.Scale, yPos.Offset)
+ buttonObjs[buttonNum].Size = UDim2.new(buttonSize, 0, ySize.Scale, ySize.Offset)
+ buttonNum = buttonNum + 1
+ end
+ end
+end
+
+local function setSliderPos(newAbsPosX,slider,sliderPosition,bar,steps)
+
+ local newStep = steps - 1 --otherwise we really get one more step than we want
+ local relativePosX = math.min(1, math.max(0, (newAbsPosX - bar.AbsolutePosition.X) / bar.AbsoluteSize.X ))
+ local wholeNum, remainder = math.modf(relativePosX * newStep)
+ if remainder > 0.5 then
+ wholeNum = wholeNum + 1
+ end
+ relativePosX = wholeNum/newStep
+
+ local result = math.ceil(relativePosX * newStep)
+ if sliderPosition.Value ~= (result + 1) then --only update if we moved a step
+ sliderPosition.Value = result + 1
+ slider.Position = UDim2.new(relativePosX,-slider.AbsoluteSize.X/2,slider.Position.Y.Scale,slider.Position.Y.Offset)
+ end
+
+end
+
+local function cancelSlide(areaSoak)
+ areaSoak.Visible = false
+ if areaSoakMouseMoveCon then areaSoakMouseMoveCon:disconnect() end
+end
+
+t.CreateStyledMessageDialog = function(title, message, style, buttons)
+ local frame = Instance.new("Frame")
+ frame.Size = UDim2.new(0.5, 0, 0, 165)
+ frame.Position = UDim2.new(0.25, 0, 0.5, -72.5)
+ frame.Name = "MessageDialog"
+ frame.Active = true
+ frame.Style = Enum.FrameStyle.RobloxRound
+
+ local styleImage = Instance.new("ImageLabel")
+ styleImage.Name = "StyleImage"
+ styleImage.BackgroundTransparency = 1
+ styleImage.Position = UDim2.new(0,5,0,15)
+ if style == "error" or style == "Error" then
+ styleImage.Size = UDim2.new(0, 71, 0, 71)
+ styleImage.Image = "http://www.syntax.eco/asset?id=42565285"
+ elseif style == "notify" or style == "Notify" then
+ styleImage.Size = UDim2.new(0, 71, 0, 71)
+ styleImage.Image = "http://www.syntax.eco/asset?id=42604978"
+ elseif style == "confirm" or style == "Confirm" then
+ styleImage.Size = UDim2.new(0, 74, 0, 76)
+ styleImage.Image = "http://www.syntax.eco/asset?id=42557901"
+ else
+ return t.CreateMessageDialog(title,message,buttons)
+ end
+ styleImage.Parent = frame
+
+ local titleLabel = Instance.new("TextLabel")
+ titleLabel.Name = "Title"
+ titleLabel.Text = title
+ titleLabel.TextStrokeTransparency = 0
+ titleLabel.BackgroundTransparency = 1
+ titleLabel.TextColor3 = Color3.new(221/255,221/255,221/255)
+ titleLabel.Position = UDim2.new(0, 80, 0, 0)
+ titleLabel.Size = UDim2.new(1, -80, 0, 40)
+ titleLabel.Font = Enum.Font.ArialBold
+ titleLabel.FontSize = Enum.FontSize.Size36
+ titleLabel.TextXAlignment = Enum.TextXAlignment.Center
+ titleLabel.TextYAlignment = Enum.TextYAlignment.Center
+ titleLabel.Parent = frame
+
+ local messageLabel = Instance.new("TextLabel")
+ messageLabel.Name = "Message"
+ messageLabel.Text = message
+ messageLabel.TextStrokeTransparency = 0
+ messageLabel.TextColor3 = Color3.new(221/255,221/255,221/255)
+ messageLabel.Position = UDim2.new(0.025, 80, 0, 45)
+ messageLabel.Size = UDim2.new(0.95, -80, 0, 55)
+ messageLabel.BackgroundTransparency = 1
+ messageLabel.Font = Enum.Font.Arial
+ messageLabel.FontSize = Enum.FontSize.Size18
+ messageLabel.TextWrap = true
+ messageLabel.TextXAlignment = Enum.TextXAlignment.Left
+ messageLabel.TextYAlignment = Enum.TextYAlignment.Top
+ messageLabel.Parent = frame
+
+ CreateButtons(frame, buttons, UDim.new(0, 105), UDim.new(0, 40) )
+
+ return frame
+end
+
+t.CreateMessageDialog = function(title, message, buttons)
+ local frame = Instance.new("Frame")
+ frame.Size = UDim2.new(0.5, 0, 0.5, 0)
+ frame.Position = UDim2.new(0.25, 0, 0.25, 0)
+ frame.Name = "MessageDialog"
+ frame.Active = true
+ frame.Style = Enum.FrameStyle.RobloxRound
+
+ local titleLabel = Instance.new("TextLabel")
+ titleLabel.Name = "Title"
+ titleLabel.Text = title
+ titleLabel.BackgroundTransparency = 1
+ titleLabel.TextColor3 = Color3.new(221/255,221/255,221/255)
+ titleLabel.Position = UDim2.new(0, 0, 0, 0)
+ titleLabel.Size = UDim2.new(1, 0, 0.15, 0)
+ titleLabel.Font = Enum.Font.ArialBold
+ titleLabel.FontSize = Enum.FontSize.Size36
+ titleLabel.TextXAlignment = Enum.TextXAlignment.Center
+ titleLabel.TextYAlignment = Enum.TextYAlignment.Center
+ titleLabel.Parent = frame
+
+ local messageLabel = Instance.new("TextLabel")
+ messageLabel.Name = "Message"
+ messageLabel.Text = message
+ messageLabel.TextColor3 = Color3.new(221/255,221/255,221/255)
+ messageLabel.Position = UDim2.new(0.025, 0, 0.175, 0)
+ messageLabel.Size = UDim2.new(0.95, 0, .55, 0)
+ messageLabel.BackgroundTransparency = 1
+ messageLabel.Font = Enum.Font.Arial
+ messageLabel.FontSize = Enum.FontSize.Size18
+ messageLabel.TextWrap = true
+ messageLabel.TextXAlignment = Enum.TextXAlignment.Left
+ messageLabel.TextYAlignment = Enum.TextYAlignment.Top
+ messageLabel.Parent = frame
+
+ CreateButtons(frame, buttons, UDim.new(0.8,0), UDim.new(0.15, 0))
+
+ return frame
+end
+
+t.CreateDropDownMenu = function(items, onSelect, forRoblox)
+ local width = UDim.new(0, 100)
+ local height = UDim.new(0, 32)
+
+ local xPos = 0.055
+ local frame = Instance.new("Frame")
+ frame.Name = "DropDownMenu"
+ frame.BackgroundTransparency = 1
+ frame.Size = UDim2.new(width, height)
+
+ local dropDownMenu = Instance.new("TextButton")
+ dropDownMenu.Name = "DropDownMenuButton"
+ dropDownMenu.TextWrap = true
+ dropDownMenu.TextColor3 = Color3.new(1,1,1)
+ dropDownMenu.Text = "Choose One"
+ dropDownMenu.Font = Enum.Font.ArialBold
+ dropDownMenu.FontSize = Enum.FontSize.Size18
+ dropDownMenu.TextXAlignment = Enum.TextXAlignment.Left
+ dropDownMenu.TextYAlignment = Enum.TextYAlignment.Center
+ dropDownMenu.BackgroundTransparency = 1
+ dropDownMenu.AutoButtonColor = true
+ dropDownMenu.Style = Enum.ButtonStyle.RobloxButton
+ dropDownMenu.Size = UDim2.new(1,0,1,0)
+ dropDownMenu.Parent = frame
+ dropDownMenu.ZIndex = 2
+
+ local dropDownIcon = Instance.new("ImageLabel")
+ dropDownIcon.Name = "Icon"
+ dropDownIcon.Active = false
+ dropDownIcon.Image = "http://www.syntax.eco/asset/?id=45732894"
+ dropDownIcon.BackgroundTransparency = 1
+ dropDownIcon.Size = UDim2.new(0,11,0,6)
+ dropDownIcon.Position = UDim2.new(1,-11,0.5, -2)
+ dropDownIcon.Parent = dropDownMenu
+ dropDownIcon.ZIndex = 2
+
+ local itemCount = #items
+ local dropDownItemCount = #items
+ local useScrollButtons = false
+ if dropDownItemCount > 6 then
+ useScrollButtons = true
+ dropDownItemCount = 6
+ end
+
+ local droppedDownMenu = Instance.new("TextButton")
+ droppedDownMenu.Name = "List"
+ droppedDownMenu.Text = ""
+ droppedDownMenu.BackgroundTransparency = 1
+ --droppedDownMenu.AutoButtonColor = true
+ droppedDownMenu.Style = Enum.ButtonStyle.RobloxButton
+ droppedDownMenu.Visible = false
+ droppedDownMenu.Active = true --Blocks clicks
+ droppedDownMenu.Position = UDim2.new(0,0,0,0)
+ droppedDownMenu.Size = UDim2.new(1,0, (1 + dropDownItemCount)*.8, 0)
+ droppedDownMenu.Parent = frame
+ droppedDownMenu.ZIndex = 2
+
+ local choiceButton = Instance.new("TextButton")
+ choiceButton.Name = "ChoiceButton"
+ choiceButton.BackgroundTransparency = 1
+ choiceButton.BorderSizePixel = 0
+ choiceButton.Text = "ReplaceMe"
+ choiceButton.TextColor3 = Color3.new(1,1,1)
+ choiceButton.TextXAlignment = Enum.TextXAlignment.Left
+ choiceButton.TextYAlignment = Enum.TextYAlignment.Center
+ choiceButton.BackgroundColor3 = Color3.new(1, 1, 1)
+ choiceButton.Font = Enum.Font.Arial
+ choiceButton.FontSize = Enum.FontSize.Size18
+ if useScrollButtons then
+ choiceButton.Size = UDim2.new(1,-13, .8/((dropDownItemCount + 1)*.8),0)
+ else
+ choiceButton.Size = UDim2.new(1, 0, .8/((dropDownItemCount + 1)*.8),0)
+ end
+ choiceButton.TextWrap = true
+ choiceButton.ZIndex = 2
+
+ local areaSoak = Instance.new("TextButton")
+ areaSoak.Name = "AreaSoak"
+ areaSoak.Text = ""
+ areaSoak.BackgroundTransparency = 1
+ areaSoak.Active = true
+ areaSoak.Size = UDim2.new(1,0,1,0)
+ areaSoak.Visible = false
+ areaSoak.ZIndex = 3
+
+ local dropDownSelected = false
+
+ local scrollUpButton
+ local scrollDownButton
+ local scrollMouseCount = 0
+
+ local setZIndex = function(baseZIndex)
+ droppedDownMenu.ZIndex = baseZIndex +1
+ if scrollUpButton then
+ scrollUpButton.ZIndex = baseZIndex + 3
+ end
+ if scrollDownButton then
+ scrollDownButton.ZIndex = baseZIndex + 3
+ end
+
+ local children = droppedDownMenu:GetChildren()
+ if children then
+ for i, child in ipairs(children) do
+ if child.Name == "ChoiceButton" then
+ child.ZIndex = baseZIndex + 2
+ elseif child.Name == "ClickCaptureButton" then
+ child.ZIndex = baseZIndex
+ end
+ end
+ end
+ end
+
+ local scrollBarPosition = 1
+ local updateScroll = function()
+ if scrollUpButton then
+ scrollUpButton.Active = scrollBarPosition > 1
+ end
+ if scrollDownButton then
+ scrollDownButton.Active = scrollBarPosition + dropDownItemCount <= itemCount
+ end
+
+ local children = droppedDownMenu:GetChildren()
+ if not children then return end
+
+ local childNum = 1
+ for i, obj in ipairs(children) do
+ if obj.Name == "ChoiceButton" then
+ if childNum < scrollBarPosition or childNum >= scrollBarPosition + dropDownItemCount then
+ obj.Visible = false
+ else
+ obj.Position = UDim2.new(0,0,((childNum-scrollBarPosition+1)*.8)/((dropDownItemCount+1)*.8),0)
+ obj.Visible = true
+ end
+ obj.TextColor3 = Color3.new(1,1,1)
+ obj.BackgroundTransparency = 1
+
+ childNum = childNum + 1
+ end
+ end
+ end
+ local toggleVisibility = function()
+ dropDownSelected = not dropDownSelected
+
+ areaSoak.Visible = not areaSoak.Visible
+ dropDownMenu.Visible = not dropDownSelected
+ droppedDownMenu.Visible = dropDownSelected
+ if dropDownSelected then
+ setZIndex(4)
+ else
+ setZIndex(2)
+ end
+ if useScrollButtons then
+ updateScroll()
+ end
+ end
+ droppedDownMenu.MouseButton1Click:connect(toggleVisibility)
+
+ local updateSelection = function(text)
+ local foundItem = false
+ local children = droppedDownMenu:GetChildren()
+ local childNum = 1
+ if children then
+ for i, obj in ipairs(children) do
+ if obj.Name == "ChoiceButton" then
+ if obj.Text == text then
+ obj.Font = Enum.Font.ArialBold
+ foundItem = true
+ scrollBarPosition = childNum
+ else
+ obj.Font = Enum.Font.Arial
+ end
+ childNum = childNum + 1
+ end
+ end
+ end
+ if not text then
+ dropDownMenu.Text = "Choose One"
+ scrollBarPosition = 1
+ else
+ if not foundItem then
+ error("Invalid Selection Update -- " .. text)
+ end
+
+ if scrollBarPosition + dropDownItemCount > itemCount + 1 then
+ scrollBarPosition = itemCount - dropDownItemCount + 1
+ end
+
+ dropDownMenu.Text = text
+ end
+ end
+
+ local function scrollDown()
+ if scrollBarPosition + dropDownItemCount <= itemCount then
+ scrollBarPosition = scrollBarPosition + 1
+ updateScroll()
+ return true
+ end
+ return false
+ end
+ local function scrollUp()
+ if scrollBarPosition > 1 then
+ scrollBarPosition = scrollBarPosition - 1
+ updateScroll()
+ return true
+ end
+ return false
+ end
+
+ if useScrollButtons then
+ --Make some scroll buttons
+ scrollUpButton = Instance.new("ImageButton")
+ scrollUpButton.Name = "ScrollUpButton"
+ scrollUpButton.BackgroundTransparency = 1
+ scrollUpButton.Image = "rbxasset://textures/ui/scrollbuttonUp.png"
+ scrollUpButton.Size = UDim2.new(0,17,0,17)
+ scrollUpButton.Position = UDim2.new(1,-11,(1*.8)/((dropDownItemCount+1)*.8),0)
+ scrollUpButton.MouseButton1Click:connect(
+ function()
+ scrollMouseCount = scrollMouseCount + 1
+ end)
+ scrollUpButton.MouseLeave:connect(
+ function()
+ scrollMouseCount = scrollMouseCount + 1
+ end)
+ scrollUpButton.MouseButton1Down:connect(
+ function()
+ scrollMouseCount = scrollMouseCount + 1
+
+ scrollUp()
+ local val = scrollMouseCount
+ wait(0.5)
+ while val == scrollMouseCount do
+ if scrollUp() == false then
+ break
+ end
+ wait(0.1)
+ end
+ end)
+
+ scrollUpButton.Parent = droppedDownMenu
+
+ scrollDownButton = Instance.new("ImageButton")
+ scrollDownButton.Name = "ScrollDownButton"
+ scrollDownButton.BackgroundTransparency = 1
+ scrollDownButton.Image = "rbxasset://textures/ui/scrollbuttonDown.png"
+ scrollDownButton.Size = UDim2.new(0,17,0,17)
+ scrollDownButton.Position = UDim2.new(1,-11,1,-11)
+ scrollDownButton.Parent = droppedDownMenu
+ scrollDownButton.MouseButton1Click:connect(
+ function()
+ scrollMouseCount = scrollMouseCount + 1
+ end)
+ scrollDownButton.MouseLeave:connect(
+ function()
+ scrollMouseCount = scrollMouseCount + 1
+ end)
+ scrollDownButton.MouseButton1Down:connect(
+ function()
+ scrollMouseCount = scrollMouseCount + 1
+
+ scrollDown()
+ local val = scrollMouseCount
+ wait(0.5)
+ while val == scrollMouseCount do
+ if scrollDown() == false then
+ break
+ end
+ wait(0.1)
+ end
+ end)
+
+ local scrollbar = Instance.new("ImageLabel")
+ scrollbar.Name = "ScrollBar"
+ scrollbar.Image = "rbxasset://textures/ui/scrollbar.png"
+ scrollbar.BackgroundTransparency = 1
+ scrollbar.Size = UDim2.new(0, 18, (dropDownItemCount*.8)/((dropDownItemCount+1)*.8), -(17) - 11 - 4)
+ scrollbar.Position = UDim2.new(1,-11,(1*.8)/((dropDownItemCount+1)*.8),17+2)
+ scrollbar.Parent = droppedDownMenu
+ end
+
+ for i,item in ipairs(items) do
+ -- needed to maintain local scope for items in event listeners below
+ local button = choiceButton:clone()
+ if forRoblox then
+ button.RobloxLocked = true
+ end
+ button.Text = item
+ button.Parent = droppedDownMenu
+ button.MouseButton1Click:connect(function()
+ --Remove Highlight
+ button.TextColor3 = Color3.new(1,1,1)
+ button.BackgroundTransparency = 1
+
+ updateSelection(item)
+ onSelect(item)
+
+ toggleVisibility()
+ end)
+ button.MouseEnter:connect(function()
+ --Add Highlight
+ button.TextColor3 = Color3.new(0,0,0)
+ button.BackgroundTransparency = 0
+ end)
+
+ button.MouseLeave:connect(function()
+ --Remove Highlight
+ button.TextColor3 = Color3.new(1,1,1)
+ button.BackgroundTransparency = 1
+ end)
+ end
+
+ --This does the initial layout of the buttons
+ updateScroll()
+
+ frame.AncestryChanged:connect(function(child,parent)
+ if parent == nil then
+ areaSoak.Parent = nil
+ else
+ areaSoak.Parent = getScreenGuiAncestor(frame)
+ end
+ end)
+
+ dropDownMenu.MouseButton1Click:connect(toggleVisibility)
+ areaSoak.MouseButton1Click:connect(toggleVisibility)
+ return frame, updateSelection
+end
+
+t.CreatePropertyDropDownMenu = function(instance, property, enum)
+
+ local items = enum:GetEnumItems()
+ local names = {}
+ local nameToItem = {}
+ for i,obj in ipairs(items) do
+ names[i] = obj.Name
+ nameToItem[obj.Name] = obj
+ end
+
+ local frame
+ local updateSelection
+ frame, updateSelection = t.CreateDropDownMenu(names, function(text) instance[property] = nameToItem[text] end)
+
+ ScopedConnect(frame, instance, "Changed",
+ function(prop)
+ if prop == property then
+ updateSelection(instance[property].Name)
+ end
+ end,
+ function()
+ updateSelection(instance[property].Name)
+ end)
+
+ return frame
+end
+
+t.GetFontHeight = function(font, fontSize)
+ if font == nil or fontSize == nil then
+ error("Font and FontSize must be non-nil")
+ end
+
+ if font == Enum.Font.Legacy then
+ if fontSize == Enum.FontSize.Size8 then
+ return 12
+ elseif fontSize == Enum.FontSize.Size9 then
+ return 14
+ elseif fontSize == Enum.FontSize.Size10 then
+ return 15
+ elseif fontSize == Enum.FontSize.Size11 then
+ return 17
+ elseif fontSize == Enum.FontSize.Size12 then
+ return 18
+ elseif fontSize == Enum.FontSize.Size14 then
+ return 21
+ elseif fontSize == Enum.FontSize.Size18 then
+ return 27
+ elseif fontSize == Enum.FontSize.Size24 then
+ return 36
+ elseif fontSize == Enum.FontSize.Size36 then
+ return 54
+ elseif fontSize == Enum.FontSize.Size48 then
+ return 72
+ else
+ error("Unknown FontSize")
+ end
+ elseif font == Enum.Font.Arial or font == Enum.Font.ArialBold then
+ if fontSize == Enum.FontSize.Size8 then
+ return 8
+ elseif fontSize == Enum.FontSize.Size9 then
+ return 9
+ elseif fontSize == Enum.FontSize.Size10 then
+ return 10
+ elseif fontSize == Enum.FontSize.Size11 then
+ return 11
+ elseif fontSize == Enum.FontSize.Size12 then
+ return 12
+ elseif fontSize == Enum.FontSize.Size14 then
+ return 14
+ elseif fontSize == Enum.FontSize.Size18 then
+ return 18
+ elseif fontSize == Enum.FontSize.Size24 then
+ return 24
+ elseif fontSize == Enum.FontSize.Size36 then
+ return 36
+ elseif fontSize == Enum.FontSize.Size48 then
+ return 48
+ else
+ error("Unknown FontSize")
+ end
+ else
+ error("Unknown Font " .. font)
+ end
+end
+
+local function layoutGuiObjectsHelper(frame, guiObjects, settingsTable)
+ local totalPixels = frame.AbsoluteSize.Y
+ local pixelsRemaining = frame.AbsoluteSize.Y
+ for i, child in ipairs(guiObjects) do
+ if child:IsA("TextLabel") or child:IsA("TextButton") then
+ local isLabel = child:IsA("TextLabel")
+ if isLabel then
+ pixelsRemaining = pixelsRemaining - settingsTable["TextLabelPositionPadY"]
+ else
+ pixelsRemaining = pixelsRemaining - settingsTable["TextButtonPositionPadY"]
+ end
+ child.Position = UDim2.new(child.Position.X.Scale, child.Position.X.Offset, 0, totalPixels - pixelsRemaining)
+ child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, pixelsRemaining)
+
+ if child.TextFits and child.TextBounds.Y < pixelsRemaining then
+ child.Visible = true
+ if isLabel then
+ child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, child.TextBounds.Y + settingsTable["TextLabelSizePadY"])
+ else
+ child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, child.TextBounds.Y + settingsTable["TextButtonSizePadY"])
+ end
+
+ while not child.TextFits do
+ child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, child.AbsoluteSize.Y + 1)
+ end
+ pixelsRemaining = pixelsRemaining - child.AbsoluteSize.Y
+
+ if isLabel then
+ pixelsRemaining = pixelsRemaining - settingsTable["TextLabelPositionPadY"]
+ else
+ pixelsRemaining = pixelsRemaining - settingsTable["TextButtonPositionPadY"]
+ end
+ else
+ child.Visible = false
+ pixelsRemaining = -1
+ end
+
+ else
+ --GuiObject
+ child.Position = UDim2.new(child.Position.X.Scale, child.Position.X.Offset, 0, totalPixels - pixelsRemaining)
+ pixelsRemaining = pixelsRemaining - child.AbsoluteSize.Y
+ child.Visible = (pixelsRemaining >= 0)
+ end
+ end
+end
+
+t.LayoutGuiObjects = function(frame, guiObjects, settingsTable)
+ if not frame:IsA("GuiObject") then
+ error("Frame must be a GuiObject")
+ end
+ for i, child in ipairs(guiObjects) do
+ if not child:IsA("GuiObject") then
+ error("All elements that are layed out must be of type GuiObject")
+ end
+ end
+
+ if not settingsTable then
+ settingsTable = {}
+ end
+
+ if not settingsTable["TextLabelSizePadY"] then
+ settingsTable["TextLabelSizePadY"] = 0
+ end
+ if not settingsTable["TextLabelPositionPadY"] then
+ settingsTable["TextLabelPositionPadY"] = 0
+ end
+ if not settingsTable["TextButtonSizePadY"] then
+ settingsTable["TextButtonSizePadY"] = 12
+ end
+ if not settingsTable["TextButtonPositionPadY"] then
+ settingsTable["TextButtonPositionPadY"] = 2
+ end
+
+ --Wrapper frame takes care of styled objects
+ local wrapperFrame = Instance.new("Frame")
+ wrapperFrame.Name = "WrapperFrame"
+ wrapperFrame.BackgroundTransparency = 1
+ wrapperFrame.Size = UDim2.new(1,0,1,0)
+ wrapperFrame.Parent = frame
+
+ for i, child in ipairs(guiObjects) do
+ child.Parent = wrapperFrame
+ end
+
+ local recalculate = function()
+ wait()
+ layoutGuiObjectsHelper(wrapperFrame, guiObjects, settingsTable)
+ end
+
+ frame.Changed:connect(
+ function(prop)
+ if prop == "AbsoluteSize" then
+ --Wait a heartbeat for it to sync in
+ recalculate(nil)
+ end
+ end)
+ frame.AncestryChanged:connect(recalculate)
+
+ layoutGuiObjectsHelper(wrapperFrame, guiObjects, settingsTable)
+end
+
+
+t.CreateSlider = function(steps,width,position)
+ local sliderGui = Instance.new("Frame")
+ sliderGui.Size = UDim2.new(1,0,1,0)
+ sliderGui.BackgroundTransparency = 1
+ sliderGui.Name = "SliderGui"
+
+ local sliderSteps = Instance.new("IntValue")
+ sliderSteps.Name = "SliderSteps"
+ sliderSteps.Value = steps
+ sliderSteps.Parent = sliderGui
+
+ local areaSoak = Instance.new("TextButton")
+ areaSoak.Name = "AreaSoak"
+ areaSoak.Text = ""
+ areaSoak.BackgroundTransparency = 1
+ areaSoak.Active = false
+ areaSoak.Size = UDim2.new(1,0,1,0)
+ areaSoak.Visible = false
+ areaSoak.ZIndex = 4
+
+ sliderGui.AncestryChanged:connect(function(child,parent)
+ if parent == nil then
+ areaSoak.Parent = nil
+ else
+ areaSoak.Parent = getScreenGuiAncestor(sliderGui)
+ end
+ end)
+
+ local sliderPosition = Instance.new("IntValue")
+ sliderPosition.Name = "SliderPosition"
+ sliderPosition.Value = 0
+ sliderPosition.Parent = sliderGui
+
+ local id = math.random(1,100)
+
+ local bar = Instance.new("TextButton")
+ bar.Text = ""
+ bar.AutoButtonColor = false
+ bar.Name = "Bar"
+ bar.BackgroundColor3 = Color3.new(0,0,0)
+ if type(width) == "number" then
+ bar.Size = UDim2.new(0,width,0,5)
+ else
+ bar.Size = UDim2.new(0,200,0,5)
+ end
+ bar.BorderColor3 = Color3.new(95/255,95/255,95/255)
+ bar.ZIndex = 2
+ bar.Parent = sliderGui
+
+ if position["X"] and position["X"]["Scale"] and position["X"]["Offset"] and position["Y"] and position["Y"]["Scale"] and position["Y"]["Offset"] then
+ bar.Position = position
+ end
+
+ local slider = Instance.new("ImageButton")
+ slider.Name = "Slider"
+ slider.BackgroundTransparency = 1
+ slider.Image = "rbxasset://textures/ui/Slider.png"
+ slider.Position = UDim2.new(0,0,0.5,-10)
+ slider.Size = UDim2.new(0,20,0,20)
+ slider.ZIndex = 3
+ slider.Parent = bar
+
+ local areaSoakMouseMoveCon = nil
+
+ areaSoak.MouseLeave:connect(function()
+ if areaSoak.Visible then
+ cancelSlide(areaSoak)
+ end
+ end)
+ areaSoak.MouseButton1Up:connect(function()
+ if areaSoak.Visible then
+ cancelSlide(areaSoak)
+ end
+ end)
+
+ slider.MouseButton1Down:connect(function()
+ areaSoak.Visible = true
+ if areaSoakMouseMoveCon then areaSoakMouseMoveCon:disconnect() end
+ areaSoakMouseMoveCon = areaSoak.MouseMoved:connect(function(x,y)
+ setSliderPos(x,slider,sliderPosition,bar,steps)
+ end)
+ end)
+
+ slider.MouseButton1Up:connect(function() cancelSlide(areaSoak) end)
+
+ sliderPosition.Changed:connect(function(prop)
+ sliderPosition.Value = math.min(steps, math.max(1,sliderPosition.Value))
+ local relativePosX = (sliderPosition.Value - 1) / (steps - 1)
+ slider.Position = UDim2.new(relativePosX,-slider.AbsoluteSize.X/2,slider.Position.Y.Scale,slider.Position.Y.Offset)
+ end)
+
+ bar.MouseButton1Down:connect(function(x,y)
+ setSliderPos(x,slider,sliderPosition,bar,steps)
+ end)
+
+ return sliderGui, sliderPosition, sliderSteps
+
+end
+
+t.CreateTrueScrollingFrame = function()
+ local lowY = nil
+ local highY = nil
+
+ local dragCon = nil
+ local upCon = nil
+
+ local internalChange = false
+
+ local descendantsChangeConMap = {}
+
+ local scrollingFrame = Instance.new("Frame")
+ scrollingFrame.Name = "ScrollingFrame"
+ scrollingFrame.Active = true
+ scrollingFrame.Size = UDim2.new(1,0,1,0)
+ scrollingFrame.ClipsDescendants = true
+
+ local controlFrame = Instance.new("Frame")
+ controlFrame.Name = "ControlFrame"
+ controlFrame.BackgroundTransparency = 1
+ controlFrame.Size = UDim2.new(0,18,1,0)
+ controlFrame.Position = UDim2.new(1,-20,0,0)
+ controlFrame.Parent = scrollingFrame
+
+ local scrollBottom = Instance.new("BoolValue")
+ scrollBottom.Value = false
+ scrollBottom.Name = "ScrollBottom"
+ scrollBottom.Parent = controlFrame
+
+ local scrollUp = Instance.new("BoolValue")
+ scrollUp.Value = false
+ scrollUp.Name = "scrollUp"
+ scrollUp.Parent = controlFrame
+
+ local scrollUpButton = Instance.new("TextButton")
+ scrollUpButton.Name = "ScrollUpButton"
+ scrollUpButton.Text = ""
+ scrollUpButton.AutoButtonColor = false
+ scrollUpButton.BackgroundColor3 = Color3.new(0,0,0)
+ scrollUpButton.BorderColor3 = Color3.new(1,1,1)
+ scrollUpButton.BackgroundTransparency = 0.5
+ scrollUpButton.Size = UDim2.new(0,18,0,18)
+ scrollUpButton.ZIndex = 2
+ scrollUpButton.Parent = controlFrame
+ for i = 1, 6 do
+ local triFrame = Instance.new("Frame")
+ triFrame.BorderColor3 = Color3.new(1,1,1)
+ triFrame.Name = "tri" .. tostring(i)
+ triFrame.ZIndex = 3
+ triFrame.BackgroundTransparency = 0.5
+ triFrame.Size = UDim2.new(0,12 - ((i -1) * 2),0,0)
+ triFrame.Position = UDim2.new(0,3 + (i -1),0.5,2 - (i -1))
+ triFrame.Parent = scrollUpButton
+ end
+ scrollUpButton.MouseEnter:connect(function()
+ scrollUpButton.BackgroundTransparency = 0.1
+ local upChildren = scrollUpButton:GetChildren()
+ for i = 1, #upChildren do
+ upChildren[i].BackgroundTransparency = 0.1
+ end
+ end)
+ scrollUpButton.MouseLeave:connect(function()
+ scrollUpButton.BackgroundTransparency = 0.5
+ local upChildren = scrollUpButton:GetChildren()
+ for i = 1, #upChildren do
+ upChildren[i].BackgroundTransparency = 0.5
+ end
+ end)
+
+ local scrollDownButton = scrollUpButton:clone()
+ scrollDownButton.Name = "ScrollDownButton"
+ scrollDownButton.Position = UDim2.new(0,0,1,-18)
+ local downChildren = scrollDownButton:GetChildren()
+ for i = 1, #downChildren do
+ downChildren[i].Position = UDim2.new(0,3 + (i -1),0.5,-2 + (i - 1))
+ end
+ scrollDownButton.MouseEnter:connect(function()
+ scrollDownButton.BackgroundTransparency = 0.1
+ local downChildren = scrollDownButton:GetChildren()
+ for i = 1, #downChildren do
+ downChildren[i].BackgroundTransparency = 0.1
+ end
+ end)
+ scrollDownButton.MouseLeave:connect(function()
+ scrollDownButton.BackgroundTransparency = 0.5
+ local downChildren = scrollDownButton:GetChildren()
+ for i = 1, #downChildren do
+ downChildren[i].BackgroundTransparency = 0.5
+ end
+ end)
+ scrollDownButton.Parent = controlFrame
+
+ local scrollTrack = Instance.new("Frame")
+ scrollTrack.Name = "ScrollTrack"
+ scrollTrack.BackgroundTransparency = 1
+ scrollTrack.Size = UDim2.new(0,18,1,-38)
+ scrollTrack.Position = UDim2.new(0,0,0,19)
+ scrollTrack.Parent = controlFrame
+
+ local scrollbar = Instance.new("TextButton")
+ scrollbar.BackgroundColor3 = Color3.new(0,0,0)
+ scrollbar.BorderColor3 = Color3.new(1,1,1)
+ scrollbar.BackgroundTransparency = 0.5
+ scrollbar.AutoButtonColor = false
+ scrollbar.Text = ""
+ scrollbar.Active = true
+ scrollbar.Name = "ScrollBar"
+ scrollbar.ZIndex = 2
+ scrollbar.BackgroundTransparency = 0.5
+ scrollbar.Size = UDim2.new(0, 18, 0.1, 0)
+ scrollbar.Position = UDim2.new(0,0,0,0)
+ scrollbar.Parent = scrollTrack
+
+ local scrollNub = Instance.new("Frame")
+ scrollNub.Name = "ScrollNub"
+ scrollNub.BorderColor3 = Color3.new(1,1,1)
+ scrollNub.Size = UDim2.new(0,10,0,0)
+ scrollNub.Position = UDim2.new(0.5,-5,0.5,0)
+ scrollNub.ZIndex = 2
+ scrollNub.BackgroundTransparency = 0.5
+ scrollNub.Parent = scrollbar
+
+ local newNub = scrollNub:clone()
+ newNub.Position = UDim2.new(0.5,-5,0.5,-2)
+ newNub.Parent = scrollbar
+
+ local lastNub = scrollNub:clone()
+ lastNub.Position = UDim2.new(0.5,-5,0.5,2)
+ lastNub.Parent = scrollbar
+
+ scrollbar.MouseEnter:connect(function()
+ scrollbar.BackgroundTransparency = 0.1
+ scrollNub.BackgroundTransparency = 0.1
+ newNub.BackgroundTransparency = 0.1
+ lastNub.BackgroundTransparency = 0.1
+ end)
+ scrollbar.MouseLeave:connect(function()
+ scrollbar.BackgroundTransparency = 0.5
+ scrollNub.BackgroundTransparency = 0.5
+ newNub.BackgroundTransparency = 0.5
+ lastNub.BackgroundTransparency = 0.5
+ end)
+
+ local mouseDrag = Instance.new("ImageButton")
+ mouseDrag.Active = false
+ mouseDrag.Size = UDim2.new(1.5, 0, 1.5, 0)
+ mouseDrag.AutoButtonColor = false
+ mouseDrag.BackgroundTransparency = 1
+ mouseDrag.Name = "mouseDrag"
+ mouseDrag.Position = UDim2.new(-0.25, 0, -0.25, 0)
+ mouseDrag.ZIndex = 10
+
+ local function positionScrollBar(x,y,offset)
+ local oldPos = scrollbar.Position
+
+ if y < scrollTrack.AbsolutePosition.y then
+ scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,0,0)
+ return (oldPos ~= scrollbar.Position)
+ end
+
+ local relativeSize = scrollbar.AbsoluteSize.Y/scrollTrack.AbsoluteSize.Y
+
+ if y > (scrollTrack.AbsolutePosition.y + scrollTrack.AbsoluteSize.y) then
+ scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,1 - relativeSize,0)
+ return (oldPos ~= scrollbar.Position)
+ end
+ local newScaleYPos = (y - scrollTrack.AbsolutePosition.y - offset)/scrollTrack.AbsoluteSize.y
+ if newScaleYPos + relativeSize > 1 then
+ newScaleYPos = 1 - relativeSize
+ scrollBottom.Value = true
+ scrollUp.Value = false
+ elseif newScaleYPos <= 0 then
+ newScaleYPos = 0
+ scrollUp.Value = true
+ scrollBottom.Value = false
+ else
+ scrollUp.Value = false
+ scrollBottom.Value = false
+ end
+ scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,newScaleYPos,0)
+
+ return (oldPos ~= scrollbar.Position)
+ end
+
+ local function drillDownSetHighLow(instance)
+ if not instance or not instance:IsA("GuiObject") then return end
+ if instance == controlFrame then return end
+ if instance:IsDescendantOf(controlFrame) then return end
+ if not instance.Visible then return end
+
+ if lowY and lowY > instance.AbsolutePosition.Y then
+ lowY = instance.AbsolutePosition.Y
+ elseif not lowY then
+ lowY = instance.AbsolutePosition.Y
+ end
+ if highY and highY < (instance.AbsolutePosition.Y + instance.AbsoluteSize.Y) then
+ highY = instance.AbsolutePosition.Y + instance.AbsoluteSize.Y
+ elseif not highY then
+ highY = instance.AbsolutePosition.Y + instance.AbsoluteSize.Y
+ end
+ local children = instance:GetChildren()
+ for i = 1, #children do
+ drillDownSetHighLow(children[i])
+ end
+ end
+
+ local function resetHighLow()
+ local firstChildren = scrollingFrame:GetChildren()
+
+ for i = 1, #firstChildren do
+ drillDownSetHighLow(firstChildren[i])
+ end
+ end
+
+ local function recalculate()
+ internalChange = true
+
+ local percentFrame = 0
+ if scrollbar.Position.Y.Scale > 0 then
+ if scrollbar.Visible then
+ percentFrame = scrollbar.Position.Y.Scale/((scrollTrack.AbsoluteSize.Y - scrollbar.AbsoluteSize.Y)/scrollTrack.AbsoluteSize.Y)
+ else
+ percentFrame = 0
+ end
+ end
+ if percentFrame > 0.99 then percentFrame = 1 end
+
+ local hiddenYAmount = (scrollingFrame.AbsoluteSize.Y - (highY - lowY)) * percentFrame
+
+ local guiChildren = scrollingFrame:GetChildren()
+ for i = 1, #guiChildren do
+ if guiChildren[i] ~= controlFrame then
+ guiChildren[i].Position = UDim2.new(guiChildren[i].Position.X.Scale,guiChildren[i].Position.X.Offset,
+ 0, math.ceil(guiChildren[i].AbsolutePosition.Y) - math.ceil(lowY) + hiddenYAmount)
+ end
+ end
+
+ lowY = nil
+ highY = nil
+ resetHighLow()
+ internalChange = false
+ end
+
+ local function setSliderSizeAndPosition()
+ if not highY or not lowY then return end
+
+ local totalYSpan = math.abs(highY - lowY)
+ if totalYSpan == 0 then
+ scrollbar.Visible = false
+ scrollDownButton.Visible = false
+ scrollUpButton.Visible = false
+
+ if dragCon then dragCon:disconnect() dragCon = nil end
+ if upCon then upCon:disconnect() upCon = nil end
+ return
+ end
+
+ local percentShown = scrollingFrame.AbsoluteSize.Y/totalYSpan
+ if percentShown >= 1 then
+ scrollbar.Visible = false
+ scrollDownButton.Visible = false
+ scrollUpButton.Visible = false
+ recalculate()
+ else
+ scrollbar.Visible = true
+ scrollDownButton.Visible = true
+ scrollUpButton.Visible = true
+
+ scrollbar.Size = UDim2.new(scrollbar.Size.X.Scale,scrollbar.Size.X.Offset,percentShown,0)
+ end
+
+ local percentPosition = (scrollingFrame.AbsolutePosition.Y - lowY)/totalYSpan
+ scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,percentPosition,-scrollbar.AbsoluteSize.X/2)
+
+ if scrollbar.AbsolutePosition.y < scrollTrack.AbsolutePosition.y then
+ scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,0,0)
+ end
+
+ if (scrollbar.AbsolutePosition.y + scrollbar.AbsoluteSize.Y) > (scrollTrack.AbsolutePosition.y + scrollTrack.AbsoluteSize.y) then
+ local relativeSize = scrollbar.AbsoluteSize.Y/scrollTrack.AbsoluteSize.Y
+ scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,1 - relativeSize,0)
+ end
+ end
+
+ local buttonScrollAmountPixels = 7
+ local reentrancyGuardScrollUp = false
+ local function doScrollUp()
+ if reentrancyGuardScrollUp then return end
+
+ reentrancyGuardScrollUp = true
+ if positionScrollBar(0,scrollbar.AbsolutePosition.Y - buttonScrollAmountPixels,0) then
+ recalculate()
+ end
+ reentrancyGuardScrollUp = false
+ end
+
+ local reentrancyGuardScrollDown = false
+ local function doScrollDown()
+ if reentrancyGuardScrollDown then return end
+
+ reentrancyGuardScrollDown = true
+ if positionScrollBar(0,scrollbar.AbsolutePosition.Y + buttonScrollAmountPixels,0) then
+ recalculate()
+ end
+ reentrancyGuardScrollDown = false
+ end
+
+ local function scrollUp(mouseYPos)
+ if scrollUpButton.Active then
+ scrollStamp = tick()
+ local current = scrollStamp
+ local upCon
+ upCon = mouseDrag.MouseButton1Up:connect(function()
+ scrollStamp = tick()
+ mouseDrag.Parent = nil
+ upCon:disconnect()
+ end)
+ mouseDrag.Parent = getScreenGuiAncestor(scrollbar)
+ doScrollUp()
+ wait(0.2)
+ local t = tick()
+ local w = 0.1
+ while scrollStamp == current do
+ doScrollUp()
+ if mouseYPos and mouseYPos > scrollbar.AbsolutePosition.y then
+ break
+ end
+ if not scrollUpButton.Active then break end
+ if tick()-t > 5 then
+ w = 0
+ elseif tick()-t > 2 then
+ w = 0.06
+ end
+ wait(w)
+ end
+ end
+ end
+
+ local function scrollDown(mouseYPos)
+ if scrollDownButton.Active then
+ scrollStamp = tick()
+ local current = scrollStamp
+ local downCon
+ downCon = mouseDrag.MouseButton1Up:connect(function()
+ scrollStamp = tick()
+ mouseDrag.Parent = nil
+ downCon:disconnect()
+ end)
+ mouseDrag.Parent = getScreenGuiAncestor(scrollbar)
+ doScrollDown()
+ wait(0.2)
+ local t = tick()
+ local w = 0.1
+ while scrollStamp == current do
+ doScrollDown()
+ if mouseYPos and mouseYPos < (scrollbar.AbsolutePosition.y + scrollbar.AbsoluteSize.x) then
+ break
+ end
+ if not scrollDownButton.Active then break end
+ if tick()-t > 5 then
+ w = 0
+ elseif tick()-t > 2 then
+ w = 0.06
+ end
+ wait(w)
+ end
+ end
+ end
+
+ scrollbar.MouseButton1Down:connect(function(x,y)
+ if scrollbar.Active then
+ scrollStamp = tick()
+ local mouseOffset = y - scrollbar.AbsolutePosition.y
+ if dragCon then dragCon:disconnect() dragCon = nil end
+ if upCon then upCon:disconnect() upCon = nil end
+ local prevY = y
+ local reentrancyGuardMouseScroll = false
+ dragCon = mouseDrag.MouseMoved:connect(function(x,y)
+ if reentrancyGuardMouseScroll then return end
+
+ reentrancyGuardMouseScroll = true
+ if positionScrollBar(x,y,mouseOffset) then
+ recalculate()
+ end
+ reentrancyGuardMouseScroll = false
+
+ end)
+ upCon = mouseDrag.MouseButton1Up:connect(function()
+ scrollStamp = tick()
+ mouseDrag.Parent = nil
+ dragCon:disconnect(); dragCon = nil
+ upCon:disconnect(); drag = nil
+ end)
+ mouseDrag.Parent = getScreenGuiAncestor(scrollbar)
+ end
+ end)
+
+ local scrollMouseCount = 0
+
+ scrollUpButton.MouseButton1Down:connect(function()
+ scrollUp()
+ end)
+ scrollUpButton.MouseButton1Up:connect(function()
+ scrollStamp = tick()
+ end)
+
+ scrollDownButton.MouseButton1Up:connect(function()
+ scrollStamp = tick()
+ end)
+ scrollDownButton.MouseButton1Down:connect(function()
+ scrollDown()
+ end)
+
+ scrollbar.MouseButton1Up:connect(function()
+ scrollStamp = tick()
+ end)
+
+ local function heightCheck(instance)
+ if highY and (instance.AbsolutePosition.Y + instance.AbsoluteSize.Y) > highY then
+ highY = instance.AbsolutePosition.Y + instance.AbsoluteSize.Y
+ elseif not highY then
+ highY = instance.AbsolutePosition.Y + instance.AbsoluteSize.Y
+ end
+ setSliderSizeAndPosition()
+ end
+
+ local function highLowRecheck()
+ local oldLowY = lowY
+ local oldHighY = highY
+ lowY = nil
+ highY = nil
+ resetHighLow()
+
+ if (lowY ~= oldLowY) or (highY ~= oldHighY) then
+ setSliderSizeAndPosition()
+ end
+ end
+
+ local function descendantChanged(this, prop)
+ if internalChange then return end
+ if not this.Visible then return end
+
+ if prop == "Size" or prop == "Position" then
+ wait()
+ highLowRecheck()
+ end
+ end
+
+ scrollingFrame.DescendantAdded:connect(function(instance)
+ if not instance:IsA("GuiObject") then return end
+
+ if instance.Visible then
+ wait() -- wait a heartbeat for sizes to reconfig
+ highLowRecheck()
+ end
+
+ descendantsChangeConMap[instance] = instance.Changed:connect(function(prop) descendantChanged(instance, prop) end)
+ end)
+
+ scrollingFrame.DescendantRemoving:connect(function(instance)
+ if not instance:IsA("GuiObject") then return end
+ if descendantsChangeConMap[instance] then
+ descendantsChangeConMap[instance]:disconnect()
+ descendantsChangeConMap[instance] = nil
+ end
+ wait() -- wait a heartbeat for sizes to reconfig
+ highLowRecheck()
+ end)
+
+ scrollingFrame.Changed:connect(function(prop)
+ if prop == "AbsoluteSize" then
+ if not highY or not lowY then return end
+
+ highLowRecheck()
+ setSliderSizeAndPosition()
+ end
+ end)
+
+ return scrollingFrame, controlFrame
+end
+
+t.CreateScrollingFrame = function(orderList,scrollStyle)
+ local frame = Instance.new("Frame")
+ frame.Name = "ScrollingFrame"
+ frame.BackgroundTransparency = 1
+ frame.Size = UDim2.new(1,0,1,0)
+
+ local scrollUpButton = Instance.new("ImageButton")
+ scrollUpButton.Name = "ScrollUpButton"
+ scrollUpButton.BackgroundTransparency = 1
+ scrollUpButton.Image = "rbxasset://textures/ui/scrollbuttonUp.png"
+ scrollUpButton.Size = UDim2.new(0,17,0,17)
+
+
+ local scrollDownButton = Instance.new("ImageButton")
+ scrollDownButton.Name = "ScrollDownButton"
+ scrollDownButton.BackgroundTransparency = 1
+ scrollDownButton.Image = "rbxasset://textures/ui/scrollbuttonDown.png"
+ scrollDownButton.Size = UDim2.new(0,17,0,17)
+
+ local scrollbar = Instance.new("ImageButton")
+ scrollbar.Name = "ScrollBar"
+ scrollbar.Image = "rbxasset://textures/ui/scrollbar.png"
+ scrollbar.BackgroundTransparency = 1
+ scrollbar.Size = UDim2.new(0, 18, 0, 150)
+
+ local scrollStamp = 0
+
+ local scrollDrag = Instance.new("ImageButton")
+ scrollDrag.Image = "http://www.syntax.eco/asset/?id=61367186"
+ scrollDrag.Size = UDim2.new(1, 0, 0, 16)
+ scrollDrag.BackgroundTransparency = 1
+ scrollDrag.Name = "ScrollDrag"
+ scrollDrag.Active = true
+ scrollDrag.Parent = scrollbar
+
+ local mouseDrag = Instance.new("ImageButton")
+ mouseDrag.Active = false
+ mouseDrag.Size = UDim2.new(1.5, 0, 1.5, 0)
+ mouseDrag.AutoButtonColor = false
+ mouseDrag.BackgroundTransparency = 1
+ mouseDrag.Name = "mouseDrag"
+ mouseDrag.Position = UDim2.new(-0.25, 0, -0.25, 0)
+ mouseDrag.ZIndex = 10
+
+ local style = "simple"
+ if scrollStyle and tostring(scrollStyle) then
+ style = scrollStyle
+ end
+
+ local scrollPosition = 1
+ local rowSize = 0
+ local howManyDisplayed = 0
+
+ local layoutGridScrollBar = function()
+ howManyDisplayed = 0
+ local guiObjects = {}
+ if orderList then
+ for i, child in ipairs(orderList) do
+ if child.Parent == frame then
+ table.insert(guiObjects, child)
+ end
+ end
+ else
+ local children = frame:GetChildren()
+ if children then
+ for i, child in ipairs(children) do
+ if child:IsA("GuiObject") then
+ table.insert(guiObjects, child)
+ end
+ end
+ end
+ end
+ if #guiObjects == 0 then
+ scrollUpButton.Active = false
+ scrollDownButton.Active = false
+ scrollDrag.Active = false
+ scrollPosition = 1
+ return
+ end
+
+ if scrollPosition > #guiObjects then
+ scrollPosition = #guiObjects
+ end
+
+ if scrollPosition < 1 then scrollPosition = 1 end
+
+ local totalPixelsY = frame.AbsoluteSize.Y
+ local pixelsRemainingY = frame.AbsoluteSize.Y
+
+ local totalPixelsX = frame.AbsoluteSize.X
+
+ local xCounter = 0
+ local rowSizeCounter = 0
+ local setRowSize = true
+
+ local pixelsBelowScrollbar = 0
+ local pos = #guiObjects
+
+ local currentRowY = 0
+
+ pos = scrollPosition
+ --count up from current scroll position to fill out grid
+ while pos <= #guiObjects and pixelsBelowScrollbar < totalPixelsY do
+ xCounter = xCounter + guiObjects[pos].AbsoluteSize.X
+ --previous pos was the end of a row
+ if xCounter >= totalPixelsX then
+ pixelsBelowScrollbar = pixelsBelowScrollbar + currentRowY
+ currentRowY = 0
+ xCounter = guiObjects[pos].AbsoluteSize.X
+ end
+ if guiObjects[pos].AbsoluteSize.Y > currentRowY then
+ currentRowY = guiObjects[pos].AbsoluteSize.Y
+ end
+ pos = pos + 1
+ end
+ --Count wherever current row left off
+ pixelsBelowScrollbar = pixelsBelowScrollbar + currentRowY
+ currentRowY = 0
+
+ pos = scrollPosition - 1
+ xCounter = 0
+
+ --objects with varying X,Y dimensions can rarely cause minor errors
+ --rechecking every new scrollPosition is necessary to avoid 100% of errors
+
+ --count backwards from current scrollPosition to see if we can add more rows
+ while pixelsBelowScrollbar + currentRowY < totalPixelsY and pos >= 1 do
+ xCounter = xCounter + guiObjects[pos].AbsoluteSize.X
+ rowSizeCounter = rowSizeCounter + 1
+ if xCounter >= totalPixelsX then
+ rowSize = rowSizeCounter - 1
+ rowSizeCounter = 0
+ xCounter = guiObjects[pos].AbsoluteSize.X
+ if pixelsBelowScrollbar + currentRowY <= totalPixelsY then
+ --It fits, so back up our scroll position
+ pixelsBelowScrollbar = pixelsBelowScrollbar + currentRowY
+ if scrollPosition <= rowSize then
+ scrollPosition = 1
+ break
+ else
+ scrollPosition = scrollPosition - rowSize
+ end
+ currentRowY = 0
+ else
+ break
+ end
+ end
+
+ if guiObjects[pos].AbsoluteSize.Y > currentRowY then
+ currentRowY = guiObjects[pos].AbsoluteSize.Y
+ end
+
+ pos = pos - 1
+ end
+
+ --Do check last time if pos = 0
+ if (pos == 0) and (pixelsBelowScrollbar + currentRowY <= totalPixelsY) then
+ scrollPosition = 1
+ end
+
+ xCounter = 0
+ --pos = scrollPosition
+ rowSizeCounter = 0
+ setRowSize = true
+ local lastChildSize = 0
+
+ local xOffset,yOffset = 0
+ if guiObjects[1] then
+ yOffset = math.ceil(math.floor(math.fmod(totalPixelsY,guiObjects[1].AbsoluteSize.X))/2)
+ xOffset = math.ceil(math.floor(math.fmod(totalPixelsX,guiObjects[1].AbsoluteSize.Y))/2)
+ end
+
+ for i, child in ipairs(guiObjects) do
+ if i < scrollPosition then
+ --print("Hiding " .. child.Name)
+ child.Visible = false
+ else
+ if pixelsRemainingY < 0 then
+ --print("Out of Space " .. child.Name)
+ child.Visible = false
+ else
+ --print("Laying out " .. child.Name)
+ --GuiObject
+ if setRowSize then rowSizeCounter = rowSizeCounter + 1 end
+ if xCounter + child.AbsoluteSize.X >= totalPixelsX then
+ if setRowSize then
+ rowSize = rowSizeCounter - 1
+ setRowSize = false
+ end
+ xCounter = 0
+ pixelsRemainingY = pixelsRemainingY - child.AbsoluteSize.Y
+ end
+ child.Position = UDim2.new(child.Position.X.Scale,xCounter + xOffset, 0, totalPixelsY - pixelsRemainingY + yOffset)
+ xCounter = xCounter + child.AbsoluteSize.X
+ child.Visible = ((pixelsRemainingY - child.AbsoluteSize.Y) >= 0)
+ if child.Visible then
+ howManyDisplayed = howManyDisplayed + 1
+ end
+ lastChildSize = child.AbsoluteSize
+ end
+ end
+ end
+
+ scrollUpButton.Active = (scrollPosition > 1)
+ if lastChildSize == 0 then
+ scrollDownButton.Active = false
+ else
+ scrollDownButton.Active = ((pixelsRemainingY - lastChildSize.Y) < 0)
+ end
+ scrollDrag.Active = #guiObjects > howManyDisplayed
+ scrollDrag.Visible = scrollDrag.Active
+ end
+
+
+
+ local layoutSimpleScrollBar = function()
+ local guiObjects = {}
+ howManyDisplayed = 0
+
+ if orderList then
+ for i, child in ipairs(orderList) do
+ if child.Parent == frame then
+ table.insert(guiObjects, child)
+ end
+ end
+ else
+ local children = frame:GetChildren()
+ if children then
+ for i, child in ipairs(children) do
+ if child:IsA("GuiObject") then
+ table.insert(guiObjects, child)
+ end
+ end
+ end
+ end
+ if #guiObjects == 0 then
+ scrollUpButton.Active = false
+ scrollDownButton.Active = false
+ scrollDrag.Active = false
+ scrollPosition = 1
+ return
+ end
+
+ if scrollPosition > #guiObjects then
+ scrollPosition = #guiObjects
+ end
+
+ local totalPixels = frame.AbsoluteSize.Y
+ local pixelsRemaining = frame.AbsoluteSize.Y
+
+ local pixelsBelowScrollbar = 0
+ local pos = #guiObjects
+ while pixelsBelowScrollbar < totalPixels and pos >= 1 do
+ if pos >= scrollPosition then
+ pixelsBelowScrollbar = pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y
+ else
+ if pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y <= totalPixels then
+ --It fits, so back up our scroll position
+ pixelsBelowScrollbar = pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y
+ if scrollPosition <= 1 then
+ scrollPosition = 1
+ break
+ else
+ --local ("Backing up ScrollPosition from -- " ..scrollPosition)
+ scrollPosition = scrollPosition - 1
+ end
+ else
+ break
+ end
+ end
+ pos = pos - 1
+ end
+
+ pos = scrollPosition
+ for i, child in ipairs(guiObjects) do
+ if i < scrollPosition then
+ --print("Hiding " .. child.Name)
+ child.Visible = false
+ else
+ if pixelsRemaining < 0 then
+ --print("Out of Space " .. child.Name)
+ child.Visible = false
+ else
+ --print("Laying out " .. child.Name)
+ --GuiObject
+ child.Position = UDim2.new(child.Position.X.Scale, child.Position.X.Offset, 0, totalPixels - pixelsRemaining)
+ pixelsRemaining = pixelsRemaining - child.AbsoluteSize.Y
+ if (pixelsRemaining >= 0) then
+ child.Visible = true
+ howManyDisplayed = howManyDisplayed + 1
+ else
+ child.Visible = false
+ end
+ end
+ end
+ end
+ scrollUpButton.Active = (scrollPosition > 1)
+ scrollDownButton.Active = (pixelsRemaining < 0)
+ scrollDrag.Active = #guiObjects > howManyDisplayed
+ scrollDrag.Visible = scrollDrag.Active
+ end
+
+
+ local moveDragger = function()
+ local guiObjects = 0
+ local children = frame:GetChildren()
+ if children then
+ for i, child in ipairs(children) do
+ if child:IsA("GuiObject") then
+ guiObjects = guiObjects + 1
+ end
+ end
+ end
+
+ if not scrollDrag.Parent then return end
+
+ local dragSizeY = scrollDrag.Parent.AbsoluteSize.y * (1/(guiObjects - howManyDisplayed + 1))
+ if dragSizeY < 16 then dragSizeY = 16 end
+ scrollDrag.Size = UDim2.new(scrollDrag.Size.X.Scale,scrollDrag.Size.X.Offset,scrollDrag.Size.Y.Scale,dragSizeY)
+
+ local relativeYPos = (scrollPosition - 1)/(guiObjects - (howManyDisplayed))
+ if relativeYPos > 1 then relativeYPos = 1
+ elseif relativeYPos < 0 then relativeYPos = 0 end
+ local absYPos = 0
+
+ if relativeYPos ~= 0 then
+ absYPos = (relativeYPos * scrollbar.AbsoluteSize.y) - (relativeYPos * scrollDrag.AbsoluteSize.y)
+ end
+
+ scrollDrag.Position = UDim2.new(scrollDrag.Position.X.Scale,scrollDrag.Position.X.Offset,scrollDrag.Position.Y.Scale,absYPos)
+ end
+
+ local reentrancyGuard = false
+ local recalculate = function()
+ if reentrancyGuard then
+ return
+ end
+ reentrancyGuard = true
+ wait()
+ local success, err = nil
+ if style == "grid" then
+ success, err = pcall(function() layoutGridScrollBar() end)
+ elseif style == "simple" then
+ success, err = pcall(function() layoutSimpleScrollBar() end)
+ end
+ if not success then print(err) end
+ moveDragger()
+ reentrancyGuard = false
+ end
+
+ local doScrollUp = function()
+ scrollPosition = (scrollPosition) - rowSize
+ if scrollPosition < 1 then scrollPosition = 1 end
+ recalculate(nil)
+ end
+
+ local doScrollDown = function()
+ scrollPosition = (scrollPosition) + rowSize
+ recalculate(nil)
+ end
+
+ local scrollUp = function(mouseYPos)
+ if scrollUpButton.Active then
+ scrollStamp = tick()
+ local current = scrollStamp
+ local upCon
+ upCon = mouseDrag.MouseButton1Up:connect(function()
+ scrollStamp = tick()
+ mouseDrag.Parent = nil
+ upCon:disconnect()
+ end)
+ mouseDrag.Parent = getScreenGuiAncestor(scrollbar)
+ doScrollUp()
+ wait(0.2)
+ local t = tick()
+ local w = 0.1
+ while scrollStamp == current do
+ doScrollUp()
+ if mouseYPos and mouseYPos > scrollDrag.AbsolutePosition.y then
+ break
+ end
+ if not scrollUpButton.Active then break end
+ if tick()-t > 5 then
+ w = 0
+ elseif tick()-t > 2 then
+ w = 0.06
+ end
+ wait(w)
+ end
+ end
+ end
+
+ local scrollDown = function(mouseYPos)
+ if scrollDownButton.Active then
+ scrollStamp = tick()
+ local current = scrollStamp
+ local downCon
+ downCon = mouseDrag.MouseButton1Up:connect(function()
+ scrollStamp = tick()
+ mouseDrag.Parent = nil
+ downCon:disconnect()
+ end)
+ mouseDrag.Parent = getScreenGuiAncestor(scrollbar)
+ doScrollDown()
+ wait(0.2)
+ local t = tick()
+ local w = 0.1
+ while scrollStamp == current do
+ doScrollDown()
+ if mouseYPos and mouseYPos < (scrollDrag.AbsolutePosition.y + scrollDrag.AbsoluteSize.x) then
+ break
+ end
+ if not scrollDownButton.Active then break end
+ if tick()-t > 5 then
+ w = 0
+ elseif tick()-t > 2 then
+ w = 0.06
+ end
+ wait(w)
+ end
+ end
+ end
+
+ local y = 0
+ scrollDrag.MouseButton1Down:connect(function(x,y)
+ if scrollDrag.Active then
+ scrollStamp = tick()
+ local mouseOffset = y - scrollDrag.AbsolutePosition.y
+ local dragCon
+ local upCon
+ dragCon = mouseDrag.MouseMoved:connect(function(x,y)
+ local barAbsPos = scrollbar.AbsolutePosition.y
+ local barAbsSize = scrollbar.AbsoluteSize.y
+
+ local dragAbsSize = scrollDrag.AbsoluteSize.y
+ local barAbsOne = barAbsPos + barAbsSize - dragAbsSize
+ y = y - mouseOffset
+ y = y < barAbsPos and barAbsPos or y > barAbsOne and barAbsOne or y
+ y = y - barAbsPos
+
+ local guiObjects = 0
+ local children = frame:GetChildren()
+ if children then
+ for i, child in ipairs(children) do
+ if child:IsA("GuiObject") then
+ guiObjects = guiObjects + 1
+ end
+ end
+ end
+
+ local doublePercent = y/(barAbsSize-dragAbsSize)
+ local rowDiff = rowSize
+ local totalScrollCount = guiObjects - (howManyDisplayed - 1)
+ local newScrollPosition = math.floor((doublePercent * totalScrollCount) + 0.5) + rowDiff
+ if newScrollPosition < scrollPosition then
+ rowDiff = -rowDiff
+ end
+
+ if newScrollPosition < 1 then
+ newScrollPosition = 1
+ end
+
+ scrollPosition = newScrollPosition
+ recalculate(nil)
+ end)
+ upCon = mouseDrag.MouseButton1Up:connect(function()
+ scrollStamp = tick()
+ mouseDrag.Parent = nil
+ dragCon:disconnect(); dragCon = nil
+ upCon:disconnect(); drag = nil
+ end)
+ mouseDrag.Parent = getScreenGuiAncestor(scrollbar)
+ end
+ end)
+
+ local scrollMouseCount = 0
+
+ scrollUpButton.MouseButton1Down:connect(
+ function()
+ scrollUp()
+ end)
+ scrollUpButton.MouseButton1Up:connect(function()
+ scrollStamp = tick()
+ end)
+
+
+ scrollDownButton.MouseButton1Up:connect(function()
+ scrollStamp = tick()
+ end)
+ scrollDownButton.MouseButton1Down:connect(
+ function()
+ scrollDown()
+ end)
+
+ scrollbar.MouseButton1Up:connect(function()
+ scrollStamp = tick()
+ end)
+ scrollbar.MouseButton1Down:connect(
+ function(x,y)
+ if y > (scrollDrag.AbsoluteSize.y + scrollDrag.AbsolutePosition.y) then
+ scrollDown(y)
+ elseif y < (scrollDrag.AbsolutePosition.y) then
+ scrollUp(y)
+ end
+ end)
+
+
+ frame.ChildAdded:connect(function()
+ recalculate(nil)
+ end)
+
+ frame.ChildRemoved:connect(function()
+ recalculate(nil)
+ end)
+
+ frame.Changed:connect(
+ function(prop)
+ if prop == "AbsoluteSize" then
+ --Wait a heartbeat for it to sync in
+ recalculate(nil)
+ end
+ end)
+ frame.AncestryChanged:connect(function() recalculate(nil) end)
+
+ return frame, scrollUpButton, scrollDownButton, recalculate, scrollbar
+end
+local function binaryGrow(min, max, fits)
+ if min > max then
+ return min
+ end
+ local biggestLegal = min
+
+ while min <= max do
+ local mid = min + math.floor((max - min) / 2)
+ if fits(mid) and (biggestLegal == nil or biggestLegal < mid) then
+ biggestLegal = mid
+
+ --Try growing
+ min = mid + 1
+ else
+ --Doesn't fit, shrink
+ max = mid - 1
+ end
+ end
+ return biggestLegal
+end
+
+
+local function binaryShrink(min, max, fits)
+ if min > max then
+ return min
+ end
+ local smallestLegal = max
+
+ while min <= max do
+ local mid = min + math.floor((max - min) / 2)
+ if fits(mid) and (smallestLegal == nil or smallestLegal > mid) then
+ smallestLegal = mid
+
+ --It fits, shrink
+ max = mid - 1
+ else
+ --Doesn't fit, grow
+ min = mid + 1
+ end
+ end
+ return smallestLegal
+end
+
+
+local function getGuiOwner(instance)
+ while instance ~= nil do
+ if instance:IsA("ScreenGui") or instance:IsA("BillboardGui") then
+ return instance
+ end
+ instance = instance.Parent
+ end
+ return nil
+end
+
+t.AutoTruncateTextObject = function(textLabel)
+ local text = textLabel.Text
+
+ local fullLabel = textLabel:Clone()
+ fullLabel.Name = "Full" .. textLabel.Name
+ fullLabel.BorderSizePixel = 0
+ fullLabel.BackgroundTransparency = 0
+ fullLabel.Text = text
+ fullLabel.TextXAlignment = Enum.TextXAlignment.Center
+ fullLabel.Position = UDim2.new(0,-3,0,0)
+ fullLabel.Size = UDim2.new(0,100,1,0)
+ fullLabel.Visible = false
+ fullLabel.Parent = textLabel
+
+ local shortText = nil
+ local mouseEnterConnection = nil
+ local mouseLeaveConnection= nil
+
+ local checkForResize = function()
+ if getGuiOwner(textLabel) == nil then
+ return
+ end
+ textLabel.Text = text
+ if textLabel.TextFits then
+ --Tear down the rollover if it is active
+ if mouseEnterConnection then
+ mouseEnterConnection:disconnect()
+ mouseEnterConnection = nil
+ end
+ if mouseLeaveConnection then
+ mouseLeaveConnection:disconnect()
+ mouseLeaveConnection = nil
+ end
+ else
+ local len = string.len(text)
+ textLabel.Text = text .. "~"
+
+ --Shrink the text
+ local textSize = binaryGrow(0, len,
+ function(pos)
+ if pos == 0 then
+ textLabel.Text = "~"
+ else
+ textLabel.Text = string.sub(text, 1, pos) .. "~"
+ end
+ return textLabel.TextFits
+ end)
+ shortText = string.sub(text, 1, textSize) .. "~"
+ textLabel.Text = shortText
+
+ --Make sure the fullLabel fits
+ if not fullLabel.TextFits then
+ --Already too small, grow it really bit to start
+ fullLabel.Size = UDim2.new(0, 10000, 1, 0)
+ end
+
+ --Okay, now try to binary shrink it back down
+ local fullLabelSize = binaryShrink(textLabel.AbsoluteSize.X,fullLabel.AbsoluteSize.X,
+ function(size)
+ fullLabel.Size = UDim2.new(0, size, 1, 0)
+ return fullLabel.TextFits
+ end)
+ fullLabel.Size = UDim2.new(0,fullLabelSize+6,1,0)
+
+ --Now setup the rollover effects, if they are currently off
+ if mouseEnterConnection == nil then
+ mouseEnterConnection = textLabel.MouseEnter:connect(
+ function()
+ fullLabel.ZIndex = textLabel.ZIndex + 1
+ fullLabel.Visible = true
+ --textLabel.Text = ""
+ end)
+ end
+ if mouseLeaveConnection == nil then
+ mouseLeaveConnection = textLabel.MouseLeave:connect(
+ function()
+ fullLabel.Visible = false
+ --textLabel.Text = shortText
+ end)
+ end
+ end
+ end
+ textLabel.AncestryChanged:connect(checkForResize)
+ textLabel.Changed:connect(
+ function(prop)
+ if prop == "AbsoluteSize" then
+ checkForResize()
+ end
+ end)
+
+ checkForResize()
+
+ local function changeText(newText)
+ text = newText
+ fullLabel.Text = text
+ checkForResize()
+ end
+
+ return textLabel, changeText
+end
+
+local function TransitionTutorialPages(fromPage, toPage, transitionFrame, currentPageValue)
+ if fromPage then
+ fromPage.Visible = false
+ if transitionFrame.Visible == false then
+ transitionFrame.Size = fromPage.Size
+ transitionFrame.Position = fromPage.Position
+ end
+ else
+ if transitionFrame.Visible == false then
+ transitionFrame.Size = UDim2.new(0.0,50,0.0,50)
+ transitionFrame.Position = UDim2.new(0.5,-25,0.5,-25)
+ end
+ end
+ transitionFrame.Visible = true
+ currentPageValue.Value = nil
+
+ local newsize, newPosition
+ if toPage then
+ --Make it visible so it resizes
+ toPage.Visible = true
+
+ newSize = toPage.Size
+ newPosition = toPage.Position
+
+ toPage.Visible = false
+ else
+ newSize = UDim2.new(0.0,50,0.0,50)
+ newPosition = UDim2.new(0.5,-25,0.5,-25)
+ end
+ transitionFrame:TweenSizeAndPosition(newSize, newPosition, Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, 0.3, true,
+ function(state)
+ if state == Enum.TweenStatus.Completed then
+ transitionFrame.Visible = false
+ if toPage then
+ toPage.Visible = true
+ currentPageValue.Value = toPage
+ end
+ end
+ end)
+end
+
+t.CreateTutorial = function(name, tutorialKey, createButtons)
+ local frame = Instance.new("Frame")
+ frame.Name = "Tutorial-" .. name
+ frame.BackgroundTransparency = 1
+ frame.Size = UDim2.new(0.6, 0, 0.6, 0)
+ frame.Position = UDim2.new(0.2, 0, 0.2, 0)
+
+ local transitionFrame = Instance.new("Frame")
+ transitionFrame.Name = "TransitionFrame"
+ transitionFrame.Style = Enum.FrameStyle.RobloxRound
+ transitionFrame.Size = UDim2.new(0.6, 0, 0.6, 0)
+ transitionFrame.Position = UDim2.new(0.2, 0, 0.2, 0)
+ transitionFrame.Visible = false
+ transitionFrame.Parent = frame
+
+ local currentPageValue = Instance.new("ObjectValue")
+ currentPageValue.Name = "CurrentTutorialPage"
+ currentPageValue.Value = nil
+ currentPageValue.Parent = frame
+
+ local boolValue = Instance.new("BoolValue")
+ boolValue.Name = "Buttons"
+ boolValue.Value = createButtons
+ boolValue.Parent = frame
+
+ local pages = Instance.new("Frame")
+ pages.Name = "Pages"
+ pages.BackgroundTransparency = 1
+ pages.Size = UDim2.new(1,0,1,0)
+ pages.Parent = frame
+
+ local function getVisiblePageAndHideOthers()
+ local visiblePage = nil
+ local children = pages:GetChildren()
+ if children then
+ for i,child in ipairs(children) do
+ if child.Visible then
+ if visiblePage then
+ child.Visible = false
+ else
+ visiblePage = child
+ end
+ end
+ end
+ end
+ return visiblePage
+ end
+
+ local showTutorial = function(alwaysShow)
+ if alwaysShow or UserSettings().GameSettings:GetTutorialState(tutorialKey) == false then
+ print("Showing tutorial-",tutorialKey)
+ local currentTutorialPage = getVisiblePageAndHideOthers()
+
+ local firstPage = pages:FindFirstChild("TutorialPage1")
+ if firstPage then
+ TransitionTutorialPages(currentTutorialPage, firstPage, transitionFrame, currentPageValue)
+ else
+ error("Could not find TutorialPage1")
+ end
+ end
+ end
+
+ local dismissTutorial = function()
+ local currentTutorialPage = getVisiblePageAndHideOthers()
+
+ if currentTutorialPage then
+ TransitionTutorialPages(currentTutorialPage, nil, transitionFrame, currentPageValue)
+ end
+
+ UserSettings().GameSettings:SetTutorialState(tutorialKey, true)
+ end
+
+ local gotoPage = function(pageNum)
+ local page = pages:FindFirstChild("TutorialPage" .. pageNum)
+ local currentTutorialPage = getVisiblePageAndHideOthers()
+ TransitionTutorialPages(currentTutorialPage, page, transitionFrame, currentPageValue)
+ end
+
+ return frame, showTutorial, dismissTutorial, gotoPage
+end
+
+local function CreateBasicTutorialPage(name, handleResize, skipTutorial, giveDoneButton)
+ local frame = Instance.new("Frame")
+ frame.Name = "TutorialPage"
+ frame.Style = Enum.FrameStyle.RobloxRound
+ frame.Size = UDim2.new(0.6, 0, 0.6, 0)
+ frame.Position = UDim2.new(0.2, 0, 0.2, 0)
+ frame.Visible = false
+
+ local frameHeader = Instance.new("TextLabel")
+ frameHeader.Name = "Header"
+ frameHeader.Text = name
+ frameHeader.BackgroundTransparency = 1
+ frameHeader.FontSize = Enum.FontSize.Size24
+ frameHeader.Font = Enum.Font.ArialBold
+ frameHeader.TextColor3 = Color3.new(1,1,1)
+ frameHeader.TextXAlignment = Enum.TextXAlignment.Center
+ frameHeader.TextWrap = true
+ frameHeader.Size = UDim2.new(1,-55, 0, 22)
+ frameHeader.Position = UDim2.new(0,0,0,0)
+ frameHeader.Parent = frame
+
+ local skipButton = Instance.new("ImageButton")
+ skipButton.Name = "SkipButton"
+ skipButton.AutoButtonColor = false
+ skipButton.BackgroundTransparency = 1
+ skipButton.Image = "rbxasset://textures/ui/closeButton.png"
+ skipButton.MouseButton1Click:connect(function()
+ skipTutorial()
+ end)
+ skipButton.MouseEnter:connect(function()
+ skipButton.Image = "rbxasset://textures/ui/closeButton_dn.png"
+ end)
+ skipButton.MouseLeave:connect(function()
+ skipButton.Image = "rbxasset://textures/ui/closeButton.png"
+ end)
+ skipButton.Size = UDim2.new(0, 25, 0, 25)
+ skipButton.Position = UDim2.new(1, -25, 0, 0)
+ skipButton.Parent = frame
+
+
+ if giveDoneButton then
+ local doneButton = Instance.new("TextButton")
+ doneButton.Name = "DoneButton"
+ doneButton.Style = Enum.ButtonStyle.RobloxButtonDefault
+ doneButton.Text = "Done"
+ doneButton.TextColor3 = Color3.new(1,1,1)
+ doneButton.Font = Enum.Font.ArialBold
+ doneButton.FontSize = Enum.FontSize.Size18
+ doneButton.Size = UDim2.new(0,100,0,50)
+ doneButton.Position = UDim2.new(0.5,-50,1,-50)
+
+ if skipTutorial then
+ doneButton.MouseButton1Click:connect(function() skipTutorial() end)
+ end
+
+ doneButton.Parent = frame
+ end
+
+ local innerFrame = Instance.new("Frame")
+ innerFrame.Name = "ContentFrame"
+ innerFrame.BackgroundTransparency = 1
+ innerFrame.Position = UDim2.new(0,0,0,25)
+ innerFrame.Parent = frame
+
+ local nextButton = Instance.new("TextButton")
+ nextButton.Name = "NextButton"
+ nextButton.Text = "Next"
+ nextButton.TextColor3 = Color3.new(1,1,1)
+ nextButton.Font = Enum.Font.Arial
+ nextButton.FontSize = Enum.FontSize.Size18
+ nextButton.Style = Enum.ButtonStyle.RobloxButtonDefault
+ nextButton.Size = UDim2.new(0,80, 0, 32)
+ nextButton.Position = UDim2.new(0.5, 5, 1, -32)
+ nextButton.Active = false
+ nextButton.Visible = false
+ nextButton.Parent = frame
+
+ local prevButton = Instance.new("TextButton")
+ prevButton.Name = "PrevButton"
+ prevButton.Text = "Previous"
+ prevButton.TextColor3 = Color3.new(1,1,1)
+ prevButton.Font = Enum.Font.Arial
+ prevButton.FontSize = Enum.FontSize.Size18
+ prevButton.Style = Enum.ButtonStyle.RobloxButton
+ prevButton.Size = UDim2.new(0,80, 0, 32)
+ prevButton.Position = UDim2.new(0.5, -85, 1, -32)
+ prevButton.Active = false
+ prevButton.Visible = false
+ prevButton.Parent = frame
+
+ if giveDoneButton then
+ innerFrame.Size = UDim2.new(1,0,1,-75)
+ else
+ innerFrame.Size = UDim2.new(1,0,1,-22)
+ end
+
+ local parentConnection = nil
+
+ local function basicHandleResize()
+ if frame.Visible and frame.Parent then
+ local maxSize = math.min(frame.Parent.AbsoluteSize.X, frame.Parent.AbsoluteSize.Y)
+ handleResize(200,maxSize)
+ end
+ end
+
+ frame.Changed:connect(
+ function(prop)
+ if prop == "Parent" then
+ if parentConnection ~= nil then
+ parentConnection:disconnect()
+ parentConnection = nil
+ end
+ if frame.Parent and frame.Parent:IsA("GuiObject") then
+ parentConnection = frame.Parent.Changed:connect(
+ function(parentProp)
+ if parentProp == "AbsoluteSize" then
+ wait()
+ basicHandleResize()
+ end
+ end)
+ basicHandleResize()
+ end
+ end
+
+ if prop == "Visible" then
+ basicHandleResize()
+ end
+ end)
+
+ return frame, innerFrame
+end
+
+t.CreateTextTutorialPage = function(name, text, skipTutorialFunc)
+ local frame = nil
+ local contentFrame = nil
+
+ local textLabel = Instance.new("TextLabel")
+ textLabel.BackgroundTransparency = 1
+ textLabel.TextColor3 = Color3.new(1,1,1)
+ textLabel.Text = text
+ textLabel.TextWrap = true
+ textLabel.TextXAlignment = Enum.TextXAlignment.Left
+ textLabel.TextYAlignment = Enum.TextYAlignment.Center
+ textLabel.Font = Enum.Font.Arial
+ textLabel.FontSize = Enum.FontSize.Size14
+ textLabel.Size = UDim2.new(1,0,1,0)
+
+ local function handleResize(minSize, maxSize)
+ size = binaryShrink(minSize, maxSize,
+ function(size)
+ frame.Size = UDim2.new(0, size, 0, size)
+ return textLabel.TextFits
+ end)
+ frame.Size = UDim2.new(0, size, 0, size)
+ frame.Position = UDim2.new(0.5, -size/2, 0.5, -size/2)
+ end
+
+ frame, contentFrame = CreateBasicTutorialPage(name, handleResize, skipTutorialFunc)
+ textLabel.Parent = contentFrame
+
+ return frame
+end
+
+t.CreateImageTutorialPage = function(name, imageAsset, x, y, skipTutorialFunc, giveDoneButton)
+ local frame = nil
+ local contentFrame = nil
+
+ local imageLabel = Instance.new("ImageLabel")
+ imageLabel.BackgroundTransparency = 1
+ imageLabel.Image = imageAsset
+ imageLabel.Size = UDim2.new(0,x,0,y)
+ imageLabel.Position = UDim2.new(0.5,-x/2,0.5,-y/2)
+
+ local function handleResize(minSize, maxSize)
+ size = binaryShrink(minSize, maxSize,
+ function(size)
+ return size >= x and size >= y
+ end)
+ if size >= x and size >= y then
+ imageLabel.Size = UDim2.new(0,x, 0,y)
+ imageLabel.Position = UDim2.new(0.5,-x/2, 0.5, -y/2)
+ else
+ if x > y then
+ --X is limiter, so
+ imageLabel.Size = UDim2.new(1,0,y/x,0)
+ imageLabel.Position = UDim2.new(0,0, 0.5 - (y/x)/2, 0)
+ else
+ --Y is limiter
+ imageLabel.Size = UDim2.new(x/y,0,1, 0)
+ imageLabel.Position = UDim2.new(0.5-(x/y)/2, 0, 0, 0)
+ end
+ end
+ size = size + 50
+ frame.Size = UDim2.new(0, size, 0, size)
+ frame.Position = UDim2.new(0.5, -size/2, 0.5, -size/2)
+ end
+
+ frame, contentFrame = CreateBasicTutorialPage(name, handleResize, skipTutorialFunc, giveDoneButton)
+ imageLabel.Parent = contentFrame
+
+ return frame
+end
+
+t.AddTutorialPage = function(tutorial, tutorialPage)
+ local transitionFrame = tutorial.TransitionFrame
+ local currentPageValue = tutorial.CurrentTutorialPage
+
+ if not tutorial.Buttons.Value then
+ tutorialPage.NextButton.Parent = nil
+ tutorialPage.PrevButton.Parent = nil
+ end
+
+ local children = tutorial.Pages:GetChildren()
+ if children and #children > 0 then
+ tutorialPage.Name = "TutorialPage" .. (#children+1)
+ local previousPage = children[#children]
+ if not previousPage:IsA("GuiObject") then
+ error("All elements under Pages must be GuiObjects")
+ end
+
+ if tutorial.Buttons.Value then
+ if previousPage.NextButton.Active then
+ error("NextButton already Active on previousPage, please only add pages with RbxGui.AddTutorialPage function")
+ end
+ previousPage.NextButton.MouseButton1Click:connect(
+ function()
+ TransitionTutorialPages(previousPage, tutorialPage, transitionFrame, currentPageValue)
+ end)
+ previousPage.NextButton.Active = true
+ previousPage.NextButton.Visible = true
+
+ if tutorialPage.PrevButton.Active then
+ error("PrevButton already Active on tutorialPage, please only add pages with RbxGui.AddTutorialPage function")
+ end
+ tutorialPage.PrevButton.MouseButton1Click:connect(
+ function()
+ TransitionTutorialPages(tutorialPage, previousPage, transitionFrame, currentPageValue)
+ end)
+ tutorialPage.PrevButton.Active = true
+ tutorialPage.PrevButton.Visible = true
+ end
+
+ tutorialPage.Parent = tutorial.Pages
+ else
+ --First child
+ tutorialPage.Name = "TutorialPage1"
+ tutorialPage.Parent = tutorial.Pages
+ end
+end
+
+t.CreateSetPanel = function(userIdsForSets, objectSelected, dialogClosed, size, position, showAdminCategories, useAssetVersionId)
+
+ if not userIdsForSets then
+ error("CreateSetPanel: userIdsForSets (first arg) is nil, should be a table of number ids")
+ end
+ if type(userIdsForSets) ~= "table" and type(userIdsForSets) ~= "userdata" then
+ error("CreateSetPanel: userIdsForSets (first arg) is of type " ..type(userIdsForSets) .. ", should be of type table or userdata")
+ end
+ if not objectSelected then
+ error("CreateSetPanel: objectSelected (second arg) is nil, should be a callback function!")
+ end
+ if type(objectSelected) ~= "function" then
+ error("CreateSetPanel: objectSelected (second arg) is of type " .. type(objectSelected) .. ", should be of type function!")
+ end
+ if dialogClosed and type(dialogClosed) ~= "function" then
+ error("CreateSetPanel: dialogClosed (third arg) is of type " .. type(dialogClosed) .. ", should be of type function!")
+ end
+
+ if showAdminCategories == nil then -- by default, don't show beta sets
+ showAdminCategories = false
+ end
+
+ local arrayPosition = 1
+ local insertButtons = {}
+ local insertButtonCons = {}
+ local contents = nil
+ local setGui = nil
+
+ -- used for water selections
+ local waterForceDirection = "NegX"
+ local waterForce = "None"
+ local waterGui, waterTypeChangedEvent = nil
+
+ local Data = {}
+ Data.CurrentCategory = nil
+ Data.Category = {}
+ local SetCache = {}
+
+ local userCategoryButtons = nil
+
+ local buttonWidth = 64
+ local buttonHeight = buttonWidth
+
+ local SmallThumbnailUrl = nil
+ local LargeThumbnailUrl = nil
+ local BaseUrl = game:GetService("ContentProvider").BaseUrl:lower()
+
+ if useAssetVersionId then
+ LargeThumbnailUrl = BaseUrl .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=420&ht=420&assetversionid="
+ SmallThumbnailUrl = BaseUrl .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=75&ht=75&assetversionid="
+ else
+ LargeThumbnailUrl = BaseUrl .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=420&ht=420&aid="
+ SmallThumbnailUrl = BaseUrl .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=75&ht=75&aid="
+ end
+
+ local function drillDownSetZIndex(parent, index)
+ local children = parent:GetChildren()
+ for i = 1, #children do
+ if children[i]:IsA("GuiObject") then
+ children[i].ZIndex = index
+ end
+ drillDownSetZIndex(children[i], index)
+ end
+ end
+
+ -- for terrain stamping
+ local currTerrainDropDownFrame = nil
+ local terrainShapes = {"Block","Vertical Ramp","Corner Wedge","Inverse Corner Wedge","Horizontal Ramp","Auto-Wedge"}
+ local terrainShapeMap = {}
+ for i = 1, #terrainShapes do
+ terrainShapeMap[terrainShapes[i]] = i - 1
+ end
+ terrainShapeMap[terrainShapes[#terrainShapes]] = 6
+
+ local function createWaterGui()
+ local waterForceDirections = {"NegX","X","NegY","Y","NegZ","Z"}
+ local waterForces = {"None", "Small", "Medium", "Strong", "Max"}
+
+ local waterFrame = Instance.new("Frame")
+ waterFrame.Name = "WaterFrame"
+ waterFrame.Style = Enum.FrameStyle.RobloxSquare
+ waterFrame.Size = UDim2.new(0,150,0,110)
+ waterFrame.Visible = false
+
+ local waterForceLabel = Instance.new("TextLabel")
+ waterForceLabel.Name = "WaterForceLabel"
+ waterForceLabel.BackgroundTransparency = 1
+ waterForceLabel.Size = UDim2.new(1,0,0,12)
+ waterForceLabel.Font = Enum.Font.ArialBold
+ waterForceLabel.FontSize = Enum.FontSize.Size12
+ waterForceLabel.TextColor3 = Color3.new(1,1,1)
+ waterForceLabel.TextXAlignment = Enum.TextXAlignment.Left
+ waterForceLabel.Text = "Water Force"
+ waterForceLabel.Parent = waterFrame
+
+ local waterForceDirLabel = waterForceLabel:Clone()
+ waterForceDirLabel.Name = "WaterForceDirectionLabel"
+ waterForceDirLabel.Text = "Water Force Direction"
+ waterForceDirLabel.Position = UDim2.new(0,0,0,50)
+ waterForceDirLabel.Parent = waterFrame
+
+ local waterTypeChangedEvent = Instance.new("BindableEvent",waterFrame)
+ waterTypeChangedEvent.Name = "WaterTypeChangedEvent"
+
+ local waterForceDirectionSelectedFunc = function(newForceDirection)
+ waterForceDirection = newForceDirection
+ waterTypeChangedEvent:Fire({waterForce, waterForceDirection})
+ end
+ local waterForceSelectedFunc = function(newForce)
+ waterForce = newForce
+ waterTypeChangedEvent:Fire({waterForce, waterForceDirection})
+ end
+
+ local waterForceDirectionDropDown, forceWaterDirectionSelection = t.CreateDropDownMenu(waterForceDirections, waterForceDirectionSelectedFunc)
+ waterForceDirectionDropDown.Size = UDim2.new(1,0,0,25)
+ waterForceDirectionDropDown.Position = UDim2.new(0,0,1,3)
+ forceWaterDirectionSelection("NegX")
+ waterForceDirectionDropDown.Parent = waterForceDirLabel
+
+ local waterForceDropDown, forceWaterForceSelection = t.CreateDropDownMenu(waterForces, waterForceSelectedFunc)
+ forceWaterForceSelection("None")
+ waterForceDropDown.Size = UDim2.new(1,0,0,25)
+ waterForceDropDown.Position = UDim2.new(0,0,1,3)
+ waterForceDropDown.Parent = waterForceLabel
+
+ return waterFrame, waterTypeChangedEvent
+ end
+
+ -- Helper Function that contructs gui elements
+ local function createSetGui()
+
+ local setGui = Instance.new("ScreenGui")
+ setGui.Name = "SetGui"
+
+ local setPanel = Instance.new("Frame")
+ setPanel.Name = "SetPanel"
+ setPanel.Active = true
+ setPanel.BackgroundTransparency = 1
+ if position then
+ setPanel.Position = position
+ else
+ setPanel.Position = UDim2.new(0.2, 29, 0.1, 24)
+ end
+ if size then
+ setPanel.Size = size
+ else
+ setPanel.Size = UDim2.new(0.6, -58, 0.64, 0)
+ end
+ setPanel.Style = Enum.FrameStyle.RobloxRound
+ setPanel.ZIndex = 6
+ setPanel.Parent = setGui
+
+ -- Children of SetPanel
+ local itemPreview = Instance.new("Frame")
+ itemPreview.Name = "ItemPreview"
+ itemPreview.BackgroundTransparency = 1
+ itemPreview.Position = UDim2.new(0.8,5,0.085,0)
+ itemPreview.Size = UDim2.new(0.21,0,0.9,0)
+ itemPreview.ZIndex = 6
+ itemPreview.Parent = setPanel
+
+ -- Children of ItemPreview
+ local textPanel = Instance.new("Frame")
+ textPanel.Name = "TextPanel"
+ textPanel.BackgroundTransparency = 1
+ textPanel.Position = UDim2.new(0,0,0.45,0)
+ textPanel.Size = UDim2.new(1,0,0.55,0)
+ textPanel.ZIndex = 6
+ textPanel.Parent = itemPreview
+
+ -- Children of TextPanel
+ local rolloverText = Instance.new("TextLabel")
+ rolloverText.Name = "RolloverText"
+ rolloverText.BackgroundTransparency = 1
+ rolloverText.Size = UDim2.new(1,0,0,48)
+ rolloverText.ZIndex = 6
+ rolloverText.Font = Enum.Font.ArialBold
+ rolloverText.FontSize = Enum.FontSize.Size24
+ rolloverText.Text = ""
+ rolloverText.TextColor3 = Color3.new(1,1,1)
+ rolloverText.TextWrap = true
+ rolloverText.TextXAlignment = Enum.TextXAlignment.Left
+ rolloverText.TextYAlignment = Enum.TextYAlignment.Top
+ rolloverText.Parent = textPanel
+
+ local largePreview = Instance.new("ImageLabel")
+ largePreview.Name = "LargePreview"
+ largePreview.BackgroundTransparency = 1
+ largePreview.Image = ""
+ largePreview.Size = UDim2.new(1,0,0,170)
+ largePreview.ZIndex = 6
+ largePreview.Parent = itemPreview
+
+ local sets = Instance.new("Frame")
+ sets.Name = "Sets"
+ sets.BackgroundTransparency = 1
+ sets.Position = UDim2.new(0,0,0,5)
+ sets.Size = UDim2.new(0.23,0,1,-5)
+ sets.ZIndex = 6
+ sets.Parent = setPanel
+
+ -- Children of Sets
+ local line = Instance.new("Frame")
+ line.Name = "Line"
+ line.BackgroundColor3 = Color3.new(1,1,1)
+ line.BackgroundTransparency = 0.7
+ line.BorderSizePixel = 0
+ line.Position = UDim2.new(1,-3,0.06,0)
+ line.Size = UDim2.new(0,3,0.9,0)
+ line.ZIndex = 6
+ line.Parent = sets
+
+ local setsLists, controlFrame = t.CreateTrueScrollingFrame()
+ setsLists.Size = UDim2.new(1,-6,0.94,0)
+ setsLists.Position = UDim2.new(0,0,0.06,0)
+ setsLists.BackgroundTransparency = 1
+ setsLists.Name = "SetsLists"
+ setsLists.ZIndex = 6
+ setsLists.Parent = sets
+ drillDownSetZIndex(controlFrame, 7)
+
+ local setsHeader = Instance.new("TextLabel")
+ setsHeader.Name = "SetsHeader"
+ setsHeader.BackgroundTransparency = 1
+ setsHeader.Size = UDim2.new(0,47,0,24)
+ setsHeader.ZIndex = 6
+ setsHeader.Font = Enum.Font.ArialBold
+ setsHeader.FontSize = Enum.FontSize.Size24
+ setsHeader.Text = "Sets"
+ setsHeader.TextColor3 = Color3.new(1,1,1)
+ setsHeader.TextXAlignment = Enum.TextXAlignment.Left
+ setsHeader.TextYAlignment = Enum.TextYAlignment.Top
+ setsHeader.Parent = sets
+
+ local cancelButton = Instance.new("TextButton")
+ cancelButton.Name = "CancelButton"
+ cancelButton.Position = UDim2.new(1,-32,0,-2)
+ cancelButton.Size = UDim2.new(0,34,0,34)
+ cancelButton.Style = Enum.ButtonStyle.RobloxButtonDefault
+ cancelButton.ZIndex = 6
+ cancelButton.Text = ""
+ cancelButton.Modal = true
+ cancelButton.Parent = setPanel
+
+ -- Children of Cancel Button
+ local cancelImage = Instance.new("ImageLabel")
+ cancelImage.Name = "CancelImage"
+ cancelImage.BackgroundTransparency = 1
+ cancelImage.Image = "http://www.syntax.eco/asset?id=54135717"
+ cancelImage.Position = UDim2.new(0,-2,0,-2)
+ cancelImage.Size = UDim2.new(0,16,0,16)
+ cancelImage.ZIndex = 6
+ cancelImage.Parent = cancelButton
+
+ return setGui
+ end
+
+ local function createSetButton(text)
+ local setButton = Instance.new("TextButton")
+
+ if text then setButton.Text = text
+ else setButton.Text = "" end
+
+ setButton.AutoButtonColor = false
+ setButton.BackgroundTransparency = 1
+ setButton.BackgroundColor3 = Color3.new(1,1,1)
+ setButton.BorderSizePixel = 0
+ setButton.Size = UDim2.new(1,-5,0,18)
+ setButton.ZIndex = 6
+ setButton.Visible = false
+ setButton.Font = Enum.Font.Arial
+ setButton.FontSize = Enum.FontSize.Size18
+ setButton.TextColor3 = Color3.new(1,1,1)
+ setButton.TextXAlignment = Enum.TextXAlignment.Left
+
+ return setButton
+ end
+
+ local function buildSetButton(name, setId, setImageId, i, count)
+ local button = createSetButton(name)
+ button.Text = name
+ button.Name = "SetButton"
+ button.Visible = true
+
+ local setValue = Instance.new("IntValue")
+ setValue.Name = "SetId"
+ setValue.Value = setId
+ setValue.Parent = button
+
+ local setName = Instance.new("StringValue")
+ setName.Name = "SetName"
+ setName.Value = name
+ setName.Parent = button
+
+ return button
+ end
+
+ local function processCategory(sets)
+ local setButtons = {}
+ local numSkipped = 0
+ for i = 1, #sets do
+ if not showAdminCategories and sets[i].Name == "Beta" then
+ numSkipped = numSkipped + 1
+ else
+ setButtons[i - numSkipped] = buildSetButton(sets[i].Name, sets[i].CategoryId, sets[i].ImageAssetId, i - numSkipped, #sets)
+ end
+ end
+ return setButtons
+ end
+
+ local function handleResize()
+ wait() -- neccessary to insure heartbeat happened
+
+ local itemPreview = setGui.SetPanel.ItemPreview
+
+ itemPreview.LargePreview.Size = UDim2.new(1,0,0,itemPreview.AbsoluteSize.X)
+ itemPreview.LargePreview.Position = UDim2.new(0.5,-itemPreview.LargePreview.AbsoluteSize.X/2,0,0)
+ itemPreview.TextPanel.Position = UDim2.new(0,0,0,itemPreview.LargePreview.AbsoluteSize.Y)
+ itemPreview.TextPanel.Size = UDim2.new(1,0,0,itemPreview.AbsoluteSize.Y - itemPreview.LargePreview.AbsoluteSize.Y)
+ end
+
+ local function makeInsertAssetButton()
+ local insertAssetButtonExample = Instance.new("Frame")
+ insertAssetButtonExample.Name = "InsertAssetButtonExample"
+ insertAssetButtonExample.Position = UDim2.new(0,128,0,64)
+ insertAssetButtonExample.Size = UDim2.new(0,64,0,64)
+ insertAssetButtonExample.BackgroundTransparency = 1
+ insertAssetButtonExample.ZIndex = 6
+ insertAssetButtonExample.Visible = false
+
+ local assetId = Instance.new("IntValue")
+ assetId.Name = "AssetId"
+ assetId.Value = 0
+ assetId.Parent = insertAssetButtonExample
+
+ local assetName = Instance.new("StringValue")
+ assetName.Name = "AssetName"
+ assetName.Value = ""
+ assetName.Parent = insertAssetButtonExample
+
+ local button = Instance.new("TextButton")
+ button.Name = "Button"
+ button.Text = ""
+ button.Style = Enum.ButtonStyle.RobloxButton
+ button.Position = UDim2.new(0.025,0,0.025,0)
+ button.Size = UDim2.new(0.95,0,0.95,0)
+ button.ZIndex = 6
+ button.Parent = insertAssetButtonExample
+
+ local buttonImage = Instance.new("ImageLabel")
+ buttonImage.Name = "ButtonImage"
+ buttonImage.Image = ""
+ buttonImage.Position = UDim2.new(0,-7,0,-7)
+ buttonImage.Size = UDim2.new(1,14,1,14)
+ buttonImage.BackgroundTransparency = 1
+ buttonImage.ZIndex = 7
+ buttonImage.Parent = button
+
+ local configIcon = buttonImage:clone()
+ configIcon.Name = "ConfigIcon"
+ configIcon.Visible = false
+ configIcon.Position = UDim2.new(1,-23,1,-24)
+ configIcon.Size = UDim2.new(0,16,0,16)
+ configIcon.Image = ""
+ configIcon.ZIndex = 6
+ configIcon.Parent = insertAssetButtonExample
+
+ return insertAssetButtonExample
+ end
+
+ local function showLargePreview(insertButton)
+ if insertButton:FindFirstChild("AssetId") then
+ delay(0,function()
+ game:GetService("ContentProvider"):Preload(LargeThumbnailUrl .. tostring(insertButton.AssetId.Value))
+ setGui.SetPanel.ItemPreview.LargePreview.Image = LargeThumbnailUrl .. tostring(insertButton.AssetId.Value)
+ end)
+ end
+ if insertButton:FindFirstChild("AssetName") then
+ setGui.SetPanel.ItemPreview.TextPanel.RolloverText.Text = insertButton.AssetName.Value
+ end
+ end
+
+ local function selectTerrainShape(shape)
+ if currTerrainDropDownFrame then
+ objectSelected(tostring(currTerrainDropDownFrame.AssetName.Value), tonumber(currTerrainDropDownFrame.AssetId.Value), shape)
+ end
+ end
+
+ local function createTerrainTypeButton(name, parent)
+ local dropDownTextButton = Instance.new("TextButton")
+ dropDownTextButton.Name = name .. "Button"
+ dropDownTextButton.Font = Enum.Font.ArialBold
+ dropDownTextButton.FontSize = Enum.FontSize.Size14
+ dropDownTextButton.BorderSizePixel = 0
+ dropDownTextButton.TextColor3 = Color3.new(1,1,1)
+ dropDownTextButton.Text = name
+ dropDownTextButton.TextXAlignment = Enum.TextXAlignment.Left
+ dropDownTextButton.BackgroundTransparency = 1
+ dropDownTextButton.ZIndex = parent.ZIndex + 1
+ dropDownTextButton.Size = UDim2.new(0,parent.Size.X.Offset - 2,0,16)
+ dropDownTextButton.Position = UDim2.new(0,1,0,0)
+
+ dropDownTextButton.MouseEnter:connect(function()
+ dropDownTextButton.BackgroundTransparency = 0
+ dropDownTextButton.TextColor3 = Color3.new(0,0,0)
+ end)
+
+ dropDownTextButton.MouseLeave:connect(function()
+ dropDownTextButton.BackgroundTransparency = 1
+ dropDownTextButton.TextColor3 = Color3.new(1,1,1)
+ end)
+
+ dropDownTextButton.MouseButton1Click:connect(function()
+ dropDownTextButton.BackgroundTransparency = 1
+ dropDownTextButton.TextColor3 = Color3.new(1,1,1)
+ if dropDownTextButton.Parent and dropDownTextButton.Parent:IsA("GuiObject") then
+ dropDownTextButton.Parent.Visible = false
+ end
+ selectTerrainShape(terrainShapeMap[dropDownTextButton.Text])
+ end)
+
+ return dropDownTextButton
+ end
+
+ local function createTerrainDropDownMenu(zIndex)
+ local dropDown = Instance.new("Frame")
+ dropDown.Name = "TerrainDropDown"
+ dropDown.BackgroundColor3 = Color3.new(0,0,0)
+ dropDown.BorderColor3 = Color3.new(1,0,0)
+ dropDown.Size = UDim2.new(0,200,0,0)
+ dropDown.Visible = false
+ dropDown.ZIndex = zIndex
+ dropDown.Parent = setGui
+
+ for i = 1, #terrainShapes do
+ local shapeButton = createTerrainTypeButton(terrainShapes[i],dropDown)
+ shapeButton.Position = UDim2.new(0,1,0,(i - 1) * (shapeButton.Size.Y.Offset))
+ shapeButton.Parent = dropDown
+ dropDown.Size = UDim2.new(0,200,0,dropDown.Size.Y.Offset + (shapeButton.Size.Y.Offset))
+ end
+
+ dropDown.MouseLeave:connect(function()
+ dropDown.Visible = false
+ end)
+ end
+
+
+ local function createDropDownMenuButton(parent)
+ local dropDownButton = Instance.new("ImageButton")
+ dropDownButton.Name = "DropDownButton"
+ dropDownButton.Image = "http://www.syntax.eco/asset/?id=67581509"
+ dropDownButton.BackgroundTransparency = 1
+ dropDownButton.Size = UDim2.new(0,16,0,16)
+ dropDownButton.Position = UDim2.new(1,-24,0,6)
+ dropDownButton.ZIndex = parent.ZIndex + 2
+ dropDownButton.Parent = parent
+
+ if not setGui:FindFirstChild("TerrainDropDown") then
+ createTerrainDropDownMenu(8)
+ end
+
+ dropDownButton.MouseButton1Click:connect(function()
+ setGui.TerrainDropDown.Visible = true
+ setGui.TerrainDropDown.Position = UDim2.new(0,parent.AbsolutePosition.X,0,parent.AbsolutePosition.Y)
+ currTerrainDropDownFrame = parent
+ end)
+ end
+
+ local function buildInsertButton()
+ local insertButton = makeInsertAssetButton()
+ insertButton.Name = "InsertAssetButton"
+ insertButton.Visible = true
+
+ if Data.Category[Data.CurrentCategory].SetName == "High Scalability" then
+ createDropDownMenuButton(insertButton)
+ end
+
+ local lastEnter = nil
+ local mouseEnterCon = insertButton.MouseEnter:connect(function()
+ lastEnter = insertButton
+ delay(0.1,function()
+ if lastEnter == insertButton then
+ showLargePreview(insertButton)
+ end
+ end)
+ end)
+ return insertButton, mouseEnterCon
+ end
+
+ local function realignButtonGrid(columns)
+ local x = 0
+ local y = 0
+ for i = 1, #insertButtons do
+ insertButtons[i].Position = UDim2.new(0, buttonWidth * x, 0, buttonHeight * y)
+ x = x + 1
+ if x >= columns then
+ x = 0
+ y = y + 1
+ end
+ end
+ end
+
+ local function setInsertButtonImageBehavior(insertFrame, visible, name, assetId)
+ if visible then
+ insertFrame.AssetName.Value = name
+ insertFrame.AssetId.Value = assetId
+ local newImageUrl = SmallThumbnailUrl .. assetId
+ if newImageUrl ~= insertFrame.Button.ButtonImage.Image then
+ delay(0,function()
+ game:GetService("ContentProvider"):Preload(SmallThumbnailUrl .. assetId)
+ insertFrame.Button.ButtonImage.Image = SmallThumbnailUrl .. assetId
+ end)
+ end
+ table.insert(insertButtonCons,
+ insertFrame.Button.MouseButton1Click:connect(function()
+ -- special case for water, show water selection gui
+ local isWaterSelected = (name == "Water") and (Data.Category[Data.CurrentCategory].SetName == "High Scalability")
+ waterGui.Visible = isWaterSelected
+ if isWaterSelected then
+ objectSelected(name, tonumber(assetId), nil)
+ else
+ objectSelected(name, tonumber(assetId))
+ end
+ end)
+ )
+ insertFrame.Visible = true
+ else
+ insertFrame.Visible = false
+ end
+ end
+
+ local function loadSectionOfItems(setGui, rows, columns)
+ local pageSize = rows * columns
+
+ if arrayPosition > #contents then return end
+
+ local origArrayPos = arrayPosition
+
+ local yCopy = 0
+ for i = 1, pageSize + 1 do
+ if arrayPosition >= #contents + 1 then
+ break
+ end
+
+ local buttonCon
+ insertButtons[arrayPosition], buttonCon = buildInsertButton()
+ table.insert(insertButtonCons,buttonCon)
+ insertButtons[arrayPosition].Parent = setGui.SetPanel.ItemsFrame
+ arrayPosition = arrayPosition + 1
+ end
+ realignButtonGrid(columns)
+
+ local indexCopy = origArrayPos
+ for index = origArrayPos, arrayPosition do
+ if insertButtons[index] then
+ if contents[index] then
+
+ -- we don't want water to have a drop down button
+ if contents[index].Name == "Water" then
+ if Data.Category[Data.CurrentCategory].SetName == "High Scalability" then
+ insertButtons[index]:FindFirstChild("DropDownButton",true):Destroy()
+ end
+ end
+
+ local assetId
+ if useAssetVersionId then
+ assetId = contents[index].AssetVersionId
+ else
+ assetId = contents[index].AssetId
+ end
+ setInsertButtonImageBehavior(insertButtons[index], true, contents[index].Name, assetId)
+ else
+ break
+ end
+ else
+ break
+ end
+ indexCopy = index
+ end
+ end
+
+ local function setSetIndex()
+ Data.Category[Data.CurrentCategory].Index = 0
+
+ rows = 7
+ columns = math.floor(setGui.SetPanel.ItemsFrame.AbsoluteSize.X/buttonWidth)
+
+ contents = Data.Category[Data.CurrentCategory].Contents
+ if contents then
+ -- remove our buttons and their connections
+ for i = 1, #insertButtons do
+ insertButtons[i]:remove()
+ end
+ for i = 1, #insertButtonCons do
+ if insertButtonCons[i] then insertButtonCons[i]:disconnect() end
+ end
+ insertButtonCons = {}
+ insertButtons = {}
+
+ arrayPosition = 1
+ loadSectionOfItems(setGui, rows, columns)
+ end
+ end
+
+ local function selectSet(button, setName, setId, setIndex)
+ if button and Data.Category[Data.CurrentCategory] ~= nil then
+ if button ~= Data.Category[Data.CurrentCategory].Button then
+ Data.Category[Data.CurrentCategory].Button = button
+
+ if SetCache[setId] == nil then
+ SetCache[setId] = game:GetService("InsertService"):GetCollection(setId)
+ end
+ Data.Category[Data.CurrentCategory].Contents = SetCache[setId]
+
+ Data.Category[Data.CurrentCategory].SetName = setName
+ Data.Category[Data.CurrentCategory].SetId = setId
+ end
+ setSetIndex()
+ end
+ end
+
+ local function selectCategoryPage(buttons, page)
+ if buttons ~= Data.CurrentCategory then
+ if Data.CurrentCategory then
+ for key, button in pairs(Data.CurrentCategory) do
+ button.Visible = false
+ end
+ end
+
+ Data.CurrentCategory = buttons
+ if Data.Category[Data.CurrentCategory] == nil then
+ Data.Category[Data.CurrentCategory] = {}
+ if #buttons > 0 then
+ selectSet(buttons[1], buttons[1].SetName.Value, buttons[1].SetId.Value, 0)
+ end
+ else
+ Data.Category[Data.CurrentCategory].Button = nil
+ selectSet(Data.Category[Data.CurrentCategory].ButtonFrame, Data.Category[Data.CurrentCategory].SetName, Data.Category[Data.CurrentCategory].SetId, Data.Category[Data.CurrentCategory].Index)
+ end
+ end
+ end
+
+ local function selectCategory(category)
+ selectCategoryPage(category, 0)
+ end
+
+ local function resetAllSetButtonSelection()
+ local setButtons = setGui.SetPanel.Sets.SetsLists:GetChildren()
+ for i = 1, #setButtons do
+ if setButtons[i]:IsA("TextButton") then
+ setButtons[i].Selected = false
+ setButtons[i].BackgroundTransparency = 1
+ setButtons[i].TextColor3 = Color3.new(1,1,1)
+ setButtons[i].BackgroundColor3 = Color3.new(1,1,1)
+ end
+ end
+ end
+
+ local function populateSetsFrame()
+ local currRow = 0
+ for i = 1, #userCategoryButtons do
+ local button = userCategoryButtons[i]
+ button.Visible = true
+ button.Position = UDim2.new(0,5,0,currRow * button.Size.Y.Offset)
+ button.Parent = setGui.SetPanel.Sets.SetsLists
+
+ if i == 1 then -- we will have this selected by default, so show it
+ button.Selected = true
+ button.BackgroundColor3 = Color3.new(0,204/255,0)
+ button.TextColor3 = Color3.new(0,0,0)
+ button.BackgroundTransparency = 0
+ end
+
+ button.MouseEnter:connect(function()
+ if not button.Selected then
+ button.BackgroundTransparency = 0
+ button.TextColor3 = Color3.new(0,0,0)
+ end
+ end)
+ button.MouseLeave:connect(function()
+ if not button.Selected then
+ button.BackgroundTransparency = 1
+ button.TextColor3 = Color3.new(1,1,1)
+ end
+ end)
+ button.MouseButton1Click:connect(function()
+ resetAllSetButtonSelection()
+ button.Selected = not button.Selected
+ button.BackgroundColor3 = Color3.new(0,204/255,0)
+ button.TextColor3 = Color3.new(0,0,0)
+ button.BackgroundTransparency = 0
+ selectSet(button, button.Text, userCategoryButtons[i].SetId.Value, 0)
+ end)
+
+ currRow = currRow + 1
+ end
+
+ local buttons = setGui.SetPanel.Sets.SetsLists:GetChildren()
+
+ -- set first category as loaded for default
+ if buttons then
+ for i = 1, #buttons do
+ if buttons[i]:IsA("TextButton") then
+ selectSet(buttons[i], buttons[i].Text, userCategoryButtons[i].SetId.Value, 0)
+ selectCategory(userCategoryButtons)
+ break
+ end
+ end
+ end
+ end
+
+ setGui = createSetGui()
+ waterGui, waterTypeChangedEvent = createWaterGui()
+ waterGui.Position = UDim2.new(0,55,0,0)
+ waterGui.Parent = setGui
+ setGui.Changed:connect(function(prop) -- this resizes the preview image to always be the right size
+ if prop == "AbsoluteSize" then
+ handleResize()
+ setSetIndex()
+ end
+ end)
+
+ local scrollFrame, controlFrame = t.CreateTrueScrollingFrame()
+ scrollFrame.Size = UDim2.new(0.54,0,0.85,0)
+ scrollFrame.Position = UDim2.new(0.24,0,0.085,0)
+ scrollFrame.Name = "ItemsFrame"
+ scrollFrame.ZIndex = 6
+ scrollFrame.Parent = setGui.SetPanel
+ scrollFrame.BackgroundTransparency = 1
+
+ drillDownSetZIndex(controlFrame,7)
+
+ controlFrame.Parent = setGui.SetPanel
+ controlFrame.Position = UDim2.new(0.76, 5, 0, 0)
+
+ local debounce = false
+ controlFrame.ScrollBottom.Changed:connect(function(prop)
+ if controlFrame.ScrollBottom.Value == true then
+ if debounce then return end
+ debounce = true
+ loadSectionOfItems(setGui, rows, columns)
+ debounce = false
+ end
+ end)
+
+ local userData = {}
+ for id = 1, #userIdsForSets do
+ local newUserData = game:GetService("InsertService"):GetUserSets(userIdsForSets[id])
+ if newUserData and #newUserData > 2 then
+ -- start at #3 to skip over My Decals and My Models for each account
+ for category = 3, #newUserData do
+ if newUserData[category].Name == "High Scalability" then -- we want high scalability parts to show first
+ table.insert(userData,1,newUserData[category])
+ else
+ table.insert(userData, newUserData[category])
+ end
+ end
+ end
+
+ end
+ if userData then
+ userCategoryButtons = processCategory(userData)
+ end
+
+ rows = math.floor(setGui.SetPanel.ItemsFrame.AbsoluteSize.Y/buttonHeight)
+ columns = math.floor(setGui.SetPanel.ItemsFrame.AbsoluteSize.X/buttonWidth)
+
+ populateSetsFrame()
+
+ insertPanelCloseCon = setGui.SetPanel.CancelButton.MouseButton1Click:connect(function()
+ setGui.SetPanel.Visible = false
+ if dialogClosed then dialogClosed() end
+ end)
+
+ local setVisibilityFunction = function(visible)
+ if visible then
+ setGui.SetPanel.Visible = true
+ else
+ setGui.SetPanel.Visible = false
+ end
+ end
+
+ local getVisibilityFunction = function()
+ if setGui then
+ if setGui:FindFirstChild("SetPanel") then
+ return setGui.SetPanel.Visible
+ end
+ end
+
+ return false
+ end
+
+ return setGui, setVisibilityFunction, getVisibilityFunction, waterTypeChangedEvent
+end
+
+t.CreateTerrainMaterialSelector = function(size,position)
+ local terrainMaterialSelectionChanged = Instance.new("BindableEvent")
+ terrainMaterialSelectionChanged.Name = "TerrainMaterialSelectionChanged"
+
+ local selectedButton = nil
+
+ local frame = Instance.new("Frame")
+ frame.Name = "TerrainMaterialSelector"
+ if size then
+ frame.Size = size
+ else
+ frame.Size = UDim2.new(0, 245, 0, 230)
+ end
+ if position then
+ frame.Position = position
+ end
+ frame.BorderSizePixel = 0
+ frame.BackgroundColor3 = Color3.new(0,0,0)
+ frame.Active = true
+
+ terrainMaterialSelectionChanged.Parent = frame
+
+ local waterEnabled = true -- todo: turn this on when water is ready
+
+ local materialToImageMap = {}
+ local materialNames = {"Grass", "Sand", "Brick", "Granite", "Asphalt", "Iron", "Aluminum", "Gold", "Plank", "Log", "Gravel", "Cinder Block", "Stone Wall", "Concrete", "Plastic (red)", "Plastic (blue)"}
+ if waterEnabled then
+ table.insert(materialNames,"Water")
+ end
+ local currentMaterial = 1
+
+ function getEnumFromName(choice)
+ if choice == "Grass" then return 1 end
+ if choice == "Sand" then return 2 end
+ if choice == "Erase" then return 0 end
+ if choice == "Brick" then return 3 end
+ if choice == "Granite" then return 4 end
+ if choice == "Asphalt" then return 5 end
+ if choice == "Iron" then return 6 end
+ if choice == "Aluminum" then return 7 end
+ if choice == "Gold" then return 8 end
+ if choice == "Plank" then return 9 end
+ if choice == "Log" then return 10 end
+ if choice == "Gravel" then return 11 end
+ if choice == "Cinder Block" then return 12 end
+ if choice == "Stone Wall" then return 13 end
+ if choice == "Concrete" then return 14 end
+ if choice == "Plastic (red)" then return 15 end
+ if choice == "Plastic (blue)" then return 16 end
+ if choice == "Water" then return 17 end
+ end
+
+ function getNameFromEnum(choice)
+ if choice == Enum.CellMaterial.Grass or choice == 1 then return "Grass"end
+ if choice == Enum.CellMaterial.Sand or choice == 2 then return "Sand" end
+ if choice == Enum.CellMaterial.Empty or choice == 0 then return "Erase" end
+ if choice == Enum.CellMaterial.Brick or choice == 3 then return "Brick" end
+ if choice == Enum.CellMaterial.Granite or choice == 4 then return "Granite" end
+ if choice == Enum.CellMaterial.Asphalt or choice == 5 then return "Asphalt" end
+ if choice == Enum.CellMaterial.Iron or choice == 6 then return "Iron" end
+ if choice == Enum.CellMaterial.Aluminum or choice == 7 then return "Aluminum" end
+ if choice == Enum.CellMaterial.Gold or choice == 8 then return "Gold" end
+ if choice == Enum.CellMaterial.WoodPlank or choice == 9 then return "Plank" end
+ if choice == Enum.CellMaterial.WoodLog or choice == 10 then return "Log" end
+ if choice == Enum.CellMaterial.Gravel or choice == 11 then return "Gravel" end
+ if choice == Enum.CellMaterial.CinderBlock or choice == 12 then return "Cinder Block" end
+ if choice == Enum.CellMaterial.MossyStone or choice == 13 then return "Stone Wall" end
+ if choice == Enum.CellMaterial.Cement or choice == 14 then return "Concrete" end
+ if choice == Enum.CellMaterial.RedPlastic or choice == 15 then return "Plastic (red)" end
+ if choice == Enum.CellMaterial.BluePlastic or choice == 16 then return "Plastic (blue)" end
+
+ if waterEnabled then
+ if choice == Enum.CellMaterial.Water or choice == 17 then return "Water" end
+ end
+ end
+
+
+ local function updateMaterialChoice(choice)
+ currentMaterial = getEnumFromName(choice)
+ terrainMaterialSelectionChanged:Fire(currentMaterial)
+ end
+
+ -- we so need a better way to do this
+ for i,v in pairs(materialNames) do
+ materialToImageMap[v] = {}
+ if v == "Grass" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=56563112"
+ elseif v == "Sand" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=62356652"
+ elseif v == "Brick" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=65961537"
+ elseif v == "Granite" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=67532153"
+ elseif v == "Asphalt" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=67532038"
+ elseif v == "Iron" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=67532093"
+ elseif v == "Aluminum" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=67531995"
+ elseif v == "Gold" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=67532118"
+ elseif v == "Plastic (red)" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=67531848"
+ elseif v == "Plastic (blue)" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=67531924"
+ elseif v == "Plank" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=67532015"
+ elseif v == "Log" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=67532051"
+ elseif v == "Gravel" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=67532206"
+ elseif v == "Cinder Block" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=67532103"
+ elseif v == "Stone Wall" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=67531804"
+ elseif v == "Concrete" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=67532059"
+ elseif v == "Water" then materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=81407474"
+ else materialToImageMap[v].Regular = "http://www.syntax.eco/asset/?id=66887593" -- fill in the rest here!!
+ end
+ end
+
+ local scrollFrame, scrollUp, scrollDown, recalculateScroll = t.CreateScrollingFrame(nil,"grid")
+ scrollFrame.Size = UDim2.new(0.85,0,1,0)
+ scrollFrame.Position = UDim2.new(0,0,0,0)
+ scrollFrame.Parent = frame
+
+ scrollUp.Parent = frame
+ scrollUp.Visible = true
+ scrollUp.Position = UDim2.new(1,-19,0,0)
+
+ scrollDown.Parent = frame
+ scrollDown.Visible = true
+ scrollDown.Position = UDim2.new(1,-19,1,-17)
+
+ local function goToNewMaterial(buttonWrap, materialName)
+ updateMaterialChoice(materialName)
+ buttonWrap.BackgroundTransparency = 0
+ selectedButton.BackgroundTransparency = 1
+ selectedButton = buttonWrap
+ end
+
+ local function createMaterialButton(name)
+ local buttonWrap = Instance.new("TextButton")
+ buttonWrap.Text = ""
+ buttonWrap.Size = UDim2.new(0,32,0,32)
+ buttonWrap.BackgroundColor3 = Color3.new(1,1,1)
+ buttonWrap.BorderSizePixel = 0
+ buttonWrap.BackgroundTransparency = 1
+ buttonWrap.AutoButtonColor = false
+ buttonWrap.Name = tostring(name)
+
+ local imageButton = Instance.new("ImageButton")
+ imageButton.AutoButtonColor = false
+ imageButton.BackgroundTransparency = 1
+ imageButton.Size = UDim2.new(0,30,0,30)
+ imageButton.Position = UDim2.new(0,1,0,1)
+ imageButton.Name = tostring(name)
+ imageButton.Parent = buttonWrap
+ imageButton.Image = materialToImageMap[name].Regular
+
+ local enumType = Instance.new("NumberValue")
+ enumType.Name = "EnumType"
+ enumType.Parent = buttonWrap
+ enumType.Value = 0
+
+ imageButton.MouseEnter:connect(function()
+ buttonWrap.BackgroundTransparency = 0
+ end)
+ imageButton.MouseLeave:connect(function()
+ if selectedButton ~= buttonWrap then
+ buttonWrap.BackgroundTransparency = 1
+ end
+ end)
+ imageButton.MouseButton1Click:connect(function()
+ if selectedButton ~= buttonWrap then
+ goToNewMaterial(buttonWrap, tostring(name))
+ end
+ end)
+
+ return buttonWrap
+ end
+
+ for i = 1, #materialNames do
+ local imageButton = createMaterialButton(materialNames[i])
+
+ if materialNames[i] == "Grass" then -- always start with grass as the default
+ selectedButton = imageButton
+ imageButton.BackgroundTransparency = 0
+ end
+
+ imageButton.Parent = scrollFrame
+ end
+
+ local forceTerrainMaterialSelection = function(newMaterialType)
+ if not newMaterialType then return end
+ if currentMaterial == newMaterialType then return end
+
+ local matName = getNameFromEnum(newMaterialType)
+ local buttons = scrollFrame:GetChildren()
+ for i = 1, #buttons do
+ if buttons[i].Name == "Plastic (blue)" and matName == "Plastic (blue)" then goToNewMaterial(buttons[i],matName) return end
+ if buttons[i].Name == "Plastic (red)" and matName == "Plastic (red)" then goToNewMaterial(buttons[i],matName) return end
+ if string.find(buttons[i].Name, matName) then
+ goToNewMaterial(buttons[i],matName)
+ return
+ end
+ end
+ end
+
+ frame.Changed:connect(function ( prop )
+ if prop == "AbsoluteSize" then
+ recalculateScroll()
+ end
+ end)
+
+ recalculateScroll()
+ return frame, terrainMaterialSelectionChanged, forceTerrainMaterialSelection
+end
+
+t.CreateLoadingFrame = function(name,size,position)
+ game:GetService("ContentProvider"):Preload("http://www.syntax.eco/asset/?id=35238053")
+
+ local loadingFrame = Instance.new("Frame")
+ loadingFrame.Name = "LoadingFrame"
+ loadingFrame.Style = Enum.FrameStyle.RobloxRound
+
+ if size then loadingFrame.Size = size
+ else loadingFrame.Size = UDim2.new(0,300,0,160) end
+ if position then loadingFrame.Position = position
+ else loadingFrame.Position = UDim2.new(0.5, -150, 0.5,-80) end
+
+ local loadingBar = Instance.new("Frame")
+ loadingBar.Name = "LoadingBar"
+ loadingBar.BackgroundColor3 = Color3.new(0,0,0)
+ loadingBar.BorderColor3 = Color3.new(79/255,79/255,79/255)
+ loadingBar.Position = UDim2.new(0,0,0,41)
+ loadingBar.Size = UDim2.new(1,0,0,30)
+ loadingBar.Parent = loadingFrame
+
+ local loadingGreenBar = Instance.new("ImageLabel")
+ loadingGreenBar.Name = "LoadingGreenBar"
+ loadingGreenBar.Image = "http://www.syntax.eco/asset/?id=35238053"
+ loadingGreenBar.Position = UDim2.new(0,0,0,0)
+ loadingGreenBar.Size = UDim2.new(0,0,1,0)
+ loadingGreenBar.Visible = false
+ loadingGreenBar.Parent = loadingBar
+
+ local loadingPercent = Instance.new("TextLabel")
+ loadingPercent.Name = "LoadingPercent"
+ loadingPercent.BackgroundTransparency = 1
+ loadingPercent.Position = UDim2.new(0,0,1,0)
+ loadingPercent.Size = UDim2.new(1,0,0,14)
+ loadingPercent.Font = Enum.Font.Arial
+ loadingPercent.Text = "0%"
+ loadingPercent.FontSize = Enum.FontSize.Size14
+ loadingPercent.TextColor3 = Color3.new(1,1,1)
+ loadingPercent.Parent = loadingBar
+
+ local cancelButton = Instance.new("TextButton")
+ cancelButton.Name = "CancelButton"
+ cancelButton.Position = UDim2.new(0.5,-60,1,-40)
+ cancelButton.Size = UDim2.new(0,120,0,40)
+ cancelButton.Font = Enum.Font.Arial
+ cancelButton.FontSize = Enum.FontSize.Size18
+ cancelButton.TextColor3 = Color3.new(1,1,1)
+ cancelButton.Text = "Cancel"
+ cancelButton.Style = Enum.ButtonStyle.RobloxButton
+ cancelButton.Parent = loadingFrame
+
+ local loadingName = Instance.new("TextLabel")
+ loadingName.Name = "loadingName"
+ loadingName.BackgroundTransparency = 1
+ loadingName.Size = UDim2.new(1,0,0,18)
+ loadingName.Position = UDim2.new(0,0,0,2)
+ loadingName.Font = Enum.Font.Arial
+ loadingName.Text = name
+ loadingName.TextColor3 = Color3.new(1,1,1)
+ loadingName.TextStrokeTransparency = 1
+ loadingName.FontSize = Enum.FontSize.Size18
+ loadingName.Parent = loadingFrame
+
+ local cancelButtonClicked = Instance.new("BindableEvent")
+ cancelButtonClicked.Name = "CancelButtonClicked"
+ cancelButtonClicked.Parent = cancelButton
+ cancelButton.MouseButton1Click:connect(function()
+ cancelButtonClicked:Fire()
+ end)
+
+ local updateLoadingGuiPercent = function(percent, tweenAction, tweenLength)
+ if percent and type(percent) ~= "number" then
+ error("updateLoadingGuiPercent expects number as argument, got",type(percent),"instead")
+ end
+
+ local newSize = nil
+ if percent < 0 then
+ newSize = UDim2.new(0,0,1,0)
+ elseif percent > 1 then
+ newSize = UDim2.new(1,0,1,0)
+ else
+ newSize = UDim2.new(percent,0,1,0)
+ end
+
+ if tweenAction then
+ if not tweenLength then
+ error("updateLoadingGuiPercent is set to tween new percentage, but got no tween time length! Please pass this in as third argument")
+ end
+
+ if (newSize.X.Scale > 0) then
+ loadingGreenBar.Visible = true
+ loadingGreenBar:TweenSize( newSize,
+ Enum.EasingDirection.Out,
+ Enum.EasingStyle.Quad,
+ tweenLength,
+ true)
+ else
+ loadingGreenBar:TweenSize( newSize,
+ Enum.EasingDirection.Out,
+ Enum.EasingStyle.Quad,
+ tweenLength,
+ true,
+ function()
+ if (newSize.X.Scale < 0) then
+ loadingGreenBar.Visible = false
+ end
+ end)
+ end
+
+ else
+ loadingGreenBar.Size = newSize
+ loadingGreenBar.Visible = (newSize.X.Scale > 0)
+ end
+ end
+
+ loadingGreenBar.Changed:connect(function(prop)
+ if prop == "Size" then
+ loadingPercent.Text = tostring( math.ceil(loadingGreenBar.Size.X.Scale * 100) ) .. "%"
+ end
+ end)
+
+ return loadingFrame, updateLoadingGuiPercent, cancelButtonClicked
+end
+
+t.CreatePluginFrame = function (name,size,position,scrollable,parent)
+ function createMenuButton(size,position,text,fontsize,name,parent)
+ local button = Instance.new("TextButton",parent)
+ button.AutoButtonColor = false
+ button.Name = name
+ button.BackgroundTransparency = 1
+ button.Position = position
+ button.Size = size
+ button.Font = Enum.Font.ArialBold
+ button.FontSize = fontsize
+ button.Text = text
+ button.TextColor3 = Color3.new(1,1,1)
+ button.BorderSizePixel = 0
+ button.BackgroundColor3 = Color3.new(20/255,20/255,20/255)
+
+ button.MouseEnter:connect(function ( )
+ if button.Selected then return end
+ button.BackgroundTransparency = 0
+ end)
+ button.MouseLeave:connect(function ( )
+ if button.Selected then return end
+ button.BackgroundTransparency = 1
+ end)
+
+ return button
+
+ end
+
+ local dragBar = Instance.new("Frame",parent)
+ dragBar.Name = tostring(name) .. "DragBar"
+ dragBar.BackgroundColor3 = Color3.new(39/255,39/255,39/255)
+ dragBar.BorderColor3 = Color3.new(0,0,0)
+ if size then
+ dragBar.Size = UDim2.new(size.X.Scale,size.X.Offset,0,20) + UDim2.new(0,20,0,0)
+ else
+ dragBar.Size = UDim2.new(0,183,0,20)
+ end
+ if position then
+ dragBar.Position = position
+ end
+ dragBar.Active = true
+ dragBar.Draggable = true
+ --dragBar.Visible = false
+ dragBar.MouseEnter:connect(function ( )
+ dragBar.BackgroundColor3 = Color3.new(49/255,49/255,49/255)
+ end)
+ dragBar.MouseLeave:connect(function ( )
+ dragBar.BackgroundColor3 = Color3.new(39/255,39/255,39/255)
+ end)
+
+ -- plugin name label
+ local pluginNameLabel = Instance.new("TextLabel",dragBar)
+ pluginNameLabel.Name = "BarNameLabel"
+ pluginNameLabel.Text = " " .. tostring(name)
+ pluginNameLabel.TextColor3 = Color3.new(1,1,1)
+ pluginNameLabel.TextStrokeTransparency = 0
+ pluginNameLabel.Size = UDim2.new(1,0,1,0)
+ pluginNameLabel.Font = Enum.Font.ArialBold
+ pluginNameLabel.FontSize = Enum.FontSize.Size18
+ pluginNameLabel.TextXAlignment = Enum.TextXAlignment.Left
+ pluginNameLabel.BackgroundTransparency = 1
+
+ -- close button
+ local closeButton = createMenuButton(UDim2.new(0,15,0,17),UDim2.new(1,-16,0.5,-8),"X",Enum.FontSize.Size14,"CloseButton",dragBar)
+ local closeEvent = Instance.new("BindableEvent")
+ closeEvent.Name = "CloseEvent"
+ closeEvent.Parent = closeButton
+ closeButton.MouseButton1Click:connect(function ()
+ closeEvent:Fire()
+ closeButton.BackgroundTransparency = 1
+ end)
+
+ -- help button
+ local helpButton = createMenuButton(UDim2.new(0,15,0,17),UDim2.new(1,-51,0.5,-8),"?",Enum.FontSize.Size14,"HelpButton",dragBar)
+ local helpFrame = Instance.new("Frame",dragBar)
+ helpFrame.Name = "HelpFrame"
+ helpFrame.BackgroundColor3 = Color3.new(0,0,0)
+ helpFrame.Size = UDim2.new(0,300,0,552)
+ helpFrame.Position = UDim2.new(1,5,0,0)
+ helpFrame.Active = true
+ helpFrame.BorderSizePixel = 0
+ helpFrame.Visible = false
+
+ helpButton.MouseButton1Click:connect(function( )
+ helpFrame.Visible = not helpFrame.Visible
+ if helpFrame.Visible then
+ helpButton.Selected = true
+ helpButton.BackgroundTransparency = 0
+ local screenGui = getScreenGuiAncestor(helpFrame)
+ if screenGui then
+ if helpFrame.AbsolutePosition.X + helpFrame.AbsoluteSize.X > screenGui.AbsoluteSize.X then --position on left hand side
+ helpFrame.Position = UDim2.new(0,-5 - helpFrame.AbsoluteSize.X,0,0)
+ else -- position on right hand side
+ helpFrame.Position = UDim2.new(1,5,0,0)
+ end
+ else
+ helpFrame.Position = UDim2.new(1,5,0,0)
+ end
+ else
+ helpButton.Selected = false
+ helpButton.BackgroundTransparency = 1
+ end
+ end)
+
+ local minimizeButton = createMenuButton(UDim2.new(0,16,0,17),UDim2.new(1,-34,0.5,-8),"-",Enum.FontSize.Size14,"MinimizeButton",dragBar)
+ minimizeButton.TextYAlignment = Enum.TextYAlignment.Top
+
+ local minimizeFrame = Instance.new("Frame",dragBar)
+ minimizeFrame.Name = "MinimizeFrame"
+ minimizeFrame.BackgroundColor3 = Color3.new(73/255,73/255,73/255)
+ minimizeFrame.BorderColor3 = Color3.new(0,0,0)
+ minimizeFrame.Position = UDim2.new(0,0,1,0)
+ if size then
+ minimizeFrame.Size = UDim2.new(size.X.Scale,size.X.Offset,0,50) + UDim2.new(0,20,0,0)
+ else
+ minimizeFrame.Size = UDim2.new(0,183,0,50)
+ end
+ minimizeFrame.Visible = false
+
+ local minimizeBigButton = Instance.new("TextButton",minimizeFrame)
+ minimizeBigButton.Position = UDim2.new(0.5,-50,0.5,-20)
+ minimizeBigButton.Name = "MinimizeButton"
+ minimizeBigButton.Size = UDim2.new(0,100,0,40)
+ minimizeBigButton.Style = Enum.ButtonStyle.RobloxButton
+ minimizeBigButton.Font = Enum.Font.ArialBold
+ minimizeBigButton.FontSize = Enum.FontSize.Size18
+ minimizeBigButton.TextColor3 = Color3.new(1,1,1)
+ minimizeBigButton.Text = "Show"
+
+ local separatingLine = Instance.new("Frame",dragBar)
+ separatingLine.Name = "SeparatingLine"
+ separatingLine.BackgroundColor3 = Color3.new(115/255,115/255,115/255)
+ separatingLine.BorderSizePixel = 0
+ separatingLine.Position = UDim2.new(1,-18,0.5,-7)
+ separatingLine.Size = UDim2.new(0,1,0,14)
+
+ local otherSeparatingLine = separatingLine:clone()
+ otherSeparatingLine.Position = UDim2.new(1,-35,0.5,-7)
+ otherSeparatingLine.Parent = dragBar
+
+ local widgetContainer = Instance.new("Frame",dragBar)
+ widgetContainer.Name = "WidgetContainer"
+ widgetContainer.BackgroundTransparency = 1
+ widgetContainer.Position = UDim2.new(0,0,1,0)
+ widgetContainer.BorderColor3 = Color3.new(0,0,0)
+ if not scrollable then
+ widgetContainer.BackgroundTransparency = 0
+ widgetContainer.BackgroundColor3 = Color3.new(72/255,72/255,72/255)
+ end
+
+ if size then
+ if scrollable then
+ widgetContainer.Size = size
+ else
+ widgetContainer.Size = UDim2.new(0,dragBar.AbsoluteSize.X,size.Y.Scale,size.Y.Offset)
+ end
+ else
+ if scrollable then
+ widgetContainer.Size = UDim2.new(0,163,0,400)
+ else
+ widgetContainer.Size = UDim2.new(0,dragBar.AbsoluteSize.X,0,400)
+ end
+ end
+ if position then
+ widgetContainer.Position = position + UDim2.new(0,0,0,20)
+ end
+
+ local frame,control,verticalDragger = nil
+ if scrollable then
+ --frame for widgets
+ frame,control = t.CreateTrueScrollingFrame()
+ frame.Size = UDim2.new(1, 0, 1, 0)
+ frame.BackgroundColor3 = Color3.new(72/255,72/255,72/255)
+ frame.BorderColor3 = Color3.new(0,0,0)
+ frame.Active = true
+ frame.Parent = widgetContainer
+ control.Parent = dragBar
+ control.BackgroundColor3 = Color3.new(72/255,72/255,72/255)
+ control.BorderSizePixel = 0
+ control.BackgroundTransparency = 0
+ control.Position = UDim2.new(1,-21,1,1)
+ if size then
+ control.Size = UDim2.new(0,21,size.Y.Scale,size.Y.Offset)
+ else
+ control.Size = UDim2.new(0,21,0,400)
+ end
+ control:FindFirstChild("ScrollDownButton").Position = UDim2.new(0,0,1,-20)
+
+ local fakeLine = Instance.new("Frame",control)
+ fakeLine.Name = "FakeLine"
+ fakeLine.BorderSizePixel = 0
+ fakeLine.BackgroundColor3 = Color3.new(0,0,0)
+ fakeLine.Size = UDim2.new(0,1,1,1)
+ fakeLine.Position = UDim2.new(1,0,0,0)
+
+ verticalDragger = Instance.new("TextButton",widgetContainer)
+ verticalDragger.ZIndex = 2
+ verticalDragger.AutoButtonColor = false
+ verticalDragger.Name = "VerticalDragger"
+ verticalDragger.BackgroundColor3 = Color3.new(50/255,50/255,50/255)
+ verticalDragger.BorderColor3 = Color3.new(0,0,0)
+ verticalDragger.Size = UDim2.new(1,20,0,20)
+ verticalDragger.Position = UDim2.new(0,0,1,0)
+ verticalDragger.Active = true
+ verticalDragger.Text = ""
+
+ local scrubFrame = Instance.new("Frame",verticalDragger)
+ scrubFrame.Name = "ScrubFrame"
+ scrubFrame.BackgroundColor3 = Color3.new(1,1,1)
+ scrubFrame.BorderSizePixel = 0
+ scrubFrame.Position = UDim2.new(0.5,-5,0.5,0)
+ scrubFrame.Size = UDim2.new(0,10,0,1)
+ scrubFrame.ZIndex = 5
+ local scrubTwo = scrubFrame:clone()
+ scrubTwo.Position = UDim2.new(0.5,-5,0.5,-2)
+ scrubTwo.Parent = verticalDragger
+ local scrubThree = scrubFrame:clone()
+ scrubThree.Position = UDim2.new(0.5,-5,0.5,2)
+ scrubThree.Parent = verticalDragger
+
+ local areaSoak = Instance.new("TextButton",getScreenGuiAncestor(parent))
+ areaSoak.Name = "AreaSoak"
+ areaSoak.Size = UDim2.new(1,0,1,0)
+ areaSoak.BackgroundTransparency = 1
+ areaSoak.BorderSizePixel = 0
+ areaSoak.Text = ""
+ areaSoak.ZIndex = 10
+ areaSoak.Visible = false
+ areaSoak.Active = true
+
+ local draggingVertical = false
+ local startYPos = nil
+ verticalDragger.MouseEnter:connect(function ()
+ verticalDragger.BackgroundColor3 = Color3.new(60/255,60/255,60/255)
+ end)
+ verticalDragger.MouseLeave:connect(function ()
+ verticalDragger.BackgroundColor3 = Color3.new(50/255,50/255,50/255)
+ end)
+ verticalDragger.MouseButton1Down:connect(function(x,y)
+ draggingVertical = true
+ areaSoak.Visible = true
+ startYPos = y
+ end)
+ areaSoak.MouseButton1Up:connect(function ( )
+ draggingVertical = false
+ areaSoak.Visible = false
+ end)
+ areaSoak.MouseMoved:connect(function(x,y)
+ if not draggingVertical then return end
+
+ local yDelta = y - startYPos
+ if not control.ScrollDownButton.Visible and yDelta > 0 then
+ return
+ end
+
+ if (widgetContainer.Size.Y.Offset + yDelta) < 150 then
+ widgetContainer.Size = UDim2.new(widgetContainer.Size.X.Scale, widgetContainer.Size.X.Offset,widgetContainer.Size.Y.Scale,150)
+ control.Size = UDim2.new (0,21,0,150)
+ return
+ end
+
+ startYPos = y
+
+ if widgetContainer.Size.Y.Offset + yDelta >= 0 then
+ widgetContainer.Size = UDim2.new(widgetContainer.Size.X.Scale, widgetContainer.Size.X.Offset,widgetContainer.Size.Y.Scale,widgetContainer.Size.Y.Offset + yDelta)
+ control.Size = UDim2.new(0,21,0,control.Size.Y.Offset + yDelta )
+ end
+ end)
+ end
+
+ local function switchMinimize()
+ minimizeFrame.Visible = not minimizeFrame.Visible
+ if scrollable then
+ frame.Visible = not frame.Visible
+ verticalDragger.Visible = not verticalDragger.Visible
+ control.Visible = not control.Visible
+ else
+ widgetContainer.Visible = not widgetContainer.Visible
+ end
+
+ if minimizeFrame.Visible then
+ minimizeButton.Text = "+"
+ else
+ minimizeButton.Text = "-"
+ end
+ end
+
+ minimizeBigButton.MouseButton1Click:connect(function ( )
+ switchMinimize()
+ end)
+
+ minimizeButton.MouseButton1Click:connect(function( )
+ switchMinimize()
+ end)
+
+ if scrollable then
+ return dragBar, frame, helpFrame, closeEvent
+ else
+ return dragBar, widgetContainer, helpFrame, closeEvent
+ end
+end
+
+t.Help =
+ function(funcNameOrFunc)
+ --input argument can be a string or a function. Should return a description (of arguments and expected side effects)
+ if funcNameOrFunc == "CreatePropertyDropDownMenu" or funcNameOrFunc == t.CreatePropertyDropDownMenu then
+ return "Function CreatePropertyDropDownMenu. " ..
+ "Arguments: (instance, propertyName, enumType). " ..
+ "Side effect: returns a container with a drop-down-box that is linked to the 'property' field of 'instance' which is of type 'enumType'"
+ end
+ if funcNameOrFunc == "CreateDropDownMenu" or funcNameOrFunc == t.CreateDropDownMenu then
+ return "Function CreateDropDownMenu. " ..
+ "Arguments: (items, onItemSelected). " ..
+ "Side effect: Returns 2 results, a container to the gui object and a 'updateSelection' function for external updating. The container is a drop-down-box created around a list of items"
+ end
+ if funcNameOrFunc == "CreateMessageDialog" or funcNameOrFunc == t.CreateMessageDialog then
+ return "Function CreateMessageDialog. " ..
+ "Arguments: (title, message, buttons). " ..
+ "Side effect: Returns a gui object of a message box with 'title' and 'message' as passed in. 'buttons' input is an array of Tables contains a 'Text' and 'Function' field for the text/callback of each button"
+ end
+ if funcNameOrFunc == "CreateStyledMessageDialog" or funcNameOrFunc == t.CreateStyledMessageDialog then
+ return "Function CreateStyledMessageDialog. " ..
+ "Arguments: (title, message, style, buttons). " ..
+ "Side effect: Returns a gui object of a message box with 'title' and 'message' as passed in. 'buttons' input is an array of Tables contains a 'Text' and 'Function' field for the text/callback of each button, 'style' is a string, either Error, Notify or Confirm"
+ end
+ if funcNameOrFunc == "GetFontHeight" or funcNameOrFunc == t.GetFontHeight then
+ return "Function GetFontHeight. " ..
+ "Arguments: (font, fontSize). " ..
+ "Side effect: returns the size in pixels of the given font + fontSize"
+ end
+ if funcNameOrFunc == "LayoutGuiObjects" or funcNameOrFunc == t.LayoutGuiObjects then
+
+ end
+ if funcNameOrFunc == "CreateScrollingFrame" or funcNameOrFunc == t.CreateScrollingFrame then
+ return "Function CreateScrollingFrame. " ..
+ "Arguments: (orderList, style) " ..
+ "Side effect: returns 4 objects, (scrollFrame, scrollUpButton, scrollDownButton, recalculateFunction). 'scrollFrame' can be filled with GuiObjects. It will lay them out and allow scrollUpButton/scrollDownButton to interact with them. Orderlist is optional (and specifies the order to layout the children. Without orderlist, it uses the children order. style is also optional, and allows for a 'grid' styling if style is passed 'grid' as a string. recalculateFunction can be called when a relayout is needed (when orderList changes)"
+ end
+ if funcNameOrFunc == "CreateTrueScrollingFrame" or funcNameOrFunc == t.CreateTrueScrollingFrame then
+ return "Function CreateTrueScrollingFrame. " ..
+ "Arguments: (nil) " ..
+ "Side effect: returns 2 objects, (scrollFrame, controlFrame). 'scrollFrame' can be filled with GuiObjects, and they will be clipped if not inside the frame's bounds. controlFrame has children scrollup and scrolldown, as well as a slider. controlFrame can be parented to any guiobject and it will readjust itself to fit."
+ end
+ if funcNameOrFunc == "AutoTruncateTextObject" or funcNameOrFunc == t.AutoTruncateTextObject then
+ return "Function AutoTruncateTextObject. " ..
+ "Arguments: (textLabel) " ..
+ "Side effect: returns 2 objects, (textLabel, changeText). The 'textLabel' input is modified to automatically truncate text (with ellipsis), if it gets too small to fit. 'changeText' is a function that can be used to change the text, it takes 1 string as an argument"
+ end
+ if funcNameOrFunc == "CreateSlider" or funcNameOrFunc == t.CreateSlider then
+ return "Function CreateSlider. " ..
+ "Arguments: (steps, width, position) " ..
+ "Side effect: returns 2 objects, (sliderGui, sliderPosition). The 'steps' argument specifies how many different positions the slider can hold along the bar. 'width' specifies in pixels how wide the bar should be (modifiable afterwards if desired). 'position' argument should be a UDim2 for slider positioning. 'sliderPosition' is an IntValue whose current .Value specifies the specific step the slider is currently on."
+ end
+ if funcNameOrFunc == "CreateLoadingFrame" or funcNameOrFunc == t.CreateLoadingFrame then
+ return "Function CreateLoadingFrame. " ..
+ "Arguments: (name, size, position) " ..
+ "Side effect: Creates a gui that can be manipulated to show progress for a particular action. Name appears above the loading bar, and size and position are udim2 values (both size and position are optional arguments). Returns 3 arguments, the first being the gui created. The second being updateLoadingGuiPercent, which is a bindable function. This function takes one argument (two optionally), which should be a number between 0 and 1, representing the percentage the loading gui should be at. The second argument to this function is a boolean value that if set to true will tween the current percentage value to the new percentage value, therefore our third argument is how long this tween should take. Our third returned argument is a BindableEvent, that when fired means that someone clicked the cancel button on the dialog."
+ end
+ if funcNameOrFunc == "CreateTerrainMaterialSelector" or funcNameOrFunc == t.CreateTerrainMaterialSelector then
+ return "Function CreateTerrainMaterialSelector. " ..
+ "Arguments: (size, position) " ..
+ "Side effect: Size and position are UDim2 values that specifies the selector's size and position. Both size and position are optional arguments. This method returns 3 objects (terrainSelectorGui, terrainSelected, forceTerrainSelection). terrainSelectorGui is just the gui object that we generate with this function, parent it as you like. TerrainSelected is a BindableEvent that is fired whenever a new terrain type is selected in the gui. ForceTerrainSelection is a function that takes an argument of Enum.CellMaterial and will force the gui to show that material as currently selected."
+ end
+ end
+
+return t
\ No newline at end of file
diff --git a/app/files/CoreGui/45374389 b/app/files/CoreGui/45374389
new file mode 100644
index 0000000..51592b8
--- /dev/null
+++ b/app/files/CoreGui/45374389
@@ -0,0 +1,23 @@
+local t = {}
+
+t.Foo =
+ function()
+ print("foo")
+ end
+
+t.Bar =
+ function()
+ print("bar")
+ end
+
+t.Help =
+ function(funcNameOrFunc)
+ --input argument can be a string or a function. Should return a description (of arguments and expected side effects)
+ if funcNameOrFunc == "Foo" or funcNameOrFunc == t.Foo then
+ return "Function Foo. Arguments: None. Side effect: prints foo"
+ elseif funcNameOrFunc == "Bar" or funcNameOrFunc == t.Bar then
+ return "Function Bar. Arguments: None. Side effect: prints bar"
+ end
+ end
+
+return t
\ No newline at end of file
diff --git a/app/files/CoreGui/46295863 b/app/files/CoreGui/46295863
new file mode 100644
index 0000000..487c77f
--- /dev/null
+++ b/app/files/CoreGui/46295863
@@ -0,0 +1,2153 @@
+local function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+
+local function waitForProperty(instance, property)
+ while not instance[property] do
+ instance.Changed:wait()
+ end
+end
+
+--Include
+local Create = assert(LoadLibrary("RbxUtility")).Create
+
+--FFlags
+local FFlagExists, FFlagValue = pcall(function () return settings():GetFFlag("LoggingConsoleEnabled") end)
+local FFlagLogginConsoleEnabled = FFlagExists and FFlagValue
+
+-- A Few Script Globals
+local gui
+if script.Parent:FindFirstChild("ControlFrame") then
+ gui = script.Parent:FindFirstChild("ControlFrame")
+else
+ gui = script.Parent
+end
+
+local helpButton = nil
+local updateCameraDropDownSelection = nil
+local updateVideoCaptureDropDownSelection = nil
+local tweenTime = 0.2
+
+local mouseLockLookScreenUrl = "http://www.syntax.eco/asset?id=54071825"
+local classicLookScreenUrl = "http://www.syntax.eco/Asset?id=45915798"
+
+local hasGraphicsSlider = (game:GetService("CoreGui").Version >= 5)
+local GraphicsQualityLevels = 10 -- how many levels we allow on graphics slider
+local recordingVideo = false
+
+local currentMenuSelection = nil
+local lastMenuSelection = {}
+
+local defaultPosition = UDim2.new(0,0,0,0)
+local newGuiPlaces = {0,41324860}
+
+local centerDialogs = {}
+local mainShield = nil
+
+local inStudioMode = UserSettings().GameSettings:InStudioMode()
+
+local macClient = false
+local success, isMac = pcall(function() return not game.GuiService.IsWindows end)
+macClient = success and isMac
+
+local function Color3I(r,g,b)
+ return Color3.new(r/255,g/255,b/255)
+end
+
+local function robloxLock(instance)
+ instance.RobloxLocked = true
+ children = instance:GetChildren()
+ if children then
+ for i, child in ipairs(children) do
+ robloxLock(child)
+ end
+ end
+end
+
+function resumeGameFunction(shield)
+ shield.Settings:TweenPosition(UDim2.new(0.5, -262,-0.5, -200),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ delay(tweenTime,function()
+ shield.Visible = false
+ for i = 1, #centerDialogs do
+ centerDialogs[i].Visible = false
+ game.GuiService:RemoveCenterDialog(centerDialogs[i])
+ end
+ game.GuiService:RemoveCenterDialog(shield)
+ settingsButton.Active = true
+ currentMenuSelection = nil
+ lastMenuSelection = {}
+ end)
+end
+
+function goToMenu(container,menuName, moveDirection,size,position)
+ if type(menuName) ~= "string" then return end
+
+ table.insert(lastMenuSelection,currentMenuSelection)
+ if menuName == "GameMainMenu" then
+ lastMenuSelection = {}
+ end
+
+ local containerChildren = container:GetChildren()
+ local selectedMenu = false
+ for i = 1, #containerChildren do
+ if containerChildren[i].Name == menuName then
+ containerChildren[i].Visible = true
+ currentMenuSelection = {container = container,name = menuName, direction = moveDirection, lastSize = size}
+ selectedMenu = true
+ if size and position then
+ containerChildren[i]:TweenSizeAndPosition(size,position,Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ elseif size then
+ containerChildren[i]:TweenSizeAndPosition(size,UDim2.new(0.5,-size.X.Offset/2,0.5,-size.Y.Offset/2),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ else
+ containerChildren[i]:TweenPosition(UDim2.new(0,0,0,0),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ end
+ else
+ if moveDirection == "left" then
+ containerChildren[i]:TweenPosition(UDim2.new(-1,-525,0,0),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ elseif moveDirection == "right" then
+ containerChildren[i]:TweenPosition(UDim2.new(1,525,0,0),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ elseif moveDirection == "up" then
+ containerChildren[i]:TweenPosition(UDim2.new(0,0,-1,-400),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ elseif moveDirection == "down" then
+ containerChildren[i]:TweenPosition(UDim2.new(0,0,1,400),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ end
+ delay(tweenTime,function()
+ containerChildren[i].Visible = false
+ end)
+ end
+ end
+end
+
+function resetLocalCharacter()
+ local player = game.Players.LocalPlayer
+ if player then
+ if player.Character and player.Character:FindFirstChild("Humanoid") then
+ player.Character.Humanoid.Health = 0
+ end
+ end
+end
+
+local function createTextButton(text,style,fontSize,buttonSize,buttonPosition)
+ local newTextButton = Instance.new("TextButton")
+ newTextButton.Font = Enum.Font.Arial
+ newTextButton.FontSize = fontSize
+ newTextButton.Size = buttonSize
+ newTextButton.Position = buttonPosition
+ newTextButton.Style = style
+ newTextButton.TextColor3 = Color3.new(1,1,1)
+ newTextButton.Text = text
+ return newTextButton
+end
+
+local function CreateTextButtons(frame, buttons, yPos, ySize)
+ if #buttons < 1 then
+ error("Must have more than one button")
+ end
+
+ local buttonNum = 1
+ local buttonObjs = {}
+
+ local function toggleSelection(button)
+ for i, obj in ipairs(buttonObjs) do
+ if obj == button then
+ obj.Style = Enum.ButtonStyle.RobloxButtonDefault
+ else
+ obj.Style = Enum.ButtonStyle.RobloxButton
+ end
+ end
+ end
+
+ for i, obj in ipairs(buttons) do
+ local button = Instance.new("TextButton")
+ button.Name = "Button" .. buttonNum
+ button.Font = Enum.Font.Arial
+ button.FontSize = Enum.FontSize.Size18
+ button.AutoButtonColor = true
+ button.Style = Enum.ButtonStyle.RobloxButton
+ button.Text = obj.Text
+ button.TextColor3 = Color3.new(1,1,1)
+ button.MouseButton1Click:connect(function() toggleSelection(button) obj.Function() end)
+ button.Parent = frame
+ button.ZIndex = 4
+ buttonObjs[buttonNum] = button
+
+ buttonNum = buttonNum + 1
+ end
+
+ toggleSelection(buttonObjs[1])
+
+ local numButtons = buttonNum-1
+
+ if numButtons == 1 then
+ frame.Button1.Position = UDim2.new(0.35, 0, yPos.Scale, yPos.Offset)
+ frame.Button1.Size = UDim2.new(.4,0,ySize.Scale, ySize.Offset)
+ elseif numButtons == 2 then
+ frame.Button1.Position = UDim2.new(0.1, 0, yPos.Scale, yPos.Offset)
+ frame.Button1.Size = UDim2.new(.35,0, ySize.Scale, ySize.Offset)
+
+ frame.Button2.Position = UDim2.new(0.55, 0, yPos.Scale, yPos.Offset)
+ frame.Button2.Size = UDim2.new(.35,0, ySize.Scale, ySize.Offset)
+ elseif numButtons >= 3 then
+ local spacing = .1 / numButtons
+ local buttonSize = .9 / numButtons
+
+ buttonNum = 1
+ while buttonNum <= numButtons do
+ buttonObjs[buttonNum].Position = UDim2.new(spacing*buttonNum + (buttonNum-1) * buttonSize, 0, yPos.Scale, yPos.Offset)
+ buttonObjs[buttonNum].Size = UDim2.new(buttonSize, 0, ySize.Scale, ySize.Offset)
+ buttonNum = buttonNum + 1
+ end
+ end
+end
+
+function setRecordGui(recording, stopRecordButton, recordVideoButton)
+ if recording then
+ stopRecordButton.Visible = true
+ recordVideoButton.Text = "Stop Recording"
+ else
+ stopRecordButton.Visible = false
+ recordVideoButton.Text = "Record Video"
+ end
+end
+
+function recordVideoClick(recordVideoButton, stopRecordButton)
+ recordingVideo = not recordingVideo
+ setRecordGui(recordingVideo, stopRecordButton, recordVideoButton)
+end
+
+local currentlyToggling = false;
+local DevConsoleToggle = nil;
+
+delay(0, function()
+ DevConsoleToggle = gui:WaitForChild("ToggleDevConsole")
+end)
+
+function toggleDeveloperConsole()
+ if not DevConsoleToggle then
+ return
+ end
+
+ DevConsoleToggle:Invoke()
+end
+
+function backToGame(buttonClicked, shield, settingsButton)
+ buttonClicked.Parent.Parent.Parent.Parent.Visible = false
+ shield.Visible = false
+ for i = 1, #centerDialogs do
+ game.GuiService:RemoveCenterDialog(centerDialogs[i])
+ centerDialogs[i].Visible = false
+ end
+ centerDialogs = {}
+ game.GuiService:RemoveCenterDialog(shield)
+ settingsButton.Active = true
+end
+
+function setDisabledState(guiObject)
+ if not guiObject then return end
+
+ if guiObject:IsA("TextLabel") then
+ guiObject.TextTransparency = 0.9
+ elseif guiObject:IsA("TextButton") then
+ guiObject.TextTransparency = 0.9
+ guiObject.Active = false
+ else
+ if guiObject["ClassName"] then
+ print("setDisabledState() got object of unsupported type. object type is ",guiObject.ClassName)
+ end
+ end
+end
+
+local function createHelpDialog(baseZIndex)
+
+ if helpButton == nil then
+ if gui:FindFirstChild("TopLeftControl") and gui.TopLeftControl:FindFirstChild("Help") then
+ helpButton = gui.TopLeftControl.Help
+ elseif gui:FindFirstChild("BottomRightControl") and gui.BottomRightControl:FindFirstChild("Help") then
+ helpButton = gui.BottomRightControl.Help
+ end
+ end
+
+ local shield = Instance.new("Frame")
+ shield.Name = "HelpDialogShield"
+ shield.Active = true
+ shield.Visible = false
+ shield.Size = UDim2.new(1,0,1,0)
+ shield.BackgroundColor3 = Color3I(51,51,51)
+ shield.BorderColor3 = Color3I(27,42,53)
+ shield.BackgroundTransparency = 0.4
+ shield.ZIndex = baseZIndex + 2
+
+ local helpDialog = Instance.new("Frame")
+ helpDialog.Name = "HelpDialog"
+ helpDialog.Style = Enum.FrameStyle.RobloxRound
+ helpDialog.Position = UDim2.new(.2, 0, .2, 0)
+ helpDialog.Size = UDim2.new(0.6, 0, 0.6, 0)
+ helpDialog.Active = true
+ helpDialog.Parent = shield
+ helpDialog.ZIndex = baseZIndex + 2
+
+ local titleLabel = Instance.new("TextLabel")
+ titleLabel.Name = "Title"
+ titleLabel.Text = "Keyboard & Mouse Controls"
+ titleLabel.Font = Enum.Font.ArialBold
+ titleLabel.FontSize = Enum.FontSize.Size36
+ titleLabel.Position = UDim2.new(0, 0, 0.025, 0)
+ titleLabel.Size = UDim2.new(1, 0, 0, 40)
+ titleLabel.TextColor3 = Color3.new(1,1,1)
+ titleLabel.BackgroundTransparency = 1
+ titleLabel.Parent = helpDialog
+ titleLabel.ZIndex = baseZIndex + 2
+
+ local buttonRow = Instance.new("Frame")
+ buttonRow.Name = "Buttons"
+ buttonRow.Position = UDim2.new(0.1, 0, .07, 40)
+ buttonRow.Size = UDim2.new(0.8, 0, 0, 45)
+ buttonRow.BackgroundTransparency = 1
+ buttonRow.Parent = helpDialog
+ buttonRow.ZIndex = baseZIndex + 2
+
+ local imageFrame = Instance.new("Frame")
+ imageFrame.Name = "ImageFrame"
+ imageFrame.Position = UDim2.new(0.05, 0, 0.075, 80)
+ imageFrame.Size = UDim2.new(0.9, 0, .9, -120)
+ imageFrame.BackgroundTransparency = 1
+ imageFrame.Parent = helpDialog
+ imageFrame.ZIndex = baseZIndex + 2
+
+ local layoutFrame = Instance.new("Frame")
+ layoutFrame.Name = "LayoutFrame"
+ layoutFrame.Position = UDim2.new(0.5, 0, 0, 0)
+ layoutFrame.Size = UDim2.new(1.5, 0, 1, 0)
+ layoutFrame.BackgroundTransparency = 1
+ layoutFrame.SizeConstraint = Enum.SizeConstraint.RelativeYY
+ layoutFrame.Parent = imageFrame
+ layoutFrame.ZIndex = baseZIndex + 2
+
+ local image = Instance.new("ImageLabel")
+ image.Name = "Image"
+ if UserSettings().GameSettings.ControlMode == Enum.ControlMode["Mouse Lock Switch"] then
+ image.Image = mouseLockLookScreenUrl
+ else
+ image.Image = classicLookScreenUrl
+ end
+ image.Position = UDim2.new(-0.5, 0, 0, 0)
+ image.Size = UDim2.new(1, 0, 1, 0)
+ image.BackgroundTransparency = 1
+ image.Parent = layoutFrame
+ image.ZIndex = baseZIndex + 2
+
+ local buttons = {}
+ buttons[1] = {}
+ buttons[1].Text = "Look"
+ buttons[1].Function = function()
+ if UserSettings().GameSettings.ControlMode == Enum.ControlMode["Mouse Lock Switch"] then
+ image.Image = mouseLockLookScreenUrl
+ else
+ image.Image = classicLookScreenUrl
+ end
+ end
+ buttons[2] = {}
+ buttons[2].Text = "Move"
+ buttons[2].Function = function()
+ image.Image = "http://www.syntax.eco/Asset?id=45915811"
+ end
+ buttons[3] = {}
+ buttons[3].Text = "Gear"
+ buttons[3].Function = function()
+ image.Image = "http://www.syntax.eco/Asset?id=45917596"
+ end
+ buttons[4] = {}
+ buttons[4].Text = "Zoom"
+ buttons[4].Function = function()
+ image.Image = "http://www.syntax.eco/Asset?id=45915825"
+ end
+
+ CreateTextButtons(buttonRow, buttons, UDim.new(0, 0), UDim.new(1,0))
+
+ if FFlagLogginConsoleEnabled then
+ local devConsoleButton = Create'TextButton'{
+ Name = "DeveloperConsoleButton";
+ Text = "Log";
+ Size = UDim2.new(0,60,0,30);
+ Style = Enum.ButtonStyle.RobloxButton;
+ Position = UDim2.new(1,-65,1,-35);
+ Font = Enum.Font.Arial;
+ FontSize = Enum.FontSize.Size18;
+ TextColor3 = Color3.new(1,1,1);
+ ZIndex = baseZIndex + 4;
+ BackgroundTransparency = 1;
+ Parent = helpDialog;
+ }
+
+ Create'TextLabel'{
+ Name = "DeveloperConsoleButton";
+ Text = "F9";
+ Size = UDim2.new(0,14,0,14);
+ Position = UDim2.new(1,-6,0, 0);
+ Font = Enum.Font.Arial;
+ FontSize = Enum.FontSize.Size12;
+ TextColor3 = Color3.new(0,1,0);
+ ZIndex = baseZIndex + 4;
+ BackgroundTransparency = 1;
+ Parent = devConsoleButton;
+ }
+
+ waitForProperty(game.Players, "LocalPlayer")
+ game.Players.LocalPlayer:GetMouse().KeyDown:connect(function(key)
+ if string.byte(key) == 34 then --F9
+ toggleDeveloperConsole()
+ end
+ end)
+
+ devConsoleButton.MouseButton1Click:connect(function()
+ toggleDeveloperConsole()
+ shield.Visible = false
+ game.GuiService:RemoveCenterDialog(shield)
+ end)
+ end
+
+
+
+ -- set up listeners for type of mouse mode, but keep constructing gui at same time
+ delay(0, function()
+ waitForChild(gui,"UserSettingsShield")
+ waitForChild(gui.UserSettingsShield,"Settings")
+ waitForChild(gui.UserSettingsShield.Settings,"SettingsStyle")
+ waitForChild(gui.UserSettingsShield.Settings.SettingsStyle, "GameSettingsMenu")
+ waitForChild(gui.UserSettingsShield.Settings.SettingsStyle.GameSettingsMenu, "CameraField")
+ waitForChild(gui.UserSettingsShield.Settings.SettingsStyle.GameSettingsMenu.CameraField, "DropDownMenuButton")
+ gui.UserSettingsShield.Settings.SettingsStyle.GameSettingsMenu.CameraField.DropDownMenuButton.Changed:connect(function(prop)
+ if prop ~= "Text" then return end
+ if buttonRow.Button1.Style == Enum.ButtonStyle.RobloxButtonDefault then -- only change if this is the currently selected panel
+ if gui.UserSettingsShield.Settings.SettingsStyle.GameSettingsMenu.CameraField.DropDownMenuButton.Text == "Classic" then
+ image.Image = classicLookScreenUrl
+ else
+ image.Image = mouseLockLookScreenUrl
+ end
+ end
+ end)
+ end)
+
+
+ local okBtn = Instance.new("TextButton")
+ okBtn.Name = "OkBtn"
+ okBtn.Text = "OK"
+ okBtn.Modal = true
+ okBtn.Size = UDim2.new(0.3, 0, 0, 45)
+ okBtn.Position = UDim2.new(0.35, 0, .975, -50)
+ okBtn.Font = Enum.Font.Arial
+ okBtn.FontSize = Enum.FontSize.Size18
+ okBtn.BackgroundTransparency = 1
+ okBtn.TextColor3 = Color3.new(1,1,1)
+ okBtn.Style = Enum.ButtonStyle.RobloxButtonDefault
+ okBtn.ZIndex = baseZIndex + 2
+ okBtn.MouseButton1Click:connect(
+ function()
+ shield.Visible = false
+ game.GuiService:RemoveCenterDialog(shield)
+ end)
+ okBtn.Parent = helpDialog
+
+ robloxLock(shield)
+ return shield
+end
+
+local function createLeaveConfirmationMenu(baseZIndex,shield)
+ local frame = Instance.new("Frame")
+ frame.Name = "LeaveConfirmationMenu"
+ frame.BackgroundTransparency = 1
+ frame.Size = UDim2.new(1,0,1,0)
+ frame.Position = UDim2.new(0,0,2,400)
+ frame.ZIndex = baseZIndex + 4
+
+ local yesButton = createTextButton("Leave",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size24,UDim2.new(0,128,0,50),UDim2.new(0,313,0.8,0))
+ yesButton.Name = "YesButton"
+ yesButton.ZIndex = baseZIndex + 4
+ yesButton.Parent = frame
+ yesButton.Modal = true
+ yesButton:SetVerb("Exit")
+
+ local noButton = createTextButton("Stay",Enum.ButtonStyle.RobloxButtonDefault,Enum.FontSize.Size24,UDim2.new(0,128,0,50),UDim2.new(0,90,0.8,0))
+ noButton.Name = "NoButton"
+ noButton.Parent = frame
+ noButton.ZIndex = baseZIndex + 4
+ noButton.MouseButton1Click:connect(function()
+ goToMenu(shield.Settings.SettingsStyle,"GameMainMenu","down",UDim2.new(0,525,0,430))
+ shield.Settings:TweenSize(UDim2.new(0,525,0,430),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ end)
+
+ local leaveText = Instance.new("TextLabel")
+ leaveText.Name = "LeaveText"
+ leaveText.Text = "Leave this game?"
+ leaveText.Size = UDim2.new(1,0,0.8,0)
+ leaveText.TextWrap = true
+ leaveText.TextColor3 = Color3.new(1,1,1)
+ leaveText.Font = Enum.Font.ArialBold
+ leaveText.FontSize = Enum.FontSize.Size36
+ leaveText.BackgroundTransparency = 1
+ leaveText.ZIndex = baseZIndex + 4
+ leaveText.Parent = frame
+
+ return frame
+end
+
+local function createResetConfirmationMenu(baseZIndex,shield)
+ local frame = Instance.new("Frame")
+ frame.Name = "ResetConfirmationMenu"
+ frame.BackgroundTransparency = 1
+ frame.Size = UDim2.new(1,0,1,0)
+ frame.Position = UDim2.new(0,0,2,400)
+ frame.ZIndex = baseZIndex + 4
+
+ local yesButton = createTextButton("Reset",Enum.ButtonStyle.RobloxButtonDefault,Enum.FontSize.Size24,UDim2.new(0,128,0,50),UDim2.new(0,313,0,299))
+ yesButton.Name = "YesButton"
+ yesButton.ZIndex = baseZIndex + 4
+ yesButton.Parent = frame
+ yesButton.Modal = true
+ yesButton.MouseButton1Click:connect(function()
+ resumeGameFunction(shield)
+ resetLocalCharacter()
+ end)
+
+ local noButton = createTextButton("Cancel",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size24,UDim2.new(0,128,0,50),UDim2.new(0,90,0,299))
+ noButton.Name = "NoButton"
+ noButton.Parent = frame
+ noButton.ZIndex = baseZIndex + 4
+ noButton.MouseButton1Click:connect(function()
+ goToMenu(shield.Settings.SettingsStyle,"GameMainMenu","down",UDim2.new(0,525,0,430))
+ shield.Settings:TweenSize(UDim2.new(0,525,0,430),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ end)
+
+ local resetCharacterText = Instance.new("TextLabel")
+ resetCharacterText.Name = "ResetCharacterText"
+ resetCharacterText.Text = "Are you sure you want to reset your character?"
+ resetCharacterText.Size = UDim2.new(1,0,0.8,0)
+ resetCharacterText.TextWrap = true
+ resetCharacterText.TextColor3 = Color3.new(1,1,1)
+ resetCharacterText.Font = Enum.Font.ArialBold
+ resetCharacterText.FontSize = Enum.FontSize.Size36
+ resetCharacterText.BackgroundTransparency = 1
+ resetCharacterText.ZIndex = baseZIndex + 4
+ resetCharacterText.Parent = frame
+
+ local fineResetCharacterText = resetCharacterText:Clone()
+ fineResetCharacterText.Name = "FineResetCharacterText"
+ fineResetCharacterText.Text = "You will be put back on a spawn point"
+ fineResetCharacterText.Size = UDim2.new(0,303,0,18)
+ fineResetCharacterText.Position = UDim2.new(0, 109, 0, 215)
+ fineResetCharacterText.FontSize = Enum.FontSize.Size18
+ fineResetCharacterText.Parent = frame
+
+ return frame
+end
+
+local function createGameMainMenu(baseZIndex, shield)
+ local gameMainMenuFrame = Instance.new("Frame")
+ gameMainMenuFrame.Name = "GameMainMenu"
+ gameMainMenuFrame.BackgroundTransparency = 1
+ gameMainMenuFrame.Size = UDim2.new(1,0,1,0)
+ gameMainMenuFrame.ZIndex = baseZIndex + 4
+ gameMainMenuFrame.Parent = settingsFrame
+
+ -- GameMainMenu Children
+
+ local gameMainMenuTitle = Instance.new("TextLabel")
+ gameMainMenuTitle.Name = "Title"
+ gameMainMenuTitle.Text = "Game Menu"
+ gameMainMenuTitle.BackgroundTransparency = 1
+ gameMainMenuTitle.TextStrokeTransparency = 0
+ gameMainMenuTitle.Font = Enum.Font.ArialBold
+ gameMainMenuTitle.FontSize = Enum.FontSize.Size36
+ gameMainMenuTitle.Size = UDim2.new(1,0,0,36)
+ gameMainMenuTitle.Position = UDim2.new(0,0,0,4)
+ gameMainMenuTitle.TextColor3 = Color3.new(1,1,1)
+ gameMainMenuTitle.ZIndex = baseZIndex + 4
+ gameMainMenuTitle.Parent = gameMainMenuFrame
+
+ local robloxHelpButton = createTextButton("Help",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size18,UDim2.new(0,164,0,50),UDim2.new(0,82,0,256))
+ robloxHelpButton.Name = "HelpButton"
+ robloxHelpButton.ZIndex = baseZIndex + 4
+ robloxHelpButton.Parent = gameMainMenuFrame
+ helpButton = robloxHelpButton
+
+ local helpDialog = createHelpDialog(baseZIndex)
+ helpDialog.Parent = gui
+
+ helpButton.MouseButton1Click:connect(
+ function()
+ table.insert(centerDialogs,helpDialog)
+ game.GuiService:AddCenterDialog(helpDialog, Enum.CenterDialogType.ModalDialog,
+ --ShowFunction
+ function()
+ helpDialog.Visible = true
+ mainShield.Visible = false
+ end,
+ --HideFunction
+ function()
+ helpDialog.Visible = false
+ end)
+ end)
+ helpButton.Active = true
+
+ local helpShortcut = Instance.new("TextLabel")
+ helpShortcut.Name = "HelpShortcutText"
+ helpShortcut.Text = "F1"
+ helpShortcut.Visible = false
+ helpShortcut.BackgroundTransparency = 1
+ helpShortcut.Font = Enum.Font.Arial
+ helpShortcut.FontSize = Enum.FontSize.Size12
+ helpShortcut.Position = UDim2.new(0,85,0,0)
+ helpShortcut.Size = UDim2.new(0,30,0,30)
+ helpShortcut.TextColor3 = Color3.new(0,1,0)
+ helpShortcut.ZIndex = baseZIndex + 4
+ helpShortcut.Parent = robloxHelpButton
+
+ local screenshotButton = createTextButton("Screenshot",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size18,UDim2.new(0,168,0,50),UDim2.new(0,254,0,256))
+ screenshotButton.Name = "ScreenshotButton"
+ screenshotButton.ZIndex = baseZIndex + 4
+ screenshotButton.Parent = gameMainMenuFrame
+ screenshotButton.Visible = not macClient
+ screenshotButton:SetVerb("Screenshot")
+
+ local screenshotShortcut = helpShortcut:clone()
+ screenshotShortcut.Name = "ScreenshotShortcutText"
+ screenshotShortcut.Text = "PrintSc"
+ screenshotShortcut.Position = UDim2.new(0,118,0,0)
+ screenshotShortcut.Visible = true
+ screenshotShortcut.Parent = screenshotButton
+
+
+ local recordVideoButton = createTextButton("Record Video",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size18,UDim2.new(0,168,0,50),UDim2.new(0,254,0,306))
+ recordVideoButton.Name = "RecordVideoButton"
+ recordVideoButton.ZIndex = baseZIndex + 4
+ recordVideoButton.Parent = gameMainMenuFrame
+ recordVideoButton.Visible = not macClient
+ recordVideoButton:SetVerb("RecordToggle")
+
+ local recordVideoShortcut = helpShortcut:clone()
+ recordVideoShortcut.Visible = hasGraphicsSlider
+ recordVideoShortcut.Name = "RecordVideoShortcutText"
+ recordVideoShortcut.Text = "F12"
+ recordVideoShortcut.Position = UDim2.new(0,120,0,0)
+ recordVideoShortcut.Parent = recordVideoButton
+
+ local stopRecordButton = Instance.new("ImageButton")
+ stopRecordButton.Name = "StopRecordButton"
+ stopRecordButton.BackgroundTransparency = 1
+ stopRecordButton.Image = "rbxasset://textures/ui/RecordStop.png"
+ stopRecordButton.Size = UDim2.new(0,59,0,27)
+ stopRecordButton:SetVerb("RecordToggle")
+
+ stopRecordButton.MouseButton1Click:connect(function() recordVideoClick(recordVideoButton, stopRecordButton) end)
+ stopRecordButton.Visible = false
+ stopRecordButton.Parent = gui
+
+ local reportAbuseButton = createTextButton("Report Abuse",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size18,UDim2.new(0,164,0,50),UDim2.new(0,82,0,306))
+ reportAbuseButton.Name = "ReportAbuseButton"
+ reportAbuseButton.ZIndex = baseZIndex + 4
+ reportAbuseButton.Parent = gameMainMenuFrame
+
+ local leaveGameButton = createTextButton("Leave Game",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size24,UDim2.new(0,340,0,50),UDim2.new(0,82,0,358))
+ leaveGameButton.Name = "LeaveGameButton"
+ leaveGameButton.ZIndex = baseZIndex + 4
+ leaveGameButton.Parent = gameMainMenuFrame
+
+ local resumeGameButton = createTextButton("Resume Game",Enum.ButtonStyle.RobloxButtonDefault,Enum.FontSize.Size24,UDim2.new(0,340,0,50),UDim2.new(0,82,0,54))
+ resumeGameButton.Name = "resumeGameButton"
+ resumeGameButton.ZIndex = baseZIndex + 4
+ resumeGameButton.Parent = gameMainMenuFrame
+ resumeGameButton.Modal = true
+ resumeGameButton.MouseButton1Click:connect(function() resumeGameFunction(shield) end)
+
+ local gameSettingsButton = createTextButton("Game Settings",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size24,UDim2.new(0,340,0,50),UDim2.new(0,82,0,156))
+ gameSettingsButton.Name = "SettingsButton"
+ gameSettingsButton.ZIndex = baseZIndex + 4
+ gameSettingsButton.Parent = gameMainMenuFrame
+
+ if game:FindFirstChild("LoadingGuiService") and #game.LoadingGuiService:GetChildren() > 0 then
+ local gameSettingsButton = createTextButton("Game Instructions",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size24,UDim2.new(0,340,0,50),UDim2.new(0,82,0,207))
+ gameSettingsButton.Name = "GameInstructions"
+ gameSettingsButton.ZIndex = baseZIndex + 4
+ gameSettingsButton.Parent = gameMainMenuFrame
+ gameSettingsButton.MouseButton1Click:connect(function()
+ if game:FindFirstChild("Players") and game.Players["LocalPlayer"] then
+ local loadingGui = game.Players.LocalPlayer:FindFirstChild("PlayerLoadingGui")
+ if loadingGui then
+ loadingGui.Visible = true
+ end
+ end
+ end)
+ end
+
+ local resetButton = createTextButton("Reset Character",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size24,UDim2.new(0,340,0,50),UDim2.new(0,82,0,105))
+ resetButton.Name = "ResetButton"
+ resetButton.ZIndex = baseZIndex + 4
+ resetButton.Parent = gameMainMenuFrame
+
+ return gameMainMenuFrame
+end
+
+local function createGameSettingsMenu(baseZIndex, shield)
+ local gameSettingsMenuFrame = Instance.new("Frame")
+ gameSettingsMenuFrame.Name = "GameSettingsMenu"
+ gameSettingsMenuFrame.BackgroundTransparency = 1
+ gameSettingsMenuFrame.Size = UDim2.new(1,0,1,0)
+ gameSettingsMenuFrame.ZIndex = baseZIndex + 4
+
+ local title = Instance.new("TextLabel")
+ title.Name = "Title"
+ title.Text = "Settings"
+ title.Size = UDim2.new(1,0,0,48)
+ title.Position = UDim2.new(0,9,0,-9)
+ title.Font = Enum.Font.ArialBold
+ title.FontSize = Enum.FontSize.Size36
+ title.TextColor3 = Color3.new(1,1,1)
+ title.ZIndex = baseZIndex + 4
+ title.BackgroundTransparency = 1
+ title.Parent = gameSettingsMenuFrame
+
+ local fullscreenText = Instance.new("TextLabel")
+ fullscreenText.Name = "FullscreenText"
+ fullscreenText.Text = "Fullscreen Mode"
+ fullscreenText.Size = UDim2.new(0,124,0,18)
+ fullscreenText.Position = UDim2.new(0,62,0,145)
+ fullscreenText.Font = Enum.Font.Arial
+ fullscreenText.FontSize = Enum.FontSize.Size18
+ fullscreenText.TextColor3 = Color3.new(1,1,1)
+ fullscreenText.ZIndex = baseZIndex + 4
+ fullscreenText.BackgroundTransparency = 1
+ fullscreenText.Parent = gameSettingsMenuFrame
+
+ local fullscreenShortcut = Instance.new("TextLabel")
+ fullscreenShortcut.Visible = hasGraphicsSlider
+ fullscreenShortcut.Name = "FullscreenShortcutText"
+ fullscreenShortcut.Text = "F11"
+ fullscreenShortcut.BackgroundTransparency = 1
+ fullscreenShortcut.Font = Enum.Font.Arial
+ fullscreenShortcut.FontSize = Enum.FontSize.Size12
+ fullscreenShortcut.Position = UDim2.new(0,186,0,141)
+ fullscreenShortcut.Size = UDim2.new(0,30,0,30)
+ fullscreenShortcut.TextColor3 = Color3.new(0,1,0)
+ fullscreenShortcut.ZIndex = baseZIndex + 4
+ fullscreenShortcut.Parent = gameSettingsMenuFrame
+
+ local studioText = Instance.new("TextLabel")
+ studioText.Visible = false
+ studioText.Name = "StudioText"
+ studioText.Text = "Studio Mode"
+ studioText.Size = UDim2.new(0,95,0,18)
+ studioText.Position = UDim2.new(0,62,0,179)
+ studioText.Font = Enum.Font.Arial
+ studioText.FontSize = Enum.FontSize.Size18
+ studioText.TextColor3 = Color3.new(1,1,1)
+ studioText.ZIndex = baseZIndex + 4
+ studioText.BackgroundTransparency = 1
+ studioText.Parent = gameSettingsMenuFrame
+
+ local studioShortcut = fullscreenShortcut:clone()
+ studioShortcut.Name = "StudioShortcutText"
+ studioShortcut.Visible = false -- TODO: turn back on when f2 hack is fixed
+ studioShortcut.Text = "F2"
+ studioShortcut.Position = UDim2.new(0,154,0,175)
+ studioShortcut.Parent = gameSettingsMenuFrame
+
+ local studioCheckbox = nil
+
+ if hasGraphicsSlider then
+ local qualityText = Instance.new("TextLabel")
+ qualityText.Name = "QualityText"
+ qualityText.Text = "Graphics Quality"
+ qualityText.Size = UDim2.new(0,128,0,18)
+ qualityText.Position = UDim2.new(0,30,0,239)
+ qualityText.Font = Enum.Font.Arial
+ qualityText.FontSize = Enum.FontSize.Size18
+ qualityText.TextColor3 = Color3.new(1,1,1)
+ qualityText.ZIndex = baseZIndex + 4
+ qualityText.BackgroundTransparency = 1
+ qualityText.Parent = gameSettingsMenuFrame
+ qualityText.Visible = not inStudioMode
+
+ local autoText = qualityText:clone()
+ autoText.Name = "AutoText"
+ autoText.Text = "Auto"
+ autoText.Position = UDim2.new(0,183,0,214)
+ autoText.TextColor3 = Color3.new(128/255,128/255,128/255)
+ autoText.Size = UDim2.new(0,34,0,18)
+ autoText.Parent = gameSettingsMenuFrame
+ autoText.Visible = not inStudioMode
+
+ local fasterText = autoText:clone()
+ fasterText.Name = "FasterText"
+ fasterText.Text = "Faster"
+ fasterText.Position = UDim2.new(0,185,0,274)
+ fasterText.TextColor3 = Color3.new(95,95,95)
+ fasterText.FontSize = Enum.FontSize.Size14
+ fasterText.Parent = gameSettingsMenuFrame
+ fasterText.Visible = not inStudioMode
+
+ local fasterShortcut = fullscreenShortcut:clone()
+ fasterShortcut.Name = "FasterShortcutText"
+ fasterShortcut.Text = "F10 + Shift"
+ fasterShortcut.Position = UDim2.new(0,185,0,283)
+ fasterShortcut.Parent = gameSettingsMenuFrame
+ fasterShortcut.Visible = not inStudioMode
+
+ local betterQualityText = autoText:clone()
+ betterQualityText.Name = "BetterQualityText"
+ betterQualityText.Text = "Better Quality"
+ betterQualityText.TextWrap = true
+ betterQualityText.Size = UDim2.new(0,41,0,28)
+ betterQualityText.Position = UDim2.new(0,390,0,269)
+ betterQualityText.TextColor3 = Color3.new(95,95,95)
+ betterQualityText.FontSize = Enum.FontSize.Size14
+ betterQualityText.Parent = gameSettingsMenuFrame
+ betterQualityText.Visible = not inStudioMode
+
+ local betterQualityShortcut = fullscreenShortcut:clone()
+ betterQualityShortcut.Name = "BetterQualityShortcut"
+ betterQualityShortcut.Text = "F10"
+ betterQualityShortcut.Position = UDim2.new(0,394,0,288)
+ betterQualityShortcut.Parent = gameSettingsMenuFrame
+ betterQualityShortcut.Visible = not inStudioMode
+
+ local autoGraphicsButton = createTextButton("X",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size18,UDim2.new(0,25,0,25),UDim2.new(0,187,0,239))
+ autoGraphicsButton.Name = "AutoGraphicsButton"
+ autoGraphicsButton.ZIndex = baseZIndex + 4
+ autoGraphicsButton.Parent = gameSettingsMenuFrame
+ autoGraphicsButton.Visible = not inStudioMode
+
+ local graphicsSlider, graphicsLevel = RbxGui.CreateSlider(GraphicsQualityLevels,150,UDim2.new(0, 230, 0, 280)) -- graphics - 1 because slider starts at 1 instead of 0
+ graphicsSlider.Parent = gameSettingsMenuFrame
+ graphicsSlider.Bar.ZIndex = baseZIndex + 4
+ graphicsSlider.Bar.Slider.ZIndex = baseZIndex + 5
+ graphicsSlider.Visible = not inStudioMode
+ graphicsLevel.Value = math.floor((settings().Rendering:GetMaxQualityLevel() - 1)/2)
+
+ local graphicsSetter = Instance.new("TextBox")
+ graphicsSetter.Name = "GraphicsSetter"
+ graphicsSetter.BackgroundColor3 = Color3.new(0,0,0)
+ graphicsSetter.BorderColor3 = Color3.new(128/255,128/255,128/255)
+ graphicsSetter.Size = UDim2.new(0,50,0,25)
+ graphicsSetter.Position = UDim2.new(0,450,0,269)
+ graphicsSetter.TextColor3 = Color3.new(1,1,1)
+ graphicsSetter.Font = Enum.Font.Arial
+ graphicsSetter.FontSize = Enum.FontSize.Size18
+ graphicsSetter.Text = "Auto"
+ graphicsSetter.ZIndex = 1
+ graphicsSetter.TextWrap = true
+ graphicsSetter.Parent = gameSettingsMenuFrame
+ graphicsSetter.Visible = not inStudioMode
+
+ local isAutoGraphics = true
+ if not inStudioMode then
+ isAutoGraphics = (UserSettings().GameSettings.SavedQualityLevel == Enum.SavedQualitySetting.Automatic)
+ else
+ settings().Rendering.EnableFRM = false
+ end
+
+ local listenToGraphicsLevelChange = true
+
+ local function setAutoGraphicsGui(active)
+ isAutoGraphics = active
+ if active then
+ autoGraphicsButton.Text = "X"
+ betterQualityText.ZIndex = 1
+ betterQualityShortcut.ZIndex = 1
+ fasterShortcut.ZIndex = 1
+ fasterText.ZIndex = 1
+ graphicsSlider.Bar.ZIndex = 1
+ graphicsSlider.Bar.Slider.ZIndex = 1
+ graphicsSetter.ZIndex = 1
+ graphicsSetter.Text = "Auto"
+ else
+ autoGraphicsButton.Text = ""
+ graphicsSlider.Bar.ZIndex = baseZIndex + 4
+ graphicsSlider.Bar.Slider.ZIndex = baseZIndex + 5
+ betterQualityShortcut.ZIndex = baseZIndex + 4
+ fasterShortcut.ZIndex = baseZIndex + 4
+ betterQualityText.ZIndex = baseZIndex + 4
+ fasterText.ZIndex = baseZIndex + 4
+ graphicsSetter.ZIndex = baseZIndex + 4
+ end
+ end
+
+ local function goToAutoGraphics()
+ setAutoGraphicsGui(true)
+
+ UserSettings().GameSettings.SavedQualityLevel = Enum.SavedQualitySetting.Automatic
+
+ settings().Rendering.QualityLevel = Enum.QualityLevel.Automatic
+ end
+
+ local function setGraphicsQualityLevel(newLevel)
+ local percentage = newLevel/GraphicsQualityLevels
+ local newSetting = math.floor((settings().Rendering:GetMaxQualityLevel() - 1) * percentage)
+ if newSetting == 20 then -- Level 20 is the same as level 21, except it doesn't render ambient occlusion
+ newSetting = 21
+ elseif newLevel == 1 then -- make sure we can go to lowest settings (for terrible computers)
+ newSetting = 1
+ elseif newSetting > settings().Rendering:GetMaxQualityLevel() then
+ newSetting = settings().Rendering:GetMaxQualityLevel() - 1
+ end
+
+ UserSettings().GameSettings.SavedQualityLevel = newLevel
+ settings().Rendering.QualityLevel = newSetting
+ end
+
+ local function goToManualGraphics(explicitLevel)
+ setAutoGraphicsGui(false)
+
+ if explicitLevel then
+ graphicsLevel.Value = explicitLevel
+ else
+ graphicsLevel.Value = math.floor((settings().Rendering.AutoFRMLevel/(settings().Rendering:GetMaxQualityLevel() - 1)) * GraphicsQualityLevels)
+ end
+
+ if explicitLevel == graphicsLevel.Value then -- make sure we are actually in right graphics mode
+ setGraphicsQualityLevel(graphicsLevel.Value)
+ end
+
+ if not explicitLevel then
+ UserSettings().GameSettings.SavedQualityLevel = graphicsLevel.Value
+ end
+ graphicsSetter.Text = tostring(graphicsLevel.Value)
+ end
+
+ local function showAutoGraphics()
+ autoText.ZIndex = baseZIndex + 4
+ autoGraphicsButton.ZIndex = baseZIndex + 4
+ end
+
+ local function hideAutoGraphics()
+ autoText.ZIndex = 1
+ autoGraphicsButton.ZIndex = 1
+ end
+
+ local function showManualGraphics()
+ graphicsSlider.Bar.ZIndex = baseZIndex + 4
+ graphicsSlider.Bar.Slider.ZIndex = baseZIndex + 5
+ betterQualityShortcut.ZIndex = baseZIndex + 4
+ fasterShortcut.ZIndex = baseZIndex + 4
+ betterQualityText.ZIndex = baseZIndex + 4
+ fasterText.ZIndex = baseZIndex + 4
+ graphicsSetter.ZIndex = baseZIndex + 4
+ end
+
+ local function hideManualGraphics()
+ betterQualityText.ZIndex = 1
+ betterQualityShortcut.ZIndex = 1
+ fasterShortcut.ZIndex = 1
+ fasterText.ZIndex = 1
+ graphicsSlider.Bar.ZIndex = 1
+ graphicsSlider.Bar.Slider.ZIndex = 1
+ graphicsSetter.ZIndex = 1
+ end
+
+ local function translateSavedQualityLevelToInt(savedQualityLevel)
+ if savedQualityLevel == Enum.SavedQualitySetting.Automatic then
+ return 0
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel1 then
+ return 1
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel2 then
+ return 2
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel3 then
+ return 3
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel4 then
+ return 4
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel5 then
+ return 5
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel6 then
+ return 6
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel7 then
+ return 7
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel8 then
+ return 8
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel9 then
+ return 9
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel10 then
+ return 10
+ end
+ end
+
+ local function enableGraphicsWidget()
+ settings().Rendering.EnableFRM = true
+
+ isAutoGraphics = (UserSettings().GameSettings.SavedQualityLevel == Enum.SavedQualitySetting.Automatic)
+ if isAutoGraphics then
+ showAutoGraphics()
+ goToAutoGraphics()
+ else
+ showAutoGraphics()
+ showManualGraphics()
+ goToManualGraphics(translateSavedQualityLevelToInt(UserSettings().GameSettings.SavedQualityLevel))
+ end
+ end
+
+ local function disableGraphicsWidget()
+ hideManualGraphics()
+ hideAutoGraphics()
+ settings().Rendering.EnableFRM = false
+ end
+
+ graphicsSetter.FocusLost:connect(function()
+ if isAutoGraphics then
+ graphicsSetter.Text = tostring(graphicsLevel.Value)
+ return
+ end
+
+ local newGraphicsValue = tonumber(graphicsSetter.Text)
+ if newGraphicsValue == nil then
+ graphicsSetter.Text = tostring(graphicsLevel.Value)
+ return
+ end
+
+ if newGraphicsValue < 1 then newGraphicsValue = 1
+ elseif newGraphicsValue >= settings().Rendering:GetMaxQualityLevel() then
+ newGraphicsValue = settings().Rendering:GetMaxQualityLevel() - 1
+ end
+
+ graphicsLevel.Value = newGraphicsValue
+ setGraphicsQualityLevel(graphicsLevel.Value)
+ graphicsSetter.Text = tostring(graphicsLevel.Value)
+ end)
+
+ graphicsLevel.Changed:connect(function(prop)
+ if isAutoGraphics then return end
+ if not listenToGraphicsLevelChange then return end
+
+ graphicsSetter.Text = tostring(graphicsLevel.Value)
+ setGraphicsQualityLevel(graphicsLevel.Value)
+ end)
+
+ -- setup our graphic mode on load
+ if inStudioMode or UserSettings().GameSettings.SavedQualityLevel == Enum.SavedQualitySetting.Automatic then
+ if inStudioMode then
+ settings().Rendering.EnableFRM = false
+ disableGraphicsWidget()
+ else
+ settings().Rendering.EnableFRM = true
+ goToAutoGraphics()
+ end
+ else
+ settings().Rendering.EnableFRM = true
+ goToManualGraphics(translateSavedQualityLevelToInt(UserSettings().GameSettings.SavedQualityLevel))
+ end
+
+ autoGraphicsButton.MouseButton1Click:connect(function()
+ if inStudioMode and not game.Players.LocalPlayer then return end
+
+ if not isAutoGraphics then
+ goToAutoGraphics()
+ else
+ goToManualGraphics(graphicsLevel.Value)
+ end
+ end)
+
+ local lastUpdate = nil
+ game.GraphicsQualityChangeRequest:connect(function(graphicsIncrease)
+ if isAutoGraphics then return end -- only can set graphics in manual mode
+
+ if graphicsIncrease then
+ if (graphicsLevel.Value + 1) > GraphicsQualityLevels then return end
+ graphicsLevel.Value = graphicsLevel.Value + 1
+ graphicsSetter.Text = tostring(graphicsLevel.Value)
+ setGraphicsQualityLevel(graphicsLevel.Value)
+
+ game:GetService("GuiService"):SendNotification("Graphics Quality",
+ "Increased to (" .. graphicsSetter.Text .. ")",
+ "",
+ 2,
+ function()
+ end)
+ else
+ if (graphicsLevel.Value - 1) <= 0 then return end
+ graphicsLevel.Value = graphicsLevel.Value - 1
+ graphicsSetter.Text = tostring(graphicsLevel.Value)
+ setGraphicsQualityLevel(graphicsLevel.Value)
+
+ game:GetService("GuiService"):SendNotification("Graphics Quality",
+ "Decreased to (" .. graphicsSetter.Text .. ")",
+ "",
+ 2,
+ function()
+ end)
+ end
+ end)
+
+ game.Players.PlayerAdded:connect(function(player)
+ if player == game.Players.LocalPlayer and inStudioMode then
+ enableGraphicsWidget()
+ end
+ end)
+ game.Players.PlayerRemoving:connect(function(player)
+ if player == game.Players.LocalPlayer and inStudioMode then
+ disableGraphicsWidget()
+ end
+ end)
+
+ studioCheckbox = createTextButton("",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size18,UDim2.new(0,25,0,25),UDim2.new(0,30,0,176))
+ studioCheckbox.Name = "StudioCheckbox"
+ studioCheckbox.ZIndex = baseZIndex + 4
+ --studioCheckbox.Parent = gameSettingsMenuFrame -- todo: enable when studio h4x aren't an issue anymore
+ studioCheckbox:SetVerb("TogglePlayMode")
+ studioCheckbox.Visible = false -- todo: enabled when studio h4x aren't an issue anymore
+
+ local wasManualGraphics = (settings().Rendering.QualityLevel ~= Enum.QualityLevel.Automatic)
+ if inStudioMode and not game.Players.LocalPlayer then
+ studioCheckbox.Text = "X"
+ disableGraphicsWidget()
+ elseif inStudioMode then
+ studioCheckbox.Text = "X"
+ enableGraphicsWidget()
+ end
+ if hasGraphicsSlider then
+ UserSettings().GameSettings.StudioModeChanged:connect(function(isStudioMode)
+ inStudioMode = isStudioMode
+ if isStudioMode then
+ wasManualGraphics = (settings().Rendering.QualityLevel ~= Enum.QualityLevel.Automatic)
+ goToAutoGraphics()
+ studioCheckbox.Text = "X"
+ autoGraphicsButton.ZIndex = 1
+ autoText.ZIndex = 1
+ else
+ if wasManualGraphics then
+ goToManualGraphics()
+ end
+ studioCheckbox.Text = ""
+ autoGraphicsButton.ZIndex = baseZIndex + 4
+ autoText.ZIndex = baseZIndex + 4
+ end
+ end)
+ else
+ studioCheckbox.MouseButton1Click:connect(function()
+ if not studioCheckbox.Active then return end
+
+ if studioCheckbox.Text == "" then
+ studioCheckbox.Text = "X"
+ else
+ studioCheckbox.Text = ""
+ end
+ end)
+ end
+ end
+
+ local fullscreenCheckbox = createTextButton("",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size18,UDim2.new(0,25,0,25),UDim2.new(0,30,0,144))
+ fullscreenCheckbox.Name = "FullscreenCheckbox"
+ fullscreenCheckbox.ZIndex = baseZIndex + 4
+ fullscreenCheckbox.Parent = gameSettingsMenuFrame
+ fullscreenCheckbox:SetVerb("ToggleFullScreen")
+ if UserSettings().GameSettings:InFullScreen() then fullscreenCheckbox.Text = "X" end
+ if hasGraphicsSlider then
+ UserSettings().GameSettings.FullscreenChanged:connect(function(isFullscreen)
+ if isFullscreen then
+ fullscreenCheckbox.Text = "X"
+ else
+ fullscreenCheckbox.Text = ""
+ end
+ end)
+ else
+ fullscreenCheckbox.MouseButton1Click:connect(function()
+ if fullscreenCheckbox.Text == "" then
+ fullscreenCheckbox.Text = "X"
+ else
+ fullscreenCheckbox.Text = ""
+ end
+ end)
+ end
+
+ if game:FindFirstChild("NetworkClient") then -- we are playing online
+ setDisabledState(studioText)
+ setDisabledState(studioShortcut)
+ setDisabledState(studioCheckbox)
+ end
+
+ local backButton
+ if hasGraphicsSlider then
+ backButton = createTextButton("OK",Enum.ButtonStyle.RobloxButtonDefault,Enum.FontSize.Size24,UDim2.new(0,180,0,50),UDim2.new(0,170,0,330))
+ backButton.Modal = true
+ else
+ backButton = createTextButton("OK",Enum.ButtonStyle.RobloxButtonDefault,Enum.FontSize.Size24,UDim2.new(0,180,0,50),UDim2.new(0,170,0,270))
+ backButton.Modal = true
+ end
+
+ backButton.Name = "BackButton"
+ backButton.ZIndex = baseZIndex + 4
+ backButton.Parent = gameSettingsMenuFrame
+
+ local syncVideoCaptureSetting = nil
+
+ if not macClient then
+ local videoCaptureLabel = Instance.new("TextLabel")
+ videoCaptureLabel.Name = "VideoCaptureLabel"
+ videoCaptureLabel.Text = "After Capturing Video"
+ videoCaptureLabel.Font = Enum.Font.Arial
+ videoCaptureLabel.FontSize = Enum.FontSize.Size18
+ videoCaptureLabel.Position = UDim2.new(0,32,0,100)
+ videoCaptureLabel.Size = UDim2.new(0,164,0,18)
+ videoCaptureLabel.BackgroundTransparency = 1
+ videoCaptureLabel.TextColor3 = Color3I(255,255,255)
+ videoCaptureLabel.TextXAlignment = Enum.TextXAlignment.Left
+ videoCaptureLabel.ZIndex = baseZIndex + 4
+ videoCaptureLabel.Parent = gameSettingsMenuFrame
+
+ local videoNames = {}
+ local videoNameToItem = {}
+ videoNames[1] = "Just Save to Disk"
+ videoNameToItem[videoNames[1]] = Enum.UploadSetting["Never"]
+ videoNames[2] = "Upload to YouTube"
+ videoNameToItem[videoNames[2]] = Enum.UploadSetting["Ask me first"]
+
+ local videoCaptureDropDown = nil
+ videoCaptureDropDown, updateVideoCaptureDropDownSelection = RbxGui.CreateDropDownMenu(videoNames,
+ function(text)
+ UserSettings().GameSettings.VideoUploadPromptBehavior = videoNameToItem[text]
+ end)
+ videoCaptureDropDown.Name = "VideoCaptureField"
+ videoCaptureDropDown.ZIndex = baseZIndex + 4
+ videoCaptureDropDown.DropDownMenuButton.ZIndex = baseZIndex + 4
+ videoCaptureDropDown.DropDownMenuButton.Icon.ZIndex = baseZIndex + 4
+ videoCaptureDropDown.Position = UDim2.new(0, 270, 0, 94)
+ videoCaptureDropDown.Size = UDim2.new(0,200,0,32)
+ videoCaptureDropDown.Parent = gameSettingsMenuFrame
+
+ syncVideoCaptureSetting = function()
+ if UserSettings().GameSettings.VideoUploadPromptBehavior == Enum.UploadSetting["Never"] then
+ updateVideoCaptureDropDownSelection(videoNames[1])
+ elseif UserSettings().GameSettings.VideoUploadPromptBehavior == Enum.UploadSetting["Ask me first"] then
+ updateVideoCaptureDropDownSelection(videoNames[2])
+ else
+ UserSettings().GameSettings.VideoUploadPromptBehavior = Enum.UploadSetting["Ask me first"]
+ updateVideoCaptureDropDownSelection(videoNames[2])
+ end
+ end
+ end
+
+ local cameraLabel = Instance.new("TextLabel")
+ cameraLabel.Name = "CameraLabel"
+ cameraLabel.Text = "Character & Camera Controls"
+ cameraLabel.Font = Enum.Font.Arial
+ cameraLabel.FontSize = Enum.FontSize.Size18
+ cameraLabel.Position = UDim2.new(0,31,0,58)
+ cameraLabel.Size = UDim2.new(0,224,0,18)
+ cameraLabel.TextColor3 = Color3I(255,255,255)
+ cameraLabel.TextXAlignment = Enum.TextXAlignment.Left
+ cameraLabel.BackgroundTransparency = 1
+ cameraLabel.ZIndex = baseZIndex + 4
+ cameraLabel.Parent = gameSettingsMenuFrame
+
+ local mouseLockLabel = game.CoreGui.RobloxGui:FindFirstChild("MouseLockLabel",true)
+
+ local enumItems = Enum.ControlMode:GetEnumItems()
+ local enumNames = {}
+ local enumNameToItem = {}
+ for i,obj in ipairs(enumItems) do
+ enumNames[i] = obj.Name
+ enumNameToItem[obj.Name] = obj
+ end
+
+ local cameraDropDown
+ cameraDropDown, updateCameraDropDownSelection = RbxGui.CreateDropDownMenu(enumNames,
+ function(text)
+ UserSettings().GameSettings.ControlMode = enumNameToItem[text]
+
+ pcall(function()
+ if mouseLockLabel and UserSettings().GameSettings.ControlMode == Enum.ControlMode["Mouse Lock Switch"] then
+ mouseLockLabel.Visible = true
+ elseif mouseLockLabel then
+ mouseLockLabel.Visible = false
+ end
+ end)
+ end)
+ cameraDropDown.Name = "CameraField"
+ cameraDropDown.ZIndex = baseZIndex + 4
+ cameraDropDown.DropDownMenuButton.ZIndex = baseZIndex + 4
+ cameraDropDown.DropDownMenuButton.Icon.ZIndex = baseZIndex + 4
+ cameraDropDown.Position = UDim2.new(0, 270, 0, 52)
+ cameraDropDown.Size = UDim2.new(0,200,0,32)
+ cameraDropDown.Parent = gameSettingsMenuFrame
+
+ return gameSettingsMenuFrame
+end
+
+if LoadLibrary then
+ RbxGui = LoadLibrary("RbxGui")
+ local baseZIndex = 0
+if UserSettings then
+
+ local createSettingsDialog = function()
+ waitForChild(gui,"BottomLeftControl")
+ settingsButton = gui.BottomLeftControl:FindFirstChild("SettingsButton")
+
+ if settingsButton == nil then
+ settingsButton = Instance.new("ImageButton")
+ settingsButton.Name = "SettingsButton"
+ settingsButton.Image = "rbxasset://textures/ui/SettingsButton.png"
+ settingsButton.BackgroundTransparency = 1
+ settingsButton.Active = false
+ settingsButton.Size = UDim2.new(0,54,0,46)
+ settingsButton.Position = UDim2.new(0,2,0,50)
+ settingsButton.Parent = gui.BottomLeftControl
+ end
+
+ local shield = Instance.new("TextButton")
+ shield.Text = ""
+ shield.Name = "UserSettingsShield"
+ shield.Active = true
+ shield.AutoButtonColor = false
+ shield.Visible = false
+ shield.Size = UDim2.new(1,0,1,0)
+ shield.BackgroundColor3 = Color3I(51,51,51)
+ shield.BorderColor3 = Color3I(27,42,53)
+ shield.BackgroundTransparency = 0.4
+ shield.ZIndex = baseZIndex + 2
+ mainShield = shield
+
+ local frame = Instance.new("Frame")
+ frame.Name = "Settings"
+ frame.Position = UDim2.new(0.5, -262, -0.5, -200)
+ frame.Size = UDim2.new(0, 525, 0, 430)
+ frame.BackgroundTransparency = 1
+ frame.Active = true
+ frame.Parent = shield
+
+ local settingsFrame = Instance.new("Frame")
+ settingsFrame.Name = "SettingsStyle"
+ settingsFrame.Size = UDim2.new(1, 0, 1, 0)
+ settingsFrame.Style = Enum.FrameStyle.RobloxRound
+ settingsFrame.Active = true
+ settingsFrame.ZIndex = baseZIndex + 3
+ settingsFrame.Parent = frame
+
+ local gameMainMenu = createGameMainMenu(baseZIndex, shield)
+ gameMainMenu.Parent = settingsFrame
+
+ gameMainMenu.ScreenshotButton.MouseButton1Click:connect(function()
+ backToGame(gameMainMenu.ScreenshotButton, shield, settingsButton)
+ end)
+
+ gameMainMenu.RecordVideoButton.MouseButton1Click:connect(function()
+ recordVideoClick(gameMainMenu.RecordVideoButton, gui.StopRecordButton)
+ backToGame(gameMainMenu.RecordVideoButton, shield, settingsButton)
+ end)
+
+ if settings():FindFirstChild("Game Options") then
+ pcall(function()
+ settings():FindFirstChild("Game Options").VideoRecordingChangeRequest:connect(function(recording)
+ recordingVideo = recording
+ setRecordGui(recording, gui.StopRecordButton, gameMainMenu.RecordVideoButton)
+ end)
+ end)
+ end
+
+ game.CoreGui.RobloxGui.Changed:connect(function(prop) -- We have stopped recording when we resize
+ if prop == "AbsoluteSize" and recordingVideo then
+ recordVideoClick(gameMainMenu.RecordVideoButton, gui.StopRecordButton)
+ end
+ end)
+
+ function localPlayerChange()
+ gameMainMenu.ResetButton.Visible = game.Players.LocalPlayer
+ if game.Players.LocalPlayer then
+ settings().Rendering.EnableFRM = true
+ elseif inStudioMode then
+ settings().Rendering.EnableFRM = false
+ end
+ end
+
+ gameMainMenu.ResetButton.Visible = game.Players.LocalPlayer
+ if game.Players.LocalPlayer ~= nil then
+ game.Players.LocalPlayer.Changed:connect(function()
+ localPlayerChange()
+ end)
+ else
+ delay(0,function()
+ waitForProperty(game.Players,"LocalPlayer")
+ gameMainMenu.ResetButton.Visible = game.Players.LocalPlayer
+ game.Players.LocalPlayer.Changed:connect(function()
+ localPlayerChange()
+ end)
+ end)
+ end
+
+ gameMainMenu.ReportAbuseButton.Visible = game:FindFirstChild("NetworkClient")
+ if not gameMainMenu.ReportAbuseButton.Visible then
+ game.ChildAdded:connect(function(child)
+ if child:IsA("NetworkClient") then
+ gameMainMenu.ReportAbuseButton.Visible = game:FindFirstChild("NetworkClient")
+ end
+ end)
+ end
+
+ gameMainMenu.ResetButton.MouseButton1Click:connect(function()
+ goToMenu(settingsFrame,"ResetConfirmationMenu","up",UDim2.new(0,525,0,370))
+ end)
+
+ gameMainMenu.LeaveGameButton.MouseButton1Click:connect(function()
+ goToMenu(settingsFrame,"LeaveConfirmationMenu","down",UDim2.new(0,525,0,300))
+ end)
+
+ if game.CoreGui.Version >= 4 then -- we can use escape!
+ game:GetService("GuiService").EscapeKeyPressed:connect(function()
+ if currentMenuSelection == nil then
+ game.GuiService:AddCenterDialog(shield, Enum.CenterDialogType.ModalDialog,
+ --showFunction
+ function()
+ settingsButton.Active = false
+ updateCameraDropDownSelection(UserSettings().GameSettings.ControlMode.Name)
+
+ if syncVideoCaptureSetting then
+ syncVideoCaptureSetting()
+ end
+
+ goToMenu(settingsFrame,"GameMainMenu","right",UDim2.new(0,525,0,430))
+ shield.Visible = true
+ shield.Active = true
+ settingsFrame.Parent:TweenPosition(UDim2.new(0.5, -262,0.5, -200),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ settingsFrame.Parent:TweenSize(UDim2.new(0,525,0,430),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ end,
+ --hideFunction
+ function()
+ settingsFrame.Parent:TweenPosition(UDim2.new(0.5, -262,-0.5, -200),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ settingsFrame.Parent:TweenSize(UDim2.new(0,525,0,430),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ shield.Visible = false
+ settingsButton.Active = true
+ end)
+ elseif #lastMenuSelection > 0 then
+ if #centerDialogs > 0 then
+ for i = 1, #centerDialogs do
+ game.GuiService:RemoveCenterDialog(centerDialogs[i])
+ centerDialogs[i].Visible = false
+ end
+ centerDialogs = {}
+ end
+
+ goToMenu(lastMenuSelection[#lastMenuSelection]["container"],lastMenuSelection[#lastMenuSelection]["name"],
+ lastMenuSelection[#lastMenuSelection]["direction"],lastMenuSelection[#lastMenuSelection]["lastSize"])
+
+ table.remove(lastMenuSelection,#lastMenuSelection)
+ if #lastMenuSelection == 1 then -- apparently lua can't reduce count to 0... T_T
+ lastMenuSelection = {}
+ end
+ else
+ resumeGameFunction(shield)
+ end
+ end)
+ end
+
+ local gameSettingsMenu = createGameSettingsMenu(baseZIndex, shield)
+ gameSettingsMenu.Visible = false
+ gameSettingsMenu.Parent = settingsFrame
+
+ gameMainMenu.SettingsButton.MouseButton1Click:connect(function()
+ goToMenu(settingsFrame,"GameSettingsMenu","left",UDim2.new(0,525,0,350))
+ end)
+
+ gameSettingsMenu.BackButton.MouseButton1Click:connect(function()
+ goToMenu(settingsFrame,"GameMainMenu","right",UDim2.new(0,525,0,430))
+ end)
+
+ local resetConfirmationWindow = createResetConfirmationMenu(baseZIndex, shield)
+ resetConfirmationWindow.Visible = false
+ resetConfirmationWindow.Parent = settingsFrame
+
+ local leaveConfirmationWindow = createLeaveConfirmationMenu(baseZIndex,shield)
+ leaveConfirmationWindow.Visible = false
+ leaveConfirmationWindow.Parent = settingsFrame
+
+ robloxLock(shield)
+
+ settingsButton.MouseButton1Click:connect(
+ function()
+ game.GuiService:AddCenterDialog(shield, Enum.CenterDialogType.ModalDialog,
+ --showFunction
+ function()
+ settingsButton.Active = false
+ updateCameraDropDownSelection(UserSettings().GameSettings.ControlMode.Name)
+
+ if syncVideoCaptureSetting then
+ syncVideoCaptureSetting()
+ end
+
+ goToMenu(settingsFrame,"GameMainMenu","right",UDim2.new(0,525,0,430))
+ shield.Visible = true
+ settingsFrame.Parent:TweenPosition(UDim2.new(0.5, -262,0.5, -200),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ settingsFrame.Parent:TweenSize(UDim2.new(0,525,0,430),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ end,
+ --hideFunction
+ function()
+ settingsFrame.Parent:TweenPosition(UDim2.new(0.5, -262,-0.5, -200),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ settingsFrame.Parent:TweenSize(UDim2.new(0,525,0,430),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ shield.Visible = false
+ settingsButton.Active = true
+ end)
+ end)
+
+ return shield
+ end
+
+ delay(0, function()
+ createSettingsDialog().Parent = gui
+
+ gui.BottomLeftControl.SettingsButton.Active = true
+ gui.BottomLeftControl.SettingsButton.Position = UDim2.new(0,2,0,-2)
+
+ if mouseLockLabel and UserSettings().GameSettings.ControlMode == Enum.ControlMode["Mouse Lock Switch"] then
+ mouseLockLabel.Visible = true
+ elseif mouseLockLabel then
+ mouseLockLabel.Visible = false
+ end
+
+ -- our script has loaded, get rid of older buttons now
+ local leaveGameButton = gui.BottomLeftControl:FindFirstChild("Exit")
+ if leaveGameButton then leaveGameButton:Remove() end
+
+ local topLeft = gui:FindFirstChild("TopLeftControl")
+ if topLeft then
+ leaveGameButton = topLeft:FindFirstChild("Exit")
+ if leaveGameButton then leaveGameButton:Remove() end
+
+ topLeft:Remove()
+ end
+ end)
+
+end --UserSettings call
+
+local createSaveDialogs = function()
+ local shield = Instance.new("TextButton")
+ shield.Text = ""
+ shield.AutoButtonColor = false
+ shield.Name = "SaveDialogShield"
+ shield.Active = true
+ shield.Visible = false
+ shield.Size = UDim2.new(1,0,1,0)
+ shield.BackgroundColor3 = Color3I(51,51,51)
+ shield.BorderColor3 = Color3I(27,42,53)
+ shield.BackgroundTransparency = 0.4
+ shield.ZIndex = baseZIndex+1
+
+ local clearAndResetDialog
+ local save
+ local saveLocal
+ local dontSave
+ local cancel
+
+ local messageBoxButtons = {}
+ messageBoxButtons[1] = {}
+ messageBoxButtons[1].Text = "Save"
+ messageBoxButtons[1].Style = Enum.ButtonStyle.RobloxButtonDefault
+ messageBoxButtons[1].Function = function() save() end
+ messageBoxButtons[2] = {}
+ messageBoxButtons[2].Text = "Cancel"
+ messageBoxButtons[2].Function = function() cancel() end
+ messageBoxButtons[3] = {}
+ messageBoxButtons[3].Text = "Don't Save"
+ messageBoxButtons[3].Function = function() dontSave() end
+
+ local saveDialogMessageBox = RbxGui.CreateStyledMessageDialog("Unsaved Changes", "Save your changes to ROBLOX before leaving?", "Confirm", messageBoxButtons)
+ saveDialogMessageBox.Visible = true
+ saveDialogMessageBox.Parent = shield
+
+
+ local errorBoxButtons = {}
+
+ local buttonOffset = 1
+ if game.LocalSaveEnabled then
+ errorBoxButtons[buttonOffset] = {}
+ errorBoxButtons[buttonOffset].Text = "Save to Disk"
+ errorBoxButtons[buttonOffset].Function = function() saveLocal() end
+ buttonOffset = buttonOffset + 1
+ end
+ errorBoxButtons[buttonOffset] = {}
+ errorBoxButtons[buttonOffset].Text = "Keep Playing"
+ errorBoxButtons[buttonOffset].Function = function() cancel() end
+ errorBoxButtons[buttonOffset+1] = {}
+ errorBoxButtons[buttonOffset+1].Text = "Don't Save"
+ errorBoxButtons[buttonOffset+1].Function = function() dontSave() end
+
+ local errorDialogMessageBox = RbxGui.CreateStyledMessageDialog("Upload Failed", "Sorry, we could not save your changes to ROBLOX. If this problem continues to occur, please make sure your Roblox account has a verified email address.", "Error", errorBoxButtons)
+ errorDialogMessageBox.Visible = false
+ errorDialogMessageBox.Parent = shield
+
+ local spinnerDialog = Instance.new("Frame")
+ spinnerDialog.Name = "SpinnerDialog"
+ spinnerDialog.Style = Enum.FrameStyle.RobloxRound
+ spinnerDialog.Size = UDim2.new(0, 350, 0, 150)
+ spinnerDialog.Position = UDim2.new(.5, -175, .5, -75)
+ spinnerDialog.Visible = false
+ spinnerDialog.Active = true
+ spinnerDialog.Parent = shield
+
+ local waitingLabel = Instance.new("TextLabel")
+ waitingLabel.Name = "WaitingLabel"
+ waitingLabel.Text = "Saving to ROBLOX..."
+ waitingLabel.Font = Enum.Font.ArialBold
+ waitingLabel.FontSize = Enum.FontSize.Size18
+ waitingLabel.Position = UDim2.new(0.5, 25, 0.5, 0)
+ waitingLabel.TextColor3 = Color3.new(1,1,1)
+ waitingLabel.Parent = spinnerDialog
+
+ local spinnerFrame = Instance.new("Frame")
+ spinnerFrame.Name = "Spinner"
+ spinnerFrame.Size = UDim2.new(0, 80, 0, 80)
+ spinnerFrame.Position = UDim2.new(0.5, -150, 0.5, -40)
+ spinnerFrame.BackgroundTransparency = 1
+ spinnerFrame.Parent = spinnerDialog
+
+ local spinnerIcons = {}
+ local spinnerNum = 1
+ while spinnerNum <= 8 do
+ local spinnerImage = Instance.new("ImageLabel")
+ spinnerImage.Name = "Spinner"..spinnerNum
+ spinnerImage.Size = UDim2.new(0, 16, 0, 16)
+ spinnerImage.Position = UDim2.new(.5+.3*math.cos(math.rad(45*spinnerNum)), -8, .5+.3*math.sin(math.rad(45*spinnerNum)), -8)
+ spinnerImage.BackgroundTransparency = 1
+ spinnerImage.Image = "http://www.syntax.eco/Asset?id=45880710"
+ spinnerImage.Parent = spinnerFrame
+
+ spinnerIcons[spinnerNum] = spinnerImage
+ spinnerNum = spinnerNum + 1
+ end
+
+ save = function()
+ saveDialogMessageBox.Visible = false
+
+ --Show the spinner dialog
+ spinnerDialog.Visible = true
+ local spin = true
+ --Make it spin
+ delay(0, function()
+ local spinPos = 0
+ while spin do
+ local pos = 0
+
+ while pos < 8 do
+ if pos == spinPos or pos == ((spinPos+1)%8) then
+ spinnerIcons[pos+1].Image = "http://www.syntax.eco/Asset?id=45880668"
+ else
+ spinnerIcons[pos+1].Image = "http://www.syntax.eco/Asset?id=45880710"
+ end
+
+ pos = pos + 1
+ end
+ spinPos = (spinPos + 1) % 8
+ wait(0.2)
+ end
+ end)
+
+ --Do the save while the spinner is going, function will wait
+ local result = game:SaveToRoblox()
+ if not result then
+ --Try once more
+ result = game:SaveToRoblox()
+ end
+
+ --Hide the spinner dialog
+ spinnerDialog.Visible = false
+ --And cause the delay thread to stop
+ spin = false
+
+ --Now process the result
+ if result then
+ --Success, close
+ game:FinishShutdown(false)
+ clearAndResetDialog()
+ else
+ --Failure, show the second dialog prompt
+ errorDialogMessageBox.Visible = true
+ end
+ end
+
+ saveLocal = function()
+ errorDialogMessageBox.Visible = false
+ game:FinishShutdown(true)
+ clearAndResetDialog()
+ end
+
+ dontSave = function()
+ saveDialogMessageBox.Visible = false
+ errorDialogMessageBox.Visible = false
+ game:FinishShutdown(false)
+ clearAndResetDialog()
+ end
+ cancel = function()
+ saveDialogMessageBox.Visible = false
+ errorDialogMessageBox.Visible = false
+ clearAndResetDialog()
+ end
+
+ clearAndResetDialog = function()
+ saveDialogMessageBox.Visible = true
+ errorDialogMessageBox.Visible = false
+ spinnerDialog.Visible = false
+ shield.Visible = false
+ game.GuiService:RemoveCenterDialog(shield)
+ end
+
+ robloxLock(shield)
+ shield.Visible = false
+ return shield
+end
+
+local createReportAbuseDialog = function()
+ --Only show things if we are a NetworkClient
+ waitForChild(game,"NetworkClient")
+
+ waitForChild(game,"Players")
+ waitForProperty(game.Players, "LocalPlayer")
+ local localPlayer = game.Players.LocalPlayer
+
+ local reportAbuseButton
+ waitForChild(gui,"UserSettingsShield")
+ waitForChild(gui.UserSettingsShield, "Settings")
+ waitForChild(gui.UserSettingsShield.Settings,"SettingsStyle")
+ waitForChild(gui.UserSettingsShield.Settings.SettingsStyle,"GameMainMenu")
+ waitForChild(gui.UserSettingsShield.Settings.SettingsStyle.GameMainMenu, "ReportAbuseButton")
+ reportAbuseButton = gui.UserSettingsShield.Settings.SettingsStyle.GameMainMenu.ReportAbuseButton
+
+ local shield = Instance.new("TextButton")
+ shield.Name = "ReportAbuseShield"
+ shield.Text = ""
+ shield.AutoButtonColor = false
+ shield.Active = true
+ shield.Visible = false
+ shield.Size = UDim2.new(1,0,1,0)
+ shield.BackgroundColor3 = Color3I(51,51,51)
+ shield.BorderColor3 = Color3I(27,42,53)
+ shield.BackgroundTransparency = 0.4
+ shield.ZIndex = baseZIndex + 1
+
+ local closeAndResetDialgo
+
+ local messageBoxButtons = {}
+ messageBoxButtons[1] = {}
+ messageBoxButtons[1].Text = "Ok"
+ messageBoxButtons[1].Modal = true
+ messageBoxButtons[1].Function = function() closeAndResetDialog() end
+ local calmingMessageBox = RbxGui.CreateMessageDialog("Thanks for your report!", "Our moderators will review the chat logs and determine what happened. The other user is probably just trying to make you mad.\n\nIf anyone used swear words, inappropriate language, or threatened you in real life, please report them for Bad Words or Threats", messageBoxButtons)
+ calmingMessageBox.Visible = false
+ calmingMessageBox.Parent = shield
+
+ local recordedMessageBox = RbxGui.CreateMessageDialog("Thanks for your report!","We've recorded your report for evaluation.", messageBoxButtons)
+ recordedMessageBox.Visible = false
+ recordedMessageBox.Parent = shield
+
+ local normalMessageBox = RbxGui.CreateMessageDialog("Thanks for your report!", "Our moderators will review the chat logs and determine what happened.", messageBoxButtons)
+ normalMessageBox.Visible = false
+ normalMessageBox.Parent = shield
+
+ local frame = Instance.new("Frame")
+ frame.Name = "Settings"
+ frame.Position = UDim2.new(0.5, -250, 0.5, -200)
+ frame.Size = UDim2.new(0.0, 500, 0.0, 400)
+ frame.BackgroundTransparency = 1
+ frame.Active = true
+ frame.Parent = shield
+
+ local settingsFrame = Instance.new("Frame")
+ settingsFrame.Name = "ReportAbuseStyle"
+ settingsFrame.Size = UDim2.new(1, 0, 1, 0)
+ settingsFrame.Style = Enum.FrameStyle.RobloxRound
+ settingsFrame.Active = true
+ settingsFrame.ZIndex = baseZIndex + 1
+ settingsFrame.Parent = frame
+
+ local title = Instance.new("TextLabel")
+ title.Name = "Title"
+ title.Text = "Report Abuse"
+ title.TextColor3 = Color3I(221,221,221)
+ title.Position = UDim2.new(0.5, 0, 0, 25)
+ title.Font = Enum.Font.SourceSansBold
+ title.FontSize = Enum.FontSize.Size48
+ title.ZIndex = baseZIndex + 2
+ title.Parent = settingsFrame
+
+ local description = Instance.new("TextLabel")
+ description.Name = "Description"
+ description.Text = "This will send a complete report to a moderator. The moderator will review the chat log and take appropriate action."
+ description.TextColor3 = Color3I(221,221,221)
+ description.Position = UDim2.new(0, 0, 0, 55)
+ description.Size = UDim2.new(1, 0, 0, 40)
+ description.BackgroundTransparency = 1
+ description.Font = Enum.Font.SourceSans
+ description.FontSize = Enum.FontSize.Size18
+ description.TextWrap = true
+ description.ZIndex = baseZIndex + 2
+ description.TextXAlignment = Enum.TextXAlignment.Left
+ description.TextYAlignment = Enum.TextYAlignment.Top
+ description.Parent = settingsFrame
+
+ local playerLabel = Instance.new("TextLabel")
+ playerLabel.Name = "PlayerLabel"
+ playerLabel.Text = "Which player?"
+ playerLabel.BackgroundTransparency = 1
+ playerLabel.Font = Enum.Font.SourceSans
+ playerLabel.FontSize = Enum.FontSize.Size18
+ playerLabel.Position = UDim2.new(0.025,20,0,137)
+ playerLabel.Size = UDim2.new(0.4,0,0,36)
+ playerLabel.TextColor3 = Color3I(255,255,255)
+ playerLabel.TextXAlignment = Enum.TextXAlignment.Left
+ playerLabel.ZIndex = baseZIndex + 2
+ playerLabel.Parent = settingsFrame
+
+ local gameOrPlayerLabel = Instance.new("TextLabel")
+ gameOrPlayerLabel.Name = "TypeLabel"
+ gameOrPlayerLabel.Text = "Game or Player:"
+ gameOrPlayerLabel.BackgroundTransparency = 1
+ gameOrPlayerLabel.Font = Enum.Font.SourceSans
+ gameOrPlayerLabel.FontSize = Enum.FontSize.Size18
+ gameOrPlayerLabel.Position = UDim2.new(0.025,20,0,100)
+ gameOrPlayerLabel.Size = UDim2.new(0.4,0,0,36)
+ gameOrPlayerLabel.TextColor3 = Color3I(255,255,255)
+ gameOrPlayerLabel.TextXAlignment = Enum.TextXAlignment.Left
+ gameOrPlayerLabel.ZIndex = baseZIndex + 2
+ gameOrPlayerLabel.Parent = settingsFrame
+
+ local abuseLabel = Instance.new("TextLabel")
+ abuseLabel.Name = "AbuseLabel"
+ abuseLabel.Text = "Type of Abuse:"
+ abuseLabel.Font = Enum.Font.SourceSans
+ abuseLabel.BackgroundTransparency = 1
+ abuseLabel.FontSize = Enum.FontSize.Size18
+ abuseLabel.Position = UDim2.new(0.025,20,0,176)
+ abuseLabel.Size = UDim2.new(0.4,0,0,36)
+ abuseLabel.TextColor3 = Color3I(255,255,255)
+ abuseLabel.TextXAlignment = Enum.TextXAlignment.Left
+ abuseLabel.ZIndex = baseZIndex + 2
+ abuseLabel.Parent = settingsFrame
+
+ local abusingPlayer = nil
+ local abuse = nil
+ local submitReportButton = nil
+ local gameOrPlayer = nil
+
+ local updatePlayerSelection = nil
+ local createPlayersDropDown = function()
+ local players = game:GetService("Players")
+ local playerNames = {}
+ local nameToPlayer = {}
+ local children = players:GetChildren()
+ local pos = 1
+ if children then
+ for i, player in ipairs(children) do
+ if player:IsA("Player") and player ~= localPlayer then
+ playerNames[pos] = player.Name
+ nameToPlayer[player.Name] = player
+ pos = pos + 1
+ end
+ end
+ end
+ local playerDropDown = nil
+ playerDropDown, updatePlayerSelection = RbxGui.CreateDropDownMenu(playerNames,
+ function(playerName)
+ abusingPlayer = nameToPlayer[playerName]
+ if abuse and abusingPlayer then
+ submitReportButton.Active = true
+ end
+ end)
+ playerDropDown.Name = "PlayersComboBox"
+ playerDropDown.ZIndex = baseZIndex + 2
+ playerDropDown.Position = UDim2.new(.425, 0, 0, 139)
+ playerDropDown.Size = UDim2.new(.55,0,0,32)
+
+ return playerDropDown
+ end
+
+ local gameOrPlayerTable = {"Game","Player"}
+ local gameOrPlayerDropDown = nil
+ gameOrPlayerDropDown = RbxGui.CreateDropDownMenu(gameOrPlayerTable,
+ function(gameOrPlayerText)
+ gameOrPlayer = gameOrPlayerText
+ if gameOrPlayer == "Game" then
+ submitReportButton.Active = true
+ playerLabel.Visible = false
+ local playerDropDown = gameOrPlayerDropDown.Parent:FindFirstChild("PlayersComboBox")
+ if playerDropDown then
+ playerDropDown.Visible = false
+ end
+ else
+ playerLabel.Visible = true
+ local playerDropDown = gameOrPlayerDropDown.Parent:FindFirstChild("PlayersComboBox")
+ if playerDropDown then
+ playerDropDown.Visible = true
+ end
+ end
+ end, true)
+ gameOrPlayerDropDown.Name = "TypeComboBox"
+ gameOrPlayerDropDown.ZIndex = baseZIndex + 2
+ gameOrPlayerDropDown.Position = UDim2.new(0.425, 0, 0, 100)
+ gameOrPlayerDropDown.Size = UDim2.new(0.55,0,0,32)
+ gameOrPlayerDropDown.Parent = settingsFrame
+
+ local abuses = {"Swearing","Bullying","Scamming","Dating","Cheating/Exploiting","Personal Questions","Offsite Links","Bad Model or Script","Bad Username"}
+ local abuseDropDown, updateAbuseSelection = RbxGui.CreateDropDownMenu(abuses,
+ function(abuseText)
+ abuse = abuseText
+ if abuse and abusingPlayer then
+ submitReportButton.Active = true
+ end
+ end, true)
+ abuseDropDown.Name = "AbuseComboBox"
+ abuseDropDown.ZIndex = baseZIndex + 2
+ abuseDropDown.Position = UDim2.new(0.425, 0, 0, 178)
+ abuseDropDown.Size = UDim2.new(0.55,0,0,32)
+ abuseDropDown.Parent = settingsFrame
+
+ local shortDescriptionLabel = Instance.new("TextLabel")
+ shortDescriptionLabel.Name = "ShortDescriptionLabel"
+ shortDescriptionLabel.Text = "Short Description: (optional)"
+ shortDescriptionLabel.Font = Enum.Font.SourceSans
+ shortDescriptionLabel.FontSize = Enum.FontSize.Size18
+ shortDescriptionLabel.Position = UDim2.new(0.025,0,0,215)
+ shortDescriptionLabel.Size = UDim2.new(0.95,0,0,36)
+ shortDescriptionLabel.TextColor3 = Color3I(255,255,255)
+ shortDescriptionLabel.TextXAlignment = Enum.TextXAlignment.Left
+ shortDescriptionLabel.BackgroundTransparency = 1
+ shortDescriptionLabel.ZIndex = baseZIndex + 2
+ shortDescriptionLabel.Parent = settingsFrame
+
+ local shortDescriptionWrapper = Instance.new("Frame")
+ shortDescriptionWrapper.Name = "ShortDescriptionWrapper"
+ shortDescriptionWrapper.Position = UDim2.new(0.025,0,0,245)
+ shortDescriptionWrapper.Size = UDim2.new(0.95,0,1,-310)
+ shortDescriptionWrapper.BackgroundColor3 = Color3I(0,0,0)
+ shortDescriptionWrapper.BorderSizePixel = 1
+ shortDescriptionWrapper.ZIndex = baseZIndex + 2
+ shortDescriptionWrapper.Parent = settingsFrame
+
+ local shortDescriptionBox = Instance.new("TextBox")
+ shortDescriptionBox.Name = "TextBox"
+ shortDescriptionBox.Text = ""
+ shortDescriptionBox.ClearTextOnFocus = false
+ shortDescriptionBox.Font = Enum.Font.SourceSans
+ shortDescriptionBox.FontSize = Enum.FontSize.Size18
+ shortDescriptionBox.Position = UDim2.new(0,3,0,3)
+ shortDescriptionBox.Size = UDim2.new(1,-6,1,-6)
+ shortDescriptionBox.TextColor3 = Color3I(255,255,255)
+ shortDescriptionBox.TextXAlignment = Enum.TextXAlignment.Left
+ shortDescriptionBox.TextYAlignment = Enum.TextYAlignment.Top
+ shortDescriptionBox.TextWrap = true
+ shortDescriptionBox.BackgroundColor3 = Color3I(0,0,0)
+ shortDescriptionBox.BorderColor3 = Color3I(206,206,206)
+ shortDescriptionBox.ZIndex = baseZIndex + 2
+ shortDescriptionBox.Parent = shortDescriptionWrapper
+
+ submitReportButton = Instance.new("TextButton")
+ submitReportButton.Name = "SubmitReportBtn"
+ submitReportButton.Active = false
+ submitReportButton.Modal = true
+ submitReportButton.Font = Enum.Font.SourceSans
+ submitReportButton.FontSize = Enum.FontSize.Size18
+ submitReportButton.Position = UDim2.new(0.1, 0, 1, -55)
+ submitReportButton.Size = UDim2.new(0.35,0,0,50)
+ submitReportButton.AutoButtonColor = true
+ submitReportButton.Style = Enum.ButtonStyle.RobloxButtonDefault
+ submitReportButton.Text = "Submit Report"
+ submitReportButton.TextColor3 = Color3I(255,255,255)
+ submitReportButton.ZIndex = baseZIndex + 2
+ submitReportButton.Parent = settingsFrame
+
+ submitReportButton.MouseButton1Click:connect(function()
+ if submitReportButton.Active then
+ if abuse and abusingPlayer then
+ frame.Visible = false
+ if gameOrPlayer == "Player" then
+ game.Players:ReportAbuse(abusingPlayer, abuse, shortDescriptionBox.Text)
+ else
+ game.Players:ReportAbuse(nil, abuse, shortDescriptionBox.Text)
+ end
+ if abuse == "Cheating/Exploiting" then
+ recordedMessageBox.Visible = true
+ elseif abuse == "Bullying" or abuse == "Swearing" then
+ calmingMessageBox.Visible = true
+ else
+ normalMessageBox.Visible = true
+ end
+ else
+ closeAndResetDialog()
+ end
+ end
+ end)
+
+ local cancelButton = Instance.new("TextButton")
+ cancelButton.Name = "CancelBtn"
+ cancelButton.Font = Enum.Font.SourceSans
+ cancelButton.FontSize = Enum.FontSize.Size18
+ cancelButton.Position = UDim2.new(0.55, 0, 1, -55)
+ cancelButton.Size = UDim2.new(0.35,0,0,50)
+ cancelButton.AutoButtonColor = true
+ cancelButton.Style = Enum.ButtonStyle.RobloxButtonDefault
+ cancelButton.Text = "Cancel"
+ cancelButton.TextColor3 = Color3I(255,255,255)
+ cancelButton.ZIndex = baseZIndex + 2
+ cancelButton.Parent = settingsFrame
+
+ closeAndResetDialog = function()
+ --Delete old player combo box
+ local oldComboBox = settingsFrame:FindFirstChild("PlayersComboBox")
+ if oldComboBox then
+ oldComboBox.Parent = nil
+ end
+
+ abusingPlayer = nil updatePlayerSelection(nil)
+ abuse = nil updateAbuseSelection(nil)
+ submitReportButton.Active = false
+ shortDescriptionBox.Text = ""
+ frame.Visible = true
+ calmingMessageBox.Visible = false
+ recordedMessageBox.Visible = false
+ normalMessageBox.Visible = false
+ shield.Visible = false
+ reportAbuseButton.Active = true
+ game.GuiService:RemoveCenterDialog(shield)
+ end
+
+ cancelButton.MouseButton1Click:connect(closeAndResetDialog)
+
+ reportAbuseButton.MouseButton1Click:connect(
+ function()
+ createPlayersDropDown().Parent = settingsFrame
+ table.insert(centerDialogs,shield)
+ game.GuiService:AddCenterDialog(shield, Enum.CenterDialogType.ModalDialog,
+ --ShowFunction
+ function()
+ reportAbuseButton.Active = false
+ shield.Visible = true
+ mainShield.Visible = false
+ end,
+ --HideFunction
+ function()
+ reportAbuseButton.Active = true
+ shield.Visible = false
+ end)
+ end)
+
+ robloxLock(shield)
+ return shield
+end
+
+local createChatBar = function()
+ --Only show a chat bar if we are a NetworkClient
+ waitForChild(game, "NetworkClient")
+
+ waitForChild(game, "Players")
+ waitForProperty(game.Players, "LocalPlayer")
+
+ local chatBar = Instance.new("Frame")
+ chatBar.Name = "ChatBar"
+ chatBar.Size = UDim2.new(1, 0, 0, 22)
+ chatBar.Position = UDim2.new(0, 0, 1, 0)
+ chatBar.BackgroundColor3 = Color3.new(0,0,0)
+ chatBar.BorderSizePixel = 0
+
+ local chatBox = Instance.new("TextBox")
+ chatBox.Text = ""
+ chatBox.Visible = false
+ chatBox.Size = UDim2.new(1,-4,1,0)
+ chatBox.Position = UDim2.new(0,2,0,0)
+ chatBox.TextXAlignment = Enum.TextXAlignment.Left
+ chatBox.Font = Enum.Font.Arial
+ chatBox.ClearTextOnFocus = false
+ chatBox.FontSize = Enum.FontSize.Size14
+ chatBox.TextColor3 = Color3.new(1,1,1)
+ chatBox.BackgroundTransparency = 1
+ --chatBox.Parent = chatBar
+
+ local chatButton = Instance.new("TextButton")
+ chatButton.Size = UDim2.new(1,-4,1,0)
+ chatButton.Position = UDim2.new(0,2,0,0)
+ chatButton.AutoButtonColor = false
+ chatButton.Text = "To chat click here or press \"/\" key"
+ chatButton.TextXAlignment = Enum.TextXAlignment.Left
+ chatButton.Font = Enum.Font.Arial
+ chatButton.FontSize = Enum.FontSize.Size14
+ chatButton.TextColor3 = Color3.new(1,1,1)
+ chatButton.BackgroundTransparency = 1
+ --chatButton.Parent = chatBar
+
+ local activateChat = function()
+ if chatBox.Visible then
+ return
+ end
+ chatButton.Visible = false
+ chatBox.Text = ""
+ chatBox.Visible = true
+ chatBox:CaptureFocus()
+ end
+
+ chatButton.MouseButton1Click:connect(activateChat)
+
+ local hotKeyEnabled = true
+ local toggleHotKey = function(value)
+ hotKeyEnabled = value
+ end
+
+ local guiService = game:GetService("GuiService")
+ local newChatMode = pcall(function()
+ --guiService:AddSpecialKey(Enum.SpecialKey.ChatHotkey)
+ --guiService.SpecialKeyPressed:connect(function(key) if key == Enum.SpecialKey.ChatHotkey and hotKeyEnabled then activateChat() end end)
+ end)
+ if not newChatMode then
+ --guiService:AddKey("/")
+ --guiService.KeyPressed:connect(function(key) if key == "/" and hotKeyEnabled then activateChat() end end)
+ end
+
+ chatBox.FocusLost:connect(
+ function(enterPressed)
+ if enterPressed then
+ if chatBox.Text ~= "" then
+ local str = chatBox.Text
+ if string.sub(str, 1, 1) == '%' then
+ game.Players:TeamChat(string.sub(str, 2))
+ else
+ game.Players:Chat(str)
+ end
+ end
+ end
+ chatBox.Text = ""
+ chatBox.Visible = false
+ chatButton.Visible = true
+ end)
+ robloxLock(chatBar)
+ return chatBar, toggleHotKey
+end
+
+--Spawn a thread for the Save dialogs
+local isSaveDialogSupported = pcall(function() local var = game.LocalSaveEnabled end)
+if isSaveDialogSupported then
+ delay(0,
+ function()
+ local saveDialogs = createSaveDialogs()
+ saveDialogs.Parent = gui
+
+ game.RequestShutdown = function()
+ table.insert(centerDialogs,saveDialogs)
+ game.GuiService:AddCenterDialog(saveDialogs, Enum.CenterDialogType.QuitDialog,
+ --ShowFunction
+ function()
+ saveDialogs.Visible = true
+ end,
+ --HideFunction
+ function()
+ saveDialogs.Visible = false
+ end)
+
+ return true
+ end
+ end)
+end
+
+--Spawn a thread for the Report Abuse dialogs
+delay(0,
+ function()
+ createReportAbuseDialog().Parent = gui
+ waitForChild(gui,"UserSettingsShield")
+ waitForChild(gui.UserSettingsShield, "Settings")
+ waitForChild(gui.UserSettingsShield.Settings,"SettingsStyle")
+ waitForChild(gui.UserSettingsShield.Settings.SettingsStyle,"GameMainMenu")
+ waitForChild(gui.UserSettingsShield.Settings.SettingsStyle.GameMainMenu, "ReportAbuseButton")
+ gui.UserSettingsShield.Settings.SettingsStyle.GameMainMenu.ReportAbuseButton.Active = true
+ end)
+
+end --LoadLibrary if
diff --git a/app/files/CoreGui/46295864 b/app/files/CoreGui/46295864
new file mode 100644
index 0000000..e5f52a5
--- /dev/null
+++ b/app/files/CoreGui/46295864
@@ -0,0 +1,2098 @@
+local function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+
+local function waitForProperty(instance, property)
+ while not instance[property] do
+ instance.Changed:wait()
+ end
+end
+
+-- A Few Script Globals
+local gui
+if script.Parent:FindFirstChild("ControlFrame") then
+ gui = script.Parent:FindFirstChild("ControlFrame")
+else
+ gui = script.Parent
+end
+
+local helpButton = nil
+local updateCameraDropDownSelection = nil
+local updateVideoCaptureDropDownSelection = nil
+local tweenTime = 0.2
+
+local mouseLockLookScreenUrl = "http://www.roblox.com/asset?id=54071825"
+local classicLookScreenUrl = "http://www.roblox.com/Asset?id=45915798"
+
+local hasGraphicsSlider = (game:GetService("CoreGui").Version >= 5)
+local GraphicsQualityLevels = 10 -- how many levels we allow on graphics slider
+local recordingVideo = false
+
+local currentMenuSelection = nil
+local lastMenuSelection = {}
+
+local defaultPosition = UDim2.new(0,0,0,0)
+local newGuiPlaces = {0,41324860}
+
+local centerDialogs = {}
+local mainShield = nil
+
+local inStudioMode = UserSettings().GameSettings:InStudioMode()
+
+local macClient = false
+local success, isMac = pcall(function() return not game.GuiService.IsWindows end)
+macClient = success and isMac
+
+local function Color3I(r,g,b)
+ return Color3.new(r/255,g/255,b/255)
+end
+
+local function robloxLock(instance)
+ instance.RobloxLocked = true
+ children = instance:GetChildren()
+ if children then
+ for i, child in ipairs(children) do
+ robloxLock(child)
+ end
+ end
+end
+
+function resumeGameFunction(shield)
+ shield.Settings:TweenPosition(UDim2.new(0.5, -262,-0.5, -200),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ delay(tweenTime,function()
+ shield.Visible = false
+ for i = 1, #centerDialogs do
+ centerDialogs[i].Visible = false
+ game.GuiService:RemoveCenterDialog(centerDialogs[i])
+ end
+ game.GuiService:RemoveCenterDialog(shield)
+ settingsButton.Active = true
+ currentMenuSelection = nil
+ lastMenuSelection = {}
+ end)
+end
+
+function goToMenu(container,menuName, moveDirection,size,position)
+ if type(menuName) ~= "string" then return end
+
+ table.insert(lastMenuSelection,currentMenuSelection)
+ if menuName == "GameMainMenu" then
+ lastMenuSelection = {}
+ end
+
+ local containerChildren = container:GetChildren()
+ local selectedMenu = false
+ for i = 1, #containerChildren do
+ if containerChildren[i].Name == menuName then
+ containerChildren[i].Visible = true
+ currentMenuSelection = {container = container,name = menuName, direction = moveDirection, lastSize = size}
+ selectedMenu = true
+ if size and position then
+ containerChildren[i]:TweenSizeAndPosition(size,position,Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ elseif size then
+ containerChildren[i]:TweenSizeAndPosition(size,UDim2.new(0.5,-size.X.Offset/2,0.5,-size.Y.Offset/2),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ else
+ containerChildren[i]:TweenPosition(UDim2.new(0,0,0,0),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ end
+ else
+ if moveDirection == "left" then
+ containerChildren[i]:TweenPosition(UDim2.new(-1,-525,0,0),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ elseif moveDirection == "right" then
+ containerChildren[i]:TweenPosition(UDim2.new(1,525,0,0),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ elseif moveDirection == "up" then
+ containerChildren[i]:TweenPosition(UDim2.new(0,0,-1,-400),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ elseif moveDirection == "down" then
+ containerChildren[i]:TweenPosition(UDim2.new(0,0,1,400),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ end
+ delay(tweenTime,function()
+ containerChildren[i].Visible = false
+ end)
+ end
+ end
+end
+
+function resetLocalCharacter()
+ local player = game.Players.LocalPlayer
+ if player then
+ if player.Character and player.Character:FindFirstChild("Humanoid") then
+ player.Character.Humanoid.Health = 0
+ end
+ end
+end
+
+local function createTextButton(text,style,fontSize,buttonSize,buttonPosition)
+ local newTextButton = Instance.new("TextButton")
+ newTextButton.Font = Enum.Font.Arial
+ newTextButton.FontSize = fontSize
+ newTextButton.Size = buttonSize
+ newTextButton.Position = buttonPosition
+ newTextButton.Style = style
+ newTextButton.TextColor3 = Color3.new(1,1,1)
+ newTextButton.Text = text
+ return newTextButton
+end
+
+local function CreateTextButtons(frame, buttons, yPos, ySize)
+ if #buttons < 1 then
+ error("Must have more than one button")
+ end
+
+ local buttonNum = 1
+ local buttonObjs = {}
+
+ local function toggleSelection(button)
+ for i, obj in ipairs(buttonObjs) do
+ if obj == button then
+ obj.Style = Enum.ButtonStyle.RobloxButtonDefault
+ else
+ obj.Style = Enum.ButtonStyle.RobloxButton
+ end
+ end
+ end
+
+ for i, obj in ipairs(buttons) do
+ local button = Instance.new("TextButton")
+ button.Name = "Button" .. buttonNum
+ button.Font = Enum.Font.Arial
+ button.FontSize = Enum.FontSize.Size18
+ button.AutoButtonColor = true
+ button.Style = Enum.ButtonStyle.RobloxButton
+ button.Text = obj.Text
+ button.TextColor3 = Color3.new(1,1,1)
+ button.MouseButton1Click:connect(function() toggleSelection(button) obj.Function() end)
+ button.Parent = frame
+ buttonObjs[buttonNum] = button
+
+ buttonNum = buttonNum + 1
+ end
+
+ toggleSelection(buttonObjs[1])
+
+ local numButtons = buttonNum-1
+
+ if numButtons == 1 then
+ frame.Button1.Position = UDim2.new(0.35, 0, yPos.Scale, yPos.Offset)
+ frame.Button1.Size = UDim2.new(.4,0,ySize.Scale, ySize.Offset)
+ elseif numButtons == 2 then
+ frame.Button1.Position = UDim2.new(0.1, 0, yPos.Scale, yPos.Offset)
+ frame.Button1.Size = UDim2.new(.35,0, ySize.Scale, ySize.Offset)
+
+ frame.Button2.Position = UDim2.new(0.55, 0, yPos.Scale, yPos.Offset)
+ frame.Button2.Size = UDim2.new(.35,0, ySize.Scale, ySize.Offset)
+ elseif numButtons >= 3 then
+ local spacing = .1 / numButtons
+ local buttonSize = .9 / numButtons
+
+ buttonNum = 1
+ while buttonNum <= numButtons do
+ buttonObjs[buttonNum].Position = UDim2.new(spacing*buttonNum + (buttonNum-1) * buttonSize, 0, yPos.Scale, yPos.Offset)
+ buttonObjs[buttonNum].Size = UDim2.new(buttonSize, 0, ySize.Scale, ySize.Offset)
+ buttonNum = buttonNum + 1
+ end
+ end
+end
+
+function setRecordGui(recording, stopRecordButton, recordVideoButton)
+ if recording then
+ stopRecordButton.Visible = true
+ recordVideoButton.Text = "Stop Recording"
+ else
+ stopRecordButton.Visible = false
+ recordVideoButton.Text = "Record Video"
+ end
+end
+
+function recordVideoClick(recordVideoButton, stopRecordButton)
+ recordingVideo = not recordingVideo
+ setRecordGui(recordingVideo, stopRecordButton, recordVideoButton)
+end
+
+function backToGame(buttonClicked, shield, settingsButton)
+ buttonClicked.Parent.Parent.Parent.Parent.Visible = false
+ shield.Visible = false
+ for i = 1, #centerDialogs do
+ game.GuiService:RemoveCenterDialog(centerDialogs[i])
+ centerDialogs[i].Visible = false
+ end
+ centerDialogs = {}
+ game.GuiService:RemoveCenterDialog(shield)
+ settingsButton.Active = true
+end
+
+function setDisabledState(guiObject)
+ if not guiObject then return end
+
+ if guiObject:IsA("TextLabel") then
+ guiObject.TextTransparency = 0.9
+ elseif guiObject:IsA("TextButton") then
+ guiObject.TextTransparency = 0.9
+ guiObject.Active = false
+ else
+ if guiObject["ClassName"] then
+ print("setDisabledState() got object of unsupported type. object type is ",guiObject.ClassName)
+ end
+ end
+end
+
+local function createHelpDialog(baseZIndex)
+
+ if helpButton == nil then
+ if gui:FindFirstChild("TopLeftControl") and gui.TopLeftControl:FindFirstChild("Help") then
+ helpButton = gui.TopLeftControl.Help
+ elseif gui:FindFirstChild("BottomRightControl") and gui.BottomRightControl:FindFirstChild("Help") then
+ helpButton = gui.BottomRightControl.Help
+ end
+ end
+
+ local shield = Instance.new("Frame")
+ shield.Name = "HelpDialogShield"
+ shield.Active = true
+ shield.Visible = false
+ shield.Size = UDim2.new(1,0,1,0)
+ shield.BackgroundColor3 = Color3I(51,51,51)
+ shield.BorderColor3 = Color3I(27,42,53)
+ shield.BackgroundTransparency = 0.4
+ shield.ZIndex = baseZIndex + 1
+
+ local helpDialog = Instance.new("Frame")
+ helpDialog.Name = "HelpDialog"
+ helpDialog.Style = Enum.FrameStyle.RobloxRound
+ helpDialog.Position = UDim2.new(.2, 0, .2, 0)
+ helpDialog.Size = UDim2.new(0.6, 0, 0.6, 0)
+ helpDialog.Active = true
+ helpDialog.Parent = shield
+
+ local titleLabel = Instance.new("TextLabel")
+ titleLabel.Name = "Title"
+ titleLabel.Text = "Keyboard & Mouse Controls"
+ titleLabel.Font = Enum.Font.ArialBold
+ titleLabel.FontSize = Enum.FontSize.Size36
+ titleLabel.Position = UDim2.new(0, 0, 0.025, 0)
+ titleLabel.Size = UDim2.new(1, 0, 0, 40)
+ titleLabel.TextColor3 = Color3.new(1,1,1)
+ titleLabel.BackgroundTransparency = 1
+ titleLabel.Parent = helpDialog
+
+ local buttonRow = Instance.new("Frame")
+ buttonRow.Name = "Buttons"
+ buttonRow.Position = UDim2.new(0.1, 0, .07, 40)
+ buttonRow.Size = UDim2.new(0.8, 0, 0, 45)
+ buttonRow.BackgroundTransparency = 1
+ buttonRow.Parent = helpDialog
+
+ local imageFrame = Instance.new("Frame")
+ imageFrame.Name = "ImageFrame"
+ imageFrame.Position = UDim2.new(0.05, 0, 0.075, 80)
+ imageFrame.Size = UDim2.new(0.9, 0, .9, -120)
+ imageFrame.BackgroundTransparency = 1
+ imageFrame.Parent = helpDialog
+
+ local layoutFrame = Instance.new("Frame")
+ layoutFrame.Name = "LayoutFrame"
+ layoutFrame.Position = UDim2.new(0.5, 0, 0, 0)
+ layoutFrame.Size = UDim2.new(1.5, 0, 1, 0)
+ layoutFrame.BackgroundTransparency = 1
+ layoutFrame.SizeConstraint = Enum.SizeConstraint.RelativeYY
+ layoutFrame.Parent = imageFrame
+
+ local image = Instance.new("ImageLabel")
+ image.Name = "Image"
+ if UserSettings().GameSettings.ControlMode == Enum.ControlMode["Mouse Lock Switch"] then
+ image.Image = mouseLockLookScreenUrl
+ else
+ image.Image = classicLookScreenUrl
+ end
+ image.Position = UDim2.new(-0.5, 0, 0, 0)
+ image.Size = UDim2.new(1, 0, 1, 0)
+ image.BackgroundTransparency = 1
+ image.Parent = layoutFrame
+
+ local buttons = {}
+ buttons[1] = {}
+ buttons[1].Text = "Look"
+ buttons[1].Function = function()
+ if UserSettings().GameSettings.ControlMode == Enum.ControlMode["Mouse Lock Switch"] then
+ image.Image = mouseLockLookScreenUrl
+ else
+ image.Image = classicLookScreenUrl
+ end
+ end
+ buttons[2] = {}
+ buttons[2].Text = "Move"
+ buttons[2].Function = function()
+ image.Image = "http://www.roblox.com/Asset?id=45915811"
+ end
+ buttons[3] = {}
+ buttons[3].Text = "Gear"
+ buttons[3].Function = function()
+ image.Image = "http://www.roblox.com/Asset?id=45917596"
+ end
+ buttons[4] = {}
+ buttons[4].Text = "Zoom"
+ buttons[4].Function = function()
+ image.Image = "http://www.roblox.com/Asset?id=45915825"
+ end
+
+ CreateTextButtons(buttonRow, buttons, UDim.new(0, 0), UDim.new(1,0))
+
+
+ -- set up listeners for type of mouse mode, but keep constructing gui at same time
+ delay(0, function()
+ waitForChild(gui,"UserSettingsShield")
+ waitForChild(gui.UserSettingsShield,"Settings")
+ waitForChild(gui.UserSettingsShield.Settings,"SettingsStyle")
+ waitForChild(gui.UserSettingsShield.Settings.SettingsStyle, "GameSettingsMenu")
+ waitForChild(gui.UserSettingsShield.Settings.SettingsStyle.GameSettingsMenu, "CameraField")
+ waitForChild(gui.UserSettingsShield.Settings.SettingsStyle.GameSettingsMenu.CameraField, "DropDownMenuButton")
+ gui.UserSettingsShield.Settings.SettingsStyle.GameSettingsMenu.CameraField.DropDownMenuButton.Changed:connect(function(prop)
+ if prop ~= "Text" then return end
+ if buttonRow.Button1.Style == Enum.ButtonStyle.RobloxButtonDefault then -- only change if this is the currently selected panel
+ if gui.UserSettingsShield.Settings.SettingsStyle.GameSettingsMenu.CameraField.DropDownMenuButton.Text == "Classic" then
+ image.Image = classicLookScreenUrl
+ else
+ image.Image = mouseLockLookScreenUrl
+ end
+ end
+ end)
+ end)
+
+
+ local okBtn = Instance.new("TextButton")
+ okBtn.Name = "OkBtn"
+ okBtn.Text = "OK"
+ okBtn.Modal = true
+ okBtn.Size = UDim2.new(0.3, 0, 0, 45)
+ okBtn.Position = UDim2.new(0.35, 0, .975, -50)
+ okBtn.Font = Enum.Font.Arial
+ okBtn.FontSize = Enum.FontSize.Size18
+ okBtn.BackgroundTransparency = 1
+ okBtn.TextColor3 = Color3.new(1,1,1)
+ okBtn.Style = Enum.ButtonStyle.RobloxButtonDefault
+ okBtn.MouseButton1Click:connect(
+ function()
+ shield.Visible = false
+ game.GuiService:RemoveCenterDialog(shield)
+ end)
+ okBtn.Parent = helpDialog
+
+ robloxLock(shield)
+ return shield
+end
+
+local function createLeaveConfirmationMenu(baseZIndex,shield)
+ local frame = Instance.new("Frame")
+ frame.Name = "LeaveConfirmationMenu"
+ frame.BackgroundTransparency = 1
+ frame.Size = UDim2.new(1,0,1,0)
+ frame.Position = UDim2.new(0,0,2,400)
+ frame.ZIndex = baseZIndex + 4
+
+ local yesButton = createTextButton("Leave",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size24,UDim2.new(0,128,0,50),UDim2.new(0,313,0.8,0))
+ yesButton.Name = "YesButton"
+ yesButton.ZIndex = baseZIndex + 4
+ yesButton.Parent = frame
+ yesButton.Modal = true
+ yesButton:SetVerb("Exit")
+
+ local noButton = createTextButton("Stay",Enum.ButtonStyle.RobloxButtonDefault,Enum.FontSize.Size24,UDim2.new(0,128,0,50),UDim2.new(0,90,0.8,0))
+ noButton.Name = "NoButton"
+ noButton.Parent = frame
+ noButton.ZIndex = baseZIndex + 4
+ noButton.MouseButton1Click:connect(function()
+ goToMenu(shield.Settings.SettingsStyle,"GameMainMenu","down",UDim2.new(0,525,0,430))
+ shield.Settings:TweenSize(UDim2.new(0,525,0,430),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ end)
+
+ local leaveText = Instance.new("TextLabel")
+ leaveText.Name = "LeaveText"
+ leaveText.Text = "Leave this game?"
+ leaveText.Size = UDim2.new(1,0,0.8,0)
+ leaveText.TextWrap = true
+ leaveText.TextColor3 = Color3.new(1,1,1)
+ leaveText.Font = Enum.Font.ArialBold
+ leaveText.FontSize = Enum.FontSize.Size36
+ leaveText.BackgroundTransparency = 1
+ leaveText.ZIndex = baseZIndex + 4
+ leaveText.Parent = frame
+
+ return frame
+end
+
+local function createResetConfirmationMenu(baseZIndex,shield)
+ local frame = Instance.new("Frame")
+ frame.Name = "ResetConfirmationMenu"
+ frame.BackgroundTransparency = 1
+ frame.Size = UDim2.new(1,0,1,0)
+ frame.Position = UDim2.new(0,0,2,400)
+ frame.ZIndex = baseZIndex + 4
+
+ local yesButton = createTextButton("Reset",Enum.ButtonStyle.RobloxButtonDefault,Enum.FontSize.Size24,UDim2.new(0,128,0,50),UDim2.new(0,313,0,299))
+ yesButton.Name = "YesButton"
+ yesButton.ZIndex = baseZIndex + 4
+ yesButton.Parent = frame
+ yesButton.Modal = true
+ yesButton.MouseButton1Click:connect(function()
+ resumeGameFunction(shield)
+ resetLocalCharacter()
+ end)
+
+ local noButton = createTextButton("Cancel",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size24,UDim2.new(0,128,0,50),UDim2.new(0,90,0,299))
+ noButton.Name = "NoButton"
+ noButton.Parent = frame
+ noButton.ZIndex = baseZIndex + 4
+ noButton.MouseButton1Click:connect(function()
+ goToMenu(shield.Settings.SettingsStyle,"GameMainMenu","down",UDim2.new(0,525,0,430))
+ shield.Settings:TweenSize(UDim2.new(0,525,0,430),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ end)
+
+ local resetCharacterText = Instance.new("TextLabel")
+ resetCharacterText.Name = "ResetCharacterText"
+ resetCharacterText.Text = "Are you sure you want to reset your character?"
+ resetCharacterText.Size = UDim2.new(1,0,0.8,0)
+ resetCharacterText.TextWrap = true
+ resetCharacterText.TextColor3 = Color3.new(1,1,1)
+ resetCharacterText.Font = Enum.Font.ArialBold
+ resetCharacterText.FontSize = Enum.FontSize.Size36
+ resetCharacterText.BackgroundTransparency = 1
+ resetCharacterText.ZIndex = baseZIndex + 4
+ resetCharacterText.Parent = frame
+
+ local fineResetCharacterText = resetCharacterText:Clone()
+ fineResetCharacterText.Name = "FineResetCharacterText"
+ fineResetCharacterText.Text = "You will be put back on a spawn point"
+ fineResetCharacterText.Size = UDim2.new(0,303,0,18)
+ fineResetCharacterText.Position = UDim2.new(0, 109, 0, 215)
+ fineResetCharacterText.FontSize = Enum.FontSize.Size18
+ fineResetCharacterText.Parent = frame
+
+ return frame
+end
+
+local function createGameMainMenu(baseZIndex, shield)
+ local gameMainMenuFrame = Instance.new("Frame")
+ gameMainMenuFrame.Name = "GameMainMenu"
+ gameMainMenuFrame.BackgroundTransparency = 1
+ gameMainMenuFrame.Size = UDim2.new(1,0,1,0)
+ gameMainMenuFrame.ZIndex = baseZIndex + 4
+ gameMainMenuFrame.Parent = settingsFrame
+
+ -- GameMainMenu Children
+
+ local gameMainMenuTitle = Instance.new("TextLabel")
+ gameMainMenuTitle.Name = "Title"
+ gameMainMenuTitle.Text = "Game Menu"
+ gameMainMenuTitle.BackgroundTransparency = 1
+ gameMainMenuTitle.TextStrokeTransparency = 0
+ gameMainMenuTitle.Font = Enum.Font.ArialBold
+ gameMainMenuTitle.FontSize = Enum.FontSize.Size36
+ gameMainMenuTitle.Size = UDim2.new(1,0,0,36)
+ gameMainMenuTitle.Position = UDim2.new(0,0,0,4)
+ gameMainMenuTitle.TextColor3 = Color3.new(1,1,1)
+ gameMainMenuTitle.ZIndex = baseZIndex + 4
+ gameMainMenuTitle.Parent = gameMainMenuFrame
+
+ local robloxHelpButton = createTextButton("Help",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size18,UDim2.new(0,164,0,50),UDim2.new(0,82,0,256))
+ robloxHelpButton.Name = "HelpButton"
+ robloxHelpButton.ZIndex = baseZIndex + 4
+ robloxHelpButton.Parent = gameMainMenuFrame
+ helpButton = robloxHelpButton
+
+ local helpDialog = createHelpDialog(baseZIndex)
+ helpDialog.Parent = gui
+
+ helpButton.MouseButton1Click:connect(
+ function()
+ table.insert(centerDialogs,helpDialog)
+ game.GuiService:AddCenterDialog(helpDialog, Enum.CenterDialogType.ModalDialog,
+ --ShowFunction
+ function()
+ helpDialog.Visible = true
+ mainShield.Visible = false
+ end,
+ --HideFunction
+ function()
+ helpDialog.Visible = false
+ end)
+ end)
+ helpButton.Active = true
+
+ local helpShortcut = Instance.new("TextLabel")
+ helpShortcut.Name = "HelpShortcutText"
+ helpShortcut.Text = "F1"
+ helpShortcut.Visible = false
+ helpShortcut.BackgroundTransparency = 1
+ helpShortcut.Font = Enum.Font.Arial
+ helpShortcut.FontSize = Enum.FontSize.Size12
+ helpShortcut.Position = UDim2.new(0,85,0,0)
+ helpShortcut.Size = UDim2.new(0,30,0,30)
+ helpShortcut.TextColor3 = Color3.new(0,1,0)
+ helpShortcut.ZIndex = baseZIndex + 4
+ helpShortcut.Parent = robloxHelpButton
+
+ local screenshotButton = createTextButton("Screenshot",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size18,UDim2.new(0,168,0,50),UDim2.new(0,254,0,256))
+ screenshotButton.Name = "ScreenshotButton"
+ screenshotButton.ZIndex = baseZIndex + 4
+ screenshotButton.Parent = gameMainMenuFrame
+ screenshotButton.Visible = not macClient
+ screenshotButton:SetVerb("Screenshot")
+
+ local screenshotShortcut = helpShortcut:clone()
+ screenshotShortcut.Name = "ScreenshotShortcutText"
+ screenshotShortcut.Text = "PrintSc"
+ screenshotShortcut.Position = UDim2.new(0,118,0,0)
+ screenshotShortcut.Visible = true
+ screenshotShortcut.Parent = screenshotButton
+
+
+ local recordVideoButton = createTextButton("Record Video",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size18,UDim2.new(0,168,0,50),UDim2.new(0,254,0,306))
+ recordVideoButton.Name = "RecordVideoButton"
+ recordVideoButton.ZIndex = baseZIndex + 4
+ recordVideoButton.Parent = gameMainMenuFrame
+ recordVideoButton.Visible = not macClient
+ recordVideoButton:SetVerb("RecordToggle")
+
+ local recordVideoShortcut = helpShortcut:clone()
+ recordVideoShortcut.Visible = hasGraphicsSlider
+ recordVideoShortcut.Name = "RecordVideoShortcutText"
+ recordVideoShortcut.Text = "F12"
+ recordVideoShortcut.Position = UDim2.new(0,120,0,0)
+ recordVideoShortcut.Parent = recordVideoButton
+
+ local stopRecordButton = Instance.new("ImageButton")
+ stopRecordButton.Name = "StopRecordButton"
+ stopRecordButton.BackgroundTransparency = 1
+ stopRecordButton.Image = "rbxasset://textures/ui/RecordStop.png"
+ stopRecordButton.Size = UDim2.new(0,59,0,27)
+ stopRecordButton:SetVerb("RecordToggle")
+
+ stopRecordButton.MouseButton1Click:connect(function() recordVideoClick(recordVideoButton, stopRecordButton) end)
+ stopRecordButton.Visible = false
+ stopRecordButton.Parent = gui
+
+ local reportAbuseButton = createTextButton("Report Abuse",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size18,UDim2.new(0,164,0,50),UDim2.new(0,82,0,306))
+ reportAbuseButton.Name = "ReportAbuseButton"
+ reportAbuseButton.ZIndex = baseZIndex + 4
+ reportAbuseButton.Parent = gameMainMenuFrame
+
+ local leaveGameButton = createTextButton("Leave Game",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size24,UDim2.new(0,340,0,50),UDim2.new(0,82,0,358))
+ leaveGameButton.Name = "LeaveGameButton"
+ leaveGameButton.ZIndex = baseZIndex + 4
+ leaveGameButton.Parent = gameMainMenuFrame
+
+ local resumeGameButton = createTextButton("Resume Game",Enum.ButtonStyle.RobloxButtonDefault,Enum.FontSize.Size24,UDim2.new(0,340,0,50),UDim2.new(0,82,0,54))
+ resumeGameButton.Name = "resumeGameButton"
+ resumeGameButton.ZIndex = baseZIndex + 4
+ resumeGameButton.Parent = gameMainMenuFrame
+ resumeGameButton.Modal = true
+ resumeGameButton.MouseButton1Click:connect(function() resumeGameFunction(shield) end)
+
+ local gameSettingsButton = createTextButton("Game Settings",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size24,UDim2.new(0,340,0,50),UDim2.new(0,82,0,156))
+ gameSettingsButton.Name = "SettingsButton"
+ gameSettingsButton.ZIndex = baseZIndex + 4
+ gameSettingsButton.Parent = gameMainMenuFrame
+
+ if game:FindFirstChild("LoadingGuiService") and #game.LoadingGuiService:GetChildren() > 0 then
+ local gameSettingsButton = createTextButton("Game Instructions",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size24,UDim2.new(0,340,0,50),UDim2.new(0,82,0,207))
+ gameSettingsButton.Name = "GameInstructions"
+ gameSettingsButton.ZIndex = baseZIndex + 4
+ gameSettingsButton.Parent = gameMainMenuFrame
+ gameSettingsButton.MouseButton1Click:connect(function()
+ if game:FindFirstChild("Players") and game.Players["LocalPlayer"] then
+ local loadingGui = game.Players.LocalPlayer:FindFirstChild("PlayerLoadingGui")
+ if loadingGui then
+ loadingGui.Visible = true
+ end
+ end
+ end)
+ end
+
+ local resetButton = createTextButton("Reset Character",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size24,UDim2.new(0,340,0,50),UDim2.new(0,82,0,105))
+ resetButton.Name = "ResetButton"
+ resetButton.ZIndex = baseZIndex + 4
+ resetButton.Parent = gameMainMenuFrame
+
+ return gameMainMenuFrame
+end
+
+local function createGameSettingsMenu(baseZIndex, shield)
+ local gameSettingsMenuFrame = Instance.new("Frame")
+ gameSettingsMenuFrame.Name = "GameSettingsMenu"
+ gameSettingsMenuFrame.BackgroundTransparency = 1
+ gameSettingsMenuFrame.Size = UDim2.new(1,0,1,0)
+ gameSettingsMenuFrame.ZIndex = baseZIndex + 4
+
+ local title = Instance.new("TextLabel")
+ title.Name = "Title"
+ title.Text = "Settings"
+ title.Size = UDim2.new(1,0,0,48)
+ title.Position = UDim2.new(0,9,0,-9)
+ title.Font = Enum.Font.ArialBold
+ title.FontSize = Enum.FontSize.Size36
+ title.TextColor3 = Color3.new(1,1,1)
+ title.ZIndex = baseZIndex + 4
+ title.BackgroundTransparency = 1
+ title.Parent = gameSettingsMenuFrame
+
+ local fullscreenText = Instance.new("TextLabel")
+ fullscreenText.Name = "FullscreenText"
+ fullscreenText.Text = "Fullscreen Mode"
+ fullscreenText.Size = UDim2.new(0,124,0,18)
+ fullscreenText.Position = UDim2.new(0,62,0,145)
+ fullscreenText.Font = Enum.Font.Arial
+ fullscreenText.FontSize = Enum.FontSize.Size18
+ fullscreenText.TextColor3 = Color3.new(1,1,1)
+ fullscreenText.ZIndex = baseZIndex + 4
+ fullscreenText.BackgroundTransparency = 1
+ fullscreenText.Parent = gameSettingsMenuFrame
+
+ local fullscreenShortcut = Instance.new("TextLabel")
+ fullscreenShortcut.Visible = hasGraphicsSlider
+ fullscreenShortcut.Name = "FullscreenShortcutText"
+ fullscreenShortcut.Text = "F11"
+ fullscreenShortcut.BackgroundTransparency = 1
+ fullscreenShortcut.Font = Enum.Font.Arial
+ fullscreenShortcut.FontSize = Enum.FontSize.Size12
+ fullscreenShortcut.Position = UDim2.new(0,186,0,141)
+ fullscreenShortcut.Size = UDim2.new(0,30,0,30)
+ fullscreenShortcut.TextColor3 = Color3.new(0,1,0)
+ fullscreenShortcut.ZIndex = baseZIndex + 4
+ fullscreenShortcut.Parent = gameSettingsMenuFrame
+
+ local studioText = Instance.new("TextLabel")
+ studioText.Visible = false
+ studioText.Name = "StudioText"
+ studioText.Text = "Studio Mode"
+ studioText.Size = UDim2.new(0,95,0,18)
+ studioText.Position = UDim2.new(0,62,0,179)
+ studioText.Font = Enum.Font.Arial
+ studioText.FontSize = Enum.FontSize.Size18
+ studioText.TextColor3 = Color3.new(1,1,1)
+ studioText.ZIndex = baseZIndex + 4
+ studioText.BackgroundTransparency = 1
+ studioText.Parent = gameSettingsMenuFrame
+
+ local studioShortcut = fullscreenShortcut:clone()
+ studioShortcut.Name = "StudioShortcutText"
+ studioShortcut.Visible = false -- TODO: turn back on when f2 hack is fixed
+ studioShortcut.Text = "F2"
+ studioShortcut.Position = UDim2.new(0,154,0,175)
+ studioShortcut.Parent = gameSettingsMenuFrame
+
+ local studioCheckbox = nil
+
+ if hasGraphicsSlider then
+ local qualityText = Instance.new("TextLabel")
+ qualityText.Name = "QualityText"
+ qualityText.Text = "Graphics Quality"
+ qualityText.Size = UDim2.new(0,128,0,18)
+ qualityText.Position = UDim2.new(0,30,0,239)
+ qualityText.Font = Enum.Font.Arial
+ qualityText.FontSize = Enum.FontSize.Size18
+ qualityText.TextColor3 = Color3.new(1,1,1)
+ qualityText.ZIndex = baseZIndex + 4
+ qualityText.BackgroundTransparency = 1
+ qualityText.Parent = gameSettingsMenuFrame
+ qualityText.Visible = not inStudioMode
+
+ local autoText = qualityText:clone()
+ autoText.Name = "AutoText"
+ autoText.Text = "Auto"
+ autoText.Position = UDim2.new(0,183,0,214)
+ autoText.TextColor3 = Color3.new(128/255,128/255,128/255)
+ autoText.Size = UDim2.new(0,34,0,18)
+ autoText.Parent = gameSettingsMenuFrame
+ autoText.Visible = not inStudioMode
+
+ local fasterText = autoText:clone()
+ fasterText.Name = "FasterText"
+ fasterText.Text = "Faster"
+ fasterText.Position = UDim2.new(0,185,0,274)
+ fasterText.TextColor3 = Color3.new(95,95,95)
+ fasterText.FontSize = Enum.FontSize.Size14
+ fasterText.Parent = gameSettingsMenuFrame
+ fasterText.Visible = not inStudioMode
+
+ local fasterShortcut = fullscreenShortcut:clone()
+ fasterShortcut.Name = "FasterShortcutText"
+ fasterShortcut.Text = "F10 + Shift"
+ fasterShortcut.Position = UDim2.new(0,185,0,283)
+ fasterShortcut.Parent = gameSettingsMenuFrame
+ fasterShortcut.Visible = not inStudioMode
+
+ local betterQualityText = autoText:clone()
+ betterQualityText.Name = "BetterQualityText"
+ betterQualityText.Text = "Better Quality"
+ betterQualityText.TextWrap = true
+ betterQualityText.Size = UDim2.new(0,41,0,28)
+ betterQualityText.Position = UDim2.new(0,390,0,269)
+ betterQualityText.TextColor3 = Color3.new(95,95,95)
+ betterQualityText.FontSize = Enum.FontSize.Size14
+ betterQualityText.Parent = gameSettingsMenuFrame
+ betterQualityText.Visible = not inStudioMode
+
+ local betterQualityShortcut = fullscreenShortcut:clone()
+ betterQualityShortcut.Name = "BetterQualityShortcut"
+ betterQualityShortcut.Text = "F10"
+ betterQualityShortcut.Position = UDim2.new(0,394,0,288)
+ betterQualityShortcut.Parent = gameSettingsMenuFrame
+ betterQualityShortcut.Visible = not inStudioMode
+
+ local autoGraphicsButton = createTextButton("X",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size18,UDim2.new(0,25,0,25),UDim2.new(0,187,0,239))
+ autoGraphicsButton.Name = "AutoGraphicsButton"
+ autoGraphicsButton.ZIndex = baseZIndex + 4
+ autoGraphicsButton.Parent = gameSettingsMenuFrame
+ autoGraphicsButton.Visible = not inStudioMode
+
+ local graphicsSlider, graphicsLevel = RbxGui.CreateSlider(GraphicsQualityLevels,150,UDim2.new(0, 230, 0, 280)) -- graphics - 1 because slider starts at 1 instead of 0
+ graphicsSlider.Parent = gameSettingsMenuFrame
+ graphicsSlider.Bar.ZIndex = baseZIndex + 4
+ graphicsSlider.Bar.Slider.ZIndex = baseZIndex + 5
+ graphicsSlider.Visible = not inStudioMode
+ graphicsLevel.Value = math.floor((settings().Rendering:GetMaxQualityLevel() - 1)/2)
+
+ local graphicsSetter = Instance.new("TextBox")
+ graphicsSetter.Name = "GraphicsSetter"
+ graphicsSetter.BackgroundColor3 = Color3.new(0,0,0)
+ graphicsSetter.BorderColor3 = Color3.new(128/255,128/255,128/255)
+ graphicsSetter.Size = UDim2.new(0,50,0,25)
+ graphicsSetter.Position = UDim2.new(0,450,0,269)
+ graphicsSetter.TextColor3 = Color3.new(1,1,1)
+ graphicsSetter.Font = Enum.Font.Arial
+ graphicsSetter.FontSize = Enum.FontSize.Size18
+ graphicsSetter.Text = "Auto"
+ graphicsSetter.ZIndex = 1
+ graphicsSetter.TextWrap = true
+ graphicsSetter.Parent = gameSettingsMenuFrame
+ graphicsSetter.Visible = not inStudioMode
+
+ local isAutoGraphics = true
+ if not inStudioMode then
+ isAutoGraphics = (UserSettings().GameSettings.SavedQualityLevel == Enum.SavedQualitySetting.Automatic)
+ else
+ settings().Rendering.EnableFRM = false
+ end
+
+ local listenToGraphicsLevelChange = true
+
+ local function setAutoGraphicsGui(active)
+ isAutoGraphics = active
+ if active then
+ autoGraphicsButton.Text = "X"
+ betterQualityText.ZIndex = 1
+ betterQualityShortcut.ZIndex = 1
+ fasterShortcut.ZIndex = 1
+ fasterText.ZIndex = 1
+ graphicsSlider.Bar.ZIndex = 1
+ graphicsSlider.Bar.Slider.ZIndex = 1
+ graphicsSetter.ZIndex = 1
+ graphicsSetter.Text = "Auto"
+ else
+ autoGraphicsButton.Text = ""
+ graphicsSlider.Bar.ZIndex = baseZIndex + 4
+ graphicsSlider.Bar.Slider.ZIndex = baseZIndex + 5
+ betterQualityShortcut.ZIndex = baseZIndex + 4
+ fasterShortcut.ZIndex = baseZIndex + 4
+ betterQualityText.ZIndex = baseZIndex + 4
+ fasterText.ZIndex = baseZIndex + 4
+ graphicsSetter.ZIndex = baseZIndex + 4
+ end
+ end
+
+ local function goToAutoGraphics()
+ setAutoGraphicsGui(true)
+
+ UserSettings().GameSettings.SavedQualityLevel = Enum.SavedQualitySetting.Automatic
+
+ settings().Rendering.QualityLevel = Enum.QualityLevel.Automatic
+ end
+
+ local function setGraphicsQualityLevel(newLevel)
+ local percentage = newLevel/GraphicsQualityLevels
+ local newSetting = math.floor((settings().Rendering:GetMaxQualityLevel() - 1) * percentage)
+ if newSetting == 20 then -- Level 20 is the same as level 21, except it doesn't render ambient occlusion
+ newSetting = 21
+ elseif newLevel == 1 then -- make sure we can go to lowest settings (for terrible computers)
+ newSetting = 1
+ elseif newSetting > settings().Rendering:GetMaxQualityLevel() then
+ newSetting = settings().Rendering:GetMaxQualityLevel() - 1
+ end
+
+ UserSettings().GameSettings.SavedQualityLevel = newLevel
+ settings().Rendering.QualityLevel = newSetting
+ end
+
+ local function goToManualGraphics(explicitLevel)
+ setAutoGraphicsGui(false)
+
+ if explicitLevel then
+ graphicsLevel.Value = explicitLevel
+ else
+ graphicsLevel.Value = math.floor((settings().Rendering.AutoFRMLevel/(settings().Rendering:GetMaxQualityLevel() - 1)) * GraphicsQualityLevels)
+ end
+
+ if explicitLevel == graphicsLevel.Value then -- make sure we are actually in right graphics mode
+ setGraphicsQualityLevel(graphicsLevel.Value)
+ end
+
+ if not explicitLevel then
+ UserSettings().GameSettings.SavedQualityLevel = graphicsLevel.Value
+ end
+ graphicsSetter.Text = tostring(graphicsLevel.Value)
+ end
+
+ local function showAutoGraphics()
+ autoText.ZIndex = baseZIndex + 4
+ autoGraphicsButton.ZIndex = baseZIndex + 4
+ end
+
+ local function hideAutoGraphics()
+ autoText.ZIndex = 1
+ autoGraphicsButton.ZIndex = 1
+ end
+
+ local function showManualGraphics()
+ graphicsSlider.Bar.ZIndex = baseZIndex + 4
+ graphicsSlider.Bar.Slider.ZIndex = baseZIndex + 5
+ betterQualityShortcut.ZIndex = baseZIndex + 4
+ fasterShortcut.ZIndex = baseZIndex + 4
+ betterQualityText.ZIndex = baseZIndex + 4
+ fasterText.ZIndex = baseZIndex + 4
+ graphicsSetter.ZIndex = baseZIndex + 4
+ end
+
+ local function hideManualGraphics()
+ betterQualityText.ZIndex = 1
+ betterQualityShortcut.ZIndex = 1
+ fasterShortcut.ZIndex = 1
+ fasterText.ZIndex = 1
+ graphicsSlider.Bar.ZIndex = 1
+ graphicsSlider.Bar.Slider.ZIndex = 1
+ graphicsSetter.ZIndex = 1
+ end
+
+ local function translateSavedQualityLevelToInt(savedQualityLevel)
+ if savedQualityLevel == Enum.SavedQualitySetting.Automatic then
+ return 0
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel1 then
+ return 1
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel2 then
+ return 2
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel3 then
+ return 3
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel4 then
+ return 4
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel5 then
+ return 5
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel6 then
+ return 6
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel7 then
+ return 7
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel8 then
+ return 8
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel9 then
+ return 9
+ elseif savedQualityLevel == Enum.SavedQualitySetting.QualityLevel10 then
+ return 10
+ end
+ end
+
+ local function enableGraphicsWidget()
+ settings().Rendering.EnableFRM = true
+
+ isAutoGraphics = (UserSettings().GameSettings.SavedQualityLevel == Enum.SavedQualitySetting.Automatic)
+ if isAutoGraphics then
+ showAutoGraphics()
+ goToAutoGraphics()
+ else
+ showAutoGraphics()
+ showManualGraphics()
+ goToManualGraphics(translateSavedQualityLevelToInt(UserSettings().GameSettings.SavedQualityLevel))
+ end
+ end
+
+ local function disableGraphicsWidget()
+ hideManualGraphics()
+ hideAutoGraphics()
+ settings().Rendering.EnableFRM = false
+ end
+
+ graphicsSetter.FocusLost:connect(function()
+ if isAutoGraphics then
+ graphicsSetter.Text = tostring(graphicsLevel.Value)
+ return
+ end
+
+ local newGraphicsValue = tonumber(graphicsSetter.Text)
+ if newGraphicsValue == nil then
+ graphicsSetter.Text = tostring(graphicsLevel.Value)
+ return
+ end
+
+ if newGraphicsValue < 1 then newGraphicsValue = 1
+ elseif newGraphicsValue >= settings().Rendering:GetMaxQualityLevel() then
+ newGraphicsValue = settings().Rendering:GetMaxQualityLevel() - 1
+ end
+
+ graphicsLevel.Value = newGraphicsValue
+ setGraphicsQualityLevel(graphicsLevel.Value)
+ graphicsSetter.Text = tostring(graphicsLevel.Value)
+ end)
+
+ graphicsLevel.Changed:connect(function(prop)
+ if isAutoGraphics then return end
+ if not listenToGraphicsLevelChange then return end
+
+ graphicsSetter.Text = tostring(graphicsLevel.Value)
+ setGraphicsQualityLevel(graphicsLevel.Value)
+ end)
+
+ -- setup our graphic mode on load
+ if inStudioMode or UserSettings().GameSettings.SavedQualityLevel == Enum.SavedQualitySetting.Automatic then
+ if inStudioMode then
+ settings().Rendering.EnableFRM = false
+ disableGraphicsWidget()
+ else
+ settings().Rendering.EnableFRM = true
+ goToAutoGraphics()
+ end
+ else
+ settings().Rendering.EnableFRM = true
+ goToManualGraphics(translateSavedQualityLevelToInt(UserSettings().GameSettings.SavedQualityLevel))
+ end
+
+ autoGraphicsButton.MouseButton1Click:connect(function()
+ if inStudioMode and not game.Players.LocalPlayer then return end
+
+ if not isAutoGraphics then
+ goToAutoGraphics()
+ else
+ goToManualGraphics(graphicsLevel.Value)
+ end
+ end)
+
+ local lastUpdate = nil
+ game.GraphicsQualityChangeRequest:connect(function(graphicsIncrease)
+ if isAutoGraphics then return end -- only can set graphics in manual mode
+
+ if graphicsIncrease then
+ if (graphicsLevel.Value + 1) > GraphicsQualityLevels then return end
+ graphicsLevel.Value = graphicsLevel.Value + 1
+ graphicsSetter.Text = tostring(graphicsLevel.Value)
+ setGraphicsQualityLevel(graphicsLevel.Value)
+
+ game:GetService("GuiService"):SendNotification("Graphics Quality",
+ "Increased to (" .. graphicsSetter.Text .. ")",
+ "",
+ 2,
+ function()
+ end)
+ else
+ if (graphicsLevel.Value - 1) <= 0 then return end
+ graphicsLevel.Value = graphicsLevel.Value - 1
+ graphicsSetter.Text = tostring(graphicsLevel.Value)
+ setGraphicsQualityLevel(graphicsLevel.Value)
+
+ game:GetService("GuiService"):SendNotification("Graphics Quality",
+ "Decreased to (" .. graphicsSetter.Text .. ")",
+ "",
+ 2,
+ function()
+ end)
+ end
+ end)
+
+ game.Players.PlayerAdded:connect(function(player)
+ if player == game.Players.LocalPlayer and inStudioMode then
+ enableGraphicsWidget()
+ end
+ end)
+ game.Players.PlayerRemoving:connect(function(player)
+ if player == game.Players.LocalPlayer and inStudioMode then
+ disableGraphicsWidget()
+ end
+ end)
+
+ studioCheckbox = createTextButton("",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size18,UDim2.new(0,25,0,25),UDim2.new(0,30,0,176))
+ studioCheckbox.Name = "StudioCheckbox"
+ studioCheckbox.ZIndex = baseZIndex + 4
+ --studioCheckbox.Parent = gameSettingsMenuFrame -- todo: enable when studio h4x aren't an issue anymore
+ studioCheckbox:SetVerb("TogglePlayMode")
+ studioCheckbox.Visible = false -- todo: enabled when studio h4x aren't an issue anymore
+
+ local wasManualGraphics = (settings().Rendering.QualityLevel ~= Enum.QualityLevel.Automatic)
+ if inStudioMode and not game.Players.LocalPlayer then
+ studioCheckbox.Text = "X"
+ disableGraphicsWidget()
+ elseif inStudioMode then
+ studioCheckbox.Text = "X"
+ enableGraphicsWidget()
+ end
+ if hasGraphicsSlider then
+ UserSettings().GameSettings.StudioModeChanged:connect(function(isStudioMode)
+ inStudioMode = isStudioMode
+ if isStudioMode then
+ wasManualGraphics = (settings().Rendering.QualityLevel ~= Enum.QualityLevel.Automatic)
+ goToAutoGraphics()
+ studioCheckbox.Text = "X"
+ autoGraphicsButton.ZIndex = 1
+ autoText.ZIndex = 1
+ else
+ if wasManualGraphics then
+ goToManualGraphics()
+ end
+ studioCheckbox.Text = ""
+ autoGraphicsButton.ZIndex = baseZIndex + 4
+ autoText.ZIndex = baseZIndex + 4
+ end
+ end)
+ else
+ studioCheckbox.MouseButton1Click:connect(function()
+ if not studioCheckbox.Active then return end
+
+ if studioCheckbox.Text == "" then
+ studioCheckbox.Text = "X"
+ else
+ studioCheckbox.Text = ""
+ end
+ end)
+ end
+ end
+
+ local fullscreenCheckbox = createTextButton("",Enum.ButtonStyle.RobloxButton,Enum.FontSize.Size18,UDim2.new(0,25,0,25),UDim2.new(0,30,0,144))
+ fullscreenCheckbox.Name = "FullscreenCheckbox"
+ fullscreenCheckbox.ZIndex = baseZIndex + 4
+ fullscreenCheckbox.Parent = gameSettingsMenuFrame
+ fullscreenCheckbox:SetVerb("ToggleFullScreen")
+ if UserSettings().GameSettings:InFullScreen() then fullscreenCheckbox.Text = "X" end
+ if hasGraphicsSlider then
+ UserSettings().GameSettings.FullscreenChanged:connect(function(isFullscreen)
+ if isFullscreen then
+ fullscreenCheckbox.Text = "X"
+ else
+ fullscreenCheckbox.Text = ""
+ end
+ end)
+ else
+ fullscreenCheckbox.MouseButton1Click:connect(function()
+ if fullscreenCheckbox.Text == "" then
+ fullscreenCheckbox.Text = "X"
+ else
+ fullscreenCheckbox.Text = ""
+ end
+ end)
+ end
+
+ if game:FindFirstChild("NetworkClient") then -- we are playing online
+ setDisabledState(studioText)
+ setDisabledState(studioShortcut)
+ setDisabledState(studioCheckbox)
+ end
+
+ local backButton
+ if hasGraphicsSlider then
+ backButton = createTextButton("OK",Enum.ButtonStyle.RobloxButtonDefault,Enum.FontSize.Size24,UDim2.new(0,180,0,50),UDim2.new(0,170,0,330))
+ backButton.Modal = true
+ else
+ backButton = createTextButton("OK",Enum.ButtonStyle.RobloxButtonDefault,Enum.FontSize.Size24,UDim2.new(0,180,0,50),UDim2.new(0,170,0,270))
+ backButton.Modal = true
+ end
+
+ backButton.Name = "BackButton"
+ backButton.ZIndex = baseZIndex + 4
+ backButton.Parent = gameSettingsMenuFrame
+
+ local syncVideoCaptureSetting = nil
+
+ if not macClient then
+ local videoCaptureLabel = Instance.new("TextLabel")
+ videoCaptureLabel.Name = "VideoCaptureLabel"
+ videoCaptureLabel.Text = "After Capturing Video"
+ videoCaptureLabel.Font = Enum.Font.Arial
+ videoCaptureLabel.FontSize = Enum.FontSize.Size18
+ videoCaptureLabel.Position = UDim2.new(0,32,0,100)
+ videoCaptureLabel.Size = UDim2.new(0,164,0,18)
+ videoCaptureLabel.BackgroundTransparency = 1
+ videoCaptureLabel.TextColor3 = Color3I(255,255,255)
+ videoCaptureLabel.TextXAlignment = Enum.TextXAlignment.Left
+ videoCaptureLabel.ZIndex = baseZIndex + 4
+ videoCaptureLabel.Parent = gameSettingsMenuFrame
+
+ local videoNames = {}
+ local videoNameToItem = {}
+ videoNames[1] = "Just Save to Disk"
+ videoNameToItem[videoNames[1]] = Enum.UploadSetting["Never"]
+ videoNames[2] = "Upload to YouTube"
+ videoNameToItem[videoNames[2]] = Enum.UploadSetting["Ask me first"]
+
+ local videoCaptureDropDown = nil
+ videoCaptureDropDown, updateVideoCaptureDropDownSelection = RbxGui.CreateDropDownMenu(videoNames,
+ function(text)
+ UserSettings().GameSettings.VideoUploadPromptBehavior = videoNameToItem[text]
+ end)
+ videoCaptureDropDown.Name = "VideoCaptureField"
+ videoCaptureDropDown.ZIndex = baseZIndex + 4
+ videoCaptureDropDown.DropDownMenuButton.ZIndex = baseZIndex + 4
+ videoCaptureDropDown.DropDownMenuButton.Icon.ZIndex = baseZIndex + 4
+ videoCaptureDropDown.Position = UDim2.new(0, 270, 0, 94)
+ videoCaptureDropDown.Size = UDim2.new(0,200,0,32)
+ videoCaptureDropDown.Parent = gameSettingsMenuFrame
+
+ syncVideoCaptureSetting = function()
+ if UserSettings().GameSettings.VideoUploadPromptBehavior == Enum.UploadSetting["Never"] then
+ updateVideoCaptureDropDownSelection(videoNames[1])
+ elseif UserSettings().GameSettings.VideoUploadPromptBehavior == Enum.UploadSetting["Ask me first"] then
+ updateVideoCaptureDropDownSelection(videoNames[2])
+ else
+ UserSettings().GameSettings.VideoUploadPromptBehavior = Enum.UploadSetting["Ask me first"]
+ updateVideoCaptureDropDownSelection(videoNames[2])
+ end
+ end
+ end
+
+ local cameraLabel = Instance.new("TextLabel")
+ cameraLabel.Name = "CameraLabel"
+ cameraLabel.Text = "Character & Camera Controls"
+ cameraLabel.Font = Enum.Font.Arial
+ cameraLabel.FontSize = Enum.FontSize.Size18
+ cameraLabel.Position = UDim2.new(0,31,0,58)
+ cameraLabel.Size = UDim2.new(0,224,0,18)
+ cameraLabel.TextColor3 = Color3I(255,255,255)
+ cameraLabel.TextXAlignment = Enum.TextXAlignment.Left
+ cameraLabel.BackgroundTransparency = 1
+ cameraLabel.ZIndex = baseZIndex + 4
+ cameraLabel.Parent = gameSettingsMenuFrame
+
+ local mouseLockLabel = game.CoreGui.RobloxGui:FindFirstChild("MouseLockLabel",true)
+
+ local enumItems = Enum.ControlMode:GetEnumItems()
+ local enumNames = {}
+ local enumNameToItem = {}
+ for i,obj in ipairs(enumItems) do
+ enumNames[i] = obj.Name
+ enumNameToItem[obj.Name] = obj
+ end
+
+ local cameraDropDown
+ cameraDropDown, updateCameraDropDownSelection = RbxGui.CreateDropDownMenu(enumNames,
+ function(text)
+ UserSettings().GameSettings.ControlMode = enumNameToItem[text]
+
+ pcall(function()
+ if mouseLockLabel and UserSettings().GameSettings.ControlMode == Enum.ControlMode["Mouse Lock Switch"] then
+ mouseLockLabel.Visible = true
+ elseif mouseLockLabel then
+ mouseLockLabel.Visible = false
+ end
+ end)
+ end)
+ cameraDropDown.Name = "CameraField"
+ cameraDropDown.ZIndex = baseZIndex + 4
+ cameraDropDown.DropDownMenuButton.ZIndex = baseZIndex + 4
+ cameraDropDown.DropDownMenuButton.Icon.ZIndex = baseZIndex + 4
+ cameraDropDown.Position = UDim2.new(0, 270, 0, 52)
+ cameraDropDown.Size = UDim2.new(0,200,0,32)
+ cameraDropDown.Parent = gameSettingsMenuFrame
+
+ return gameSettingsMenuFrame
+end
+
+if LoadLibrary then
+ RbxGui = LoadLibrary("RbxGui")
+ local baseZIndex = 0
+if UserSettings then
+
+ local createSettingsDialog = function()
+ waitForChild(gui,"BottomLeftControl")
+ settingsButton = gui.BottomLeftControl:FindFirstChild("SettingsButton")
+
+ if settingsButton == nil then
+ settingsButton = Instance.new("ImageButton")
+ settingsButton.Name = "SettingsButton"
+ settingsButton.Image = "rbxasset://textures/ui/SettingsButton.png"
+ settingsButton.BackgroundTransparency = 1
+ settingsButton.Active = false
+ settingsButton.Size = UDim2.new(0,54,0,46)
+ settingsButton.Position = UDim2.new(0,2,0,50)
+ settingsButton.Parent = gui.BottomLeftControl
+ end
+
+ local shield = Instance.new("TextButton")
+ shield.Text = ""
+ shield.Name = "UserSettingsShield"
+ shield.Active = true
+ shield.AutoButtonColor = false
+ shield.Visible = false
+ shield.Size = UDim2.new(1,0,1,0)
+ shield.BackgroundColor3 = Color3I(51,51,51)
+ shield.BorderColor3 = Color3I(27,42,53)
+ shield.BackgroundTransparency = 0.4
+ shield.ZIndex = baseZIndex + 2
+ mainShield = shield
+
+ local frame = Instance.new("Frame")
+ frame.Name = "Settings"
+ frame.Position = UDim2.new(0.5, -262, -0.5, -200)
+ frame.Size = UDim2.new(0, 525, 0, 430)
+ frame.BackgroundTransparency = 1
+ frame.Active = true
+ frame.Parent = shield
+
+ local settingsFrame = Instance.new("Frame")
+ settingsFrame.Name = "SettingsStyle"
+ settingsFrame.Size = UDim2.new(1, 0, 1, 0)
+ settingsFrame.Style = Enum.FrameStyle.RobloxRound
+ settingsFrame.Active = true
+ settingsFrame.ZIndex = baseZIndex + 3
+ settingsFrame.Parent = frame
+
+ local gameMainMenu = createGameMainMenu(baseZIndex, shield)
+ gameMainMenu.Parent = settingsFrame
+
+ gameMainMenu.ScreenshotButton.MouseButton1Click:connect(function()
+ backToGame(gameMainMenu.ScreenshotButton, shield, settingsButton)
+ end)
+
+ gameMainMenu.RecordVideoButton.MouseButton1Click:connect(function()
+ recordVideoClick(gameMainMenu.RecordVideoButton, gui.StopRecordButton)
+ backToGame(gameMainMenu.RecordVideoButton, shield, settingsButton)
+ end)
+
+ if settings():FindFirstChild("Game Options") then
+ pcall(function()
+ settings():FindFirstChild("Game Options").VideoRecordingChangeRequest:connect(function(recording)
+ recordingVideo = recording
+ setRecordGui(recording, gui.StopRecordButton, gameMainMenu.RecordVideoButton)
+ end)
+ end)
+ end
+
+ game.CoreGui.RobloxGui.Changed:connect(function(prop) -- We have stopped recording when we resize
+ if prop == "AbsoluteSize" and recordingVideo then
+ recordVideoClick(gameMainMenu.RecordVideoButton, gui.StopRecordButton)
+ end
+ end)
+
+ function localPlayerChange()
+ gameMainMenu.ResetButton.Visible = game.Players.LocalPlayer
+ if game.Players.LocalPlayer then
+ settings().Rendering.EnableFRM = true
+ elseif inStudioMode then
+ settings().Rendering.EnableFRM = false
+ end
+ end
+
+ gameMainMenu.ResetButton.Visible = game.Players.LocalPlayer
+ if game.Players.LocalPlayer ~= nil then
+ game.Players.LocalPlayer.Changed:connect(function()
+ localPlayerChange()
+ end)
+ else
+ delay(0,function()
+ waitForProperty(game.Players,"LocalPlayer")
+ gameMainMenu.ResetButton.Visible = game.Players.LocalPlayer
+ game.Players.LocalPlayer.Changed:connect(function()
+ localPlayerChange()
+ end)
+ end)
+ end
+
+ gameMainMenu.ReportAbuseButton.Visible = game:FindFirstChild("NetworkClient")
+ if not gameMainMenu.ReportAbuseButton.Visible then
+ game.ChildAdded:connect(function(child)
+ if child:IsA("NetworkClient") then
+ gameMainMenu.ReportAbuseButton.Visible = game:FindFirstChild("NetworkClient")
+ end
+ end)
+ end
+
+ gameMainMenu.ResetButton.MouseButton1Click:connect(function()
+ goToMenu(settingsFrame,"ResetConfirmationMenu","up",UDim2.new(0,525,0,370))
+ end)
+
+ gameMainMenu.LeaveGameButton.MouseButton1Click:connect(function()
+ goToMenu(settingsFrame,"LeaveConfirmationMenu","down",UDim2.new(0,525,0,300))
+ end)
+
+ if game.CoreGui.Version >= 4 then -- we can use escape!
+ game:GetService("GuiService").EscapeKeyPressed:connect(function()
+ if currentMenuSelection == nil then
+ game.GuiService:AddCenterDialog(shield, Enum.CenterDialogType.ModalDialog,
+ --showFunction
+ function()
+ settingsButton.Active = false
+ updateCameraDropDownSelection(UserSettings().GameSettings.ControlMode.Name)
+
+ if syncVideoCaptureSetting then
+ syncVideoCaptureSetting()
+ end
+
+ goToMenu(settingsFrame,"GameMainMenu","right",UDim2.new(0,525,0,430))
+ shield.Visible = true
+ shield.Active = true
+ settingsFrame.Parent:TweenPosition(UDim2.new(0.5, -262,0.5, -200),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ settingsFrame.Parent:TweenSize(UDim2.new(0,525,0,430),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ end,
+ --hideFunction
+ function()
+ settingsFrame.Parent:TweenPosition(UDim2.new(0.5, -262,-0.5, -200),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ settingsFrame.Parent:TweenSize(UDim2.new(0,525,0,430),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ shield.Visible = false
+ settingsButton.Active = true
+ end)
+ elseif #lastMenuSelection > 0 then
+ if #centerDialogs > 0 then
+ for i = 1, #centerDialogs do
+ game.GuiService:RemoveCenterDialog(centerDialogs[i])
+ centerDialogs[i].Visible = false
+ end
+ centerDialogs = {}
+ end
+
+ goToMenu(lastMenuSelection[#lastMenuSelection]["container"],lastMenuSelection[#lastMenuSelection]["name"],
+ lastMenuSelection[#lastMenuSelection]["direction"],lastMenuSelection[#lastMenuSelection]["lastSize"])
+
+ table.remove(lastMenuSelection,#lastMenuSelection)
+ if #lastMenuSelection == 1 then -- apparently lua can't reduce count to 0... T_T
+ lastMenuSelection = {}
+ end
+ else
+ resumeGameFunction(shield)
+ end
+ end)
+ end
+
+ local gameSettingsMenu = createGameSettingsMenu(baseZIndex, shield)
+ gameSettingsMenu.Visible = false
+ gameSettingsMenu.Parent = settingsFrame
+
+ gameMainMenu.SettingsButton.MouseButton1Click:connect(function()
+ goToMenu(settingsFrame,"GameSettingsMenu","left",UDim2.new(0,525,0,350))
+ end)
+
+ gameSettingsMenu.BackButton.MouseButton1Click:connect(function()
+ goToMenu(settingsFrame,"GameMainMenu","right",UDim2.new(0,525,0,430))
+ end)
+
+ local resetConfirmationWindow = createResetConfirmationMenu(baseZIndex, shield)
+ resetConfirmationWindow.Visible = false
+ resetConfirmationWindow.Parent = settingsFrame
+
+ local leaveConfirmationWindow = createLeaveConfirmationMenu(baseZIndex,shield)
+ leaveConfirmationWindow.Visible = false
+ leaveConfirmationWindow.Parent = settingsFrame
+
+ robloxLock(shield)
+
+ settingsButton.MouseButton1Click:connect(
+ function()
+ game.GuiService:AddCenterDialog(shield, Enum.CenterDialogType.ModalDialog,
+ --showFunction
+ function()
+ settingsButton.Active = false
+ updateCameraDropDownSelection(UserSettings().GameSettings.ControlMode.Name)
+
+ if syncVideoCaptureSetting then
+ syncVideoCaptureSetting()
+ end
+
+ goToMenu(settingsFrame,"GameMainMenu","right",UDim2.new(0,525,0,430))
+ shield.Visible = true
+ settingsFrame.Parent:TweenPosition(UDim2.new(0.5, -262,0.5, -200),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ settingsFrame.Parent:TweenSize(UDim2.new(0,525,0,430),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ end,
+ --hideFunction
+ function()
+ settingsFrame.Parent:TweenPosition(UDim2.new(0.5, -262,-0.5, -200),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ settingsFrame.Parent:TweenSize(UDim2.new(0,525,0,430),Enum.EasingDirection.InOut,Enum.EasingStyle.Sine,tweenTime,true)
+ shield.Visible = false
+ settingsButton.Active = true
+ end)
+ end)
+
+ return shield
+ end
+
+ delay(0, function()
+ createSettingsDialog().Parent = gui
+
+ gui.BottomLeftControl.SettingsButton.Active = true
+ gui.BottomLeftControl.SettingsButton.Position = UDim2.new(0,2,0,-2)
+
+ if mouseLockLabel and UserSettings().GameSettings.ControlMode == Enum.ControlMode["Mouse Lock Switch"] then
+ mouseLockLabel.Visible = true
+ elseif mouseLockLabel then
+ mouseLockLabel.Visible = false
+ end
+
+ -- our script has loaded, get rid of older buttons now
+ local leaveGameButton = gui.BottomLeftControl:FindFirstChild("Exit")
+ if leaveGameButton then leaveGameButton:Remove() end
+
+ local toolButton = gui.BottomLeftControl:FindFirstChild("ToolButton")
+ if toolButton then toolButton:Remove() end
+
+ local topLeft = gui:FindFirstChild("TopLeftControl")
+ if topLeft then topLeft:Remove() end
+
+ local toggle = gui.BottomLeftControl:FindFirstChild("TogglePlayMode")
+ if toggle then toggle:Remove() end
+
+ local bottomRightChildren = gui.BottomRightControl:GetChildren()
+ for i = 1, #bottomRightChildren do
+ if not string.find(bottomRightChildren[i].Name,"Camera") then
+ bottomRightChildren[i]:Remove()
+ end
+ end
+ end)
+
+end --UserSettings call
+
+local createSaveDialogs = function()
+ local shield = Instance.new("TextButton")
+ shield.Text = ""
+ shield.AutoButtonColor = false
+ shield.Name = "SaveDialogShield"
+ shield.Active = true
+ shield.Visible = false
+ shield.Size = UDim2.new(1,0,1,0)
+ shield.BackgroundColor3 = Color3I(51,51,51)
+ shield.BorderColor3 = Color3I(27,42,53)
+ shield.BackgroundTransparency = 0.4
+ shield.ZIndex = baseZIndex+1
+
+ local clearAndResetDialog
+ local save
+ local saveLocal
+ local dontSave
+ local cancel
+
+ local messageBoxButtons = {}
+ messageBoxButtons[1] = {}
+ messageBoxButtons[1].Text = "Save"
+ messageBoxButtons[1].Style = Enum.ButtonStyle.RobloxButtonDefault
+ messageBoxButtons[1].Function = function() save() end
+ messageBoxButtons[2] = {}
+ messageBoxButtons[2].Text = "Cancel"
+ messageBoxButtons[2].Function = function() cancel() end
+ messageBoxButtons[3] = {}
+ messageBoxButtons[3].Text = "Don't Save"
+ messageBoxButtons[3].Function = function() dontSave() end
+
+ local saveDialogMessageBox = RbxGui.CreateStyledMessageDialog("Unsaved Changes", "Save your changes to ROBLOX before leaving?", "Confirm", messageBoxButtons)
+ saveDialogMessageBox.Visible = true
+ saveDialogMessageBox.Parent = shield
+
+
+ local errorBoxButtons = {}
+
+ local buttonOffset = 1
+ if game.LocalSaveEnabled then
+ errorBoxButtons[buttonOffset] = {}
+ errorBoxButtons[buttonOffset].Text = "Save to Disk"
+ errorBoxButtons[buttonOffset].Function = function() saveLocal() end
+ buttonOffset = buttonOffset + 1
+ end
+ errorBoxButtons[buttonOffset] = {}
+ errorBoxButtons[buttonOffset].Text = "Keep Playing"
+ errorBoxButtons[buttonOffset].Function = function() cancel() end
+ errorBoxButtons[buttonOffset+1] = {}
+ errorBoxButtons[buttonOffset+1].Text = "Don't Save"
+ errorBoxButtons[buttonOffset+1].Function = function() dontSave() end
+
+ local errorDialogMessageBox = RbxGui.CreateStyledMessageDialog("Upload Failed", "Sorry, we could not save your changes to ROBLOX.", "Error", errorBoxButtons)
+ errorDialogMessageBox.Visible = false
+ errorDialogMessageBox.Parent = shield
+
+ local spinnerDialog = Instance.new("Frame")
+ spinnerDialog.Name = "SpinnerDialog"
+ spinnerDialog.Style = Enum.FrameStyle.RobloxRound
+ spinnerDialog.Size = UDim2.new(0, 350, 0, 150)
+ spinnerDialog.Position = UDim2.new(.5, -175, .5, -75)
+ spinnerDialog.Visible = false
+ spinnerDialog.Active = true
+ spinnerDialog.Parent = shield
+
+ local waitingLabel = Instance.new("TextLabel")
+ waitingLabel.Name = "WaitingLabel"
+ waitingLabel.Text = "Saving to ROBLOX..."
+ waitingLabel.Font = Enum.Font.ArialBold
+ waitingLabel.FontSize = Enum.FontSize.Size18
+ waitingLabel.Position = UDim2.new(0.5, 25, 0.5, 0)
+ waitingLabel.TextColor3 = Color3.new(1,1,1)
+ waitingLabel.Parent = spinnerDialog
+
+ local spinnerFrame = Instance.new("Frame")
+ spinnerFrame.Name = "Spinner"
+ spinnerFrame.Size = UDim2.new(0, 80, 0, 80)
+ spinnerFrame.Position = UDim2.new(0.5, -150, 0.5, -40)
+ spinnerFrame.BackgroundTransparency = 1
+ spinnerFrame.Parent = spinnerDialog
+
+ local spinnerIcons = {}
+ local spinnerNum = 1
+ while spinnerNum <= 8 do
+ local spinnerImage = Instance.new("ImageLabel")
+ spinnerImage.Name = "Spinner"..spinnerNum
+ spinnerImage.Size = UDim2.new(0, 16, 0, 16)
+ spinnerImage.Position = UDim2.new(.5+.3*math.cos(math.rad(45*spinnerNum)), -8, .5+.3*math.sin(math.rad(45*spinnerNum)), -8)
+ spinnerImage.BackgroundTransparency = 1
+ spinnerImage.Image = "http://www.roblox.com/Asset?id=45880710"
+ spinnerImage.Parent = spinnerFrame
+
+ spinnerIcons[spinnerNum] = spinnerImage
+ spinnerNum = spinnerNum + 1
+ end
+
+ save = function()
+ saveDialogMessageBox.Visible = false
+
+ --Show the spinner dialog
+ spinnerDialog.Visible = true
+ local spin = true
+ --Make it spin
+ delay(0, function()
+ local spinPos = 0
+ while spin do
+ local pos = 0
+
+ while pos < 8 do
+ if pos == spinPos or pos == ((spinPos+1)%8) then
+ spinnerIcons[pos+1].Image = "http://www.roblox.com/Asset?id=45880668"
+ else
+ spinnerIcons[pos+1].Image = "http://www.roblox.com/Asset?id=45880710"
+ end
+
+ pos = pos + 1
+ end
+ spinPos = (spinPos + 1) % 8
+ wait(0.2)
+ end
+ end)
+
+ --Do the save while the spinner is going, function will wait
+ local result = game:SaveToRoblox()
+ if not result then
+ --Try once more
+ result = game:SaveToRoblox()
+ end
+
+ --Hide the spinner dialog
+ spinnerDialog.Visible = false
+ --And cause the delay thread to stop
+ spin = false
+
+ --Now process the result
+ if result then
+ --Success, close
+ game:FinishShutdown(false)
+ clearAndResetDialog()
+ else
+ --Failure, show the second dialog prompt
+ errorDialogMessageBox.Visible = true
+ end
+ end
+
+ saveLocal = function()
+ errorDialogMessageBox.Visible = false
+ game:FinishShutdown(true)
+ clearAndResetDialog()
+ end
+
+ dontSave = function()
+ saveDialogMessageBox.Visible = false
+ errorDialogMessageBox.Visible = false
+ game:FinishShutdown(false)
+ clearAndResetDialog()
+ end
+ cancel = function()
+ saveDialogMessageBox.Visible = false
+ errorDialogMessageBox.Visible = false
+ clearAndResetDialog()
+ end
+
+ clearAndResetDialog = function()
+ saveDialogMessageBox.Visible = true
+ errorDialogMessageBox.Visible = false
+ spinnerDialog.Visible = false
+ shield.Visible = false
+ game.GuiService:RemoveCenterDialog(shield)
+ end
+
+ robloxLock(shield)
+ shield.Visible = false
+ return shield
+end
+
+local createReportAbuseDialog = function()
+ --Only show things if we are a NetworkClient
+ waitForChild(game,"NetworkClient")
+
+ waitForChild(game,"Players")
+ waitForProperty(game.Players, "LocalPlayer")
+ local localPlayer = game.Players.LocalPlayer
+
+ local reportAbuseButton
+ waitForChild(gui,"UserSettingsShield")
+ waitForChild(gui.UserSettingsShield, "Settings")
+ waitForChild(gui.UserSettingsShield.Settings,"SettingsStyle")
+ waitForChild(gui.UserSettingsShield.Settings.SettingsStyle,"GameMainMenu")
+ waitForChild(gui.UserSettingsShield.Settings.SettingsStyle.GameMainMenu, "ReportAbuseButton")
+ reportAbuseButton = gui.UserSettingsShield.Settings.SettingsStyle.GameMainMenu.ReportAbuseButton
+
+ local shield = Instance.new("TextButton")
+ shield.Name = "ReportAbuseShield"
+ shield.Text = ""
+ shield.AutoButtonColor = false
+ shield.Active = true
+ shield.Visible = false
+ shield.Size = UDim2.new(1,0,1,0)
+ shield.BackgroundColor3 = Color3I(51,51,51)
+ shield.BorderColor3 = Color3I(27,42,53)
+ shield.BackgroundTransparency = 0.4
+ shield.ZIndex = baseZIndex + 1
+
+ local closeAndResetDialgo
+
+ local messageBoxButtons = {}
+ messageBoxButtons[1] = {}
+ messageBoxButtons[1].Text = "Ok"
+ messageBoxButtons[1].Modal = true
+ messageBoxButtons[1].Function = function() closeAndResetDialog() end
+ local calmingMessageBox = RbxGui.CreateMessageDialog("Thanks for your report!", "Our moderators will review the chat logs and determine what happened. The other user is probably just trying to make you mad.\n\nIf anyone used swear words, inappropriate language, or threatened you in real life, please report them for Bad Words or Threats", messageBoxButtons)
+ calmingMessageBox.Visible = false
+ calmingMessageBox.Parent = shield
+
+ local normalMessageBox = RbxGui.CreateMessageDialog("Thanks for your report!", "Our moderators will review the chat logs and determine what happened.", messageBoxButtons)
+ normalMessageBox.Visible = false
+ normalMessageBox.Parent = shield
+
+ local frame = Instance.new("Frame")
+ frame.Name = "Settings"
+ frame.Position = UDim2.new(0.5, -250, 0.5, -200)
+ frame.Size = UDim2.new(0.0, 500, 0.0, 400)
+ frame.BackgroundTransparency = 1
+ frame.Active = true
+ frame.Parent = shield
+
+ local settingsFrame = Instance.new("Frame")
+ settingsFrame.Name = "ReportAbuseStyle"
+ settingsFrame.Size = UDim2.new(1, 0, 1, 0)
+ settingsFrame.Style = Enum.FrameStyle.RobloxRound
+ settingsFrame.Active = true
+ settingsFrame.ZIndex = baseZIndex + 1
+ settingsFrame.Parent = frame
+
+ local title = Instance.new("TextLabel")
+ title.Name = "Title"
+ title.Text = "Report Abuse"
+ title.TextColor3 = Color3I(221,221,221)
+ title.Position = UDim2.new(0.5, 0, 0, 30)
+ title.Font = Enum.Font.ArialBold
+ title.FontSize = Enum.FontSize.Size36
+ title.ZIndex = baseZIndex + 2
+ title.Parent = settingsFrame
+
+ local description = Instance.new("TextLabel")
+ description.Name = "Description"
+ description.Text = "This will send a complete report to a moderator. The moderator will review the chat log and take appropriate action."
+ description.TextColor3 = Color3I(221,221,221)
+ description.Position = UDim2.new(0, 0, 0, 55)
+ description.Size = UDim2.new(1, 0, 0, 40)
+ description.BackgroundTransparency = 1
+ description.Font = Enum.Font.Arial
+ description.FontSize = Enum.FontSize.Size18
+ description.TextWrap = true
+ description.ZIndex = baseZIndex + 2
+ description.TextXAlignment = Enum.TextXAlignment.Left
+ description.TextYAlignment = Enum.TextYAlignment.Top
+ description.Parent = settingsFrame
+
+ local playerLabel = Instance.new("TextLabel")
+ playerLabel.Name = "PlayerLabel"
+ playerLabel.Text = "Which player?"
+ playerLabel.BackgroundTransparency = 1
+ playerLabel.Font = Enum.Font.Arial
+ playerLabel.FontSize = Enum.FontSize.Size18
+ playerLabel.Position = UDim2.new(0.025,0,0,100)
+ playerLabel.Size = UDim2.new(0.4,0,0,36)
+ playerLabel.TextColor3 = Color3I(255,255,255)
+ playerLabel.TextXAlignment = Enum.TextXAlignment.Left
+ playerLabel.ZIndex = baseZIndex + 2
+ playerLabel.Parent = settingsFrame
+
+ local abusingPlayer = nil
+ local abuse = nil
+ local submitReportButton = nil
+
+ local updatePlayerSelection = nil
+ local createPlayersDropDown = function()
+ local players = game:GetService("Players")
+ local playerNames = {}
+ local nameToPlayer = {}
+ local children = players:GetChildren()
+ local pos = 1
+ if children then
+ for i, player in ipairs(children) do
+ if player:IsA("Player") and player ~= localPlayer then
+ playerNames[pos] = player.Name
+ nameToPlayer[player.Name] = player
+ pos = pos + 1
+ end
+ end
+ end
+ local playerDropDown = nil
+ playerDropDown, updatePlayerSelection = RbxGui.CreateDropDownMenu(playerNames,
+ function(playerName)
+ abusingPlayer = nameToPlayer[playerName]
+ if abuse and abusingPlayer then
+ submitReportButton.Active = true
+ end
+ end)
+ playerDropDown.Name = "PlayersComboBox"
+ playerDropDown.ZIndex = baseZIndex + 2
+ playerDropDown.Position = UDim2.new(.425, 0, 0, 102)
+ playerDropDown.Size = UDim2.new(.55,0,0,32)
+
+ return playerDropDown
+ end
+
+ local abuseLabel = Instance.new("TextLabel")
+ abuseLabel.Name = "AbuseLabel"
+ abuseLabel.Text = "What did they do?"
+ abuseLabel.Font = Enum.Font.Arial
+ abuseLabel.BackgroundTransparency = 1
+ abuseLabel.FontSize = Enum.FontSize.Size18
+ abuseLabel.Position = UDim2.new(0.025,0,0,140)
+ abuseLabel.Size = UDim2.new(0.4,0,0,36)
+ abuseLabel.TextColor3 = Color3I(255,255,255)
+ abuseLabel.TextXAlignment = Enum.TextXAlignment.Left
+ abuseLabel.ZIndex = baseZIndex + 2
+ abuseLabel.Parent = settingsFrame
+
+ local abuses = {"Bad Words or Threats","Bad Username","Talking about Dating","Account Trading or Sharing","Asking Personal Questions","Rude or Mean Behavior","False Reporting Me"}
+ local abuseDropDown, updateAbuseSelection = RbxGui.CreateDropDownMenu(abuses,
+ function(abuseText)
+ abuse = abuseText
+ if abuse and abusingPlayer then
+ submitReportButton.Active = true
+ end
+ end, true)
+ abuseDropDown.Name = "AbuseComboBox"
+ abuseDropDown.ZIndex = baseZIndex + 2
+ abuseDropDown.Position = UDim2.new(0.425, 0, 0, 142)
+ abuseDropDown.Size = UDim2.new(0.55,0,0,32)
+ abuseDropDown.Parent = settingsFrame
+
+ local shortDescriptionLabel = Instance.new("TextLabel")
+ shortDescriptionLabel.Name = "ShortDescriptionLabel"
+ shortDescriptionLabel.Text = "Short Description: (optional)"
+ shortDescriptionLabel.Font = Enum.Font.Arial
+ shortDescriptionLabel.FontSize = Enum.FontSize.Size18
+ shortDescriptionLabel.Position = UDim2.new(0.025,0,0,180)
+ shortDescriptionLabel.Size = UDim2.new(0.95,0,0,36)
+ shortDescriptionLabel.TextColor3 = Color3I(255,255,255)
+ shortDescriptionLabel.TextXAlignment = Enum.TextXAlignment.Left
+ shortDescriptionLabel.BackgroundTransparency = 1
+ shortDescriptionLabel.ZIndex = baseZIndex + 2
+ shortDescriptionLabel.Parent = settingsFrame
+
+ local shortDescriptionWrapper = Instance.new("Frame")
+ shortDescriptionWrapper.Name = "ShortDescriptionWrapper"
+ shortDescriptionWrapper.Position = UDim2.new(0.025,0,0,220)
+ shortDescriptionWrapper.Size = UDim2.new(0.95,0,1,-310)
+ shortDescriptionWrapper.BackgroundColor3 = Color3I(0,0,0)
+ shortDescriptionWrapper.BorderSizePixel = 0
+ shortDescriptionWrapper.ZIndex = baseZIndex + 2
+ shortDescriptionWrapper.Parent = settingsFrame
+
+ local shortDescriptionBox = Instance.new("TextBox")
+ shortDescriptionBox.Name = "TextBox"
+ shortDescriptionBox.Text = ""
+ shortDescriptionBox.ClearTextOnFocus = false
+ shortDescriptionBox.Font = Enum.Font.Arial
+ shortDescriptionBox.FontSize = Enum.FontSize.Size18
+ shortDescriptionBox.Position = UDim2.new(0,3,0,3)
+ shortDescriptionBox.Size = UDim2.new(1,-6,1,-6)
+ shortDescriptionBox.TextColor3 = Color3I(255,255,255)
+ shortDescriptionBox.TextXAlignment = Enum.TextXAlignment.Left
+ shortDescriptionBox.TextYAlignment = Enum.TextYAlignment.Top
+ shortDescriptionBox.TextWrap = true
+ shortDescriptionBox.BackgroundColor3 = Color3I(0,0,0)
+ shortDescriptionBox.BorderSizePixel = 0
+ shortDescriptionBox.ZIndex = baseZIndex + 2
+ shortDescriptionBox.Parent = shortDescriptionWrapper
+
+ submitReportButton = Instance.new("TextButton")
+ submitReportButton.Name = "SubmitReportBtn"
+ submitReportButton.Active = false
+ submitReportButton.Modal = true
+ submitReportButton.Font = Enum.Font.Arial
+ submitReportButton.FontSize = Enum.FontSize.Size18
+ submitReportButton.Position = UDim2.new(0.1, 0, 1, -80)
+ submitReportButton.Size = UDim2.new(0.35,0,0,50)
+ submitReportButton.AutoButtonColor = true
+ submitReportButton.Style = Enum.ButtonStyle.RobloxButtonDefault
+ submitReportButton.Text = "Submit Report"
+ submitReportButton.TextColor3 = Color3I(255,255,255)
+ submitReportButton.ZIndex = baseZIndex + 2
+ submitReportButton.Parent = settingsFrame
+
+ submitReportButton.MouseButton1Click:connect(function()
+ if submitReportButton.Active then
+ if abuse and abusingPlayer then
+ frame.Visible = false
+ game.Players:ReportAbuse(abusingPlayer, abuse, shortDescriptionBox.Text)
+ if abuse == "Rude or Mean Behavior" or abuse == "False Reporting Me" then
+ calmingMessageBox.Visible = true
+ else
+ normalMessageBox.Visible = true
+ end
+ else
+ closeAndResetDialog()
+ end
+ end
+ end)
+
+ local cancelButton = Instance.new("TextButton")
+ cancelButton.Name = "CancelBtn"
+ cancelButton.Font = Enum.Font.Arial
+ cancelButton.FontSize = Enum.FontSize.Size18
+ cancelButton.Position = UDim2.new(0.55, 0, 1, -80)
+ cancelButton.Size = UDim2.new(0.35,0,0,50)
+ cancelButton.AutoButtonColor = true
+ cancelButton.Style = Enum.ButtonStyle.RobloxButtonDefault
+ cancelButton.Text = "Cancel"
+ cancelButton.TextColor3 = Color3I(255,255,255)
+ cancelButton.ZIndex = baseZIndex + 2
+ cancelButton.Parent = settingsFrame
+
+ closeAndResetDialog = function()
+ --Delete old player combo box
+ local oldComboBox = settingsFrame:FindFirstChild("PlayersComboBox")
+ if oldComboBox then
+ oldComboBox.Parent = nil
+ end
+
+ abusingPlayer = nil updatePlayerSelection(nil)
+ abuse = nil updateAbuseSelection(nil)
+ submitReportButton.Active = false
+ shortDescriptionBox.Text = ""
+ frame.Visible = true
+ calmingMessageBox.Visible = false
+ normalMessageBox.Visible = false
+ shield.Visible = false
+ reportAbuseButton.Active = true
+ game.GuiService:RemoveCenterDialog(shield)
+ end
+
+ cancelButton.MouseButton1Click:connect(closeAndResetDialog)
+
+ reportAbuseButton.MouseButton1Click:connect(
+ function()
+ createPlayersDropDown().Parent = settingsFrame
+ table.insert(centerDialogs,shield)
+ game.GuiService:AddCenterDialog(shield, Enum.CenterDialogType.ModalDialog,
+ --ShowFunction
+ function()
+ reportAbuseButton.Active = false
+ shield.Visible = true
+ mainShield.Visible = false
+ end,
+ --HideFunction
+ function()
+ reportAbuseButton.Active = true
+ shield.Visible = false
+ end)
+ end)
+
+ robloxLock(shield)
+ return shield
+end
+
+local createChatBar = function()
+ --Only show a chat bar if we are a NetworkClient
+ waitForChild(game, "NetworkClient")
+
+ waitForChild(game, "Players")
+ waitForProperty(game.Players, "LocalPlayer")
+
+ local chatBar = Instance.new("Frame")
+ chatBar.Name = "ChatBar"
+ chatBar.Size = UDim2.new(1, 0, 0, 22)
+ chatBar.Position = UDim2.new(0, 0, 1, 0)
+ chatBar.BackgroundColor3 = Color3.new(0,0,0)
+ chatBar.BorderSizePixel = 0
+
+ local chatBox = Instance.new("TextBox")
+ chatBox.Text = ""
+ chatBox.Visible = false
+ chatBox.Size = UDim2.new(1,-4,1,0)
+ chatBox.Position = UDim2.new(0,2,0,0)
+ chatBox.TextXAlignment = Enum.TextXAlignment.Left
+ chatBox.Font = Enum.Font.Arial
+ chatBox.ClearTextOnFocus = false
+ chatBox.FontSize = Enum.FontSize.Size14
+ chatBox.TextColor3 = Color3.new(1,1,1)
+ chatBox.BackgroundTransparency = 1
+ --chatBox.Parent = chatBar
+
+ local chatButton = Instance.new("TextButton")
+ chatButton.Size = UDim2.new(1,-4,1,0)
+ chatButton.Position = UDim2.new(0,2,0,0)
+ chatButton.AutoButtonColor = false
+ chatButton.Text = "To chat click here or press \"/\" key"
+ chatButton.TextXAlignment = Enum.TextXAlignment.Left
+ chatButton.Font = Enum.Font.Arial
+ chatButton.FontSize = Enum.FontSize.Size14
+ chatButton.TextColor3 = Color3.new(1,1,1)
+ chatButton.BackgroundTransparency = 1
+ --chatButton.Parent = chatBar
+
+ local activateChat = function()
+ if chatBox.Visible then
+ return
+ end
+ chatButton.Visible = false
+ chatBox.Text = ""
+ chatBox.Visible = true
+ chatBox:CaptureFocus()
+ end
+
+ chatButton.MouseButton1Click:connect(activateChat)
+
+ local hotKeyEnabled = true
+ local toggleHotKey = function(value)
+ hotKeyEnabled = value
+ end
+
+ local guiService = game:GetService("GuiService")
+ local newChatMode = pcall(function()
+ --guiService:AddSpecialKey(Enum.SpecialKey.ChatHotkey)
+ --guiService.SpecialKeyPressed:connect(function(key) if key == Enum.SpecialKey.ChatHotkey and hotKeyEnabled then activateChat() end end)
+ end)
+ if not newChatMode then
+ --guiService:AddKey("/")
+ --guiService.KeyPressed:connect(function(key) if key == "/" and hotKeyEnabled then activateChat() end end)
+ end
+
+ chatBox.FocusLost:connect(
+ function(enterPressed)
+ if enterPressed then
+ if chatBox.Text ~= "" then
+ local str = chatBox.Text
+ if string.sub(str, 1, 1) == '%' then
+ game.Players:TeamChat(string.sub(str, 2))
+ else
+ game.Players:Chat(str)
+ end
+ end
+ end
+ chatBox.Text = ""
+ chatBox.Visible = false
+ chatButton.Visible = true
+ end)
+ robloxLock(chatBar)
+ return chatBar, toggleHotKey
+end
+
+--Spawn a thread for the Save dialogs
+local isSaveDialogSupported = pcall(function() local var = game.LocalSaveEnabled end)
+if isSaveDialogSupported then
+ delay(0,
+ function()
+ local saveDialogs = createSaveDialogs()
+ saveDialogs.Parent = gui
+
+ game.RequestShutdown = function()
+ table.insert(centerDialogs,saveDialogs)
+ game.GuiService:AddCenterDialog(saveDialogs, Enum.CenterDialogType.QuitDialog,
+ --ShowFunction
+ function()
+ saveDialogs.Visible = true
+ end,
+ --HideFunction
+ function()
+ saveDialogs.Visible = false
+ end)
+
+ return true
+ end
+ end)
+end
+
+--Spawn a thread for the Report Abuse dialogs
+delay(0,
+ function()
+ createReportAbuseDialog().Parent = gui
+ waitForChild(gui,"UserSettingsShield")
+ waitForChild(gui.UserSettingsShield, "Settings")
+ waitForChild(gui.UserSettingsShield.Settings,"SettingsStyle")
+ waitForChild(gui.UserSettingsShield.Settings.SettingsStyle,"GameMainMenu")
+ waitForChild(gui.UserSettingsShield.Settings.SettingsStyle.GameMainMenu, "ReportAbuseButton")
+ gui.UserSettingsShield.Settings.SettingsStyle.GameMainMenu.ReportAbuseButton.Active = true
+ end)
+
+--Spawn a thread for Chat Bar
+local coreGuiVersion = game.CoreGui.Version
+local success, luaChat = pcall(function() return game.GuiService.UseLuaChat end)
+if success and luaChat then
+
+ --[[delay(0,
+ function()
+ waitForChild(game, "Players")
+ waitForProperty(game.Players, "LocalPlayer")
+
+ local advancedChatBarSupported = game.Players.LocalPlayer.ChatMode
+ local chatBar, toggleHotKey = createChatBar()
+
+ [if advancedChatBarSupported then
+ local function toggleChatBar(chatMode)
+ if chatMode == Enum.ChatMode.Menu then
+ chatBar.Parent = nil
+ game.GuiService:SetGlobalSizeOffsetPixel(0,0)
+ toggleHotKey(false)
+ elseif chatMode == Enum.ChatMode.TextAndMenu then
+ --chatBar.Parent = gui
+ --game.GuiService:SetGlobalSizeOffsetPixel(0,-22)
+ toggleHotKey(true)
+ end
+ end
+ game.Players.LocalPlayer.Changed:connect(
+ function(prop)
+ if prop == "ChatMode" then
+ toggleChatBar(game.Players.LocalPlayer.ChatMode)
+ end
+ end)
+ toggleChatBar(game.Players.LocalPlayer.ChatMode)
+ else
+ --chatBar.Parent = gui
+ --game.GuiService:SetGlobalSizeOffsetPixel(0,-22)
+ end
+ end)]]
+end
+
+
+local BurningManPlaceID = 41324860
+-- TODO: remove click to walk completely if testing shows we don't need it
+-- Removes click to walk option from Burning Man
+delay(0,
+ function()
+ waitForChild(game,"NetworkClient")
+ waitForChild(game,"Players")
+ waitForProperty(game.Players, "LocalPlayer")
+ waitForProperty(game.Players.LocalPlayer, "Character")
+ waitForChild(game.Players.LocalPlayer.Character, "Humanoid")
+ waitForProperty(game, "PlaceId")
+
+ if game.PlaceId == BurningManPlaceID then
+ game.Players.LocalPlayer.Character.Humanoid:SetClickToWalkEnabled(false)
+ game.Players.LocalPlayer.CharacterAdded:connect(function(character)
+ waitForChild(character, "Humanoid")
+ character.Humanoid:SetClickToWalkEnabled(false)
+ end)
+ end
+ end)
+
+end --LoadLibrary if
diff --git a/app/files/CoreGui/48488235 b/app/files/CoreGui/48488235
new file mode 100644
index 0000000..8ef1b10
--- /dev/null
+++ b/app/files/CoreGui/48488235
@@ -0,0 +1,3074 @@
+--------------------
+-- Super Util
+--------------------
+
+local ADMINS =
+{
+ rbadam = 1,
+ adamintygum = 1,
+ androidtest = 1,
+ robloxfrenchie = 1,
+ jackssmirkingrevenge = 1,
+ lindapepita = 1,
+ vaiobot = 1,
+ goddessnoob = 1,
+ effward = 1,
+ blockhaak = 1,
+ drewbda = 1,
+ ['659223'] = 1,
+ tone = 1,
+ fasterbuilder19 = 1,
+ zeuxcg = 1,
+ concol2 = 1,
+ reesemcblox = 1,
+ jeditkacheff = 'http://www.syntax.eco/asset/?id=134032333',
+ whkm1980 = 1,
+ chiefjustus = 1,
+ ellissar = 1,
+ arbolito = 1,
+ noob007 = 1,
+ limon = 1,
+ cmed = 1,
+ hawkington = 1,
+ tabemono = 1,
+ autoconfig = 1,
+ brighteyes = 1,
+ monsterinc3d = 1,
+ mrdoombringer = 1,
+ isolatedevent = 1,
+ countonconnor = 1,
+ scubasomething = 1,
+ onlytwentycharacters = 1,
+ lordrugdumph = 1,
+ bellavour = 1,
+ ['david.baszucki'] = 1,
+ ibanez2189 = 1,
+ sorcus = 'http://www.syntax.eco/asset/?id=113059239',
+ deeana00 = 1,
+ thelorekt = 1,
+ niquemonster = 1,
+ thorasaur = 1,
+ ['mse6'] = 1,
+ corgiparade = 1,
+ varia = 1,
+ ['4runningwolves'] = 1,
+ pulmoesflor = 1,
+ olive71 = 1,
+ groundcontroll2 = 1,
+ gurukrish = 1,
+ countvelcro = 1,
+ iltalumi = 1,
+ juanjuan23 = 1,
+ ostrichsized = 1,
+ jackintheblox = 1,
+ slingshotjunkie = 1,
+ gordonrox24 = 1,
+ sharpnine = 1,
+ motornerve = 1,
+ motornerve = 1,
+ watchmedogood = 1,
+ jmargh = 1,
+ jaykorean = 1,
+ foyle = 1,
+ majortom4321 = 1,
+ shedletsky = 'http://www.syntax.eco/asset/?id=105897927',
+ supernovacaine = 1,
+ ffjosh = 1,
+ sickenedmonkey = 1,
+ doughtless = 1,
+ kbux = 1,
+ totallynothere = 1,
+ erzastar = 1,
+ keith = 1,
+ chro = 1,
+ solarcrane = 1,
+ glorioussalt = 1,
+ uristmcsparks = 1,
+ itolauren = 1,
+ malcomso = 1,
+ stickmasterluke = 1,
+ windlight13 = 1,
+ yumyumcheerios = 1,
+ stravant = 1,
+ byteme = 1,
+ imaginationsensation = 1,
+ ['matt.dusek'] = 1,
+ mcrtest = 1,
+ seranok = 1,
+ maxvee = 1,
+ coatp0cketninja = 1,
+ screenme = 1,
+ b1tsh1ft = 1,
+ totbl = 1,
+ aquabot8 = 1,
+ grossinger = 1,
+ merely = 1,
+ cdakkar = 1,
+ siekiera = 1,
+ robloxkidsaccount = 1,
+ flotsamthespork = 1,
+ soggoth = 1,
+ phil = 1,
+ orcasparkles = 1,
+ skullgoblin = 1,
+ rickrosstheb0ss = 1,
+ argonpirate = 1,
+ nobledragon = 1,
+ squidcod = 1,
+ raeglyn = 1,
+ robloxsai = 'http://www.syntax.eco/asset/?id=161078086',
+ briarroze = 1,
+ hawkeyebandit = 1,
+ dapperbuffalo = 1,
+ vukota = 1,
+ swiftstone = 1,
+ gemlocker = 1,
+ loopylens = 1,
+ tarabyte = 1,
+ timobius = 1,
+ tobotrobot = 1,
+ foster008 = 1,
+ twberg = 1,
+ darthvaden = 1,
+ khanovich = 1,
+ codewriter = 1,
+ vladthefirst = 1,
+ phaedre = 1,
+ gorroth = 1,
+ sphinxshen = 1,
+ jynj1984 = 1,
+ roboyz = 1,
+ zodiaczak = 1,
+}
+
+
+
+local Images = {
+ bottomDark = '94691904',
+ bottomLight = '94691940',
+ midDark = '94691980',
+ midLight = '94692025',
+ LargeDark = '96098866',
+ LargeLight = '96098920',
+ LargeHeader = '96097470',
+ NormalHeader = '94692054',
+ LargeBottom = '96397271',
+ NormalBottom = '94754966',
+ DarkBluePopupMid = '97114905',
+ LightBluePopupMid = '97114905',
+ DarkPopupMid = '97112126',
+ LightPopupMid = '97109338',
+ DarkBluePopupTop = '97114838',
+ DarkBluePopupBottom = '97114758',
+ DarkPopupBottom = '100869219',
+ LightPopupBottom = '97109175',
+}
+
+local BASE_TWEEN = .25
+
+local MOUSE_HOLD_TIME = .15
+local MOUSE_DRAG_DISTANCE = 15
+
+--[[
+ Generic object Create function, which I am using to create Gui's
+ Thanks to Stravant!
+--]]
+local Obj = {}
+function Obj.Create(guiType)
+ return function(data)
+ local obj = Instance.new(guiType)
+ for k, v in pairs(data) do
+ if type(k) == 'number' then
+ v.Parent = obj
+ else
+ obj[k] = v
+ end
+ end
+ return obj
+ end
+end
+
+--[[
+ makes a full sized background for a guiobject
+ @Args:
+ imgName asset name of image to fill background
+ @Return: background gui object
+--]]
+function MakeBackgroundGuiObj(imgName)
+ return Obj.Create'ImageLabel'
+ {
+ Name = 'Background',
+ BackgroundTransparency = 1,
+ Image = imgName,
+ Position = UDim2.new(0, 0, 0, 0),
+ Size = UDim2.new(1,0,1,0),
+ }
+end
+--[[ turns 255 integer color value to a color3 --]]
+function Color3I(r,g,b)
+ return Color3.new(r/255,g/255,b/255)
+end
+
+--[[
+ Gets correct icon for builder's club status to display by name
+ @Args:
+ membershipType Enum of membership status
+ @Return: string of image asset
+--]]
+function getMembershipTypeIcon(membershipType,player)
+ if player:IsInGroup(1200769) then
+ return "http://www.syntax.eco/asset/?id=99727663"
+ elseif membershipType == Enum.MembershipType.None then
+ return ""
+ elseif membershipType == Enum.MembershipType.BuildersClub then
+ return "rbxasset://textures/ui/TinyBcIcon.png"
+ elseif membershipType == Enum.MembershipType.TurboBuildersClub then
+ return "rbxasset://textures/ui/TinyTbcIcon.png"
+ elseif membershipType == Enum.MembershipType.OutrageousBuildersClub then
+ return "rbxasset://textures/ui/TinyObcIcon.png"
+ else
+ error("Unknown membershipType" .. membershipType)
+ end
+end
+
+local function getFriendStatusIcon(friendStatus)
+ if friendStatus == Enum.FriendStatus.Unknown or friendStatus == Enum.FriendStatus.NotFriend then
+ return ""
+ elseif friendStatus == Enum.FriendStatus.Friend then
+ return "http://www.syntax.eco/asset/?id=99749771"
+ elseif friendStatus == Enum.FriendStatus.FriendRequestSent then
+ return "http://www.syntax.eco/asset/?id=99776888"
+ elseif friendStatus == Enum.FriendStatus.FriendRequestReceived then
+ return "http://www.syntax.eco/asset/?id=99776838"
+ else
+ error("Unknown FriendStatus: " .. friendStatus)
+ end
+end
+
+
+--[[
+ Utility function to create buttons for the popup menus
+ @Args:
+ nparent what to parent this button to
+ ntext text to put on this button
+ index number index of this entry in menu
+ last is this the last element of the popup menu
+ @Return: a popup menu button
+--]]
+function MakePopupButton(nparent,ntext,index,last)
+ local tobj = Obj.Create"ImageButton"
+ {
+ Name = 'ReportButton',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0,0,1*index,0),
+ Size = UDim2.new(1, 0, 1, 0),
+ ZIndex=7,
+ Obj.Create'TextLabel'
+ {
+ Name = 'ButtonText',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(.07, 0, .07, 0),
+ Size = UDim2.new(.86,0,.86,0),
+ Parent = HeaderFrame,
+ Font = 'ArialBold',
+ Text = ntext,
+ FontSize = 'Size14',
+ TextScaled = true,
+ TextColor3 = Color3.new(1,1,1),
+ TextStrokeTransparency = 1,
+ ZIndex=7,
+ },
+ Parent = nparent,
+ }
+ if index==0 then
+ tobj.Image = 'http://www.syntax.eco/asset/?id=97108784'
+ elseif last then
+ if index%2==1 then
+ tobj.Image = 'http://www.syntax.eco/asset/?id='.. Images['LightPopupBottom']
+ else
+ tobj.Image = 'http://www.syntax.eco/asset/?id='.. Images['DarkPopupBottom']
+ end
+ else
+ if index%2==1 then
+ tobj.Image = 'http://www.syntax.eco/asset/?id=97112126'
+ else
+ tobj.Image = 'http://www.syntax.eco/asset/?id=97109338'
+ end
+ end
+ return tobj
+end
+
+
+--[[
+ obligatory wait for child function
+ @Args:
+ parent Parent object to look for child in
+ child name of child object to look for
+ @Return: object waited for
+--]]
+function WaitForChild(parent,child)
+ while not parent:FindFirstChild(child) do
+ wait() debugprint(" child "..parent.Name.." waiting for "..child)
+ end
+ return parent[child]
+end
+
+ ---------------------------
+ -- Workspace Objects
+ ---------------------------
+
+-- might want to move all this to an init function, wait for localplayer elsewhere
+local Players = game:GetService('Players')
+-- make sure this doesn't run on the server(it will if you dont do this)
+while not Players.LocalPlayer do
+ Players.Changed:wait()
+end
+
+local LocalPlayer = Players.LocalPlayer
+local Mouse = LocalPlayer:GetMouse()
+
+local ScreenGui = Obj.Create"Frame"
+{
+ Name = 'PlayerListScreen',
+ Size = UDim2.new(1, 0, 1, 0),
+ BackgroundTransparency = 1,
+ Parent = script.Parent
+}
+local MainFrame = Obj.Create"Frame"
+{
+ Name = 'LeaderBoardFrame',
+ Position = UDim2.new(1, -150, 0.005, 0),
+ Size = UDim2.new(0, 150, 0, 800),
+ BackgroundTransparency = 1,
+ Parent = ScreenGui,
+}
+
+--frame used for expanding leaderstats when frame is 'focused'
+local FocusFrame = Obj.Create"Frame"
+{
+ Name = 'FocusFrame',
+ Position = UDim2.new(0, 0, 0, 0),
+ Size = UDim2.new(1, 0, 0, 100),
+ BackgroundTransparency = 1,
+ Active = true,
+ Parent = MainFrame,
+}
+
+local TemplateFrameYSize = 0.670000017
+
+ -- HEADER
+local HeaderFrame = Obj.Create"Frame"
+{
+ Name = 'Header',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0,0,0,0),
+ Size = UDim2.new(1, 0, .07, 0),
+ Parent = MainFrame,
+ MakeBackgroundGuiObj('http://www.syntax.eco/asset/?id=94692054'),
+}
+ local HeaderFrameHeight = HeaderFrame.Size.Y.Scale
+ local MaximizeButton = Obj.Create"ImageButton"
+ {
+ Name = 'MaximizeButton',
+ Active = true,
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0, 0, 0, 0),
+ Size = UDim2.new(1,0,1,0),
+ Parent = HeaderFrame,
+ }
+ local HeaderName = Obj.Create"TextLabel"
+ {
+ Name = 'PlayerName',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0, 0, .01, 0),
+ Size = UDim2.new(.98,0,.38,0),
+ Parent = HeaderFrame,
+ Font = 'ArialBold',
+ Text = LocalPlayer.Name,
+ FontSize='Size24',
+ --TextScaled = true,
+ TextColor3 = Color3.new(1,1,1),
+ TextStrokeColor3 = Color3.new(0,0,0),
+ TextStrokeTransparency = 0,
+ TextXAlignment = 'Right',
+ TextYAlignment = 'Center',
+ }
+ local HeaderScore = Obj.Create"TextLabel"
+ {
+ Name = 'PlayerScore',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0, 0, .4, 0),
+ Size = UDim2.new(.98,0,0,30),
+ Parent = HeaderFrame,
+ Font = 'ArialBold',
+ Text = '',
+ FontSize='Size24',
+ TextYAlignment = 'Top',
+ --TextScaled = true,
+ TextColor3 = Color3.new(1,1,1),
+ TextStrokeTransparency = 1,
+ TextXAlignment = 'Right',
+ TextYAlignment = 'Top',
+ }
+ -- BOTTOM
+ --used for shifting bottom frame for mouse over effects
+local BottomShiftFrame = Obj.Create"Frame"
+{
+ Name= 'BottomShiftFrame',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0,0,HeaderFrameHeight,0),
+ Size = UDim2.new(1,0,1,0),
+ Parent=MainFrame,
+}
+ local BottomFrame = Obj.Create"Frame"
+ {
+ Name = 'Bottom',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0,0,.07,0),
+ Size = UDim2.new(1, 0, .03, 0),
+ Parent = BottomShiftFrame,
+ MakeBackgroundGuiObj('http://www.syntax.eco/asset/?id=94754966'),
+ }
+ local ExtendButton = Obj.Create"ImageButton"
+ {
+ Name = 'bigbutton',
+ Active = true,
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0, 0, 0, 0),
+ Size = UDim2.new(1,0,1.5,0),
+ ZIndex = 3,
+ Parent = BottomFrame,
+ }
+ local ExtendTab = Obj.Create"ImageButton"
+ {
+ Name = 'extendTab',
+ Active = true,
+ BackgroundTransparency = 1,
+ Image = 'http://www.syntax.eco/asset/?id=94692731',
+ Position = UDim2.new(.608, 0, .3, 0),
+ Size = UDim2.new(.3,0,.7,0),
+ Parent = BottomFrame,
+ }
+local TopClipFrame = Obj.Create"Frame"
+{
+ Name = 'ListFrame',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(-1,0,.07,0),
+ Size = UDim2.new(2, 0, 1, 0),
+ Parent = MainFrame,
+ ClipsDescendants = true,
+}
+ local BottomClipFrame = Obj.Create"Frame"
+ {
+ Name = 'BottomFrame',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0,0, - .8,0),
+ Size = UDim2.new(1, 0, 1, 0),
+ Parent = TopClipFrame,
+ ClipsDescendants = true,
+ }
+ local ScrollBarFrame = Obj.Create"Frame"
+ {
+ Name = 'ScrollBarFrame',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(.987,0,.8,0),
+ Size = UDim2.new(.01, 0, .2, 0),
+ Parent = BottomClipFrame,
+ }
+ local ScrollBar = Obj.Create"Frame"
+ {
+ Name = 'ScrollBar',
+ BackgroundTransparency = 0,
+ BackgroundColor3 = Color3.new(.2,.2,.2),
+ Position = UDim2.new(0,0,0,0),
+ Size = UDim2.new(1, 0, .5, 0),
+ ZIndex = 5,
+ Parent = ScrollBarFrame,
+
+ }
+ local ListFrame = Obj.Create"Frame"
+ {
+ Name = 'SubFrame',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0,0,.8,0),
+ Size = UDim2.new(1, 0, 1, 0),
+ Parent = BottomClipFrame,
+ }
+local PopUpClipFrame = Obj.Create"Frame"
+{
+ Name = 'PopUpFrame',
+ BackgroundTransparency = 1,
+ SizeConstraint='RelativeXX',
+ Position = MainFrame.Position + UDim2.new( 0,-150,0,0),
+ Size = UDim2.new(0,150,0,800),
+ Parent = MainFrame,
+ ClipsDescendants = true,
+ ZIndex=7,
+}
+local PopUpPanel = nil
+ local PopUpPanelTemplate = Obj.Create"Frame"
+ {
+ Name = 'Panel',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(1,0,0,0),
+ Size = UDim2.new(1,0,.032,0),
+ Parent = PopUpClipFrame,
+ }
+
+local StatTitles = Obj.Create"Frame"
+{
+ Name = 'StatTitles',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0,0,1,-10),
+ Size = UDim2.new(1, 0, 0, 0),
+ Parent = HeaderFrame,
+}
+
+local IsMinimized = Instance.new('BoolValue')
+local IsMaximized = Instance.new('BoolValue')
+local IsTabified = Instance.new('BoolValue')
+local AreNamesExpanded = Instance.new('BoolValue')
+
+
+local MiddleTemplate = Obj.Create"Frame"
+{
+ Name = 'MidTemplate',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(100,0,.07,0),
+ Size = UDim2.new(.5, 0, .025, 0),--UDim2.new(1, 0, .03, 0),
+ Obj.Create'ImageLabel'
+ {
+ Name = 'BCLabel',
+ Active = true,
+ BackgroundTransparency = 1,
+ Position = UDim2.new(.005, 5, .20, 0),
+ Size = UDim2.new(0,16,0,16),
+ SizeConstraint = 'RelativeYY',
+ Image = "",
+ ZIndex = 3,
+ },
+ Obj.Create'ImageLabel'
+ {
+ Name = 'FriendLabel',
+ Active = true,
+ BackgroundTransparency = 1,
+ Position = UDim2.new(.005, 5, .15, 0),
+ Size = UDim2.new(0,16,0,16),
+ SizeConstraint = 'RelativeYY',
+ Image = "",
+ ZIndex = 3,
+ },
+ Obj.Create"ImageButton"
+ {
+ Name = 'ClickListener',
+ Active = true,
+ BackgroundTransparency = 1,
+ Position = UDim2.new(.005, 1, 0, 0),
+ Size = UDim2.new(.96,0,1,0),
+ ZIndex = 3,
+ },
+ Obj.Create"Frame"
+ {
+ Name = 'TitleFrame',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(.01, 0, 0, 0),
+ Size = UDim2.new(0,140,1,0),
+ ClipsDescendants=true,
+ Obj.Create"TextLabel"
+ {
+ Name = 'Title',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0, 5, 0, 0),
+ Size = UDim2.new(100,0,1,0),
+ Font = 'Arial',
+ FontSize='Size14',
+ TextColor3 = Color3.new(1,1,1),
+ TextXAlignment = 'Left',
+ TextYAlignment = 'Center',
+ ZIndex = 3,
+ },
+ },
+
+ Obj.Create"TextLabel"
+ {
+ Name = 'PlayerScore',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0, 0, 0, 0),
+ Size = UDim2.new(1,0,1,0),
+ Font = 'ArialBold',
+ Text = '',
+ FontSize='Size14',
+ TextColor3 = Color3.new(1,1,1),
+ TextXAlignment = 'Right',
+ TextYAlignment = 'Center',
+ ZIndex = 3,
+ },
+ --Obj.Create'IntValue'{Name = 'ID'},
+ --Obj.Create'ObjectValue'{Name = 'Player'},
+ --Obj.Create'IntValue'{Name = 'Score'},
+ ZIndex = 3,
+}
+local MiddleBGTemplate = Obj.Create"Frame"
+{
+ Name = 'MidBGTemplate',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(100,0,.07,0),
+ Size = UDim2.new(.5, 0, .025, 0),--UDim2.new(1, 0, .03, 0),
+ MakeBackgroundGuiObj('http://www.syntax.eco/asset/?id=94692025'),
+
+}
+
+ -- REPORT ABUSE OBJECTS
+
+local ReportAbuseShield = Obj.Create"TextButton"
+{
+ Name = "ReportAbuseShield",
+ Text = "",
+ AutoButtonColor = false,
+ Active = true,
+ Visible = true,
+ Size = UDim2.new(1,0,1,0),
+ BackgroundColor3 = Color3I(51,51,51),
+ BorderColor3 = Color3I(27,42,53),
+ BackgroundTransparency = 1,
+}
+
+local ReportAbuseFrame = Obj.Create "Frame"
+{
+ Name = "Settings",
+ Position = UDim2.new(0.5, - 250, 0.5, - 200),
+ Size = UDim2.new(0.0, 500, 0.0, 400),
+ BackgroundTransparency = 1,
+ Active = true,
+ Parent = ReportAbuseShield,
+}
+
+local AbuseSettingsFrame = Obj.Create"Frame"
+{
+ Name = "ReportAbuseStyle",
+ Size = UDim2.new(1, 0, 1, 0),
+ Active = true,
+ BackgroundTransparency = 1,
+ MakeBackgroundGuiObj('http://www.syntax.eco/asset/?id=96488767'), -- 96480351'),
+ Obj.Create"TextLabel"
+ {
+ Name = "Title",
+ Text = "Report Abuse",
+ TextColor3 = Color3I(221,221,221),
+ Position = UDim2.new(0.5, 0, 0, 30),
+ Font = Enum.Font.ArialBold,
+ FontSize = Enum.FontSize.Size36,
+ },
+ Obj.Create"TextLabel"
+ {
+ Name = "Description",
+ Text = "This will send a complete report to a moderator. The moderator will review the chat log and take appropriate action.",
+ TextColor3 = Color3I(221,221,221),
+ Position = UDim2.new(.01, 0, 0, 55),
+ Size = UDim2.new(.99, 0, 0, 40),
+ BackgroundTransparency = 1,
+ Font = Enum.Font.Arial,
+ FontSize = Enum.FontSize.Size18,
+ TextWrap = true,
+ TextXAlignment = Enum.TextXAlignment.Left,
+ TextYAlignment = Enum.TextYAlignment.Top,
+ },
+ Obj.Create"TextLabel"
+ {
+ Name = "AbuseLabel",
+ Text = "What did they do?",
+ Font = Enum.Font.Arial,
+ BackgroundTransparency = 1,
+ FontSize = Enum.FontSize.Size18,
+ Position = UDim2.new(0.025,0,0,140),
+ Size = UDim2.new(0.4,0,0,36),
+ TextColor3 = Color3I(255,255,255),
+ TextXAlignment = Enum.TextXAlignment.Left,
+ },
+ Obj.Create"TextLabel"
+ {
+ Name = "ShortDescriptionLabel",
+ Text = "Short Description: (optional)",
+ Font = Enum.Font.Arial,
+ FontSize = Enum.FontSize.Size18,
+ Position = UDim2.new(0.025,0,0,180),
+ Size = UDim2.new(0.95,0,0,36),
+ TextColor3 = Color3I(255,255,255),
+ TextXAlignment = Enum.TextXAlignment.Left,
+ BackgroundTransparency = 1,
+ },
+ Obj.Create"TextLabel"
+ {
+ Name = "ReportingPlayerLabel",
+ Text = "Reporting Player",
+ BackgroundTransparency = 1,
+ Font = Enum.Font.Arial,
+ FontSize = Enum.FontSize.Size18,
+ Position = UDim2.new(0.025,0,0,100),
+ Size = UDim2.new(0.95,0,0,36),
+ TextColor3 = Color3I(255,255,255),
+ TextXAlignment = Enum.TextXAlignment.Left,
+ Parent = AbuseSettingsFrame
+ },
+
+ Parent = ReportAbuseFrame,
+}
+
+local AbusePlayerLabel = Obj.Create"TextLabel"
+{
+ Name = "PlayerLabel",
+ Text = "",
+ BackgroundTransparency = 1,
+ Font = Enum.Font.ArialBold,
+ FontSize = Enum.FontSize.Size18,
+ Position = UDim2.new(0.025,0,0,100),
+ Size = UDim2.new(0.95,0,0,36),
+ TextColor3 = Color3I(255,255,255),
+ TextXAlignment = Enum.TextXAlignment.Right,
+ Parent = AbuseSettingsFrame
+}
+
+local SubmitReportButton = Obj.Create"ImageButton"
+{
+ Name = "SubmitReportBtn",
+ Active = false,
+ BackgroundTransparency = 1,
+ Position = UDim2.new(.5, - 200, 1, - 80),
+ Size = UDim2.new(0,150,0,50),
+ AutoButtonColor = false,
+ Image = 'http://www.syntax.eco/asset/?id=96502438', -- 96501119',
+ Parent = AbuseSettingsFrame,
+}
+
+local CancelReportButton = Obj.Create"ImageButton"
+{
+ Name = "CancelBtn",
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0.5, 50, 1, - 80),
+ Size = UDim2.new(0,150,0,50),
+ AutoButtonColor = true,
+ Image = 'http://www.syntax.eco/asset/?id=96500683',
+ Parent = AbuseSettingsFrame,
+}
+
+local AbuseDescriptionWrapper = Obj.Create"Frame"
+{
+ Name = "AbuseDescriptionWrapper",
+ Position = UDim2.new(0.025,0,0,220),
+ Size = UDim2.new(0.95,0,1, - 310),
+ BackgroundColor3 = Color3I(0,0,0),
+ BorderSizePixel = 0,
+ Parent = AbuseSettingsFrame,
+}
+
+local AbuseDescriptionBox
+
+local OriginalAbuseDescriptionBox = Obj.Create"TextBox"
+{
+ Name = "TextBox",
+ Text = "",
+ ClearTextOnFocus = false,
+ Font = Enum.Font.Arial,
+ FontSize = Enum.FontSize.Size18,
+ Position = UDim2.new(0,3,0,3),
+ Size = UDim2.new(1, - 6,1, - 6),
+ TextColor3 = Color3I(255,255,255),
+ TextXAlignment = Enum.TextXAlignment.Left,
+ TextYAlignment = Enum.TextYAlignment.Top,
+ TextWrap = true,
+ BackgroundColor3 = Color3I(0,0,0),
+ BorderSizePixel = 0,
+}
+
+local CalmingAbuseBox = Obj.Create'Frame'
+{
+ Name = 'AbuseFeedbackBox',
+ BackgroundTransparency = 1,
+ Position=UDim2.new(0.25, 0, 0.300000012, 0),
+ Size=UDim2.new(0.5, 0, 0.370000005, 0),
+ MakeBackgroundGuiObj('http://www.syntax.eco/asset/?id=96506233'),
+ Obj.Create'TextLabel'
+ {
+ Name = 'Header',
+ Position=UDim2.new(0,10,.05,0),
+ Size=UDim2.new(1, -30, .15, 0),
+ TextScaled = true,
+ BackgroundTransparency = 1,
+ TextXAlignment = Enum.TextXAlignment.Center,
+ TextYAlignment = Enum.TextYAlignment.Top,
+ Text = 'Thanks for your report!',
+ TextColor3 = Color3.new(1,1,1),
+ FontSize = Enum.FontSize.Size48,
+ Font = 'ArialBold',
+ },
+ Obj.Create'TextLabel'
+ {
+ Name = 'content',
+ Position = UDim2.new(0,10,.20,0),
+ Size = UDim2.new(1, -30, .40, 0),
+ TextScaled = true,
+ BackgroundTransparency = 1,
+ TextColor3 = Color3.new(1,1,1),
+ Text = 'Our moderators will review the chat logs and determine what happened. The other user is probably just trying to make you mad.\n\nIf anyone used swear words, inappropriate language, or threatened you in real life, please report them for Bad Words or Threats',
+ TextWrapped = true,
+ TextYAlignment = Enum.TextYAlignment.Top,
+ FontSize = Enum.FontSize.Size24,
+ Font = 'Arial',
+ },
+ Obj.Create'ImageButton'
+ {
+ Name = "OkButton",
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0.5, -75, 1, -80),
+ Size = UDim2.new(0,150,0,50),
+ AutoButtonColor = true,
+ Image = 'http://www.syntax.eco/asset/?id=96507959',
+ }
+}
+local NormalAbuseBox = Obj.Create'Frame'
+{
+ Name = 'AbuseFeedbackBox',
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0.25, 0, 0.300000012, 0),
+ Size = UDim2.new(0.5, 0, 0.370000005, 0),
+ MakeBackgroundGuiObj('http://www.syntax.eco/asset/?id=96506233'),
+ Obj.Create'TextLabel'
+ {
+ Name = 'Header',
+ Position = UDim2.new(0,10,.05,0),
+ Size = UDim2.new(1, -30, .15, 0),
+ TextScaled = true,
+ BackgroundTransparency = 1,
+ TextColor3 = Color3.new(1,1,1),
+ TextXAlignment = Enum.TextXAlignment.Center,
+ TextYAlignment = Enum.TextYAlignment.Top,
+ Text = 'Thanks for your report!',
+ FontSize = Enum.FontSize.Size48,
+ Font ='ArialBold'
+ },
+ Obj.Create'TextLabel'
+ {
+ Name = 'content',
+ Position = UDim2.new(0,10,.20,0),
+ Size = UDim2.new(1, -30, .15, 0),
+ TextScaled = true,
+ BackgroundTransparency = 1,
+ TextColor3 = Color3.new(1,1,1),
+ Text = "Our moderators will review the chat logs and determine what happened.",
+ TextWrapped = true,
+ TextYAlignment = Enum.TextYAlignment.Top,
+ FontSize = Enum.FontSize.Size24,
+ Font = 'Arial',
+ },
+ Obj.Create'ImageButton'
+ {
+ Name = "OkButton",
+ BackgroundTransparency = 1,
+ Position = UDim2.new(0.5, -75, 1, -80),
+ Size = UDim2.new(0,150,0,50),
+ AutoButtonColor = true,
+ Image = 'http://www.syntax.eco/asset/?id=96507959',
+ },
+}
+
+local BigButton=Instance.new('ImageButton')
+ BigButton.Size=UDim2.new(1,0,1,0)
+ BigButton.BackgroundTransparency=1
+ BigButton.ZIndex=8
+ BigButton.Visible=false
+ --BigButton.Active=false
+ BigButton.Parent=ScreenGui
+
+
+ local debugFrame = Obj.Create"Frame"
+ {
+ Name = 'debugframe',
+ Position = UDim2.new(0, 0, 0, 0),
+ Size = UDim2.new(0, 150, 0, 800),--0.99000001
+ BackgroundTransparency = 1,
+
+ }
+ local debugplayers = Obj.Create"TextLabel"
+ {
+ BackgroundTransparency = .8,
+ Position = UDim2.new(0, 0, .01, 0),
+ Size = UDim2.new(1,0,.5,0),
+ Parent = debugFrame,
+ Font = 'ArialBold',
+ Text = '--',
+ FontSize='Size14',
+ TextWrapped=true,
+ TextColor3 = Color3.new(1,1,1),
+ TextStrokeColor3 = Color3.new(0,0,0),
+ TextStrokeTransparency = 0,
+ TextXAlignment = 'Right',
+ TextYAlignment = 'Center',
+ }
+ local debugOutput = Obj.Create"TextLabel"
+ {
+ BackgroundTransparency = .8,
+ Position = UDim2.new(0, 0, .5, 0),
+ Size = UDim2.new(1,0,.5,0),
+ Parent = debugFrame,
+ Font = 'ArialBold',
+ Text = '--',
+ FontSize='Size14',
+ TextWrapped=true,
+ TextColor3 = Color3.new(1,1,1),
+ TextStrokeColor3 = Color3.new(0,0,0),
+ TextStrokeTransparency = 0,
+ TextXAlignment = 'Right',
+ TextYAlignment = 'Center',
+ }
+
+
+--[[
+ simple function to toggle the display of debug output
+--]]
+local DebugPrintEnabled=true
+function debugprint(str)
+ --print(str)
+ if DebugPrintEnabled then
+ debugOutput.Text=str
+ end
+end
+
+
+ -------------------------
+ -- Script objects
+ -------------------------
+local RbxGui = assert(LoadLibrary('RbxGui'))
+
+ -- number of entries to show if you click minimize
+local DefaultEntriesOnScreen = 8
+
+
+
+
+
+for _,i in pairs(Images) do
+ Game:GetService("ContentProvider"):Preload("http://www.syntax.eco/asset/?id="..i)
+end
+
+ -- ordered array of 'score data', each entry has:
+ -- Name(String)
+ -- Priority(number)
+ -- IsPrimary (bool, should it be shown in upper right)
+ -- MaxLength (integer, of the length of the longest element for this column)
+local ScoreNames = {}
+ -- prevents flipping in playerlist panels
+local AddId = 0
+ -- intermediate table form of all player entries in format of:
+ -- Frame
+ -- Player
+ -- Score
+ -- ID
+ -- MyTeam (team ENRTY(not actual team) I am currently on)
+local PlayerFrames = {}
+ -- intermediate ordered frame array, composed of Entrys of
+ -- Frame
+ -- MyTeam (my team object)
+ -- MyPlayers ( an ordered array of all player frames in team )
+ -- AutoHide (bool saying whether it should be hidden)
+ -- IsHidden (bool)
+ -- ID (int to prevent flipping out of leaderboard, fun times)
+local TeamFrames = {}
+ -- one special entry from teamFrames, for unaffiliated players, only shown if players non - empty
+local NeutralTeam = nil
+
+ -- final 'to be displayed' list of frames
+local MiddleFrames = {}
+local MiddleFrameBackgrounds = {}
+local MiddleFrameHeight = .03
+ -- time of last click
+local LastClick = 0
+local ButtonCooldown = .25
+
+local OnIos = false
+pcall(function() OnIos = Game:GetService('UserInputService').TouchEnabled end)
+
+
+ -- you get 200 of x screen space per stat added, start width 16%
+local BaseScreenXSize = 150
+local SpacingPerStat = 10 --spacing between stats
+
+
+local MaximizedBounds = UDim2.new(.5,0,1,0)
+local MaximizedPosition = UDim2.new(.25,0,.1,0)
+local NormalBounds = UDim2.new(0,BaseScreenXSize, 0, 800)
+local NormalPosition = UDim2.new(1 , - BaseScreenXSize, 0.005, 0)
+
+local MinimizedBounds = UDim2.new(0, BaseScreenXSize, 0.99000001, 0)
+
+--free space to give last stat on the right
+local RightEdgeSpace = -.04
+
+ -- where the scroll par currently is positioned
+local ScrollPosition = 0.75999999
+local IsDragging = false -- am I dragging the player list
+
+local DefaultBottomClipPos = BottomClipFrame.Position.Y.Scale
+
+local LastSelectedPlayerEntry = nil
+local SelectedPlayerEntry = nil
+local SelectedPlayer = nil
+
+ -- locks(semaphores) for stopping race conditions
+local AddingFrameLock = false
+local ChangingOrderLock = false
+local AddingStatLock = false
+local BaseUpdateLock = false
+local WaitForClickLock = false
+local InPopupWaitForClick=false
+local PlayerChangedLock = false
+local NeutralTeamLock = false
+
+local ScrollWheelConnections = {}
+
+
+local DefaultListSize = 15
+if not OnIos then DefaultListSize = 24 end
+local DidMinimizeDrag = false
+
+--local PlaceCreatorId=game.CreatorId
+
+ -- report abuse objects
+local AbuseName
+local Abuses = {
+ "Bad Words or Threats",
+ "Bad Username",
+ "Talking about Dating",
+ "Account Trading or Sharing",
+ "Asking Personal Questions",
+ "Rude or Mean Behavior",
+ "False Reporting Me"
+}
+local UpdateAbuseFunction
+local AbuseDropDown, UpdateAbuseSelection
+
+local PrivilegeLevel =
+{
+ Owner = 255,
+ Admin = 240,
+ Member = 128,
+ Visitor = 10,
+ Banned = 0,
+}
+
+
+local IsPersonalServer = not not game.Workspace:FindFirstChild("PSVariable")
+
+game.Workspace.ChildAdded:connect(function(nchild)
+ if nchild.Name=='PSVariable' and nchild:IsA('BoolValue') then
+ IsPersonalServer=true
+ end
+end)
+ -------------------------------
+ -- Static Functions
+ -------------------------------
+function GetTotalEntries()
+ return math.min(#MiddleFrameBackgrounds,DefaultEntriesOnScreen)
+end
+
+function GetEntryListLength()
+ local numEnts=#PlayerFrames+#TeamFrames
+ if NeutralTeam then
+ numEnts=numEnts+1
+ end
+ return numEnts
+end
+
+function AreAllEntriesOnScreen()
+ return #MiddleFrameBackgrounds * MiddleTemplate.Size.Y.Scale <= 1 + DefaultBottomClipPos
+end
+
+function GetLengthOfVisbleScroll()
+ return 1 + DefaultBottomClipPos
+end
+
+function GetMaxScroll()
+ return DefaultBottomClipPos * - 1
+end
+ -- can be optimized by caching when this varible changes
+function GetMinScroll()
+ if AreAllEntriesOnScreen() then
+ return GetMaxScroll()
+ else
+ return (GetMaxScroll() - (#MiddleFrameBackgrounds * MiddleTemplate.Size.Y.Scale)) + (1 + DefaultBottomClipPos)
+ end
+end
+
+function AbsoluteToPercent(x,y)
+ return Vector2.new(x,y)/ScreenGui.AbsoluteSize
+end
+--[[
+ tweens property of element from starta to enda over length of time
+ Warning: should be put in a Spawn call
+ @Args:
+ element textobject to tween transparency on
+ propName
+ starta alpha to start tweening
+ enda alpha to end tweening on
+ length how many seconds to spend tweening
+--]]
+function TweenProperty(obj, propName, inita, enda, length)
+ local startTime = tick()
+ while tick()-startTimenrank do
+ game:GetService("PersonalServerService"):Demote(player)
+ end
+end
+--[[
+ called when player selects new privilege level from popup menu
+ @Args:
+ player player to set privileges on
+ nlevel new privilege level for this player
+--]]
+function OnPrivilegeLevelSelect(player,nlevel,BanPlayerButton,VisitorButton,MemberButton,AdminButton)
+ debugprint('setting privilege level')
+ SetPrivilegeRank(player,nlevel)
+ HighlightMyRank(player,BanPlayerButton,VisitorButton,MemberButton,AdminButton)
+end
+
+--[[
+ Highlights current rank of this player in the popup menu
+ @Args:
+ player Player to check for rank on
+--]]
+function HighlightMyRank(player,BanPlayerButton,VisitorButton,MemberButton,AdminButton)
+ BanPlayerButton.Image= 'http://www.syntax.eco/asset/?id='..Images['LightPopupMid']
+ VisitorButton.Image= 'http://www.syntax.eco/asset/?id='..Images['DarkPopupMid']
+ MemberButton.Image= 'http://www.syntax.eco/asset/?id='..Images['LightPopupMid']
+ AdminButton.Image= 'http://www.syntax.eco/asset/?id='..Images['DarkPopupBottom']
+
+ local rank=player.PersonalServerRank
+ if rank <= PrivilegeLevel['Banned'] then
+ BanPlayerButton.Image='http://www.syntax.eco/asset/?id='..Images['LightBluePopupMid']
+ elseif rank <= PrivilegeLevel['Visitor'] then
+ VisitorButton.Image='http://www.syntax.eco/asset/?id='..Images['DarkBluePopupMid']
+ elseif rank <= PrivilegeLevel['Member'] then
+ MemberButton.Image='http://www.syntax.eco/asset/?id='..Images['LightBluePopupMid']
+ elseif rank <= PrivilegeLevel['Admin'] then
+ AdminButton.Image= 'http://www.syntax.eco/asset/?id='..Images['DarkBluePopupBottom']
+ end
+end
+
+ --------------------------
+ -- Report abuse handling
+ --------------------------
+--[[
+ does final reporting of abuse on selected player, calls closeAbuseDialog
+--]]
+function OnSubmitAbuse()
+ if SubmitReportButton.Active then
+ if AbuseName and SelectedPlayer then
+ AbuseSettingsFrame.Visible = false
+ game.Players:ReportAbuse(SelectedPlayer, AbuseName, AbuseDescriptionBox.Text)
+ if AbuseName == "Rude or Mean Behavior" or AbuseName == "False Reporting Me" then
+ CalmingAbuseBox.Parent = ReportAbuseShield
+ else
+ debugprint('opening abuse box')
+ NormalAbuseBox.Parent = ReportAbuseShield
+ end
+ else
+ CloseAbuseDialog()
+ end
+ end
+end
+
+--[[
+ opens the abuse dialog, initialises text to display selectedplayer
+--]]
+function OpenAbuseDialog()
+ debugprint('adding report dialog')
+ AbusePlayerLabel.Text = SelectedPlayer.Name
+ --AbuseDescriptionBox.Text = ""
+ PopUpPanel:TweenPosition(UDim2.new(1,0,0,0), "Out", "Linear", BASE_TWEEN,true)
+ AbuseDescriptionBox=OriginalAbuseDescriptionBox:Clone()
+ AbuseDescriptionBox.Parent = AbuseDescriptionWrapper
+ ReportAbuseShield.Parent = ScreenGui
+ ClosePopUpPanel()
+end
+--[[
+ resets and closes abuse dialog
+--]]
+function CloseAbuseDialog()
+ AbuseName = nil
+ SubmitReportButton.Active = false
+ SubmitReportButton.Image = 'http://www.syntax.eco/asset/?id=96502438' -- 96501119',
+ AbuseDescriptionBox:Destroy()
+ CalmingAbuseBox.Parent = nil
+ NormalAbuseBox.Parent = nil
+ ReportAbuseShield.Parent = nil
+ AbuseSettingsFrame.Visible = true
+end
+
+--[[
+ creates dropdownbox, registers all listeners for abuse dialog
+--]]
+function InitReportAbuse()
+
+ UpdateAbuseFunction = function(abuseText)
+ AbuseName = abuseText
+ if AbuseName and SelectedPlayer then
+ SubmitReportButton.Active = true
+ SubmitReportButton.Image = 'http://www.syntax.eco/asset/?id=96501119'
+ end
+ end
+
+ AbuseDropDown, UpdateAbuseSelection = RbxGui.CreateDropDownMenu(Abuses, UpdateAbuseFunction, true)
+ AbuseDropDown.Name = "AbuseComboBox"
+ AbuseDropDown.Position = UDim2.new(0.425, 0, 0, 142)
+ AbuseDropDown.Size = UDim2.new(0.55,0,0,32)
+ AbuseDropDown.Parent = AbuseSettingsFrame
+
+
+ CancelReportButton.MouseButton1Click:connect(CloseAbuseDialog)
+ SubmitReportButton.MouseButton1Click:connect(OnSubmitAbuse)
+
+ CalmingAbuseBox:FindFirstChild('OkButton').MouseButton1Down:connect(CloseAbuseDialog)
+ NormalAbuseBox:FindFirstChild('OkButton').MouseButton1Down:connect(CloseAbuseDialog)
+end
+
+-------------------------------------
+-- Friend/unfriending
+-------------------------------------
+--[[
+ gets enum val of friend status, uses pcall for some reason?(from old playerlist)
+ @Args:
+ player player object to check if friends with
+ @Return: enum of friend status
+--]]
+local function GetFriendStatus(player)
+ if player == game.Players.LocalPlayer then
+ return Enum.FriendStatus.NotFriend
+ else
+ local success, result = pcall(function() return game.Players.LocalPlayer:GetFriendStatus(player) end)
+ if success then
+ return result
+ else
+ return Enum.FriendStatus.NotFriend
+ end
+ end
+end
+
+--[[
+ when friend button is clicked, tries to take appropriate action,
+ based on current friend status with SelectedPlayer
+--]]
+function OnFriendButtonSelect()
+
+ local friendStatus=GetFriendStatus(SelectedPlayer)
+ if friendStatus==Enum.FriendStatus.Friend then
+ LocalPlayer:RevokeFriendship(SelectedPlayer)
+ elseif friendStatus==Enum.FriendStatus.Unknown or friendStatus==Enum.FriendStatus.NotFriend then
+ LocalPlayer:RequestFriendship(SelectedPlayer)
+ elseif friendStatus==Enum.FriendStatus.FriendRequestSent then
+ LocalPlayer:RevokeFriendship(SelectedPlayer)
+ elseif friendStatus==Enum.FriendStatus.FriendRequestReceived then
+ LocalPlayer:RequestFriendship(SelectedPlayer)
+ end
+ --PersonalServerPanel:TweenPosition(UDim2.new(1,0,0,0), "Out", "Quad", .5,true)
+ ClosePopUpPanel()
+ --PopUpPanel:TweenPosition(UDim2.new(1,0,0,0), "Out", "Linear", BASE_TWEEN,true)
+end
+
+function OnFriendRefuseButtonSelect()
+
+
+ LocalPlayer:RevokeFriendship(SelectedPlayer)
+ ClosePopUpPanel()
+ PopUpPanel:TweenPosition(UDim2.new(1,0,0,0), "Out", "Linear", BASE_TWEEN,true)
+end
+------------------------------------
+-- Player Entry Handling
+------------------------------------
+--[[
+ used by lua's table.sort to sort player entries
+--]]
+function PlayerSortFunction(a,b)
+ -- prevents flipping out leaderboard
+ if a['Score'] == b['Score'] then
+ return a['Player'].Name:upper() < b['Player'].Name:upper()
+ end
+ if not a['Score'] then return false end
+ if not b['Score'] then return true end
+ return a['Score'] < b['Score']
+end
+
+ ---------------------------------
+ -- Stat Handling
+ ---------------------------------
+ -- removes and closes all leaderboard stuffs
+function BlowThisPopsicleStand()
+ --ScreenGui:Destroy()
+ --script:Destroy()
+ --time to make the fanboys rage...
+ Tabify()
+end
+--[[
+ used by lua's table.sort to prioritize score entries
+--]]
+function StatSort(a,b)
+ -- primary stats should be shown before all others
+ if a.IsPrimary ~= b.IsPrimary then
+ return a.IsPrimary
+ end
+ -- if priorities are equal, then return the first added one
+ if a.Priority == b.Priority then
+ return a.AddId < b.AddId
+ end
+ return a.Priority < b.Priority
+end
+--[[
+ doing WAAY too much here, for optimization update only your team
+ @Args:
+ playerEntry Entry of player who had a stat change
+ property Name of stat changed
+--]]
+function StatChanged(playerEntry,property)
+
+ -- if(playerEntry['MyTeam']) then
+ -- UpdateSingleTeam(playerEntry['MyTeam'])
+ -- else
+ BaseUpdate()
+ -- end
+end
+--[[
+ Called when stat is added
+ if playerEntry is localplayer, will add to score names and re-sort the stats, and resize the width of the leaderboard
+ for all players, will add a listener for if this stat changes
+ if stat is a string value, crashes the leaderboard
+ Note:change crash to a 'tabify' leaderboard later
+ @Args:
+ nchild new child value to leaderstats
+ playerEntry entry this stat was added to
+--]]
+function StatAdded(nchild,playerEntry)
+ -- dont re - add a leaderstat I alreday have
+ while AddingStatLock do debugprint('in stat added function lock') wait(1/30) end
+ AddingStatLock = true
+ if not (nchild:IsA('StringValue') or nchild:IsA('IntValue') or nchild:IsA('BoolValue') or nchild:IsA('NumberValue') or nchild:IsA('DoubleConstrainedValue') or nchild:IsA('IntConstrainedValue')) then
+ BlowThisPopsicleStand()
+ else
+ local haveScore = false
+ for _,i in pairs(ScoreNames) do
+ if i['Name']==nchild.Name then haveScore=true end
+ end
+ if not haveScore then
+ local nstat = {}
+ nstat['Name'] = nchild.Name
+ nstat['Priority'] = 0
+ if(nchild:FindFirstChild('Priority')) then
+ nstat['Priority'] = nchild.Priority
+ end
+ nstat['IsPrimary'] = false
+ if(nchild:FindFirstChild('IsPrimary')) then
+ nstat['IsPrimary'] = true
+ end
+ nstat.AddId = AddId
+ AddId = AddId + 1
+ table.insert(ScoreNames,nstat)
+ table.sort(ScoreNames,StatSort)
+ if not StatTitles:FindFirstChild(nstat['Name']) then
+ CreateStatTitle(nstat['Name'])
+ end
+ UpdateMaximize()
+
+ end
+ end
+ AddingStatLock = false
+ StatChanged(playerEntry)
+ nchild.Changed:connect(function(property) StatChanged(playerEntry,property) end)
+
+
+end
+--returns whether any of the existing players has this stat
+function DoesStatExist(statName, exception)
+ for _,playerf in pairs(PlayerFrames) do
+ if playerf['Player'] ~= exception and playerf['Player']:FindFirstChild('leaderstats') and playerf['Player'].leaderstats:FindFirstChild(statName) then
+ --print('player:' .. playerf['Player'].Name ..' has stat')
+ return true
+ end
+ end
+ return false
+end
+
+
+
+--[[
+ Called when stat is removed from player
+ for all players, destroys the stat frame associated with this value,
+ then calls statchanged(to resize frame)
+ if playerEntry==localplayer, will remove from scorenames
+ @Args:
+ nchild ___value to be removed
+ playerEntry entry of player value is being removed from
+--]]
+function StatRemoved(nchild,playerEntry)
+ while AddingStatLock do debugprint('In Adding Stat Lock1') wait(1/30) end
+ AddingStatLock = true
+ if playerEntry['Frame']:FindFirstChild(nchild.Name) then
+ debugprint('Destroyed frame!')
+ playerEntry['Frame'][nchild.Name].Parent = nil
+ end
+ if not DoesStatExist(nchild.Name, playerEntry['Player']) then
+ for i,val in ipairs(ScoreNames) do
+ if val['Name'] == nchild.Name then
+ table.remove(ScoreNames,i)
+ if StatTitles:FindFirstChild(nchild.Name) then
+ StatTitles[nchild.Name]:Destroy()
+ end
+ for _,teamf in pairs(TeamFrames) do
+ if teamf['Frame']:FindFirstChild(nchild.Name) then
+ teamf['Frame'][nchild.Name]:Destroy()
+ end
+ end
+ end
+ end
+ end
+ AddingStatLock = false
+ StatChanged(playerEntry)
+end
+--[[
+ clears all stats from a given playerEntry
+ used when leaderstats are removed, or when new leaderstats are added(for weird edge case)+
+--]]
+function RemoveAllStats(playerEntry)
+ for i,val in ipairs(ScoreNames) do
+ StatRemoved(val,playerEntry)
+ end
+
+end
+
+
+function GetScoreValue(score)
+ if score:IsA('DoubleConstrainedValue') or score:IsA('IntConstrainedValue') then
+ return score.ConstrainedValue
+ elseif score:IsA('BoolValue') then
+ if score.Value then return 1 else return 0 end
+ else
+ return score.Value
+ end
+end
+--[[
+
+--]]
+function MakeScoreEntry(entry,scoreval,panel)
+ if not panel:FindFirstChild('PlayerScore') then return end
+ local nscoretxt = panel:FindFirstChild('PlayerScore'):Clone()
+ local thisScore = nil
+ --here lies the resting place of a once great and terrible bug
+ --may its treachery never be forgoten, lest its survivors fall for it again
+ --RIP the leaderstat bug, oct 2012-nov 2012
+ wait()
+ if entry['Player']:FindFirstChild('leaderstats') and entry['Player'].leaderstats:FindFirstChild(scoreval['Name']) then
+ thisScore = entry['Player']:FindFirstChild('leaderstats'):FindFirstChild(scoreval['Name'])
+ else
+ return
+ end
+
+ if not entry['Player'].Parent then return end
+
+ nscoretxt.Name = scoreval['Name']
+ nscoretxt.Text = tostring(GetScoreValue(thisScore))
+ if scoreval['Name'] == ScoreNames[1]['Name'] then
+ debugprint('changing score')
+ entry['Score'] = GetScoreValue(thisScore)
+ if entry['Player'] == LocalPlayer then HeaderScore.Text = tostring(GetScoreValue(thisScore)) end
+ end
+
+ thisScore.Changed:connect(function()
+ if not thisScore.Parent then return end
+ if scoreval['Name'] == ScoreNames[1]['Name'] then
+
+ entry['Score'] = GetScoreValue(thisScore)
+ if entry['Player'] == LocalPlayer then HeaderScore.Text = tostring(GetScoreValue(thisScore)) end
+ end
+ nscoretxt.Text = tostring(GetScoreValue(thisScore))
+ BaseUpdate()
+ end)
+ return nscoretxt
+
+end
+
+function CreateStatTitle(statName)
+
+ local ntitle = MiddleTemplate:FindFirstChild('PlayerScore'):Clone()
+ ntitle.Name = statName
+ ntitle.Text = statName
+ -- ntitle
+ if IsMaximized.Value then
+ ntitle.TextTransparency = 0
+ else
+ ntitle.TextTransparency = 1
+ end
+ ntitle.Parent = StatTitles
+end
+
+function RecreateScoreColumns(ptable)
+ while AddingStatLock do debugprint ('In Adding Stat Lock2') wait(1/30) end
+ AddingStatLock = true
+ local Xoffset=5--15 --current offset from Right
+ local maxXOffset=Xoffset
+ local MaxSizeColumn=0 --max size for this column
+
+ -- foreach known leaderstat
+ for j = #ScoreNames, 1,-1 do
+ local scoreval = ScoreNames[j]
+
+ MaxSizeColumn=0
+ -- for each entry in this player table
+ for i,entry in ipairs(ptable) do
+ local panel = entry['Frame']
+ local tplayer = entry['Player']
+ -- if this panel does not have an element named after this stat
+ if not panel:FindFirstChild(scoreval['Name']) then
+ -- make an entry for this object
+ local nentry = MakeScoreEntry(entry,scoreval,panel)
+ if nentry then
+ debugprint('adding '..nentry.Name..' to '..entry['Player'].Name )
+ nentry.Parent = panel
+ -- add score to team
+ if entry['MyTeam'] and entry['MyTeam'] ~= NeutralTeam and not entry['MyTeam']['Frame']:FindFirstChild(scoreval['Name']) then
+ local ntitle = nentry:Clone()
+ --ntitle.TextXAlignment = 'Right'
+ ntitle.Parent = entry['MyTeam']['Frame']
+ end
+
+ end
+ end
+ scoreval['XOffset']=Xoffset
+
+ if panel:FindFirstChild(scoreval['Name']) then
+ MaxSizeColumn=math.max(MaxSizeColumn,panel[scoreval['Name']].TextBounds.X)
+ end
+ end
+
+ if AreNamesExpanded.Value then
+ MaxSizeColumn=math.max(MaxSizeColumn,StatTitles[scoreval['Name'] ].TextBounds.X)
+ StatTitles[scoreval['Name'] ]:TweenPosition(UDim2.new(RightEdgeSpace,-Xoffset,0,0),'Out','Linear',BASE_TWEEN,true)
+ else
+ StatTitles[scoreval['Name'] ]:TweenPosition(UDim2.new((.4+((.6/#ScoreNames)*(j-1)))-1,0,0,0),'Out','Linear',BASE_TWEEN,true)
+ end
+ scoreval['ColumnSize']=MaxSizeColumn
+ Xoffset= Xoffset+SpacingPerStat+MaxSizeColumn
+ maxXOffset=math.max(Xoffset,maxXOffset)
+ end
+ NormalBounds = UDim2.new(0, BaseScreenXSize+maxXOffset-SpacingPerStat,0,800)
+ NormalPosition = UDim2.new(1 , -NormalBounds.X.Offset, NormalPosition.Y.Scale, 0)
+ UpdateHeaderNameSize()
+ UpdateMaximize()
+
+ AddingStatLock = false
+end
+ ---------------------------
+ -- Minimizing and maximizing
+ ---------------------------
+
+function ToggleMinimize()
+ IsMinimized.Value = not IsMinimized.Value
+ UpdateStatNames()
+end
+
+function ToggleMaximize()
+ IsMaximized.Value = not IsMaximized.Value
+ RecreateScoreColumns(PlayerFrames) --done to re-position stat names NOTE: optimize-able
+end
+
+function Tabify()
+ IsTabified.Value= true
+ IsMaximized.Value=false
+ IsMinimized.Value=true
+ UpdateMinimize()
+ IsTabified.Value= true
+ ScreenGui:TweenPosition(UDim2.new(NormalBounds.X.Scale, NormalBounds.X.Offset-10, 0,0),'Out','Linear',BASE_TWEEN*1.2,true)
+end
+
+function UnTabify()
+ if IsTabified.Value then
+ IsTabified.Value= false
+ ScreenGui:TweenPosition(UDim2.new(0, 0, 0,0),'Out','Linear',BASE_TWEEN*1.2,true)
+ end
+end
+
+--[[
+ Does more than it looks like
+ monitors positions of the clipping frames and bottom frames
+ called from EVERYWHERE, too much probably
+--]]
+function UpdateMinimize()
+
+ if IsMinimized.Value then
+ if IsMaximized.Value then
+ ToggleMaximize()
+ end
+ if not IsTabified.Value then
+ MainFrame.Size = UDim2.new(0.010, HeaderName.TextBounds.X, NormalBounds.Y.Scale,NormalBounds.Y.Offset)
+ MainFrame.Position = UDim2.new(.990, -HeaderName.TextBounds.X, NormalPosition.Y.Scale,0)
+ else
+ MainFrame.Size = NormalBounds
+ MainFrame.Position = NormalPosition
+ end
+ --(#MiddleFrameBackgrounds*MiddleBGTemplate.Size.Y.Scale)
+ BottomClipFrame.Position = UDim2.new(0,0,-1,0)
+ BottomFrame.Position = UDim2.new(0,0,0,0)
+ FocusFrame.Size=UDim2.new(1,0,HeaderFrameHeight,0)
+ ExtendTab.Image = 'http://www.syntax.eco/asset/?id=94692731'
+ else
+ if not IsMaximized.Value then
+ MainFrame.Size = NormalBounds
+ MainFrame.Position = NormalPosition
+ end
+ --do limiting
+ DefaultBottomClipPos = math.min(math.max(DefaultBottomClipPos,-1),-1+(#MiddleFrameBackgrounds*MiddleBGTemplate.Size.Y.Scale))
+ UpdateScrollPosition()
+
+ BottomClipFrame.Position=UDim2.new(0,0,DefaultBottomClipPos,0)
+ local bottomPositon = (DefaultBottomClipPos+BottomClipFrame.Size.Y.Scale)
+ BottomFrame.Position=UDim2.new(0,0,bottomPositon,0)
+ FocusFrame.Size=UDim2.new(1,0,bottomPositon + HeaderFrameHeight,0)
+ ExtendTab.Image = 'http://www.syntax.eco/asset/?id=94825585'
+ end
+end
+
+--[[
+ Manages the position/size of the mainFrame, swaps out different resolution images for the frame
+ fades in and out the stat names, moves position of headername and header score
+--]]
+function UpdateMaximize()
+ if IsMaximized.Value then
+ for j = 1, #ScoreNames,1 do
+ local scoreval = ScoreNames[j]
+ StatTitles[scoreval['Name'] ]:TweenPosition(UDim2.new(.4+((.6/#ScoreNames)*(j-1))-1,0,0,0),'Out','Linear',BASE_TWEEN,true)
+ end
+
+ if IsMinimized.Value then
+ ToggleMinimize()
+ else
+ UpdateMinimize()
+ end
+
+
+ MainFrame:TweenSizeAndPosition(MaximizedBounds,MaximizedPosition,'Out','Linear',BASE_TWEEN*1.2,true)
+ HeaderScore:TweenPosition(UDim2.new(0,0,HeaderName.Position.Y.Scale,0), "Out", "Linear", BASE_TWEEN*1.2,true)
+ HeaderName:TweenPosition(UDim2.new( - .1, - HeaderScore.TextBounds.x,HeaderName.Position.Y.Scale,0), "Out", "Linear", BASE_TWEEN*1.2,true)
+ HeaderFrame.Background.Image = 'http://www.syntax.eco/asset/?id='..Images['LargeHeader']
+ BottomFrame.Background.Image = 'http://www.syntax.eco/asset/?id='..Images['LargeBottom']
+ for index, i in ipairs(MiddleFrameBackgrounds) do
+ if (index%2) ~= 1 then
+ i.Background.Image = 'http://www.syntax.eco/asset/?id='..Images['LargeDark']
+ else
+ i.Background.Image = 'http://www.syntax.eco/asset/?id='..Images['LargeLight']
+ end
+ end
+ for index, i in ipairs(MiddleFrames) do
+ if i:FindFirstChild('ClickListener') then
+ i.ClickListener.Size = UDim2.new(.974,0,i.ClickListener.Size.Y.Scale,0)
+ end
+ for j=1, #ScoreNames,1 do
+ local scoreval = ScoreNames[j]
+ if i:FindFirstChild(scoreval['Name']) then
+ i[scoreval['Name']]:TweenPosition(UDim2.new(.4+((.6/#ScoreNames)*(j-1))-1,0,0,0), "Out", "Linear", BASE_TWEEN,true)
+ end
+ end
+ end
+ for i,entry in ipairs(PlayerFrames) do
+ WaitForChild(entry['Frame'],'TitleFrame').Size=UDim2.new(.38,0,entry['Frame'].TitleFrame.Size.Y.Scale,0)
+ end
+
+ for i,entry in ipairs(TeamFrames) do
+ WaitForChild(entry['Frame'],'TitleFrame').Size=UDim2.new(.38,0,entry['Frame'].TitleFrame.Size.Y.Scale,0)
+ end
+
+ else
+ if not IsMinimized.Value then
+ MainFrame:TweenSizeAndPosition(NormalBounds,NormalPosition,'Out','Linear',BASE_TWEEN*1.2,true)
+ end
+ HeaderScore:TweenPosition(UDim2.new(0,0,.4,0), "Out", "Linear", BASE_TWEEN*1.2,true)
+ HeaderName:TweenPosition(UDim2.new(0,0,HeaderName.Position.Y.Scale,0), "Out", "Linear", BASE_TWEEN*1.2,true)
+ HeaderFrame.Background.Image = 'http://www.syntax.eco/asset/?id='..Images['NormalHeader']
+ BottomFrame.Background.Image = 'http://www.syntax.eco/asset/?id='..Images['NormalBottom']
+ for index, i in ipairs(MiddleFrameBackgrounds) do
+ if index%2 ~= 1 then
+ i.Background.Image = 'http://www.syntax.eco/asset/?id='..Images['midDark']
+ else
+ i.Background.Image = 'http://www.syntax.eco/asset/?id='..Images['midLight']
+ end
+ end
+ for index, i in ipairs(MiddleFrames) do
+ if i:FindFirstChild('ClickListener') then
+ i.ClickListener.Size = UDim2.new(.96,0,i.ClickListener.Size.Y.Scale,0)
+ for j=1, #ScoreNames,1 do
+ local scoreval = ScoreNames[j]
+ if i:FindFirstChild(scoreval['Name']) and scoreval['XOffset'] then
+ --print('updateing stat position: ' .. scoreval['Name'])
+ i[scoreval['Name']]:TweenPosition(UDim2.new(RightEdgeSpace,-scoreval['XOffset'],0,0), "Out", "Linear", BASE_TWEEN,true)
+ end
+ end
+ end
+ end
+
+ for i,entry in ipairs(TeamFrames) do
+ WaitForChild(entry['Frame'],'TitleFrame').Size=UDim2.new(0,BaseScreenXSize*.9,entry['Frame'].TitleFrame.Size.Y.Scale,0)
+
+ end
+ for i,entry in ipairs(PlayerFrames) do
+ WaitForChild(entry['Frame'],'TitleFrame').Size=UDim2.new(0,BaseScreenXSize*.9,entry['Frame'].TitleFrame.Size.Y.Scale,0)
+ end
+ end
+end
+
+function UpdateStatNames()
+ if not AreNamesExpanded.Value or IsMinimized.Value then
+ CloseNames()
+ else
+ ExpandNames()
+ end
+end
+
+function ExpandNames()
+ if #ScoreNames ~= 0 then
+ for _,i in pairs(StatTitles:GetChildren()) do
+ Spawn(function()TweenProperty(i,'TextTransparency',i.TextTransparency,0,BASE_TWEEN) end)
+ end
+ HeaderFrameHeight=.09
+ --as of writing, this and 'CloseNames' are the only places headerframe is resized
+ HeaderFrame:TweenSizeAndPosition(UDim2.new(HeaderFrame.Size.X.Scale, HeaderFrame.Size.X.Offset, HeaderFrameHeight,0),
+ HeaderFrame.Position,'Out','Linear',BASE_TWEEN*1.2,true)
+ TopClipFrame:TweenPosition(UDim2.new(TopClipFrame.Position.X.Scale,0,HeaderFrameHeight,0),'Out','Linear',BASE_TWEEN*1.2,true)
+ BottomShiftFrame:TweenPosition(UDim2.new(0,0,HeaderFrameHeight,0), "Out", 'Linear', BASE_TWEEN*1.2,true)
+
+ end
+
+end
+
+function CloseNames()
+ if #ScoreNames ~= 0 then
+ HeaderFrameHeight=.07
+ if not (IsMaximized.Value) then
+ for _,i in pairs(StatTitles:GetChildren()) do
+ Spawn(function()TweenProperty(i,'TextTransparency',i.TextTransparency,1,BASE_TWEEN) end)
+ end
+ end
+ BottomShiftFrame:TweenPosition(UDim2.new(0,0,HeaderFrameHeight,0), "Out", 'Linear', BASE_TWEEN*1.2,true)
+ HeaderFrame:TweenSizeAndPosition(UDim2.new(HeaderFrame.Size.X.Scale, HeaderFrame.Size.X.Offset, HeaderFrameHeight,0),
+ HeaderFrame.Position,'Out','Linear',BASE_TWEEN*1.2,true)
+ TopClipFrame:TweenPosition(UDim2.new(TopClipFrame.Position.X.Scale,0,HeaderFrameHeight,0),'Out','Linear',BASE_TWEEN*1.2,true)
+ end
+end
+
+function OnScrollWheelMove(direction)
+ if not (IsTabified.Value or IsMinimized.Value or InPopupWaitForClick) then
+ local StartFrame = ListFrame.Position
+ local newFrameY = math.max(math.min(StartFrame.Y.Scale + (direction),GetMaxScroll()),GetMinScroll())
+
+ ListFrame.Position = UDim2.new(StartFrame.X.Scale,StartFrame.X.Offset,newFrameY,StartFrame.Y.Offset)
+ UpdateScrollPosition()
+ end
+end
+
+function AttachScrollWheel()
+ if ScrollWheelConnections then return end
+ ScrollWheelConnections = {}
+ table.insert(ScrollWheelConnections,Mouse.WheelForward:connect(function()
+ OnScrollWheelMove(.05)
+ end))
+ table.insert(ScrollWheelConnections,Mouse.WheelBackward:connect(function()
+ OnScrollWheelMove(-.05)
+ end))
+end
+
+function DetachScrollWheel()
+ if ScrollWheelConnections then
+ for _,i in pairs(ScrollWheelConnections) do
+ i:disconnect()
+ end
+ end
+ ScrollWheelConnections=nil
+end
+
+FocusFrame.MouseEnter:connect(function()
+ if not (IsMinimized.Value or IsTabified.Value) then
+ AttachScrollWheel()
+ end
+end)
+FocusFrame.MouseLeave:connect(function()
+ --if not (IsMaximized.Value or IsMinimized.Value) then
+ DetachScrollWheel()
+ --end
+end)
+
+ ------------------------
+ -- Scroll Bar functions
+ ------------------------
+--[[
+ updates whether the scroll bar should be showing, if it is showing, updates
+ the size of it
+--]]
+function UpdateScrollBarVisibility()
+ if AreAllEntriesOnScreen() then
+ ScrollBar.BackgroundTransparency = 1
+ else
+ ScrollBar.BackgroundTransparency = 0
+ UpdateScrollBarSize()
+ end
+end
+--[[
+ updates size of scrollbar depending on how many entries exist
+--]]
+function UpdateScrollBarSize()
+ local entryListSize = #MiddleFrameBackgrounds * MiddleTemplate.Size.Y.Scale
+ local shownAreaSize = ((BottomClipFrame.Position.Y.Scale) + 1)
+ ScrollBar.Size = UDim2.new(1,0,shownAreaSize/entryListSize,0)
+
+end
+--[[
+ updates position of listframe so that no gaps at the bottom or top of the list are visible
+ updates position of scrollbar to match what parts of the list are visible
+--]]
+function UpdateScrollPosition()
+ local minPos = GetMinScroll()
+ local maxPos = GetMaxScroll()
+ local scrollLength = maxPos - minPos
+
+ local yscrollpos=math.max(math.min(ListFrame.Position.Y.Scale,maxPos),minPos)
+ ListFrame.Position=UDim2.new(ListFrame.Position.X.Scale,ListFrame.Position.X.Offset,yscrollpos,ListFrame.Position.Y.Offset)
+
+ local adjustedLength = 1 - ScrollBar.Size.Y.Scale
+ ScrollBar.Position = UDim2.new(0,0,adjustedLength - (adjustedLength * ((ListFrame.Position.Y.Scale - minPos)/scrollLength)),0)
+end
+
+--[[
+ WARNING:this is in a working state, but uses massive hacks
+ revize when global input is available
+ Manages scrolling of the playerlist on mouse drag
+--]]
+function StartDrag(entry,startx,starty)
+ local startDragTime = tick()
+ local stopDrag = false
+ local openPanel = true
+ local draggedFrame = WaitForChild(entry['Frame'],'ClickListener')
+ local function dragExit()
+ stopDrag = true
+
+ if entry['Player'] and SelectedPlayer and openPanel
+ and entry['Player']~=LocalPlayer and SelectedPlayer.userId>1 and LocalPlayer.userId>1 then
+ ActivatePlayerEntryPanel(entry)
+ end
+ end
+ local startY = nil
+ local StartFrame = ListFrame.Position
+ local function dragpoll(nx,ny)
+ if not startY then
+ startY = AbsoluteToPercent(nx,ny).Y
+ end
+ local nowY = AbsoluteToPercent(nx,ny).Y
+ debugprint('drag dist:'..Vector2.new(startx-nx,starty-ny).magnitude)
+ if Vector2.new(startx-nx,starty-ny).magnitude>MOUSE_DRAG_DISTANCE then
+ openPanel=false
+ end
+
+ local newFrameY = math.max(math.min(StartFrame.Y.Scale + (nowY - startY),GetMaxScroll()),GetMinScroll())
+ ListFrame.Position = UDim2.new(StartFrame.X.Scale,StartFrame.X.Offset,newFrameY,StartFrame.Y.Offset)
+ UpdateScrollPosition()
+ end
+ WaitForClick(ScreenGui,dragpoll,dragExit)
+end
+
+
+function StartMinimizeDrag()
+ Delay(0,function()
+ local startTime=tick()
+ debugprint('Got Click2')
+ local stopDrag = false
+ local function dragExit()
+ --debugprint('undone click2')
+ if tick()-startTime<.25 then --was click
+ ToggleMinimize()
+ else --was drag
+ if (DefaultBottomClipPos >= -1+(#MiddleFrameBackgrounds*MiddleBGTemplate.Size.Y.Scale)) then
+ DidMinimizeDrag = false
+ else
+ DidMinimizeDrag = true
+ end
+ if IsMinimized.Value then
+ ToggleMinimize()
+ end
+ end
+ stopDrag = true
+ end
+ local startY = nil
+ local StartFrame = DefaultBottomClipPos
+ local function dragpoll(nx,ny)
+ if not IsMinimized.Value then
+
+ if not startY then
+ startY = AbsoluteToPercent(nx,ny).Y
+ end
+ local nowY = AbsoluteToPercent(nx,ny).Y
+ local newFrameY
+ newFrameY = math.min(math.max(StartFrame + (nowY - startY),-1),-1+(#MiddleFrameBackgrounds*MiddleBGTemplate.Size.Y.Scale))
+ DefaultBottomClipPos = newFrameY
+ UpdateMinimize()
+ ScrollBarFrame.Size= UDim2.new(ScrollBarFrame.Size.X.Scale,0,(DefaultBottomClipPos+BottomClipFrame.Size.Y.Scale),0)
+ ScrollBarFrame.Position= UDim2.new(ScrollBarFrame.Position.X.Scale,0,1-ScrollBarFrame.Size.Y.Scale,0)
+ UpdateScrollBarSize()
+ UpdateScrollPosition()
+ UpdateScrollBarVisibility()
+
+ end
+ end
+ Spawn(function() WaitForClick(ScreenGui,dragpoll,dragExit) end)
+ end)
+
+end
+
+ -------------------------------
+ -- Input Callback functions
+ -------------------------------
+IsMaximized.Value=false
+IsMinimized.Value=false
+IsMaximized.Changed:connect(UpdateMaximize)
+IsMinimized.Changed:connect(UpdateMinimize)
+
+ExtendButton.MouseButton1Down:connect(function()
+ if(time() - LastClick < ButtonCooldown) or InPopupWaitForClick then return end
+ LastClick = time()
+ if IsTabified.Value then
+ UnTabify()
+ else
+ StartMinimizeDrag()
+ end
+end)
+
+MaximizeButton.MouseButton1Click:connect(function()
+ if(time() - LastClick < ButtonCooldown) or InPopupWaitForClick then return end
+ LastClick = time()
+ if IsTabified.Value then
+ UnTabify()
+ elseif not AreNamesExpanded.Value then
+ AreNamesExpanded.Value = true
+ BaseUpdate()
+ else
+ ToggleMaximize()
+ end
+end)
+
+MaximizeButton.MouseButton2Click:connect(function()
+ if(time() - LastClick < ButtonCooldown) or InPopupWaitForClick then return end
+ LastClick = time()
+ if IsTabified.Value then
+ UnTabify()
+ elseif IsMaximized.Value then
+ ToggleMaximize()
+ elseif AreNamesExpanded.Value then
+ AreNamesExpanded.Value = false
+ BaseUpdate()
+ else
+ Tabify()
+ end
+end)
+
+
+-------------------------------
+-- MiddleFrames management
+-------------------------------
+--[[
+ adds a background frame to the listframe
+--]]
+function AddMiddleBGFrame()
+ local nBGFrame = MiddleBGTemplate:Clone()
+ nBGFrame.Position = UDim2.new(.5,0,((#MiddleFrameBackgrounds) * nBGFrame.Size.Y.Scale),0)
+ if (#MiddleFrameBackgrounds+1)%2 ~= 1 then
+ if IsMaximized.Value then
+ nBGFrame.Background.Image = 'http://www.syntax.eco/asset/?id='..Images['LargeDark']
+ else
+ nBGFrame.Background.Image = 'http://www.syntax.eco/asset/?id='..Images['midDark']
+ end
+ else
+ if IsMaximized.Value then
+ nBGFrame.Background.Image = 'http://www.syntax.eco/asset/?id='..Images['LargeLight']
+ else
+ nBGFrame.Background.Image = 'http://www.syntax.eco/asset/?id='..Images['midLight']
+ end
+ end
+ nBGFrame.Parent = ListFrame
+ table.insert(MiddleFrameBackgrounds,nBGFrame)
+
+ if #MiddleFrameBackgrounds 1 do
+ fSize=fSize-1
+ tHeader.FontSize=FONT_SIZES[fSize]
+ wait(.2)
+ end
+ HeaderName.FontSize=tHeader.FontSize
+ tHeader:Destroy()
+ end)
+end
+ScreenGui.Changed:connect(UpdateHeaderNameSize)
+
+--[[
+ called only when the leaderstats object is added to a given player entry
+ removes old stats, adds any existing stats, and sets up listeners for new stats
+ @Args:
+ playerEntry A reference to the ENTRY(table) of the player who had leaderstats added
+--]]
+function LeaderstatsAdded(playerEntry)
+ --RemoveAllStats(playerEntry)
+ local nplayer = playerEntry['Player']
+ for _,i in pairs(nplayer.leaderstats:GetChildren()) do
+ StatAdded(i,playerEntry)
+ end
+ nplayer.leaderstats.ChildAdded:connect(function(nchild) StatAdded(nchild,playerEntry) end)
+ nplayer.leaderstats.ChildRemoved:connect(function(nchild) StatRemoved(nchild,playerEntry) end)
+end
+--[[
+ called when leaderstats object is removed from play in player entry
+ Note: may not be needed, might be able to just rely on leaderstats added
+ @Args:
+ oldLeaderstats leaderstats object to be removed
+ playerEntry A reference to the ENTRY(table) of the player
+--]]
+function LeaderstatsRemoved(oldLeaderstats,playerEntry)
+ while AddingFrameLock do debugprint('waiting to insert '..playerEntry['Player'].Name) wait(1/30) end
+ AddingFrameLock = true
+ RemoveAllStats(playerEntry)
+ AddingFrameLock = false
+end
+
+function ClosePopUpPanel()
+ if SelectedPlayerEntry then
+ local tframe = SelectedPlayerEntry['Frame']
+ Spawn(function() TweenProperty(tframe,'BackgroundTransparency',.5,1,BASE_TWEEN) end)
+ end
+ PopUpPanel:TweenPosition(UDim2.new(1,0,0,0), "Out", "Linear", BASE_TWEEN,true)
+ wait(.1)
+ InPopupWaitForClick= false
+ SelectedPlayerEntry = nil
+end
+
+--[[
+ prepares the needed popup to be tweened on screen, and updates the position of the popup clip
+ frame to match the selected player frame's position
+--]]
+function InitMovingPanel( entry, player)
+ PopUpClipFrame.Parent= ScreenGui
+
+ if PopUpPanel then
+ PopUpPanel:Destroy()
+ end
+ PopUpPanel= PopUpPanelTemplate:Clone()
+ PopUpPanel.Parent= PopUpClipFrame
+
+ local nextIndex = 2
+ local friendStatus = GetFriendStatus(player)
+ debugprint (tostring(friendStatus))
+ local showRankMenu = IsPersonalServer and LocalPlayer.PersonalServerRank >= PrivilegeLevel['Admin'] and LocalPlayer.PersonalServerRank > SelectedPlayer.PersonalServerRank
+
+
+ local ReportPlayerButton = MakePopupButton(PopUpPanel,'Report Player',0)
+ ReportPlayerButton.MouseButton1Click:connect(function() OpenAbuseDialog() end)
+
+ local enableFriendRequests = true
+
+ if enableFriendRequests then
+ local FriendPlayerButton = MakePopupButton(PopUpPanel,'Friend',1, not showRankMenu and friendStatus~=Enum.FriendStatus.FriendRequestReceived)
+ FriendPlayerButton.MouseButton1Click:connect(OnFriendButtonSelect)
+
+
+ if friendStatus==Enum.FriendStatus.Friend then
+ FriendPlayerButton:FindFirstChild('ButtonText').Text='UnFriend Player'
+ elseif friendStatus==Enum.FriendStatus.Unknown or friendStatus==Enum.FriendStatus.NotFriend then
+ FriendPlayerButton:FindFirstChild('ButtonText').Text='Send Request'
+ elseif friendStatus==Enum.FriendStatus.FriendRequestSent then
+ FriendPlayerButton:FindFirstChild('ButtonText').Text='Revoke Request'
+ elseif friendStatus==Enum.FriendStatus.FriendRequestReceived then
+ FriendPlayerButton:FindFirstChild('ButtonText').Text='Accept Friend'
+ local FriendRefuseButton = MakePopupButton(PopUpPanel,'Decline Friend',2,not showRankMenu)
+ FriendRefuseButton.MouseButton1Click:connect(OnFriendRefuseButtonSelect)
+ nextIndex=nextIndex+1
+ end
+ end
+
+ if showRankMenu then
+ local BanPlayerButton = MakePopupButton(PopUpPanel,'Ban',nextIndex)
+ local VisitorButton = MakePopupButton(PopUpPanel,'Visitor',nextIndex+1)
+ local MemberButton = MakePopupButton(PopUpPanel,'Member',nextIndex+2)
+ local AdminButton = MakePopupButton(PopUpPanel,'Admin',nextIndex+3,true)
+
+ BanPlayerButton.MouseButton1Click:connect(function()
+ OnPrivilegeLevelSelect(player,PrivilegeLevel['Banned'],BanPlayerButton,VisitorButton,MemberButton,AdminButton)
+ end)
+ VisitorButton.MouseButton1Click:connect(function()
+ OnPrivilegeLevelSelect(player,PrivilegeLevel['Visitor'],BanPlayerButton,VisitorButton,MemberButton,AdminButton)
+ end)
+ MemberButton.MouseButton1Click:connect(function()
+ OnPrivilegeLevelSelect(player,PrivilegeLevel['Member'],BanPlayerButton,VisitorButton,MemberButton,AdminButton)
+ end)
+ AdminButton.MouseButton1Click:connect(function()
+ OnPrivilegeLevelSelect(player,PrivilegeLevel['Admin'],BanPlayerButton,VisitorButton,MemberButton,AdminButton)
+ end)
+
+ HighlightMyRank(SelectedPlayer,BanPlayerButton,VisitorButton,MemberButton,AdminButton)
+ end
+
+ PopUpPanel:TweenPosition(UDim2.new(0,0,0,0), "Out", "Linear", BASE_TWEEN,true)
+ Delay(0, function()
+ local tconnection
+ tconnection = Mouse.Button1Down:connect(function()
+ tconnection:disconnect()
+ ClosePopUpPanel()
+ end)
+ end)
+
+ local myFrame = entry['Frame']
+ -- THIS IS GARBAGE.
+ -- if I parent to frame to auto update position, it gets clipped
+ -- sometimes garbage is the only option.
+ Spawn(function()
+ while InPopupWaitForClick do
+ PopUpClipFrame.Position=UDim2.new( 0,myFrame.AbsolutePosition.X-PopUpClipFrame.Size.X.Offset,0,myFrame.AbsolutePosition.Y)
+ wait()
+ end
+ end)
+
+end
+
+--[[
+ Called when a player entry in the leaderboard is clicked
+ either will highlight entry and start the drag event, or open a popup menu
+ @Args:
+ entry the player entry clicked
+--]]
+function OnPlayerEntrySelect(entry,startx,starty)
+
+ if not InPopupWaitForClick then
+
+ SelectedPlayerEntry = entry
+ SelectedPlayer = entry['Player']
+
+ StartDrag(entry,startx,starty)
+ end
+
+
+end
+
+function ActivatePlayerEntryPanel(entry)
+ entry['Frame'].BackgroundColor3 = Color3.new(0,1,1)
+ Spawn(function() TweenProperty(entry['Frame'],'BackgroundTransparency',1,.5,.5) end)
+ InPopupWaitForClick=true
+ InitMovingPanel(entry,entry['Player'])
+end
+
+--[[
+ the basic update for the playerlist mode's state,
+ assures the order and length of the player frames
+--]]
+function PlayerListModeUpdate()
+ RecreateScoreColumns(PlayerFrames)
+ table.sort(PlayerFrames,PlayerSortFunction)
+ for i,val in ipairs(PlayerFrames) do
+ MiddleFrames[i] = val['Frame']
+ end
+ for i = #PlayerFrames + 1,#MiddleFrames,1 do
+ MiddleFrames[i] = nil
+ end
+ UpdateMinimize()
+end
+--[[
+ this one's a doozie, happens when a player is added to the game
+ inits their player frame and player entry, assigns them to a team if possible,
+ and hooks up their leaderstats
+ @Args:
+ nplayer new player object to insert
+--]]
+function InsertPlayerFrame(nplayer)
+ while AddingFrameLock do debugprint('waiting to insert '..nplayer.Name) wait(1/30) end
+ AddingFrameLock = true
+
+ local nFrame = MiddleTemplate:Clone()
+
+ local playerName = nplayer.Name
+ local clansEnabled, clanTag = pcall(function() nplayer:GetClanTag() end)
+ if clansEnabled and clanTag and clanTag ~= "" then
+ playerName = "[" .. tostring(clanTag) .. "] " .. playerName
+ end
+
+ WaitForChild(WaitForChild(nFrame,'TitleFrame'),'Title').Text = playerName
+
+ nFrame.Position = UDim2.new(1,0,((#MiddleFrames) * nFrame.Size.Y.Scale),0)
+
+ local nfriendstatus = GetFriendStatus(nplayer)
+
+ nFrame:FindFirstChild('BCLabel').Image = getMembershipTypeIcon(nplayer.MembershipType,nplayer)
+ nFrame:FindFirstChild('FriendLabel').Image = getFriendStatusIcon(nfriendstatus)
+ nFrame.Name = nplayer.Name
+ WaitForChild(WaitForChild(nFrame,'TitleFrame'),'Title').Text = playerName
+
+ --move for bc label
+ nFrame.FriendLabel.Position=nFrame.FriendLabel.Position+UDim2.new(0,17,0,0)
+ nFrame.TitleFrame.Title.Position=nFrame.TitleFrame.Title.Position+UDim2.new(0,17,0,0)
+
+ if(nFrame:FindFirstChild('FriendLabel').Image ~= '') then
+ nFrame.TitleFrame.Title.Position=nFrame.TitleFrame.Title.Position+UDim2.new(0,17,0,0)
+ end
+
+ if nplayer.Name == LocalPlayer.Name then
+ nFrame.TitleFrame.Title.Font = 'ArialBold'
+ nFrame.PlayerScore.Font = 'ArialBold'
+ ChangeHeaderName(playerName)
+ local dropShadow = nFrame.TitleFrame.Title:Clone()
+ dropShadow.TextColor3 = Color3.new(0,0,0)
+ dropShadow.TextTransparency=0
+ dropShadow.ZIndex=2
+ dropShadow.Position=nFrame.TitleFrame.Title.Position+UDim2.new(0,1,0,1)
+ dropShadow.Name='DropShadow'
+ dropShadow.Parent= nFrame.TitleFrame
+ else
+ --Delay(2, function () OnFriendshipChanged(nplayer,LocalPlayer:GetFriendStatus(nplayer)) end)
+ end
+ nFrame.TitleFrame.Title.Font = 'ArialBold'
+
+
+ nFrame.Parent = ListFrame
+ nFrame.Position = UDim2.new(.5,0,((#MiddleFrames) * nFrame.Size.Y.Scale),0)
+ UpdateMinimize()
+ local nentry = {}
+ nentry['Frame'] = nFrame
+ nentry['Player'] = nplayer
+ nentry['ID'] = AddId
+ AddId = AddId + 1
+ table.insert(PlayerFrames,nentry)
+ if #TeamFrames~=0 then
+
+ if nplayer.Neutral then
+ nentry['MyTeam'] = nil
+ if not NeutralTeam then
+ AddNeutralTeam()
+ else
+ AddPlayerToTeam(NeutralTeam,nentry)
+ end
+
+ else
+ local addedToTeam=false
+ for i,tval in ipairs(TeamFrames) do
+ if tval['MyTeam'].TeamColor == nplayer.TeamColor then
+ AddPlayerToTeam(tval,nentry)
+ nentry['MyTeam'] = tval
+ addedToTeam=true
+ end
+ end
+ if not addedToTeam then
+ nentry['MyTeam']=nil
+ if not NeutralTeam then
+ AddNeutralTeam()
+ else
+ AddPlayerToTeam(NeutralTeam,nentry)
+ end
+ nentry['MyTeam'] = NeutralTeam
+ end
+ end
+
+ end
+
+ if nplayer:FindFirstChild('leaderstats') then
+ LeaderstatsAdded(nentry)
+ end
+
+ nplayer.ChildAdded:connect(function(nchild)
+ if nchild.Name == 'leaderstats' then
+ while AddingFrameLock do debugprint('in adding leaderstats lock') wait(1/30) end
+ AddingFrameLock = true
+ LeaderstatsAdded(nentry)
+ AddingFrameLock = false
+ end
+ end)
+
+ nplayer.ChildRemoved:connect(function (nchild)
+ if nplayer==LocalPlayer and nchild.Name == 'leaderstats' then
+ LeaderstatsRemoved(nchild,nentry)
+ end
+ end)
+ nplayer.Changed:connect(function(prop)PlayerChanged(nentry,prop) end)
+
+ local listener = WaitForChild(nFrame,'ClickListener')
+ listener.Active = true
+ listener.MouseButton1Down:connect(function(nx,ny) OnPlayerEntrySelect(nentry, nx,ny) end)
+
+ AddMiddleBGFrame()
+ BaseUpdate()
+ AddingFrameLock = false
+end
+
+--[[
+ Note:major optimization can be done here
+ removes this player's frame if it exists, calls base update
+--]]
+function RemovePlayerFrame(tplayer)
+ while AddingFrameLock do debugprint('in removing player frame lock') wait(1/30) end
+ AddingFrameLock = true
+
+ local tteam
+ for i,key in ipairs(PlayerFrames) do
+ if tplayer == key['Player'] then
+ if PopUpClipFrame.Parent == key['Frame'] then
+ PopUpClipFrame.Parent = nil
+ end
+ key['Frame']:Destroy()
+ tteam=key['MyTeam']
+ table.remove(PlayerFrames,i)
+ end
+ end
+ if tteam then
+ for j,tentry in ipairs(tteam['MyPlayers']) do
+ if tentry['Player'] == tplayer then
+ RemovePlayerFromTeam(tteam,j)
+ end
+ end
+ end
+
+ RemoveMiddleBGFrame()
+ UpdateMinimize()
+ BaseUpdate()
+ AddingFrameLock = false
+end
+
+Players.ChildRemoved:connect(RemovePlayerFrame)
+
+----------------------------
+-- Team Callback Functions
+----------------------------
+--[[
+ turns a list of team entries with sub lists of players into a single ordered
+ list, in the correct order,and of the correct length
+ @Args:
+ tframes the team entries to unroll
+ outframes the list to unroll these entries into
+--]]
+function UnrollTeams(tframes,outframes)
+ local numEntries = 0
+ if NeutralTeam and not NeutralTeam['IsHidden'] then
+ for i,val in ipairs(NeutralTeam['MyPlayers']) do
+ numEntries = numEntries + 1
+ outframes[numEntries] = val['Frame']
+ end
+ numEntries = numEntries + 1
+ outframes[numEntries] = NeutralTeam['Frame']
+ end
+ for i,val in ipairs(tframes) do
+ if not val['IsHidden'] then
+ for j,pval in ipairs(val.MyPlayers) do
+ numEntries = numEntries + 1
+ outframes[numEntries] = pval['Frame']
+ end
+ numEntries = numEntries + 1
+ outframes[numEntries] = val['Frame']
+ end
+ end
+ -- clear any additional entries from outframes
+ for i = numEntries + 1,#outframes,1 do
+ outframes[i] = nil
+ end
+end
+--[[
+ uses lua's table.sort to sort the teams
+--]]
+function TeamSortFunc(a,b)
+ if a['TeamScore'] == b['TeamScore'] then
+ return a['ID'] < b['ID']
+ end
+ if not a['TeamScore'] then return false end
+ if not b['TeamScore'] then return true end
+ return a['TeamScore'] < b['TeamScore']
+
+end
+--[[
+ consider adding lock with wait for performance
+ sorts each of the team's player lists induvidually, adds up the team scores.
+ @Args:
+ tentries table of team entries
+--]]
+function SortTeams(tentries)
+
+ for i,val in ipairs(tentries) do
+
+ table.sort(val['MyPlayers'],PlayerSortFunction)
+ AddTeamScores(val)
+ end
+ table.sort(tentries,TeamSortFunc)
+end
+--[[
+ base update for team mode, adds up the scores of all teams, sorts them,
+ then unrolls them into middleframes
+--]]
+function TeamListModeUpdate()
+ RecreateScoreColumns(PlayerFrames)
+ SortTeams(TeamFrames)
+ if NeutralTeam then
+ AddTeamScores(NeutralTeam)
+ --RecreateScoreColumns(NeutralTeam['MyPlayers'])
+ end
+ UnrollTeams(TeamFrames,MiddleFrames)
+end
+--[[
+ adds up all the score of this team's players to form the team score
+ @Args:
+ team team entry to sum the scores of
+--]]
+function AddTeamScores(team)
+
+ for j = 1, #ScoreNames,1 do
+ local i = ScoreNames[j]
+ local tscore = 0
+ for _,j in ipairs(team['MyPlayers']) do
+ local tval = j['Player']:FindFirstChild('leaderstats') and j['Player'].leaderstats:FindFirstChild(i['Name'])
+ if tval and not tval:IsA('StringValue') then
+ tscore = tscore + GetScoreValue((j['Player'].leaderstats)[i['Name'] ])
+ end
+ end
+ if team['Frame']:FindFirstChild(i['Name']) then
+ --team['Frame'][i['Name'] ].Size = UDim2.new(1 - (ScrollBarFrame.Size.X.Scale * 2),- ((j-1) * SpacingPerStat),1,0)
+ team['Frame'][i['Name'] ].Text = tostring(tscore)
+ end
+ end
+ UpdateMinimize()
+
+end
+
+--[[
+ finds previous team this player was on, and if it exists calls removeplayerfromteam
+ @Args
+ entry Player entry
+--]]
+function FindRemovePlayerFromTeam(entry)
+ if entry['MyTeam'] then
+ for j,oldEntry in ipairs(entry['MyTeam']['MyPlayers']) do
+ if oldEntry['Player'] == entry['Player'] then
+ RemovePlayerFromTeam(entry['MyTeam'],j)
+ return
+ end
+ end
+ elseif NeutralTeam then
+ for j,oldEntry in ipairs(NeutralTeam['MyPlayers']) do
+ if oldEntry['Player'] == entry['Player'] then
+ RemovePlayerFromTeam(NeutralTeam,j)
+ return
+ end
+ end
+ end
+end
+--[[
+ removes a single player from a given team (not usually called directly)
+ @Args:
+ teamEntry team entry to remove player from
+ index index of player in 'MyPlayers' list to remove
+--]]
+function RemovePlayerFromTeam(teamEntry,index)
+ table.remove(teamEntry['MyPlayers'],index)
+ --if teamEntry['AutoHide'] and #teamEntry['MyPlayers'] == 0 then
+ if teamEntry==NeutralTeam and #teamEntry['MyPlayers']==0 then
+ RemoveNeutralTeam()
+ end
+end
+--[[
+ adds player entry entry to teamentry
+ removes them from any previous team
+ @Args:
+ teamEntry entry of team to add player to
+ entry player entry to add to this team
+--]]
+function AddPlayerToTeam(teamEntry,entry)
+ FindRemovePlayerFromTeam(entry)
+ table.insert(teamEntry['MyPlayers'],entry)
+ entry['MyTeam'] = teamEntry
+ if teamEntry['IsHidden'] then
+ teamEntry['Frame'].Parent = ListFrame
+ AddMiddleBGFrame()
+ end
+ teamEntry['IsHidden'] = false
+end
+
+
+function SetPlayerToTeam(entry)
+ FindRemovePlayerFromTeam(entry)
+ -- check to see if team exists, if it does add to that team
+ local setToTeam = false
+ for i,tframe in ipairs(TeamFrames) do
+ -- add my entry on the new team
+ if tframe['MyTeam'].TeamColor == entry['Player'].TeamColor then
+ AddPlayerToTeam(tframe,entry)
+ setToTeam = true
+ end
+ end
+ -- if player was set to an invalid team, then set it back to neutral
+ if not setToTeam and #(game.Teams:GetTeams())>0 then
+ debugprint(entry['Player'].Name..'could not find team')
+ entry['MyTeam']=nil
+ if not NeutralTeam then
+ AddNeutralTeam()
+ else AddPlayerToTeam(NeutralTeam,entry) end
+ end
+end
+
+--[[
+ Note:another big one, consiter breaking up
+ called when any children of player changes
+ handles 'Neutral', teamColor, Name and MembershipType changes
+ @Args
+ entry Player entry changed
+ property name of property changed
+--]]
+function PlayerChanged(entry, property)
+ while PlayerChangedLock do
+ debugprint('in playerchanged lock')
+ wait(1/30)
+ end
+ PlayerChangedLock=true
+ if property == 'Neutral' then
+ -- if player changing to neutral
+ if entry['Player'].Neutral and #(game.Teams:GetTeams())>0 then
+ debugprint(entry['Player'].Name..'setting to neutral')
+ FindRemovePlayerFromTeam(entry)
+ entry['MyTeam']=nil
+ if not NeutralTeam then
+ debugprint(entry['Player'].Name..'creating neutral team')
+ AddNeutralTeam()
+ else
+ debugprint(entry['Player'].Name..'adding to neutral team')
+ AddPlayerToTeam(NeutralTeam,entry)
+ end
+ elseif #(game.Teams:GetTeams())>0 then -- else player switching to a team, or a weird edgecase
+ debugprint(entry['Player'].Name..'has been set non-neutral')
+ SetPlayerToTeam(entry)
+ end
+ BaseUpdate()
+ elseif property == 'TeamColor' and not entry['Player'].Neutral and entry['Player'] ~= entry['MyTeam'] then
+ debugprint(entry['Player'].Name..'setting to new team')
+ SetPlayerToTeam(entry)
+ BaseUpdate()
+ elseif property == 'Name' or property == 'MembershipType' then
+ entry['Frame']:FindFirstChild('BCLabel').Image = getMembershipTypeIcon(entry['Player'].MembershipType,entry['Player'])
+ entry['Frame'].Name = entry['Player'].Name
+ entry['Frame'].TitleFrame.Title.Text = entry['Player'].Name
+ if(entry['Frame'].BCLabel.Image ~= '') then
+ entry['Frame'].TitleFrame.Title.Position=UDim2.new(.01, 30, .1, 0)
+ end
+ if entry['Player'] == LocalPlayer then
+ entry['Frame'].TitleFrame.DropShadow.Text= entry['Player'].Name
+ ChangeHeaderName(entry['Player'].Name)
+ end
+ BaseUpdate()
+ end
+ PlayerChangedLock=false
+end
+
+function OnFriendshipChanged(player,friendStatus)
+
+ Delay(.5,function()
+ debugprint('friend status changed for:'..player.Name .." ".. tostring(friendStatus) .. " vs " .. tostring(GetFriendStatus(player)) )
+ for _, entry in ipairs(PlayerFrames) do
+ if entry['Player']==player then
+ local nicon = getFriendStatusIcon(friendStatus)
+ if nicon == '' and entry['Frame'].FriendLabel.Image ~= '' then
+ entry['Frame'].TitleFrame.Title.Position=entry['Frame'].TitleFrame.Title.Position-UDim2.new(0,17,0,0)
+ elseif nicon ~= '' and entry['Frame'].FriendLabel.Image == '' then
+ entry['Frame'].TitleFrame.Title.Position=entry['Frame'].TitleFrame.Title.Position+UDim2.new(0,17,0,0)
+ debugprint('confirmed status:'..player.Name)
+ end
+ entry['Frame'].FriendLabel.Image = nicon
+ return
+ end
+ end
+ end)
+end
+
+LocalPlayer.FriendStatusChanged:connect(OnFriendshipChanged)
+
+--[[
+ adds a neutral team if nessisary
+ Note: a lot of redundant code here, might want to refactor to share a function with insertteamframe
+--]]
+function AddNeutralTeam()
+ while NeutralTeamLock do debugprint('in neutral team 2 lock') wait() end
+ NeutralTeamLock = true
+
+ local defaultTeam = Instance.new('Team')
+ defaultTeam.TeamColor = BrickColor.new('White')
+ defaultTeam.Name = 'Neutral'
+ local nentry = {}
+ nentry['MyTeam'] = defaultTeam
+ nentry['MyPlayers'] = {}
+ nentry['Frame'] = MiddleTemplate:Clone()
+ WaitForChild(WaitForChild(nentry['Frame'],'TitleFrame'),'Title').Text = defaultTeam.Name
+ nentry['Frame'].TitleFrame.Position=UDim2.new(nentry['Frame'].TitleFrame.Position.X.Scale,nentry['Frame'].TitleFrame.Position.X.Offset,.1,0)
+ nentry['Frame'].TitleFrame.Size=UDim2.new(nentry['Frame'].TitleFrame.Size.X.Scale,nentry['Frame'].TitleFrame.Size.X.Offset,.8,0)
+ nentry['Frame'].TitleFrame.Title.Font = 'ArialBold'
+ nentry['Frame'].Position = UDim2.new(1,0,((#MiddleFrames) * nentry['Frame'].Size.Y.Scale),0)
+ WaitForChild(nentry['Frame'],'ClickListener').MouseButton1Down:connect(function(nx,ny) StartDrag(nentry,nx,ny) end)
+ nentry['Frame'].ClickListener.BackgroundColor3 = Color3.new(1,1,1)
+ nentry['Frame'].ClickListener.BackgroundTransparency = .7
+ nentry['Frame'].ClickListener.AutoButtonColor=false
+ nentry['AutoHide'] = true
+ nentry['IsHidden'] = true
+ for _,i in pairs(PlayerFrames) do
+ if i['Player'].Neutral or not i['MyTeam'] then
+ AddPlayerToTeam(nentry,i)
+ end
+ end
+ if #nentry['MyPlayers'] > 0 then
+ NeutralTeam = nentry
+ UpdateMinimize()
+ BaseUpdate()
+ end
+ NeutralTeamLock = false
+end
+
+function RemoveNeutralTeam()
+ while NeutralTeamLock do debugprint('in neutral team lock') wait() end
+ NeutralTeamLock = true
+ NeutralTeam['Frame']:Destroy()
+ NeutralTeam=nil
+ RemoveMiddleBGFrame()
+ NeutralTeamLock = false
+end
+
+--[[
+
+--]]
+function TeamScoreChanged(entry,nscore)
+ WaitForChild(entry['Frame'],'PlayerScore').Text = tostring(nscore)
+ entry['TeamScore'] = nscore
+end
+--[[
+ called when child added to a team, used for autohide functionality
+ Note: still has teamscore, consiter removing
+--]]
+function TeamChildAdded(entry,nchild)
+ if nchild.Name == 'AutoHide' then
+ entry['AutoHide'] = true
+ elseif nchild.Name == 'TeamScore' then
+ WaitForChild(entry['Frame'],'PlayerScore').Text = tostring(nchild.Value)
+ entry['TeamScore'] = nchild.Value
+ nchild.Changed:connect(function() TeamScoreChanged(entry,nchild.Value) end)
+ end
+end
+--[[
+ called when child added to a team, used for autohide functionality
+ Note: still has teamscore, consiter removing
+--]]
+function TeamChildRemoved(entry,nchild)
+ if nchild.Name == 'AutoHide' then
+ entry['AutoHide'] = false
+ elseif nchild.Name == 'TeamScore' then
+ WaitForChild(entry['Frame'],'PlayerScore').Text = ""
+ entry['TeamScore'] = nil
+ end
+end
+
+function TeamChanged(entry, property)
+ if property=='Name' then
+ WaitForChild(WaitForChild(entry['Frame'],'TitleFrame'),'Title').Text = entry['MyTeam'].Name
+
+ elseif property=='TeamColor' then
+ entry['Frame'].ClickListener.BackgroundColor3 = entry['MyTeam'].TeamColor.Color
+
+ for _,i in pairs(TeamFrames) do
+ if i['MyTeam'].TeamColor == entry['MyTeam'] then
+ RemoveTeamFrame(entry['MyTeam']) --NO DUPLICATE TEAMS!
+ end
+ end
+
+ entry['MyPlayers']={}
+
+ for _,i in pairs(PlayerFrames) do
+ SetPlayerToTeam(i)
+ end
+ BaseUpdate()
+ end
+end
+
+--[[
+ creates team entry and frame for this team, sets up listeners for this team
+ adds any players intended for this team,Creates neutral team if this is the first team added
+ Note:might be best to break this into multiple functions to simplify
+ @Args:
+ nteam new team object added
+--]]
+function InsertTeamFrame(nteam)
+ while AddingFrameLock do debugprint('in adding team frame lock') wait(1/30) end
+ AddingFrameLock = true
+ --for _,i in pairs(TeamFrames) do
+ local nentry = {}
+ nentry['MyTeam'] = nteam
+ nentry['MyPlayers'] = {}
+ nentry['Frame'] = MiddleTemplate:Clone()
+ WaitForChild(WaitForChild(nentry['Frame'],'TitleFrame'),'Title').Text = nteam.Name
+ nentry['Frame'].TitleFrame.Title.Font = 'ArialBold'
+ nentry['Frame'].TitleFrame.Title.FontSize = 'Size18'
+ nentry['Frame'].TitleFrame.Position=UDim2.new(nentry['Frame'].TitleFrame.Position.X.Scale,nentry['Frame'].TitleFrame.Position.X.Offset,.1,0)
+ nentry['Frame'].TitleFrame.Size=UDim2.new(nentry['Frame'].TitleFrame.Size.X.Scale,nentry['Frame'].TitleFrame.Size.X.Offset,.8,0)
+ nentry['Frame'].Position = UDim2.new(0.5,0,((#MiddleFrames) * nentry['Frame'].Size.Y.Scale),0)
+ WaitForChild(nentry['Frame'],'ClickListener').MouseButton1Down:connect(function(nx,ny) StartDrag(nentry,nx,ny) end)
+ nentry['Frame'].ClickListener.BackgroundColor3 = nteam.TeamColor.Color
+ nentry['Frame'].ClickListener.BackgroundTransparency = .7
+ nentry['Frame'].ClickListener.AutoButtonColor=false
+ AddId = AddId + 1
+ nentry['ID'] = AddId
+ nentry['AutoHide'] = false
+ if nteam:FindFirstChild('AutoHide') then
+ nentry['AutoHide'] = true
+ end
+ if nteam:FindFirstChild('TeamScore') then
+ TeamChildAdded(nentry,nteam.TeamScore)
+
+ end
+
+ nteam.ChildAdded:connect(function(nchild) TeamChildAdded(nentry,nchild) end)
+ nteam.ChildRemoved:connect(function(nchild) TeamChildRemoved(nentry,nchild) end)
+ nteam.Changed:connect(function(prop) TeamChanged(nentry,prop) end)
+
+ for _,i in pairs(PlayerFrames) do
+ if not i['Player'].Neutral and i['Player'].TeamColor == nteam.TeamColor then
+ AddPlayerToTeam(nentry,i)
+ end
+ end
+ nentry['IsHidden'] = false
+ if not nentry['AutoHide'] or #nentry['MyPlayers'] > 0 then
+ nentry['Frame'].Parent = ListFrame
+ AddMiddleBGFrame()
+ else
+ nentry['IsHidden'] = true
+ nentry['Frame'].Parent = nil
+ end
+
+ table.insert(TeamFrames,nentry)
+ UpdateMinimize()
+ BaseUpdate()
+ if #TeamFrames == 1 and not NeutralTeam then
+ AddNeutralTeam()
+ end
+ AddingFrameLock = false
+end
+--[[
+ removes team from team list
+ @Args:
+ nteam Teamobject to remove
+--]]
+function RemoveTeamFrame(nteam)
+ while AddingFrameLock do debugprint('in removing team frame lock') wait(1/30) end
+ AddingFrameLock = true
+ if IsMinimized.Value then
+ end
+ local myEntry
+ for i,key in ipairs(TeamFrames) do
+ if nteam == key['MyTeam'] then
+ myEntry = key
+ key['Frame']:Destroy()
+ table.remove(TeamFrames,i)
+ end
+ end
+ if #TeamFrames==0 then
+ debugprint('removeteamframe, remove neutral')
+ if NeutralTeam then
+ RemoveNeutralTeam()
+ end
+ end
+ for i,key in ipairs(myEntry['MyPlayers']) do
+ RemovePlayerFromTeam(myEntry,i)
+ PlayerChanged(key, 'TeamColor')
+ end
+ RemoveMiddleBGFrame()
+ BaseUpdate()
+ AddingFrameLock = false
+end
+
+function TeamAdded(nteam)
+ InsertTeamFrame(nteam)
+end
+
+function TeamRemoved(nteam)
+ RemoveTeamFrame(nteam)
+end
+ ---------------------------------
+--[[
+ called when ANYTHING changes the state of the playerlist
+ re-sorts everything,assures correct positions of all elements
+--]]
+function BaseUpdate()
+ while BaseUpdateLock do debugprint('in baseupdate lock') wait(1/30) end
+ BaseUpdateLock = true
+ --print ('baseupdate')
+ UpdateStatNames()
+
+ if #TeamFrames == 0 and not NeutralTeam then
+ PlayerListModeUpdate()
+ else
+ TeamListModeUpdate()
+ end
+ for i,key in ipairs(MiddleFrames) do
+ if key.Parent ~= nil then
+ key.Position = UDim2.new(.5,0,((#MiddleFrames - (i)) * key.Size.Y.Scale),0)
+ end
+ end
+ if not IsMinimized.Value and #MiddleFrames>DefaultEntriesOnScreen then
+ UpdateScrollPosition()
+ end
+
+ UpdateMinimize()
+
+ UpdateScrollBarSize()
+ UpdateScrollPosition()
+
+ UpdateScrollBarVisibility()
+ --debugprint('EndBaseUpdate')
+ BaseUpdateLock = false
+end
+
+--[[
+ code for attaching tab key to maximizing player list
+--]]
+game.GuiService:AddKey("\t")
+local LastTabTime = time()
+game.GuiService.KeyPressed:connect(
+function(key)
+ if key == "\t" then
+ debugprint('caught tab key')
+ local modalCheck, isModal = pcall(function() return game.GuiService.IsModalDialog end)
+ if modalCheck == false or (modalCheck and isModal == false) then
+ if time() - LastTabTime > 0.4 then
+ LastTabTime = time()
+ if IsTabified.Value then
+ if not IsMaximized.Value then
+ ScreenGui:TweenPosition(UDim2.new(0, 0, 0,0),'Out','Linear',BASE_TWEEN*1.2,true)
+ IsMaximized.Value = true
+ else
+ ScreenGui:TweenPosition(UDim2.new(NormalBounds.X.Scale, NormalBounds.X.Offset-10, 0,0),'Out','Linear',BASE_TWEEN*1.2,true)
+ IsMaximized.Value = false
+ IsMinimized.Value=true
+ end
+ else
+ ToggleMaximize()
+ end
+
+ end
+ end
+ end
+end)
+
+
+function PlayersChildAdded(tplayer)
+ if tplayer:IsA('Player') then
+ Spawn(function() debugPlayerAdd(tplayer) end)
+ else
+ BlowThisPopsicleStand()
+ end
+end
+
+function coreGuiChanged(coreGuiType, enabled)
+ if coreGuiType == Enum.CoreGuiType.All or coreGuiType == Enum.CoreGuiType.PlayerList then
+ MainFrame.Visible = enabled
+ end
+end
+
+function TeamsChildAdded(nteam)
+ if nteam:IsA('Team') then
+ TeamAdded(nteam)
+ else
+ BlowThisPopsicleStand()
+ end
+end
+
+function TeamsChildRemoved(nteam)
+ if nteam:IsA('Team') then
+ TeamRemoved(nteam)
+ else
+ BlowThisPopsicleStand()
+ end
+end
+
+ ----------------------------
+ -- Hookups and initialization
+ ----------------------------
+function debugPlayerAdd(p)
+ InsertPlayerFrame(p)
+end
+
+pcall(function()
+ coreGuiChanged(Enum.CoreGuiType.PlayerList, Game.StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.PlayerList))
+ Game.StarterGui.CoreGuiChangedSignal:connect(coreGuiChanged)
+end)
+
+while not game:GetService('Teams') do wait(1/30) debugprint('Waiting For Teams') end
+for _,i in pairs(game.Teams:GetTeams()) do TeamAdded(i) end
+for _,i in pairs(Players:GetPlayers()) do Spawn(function() debugPlayerAdd(i) end) end
+
+game.Teams.ChildAdded:connect(TeamsChildAdded)
+game.Teams.ChildRemoved:connect(TeamsChildRemoved)
+Players.ChildAdded:connect(PlayersChildAdded)
+
+InitReportAbuse()
+AreNamesExpanded.Value = true
+BaseUpdate()
+
+
+
+--UGGGLY,find a better way later
+wait(2)
+IsPersonalServer= not not game.Workspace:FindFirstChild("PSVariable")
+
+ ----------------------------
+ -- Running Logic
+ ----------------------------
+
+ --debug stuffs, will only run for 'newplayerlistisbad'
+ if LocalPlayer.Name == 'newplayerlistisbad' or LocalPlayer.Name == 'imtotallyadmin' then
+ debugFrame.Parent = ScreenGui
+ Spawn(function()
+ while true do
+ local str_players=''
+ for _,i in pairs(game.Players:GetPlayers()) do
+ str_players= str_players .." " .. i.Name
+ end
+ debugplayers.Text=str_players
+ wait(.5)
+ end
+ end)
+ end
+
+
diff --git a/app/files/CoreGui/48488236 b/app/files/CoreGui/48488236
new file mode 100644
index 0000000..8ac9234
--- /dev/null
+++ b/app/files/CoreGui/48488236
@@ -0,0 +1,2425 @@
+local RbxGui
+
+local localTesting = true
+
+local screen = script.Parent
+local screenResizeCon = nil
+
+local friendWord = "Friend"
+local friendWordLowercase = "friend"
+
+local elementNames = {}
+local elementNameToElement = {}
+
+local privilegeOwner = 255
+local privilegeAdmin = 240
+local privilegeMember = 128
+local privilegeVisitor = 10
+local privilegeBanned = 0
+
+local inContextMenu = false
+local contextMenu3d = true
+
+local bigEasingStyle = Enum.EasingStyle.Back
+local smallEasingStyle = Enum.EasingStyle.Quart
+
+local personalServerContextAdded = false
+local personalServerPlace = false
+local success = pcall(function() personalServerPlace = game.IsPersonalServer end)
+if not success then
+ personalServerPlace = false
+end
+
+local friendRequestBlacklist = {}
+local otherPlayerBlacklist = {}
+
+local currentSortName = ""
+
+local function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+
+local function waitForProperty(instance, prop)
+ while not instance[prop] do
+ instance.Changed:wait()
+ end
+end
+
+local function Color3I(r,g,b)
+ return Color3.new(r/255,g/255,b/255)
+end
+
+function robloxLock(instance)
+ instance.RobloxLocked = true
+ children = instance:GetChildren()
+ if children then
+ for i, child in ipairs(children) do
+ robloxLock(child)
+ end
+ end
+end
+
+function ArrayRemove(t, obj)
+ for i, obj2 in ipairs(t) do
+ if obj == obj2 then
+ table.remove(t, i)
+ return true
+ end
+ end
+ return false
+end
+
+local function getPlayers()
+ local result = {}
+ local players = game:GetService("Players"):GetChildren()
+ if players then
+ for i, player in ipairs(players) do
+ if player:IsA("Player") then
+ table.insert(result, player)
+ end
+ end
+ end
+ return result
+end
+
+local brickColorTable = {}
+for i = 0, 63 do
+ brickColorTable[BrickColor.palette(i).Name] = BrickColor.palette(i).Color
+end
+
+local function remapColor(i, j)
+ brickColorTable[BrickColor.palette(i).Name] = BrickColor.palette(j).Color
+end
+
+remapColor(13, 12)
+remapColor(14, 12)
+remapColor(15, 12)
+remapColor(61, 29)
+remapColor(63, 62)
+remapColor(56, 50)
+remapColor(45, 53)
+remapColor(51, 20)
+remapColor(4, 20)
+remapColor(59, 35)
+remapColor(60, 29)
+
+local function getColor(brickColor)
+ if brickColorTable[brickColor.Name] then
+ return brickColorTable[brickColor.Name]
+ else
+ return brickColor.Color;
+ end
+end
+
+
+
+local function getTeams()
+ local result = {}
+ local teams = game:GetService("Teams"):GetChildren()
+ for i, team in ipairs(teams) do
+ if team:IsA("Team") then
+ table.insert(result, team)
+ end
+ end
+ return result
+end
+
+local supportFriends = true
+local currentBoardType = "PlayerList"
+local currentStatCount = 0
+
+local createBoardsFunction = nil
+
+
+local playerTable = {}
+local teamTable = {}
+local teamColorTable = {}
+
+local removePlayerFunction = nil
+local recreatePlayerFunction = nil
+local addPlayerFunction = function(player)
+ if recreatePlayerFunction then
+ recreatePlayerFunction(player)
+ end
+end
+local sortPlayerListsFunction = nil
+
+local minimizedState = nil
+local bigWindowImposter = nil
+local smallWindowPosition = UDim2.new(0, -20, 0,5)
+local smallWindowSize = UDim2.new(1,0,1,0)
+local bigWindowSize = UDim2.new(0.6,0,0.6,0)
+local bigWindowPosition = UDim2.new(.2, 0, .2,0)
+
+local smallWindowHeaderYSize = 32
+
+local debounceTeamsChanged = false
+
+local currentWindowState = "Small"
+local previousWindowState = nil
+local transitionWindowsFunction = nil
+
+local container = nil
+local topRightTrayContainer = nil
+
+local playerContextMenu = nil
+local contextMenuElements = {}
+local updateContextMenuItems = nil
+
+local function addContextMenuLabel(getText1, getText2, isVisible)
+ local t = {}
+ t.Type = "Label"
+ t.GetText1 = getText1
+ t.GetText2 = getText2
+ t.IsVisible = isVisible
+ table.insert(contextMenuElements, t)
+end
+local function addContextMenuButton(text, isVisible, isActive, doIt)
+ local t = {}
+ t.Text = text
+ t.Type = "Button"
+ t.IsVisible = isVisible
+ t.IsActive = isActive
+ t.DoIt = doIt
+ table.insert(contextMenuElements, t)
+end
+
+local function getFriendStatus(player)
+ if player == game.Players.LocalPlayer then
+ return Enum.FriendStatus.NotFriend
+ else
+ local success, result = pcall(function() return game.Players.LocalPlayer:GetFriendStatus(player) end)
+ if success then
+ return result
+ else
+ return Enum.FriendStatus.NotFriend
+ end
+ end
+end
+
+
+local function getPrivilegeType(player)
+ local rank = player.PersonalServerRank
+ if rank >= privilegeOwner then
+ return privilegeOwner
+ elseif rank < privilegeOwner and rank >= privilegeAdmin then
+ return privilegeAdmin
+ elseif rank < privilegeAdmin and rank >= privilegeMember then
+ return privilegeMember
+ elseif rank < privilegeMember and rank >= privilegeVisitor then
+ return privilegeVisitor
+ else
+ return privilegeBanned
+ end
+end
+
+--Populate the ContextMenus
+addContextMenuLabel(
+ --GetText1
+ function(player)
+ return "Loading..."
+ end,
+ --GetText2
+ nil,
+ --IsVisible
+ function(player)
+ return getFriendStatus(player) == Enum.FriendStatus.Unknown
+ end)
+
+addContextMenuButton("Send " .. friendWord .. " Request",
+ --IsVisible
+ function(player)
+ return (not otherPlayerBlacklist[player]) and (getFriendStatus(player) == Enum.FriendStatus.NotFriend)
+ end,
+ --IsActive
+ function(player)
+ return true
+ end,
+ --DoIt?
+ function(player)
+ otherPlayerBlacklist[player] = true
+ return game.Players.LocalPlayer:RequestFriendship(player)
+ end
+)
+addContextMenuButton("Un" .. friendWordLowercase,
+ --IsVisible
+ function(player)
+ return getFriendStatus(player) == Enum.FriendStatus.Friend
+ end,
+ --IsActive
+ function(player)
+ return true
+ end,
+ --DoIt
+ function(player)
+ return game.Players.LocalPlayer:RevokeFriendship(player)
+ end
+)
+addContextMenuButton("Accept " .. friendWord .. " Request",
+ --IsVisible
+ function(player)
+ return (not friendRequestBlacklist[player]) and (getFriendStatus(player) == Enum.FriendStatus.FriendRequestReceived)
+ end,
+ --IsActive
+ function(player)
+ return true
+ end,
+ --DoIt
+ function(player)
+ return game.Players.LocalPlayer:RequestFriendship(player)
+ end
+)
+
+addContextMenuButton("Deny " .. friendWord .. " Request",
+ --IsVisible
+ function(player)
+ return getFriendStatus(player) == Enum.FriendStatus.FriendRequestReceived
+ end,
+ --IsActive
+ function(player)
+ return true
+ end,
+ --DoIt
+ function(player)
+ friendRequestBlacklist[player] = true
+ return game.Players.LocalPlayer:RevokeFriendship(player)
+ end
+)
+
+addContextMenuButton("Cancel " .. friendWord .. " Request",
+ --IsVisible
+ function(player)
+ return false -- disable cancel request for now (can lead to griefing)
+ --return getFriendStatus(player) == Enum.FriendStatus.FriendRequestSent
+ end,
+ --IsActive
+ function(player)
+ return true
+ end,
+ --DoIt
+ function(player)
+ otherPlayerBlacklist[player] = false
+ return game.Players.LocalPlayer:RevokeFriendship(player)
+ end
+)
+
+function addPersonalServerContext()
+ if personalServerContextAdded then return end
+ personalServerContextAdded = true
+ addContextMenuButton("Ban",
+ --IsVisible
+ function(player)
+ return ( getPrivilegeType(game.Players.LocalPlayer) >= privilegeAdmin and (getPrivilegeType(player) < privilegeAdmin) )
+ end,
+ --IsActive
+ function(player)
+ return true
+ end,
+ --DoIt
+ function(player)
+ player.PersonalServerRank = privilegeBanned
+ return true
+ end
+ )
+ addContextMenuButton("Promote to Visitor",
+ --IsVisible
+ function(player)
+ return ( getPrivilegeType(game.Players.LocalPlayer) >= privilegeAdmin ) and ( getPrivilegeType(player) == privilegeBanned )
+ end,
+ --IsActive
+ function(player)
+ return true
+ end,
+ --DoIt
+ function(player)
+ game:GetService("PersonalServerService"):Promote(player)
+ return true
+ end
+ )
+ addContextMenuButton("Promote to Member",
+ --IsVisible
+ function(player)
+ return ( getPrivilegeType(game.Players.LocalPlayer) >= privilegeAdmin ) and ( getPrivilegeType(player) == privilegeVisitor )
+ end,
+ --IsActive
+ function(player)
+ return true
+ end,
+ --DoIt
+ function(player)
+ game:GetService("PersonalServerService"):Promote(player)
+ return true
+ end
+ )
+ addContextMenuButton("Promote to Admin",
+ --IsVisible
+ function(player)
+ return ( getPrivilegeType(game.Players.LocalPlayer) == privilegeOwner ) and ( getPrivilegeType(player) == privilegeMember )
+ end,
+ --IsActive
+ function(player)
+ return true
+ end,
+ --DoIt
+ function(player)
+ game:GetService("PersonalServerService"):Promote(player)
+ return true
+ end
+ )
+ addContextMenuButton("Demote to Member",
+ --IsVisible
+ function(player)
+ return ( getPrivilegeType(game.Players.LocalPlayer) == privilegeOwner ) and ( getPrivilegeType(player) == privilegeAdmin )
+ end,
+ --IsActive
+ function(player)
+ return true
+ end,
+ --DoIt
+ function(player)
+ game:GetService("PersonalServerService"):Demote(player)
+ return true
+ end
+ )
+ addContextMenuButton("Demote to Visitor",
+ --IsVisible
+ function(player)
+ return ( getPrivilegeType(game.Players.LocalPlayer) >= privilegeAdmin ) and ( getPrivilegeType(player) == privilegeMember )
+ end,
+ --IsActive
+ function(player)
+ return true
+ end,
+ --DoIt
+ function(player)
+ game:GetService("PersonalServerService"):Demote(player)
+ return true
+ end
+ )
+end
+
+local function setupBuildToolManagement()
+ local buildToolManagerAssetId = 64164692
+ game:GetService("ScriptContext"):AddCoreScript(buildToolManagerAssetId,game.Players.LocalPlayer,"BuildToolManager")
+end
+
+
+local function getStatColumns(players)
+ for i, player in ipairs(players) do
+ local leaderstats = player:FindFirstChild("leaderstats")
+ if leaderstats then
+ local stats = {}
+ local children = leaderstats:GetChildren()
+ if children then
+ for i, stat in ipairs(children) do
+ if stat:IsA("IntValue") then
+ table.insert(stats, stat)
+ else
+ --TODO: This should check for IntValue only but current ScoreHud does not
+ table.insert(stats, stat)
+ end
+ end
+ end
+ return stats
+ end
+ end
+ return nil
+end
+
+local function determineBoardType()
+ local players = getPlayers()
+
+ local foundLeaderstats = false
+ local numStats = 0
+ local foundTeam = false
+
+ local stats = getStatColumns(players)
+ if stats then
+ foundLeaderstats = true
+ numStats = #stats
+ end
+
+ for i, player in ipairs(players) do
+ if not foundTeam then
+ if not player.Neutral then
+ foundTeam = true
+ break
+ end
+ end
+ end
+
+ if foundLeaderstats and foundTeam then
+ return "TeamScore", numStats
+ elseif foundLeaderstats then
+ return "PlayerScore", numStats
+ elseif foundTeam then
+ return "TeamList", numStats
+ else
+ return "PlayerList", numStats
+ end
+end
+
+local function toggleBigWindow()
+ if container == nil then
+ return
+ end
+
+ if currentWindowState == "Big" then
+ --Hide it
+ if previousWindowState == nil or previousWindowState == "Big" or previousWindowState == "None" then
+ transitionWindowsFunction("None")
+ else
+ transitionWindowsFunction("Small")
+ end
+ else
+ previousWindowState = currentWindowState
+ transitionWindowsFunction("Big")
+ end
+end
+local previousBigPlayerList = nil
+local function rebuildBoard(owner, boardType, numStats)
+ if topRightTrayContainer == nil then
+ topRightTrayContainer = owner:FindFirstChild("PlayerListTopRightFrame")
+ if topRightTrayContainer == nil then
+ topRightTrayContainer = Instance.new("Frame")
+ topRightTrayContainer.Name = "PlayerListTopRightFrame"
+ topRightTrayContainer.BackgroundTransparency = 1
+ topRightTrayContainer.Size = UDim2.new(0.2, 16, 0.42, 16)
+ topRightTrayContainer.Position = UDim2.new(0.8, 0, 0, 0)
+ topRightTrayContainer.Parent = container
+ end
+ end
+ if minimizedState == nil then
+ minimizedState = Instance.new("Frame")
+ minimizedState.Name = "MinimizedPlayerlist"
+ minimizedState.BackgroundTransparency = 1
+ minimizedState.Position = UDim2.new(1, -166, 0,0)
+ minimizedState.Size = UDim2.new(0, 151, 0, 30)
+
+ playerListButton = Instance.new("ImageButton")
+ playerListButton.Name = "GoSmallButton"
+ playerListButton.Image = "rbxasset://textures/ui/playerlist_hidden_small.png"
+ playerListButton.BackgroundTransparency = 1
+ playerListButton.Size = UDim2.new(0.0, 35, 0, 30)
+ playerListButton.Position = UDim2.new(1, -35, 0, 0)
+ playerListButton.MouseButton1Click:connect(
+ function()
+ transitionWindowsFunction("Small")
+ end)
+ playerListButton.Parent = minimizedState
+
+ minimizedState.Visible = false
+ robloxLock(minimizedState)
+ minimizedState.Parent = topRightTrayContainer
+ end
+ if bigWindowImposter == nil then
+ bigWindowImposter = owner:FindFirstChild("BigPlayerListWindowImposter")
+ if bigWindowImposter == nil then
+ bigWindowImposter = Instance.new("Frame")
+ bigWindowImposter.Name = "BigPlayerListWindowImposter"
+ bigWindowImposter.Visible = false
+ bigWindowImposter.BackgroundColor3 = Color3.new(0,0,0)
+ bigWindowImposter.BackgroundTransparency = 0.7
+ bigWindowImposter.BorderSizePixel = 0
+ bigWindowImposter.Size = UDim2.new(0.4, 7, 0.4, 7)
+ bigWindowImposter.Position = UDim2.new(0.3, 0, 0.3, 0)
+ robloxLock(bigWindowImposter)
+ bigWindowImposter.Parent = container
+ end
+ end
+ if container == nil or container ~= owner then
+ container = owner
+
+ topRightTrayContainer.Parent = container
+ bigWindowImposter.Parent = container
+ end
+
+ local smallVisible = true
+ local bigVisible = false
+ if container then
+ if topRightTrayContainer then
+ --Delete the old boards
+ if topRightTrayContainer:FindFirstChild("SmallPlayerlist") then
+ smallVisible = topRightTrayContainer.SmallPlayerlist.Visible
+ topRightTrayContainer.SmallPlayerlist.Parent = nil
+ end
+ end
+ if container:FindFirstChild("BigPlayerlist") then
+ bigVisible = container.BigPlayerlist.Visible or (previousBigPlayerList ~= nil)
+ container.BigPlayerlist.Parent = nil
+ if previousBigPlayerList ~= nil then
+ pcall(function() game.GuiService:RemoveCenterDialog(previousBigPlayerList) end)
+ previousBigPlayerList = nil
+ end
+ end
+ end
+
+ local smallBoard, bigBoard = createBoardsFunction(boardType, numStats)
+ if smallBoard then
+ smallBoard.Visible = smallVisible
+ smallBoard.Parent = topRightTrayContainer
+ recalculateSmallPlayerListSize(smallBoard)
+ end
+ if bigBoard then
+ if bigVisible then
+ previousBigPlayerList = bigBoard
+ local centerDialogSupported, msg = pcall(function() game.GuiService:AddCenterDialog(previousBigPlayerList, Enum.CenterDialogType.PlayerInitiatedDialog,
+ function()
+ previousBigPlayerList.Visible = true
+ end,
+ function()
+ previousBigPlayerList.Visible = false
+ end)
+ end)
+ bigBoard.Visible = bigVisible
+ else
+ bigBoard.Visible = false
+ end
+ bigBoard.Parent = container
+ end
+ return container
+end
+
+function recalculateSmallPlayerListSize(smallPlayerList)
+ waitForChild(smallPlayerList,"ScrollingArea")
+ waitForChild(smallPlayerList.ScrollingArea, "ScrollingFrame")
+ local scrollingFrame = smallPlayerList.ScrollingArea.ScrollingFrame
+ local playerLines = scrollingFrame:GetChildren()
+
+ local totalPlayerListSize = 0
+ for i = 1, #playerLines do
+ totalPlayerListSize = totalPlayerListSize + playerLines[i].AbsoluteSize.Y
+ end
+
+ if not smallPlayerList.Parent then return end
+
+ local yOffset = math.max(0,(smallPlayerList.Size.Y.Scale * smallPlayerList.Parent.AbsoluteSize.Y) - totalPlayerListSize - smallWindowHeaderYSize)
+ smallPlayerList.Size = UDim2.new(smallPlayerList.Size.X.Scale,smallPlayerList.Size.X.Offset,smallPlayerList.Size.Y.Scale,-yOffset)
+end
+
+
+local function showBigPlayerWindow()
+ if container:FindFirstChild("BigPlayerlist") then
+ if container.BigPlayerlist.Visible then
+ return
+ end
+ end
+
+ bigWindowImposter.Visible = true
+ bigWindowImposter:TweenSizeAndPosition(bigWindowSize, bigWindowPosition, Enum.EasingDirection.Out, bigEasingStyle, 0.3, true,
+ function(state)
+ if state == Enum.TweenStatus.Completed then
+ bigWindowImposter.Visible = false
+ if container:FindFirstChild("BigPlayerlist") then
+ container.BigPlayerlist.Visible = true
+ end
+ end
+ end)
+end
+
+local function hideBigPlayerWindow(completed)
+ if playerContextMenu then
+ playerContextMenu.Visible = false
+ end
+
+ if container:FindFirstChild("BigPlayerlist") then
+ if container.BigPlayerlist.Visible == false and bigWindowImposter.Visible == false then
+ if completed then
+ completed()
+ end
+ --Already completely hidden
+ return
+ end
+ container.BigPlayerlist.Visible = false
+ end
+
+ local completedFunction = completed
+ bigWindowImposter.Visible = true
+ bigWindowImposter:TweenSizeAndPosition(UDim2.new(0.4, 0, 0.4, 0), UDim2.new(0.3, 0, 0.3, 0), Enum.EasingDirection.In, Enum.EasingStyle.Quart, 0.15, true,
+ function(state)
+ if state == Enum.TweenStatus.Completed then
+ bigWindowImposter.Visible = false
+ if completedFunction then
+ completedFunction()
+ end
+ end
+ end)
+end
+local function hideSmallPlayerWindow(completed)
+ if playerContextMenu then
+ playerContextMenu.Visible = false
+ end
+ if topRightTrayContainer:FindFirstChild("SmallPlayerlist") then
+ local completedFunction = completed
+ if topRightTrayContainer.SmallPlayerlist.Visible then
+ topRightTrayContainer.SmallPlayerlist:TweenPosition(UDim2.new(1,0,smallWindowPosition.Y.Scale, smallWindowPosition.Y.Offset), Enum.EasingDirection.Out, smallEasingStyle, 0.3, true,
+ function(state)
+ if state == Enum.TweenStatus.Completed then
+ if topRightTrayContainer:FindFirstChild("SmallPlayerlist") then
+ topRightTrayContainer.SmallPlayerlist.Visible = false
+ end
+ if completedFunction then
+ completedFunction()
+ end
+ end
+ end)
+ return
+ end
+ end
+ if completed then
+ completed()
+ end
+end
+
+
+transitionWindowsFunction = function(desiredState)
+ if desiredState == "Big" then
+ minimizedState.Visible = false
+ hideSmallPlayerWindow()
+
+ if previousBigPlayerList ~= nil then
+ if previousBigPlayerList ~= container:FindFirstChild("BigPlayerlist") then
+ pcall(function() game.GuiService:RemoveCenterDialog(previousBigPlayerList) end)
+ previousBigPlayerList = nil
+ previousBigPlayerList = container:FindFirstChild("BigPlayerlist")
+ end
+ else
+ previousBigPlayerList = container:FindFirstChild("BigPlayerlist")
+ end
+
+ if previousBigPlayerList then
+ local firstShow = false
+ local centerDialogSupported, msg = pcall(function() game.GuiService:AddCenterDialog(previousBigPlayerList, Enum.CenterDialogType.PlayerInitiatedDialog,
+ function()
+ if not firstShow then
+ showBigPlayerWindow()
+ firstShow = true
+ else
+ previousBigPlayerList.Visible = true
+ end
+ end,
+ function()
+ if previousBigPlayerList then
+ previousBigPlayerList.Visible = false
+ end
+ end)
+ end)
+ if centerDialogSupported == false then
+ print("Exception", msg)
+ showBigPlayerWindow()
+ end
+ else
+ showBigPlayerWindow()
+ end
+ currentWindowState = "Big"
+ elseif desiredState == "Small" then
+ minimizedState.Visible = false
+ if previousBigPlayerList ~= nil then
+ pcall(function() game.GuiService:RemoveCenterDialog(previousBigPlayerList) end)
+ previousBigPlayerList = nil
+ end
+
+ hideBigPlayerWindow()
+ if topRightTrayContainer:FindFirstChild("SmallPlayerlist") then
+ if not topRightTrayContainer.SmallPlayerlist.Visible or topRightTrayContainer.SmallPlayerlist.Position ~= smallWindowPosition then
+ topRightTrayContainer.SmallPlayerlist.Visible = true
+ topRightTrayContainer.SmallPlayerlist:TweenPosition(smallWindowPosition, Enum.EasingDirection.Out, smallEasingStyle, 0.3, true)
+ end
+ end
+ currentWindowState = "Small"
+ elseif desiredState == "None" then
+ if previousBigPlayerList ~= nil then
+ pcall(function() game.GuiService:RemoveCenterDialog(previousBigPlayerList) end)
+ previousBigPlayerList = nil
+ end
+
+ local smallDone = false
+ local bigDone = false
+ hideSmallPlayerWindow(
+ function()
+ smallDone = true
+ if bigDone and smallDone then
+ minimizedState.Visible = true
+ end
+ end)
+ hideBigPlayerWindow(
+ function()
+ bigDone = true
+ if bigDone and smallDone then
+ minimizedState.Visible = true
+ end
+ end)
+ currentWindowState = "None"
+ end
+end
+
+local function getStatValuesForPlayer(player)
+ local leaderstats = player:FindFirstChild("leaderstats")
+ if leaderstats then
+ local children = leaderstats:GetChildren()
+ if children then
+ local result = {}
+ --Just go based on position
+ for i, stat in ipairs(children) do
+ if stat:IsA("IntValue") then
+ table.insert(result, stat)
+ else
+ table.insert(result, 0)
+ end
+ end
+
+ return result, leaderstats
+ end
+ end
+ return nil
+end
+
+--ChildAdded on Player (if it's name is "leaderstats")
+
+if UserSettings and LoadLibrary then
+
+ RbxGui,msg = LoadLibrary("RbxGui")
+
+ local function createTeamName(name, color)
+ local fontHeight = 20
+ local frame = Instance.new("Frame")
+ frame.Name = "Team-" .. name
+ frame.BorderSizePixel = 0
+ frame.BackgroundTransparency = 0.5
+ frame.BackgroundColor3 = Color3.new(1,1,1)
+ frame.Size = UDim2.new(1, 0, 0, fontHeight)
+ frame.Position = UDim2.new(0,0,0,0)
+
+ local label = Instance.new("TextLabel")
+ label.Name = "NameLabel"
+ label.Text = " " .. name
+ label.Font = Enum.Font.ArialBold
+ label.FontSize = Enum.FontSize.Size18
+ label.Position = UDim2.new(0,0,0,0)
+ label.Size = UDim2.new(1,0,1,0)
+ label.TextColor3 = Color3.new(1,1,1)
+ label.BackgroundTransparency = 0.5
+ label.BackgroundColor3 = getColor(color)
+ label.BorderSizePixel = 0
+ label.TextXAlignment = Enum.TextXAlignment.Left
+
+ local changeFunc = nil
+ label, changeFunc = RbxGui.AutoTruncateTextObject(label)
+ label.Parent = frame
+
+ return frame, changeFunc
+ end
+
+ local function getFriendStatusIcon(friendStatus)
+ if friendStatus == Enum.FriendStatus.Unknown or friendStatus == Enum.FriendStatus.NotFriend then
+ return nil
+ elseif friendStatus == Enum.FriendStatus.Friend then
+ return "rbxasset://textures/ui/PlayerlistFriendIcon.png"
+ elseif friendStatus == Enum.FriendStatus.FriendRequestSent then
+ return "rbxasset://textures/ui/PlayerlistFriendRequestSentIcon.png"
+ elseif friendStatus == Enum.FriendStatus.FriendRequestReceived then
+ return "rbxasset://textures/ui/PlayerlistFriendRequestReceivedIcon.png"
+ else
+ error("Unknown FriendStatus: " .. friendStatus)
+ end
+ end
+
+ local function getMembershipTypeIcon(membershipType)
+ if membershipType == Enum.MembershipType.None then
+ return ""
+ elseif membershipType == Enum.MembershipType.BuildersClub then
+ return "rbxasset://textures/ui/TinyBcIcon.png"
+ elseif membershipType == Enum.MembershipType.TurboBuildersClub then
+ return "rbxasset://textures/ui/TinyTbcIcon.png"
+ elseif membershipType == Enum.MembershipType.OutrageousBuildersClub then
+ return "rbxasset://textures/ui/TinyObcIcon.png"
+ else
+ error("Uknown membershipType" .. membershipType)
+ end
+ end
+
+
+ local function updatePlayerFriendStatus(nameObject, friendStatus)
+ local fontHeight = 20
+
+ local friendIconImage = getFriendStatusIcon(friendStatus)
+ nameObject.MembershipTypeLabel.FriendStatusLabel.Visible = (friendIconImage ~= nil)
+
+ if friendIconImage ~= nil then
+ --Show friend icon
+ nameObject.MembershipTypeLabel.FriendStatusLabel.Image = friendIconImage
+ nameObject.NameLabel.Position =UDim2.new(0,2*fontHeight,0,1)
+ nameObject.NameLabel.Size = UDim2.new(1,-2*fontHeight,1,-2)
+ else
+ --Hide the friend icon
+ nameObject.NameLabel.Position = UDim2.new(0,fontHeight+1,0,1)
+ nameObject.NameLabel.Size = UDim2.new(1,-(fontHeight+1),1,-2)
+ end
+ end
+ local function updatePlayerName(nameObject, membershipStatus, teamColor)
+ local fontHeight = 20
+
+ nameObject.Size = UDim2.new(1,0,0,fontHeight)
+ nameObject.MembershipTypeLabel.Image = getMembershipTypeIcon(membershipStatus)
+ end
+
+
+ local function updatePlayerNameColor(player, teamColor)
+ local function updatePlayerNameColorHelper(nameObject)
+ if teamColor ~= nil then
+ nameObject.NameLabel.TextColor3 = getColor(teamColor)
+ nameObject.NameLabel.FullNameLabel.TextColor3 = getColor(teamColor)
+ else
+ nameObject.NameLabel.TextColor3 = Color3.new(1,1,1)
+ nameObject.NameLabel.FullNameLabel.TextColor3 = Color3.new(1,1,1)
+ end
+ end
+
+ updatePlayerNameColorHelper(playerTable[player].NameObjectSmall)
+ updatePlayerNameColorHelper(playerTable[player].NameObjectBig)
+ end
+
+
+ local function createPlayerName(name, membershipStatus, teamColor, friendStatus)
+ local frame = Instance.new("Frame")
+ frame.Name = "Player_" .. name
+ frame.BackgroundColor3 = Color3.new(1,1,1)
+ frame.BackgroundTransparency = 0.5
+ frame.BorderSizePixel = 0
+
+ local membershipStatusLabel = Instance.new("ImageLabel")
+ membershipStatusLabel.Name = "MembershipTypeLabel"
+ membershipStatusLabel.BackgroundTransparency = 1
+ membershipStatusLabel.Size = UDim2.new(1,0,1,0)
+ membershipStatusLabel.Position = UDim2.new(0,0,0,0)
+ membershipStatusLabel.SizeConstraint = Enum.SizeConstraint.RelativeYY
+ membershipStatusLabel.Parent = frame
+
+ local friendStatusLabel = Instance.new("ImageLabel")
+ friendStatusLabel.Name = "FriendStatusLabel"
+ friendStatusLabel.Visible = false
+ friendStatusLabel.BackgroundTransparency = 1
+ friendStatusLabel.Size = UDim2.new(1,0,1,0)
+ friendStatusLabel.Position = UDim2.new(1,0,0,0)
+ friendStatusLabel.Parent = membershipStatusLabel
+
+ local changeNameFunction
+ local nameLabel = Instance.new("TextLabel")
+ nameLabel.Name = "NameLabel"
+ nameLabel.Text = name
+ nameLabel.Font = Enum.Font.ArialBold
+ nameLabel.FontSize = Enum.FontSize.Size14
+ nameLabel.TextColor3 = Color3.new(1,1,1)
+ nameLabel.BackgroundTransparency = 1
+ nameLabel.BackgroundColor3 = Color3.new(0,0,0)
+ nameLabel.TextXAlignment = Enum.TextXAlignment.Left
+ nameLabel, changeNameFunction = RbxGui.AutoTruncateTextObject(nameLabel)
+ nameLabel.Parent = frame
+
+ updatePlayerName(frame, membershipStatus, teamColor)
+ if supportFriends and not friendRequestBlacklist[game.Players:FindFirstChild(name)] then
+ updatePlayerFriendStatus(frame, friendStatus)
+ else
+ updatePlayerFriendStatus(frame, Enum.FriendStatus.NotFriend)
+ end
+ return frame, changeNameFunction
+ end
+
+ local function createStatColumn(i, numColumns, isTeam, color3, isHeader, stat)
+ local textLabel = Instance.new("TextButton")
+ textLabel.Name = "Stat" .. i
+ textLabel.AutoButtonColor = false
+ textLabel.TextColor3 = Color3.new(1,1,1)
+ textLabel.TextXAlignment = Enum.TextXAlignment.Right
+ textLabel.TextYAlignment = Enum.TextYAlignment.Center
+ textLabel.FontSize = Enum.FontSize.Size14
+
+ if isHeader then
+ textLabel.FontSize = Enum.FontSize.Size18
+ else
+ textLabel.FontSize = Enum.FontSize.Size14
+ end
+ if isHeader or isTeam then
+ textLabel.Font = Enum.Font.ArialBold
+ else
+ textLabel.Font = Enum.Font.Arial
+ end
+
+ if isTeam then
+ textLabel.BackgroundColor3 = color3
+ textLabel.Text = 0
+ else
+ textLabel.BackgroundColor3 = Color3.new(0,0,0)
+ textLabel.Text = ""
+ end
+ textLabel.BackgroundTransparency = 1
+ if i == numColumns then
+ textLabel.Size = UDim2.new(1/numColumns, -6, 1, 0)
+ else
+ textLabel.Size = UDim2.new(1/numColumns, -4, 1, 0)
+ end
+
+ textLabel.Position = UDim2.new((i-1) * (1/numColumns), 0, 0, 0)
+
+ local truncLabel, changer = RbxGui.AutoTruncateTextObject(textLabel)
+
+ if isHeader then
+ local mouseCon = {}
+
+ mouseCon[1] = truncLabel.MouseEnter:connect(function()
+ truncLabel.BackgroundTransparency = 0.2
+ end)
+ mouseCon[2] = truncLabel.MouseLeave:connect(function()
+ truncLabel.BackgroundTransparency = 1
+ end)
+
+ mouseCon[3] = truncLabel.MouseButton1Click:connect(function()
+ sortPlayerListsFunction(truncLabel:GetChildren()[1].Name, (currentSortName == truncLabel:GetChildren()[1].Name) )
+ truncLabel.BackgroundTransparency = 1
+ end)
+
+ mouseCon[4] = truncLabel:GetChildren()[1].MouseButton1Click:connect(function()
+ sortPlayerListsFunction(textLabel.Name, (currentSortName == truncLabel.Name) )
+ truncLabel.BackgroundTransparency = 1
+ end)
+
+ mouseCon[5] = nil
+ mouseCon[5] = truncLabel.AncestryChanged:connect(function(child,parent)
+ if parent == nil then
+ for i,connection in pairs(mouseCon) do
+ connection:disconnect()
+ end
+ end
+ end)
+ end
+
+ return truncLabel, changer
+ end
+
+ local function createStatHeaders(stats, numColumns, isBig)
+ local frame = Instance.new("Frame")
+ frame.Name = "Headers"
+ frame.BorderSizePixel = 0
+ frame.BackgroundColor3 = Color3.new(0,0,0)
+ frame.BackgroundTransparency = 1
+
+ local nameSize
+ if isBig then
+ nameSize = 0.5
+ elseif numColumns == 1 then
+ nameSize = 0.7
+ elseif numColumns == 2 then
+ nameSize = 0.6
+ else
+ nameSize = 0.45
+ end
+ frame.Size = UDim2.new(1-nameSize, 0, 1,0)
+ if isBig then
+ frame.Position = UDim2.new(nameSize,-25, 0,0)
+ else
+ frame.Position = UDim2.new(nameSize,0, 0,0)
+ end
+
+ local i = 1
+ while i <= numColumns do
+ local headerColumn, changeText = createStatColumn(i, numColumns, false, nil, true,stats[i])
+ changeText(stats[i].Name)
+ headerColumn.Parent = frame
+ i = i + 1
+ end
+ return frame, textChangers
+ end
+
+ local function createStatColumns(nameObject, numColumns, isTeam, isBig)
+ local frame = Instance.new("Frame")
+ frame.Name = nameObject.Name .. "_WithStats"
+ frame.BorderSizePixel = 0
+ frame.BackgroundColor3 = nameObject.BackgroundColor3
+ frame.BackgroundTransparency = nameObject.BackgroundTransparency
+ frame.Size = nameObject.Size
+ frame.Position = nameObject.Position
+
+ nameObject.BackgroundTransparency = 1
+
+ if numColumns == 0 then
+ nameObject.Size = UDim2.new(1,0,1,0)
+ nameObject.Position = UDim2.new(0,0,0,0)
+ nameObject.Parent = frame
+ return frame
+ end
+
+ local statFrame = Instance.new("Frame")
+ statFrame.Name = "Stats"
+ if isTeam then
+ statFrame.BorderSizePixel = 0
+ statFrame.BackgroundColor3 = nameObject.NameLabel.BackgroundColor3
+ statFrame.BackgroundTransparency = nameObject.NameLabel.BackgroundTransparency
+ else
+ statFrame.BackgroundTransparency = 1
+ end
+
+ local nameSize
+ if isBig then
+ nameSize = 0.5
+ elseif numColumns == 1 then
+ nameSize = 0.7
+ elseif numColumns == 2 then
+ nameSize = 0.6
+ else
+ nameSize = 0.45
+ end
+ nameObject.Size = UDim2.new(nameSize, 0, 1, 0)
+ nameObject.Position = UDim2.new(0, 0, 0, 0)
+ statFrame.Size = UDim2.new(1-nameSize,0, 1,0)
+ statFrame.Position = UDim2.new(nameSize,0, 0,0)
+
+ nameObject.Parent = frame
+ statFrame.Parent = frame
+
+ local textChangers = {}
+ local i = 1
+ while i <= numColumns do
+ local statColumn, changeText = createStatColumn(i, numColumns, isTeam, statFrame.BackgroundColor3)
+ statColumn.Parent = statFrame
+ table.insert(textChangers, changeText)
+
+ i = i + 1
+ end
+
+ return frame, statFrame, textChangers
+ end
+
+ local function createAlternatingRows(objects)
+ for i, line in ipairs(objects) do
+ if i % 2 == 0 then
+ line.BackgroundTransparency = 1
+ else
+ line.BackgroundTransparency = 0.95
+ end
+ end
+ end
+ local removeFromTeam = nil
+
+ local function clearTableEntry(obj, tableInfo)
+ if tableInfo.MainObjectSmall then
+ tableInfo.MainObjectSmall.Parent = nil
+ tableInfo.MainObjectSmall = nil
+ end
+ if tableInfo.MainObjectBig then
+ tableInfo.MainObjectBig.Parent = nil
+ tableInfo.MainObjectBig = nil
+ end
+ if tableInfo.Connections then
+ for i, connection in ipairs(tableInfo.Connections) do
+ connection:disconnect()
+ end
+ tableInfo.Connections = nil
+ end
+ if tableInfo.LeaderStatConnections then
+ for i, connection in ipairs(tableInfo.LeaderStatConnections) do
+ connection:disconnect()
+ end
+ tableInfo.LeaderStatConnections = nil
+ end
+ if tableInfo.CurrentTeam then
+ removeFromTeam(obj)
+ tableInfo.CurrentTeam = nil
+ end
+ if tableInfo.Players then
+ for i, player in ipairs(tableInfo.Players) do
+ playerTable[player].CurrentTeam = nil
+ end
+ tableInfo.Players = {}
+ end
+ if tableInfo.StatValues then
+ tableInfo.StatValues = nil
+ end
+ end
+
+ local function resetPlayerTable()
+ for player, info in pairs(playerTable) do
+ clearTableEntry(player, info)
+ playerTable[player] = nil
+ end
+ playerTable = {}
+ end
+
+ local function resetTeamTable()
+ for team, info in pairs(teamTable) do
+ clearTableEntry(team, info)
+ teamTable[team] = nil
+ end
+ teamTable = {}
+ teamColorTable = {}
+ end
+
+ local function getBoardTypeInfo()
+ local isTeam = (currentBoardType == "TeamScore" or currentBoardType == "TeamList")
+ local isScore = (currentBoardType == "TeamScore" or currentBoardType == "PlayerScore")
+ return isTeam, isScore
+ end
+
+
+ local function recomputeTeamScore(team, column)
+ if not team or team == "Neutral" then
+ return
+ end
+
+ local function recomputeScoreHelper(statChangers)
+ if statChangers and column <= #statChangers then
+ local sum = 0
+ for i, p in ipairs(teamTable[team].Players) do
+ if playerTable[p].StatValues and column <= #playerTable[p].StatValues then
+ sum = sum + playerTable[p].StatValues[column].Value
+ end
+ end
+ statChangers[column](sum)
+ end
+ end
+
+ recomputeScoreHelper(teamTable[team].StatChangersSmall)
+ recomputeScoreHelper(teamTable[team].StatChangersBig)
+ end
+ local function recomputeCompleteTeamScore(team)
+ local col = 1
+ while col <= currentStatCount do
+ recomputeTeamScore(team, col)
+ col = col + 1
+ end
+ end
+ removeFromTeam = function(player)
+ if playerTable[player].CurrentTeam ~= nil and teamTable[playerTable[player].CurrentTeam] ~= nil then
+ ArrayRemove(teamTable[playerTable[player].CurrentTeam].Players, player)
+ recomputeCompleteTeamScore(playerTable[player].CurrentTeam)
+ playerTable[player].CurrentTeam = nil
+ end
+ end
+
+ local function assignToTeam(player)
+ local isTeam, isScore = getBoardTypeInfo()
+
+ if isTeam then
+ local newTeam = nil
+
+ if player.Neutral or teamColorTable[player.TeamColor.Name] == nil then
+ newTeam = "Neutral"
+ else
+ newTeam = teamColorTable[player.TeamColor.Name]
+ end
+
+ if playerTable[player].CurrentTeam == newTeam then
+ return
+ end
+
+ local oldTeam = playerTable[player].LastTeam
+ removeFromTeam(player)
+
+ playerTable[player].CurrentTeam = newTeam
+
+ if teamTable[oldTeam] and teamTable[oldTeam]["NameChangeFuncBig"] then
+ if #teamTable[oldTeam].Players < 1 then
+ teamTable[oldTeam]["NameChangeFuncBig"](" " .. oldTeam.Name)
+ else
+ teamTable[oldTeam]["NameChangeFuncBig"](" " .. oldTeam.Name .. " (" .. tostring(#teamTable[oldTeam].Players) ..")")
+ end
+ end
+
+ if teamTable[newTeam] then
+ table.insert(teamTable[newTeam].Players, player)
+ if newTeam["Name"] then
+ if teamTable[newTeam]["NameChangeFuncBig"] then
+ if #teamTable[newTeam].Players < 1 then
+ teamTable[newTeam]["NameChangeFuncBig"](" " .. newTeam.Name)
+ else
+ teamTable[newTeam]["NameChangeFuncBig"](" " .. newTeam.Name .. " (" .. tostring(#teamTable[newTeam].Players) ..")")
+ end
+ end
+ end
+ end
+
+ if newTeam == "Neutral" then
+ updatePlayerNameColor(player, nil)
+ else
+ updatePlayerNameColor(player, player.TeamColor)
+ end
+
+ playerTable[player].LastTeam = newTeam
+
+ recomputeCompleteTeamScore(newTeam)
+
+ --Relayout
+ if sortPlayerListsFunction then
+ sortPlayerListsFunction()
+ end
+ end
+ end
+
+ local function buildTeamObject(team, numStatColumns, suffix)
+ local isTeam, isScore = getBoardTypeInfo()
+ local teamObject, changeFunc = createTeamName(team.Name, team.TeamColor)
+ teamObject.NameLabel.Text = " " .. team.Name .. " (0)"
+ if not teamTable[team] then
+ teamTable[team] = {}
+ end
+ teamTable[team]["NameObject" .. suffix] = teamObject
+ teamTable[team]["NameChangeFunc" .. suffix] = changeFunc
+ if isScore then
+ local statObject
+ local textChangers
+ teamObject, statObject, textChangers = createStatColumns(teamObject, numStatColumns, true, suffix == "Big")
+ teamTable[team]["StatObject" .. suffix] = statObject
+ teamTable[team]["StatChangers" .. suffix] = textChangers
+ end
+ teamTable[team]["MainObject" .. suffix] = teamObject
+ changeFunc(" " .. team.Name)
+ if not teamTable[team].Players then
+ teamTable[team].Players = {}
+ else
+ if suffix ~= "Small" and #teamTable[team].Players > 0 then
+ changeFunc(" " .. team.Name .. " (" .. tostring(#teamTable[team].Players) ..")")
+ end
+ end
+
+ return teamObject
+ end
+
+ local currentContextMenuPlayer = nil
+ local function updatePlayerContextMenu(player,x,y)
+ currentContextMenuPlayer = player
+ local elementHeight = 18
+ local function highlight(button)
+ button.TextColor3 = Color3.new(0,0,0)
+ button.BackgroundColor3 = Color3.new(0.8,0.8,0.8)
+ end
+ local function clearHighlight(button)
+ button.TextColor3 = Color3.new(1,1,1)
+ button.BackgroundColor3 = Color3.new(0,0,0)
+ end
+ if playerContextMenu == nil then
+ elementNames = {}
+ elementNameToElement = {}
+
+ for i, contextElement in ipairs(contextMenuElements) do
+ table.insert(elementNames, contextElement.Text)
+ elementNameToElement[tostring(contextElement.Text)] = contextElement
+ end
+
+ playerContextMenu = Instance.new("TextButton")
+ playerContextMenu.Name = "PlayerListContextMenu"
+ playerContextMenu.Style = Enum.ButtonStyle.RobloxButton
+ playerContextMenu.Text = ""
+ playerContextMenu.Visible = false
+ playerContextMenu.ZIndex = 4
+
+ playerContextMenu.MouseLeave:connect(function()
+ local menuChildren = playerContextMenu:GetChildren()
+ for i = 1, #menuChildren do
+ if menuChildren[i].Name == "ChoiceButton" then
+ menuChildren[i].TextColor3 = Color3.new(1,1,1)
+ menuChildren[i].BackgroundTransparency = 1
+ end
+ end
+ playerContextMenu.Visible = false
+ inContextMenu = false
+ end)
+
+ playerContextMenu.MouseEnter:connect(function()
+ inContextMenu = true
+ end)
+
+ for i = 1, #elementNames do
+ local newElementButton = Instance.new("TextButton")
+ newElementButton.Name = "ChoiceButton"
+ newElementButton.Text = elementNames[i]
+ newElementButton.TextColor3 = Color3.new(1,1,1)
+ newElementButton.Font = Enum.Font.Arial
+ newElementButton.FontSize = Enum.FontSize.Size14
+ newElementButton.BackgroundTransparency = 1
+ newElementButton.TextWrap = true
+ newElementButton.Size = UDim2.new(1,0,0,elementHeight)
+ newElementButton.Position = UDim2.new(0,0,0,elementHeight * (i - 1))
+ newElementButton.ZIndex = playerContextMenu.ZIndex + 1
+
+ newElementButton.MouseEnter:connect(function()
+ newElementButton.TextColor3 = Color3.new(0,0,0)
+ newElementButton.BackgroundTransparency = 0
+ end)
+
+ newElementButton.MouseLeave:connect(function()
+ newElementButton.TextColor3 = Color3.new(1,1,1)
+ newElementButton.BackgroundTransparency = 1
+ end)
+
+ newElementButton.MouseButton1Click:connect(function()
+ local element = elementNameToElement[newElementButton.Text]
+ pcall(function() element.DoIt(currentContextMenuPlayer) end)
+ playerContextMenu.Visible = false
+ newElementButton.TextColor3 = Color3.new(1,1,1)
+ newElementButton.BackgroundTransparency = 1
+ end)
+
+ newElementButton.Parent = playerContextMenu
+ end
+
+ robloxLock(playerContextMenu)
+ playerContextMenu.Parent = script.Parent
+
+ end
+
+ local visibleElements = 0
+ for i, contextElement in ipairs(contextMenuElements) do
+ local isVisible = false
+
+ if contextElement.IsVisible then
+ local success, visible = pcall(function() return contextElement.IsVisible(currentContextMenuPlayer) end)
+ if success then
+ isVisible = visible
+ else
+ print("Error in IsVisible call: " .. visible)
+ end
+ end
+
+ if isVisible then
+ local foundElement = false
+ for i = 1, #elementNames do
+ if elementNames[i] == contextElement.Text then
+ foundElement = true
+ break
+ end
+ end
+ if not foundElement then
+ table.insert(elementNames,contextElement.Text)
+ end
+ visibleElements = visibleElements + 1
+ else
+ for i = 1, #elementNames do
+ if elementNames[i] == contextElement.Text then
+ table.remove(elementNames,i)
+ break
+ end
+ end
+ end
+ end
+ playerContextMenu.Size = UDim2.new(0, 150, 0, elementHeight + (elementHeight * visibleElements) )
+
+ if x and y then
+ x = x - (playerContextMenu.AbsoluteSize.X/2)
+ if x + playerContextMenu.AbsoluteSize.X >= script.Parent.AbsoluteSize.X then
+ x = script.Parent.AbsoluteSize.X - playerContextMenu.AbsoluteSize.X
+ end
+ playerContextMenu.Position = UDim2.new(0, x, 0, y - 3)
+ end
+
+ local elementPos = 0
+ local contextChildren = playerContextMenu:GetChildren()
+ for i = 1, #contextChildren do
+ if contextChildren[i]:IsA("GuiObject") and contextChildren[i].Name == "ChoiceButton" then
+ contextChildren[i].Visible = false
+ for j = 1, #elementNames do
+ if elementNames[j] == contextChildren[i].Text then
+ contextChildren[i].Visible = true
+ contextChildren[i].Position = UDim2.new(0,0,0,elementPos * elementHeight)
+ elementPos = elementPos + 1
+ break
+ end
+ end
+ end
+ end
+ end
+
+ local function playerContextMenuHasItems()
+ if playerContextMenu then
+ local children = playerContextMenu:GetChildren()
+ for i = 1, #children do
+ if children[i]:IsA("GuiObject") and children[i].Name == "ChoiceButton" and children[i].Visible then
+ return true
+ end
+ end
+ end
+ return false
+ end
+
+ local function showPlayerMenu(player, x, y)
+ updatePlayerContextMenu(player,x,y)
+ if not playerContextMenuHasItems() then return end -- don't show if we have nothing to show
+ playerContextMenu.Visible = true
+ end
+
+ local function buildPlayerObject(player, numStatColumns, suffix)
+ if not player then return nil end
+
+ local isTeam, isScore = getBoardTypeInfo()
+
+ local playerObject = nil
+ local changePlayerNameFunction = nil
+ local currentColor = nil
+ if isTeam and not player.Neutral then
+ currentColor = player.TeamColor.Color
+ else
+ currentColor = Color3.new(1,1,1)
+ end
+ playerObject, changePlayerNameFunction = createPlayerName(player.Name, player.MembershipType, currentColor, getFriendStatus(player))
+
+ if not playerTable[player] then
+ playerTable[player] = {}
+ end
+ if not playerTable[player].Connections then
+ playerTable[player].Connections = {}
+ end
+ if not playerTable[player].CurrentTeam then
+ playerTable[player].CurrentTeam = nil
+ end
+ if not playerTable[player].LastTeam then
+ playerTable[player].LastTeam = nil
+ end
+ playerTable[player]["NameObject" .. suffix] = playerObject
+ playerTable[player]["ChangeName" .. suffix] = changePlayerNameFunction
+
+ if isScore then
+ local statObject = nil
+ local textChangers = nil
+ playerObject, statObject, textChangers = createStatColumns(playerObject, numStatColumns, false, suffix == "Big")
+ playerTable[player]["StatObject" .. suffix]= statObject
+ playerTable[player]["StatChangers" .. suffix] = textChangers
+
+ local statValues, leaderstats = getStatValuesForPlayer(player)
+ if not statValues or #statValues < numStatColumns then
+ if not playerTable[player].LeaderStatConnections then
+ playerTable[player].LeaderStatConnections = {}
+ end
+ --Setup a listener to see when this data gets filled in
+ if not leaderstats then
+ --We don't even have a leaderstats child, wait for one
+ table.insert(playerTable[player].LeaderStatConnections,
+ player.ChildAdded:connect(
+ function(child)
+ if child.Name == "leaderstats" then
+ --Connections will be torn down
+ recreatePlayerFunction(player)
+ else
+ table.insert(playerTable[player].LeaderStatConnections,
+ child.Changed:connect(
+ function(prop)
+ if prop == "Name" and child.Name == "leaderstats" then
+ --Connections will be torn down
+ recreatePlayerFunction(player)
+ end
+ end))
+ end
+ end))
+ else
+ --We have a leaderstats, but not enough children, recreate if we get them
+ table.insert(playerTable[player].LeaderStatConnections,
+ leaderstats.ChildAdded:connect(
+ function(child)
+ --TODO only look for IntValue
+ recreatePlayerFunction(player)
+ end)
+ )
+ table.insert(playerTable[player].LeaderStatConnections,
+ leaderstats.AncestryChanged:connect(
+ function(child)
+ --We got deleted, try again
+ recreatePlayerFunction(player)
+ end)
+ )
+ end
+ end
+ if statValues then
+ if not playerTable[player].StatValues then
+ playerTable[player].StatValues = {}
+ end
+ local pos = 1
+ while pos <= numStatColumns and pos <= #statValues do
+ local currentColumn = pos
+ local statValue = statValues[pos]
+ local statChanger = textChangers[pos]
+
+ local updateStat = function(val)
+ statChanger(val)
+ if playerTable[player] ~= nil then recomputeTeamScore(playerTable[player].CurrentTeam, currentColumn) end
+ end
+ if pos > #playerTable[player].StatValues then
+ table.insert(playerTable[player].StatValues, statValue)
+ end
+
+ if type(statValue) ~= "number" and statValue["Changed"] then
+ table.insert(playerTable[player].Connections,
+ statValue.Changed:connect(updateStat)
+ )
+ end
+
+ table.insert(playerTable[player].Connections,
+ statValue.AncestryChanged:connect(
+ function()
+ recreatePlayerFunction(player)
+ end)
+ )
+ updateStat(statValue.Value)
+ pos = pos + 1
+ end
+ end
+ end
+
+ if supportFriends and player ~= game.Players.LocalPlayer and player.userId > 0 and game.Players.LocalPlayer.userId > 0 then
+ local button = Instance.new("TextButton")
+ button.Name = playerObject.Name .. "Button"
+ button.Text = ""
+ button.Active = false
+ button.Size = playerObject.Size
+ button.Position = playerObject.Position
+ button.BackgroundColor3 = playerObject.BackgroundColor3
+
+ local secondButton = Instance.new("TextButton")
+ secondButton.Name = playerObject.Name .. "RealButton"
+ secondButton.Text = ""
+ secondButton.BackgroundTransparency = 1
+ secondButton.BackgroundColor3 = playerObject.BackgroundColor3
+ local theNameLabel = playerObject:findFirstChild("NameLabel",true)
+ if theNameLabel then
+ theNameLabel.TextColor3 = Color3.new(1,1,1)
+ secondButton.Parent = theNameLabel
+ end
+ secondButton.Parent.BackgroundTransparency = 1
+ secondButton.Parent.Visible = true
+ secondButton.ZIndex = 2
+ secondButton.Size = UDim2.new(1,0,1,0)
+
+ local previousTransparency = nil
+ table.insert(playerTable[player].Connections,
+ secondButton.MouseEnter:connect(
+ function(x,y)
+ if playerContextMenu and playerContextMenu.Visible then
+ return
+ end -- don't update if we currently see it
+
+ updatePlayerContextMenu(player,x,y)
+ if not playerContextMenuHasItems() then return end -- don't show if we have nothing to show
+
+ if previousTransparency == nil then
+ previousTransparency = secondButton.BackgroundTransparency
+ end
+ secondButton.Parent.BackgroundTransparency = 0
+ end))
+ table.insert(playerTable[player].Connections,
+ secondButton.MouseLeave:connect(
+ function()
+ if previousTransparency ~= nil then
+ previousTransparency = nil
+ end
+ delay(0.01,function()
+ if playerContextMenu and not inContextMenu then
+ playerContextMenu.Visible = false
+ end
+ end)
+ secondButton.Parent.BackgroundTransparency = 1
+ end))
+
+ local mouseDownX, mouseDownY
+ table.insert(playerTable[player].Connections,
+ secondButton.MouseButton1Down:connect(function(x,y)
+ mouseDownX = x
+ mouseDownY = y
+ end))
+ table.insert(playerTable[player].Connections,
+ secondButton.MouseButton1Click:connect(function()
+ showPlayerMenu(player, mouseDownX, secondButton.AbsolutePosition.Y + secondButton.AbsoluteSize.Y )
+ end))
+ playerObject.BackgroundTransparency = 1
+ playerObject.Size = UDim2.new(1,0,1,0)
+ playerObject.Position = UDim2.new(0,0,0,0)
+ playerObject.Parent = button
+
+ playerTable[player]["MainObject" .. suffix] = button
+
+ playerObject = button
+ else
+ playerTable[player]["MainObject" .. suffix] = playerObject
+
+ if player == game.Players.LocalPlayer and supportFriends then
+ table.insert(playerTable[player].Connections,
+ player.FriendStatusChanged:connect(
+ function(otherPlayer, friendStatus)
+ if friendRequestBlacklist[otherPlayer] then
+ updatePlayerFriendStatus(playerTable[otherPlayer]["NameObject" .. suffix], Enum.FriendStatus.NotFriend)
+ elseif playerTable[otherPlayer] then
+ updatePlayerFriendStatus(playerTable[otherPlayer]["NameObject" .. suffix], friendStatus)
+ end
+ end)
+ )
+ end
+ end
+ table.insert(playerTable[player].Connections,
+ player.Changed:connect(
+ function(prop)
+ if prop == "MembershipType" then
+ updatePlayerName(playerTable[player]["NameObject" .. suffix], player.MembershipType, currentColor)
+ elseif prop == "Name" then
+ playerTable[player]["ChangeName" .. suffix](player.Name)
+ elseif prop == "Neutral" or prop == "TeamColor" then
+ assignToTeam(player)
+ end
+ end)
+ )
+ return playerObject
+ end
+
+ local function doSort(tableToSort, objectName, order, startPos, sortType, ascending)
+ local orderedPlayerTable = {}
+ getLocalPlayer = false
+ for i, player in ipairs(tableToSort) do
+ if playerTable[player] then
+ if playerTable[player][objectName] ~= nil then
+ if player ~= game.Players.LocalPlayer then
+ table.insert(orderedPlayerTable,playerTable[player][objectName])
+ else
+ getLocalPlayer = true
+ end
+ end
+ end
+ end
+
+ if sortType == nil then -- default back to alphabetical sort
+ table.sort(orderedPlayerTable,function(a,b)
+ return string.lower(a:FindFirstChild("FullNameLabel",true).Text) < string.lower(b:FindFirstChild("FullNameLabel",true).Text)
+ end)
+ else -- we are sorting by a value
+ table.sort(orderedPlayerTable,function(a,b)
+ if ascending then
+ currentSortName = ""
+ return tonumber(a:FindFirstChild(sortType,true).Text) > tonumber(b:FindFirstChild(sortType,true).Text)
+ else
+ currentSortName = sortType
+ return tonumber(a:FindFirstChild(sortType,true).Text) < tonumber(b:FindFirstChild(sortType,true).Text)
+ end
+ end)
+ end
+ if getLocalPlayer and playerTable[game.Players.LocalPlayer] and playerTable[game.Players.LocalPlayer][objectName] then
+ table.insert(orderedPlayerTable,1,playerTable[game.Players.LocalPlayer][objectName])
+ end
+ for i = 1, #orderedPlayerTable do
+ order[orderedPlayerTable[i]] = startPos
+ startPos = startPos + 1
+ end
+
+ return startPos
+ end
+
+ local function orderScrollList(scrollOrder, objectName, scrollFrame, sortType, ascending)
+ local pos = 0
+ local order = {}
+ local isTeam, isScore = getBoardTypeInfo()
+ for i, obj in ipairs(scrollOrder) do
+ order[obj] = 0
+ end
+
+ if isTeam then
+ local teams = getTeams()
+ for i, team in ipairs(teams) do
+ if teamTable[team][objectName] then
+ order[teamTable[team][objectName]] = pos
+ pos = pos + 1
+ end
+ pos = doSort(teamTable[team].Players, objectName, order, pos, sortType, ascending)
+ end
+
+ if #teamTable["Neutral"].Players > 0 then
+ teamTable["Neutral"][objectName].Parent = scrollFrame
+ order[teamTable["Neutral"][objectName]] = pos
+ pos = pos + 1
+ doSort(teamTable["Neutral"].Players, objectName, order, pos, sortType, ascending)
+ else
+ teamTable["Neutral"][objectName].Parent = nil
+ end
+ else
+ local players = getPlayers()
+ doSort(players, objectName, order, pos, sortType, ascending)
+ end
+
+ table.sort(scrollOrder,
+ function(a,b)
+ return order[a] < order[b]
+ end)
+ end
+
+ local function createPlayerListBasics(frame, isBig)
+ local headerFrame = Instance.new("Frame")
+ headerFrame.Name = "Header"
+ headerFrame.BackgroundTransparency = 1
+ headerFrame.Size = UDim2.new(1,-13,0,26)
+ headerFrame.Position = UDim2.new(0,0,0,0)
+ headerFrame.Parent = frame
+
+ local lowerPaneFrame = Instance.new("Frame")
+ lowerPaneFrame.Name = "ScrollingArea"
+ lowerPaneFrame.BackgroundTransparency = 1
+ lowerPaneFrame.Size = UDim2.new(1,-3,1,-26)
+ if not isBig then lowerPaneFrame.Size = UDim2.new(1,-3,1,-30) end
+ lowerPaneFrame.Position = UDim2.new(0,0,0,26)
+ lowerPaneFrame.Parent = frame
+
+ local scrollOrder = {}
+ local scrollFrame, scrollUp, scrollDown, recalculateScroll, scrollBar = RbxGui.CreateScrollingFrame(scrollOrder)
+
+ scrollBar.Size = UDim2.new(0, 17, 1, -36)
+ if isBig then scrollBar.Size = UDim2.new(0, 17, 1, -61) end
+ scrollBar.Parent = lowerPaneFrame
+
+ scrollFrame.Parent = lowerPaneFrame
+ scrollUp.Parent = lowerPaneFrame
+ scrollDown.Parent = lowerPaneFrame
+
+ if isBig then
+ scrollFrame.Position = UDim2.new(0,0,0,0)
+ scrollUp.Position = UDim2.new(1,-41,0,5)
+ scrollDown.Position = UDim2.new(1,-41,1,-35)
+ scrollBar.Position = UDim2.new(1, -41, 0, 24)
+
+ scrollFrame.Size = UDim2.new(1,-48,1,-16)
+ headerFrame.Size = UDim2.new(1,-20,0,26)
+
+ else
+ scrollBar.Position = UDim2.new(1, -15, 0, 14)
+ scrollBar.Size = UDim2.new(0,17,1,-36)
+ scrollFrame.Position = UDim2.new(0,1,0,0)
+ scrollUp.Position = UDim2.new(1,-15,0,-5)
+ scrollDown.Position = UDim2.new(1,-15,1,-20)
+
+ lowerPaneFrame.Position = UDim2.new(0,0,0,30)
+
+ local toggleScrollBar = function(visible)
+ if visible then
+ scrollFrame.Size = UDim2.new(1,-16,1,0)
+ headerFrame.Size = UDim2.new(1,-16,0,smallWindowHeaderYSize)
+ else
+ scrollFrame.Size = UDim2.new(1,0,1,0)
+ headerFrame.Size = UDim2.new(1,0,0,smallWindowHeaderYSize)
+ end
+ scrollUp.Visible = visible
+ scrollDown.Visible = visible
+ scrollBar.Visible = visible
+ end
+ scrollUp.Changed:connect(function(prop)
+ if prop == "Active" then
+ toggleScrollBar(scrollUp.Active or scrollDown.Active)
+ end
+ end)
+
+ scrollDown.Changed:connect(function(prop)
+ if prop == "Active" then
+ toggleScrollBar(scrollUp.Active or scrollDown.Active)
+ end
+ end)
+
+ toggleScrollBar(scrollUp.Active or scrollDown.Active)
+ end
+ return headerFrame, scrollFrame, recalculateScroll, scrollOrder
+ end
+
+ createBoardsFunction = function (boardType, numStatColumns)
+ local updatePlayerCount = function()
+ return #getPlayers()
+ end
+
+ local smallFrame = Instance.new("Frame")
+ smallFrame.Name = "SmallPlayerlist"
+ smallFrame.Position = smallWindowPosition
+ smallFrame.Active = false
+ smallFrame.Size = smallWindowSize
+ smallFrame.BackgroundColor3 = Color3.new(0,0,0)
+ smallFrame.BackgroundTransparency = 0.7
+ smallFrame.BorderSizePixel = 0
+
+ local bigFrame = Instance.new("Frame")
+ bigFrame.Name = "BigPlayerlist"
+ bigFrame.Size = bigWindowSize
+ bigFrame.Position = bigWindowPosition
+ bigFrame.BackgroundColor3 = Color3.new(0,0,0)
+ bigFrame.BackgroundTransparency = 0.7
+ bigFrame.BorderSizePixel = 0
+ bigFrame.Visible = false
+
+ local bigFrameWrapper = Instance.new("Frame")
+ bigFrameWrapper.Name = "Expander"
+ bigFrameWrapper.Size = UDim2.new(1,21,1,16)
+ bigFrameWrapper.Position = UDim2.new(0, 0, 0,0)
+ bigFrameWrapper.BackgroundTransparency = 1
+ bigFrameWrapper.Parent = bigFrame
+
+ local smallHeaderFrame, scrollFrameSmall, recalculateScrollSmall, scrollOrderSmall = createPlayerListBasics(smallFrame, false)
+ local bigHeaderFrame, scrollFrameBig, recalculateScrollBig, scrollOrderBig = createPlayerListBasics(bigFrameWrapper, true)
+
+ local playerListButton = Instance.new("ImageButton")
+ playerListButton.Name = "GoBigButton"
+ playerListButton.BackgroundTransparency = 1
+ playerListButton.Image = "rbxasset://textures/ui/playerlist_small_maximize.png"
+ playerListButton.Size = UDim2.new(0.0, 35, 0, 29)
+ playerListButton.Position = UDim2.new(0, 0, 0, 0)
+ playerListButton.MouseButton1Click:connect(
+ function()
+ toggleBigWindow()
+ end)
+ playerListButton.Parent = smallHeaderFrame
+
+ playerListButton = Instance.new("ImageButton")
+ playerListButton.Name = "CloseButton"
+ playerListButton.BackgroundTransparency = 1
+ playerListButton.Image = "rbxasset://textures/ui/playerlist_small_hide.png"
+ playerListButton.Size = UDim2.new(0.0, 38, 0, 29)
+ playerListButton.Position = UDim2.new(0, 35, 0, 0)
+ playerListButton.MouseButton1Click:connect(
+ function()
+ transitionWindowsFunction("None")
+ end)
+ playerListButton.Parent = smallHeaderFrame
+
+ playerListButton = Instance.new("ImageButton")
+ playerListButton.Name = "CloseButton"
+ playerListButton.Image = "rbxasset://textures/ui/playerlist_big_hide.png"
+ playerListButton.BackgroundTransparency = 1
+ playerListButton.Size = UDim2.new(0.0, 29, 0, 29)
+ playerListButton.Position = UDim2.new(1, -30, 0.5, -13)
+ playerListButton.MouseButton1Click:connect(
+ function()
+ toggleBigWindow()
+ end)
+ playerListButton.Parent = bigHeaderFrame
+
+ local placeName = Instance.new("TextButton")
+ placeName.Name = "PlaceName"
+ placeName.Text = " Players (" .. tostring(updatePlayerCount()) .. ")"
+ placeName.AutoButtonColor = false
+ placeName.FontSize = Enum.FontSize.Size24
+ placeName.TextXAlignment = Enum.TextXAlignment.Left
+ placeName.Font = Enum.Font.ArialBold
+ placeName.BorderSizePixel = 0
+ placeName.BackgroundColor3 = Color3.new(0,0,0)
+ placeName.BackgroundTransparency = 1
+ placeName.TextColor3 = Color3.new(1,1,1)
+ placeName.Size = UDim2.new(0.4, 0, 1, 0)
+ placeName.Position = UDim2.new(0, 0, 0.0, 0)
+ placeName = RbxGui.AutoTruncateTextObject(placeName)
+ placeName.Parent = bigHeaderFrame
+
+ placeName.MouseEnter:connect(function()
+ placeName.BackgroundTransparency = 0.2
+ end)
+
+ placeName.MouseLeave:connect(function()
+ placeName.BackgroundTransparency = 1
+ end)
+
+ placeName.MouseButton1Click:connect(function()
+ sortPlayerListsFunction()
+ end)
+
+ currentBoardType = boardType
+ currentStatCount = numStatColumns
+ local isTeam, isScore = getBoardTypeInfo()
+ local players = getPlayers()
+
+ if isScore then
+ local statColumns = getStatColumns(players)
+ numStatColumns = #statColumns
+ if numStatColumns > 3 then
+ numStatColumns = 3
+ end
+ createStatHeaders(statColumns, numStatColumns, false).Parent = smallHeaderFrame
+ createStatHeaders(statColumns, currentStatCount, true).Parent = bigHeaderFrame
+ end
+
+ --Clean up all old stuff
+ resetPlayerTable()
+ updatePlayerCount()
+
+ for i, player in ipairs(players) do
+ local playerObject = buildPlayerObject(player, numStatColumns, "Small")
+ table.insert(scrollOrderSmall, playerObject)
+ playerObject.Parent = scrollFrameSmall
+
+ playerObject = buildPlayerObject(player, currentStatCount, "Big")
+ table.insert(scrollOrderBig, playerObject)
+ playerObject.Parent = scrollFrameBig
+ end
+
+ --Clean up old stuff
+ resetTeamTable()
+
+ local teamStatObjects = {}
+ if isTeam then
+ local teams = getTeams()
+ local i = #teams
+ while i >= 1 do
+ --We go backwards so the "first" team color gets the team
+ local team = teams[i]
+ teamColorTable[team.TeamColor.Name] = team
+ i = i - 1
+ end
+
+ --Adding/Removing a Team causes a full invalidation of the board
+ for i, team in ipairs(teams) do
+ local teamObject = buildTeamObject(team, numStatColumns, "Small")
+ table.insert(scrollOrderSmall, teamObject)
+ teamObject.Parent = scrollFrameSmall
+
+ teamObject = buildTeamObject(team, currentStatCount, "Big")
+ table.insert(scrollOrderBig, teamObject)
+ teamObject.Parent = scrollFrameBig
+ end
+
+ teamTable["Neutral"] = {}
+ teamTable["Neutral"].Players = {}
+
+ local neutralTeamObject = createTeamName("Neutral", BrickColor.palette(8))
+ teamTable["Neutral"].NameObjectSmall = neutralTeamObject
+ teamTable["Neutral"].StatObjectSmall = nil
+ teamTable["Neutral"].MainObjectSmall = neutralTeamObject
+ table.insert(scrollOrderSmall, neutralTeamObject)
+
+ neutralTeamObject = createTeamName("Neutral", BrickColor.palette(8))
+ teamTable["Neutral"].NameObjectBig = neutralTeamObject
+ teamTable["Neutral"].StatObjectBig = nil
+ teamTable["Neutral"].MainObjectBig = neutralTeamObject
+ table.insert(scrollOrderBig, neutralTeamObject)
+
+ local neutralPlayers = {}
+ for i, player in ipairs(players) do
+ assignToTeam(player)
+ end
+ end
+
+ removePlayerFunction = function(player)
+ if playerTable[player] then
+ clearTableEntry(player, playerTable[player])
+
+ placeName.Text = " Players (" .. tostring(updatePlayerCount()) .. ")"
+
+ ArrayRemove(scrollOrderSmall, playerTable[player].MainObjectSmall)
+ ArrayRemove(scrollOrderBig, playerTable[player].MainObjectBig)
+
+ playerTable[player] = nil
+ recalculateSmallPlayerListSize(smallFrame)
+ end
+ end
+ recreatePlayerFunction = function(player)
+ placeName.Text = " Players (" .. tostring(updatePlayerCount()) .. ")"
+
+ removePlayerFunction(player)
+
+ local playerObject = buildPlayerObject(player, numStatColumns, "Small")
+ table.insert(scrollOrderSmall, playerObject)
+ robloxLock(playerObject)
+ playerObject.Parent = scrollFrameSmall
+
+ playerObject = buildPlayerObject(player, currentStatCount, "Big")
+ table.insert(scrollOrderBig, playerObject)
+ robloxLock(playerObject)
+ playerObject.Parent = scrollFrameBig
+
+ local isTeam, isScore = getBoardTypeInfo()
+ if isTeam then
+ assignToTeam(player)
+ end
+
+ sortPlayerListsFunction()
+ recalculateSmallPlayerListSize(smallFrame)
+ end
+
+ if screenResizeCon then screenResizeCon:disconnect() end
+ screenResizeCon = screen.Changed:connect(
+ function(prop)
+ if prop == "AbsoluteSize" then
+ wait()
+ recalculateSmallPlayerListSize(smallFrame)
+ end
+ end)
+
+ sortPlayerListsFunction = function(sortType, ascending)
+ orderScrollList(scrollOrderSmall, "MainObjectSmall", scrollFrameSmall, sortType, ascending)
+ recalculateScrollSmall()
+ createAlternatingRows(scrollOrderSmall)
+
+ orderScrollList(scrollOrderBig, "MainObjectBig", scrollFrameBig, sortType, ascending)
+ recalculateScrollBig()
+ createAlternatingRows(scrollOrderBig)
+ end
+
+ sortPlayerListsFunction()
+
+ robloxLock(smallFrame)
+ robloxLock(bigFrame)
+ return smallFrame, bigFrame
+ end
+
+ --Teams changing invalidates the whole board
+ local function teamsChanged()
+ if debounceTeamsChanged then
+ return
+ end
+
+ debounceTeamsChanged = true
+ wait()
+ rebuildBoard(script.Parent, determineBoardType())
+ debounceTeamsChanged = false
+ end
+
+
+ local checkIfBoardChanged = function()
+ local newBoardType, numStats = determineBoardType()
+ if newBoardType ~= currentBoardType or numStats ~= currentStatCount then
+ rebuildBoard(script.Parent, newBoardType, numStats)
+ end
+ end
+
+ local function buildPlayerList()
+ waitForChild(game, "Players")
+ waitForProperty(game.Players, "LocalPlayer")
+
+ local teams = game:GetService("Teams")
+ if teams then
+ local teamConnections = {}
+
+ teams.ChildAdded:connect(
+ function(child)
+ if child:IsA("Team") then
+ teamsChanged()
+ teamConnections[child] = child.Changed:connect(
+ function(prop)
+ if prop == "TeamColor" or prop == "Name" then
+ --Rebuild when things change
+ teamsChanged()
+ end
+ end)
+ end
+ end)
+ teams.ChildRemoved:connect(
+ function(child)
+ if child:IsA("Team") then
+ if teamConnections[child] then
+ teamConnections[child]:disconnect()
+ teamConnections[child] = nil
+ end
+ teamsChanged()
+ end
+ end)
+ end
+
+ game.Players.ChildAdded:connect(
+ function(player)
+ if player:IsA("Player") then
+ addPlayerFunction(player)
+ end
+ end)
+
+ game.Players.ChildRemoved:connect(
+ function(player)
+ if player:IsA("Player") then
+ if removePlayerFunction then
+ removePlayerFunction(player)
+ end
+ end
+ end)
+
+ rebuildBoard(script.Parent, determineBoardType())
+ game.GuiService.ShowLegacyPlayerList = false
+
+ game.GuiService:AddKey("\t")
+ local lastTime = nil
+ game.GuiService.KeyPressed:connect(
+ function(key)
+ if key == "\t" then
+ local modalCheck, isModal = pcall(function() return game.GuiService.IsModalDialog end)
+ if modalCheck == false or (modalCheck and isModal == false) then
+ local currentTime = time()
+ if lastTime == nil or currentTime - lastTime > 0.4 then
+ lastTime = currentTime
+ toggleBigWindow()
+ end
+ end
+ end
+ end)
+
+ delay(0,
+ function()
+ while true do
+ wait(5)
+ checkIfBoardChanged()
+ end
+ end)
+ end
+
+ if game.CoreGui.Version >= 2 then
+ buildPlayerList()
+ end
+end
+
+if not personalServerPlace then -- one more backup check
+ local theBool = game.Workspace:FindFirstChild("PSVariable")
+ if theBool and theBool:IsA("BoolValue") then
+ personalServerPlace = true
+ end
+end
+
+if personalServerPlace then
+ addPersonalServerContext()
+ setupBuildToolManagement()
+else
+ local psVarCon = nil
+ psVarCon = game.Workspace.ChildAdded:connect(function(child)
+ if child:IsA("BoolValue") and child.Name == "PSVariable" then
+ psVarCon:disconnect()
+ personalServerPlace = true
+ addPersonalServerContext()
+ setupBuildToolManagement()
+ end
+ end)
+end
+
+
+---------------------------------- Start Player Hover Code ----------------------------------------
+if contextMenu3d then
+ local inMenu = false
+
+ function waitForProperty(instance, name)
+ while not instance[name] do
+ instance.Changed:wait()
+ end
+ end
+
+ function makeNewActionButton()
+ local button = Instance.new("TextButton")
+ button.Name = "ActionButton"
+ button.Style = Enum.ButtonStyle.RobloxButtonDefault
+ button.BackgroundColor3 = Color3.new(0,0,0)
+ button.BorderColor3 = Color3.new(1,0,0)
+ button.BackgroundTransparency = 0.5
+ button.Size = UDim2.new(1,0,0,50)
+ button.Text = ""
+ button.Font = Enum.Font.ArialBold
+ button.FontSize = Enum.FontSize.Size18
+ button.TextColor3 = Color3.new(1,1,1)
+ button.ZIndex = 4
+ return button
+ end
+
+ function getContextElements(currentContextMenuPlayer)
+ local elements = {}
+ for i, contextElement in ipairs(contextMenuElements) do
+ local element = contextElement
+
+ local isVisible = false
+
+ if contextElement.IsVisible then
+ local success, visible = pcall(function() return contextElement.IsVisible(currentContextMenuPlayer) end)
+ if success then
+ isVisible = visible
+ else
+ print("Error in IsVisible call: " .. visible)
+ end
+ end
+
+ if element.Type == "Button" then
+ local button = makeNewActionButton()
+ button.Name = "ContextButton" .. i
+ button.Visible = isVisible
+ button.Text = contextElement.Text
+ button.MouseButton1Click:connect(function()
+ if button.Active then
+ local success, result = pcall(function() element.DoIt(currentContextMenuPlayer) end)
+ end
+ end)
+
+ contextElement.Button = button
+ contextElement.Element = button
+
+ table.insert(elements,contextElement)
+ end
+ end
+
+ return elements
+ end
+
+ function findContextElement(contextElements, button)
+ for i = 1, #contextElements do
+ if contextElements[i].Button == button then
+ return contextElements[i]
+ end
+ end
+ end
+
+ function populateActions(scrollFrame, nullFrame, recalcFunction, otherPlayer)
+ local elements = getContextElements(otherPlayer)
+ for i = 1, #elements do
+ if elements[i].Button.Visible then
+ elements[i].Button.Parent = scrollFrame
+ else
+ elements[i].Button.Parent = nullFrame
+ end
+
+ local actionButtonCon
+ actionButtonCon = elements[i].Button.MouseButton1Click:connect(function()
+ actionButtonCon:disconnect()
+
+ local nullFrameChildren = nullFrame:GetChildren()
+ for j = 1, #nullFrameChildren do
+ local contextElement = findContextElement(elements, nullFrameChildren[j])
+ pcall(function() nullFrameChildren[j].Visible = contextElement.IsVisible(otherPlayer) end)
+ if nullFrameChildren[j].Visible then
+ nullFrameChildren[j].Parent = scrollFrame
+ end
+ end
+
+ local scrollFrameChildren = scrollFrame:GetChildren()
+ for j = 1, #scrollFrameChildren do
+ local contextElement = findContextElement(elements, scrollFrameChildren[j])
+ pcall(function() scrollFrameChildren[j].Visible = contextElement.IsVisible(otherPlayer) end)
+ if not scrollFrameChildren[j].Visible then
+ scrollFrameChildren[j].Parent = nullFrame
+ end
+ end
+
+ elements[i].Button.Parent = nullFrame
+ recalcFunction()
+ end)
+ end
+ end
+
+
+ function createContextMenu(otherPlayer)
+
+ local frame = Instance.new("Frame")
+ frame.Name = "ContextMenuFrame"
+ frame.Style = Enum.FrameStyle.RobloxRound
+ frame.Size = UDim2.new(0,300,0,400)
+ frame.Position = UDim2.new(0.5,-150,0.5,-200)
+ frame.ZIndex = 2
+
+ local scrollFrame, scrollUpButton, scrollDownButton, recalc, scrollBar = RbxGui.CreateScrollingFrame()
+
+ scrollFrame.Name = "Actions"
+ scrollFrame.BackgroundTransparency = 1
+ scrollFrame.Position = UDim2.new(0,0,0,25)
+ scrollFrame.Size = UDim2.new(1,-20,1,-80)
+ scrollFrame.ZIndex = 3
+ scrollFrame.Parent = frame
+
+ local nullFrame = Instance.new("Frame")
+ nullFrame.Name = "NullFrame"
+ nullFrame.BackgroundTransparency = 1
+ nullFrame.Visible = false
+ nullFrame.Parent = frame
+
+ local scrollButtons = Instance.new("Frame")
+ scrollButtons.Name = "ScrollButtons"
+ scrollButtons.BackgroundTransparency = 1
+ scrollButtons.Position = UDim2.new(1,-17,0,25)
+ scrollButtons.Size = UDim2.new(0,17,1,-80)
+ scrollButtons.ZIndex = 3
+ scrollButtons.Parent = frame
+
+ scrollUpButton.ZIndex = 3
+ scrollUpButton.Parent = scrollButtons
+ scrollDownButton.Position = UDim2.new(0,0,1,-17)
+ scrollDownButton.ZIndex = 3
+ scrollDownButton.Parent = scrollButtons
+
+ scrollBar.Size = UDim2.new(1,0,1,-34)
+ scrollBar.Position = UDim2.new(0,0,0,17)
+ scrollBar.Parent = scrollButtons
+
+ local playerImage = Instance.new("ImageLabel")
+ playerImage.Name = "PlayerImage"
+ playerImage.BackgroundTransparency = 1
+ playerImage.Image = "http://www.roblox.com/thumbs/avatar.ashx?userId=" .. tostring(otherPlayer.userId) .. "&x=352&y=352"
+ playerImage.Position = UDim2.new(0.5,-150,0.5,-150)
+ playerImage.Size = UDim2.new(0,300,0,300)
+ playerImage.Parent = frame
+
+ local playerName = Instance.new("TextLabel")
+ playerName.Name = "PlayerName"
+ playerName.BackgroundTransparency = 1
+ playerName.Font = Enum.Font.ArialBold
+ playerName.FontSize = Enum.FontSize.Size24
+ playerName.Position = UDim2.new(0,-8,0,-6)
+ playerName.Size = UDim2.new(1,16,0,24)
+ playerName.Text = otherPlayer["Name"]
+ playerName.TextColor3 = Color3.new(1,1,1)
+ playerName.TextWrap = true
+ playerName.ZIndex = 3
+ playerName.Parent = frame
+
+ local doneButtonCon
+
+ local doneButton = Instance.new("TextButton")
+ doneButton.Name = "DoneButton"
+ doneButton.Style = Enum.ButtonStyle.RobloxButton
+ doneButton.Font = Enum.Font.ArialBold
+ doneButton.FontSize = Enum.FontSize.Size36
+ doneButton.Position = UDim2.new(0.25,0,1,-50)
+ doneButton.Size = UDim2.new(0.5,0,0,50)
+ doneButton.Text = "Done"
+ doneButton.TextColor3 = Color3.new(1,1,1)
+ doneButton.ZIndex = 3
+ doneButton.Parent = frame
+ doneButton.Modal = true
+ doneButtonCon = doneButton.MouseButton1Click:connect(function()
+ doneButtonCon:disconnect()
+ inMenu = false
+ game.GuiService:RemoveCenterDialog(frame)
+ frame:remove()
+ end)
+
+ populateActions(scrollFrame, nullFrame, recalc, otherPlayer)
+ recalc()
+
+ return frame
+ end
+
+ function makeContextInvisible(menu)
+ menu.Visible = false
+ end
+
+ function goToContextMenu(otherPlayer)
+
+ local menu = createContextMenu(otherPlayer)
+
+ game.GuiService:AddCenterDialog(menu, Enum.CenterDialogType.PlayerInitiatedDialog,
+ --ShowFunction
+ function()
+ menu.Visible = true
+ menu:TweenSize(UDim2.new(0,300,0,400),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,0.5,true)
+ end,
+ --HideFunction
+ function()
+ menu:TweenSize(UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,0.5,true,function() makeContextInvisible(menu) end)
+ end)
+ menu.Parent = game.CoreGui.RobloxGui
+
+ inMenu = true
+ end
+
+ waitForProperty(game.Players, "LocalPlayer")
+
+ local currSelectedPlayer = nil
+ if game.Players.LocalPlayer["HoverOnPlayerChanged"] then
+ game.Players.LocalPlayer.HoverOnPlayerChanged:connect(function(otherPlayer)
+ if not inMenu then
+ if otherPlayer and otherPlayer.userId < 0 then return end -- we don't want this for guests
+ end
+ wait(0.5)
+ currSelectedPlayer = otherPlayer
+ end)
+ end
+
+ if game.Players.LocalPlayer["MouseDownOnPlayer"] then
+ game.Players.LocalPlayer.MouseDownOnPlayer:connect(function(otherPlayer)
+ if currSelectedPlayer ~= otherPlayer then return end
+ if not inMenu and otherPlayer.userId > 0 then
+ goToContextMenu(otherPlayer)
+ end
+ end)
+ end
+end
+---------------------------------- End Player Hover Code ----------------------------------------
\ No newline at end of file
diff --git a/app/files/CoreGui/48488398 b/app/files/CoreGui/48488398
new file mode 100644
index 0000000..76a4ece
--- /dev/null
+++ b/app/files/CoreGui/48488398
@@ -0,0 +1,306 @@
+function waitForProperty(instance, property)
+ while not instance[property] do
+ instance.Changed:wait()
+ end
+end
+function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+
+waitForProperty(game.Players,"LocalPlayer")
+waitForChild(script.Parent,"Popup")
+waitForChild(script.Parent.Popup,"AcceptButton")
+script.Parent.Popup.AcceptButton.Modal = true
+
+local localPlayer = game.Players.LocalPlayer
+local teleportUI = nil
+
+local acceptedTeleport = Instance.new("IntValue")
+
+local friendRequestBlacklist = {}
+
+local teleportEnabled = true
+
+local makePopupInvisible = function()
+ if script.Parent.Popup then script.Parent.Popup.Visible = false end
+end
+
+function makeFriend(fromPlayer,toPlayer)
+
+ local popup = script.Parent:FindFirstChild("Popup")
+ if popup == nil then return end -- there is no popup!
+ if popup.Visible then return end -- currently popping something, abort!
+ if friendRequestBlacklist[fromPlayer] then return end -- previously cancelled friend request, we don't want it!
+
+ popup.PopupText.Text = "Accept Friend Request from " .. tostring(fromPlayer.Name) .. "?"
+ popup.PopupImage.Image = "http://www.syntax.eco/thumbs/avatar.ashx?userId="..tostring(fromPlayer.userId).."&x=352&y=352"
+
+ showTwoButtons()
+ popup.Visible = true
+ popup.AcceptButton.Text = "Accept"
+ popup.DeclineButton.Text = "Decline"
+ popup:TweenSize(UDim2.new(0,330,0,350),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true)
+
+ local yesCon, noCon
+
+ yesCon = popup.AcceptButton.MouseButton1Click:connect(function()
+ popup.Visible = false
+ toPlayer:RequestFriendship(fromPlayer)
+ if yesCon then yesCon:disconnect() end
+ if noCon then noCon:disconnect() end
+ popup:TweenSize(UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true,makePopupInvisible())
+ end)
+
+ noCon = popup.DeclineButton.MouseButton1Click:connect(function()
+ popup.Visible = false
+ toPlayer:RevokeFriendship(fromPlayer)
+ friendRequestBlacklist[fromPlayer] = true
+ print("pop up blacklist")
+ if yesCon then yesCon:disconnect() end
+ if noCon then noCon:disconnect() end
+ popup:TweenSize(UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true,makePopupInvisible())
+ end)
+end
+
+
+game.Players.FriendRequestEvent:connect(function(fromPlayer,toPlayer,event)
+
+ -- if this doesn't involve me, then do nothing
+ if fromPlayer ~= localPlayer and toPlayer ~= localPlayer then return end
+
+ if fromPlayer == localPlayer then
+ if event == Enum.FriendRequestEvent.Accept then
+ game:GetService("GuiService"):SendNotification("You are Friends",
+ "With " .. toPlayer.Name .. "!",
+ "http://www.syntax.eco/thumbs/avatar.ashx?userId="..tostring(toPlayer.userId).."&x=48&y=48",
+ 5,
+ function()
+
+ end)
+ end
+ elseif toPlayer == localPlayer then
+ if event == Enum.FriendRequestEvent.Issue then
+ if friendRequestBlacklist[fromPlayer] then return end -- previously cancelled friend request, we don't want it!
+ game:GetService("GuiService"):SendNotification("Friend Request",
+ "From " .. fromPlayer.Name,
+ "http://www.syntax.eco/thumbs/avatar.ashx?userId="..tostring(fromPlayer.userId).."&x=48&y=48",
+ 8,
+ function()
+ makeFriend(fromPlayer,toPlayer)
+ end)
+ elseif event == Enum.FriendRequestEvent.Accept then
+ game:GetService("GuiService"):SendNotification("You are Friends",
+ "With " .. fromPlayer.Name .. "!",
+ "http://www.syntax.eco/thumbs/avatar.ashx?userId="..tostring(fromPlayer.userId).."&x=48&y=48",
+ 5,
+ function()
+
+ end)
+ end
+ end
+end)
+
+function showOneButton()
+ local popup = script.Parent:FindFirstChild("Popup")
+ if popup then
+ popup.OKButton.Visible = true
+ popup.DeclineButton.Visible = false
+ popup.AcceptButton.Visible = false
+ end
+end
+
+function showTwoButtons()
+ local popup = script.Parent:FindFirstChild("Popup")
+ if popup then
+ popup.OKButton.Visible = false
+ popup.DeclineButton.Visible = true
+ popup.AcceptButton.Visible = true
+ end
+end
+
+function onTeleport(teleportState, placeId, spawnName)
+ if game:GetService("TeleportService").CustomizedTeleportUI == false then
+ if teleportState == Enum.TeleportState.Started then
+ showTeleportUI("Teleport started...", 0)
+ elseif teleportState == Enum.TeleportState.WaitingForServer then
+ showTeleportUI("Requesting server...", 0)
+ elseif teleportState == Enum.TeleportState.InProgress then
+ showTeleportUI("Teleporting...", 0)
+ elseif teleportState == Enum.TeleportState.Failed then
+ showTeleportUI("Teleport failed. Insufficient privileges or target place does not exist.", 3)
+ end
+ end
+end
+
+function showTeleportUI(message, timer)
+ if teleportUI ~= nil then
+ teleportUI:Remove()
+ end
+ waitForChild(localPlayer, "PlayerGui")
+ teleportUI = Instance.new("Message", localPlayer.PlayerGui)
+ teleportUI.Text = message
+ if timer > 0 then
+ wait(timer)
+ teleportUI:Remove()
+ end
+end
+
+if teleportEnabled then
+
+ localPlayer.OnTeleport:connect(onTeleport)
+
+ game:GetService("TeleportService").ErrorCallback = function(message)
+ local popup = script.Parent:FindFirstChild("Popup")
+ showOneButton()
+ popup.PopupText.Text = message
+ local clickCon
+ clickCon = popup.OKButton.MouseButton1Click:connect(function()
+ game:GetService("TeleportService"):TeleportCancel()
+ if clickCon then clickCon:disconnect() end
+ game.GuiService:RemoveCenterDialog(script.Parent:FindFirstChild("Popup"))
+ popup:TweenSize(UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true,makePopupInvisible())
+ end)
+ game.GuiService:AddCenterDialog(script.Parent:FindFirstChild("Popup"), Enum.CenterDialogType.QuitDialog,
+ --ShowFunction
+ function()
+ showOneButton()
+ script.Parent:FindFirstChild("Popup").Visible = true
+ popup:TweenSize(UDim2.new(0,330,0,350),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true)
+ end,
+ --HideFunction
+ function()
+ popup:TweenSize(UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true,makePopupInvisible())
+ end)
+
+ end
+ game:GetService("TeleportService").ConfirmationCallback = function(message, placeId, spawnName)
+ local popup = script.Parent:FindFirstChild("Popup")
+ popup.PopupText.Text = message
+ popup.PopupImage.Image = ""
+
+ local yesCon, noCon
+
+ local function killCons()
+ if yesCon then yesCon:disconnect() end
+ if noCon then noCon:disconnect() end
+ game.GuiService:RemoveCenterDialog(script.Parent:FindFirstChild("Popup"))
+ popup:TweenSize(UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true,makePopupInvisible())
+ end
+
+ yesCon = popup.AcceptButton.MouseButton1Click:connect(function()
+ killCons()
+ local success, err = pcall(function() game:GetService("TeleportService"):TeleportImpl(placeId,spawnName) end)
+ if not success then
+ showOneButton()
+ popup.PopupText.Text = err
+ local clickCon
+ clickCon = popup.OKButton.MouseButton1Click:connect(function()
+ if clickCon then clickCon:disconnect() end
+ game.GuiService:RemoveCenterDialog(script.Parent:FindFirstChild("Popup"))
+ popup:TweenSize(UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true,makePopupInvisible())
+ end)
+ game.GuiService:AddCenterDialog(script.Parent:FindFirstChild("Popup"), Enum.CenterDialogType.QuitDialog,
+ --ShowFunction
+ function()
+ showOneButton()
+ script.Parent:FindFirstChild("Popup").Visible = true
+ popup:TweenSize(UDim2.new(0,330,0,350),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true)
+ end,
+ --HideFunction
+ function()
+ popup:TweenSize(UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true,makePopupInvisible())
+ end)
+ end
+ end)
+
+ noCon = popup.DeclineButton.MouseButton1Click:connect(function()
+ killCons()
+ local success = pcall(function() game:GetService("TeleportService"):TeleportCancel() end)
+ end)
+
+ local centerDialogSuccess = pcall(function() game.GuiService:AddCenterDialog(script.Parent:FindFirstChild("Popup"), Enum.CenterDialogType.QuitDialog,
+ --ShowFunction
+ function()
+ showTwoButtons()
+ popup.AcceptButton.Text = "Leave"
+ popup.DeclineButton.Text = "Stay"
+ script.Parent:FindFirstChild("Popup").Visible = true
+ popup:TweenSize(UDim2.new(0,330,0,350),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true)
+ end,
+ --HideFunction
+ function()
+ popup:TweenSize(UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true,makePopupInvisible())
+ end)
+ end)
+
+ if centerDialogSuccess == false then
+ script.Parent:FindFirstChild("Popup").Visible = true
+ popup.AcceptButton.Text = "Leave"
+ popup.DeclineButton.Text = "Stay"
+ popup:TweenSize(UDim2.new(0,330,0,350),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true)
+ end
+ return true
+
+ end
+end
+
+game:GetService("MarketplaceService").ClientLuaDialogRequested:connect(function(message, accept, decline)
+ local popup = script.Parent:FindFirstChild("Popup")
+ popup.PopupText.Text = message
+ popup.PopupImage.Image = ""
+
+ local yesCon, noCon
+
+ local function killCons()
+ if yesCon then yesCon:disconnect() end
+ if noCon then noCon:disconnect() end
+ game.GuiService:RemoveCenterDialog(script.Parent:FindFirstChild("Popup"))
+ popup:TweenSize(UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true,makePopupInvisible())
+ end
+
+ yesCon = popup.AcceptButton.MouseButton1Click:connect(function()
+ killCons()
+ game:GetService("MarketplaceService"):SignalServerLuaDialogClosed(true);
+ end)
+
+ noCon = popup.DeclineButton.MouseButton1Click:connect(function()
+ killCons()
+ game:GetService("MarketplaceService"):SignalServerLuaDialogClosed(false);
+ end)
+
+ local centerDialogSuccess = pcall(function() game.GuiService:AddCenterDialog(script.Parent:FindFirstChild("Popup"), Enum.CenterDialogType.QuitDialog,
+ function()
+ showTwoButtons()
+ popup.AcceptButton.Text = accept
+ popup.DeclineButton.Text = decline
+ script.Parent:FindFirstChild("Popup").Visible = true
+ popup:TweenSize(UDim2.new(0,330,0,350),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true)
+ end,
+ function()
+ popup:TweenSize(UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true,makePopupInvisible())
+ end)
+ end)
+
+ if centerDialogSuccess == false then
+ script.Parent:FindFirstChild("Popup").Visible = true
+ popup.AcceptButton.Text = accept
+ popup.DeclineButton.Text = decline
+ popup:TweenSize(UDim2.new(0,330,0,350),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,1,true)
+ end
+
+ return true
+
+end)
+
+Game:GetService("PointsService").PointsAwarded:connect( function(userId, pointsAwarded, userBalanceInGame, userTotalBalance)
+ if userId == Game.Players.LocalPlayer.userId then
+ game:GetService("GuiService"):SendNotification("Points Awarded!",
+ "You received " ..tostring(pointsAwarded) .. " points!",
+ "http://www.syntax.eco/asset?id=155363793",
+ 5,
+ function()
+ end)
+ end
+end)
diff --git a/app/files/CoreGui/48488451 b/app/files/CoreGui/48488451
new file mode 100644
index 0000000..cfcb8b7
--- /dev/null
+++ b/app/files/CoreGui/48488451
@@ -0,0 +1,72 @@
+--build our gui
+
+local popupFrame = Instance.new("Frame")
+popupFrame.Position = UDim2.new(0.5,-165,0.5,-175)
+popupFrame.Size = UDim2.new(0,330,0,350)
+popupFrame.Style = Enum.FrameStyle.RobloxRound
+popupFrame.ZIndex = 4
+popupFrame.Name = "Popup"
+popupFrame.Visible = false
+popupFrame.Parent = script.Parent
+
+local darken = popupFrame:clone()
+darken.Size = UDim2.new(1,16,1,16)
+darken.Position = UDim2.new(0,-8,0,-8)
+darken.Name = "Darken"
+darken.ZIndex = 1
+darken.Parent = popupFrame
+
+local acceptButton = Instance.new("TextButton")
+acceptButton.Position = UDim2.new(0,20,0,270)
+acceptButton.Size = UDim2.new(0,100,0,50)
+acceptButton.Font = Enum.Font.ArialBold
+acceptButton.FontSize = Enum.FontSize.Size24
+acceptButton.Style = Enum.ButtonStyle.RobloxButton
+acceptButton.TextColor3 = Color3.new(248/255,248/255,248/255)
+acceptButton.Text = "Yes"
+acceptButton.ZIndex = 5
+acceptButton.Name = "AcceptButton"
+acceptButton.Parent = popupFrame
+
+local declineButton = acceptButton:clone()
+declineButton.Position = UDim2.new(1,-120,0,270)
+declineButton.Text = "No"
+declineButton.Name = "DeclineButton"
+declineButton.Parent = popupFrame
+
+local okButton = acceptButton:clone()
+okButton.Name = "OKButton"
+okButton.Text = "OK"
+okButton.Position = UDim2.new(0.5,-50,0,270)
+okButton.Visible = false
+okButton.Parent = popupFrame
+
+local popupImage = Instance.new("ImageLabel")
+popupImage.BackgroundTransparency = 1
+popupImage.Position = UDim2.new(0.5,-140,0,0)
+popupImage.Size = UDim2.new(0,280,0,280)
+popupImage.ZIndex = 3
+popupImage.Name = "PopupImage"
+popupImage.Parent = popupFrame
+
+local backing = Instance.new("ImageLabel")
+backing.BackgroundTransparency = 1
+backing.Size = UDim2.new(1,0,1,0)
+backing.Image = "http://www.syntax.eco/asset/?id=47574181"
+backing.Name = "Backing"
+backing.ZIndex = 2
+backing.Parent = popupImage
+
+local popupText = Instance.new("TextLabel")
+popupText.Name = "PopupText"
+popupText.Size = UDim2.new(1,0,0.8,0)
+popupText.Font = Enum.Font.ArialBold
+popupText.FontSize = Enum.FontSize.Size36
+popupText.BackgroundTransparency = 1
+popupText.Text = "Hello I'm a popup"
+popupText.TextColor3 = Color3.new(248/255,248/255,248/255)
+popupText.TextWrap = true
+popupText.ZIndex = 5
+popupText.Parent = popupFrame
+
+script:remove()
\ No newline at end of file
diff --git a/app/files/CoreGui/53878047 b/app/files/CoreGui/53878047
new file mode 100644
index 0000000..4267ddf
--- /dev/null
+++ b/app/files/CoreGui/53878047
@@ -0,0 +1,874 @@
+-- This script creates almost all gui elements found in the backpack (warning: there are a lot!)
+-- TODO: automate this process
+
+if game.CoreGui.Version < 3 then return end -- peace out if we aren't using the right client
+
+local gui = script.Parent
+
+-- A couple of necessary functions
+local function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+local function waitForProperty(instance, property)
+ while not instance[property] do
+ instance.Changed:wait()
+ end
+end
+
+local function IsTouchDevice()
+ local touchEnabled = false
+ pcall(function() touchEnabled = Game:GetService('UserInputService').TouchEnabled end)
+ return touchEnabled
+end
+
+local function IsPhone()
+ if gui.AbsoluteSize.Y <= 320 then
+ return true
+ end
+ return false
+end
+
+waitForChild(game,"Players")
+waitForProperty(game.Players,"LocalPlayer")
+local player = game.Players.LocalPlayer
+
+-- First up is the current loadout
+local CurrentLoadout = Instance.new("Frame")
+CurrentLoadout.Name = "CurrentLoadout"
+CurrentLoadout.Position = UDim2.new(0.5, -300, 1, -85)
+CurrentLoadout.Size = UDim2.new(0, 600, 0, 54)
+CurrentLoadout.BackgroundTransparency = 1
+CurrentLoadout.RobloxLocked = true
+CurrentLoadout.Parent = gui
+
+local CLBackground = Instance.new('ImageLabel')
+CLBackground.Name = 'Background';
+CLBackground.Size = UDim2.new(1.2, 0, 1.2, 0);
+CLBackground.Image = "http://www.syntax.eco/asset/?id=96536002"
+CLBackground.BackgroundTransparency = 1.0;
+CLBackground.Position = UDim2.new(-0.1, 0, -0.1, 0);
+CLBackground.ZIndex = 0.0;
+CLBackground.Parent = CurrentLoadout
+CLBackground.Visible = false
+
+local BackgroundUp = Instance.new('ImageLabel')
+BackgroundUp.Size = UDim2.new(1, 0, 0.025, 1)
+BackgroundUp.Position = UDim2.new(0, 0, 0, 0)
+BackgroundUp.Image = 'http://www.syntax.eco/asset/?id=97662207'
+BackgroundUp.BackgroundTransparency = 1.0
+BackgroundUp.Parent = CLBackground
+
+local Debounce = Instance.new("BoolValue")
+Debounce.Name = "Debounce"
+Debounce.RobloxLocked = true
+Debounce.Parent = CurrentLoadout
+
+local BackpackButton = Instance.new("ImageButton")
+BackpackButton.RobloxLocked = true
+BackpackButton.Visible = false
+BackpackButton.Name = "BackpackButton"
+BackpackButton.BackgroundTransparency = 1
+BackpackButton.Image = "http://www.syntax.eco/asset/?id=97617958"
+BackpackButton.Position = UDim2.new(0.5, -60, 1, -108)
+BackpackButton.Size = UDim2.new(0, 120, 0, 18)
+waitForChild(gui,"ControlFrame")
+BackpackButton.Parent = gui.ControlFrame
+
+local NumSlots = 9
+
+if IsPhone() then
+ NumSlots = 3
+ CurrentLoadout.Size = UDim2.new(0,180,0,54)
+ CurrentLoadout.Position = UDim2.new(0.5,-90,1,-85)
+end
+
+for i = 0, NumSlots do
+ local slotFrame = Instance.new("Frame")
+ slotFrame.RobloxLocked = true
+ slotFrame.BackgroundColor3 = Color3.new(0,0,0)
+ slotFrame.BackgroundTransparency = 1
+ slotFrame.BorderColor3 = Color3.new(1, 1, 1)
+ slotFrame.Name = "Slot" .. tostring(i)
+ slotFrame.ZIndex = 4.0
+ if i == 0 then
+ slotFrame.Position = UDim2.new(0.9, 0, 0, 0)
+ else
+ slotFrame.Position = UDim2.new((i - 1) * 0.1, (i-1)* 6,0,0)
+ end
+
+
+ slotFrame.Size = UDim2.new(0, 54, 1, 0)
+ slotFrame.Parent = CurrentLoadout
+
+ if gui.AbsoluteSize.Y <= 320 then
+ slotFrame.Position = UDim2.new(0, (i-1)* 60, 0, -50)
+ print('Well got here', slotFrame, slotFrame.Position.X.Scale, slotFrame.Position.X.Offset)
+ end
+ if gui.AbsoluteSize.Y <= 320 and i == 0 then
+ slotFrame:Destroy()
+ end
+end
+
+local TempSlot = Instance.new("ImageButton")
+TempSlot.Name = "TempSlot"
+TempSlot.Active = true
+TempSlot.Size = UDim2.new(1,0,1,0)
+TempSlot.BackgroundTransparency = 1.0
+TempSlot.Style = 'Custom'
+TempSlot.Visible = false
+TempSlot.RobloxLocked = true
+TempSlot.Parent = CurrentLoadout
+TempSlot.ZIndex = 3.0
+
+ local slotBackground = Instance.new('ImageLabel')
+ slotBackground.Name = 'Background'
+ slotBackground.BackgroundTransparency = 1.0
+ slotBackground.Image = 'http://www.syntax.eco/asset/?id=97613075'
+ slotBackground.Size = UDim2.new(1, 0, 1, 0)
+ slotBackground.Parent = TempSlot
+
+ local HighLight = Instance.new('ImageLabel')
+ HighLight.Name = 'Highlight'
+ HighLight.BackgroundTransparency = 1.0
+ HighLight.Image = 'http://www.syntax.eco/asset/?id=97643886'
+ HighLight.Size = UDim2.new(1, 0, 1, 0)
+ --HighLight.Parent = TempSlot
+ HighLight.Visible = false
+
+ -- TempSlot Children
+ local GearReference = Instance.new("ObjectValue")
+ GearReference.Name = "GearReference"
+ GearReference.RobloxLocked = true
+ GearReference.Parent = TempSlot
+
+
+ local ToolTipLabel = Instance.new("TextLabel")
+ ToolTipLabel.Name = "ToolTipLabel"
+ ToolTipLabel.RobloxLocked = true
+ ToolTipLabel.Text = ""
+ ToolTipLabel.BackgroundTransparency = 0.5
+ ToolTipLabel.BorderSizePixel = 0
+ ToolTipLabel.Visible = false
+ ToolTipLabel.TextColor3 = Color3.new(1,1,1)
+ ToolTipLabel.BackgroundColor3 = Color3.new(0,0,0)
+ ToolTipLabel.TextStrokeTransparency = 0
+ ToolTipLabel.Font = Enum.Font.ArialBold
+ ToolTipLabel.FontSize = Enum.FontSize.Size14
+ --ToolTipLabel.TextWrap = true
+ ToolTipLabel.Size = UDim2.new(1,60,0,20)
+ ToolTipLabel.Position = UDim2.new(0,-30,0,-30)
+ ToolTipLabel.Parent = TempSlot
+
+
+ local Kill = Instance.new("BoolValue")
+ Kill.Name = "Kill"
+ Kill.RobloxLocked = true
+ Kill.Parent = TempSlot
+
+ local GearImage = Instance.new("ImageLabel")
+ GearImage.Name = "GearImage"
+ GearImage.BackgroundTransparency = 1
+ GearImage.Position = UDim2.new(0, 0, 0, 0)
+ GearImage.Size = UDim2.new(1, 0, 1, 0)
+ GearImage.ZIndex = 5.0
+ GearImage.RobloxLocked = true
+ GearImage.Parent = TempSlot
+
+ local SlotNumber = Instance.new("TextLabel")
+ SlotNumber.Name = "SlotNumber"
+ SlotNumber.BackgroundTransparency = 1
+ SlotNumber.BorderSizePixel = 0
+ SlotNumber.Font = Enum.Font.ArialBold
+ SlotNumber.FontSize = Enum.FontSize.Size18
+ SlotNumber.Position = UDim2.new(0, 0, 0, 0)
+ SlotNumber.Size = UDim2.new(0,10,0,15)
+ SlotNumber.TextColor3 = Color3.new(1,1,1)
+ SlotNumber.TextTransparency = 0
+ SlotNumber.TextXAlignment = Enum.TextXAlignment.Left
+ SlotNumber.TextYAlignment = Enum.TextYAlignment.Bottom
+ SlotNumber.RobloxLocked = true
+ SlotNumber.Parent = TempSlot
+ SlotNumber.ZIndex = 5
+
+ if IsTouchDevice() then
+ SlotNumber.Visible = false
+ end
+
+ local SlotNumberDownShadow = SlotNumber:Clone()
+ SlotNumberDownShadow.Name = "SlotNumberDownShadow"
+ SlotNumberDownShadow.TextColor3 = Color3.new(0,0,0)
+ SlotNumberDownShadow.Position = UDim2.new(0, 1, 0, -1)
+ SlotNumberDownShadow.Parent = TempSlot
+ SlotNumberDownShadow.ZIndex = 2
+
+ local SlotNumberUpShadow = SlotNumberDownShadow:Clone()
+ SlotNumberUpShadow.Name = "SlotNumberUpShadow"
+ SlotNumberUpShadow.Position = UDim2.new(0, -1, 0, -1)
+ SlotNumberUpShadow.Parent = TempSlot
+
+ local GearText = Instance.new("TextLabel")
+ GearText.RobloxLocked = true
+ GearText.Name = "GearText"
+ GearText.BackgroundTransparency = 1
+ GearText.Font = Enum.Font.Arial
+ GearText.FontSize = Enum.FontSize.Size14
+ GearText.Position = UDim2.new(0,-8,0,-8)
+ GearText.Size = UDim2.new(1,16,1,16)
+ GearText.Text = ""
+ GearText.TextColor3 = Color3.new(1,1,1)
+ GearText.TextWrap = true
+ GearText.Parent = TempSlot
+ GearText.ZIndex = 5.0
+
+--- Great, now lets make the inventory!
+
+local Backpack = Instance.new("Frame")
+Backpack.RobloxLocked = true
+Backpack.Visible = false
+Backpack.Name = "Backpack"
+Backpack.Position = UDim2.new(0.5, 0, 0.5, 0)
+Backpack.BackgroundColor3 = Color3.new(32/255, 32/255, 32/255)
+Backpack.BackgroundTransparency = 0.0
+Backpack.BorderSizePixel = 0
+Backpack.Parent = gui
+Backpack.Active = true
+
+ -- Backpack Children
+ local SwapSlot = Instance.new("BoolValue")
+ SwapSlot.RobloxLocked = true
+ SwapSlot.Name = "SwapSlot"
+ SwapSlot.Parent = Backpack
+
+ -- SwapSlot Children
+ local Slot = Instance.new("IntValue")
+ Slot.RobloxLocked = true
+ Slot.Name = "Slot"
+ Slot.Parent = SwapSlot
+
+ local GearButton = Instance.new("ObjectValue")
+ GearButton.RobloxLocked = true
+ GearButton.Name = "GearButton"
+ GearButton.Parent = SwapSlot
+
+ local Tabs = Instance.new("Frame")
+ Tabs.Name = "Tabs"
+ Tabs.Visible = false
+ Tabs.Active = false
+ Tabs.RobloxLocked = true
+ Tabs.BackgroundColor3 = Color3.new(0,0,0)
+ Tabs.BackgroundTransparency = 0.08
+ Tabs.BorderSizePixel = 0
+ Tabs.Position = UDim2.new(0,0,-0.1,-4)
+ Tabs.Size = UDim2.new(1,0,0.1,4)
+ Tabs.Parent = Backpack
+
+ -- Tabs Children
+
+ local tabLine = Instance.new("Frame")
+ tabLine.RobloxLocked = true
+ tabLine.Name = "TabLine"
+ tabLine.BackgroundColor3 = Color3.new(53/255, 53/255, 53/255)
+ tabLine.BorderSizePixel = 0
+ tabLine.Position = UDim2.new(0,5,1,-4)
+ tabLine.Size = UDim2.new(1,-10,0,4)
+ tabLine.ZIndex = 2
+ tabLine.Parent = Tabs
+
+ local InventoryButton = Instance.new("TextButton")
+ InventoryButton.RobloxLocked = true
+ InventoryButton.Name = "InventoryButton"
+ InventoryButton.Size = UDim2.new(0,60,0,30)
+ InventoryButton.Position = UDim2.new(0,7,1,-31)
+ InventoryButton.BackgroundColor3 = Color3.new(1,1,1)
+ InventoryButton.BorderColor3 = Color3.new(1,1,1)
+ InventoryButton.Font = Enum.Font.ArialBold
+ InventoryButton.FontSize = Enum.FontSize.Size18
+ InventoryButton.Text = "Gear"
+ InventoryButton.AutoButtonColor = false
+ InventoryButton.TextColor3 = Color3.new(0,0,0)
+ InventoryButton.Selected = true
+ InventoryButton.Active = true
+ InventoryButton.ZIndex = 3
+ InventoryButton.Parent = Tabs
+
+ if game.CoreGui.Version >= 8 then
+ local WardrobeButton = Instance.new("TextButton")
+ WardrobeButton.RobloxLocked = true
+ WardrobeButton.Name = "WardrobeButton"
+ WardrobeButton.Size = UDim2.new(0,90,0,30)
+ WardrobeButton.Position = UDim2.new(0,77,1,-31)
+ WardrobeButton.BackgroundColor3 = Color3.new(0,0,0)
+ WardrobeButton.BorderColor3 = Color3.new(1,1,1)
+ WardrobeButton.Font = Enum.Font.ArialBold
+ WardrobeButton.FontSize = Enum.FontSize.Size18
+ WardrobeButton.Text = "Wardrobe"
+ WardrobeButton.AutoButtonColor = false
+ WardrobeButton.TextColor3 = Color3.new(1,1,1)
+ WardrobeButton.Selected = false
+ WardrobeButton.Active = true
+ WardrobeButton.Parent = Tabs
+ end
+
+ local closeButton = Instance.new("TextButton")
+ closeButton.RobloxLocked = true
+ closeButton.Name = "CloseButton"
+ closeButton.Font = Enum.Font.ArialBold
+ closeButton.FontSize = Enum.FontSize.Size24
+ closeButton.Position = UDim2.new(1,-33,0,4)
+ closeButton.Size = UDim2.new(0,30,0,30)
+ closeButton.Style = Enum.ButtonStyle.RobloxButton
+ closeButton.Text = ""
+ closeButton.TextColor3 = Color3.new(1,1,1)
+ closeButton.Parent = Tabs
+ closeButton.Modal = true
+
+ --closeButton child
+ local XImage = Instance.new("ImageLabel")
+ XImage.RobloxLocked = true
+ XImage.Name = "XImage"
+ game:GetService("ContentProvider"):Preload("http://www.syntax.eco/asset/?id=75547445")
+ XImage.Image = "http://www.syntax.eco/asset/?id=75547445" --TODO: move to rbxasset
+ XImage.BackgroundTransparency = 1
+ XImage.Position = UDim2.new(-.25,-1,-.25,-1)
+ XImage.Size = UDim2.new(1.5,2,1.5,2)
+ XImage.ZIndex = 2
+ XImage.Parent = closeButton
+
+ -- Generic Search gui used across backpack
+ local SearchFrame = Instance.new("Frame")
+ SearchFrame.RobloxLocked = true
+ SearchFrame.Name = "SearchFrame"
+ SearchFrame.BackgroundTransparency = 1
+ SearchFrame.Position = UDim2.new(1,-220,0,2)
+ SearchFrame.Size = UDim2.new(0,220,0,24)
+ SearchFrame.Parent = Backpack
+
+ -- SearchFrame Children
+ local SearchButton = Instance.new("ImageButton")
+ SearchButton.RobloxLocked = true
+ SearchButton.Name = "SearchButton"
+ SearchButton.Size = UDim2.new(0,25,0,25)
+ SearchButton.BackgroundTransparency = 1
+ SearchButton.Image = "rbxasset://textures/ui/SearchIcon.png"
+ SearchButton.Parent = SearchFrame
+
+ local SearchBoxFrame = Instance.new("TextButton")
+ SearchBoxFrame.RobloxLocked = true
+ SearchBoxFrame.Position = UDim2.new(0,25,0,0)
+ SearchBoxFrame.Size = UDim2.new(1,-28,0,26)
+ SearchBoxFrame.Name = "SearchBoxFrame"
+ SearchBoxFrame.Text = ""
+ SearchBoxFrame.Style = Enum.ButtonStyle.RobloxButton
+ SearchBoxFrame.Parent = SearchFrame
+
+ -- SearchBoxFrame Children
+ local SearchBox = Instance.new("TextBox")
+ SearchBox.RobloxLocked = true
+ SearchBox.Name = "SearchBox"
+ SearchBox.BackgroundTransparency = 1
+ SearchBox.Font = Enum.Font.ArialBold
+ SearchBox.FontSize = Enum.FontSize.Size12
+ SearchBox.Position = UDim2.new(0,-5,0,-5)
+ SearchBox.Size = UDim2.new(1,10,1,10)
+ SearchBox.TextColor3 = Color3.new(1,1,1)
+ SearchBox.TextXAlignment = Enum.TextXAlignment.Left
+ SearchBox.ZIndex = 2
+ SearchBox.TextWrap = true
+ SearchBox.Text = "Search..."
+ SearchBox.Parent = SearchBoxFrame
+
+
+ local ResetButton = Instance.new("TextButton")
+ ResetButton.RobloxLocked = true
+ ResetButton.Visible = false
+ ResetButton.Name = "ResetButton"
+ ResetButton.Position = UDim2.new(1,-26,0,3)
+ ResetButton.Size = UDim2.new(0,20,0,20)
+ ResetButton.Style = Enum.ButtonStyle.RobloxButtonDefault
+ ResetButton.Text = "X"
+ ResetButton.TextColor3 = Color3.new(1,1,1)
+ ResetButton.Font = Enum.Font.ArialBold
+ ResetButton.FontSize = Enum.FontSize.Size18
+ ResetButton.ZIndex = 3
+ ResetButton.Parent = SearchFrame
+
+------------------------------- GEAR -------------------------------------------------------
+ local Gear = Instance.new("Frame")
+ Gear.Name = "Gear"
+ Gear.RobloxLocked = true
+ Gear.BackgroundTransparency = 1
+ Gear.Size = UDim2.new(1,0,1,0)
+ Gear.ClipsDescendants = true
+ Gear.Parent = Backpack
+
+ -- Gear Children
+ local AssetsList = Instance.new("Frame")
+ AssetsList.RobloxLocked = true
+ AssetsList.Name = "AssetsList"
+ AssetsList.BackgroundTransparency = 1
+ AssetsList.Size = UDim2.new(0.2,0,1,0)
+ AssetsList.Style = Enum.FrameStyle.RobloxSquare
+ AssetsList.Visible = false
+ AssetsList.Parent = Gear
+
+ local GearGrid = Instance.new("Frame")
+ GearGrid.RobloxLocked = true
+ GearGrid.Name = "GearGrid"
+ GearGrid.Size = UDim2.new(0.95, 0, 1, 0)
+ GearGrid.BackgroundTransparency = 1
+ GearGrid.Parent = Gear
+
+
+ local GearButton = Instance.new("ImageButton")
+ GearButton.RobloxLocked = true
+ GearButton.Visible = false
+ GearButton.Name = "GearButton"
+ GearButton.Size = UDim2.new(0, 54, 0, 54)
+ GearButton.Style = 'Custom'
+ GearButton.Parent = GearGrid
+ GearButton.BackgroundTransparency = 1.0
+
+ local slotBackground = Instance.new('ImageLabel')
+ slotBackground.Name = 'Background'
+ slotBackground.BackgroundTransparency = 1.0
+ slotBackground.Image = 'http://www.syntax.eco/asset/?id=97613075'
+ slotBackground.Size = UDim2.new(1, 0, 1, 0)
+ slotBackground.Parent = GearButton
+
+
+ -- GearButton Children
+ local GearReference = Instance.new("ObjectValue")
+ GearReference.RobloxLocked = true
+ GearReference.Name = "GearReference"
+ GearReference.Parent = GearButton
+
+ local GreyOutButton = Instance.new("Frame")
+ GreyOutButton.RobloxLocked = true
+ GreyOutButton.Name = "GreyOutButton"
+ GreyOutButton.BackgroundTransparency = 0.5
+ GreyOutButton.Size = UDim2.new(1,0,1,0)
+ GreyOutButton.Active = true
+ GreyOutButton.Visible = false
+ GreyOutButton.ZIndex = 3
+ GreyOutButton.Parent = GearButton
+
+ local GearText = Instance.new("TextLabel")
+ GearText.RobloxLocked = true
+ GearText.Name = "GearText"
+ GearText.BackgroundTransparency = 1
+ GearText.Font = Enum.Font.Arial
+ GearText.FontSize = Enum.FontSize.Size14
+ GearText.Position = UDim2.new(0,-8,0,-8)
+ GearText.Size = UDim2.new(1,16,1,16)
+ GearText.Text = ""
+ GearText.ZIndex = 2
+ GearText.TextColor3 = Color3.new(1,1,1)
+ GearText.TextWrap = true
+ GearText.Parent = GearButton
+
+ local GearGridScrollingArea = Instance.new("Frame")
+ GearGridScrollingArea.RobloxLocked = true
+ GearGridScrollingArea.Name = "GearGridScrollingArea"
+ GearGridScrollingArea.Position = UDim2.new(1, -19, 0, 35)
+ GearGridScrollingArea.Size = UDim2.new(0, 17, 1, -45)
+ GearGridScrollingArea.BackgroundTransparency = 1
+ GearGridScrollingArea.Parent = Gear
+
+ local GearLoadouts = Instance.new("Frame")
+ GearLoadouts.RobloxLocked = true
+ GearLoadouts.Name = "GearLoadouts"
+ GearLoadouts.BackgroundTransparency = 1
+ GearLoadouts.Position = UDim2.new(0.7,23,0.5,1)
+ GearLoadouts.Size = UDim2.new(0.3,-23,0.5,-1)
+ GearLoadouts.Parent = Gear
+ GearLoadouts.Visible = false
+
+ -- GearLoadouts Children
+ local GearLoadoutsHeader = Instance.new("Frame")
+ GearLoadoutsHeader.RobloxLocked = true
+ GearLoadoutsHeader.Name = "GearLoadoutsHeader"
+ GearLoadoutsHeader.BackgroundColor3 = Color3.new(0,0,0)
+ GearLoadoutsHeader.BackgroundTransparency = 0.2
+ GearLoadoutsHeader.BorderColor3 = Color3.new(1,0,0)
+ GearLoadoutsHeader.Size = UDim2.new(1,2,0.15,-1)
+ GearLoadoutsHeader.Parent = GearLoadouts
+
+ -- GearLoadoutsHeader Children
+ local LoadoutsHeaderText = Instance.new("TextLabel")
+ LoadoutsHeaderText.RobloxLocked = true
+ LoadoutsHeaderText.Name = "LoadoutsHeaderText"
+ LoadoutsHeaderText.BackgroundTransparency = 1
+ LoadoutsHeaderText.Font = Enum.Font.ArialBold
+ LoadoutsHeaderText.FontSize = Enum.FontSize.Size18
+ LoadoutsHeaderText.Size = UDim2.new(1,0,1,0)
+ LoadoutsHeaderText.Text = "Loadouts"
+ LoadoutsHeaderText.TextColor3 = Color3.new(1,1,1)
+ LoadoutsHeaderText.Parent = GearLoadoutsHeader
+
+ local GearLoadoutsScrollingArea = GearGridScrollingArea:clone()
+ GearLoadoutsScrollingArea.RobloxLocked = true
+ GearLoadoutsScrollingArea.Name = "GearLoadoutsScrollingArea"
+ GearLoadoutsScrollingArea.Position = UDim2.new(1,-15,0.15,2)
+ GearLoadoutsScrollingArea.Size = UDim2.new(0,17,0.85,-2)
+ GearLoadoutsScrollingArea.Parent = GearLoadouts
+
+ local LoadoutsList = Instance.new("Frame")
+ LoadoutsList.RobloxLocked = true
+ LoadoutsList.Name = "LoadoutsList"
+ LoadoutsList.Position = UDim2.new(0,0,0.15,2)
+ LoadoutsList.Size = UDim2.new(1,-17,0.85,-2)
+ LoadoutsList.Style = Enum.FrameStyle.RobloxSquare
+ LoadoutsList.Parent = GearLoadouts
+
+ local GearPreview = Instance.new("Frame")
+ GearPreview.RobloxLocked = true
+ GearPreview.Name = "GearPreview"
+ GearPreview.Position = UDim2.new(0.7,23,0,0)
+ GearPreview.Size = UDim2.new(0.3,-28,0.5,-1)
+ GearPreview.BackgroundTransparency = 1
+ GearPreview.ZIndex = 7
+ GearPreview.Parent = Gear
+
+ -- GearPreview Children
+ local GearStats = Instance.new("Frame")
+ GearStats.RobloxLocked = true
+ GearStats.Name = "GearStats"
+ GearStats.BackgroundTransparency = 1
+ GearStats.Position = UDim2.new(0,0,0.75,0)
+ GearStats.Size = UDim2.new(1,0,0.25,0)
+ GearStats.ZIndex = 8
+ GearStats.Parent = GearPreview
+
+ -- GearStats Children
+ local GearName = Instance.new("TextLabel")
+ GearName.RobloxLocked = true
+ GearName.Name = "GearName"
+ GearName.BackgroundTransparency = 1
+ GearName.Font = Enum.Font.ArialBold
+ GearName.FontSize = Enum.FontSize.Size18
+ GearName.Position = UDim2.new(0,-3,0,0)
+ GearName.Size = UDim2.new(1,6,1,5)
+ GearName.Text = ""
+ GearName.TextColor3 = Color3.new(1,1,1)
+ GearName.TextWrap = true
+ GearName.ZIndex = 9
+ GearName.Parent = GearStats
+
+ local GearImage = Instance.new("ImageLabel")
+ GearImage.RobloxLocked = true
+ GearImage.Name = "GearImage"
+ GearImage.Image = ""
+ GearImage.BackgroundTransparency = 1
+ GearImage.Position = UDim2.new(0.125,0,0,0)
+ GearImage.Size = UDim2.new(0.75,0,0.75,0)
+ GearImage.ZIndex = 8
+ GearImage.Parent = GearPreview
+
+ --GearImage Children
+ local GearIcons = Instance.new("Frame")
+ GearIcons.BackgroundColor3 = Color3.new(0,0,0)
+ GearIcons.BackgroundTransparency = 0.5
+ GearIcons.BorderSizePixel = 0
+ GearIcons.RobloxLocked = true
+ GearIcons.Name = "GearIcons"
+ GearIcons.Position = UDim2.new(0.4,2,0.85,-2)
+ GearIcons.Size = UDim2.new(0.6,0,0.15,0)
+ GearIcons.Visible = false
+ GearIcons.ZIndex = 9
+ GearIcons.Parent = GearImage
+
+ -- GearIcons Children
+ local GenreImage = Instance.new("ImageLabel")
+ GenreImage.RobloxLocked = true
+ GenreImage.Name = "GenreImage"
+ GenreImage.BackgroundColor3 = Color3.new(102/255,153/255,1)
+ GenreImage.BackgroundTransparency = 0.5
+ GenreImage.BorderSizePixel = 0
+ GenreImage.Size = UDim2.new(0.25,0,1,0)
+ GenreImage.Parent = GearIcons
+
+ local AttributeOneImage = GenreImage:clone()
+ AttributeOneImage.RobloxLocked = true
+ AttributeOneImage.Name = "AttributeOneImage"
+ AttributeOneImage.BackgroundColor3 = Color3.new(1,51/255,0)
+ AttributeOneImage.Position = UDim2.new(0.25,0,0,0)
+ AttributeOneImage.Parent = GearIcons
+
+ local AttributeTwoImage = GenreImage:clone()
+ AttributeTwoImage.RobloxLocked = true
+ AttributeTwoImage.Name = "AttributeTwoImage"
+ AttributeTwoImage.BackgroundColor3 = Color3.new(153/255,1,153/255)
+ AttributeTwoImage.Position = UDim2.new(0.5,0,0,0)
+ AttributeTwoImage.Parent = GearIcons
+
+ local AttributeThreeImage = GenreImage:clone()
+ AttributeThreeImage.RobloxLocked = true
+ AttributeThreeImage.Name = "AttributeThreeImage"
+ AttributeThreeImage.BackgroundColor3 = Color3.new(0,0.5,0.5)
+ AttributeThreeImage.Position = UDim2.new(0.75,0,0,0)
+ AttributeThreeImage.Parent = GearIcons
+
+------------------------------- WARDROBE -------------------------------------------------------
+ if game.CoreGui.Version < 8 then
+ -- no need for this to stick around, we aren't ready for wardrobe
+ script:remove()
+ return
+ end
+
+ local function makeCharFrame(frameName, parent)
+ local frame = Instance.new("Frame")
+ frame.RobloxLocked = true
+ frame.Size = UDim2.new(1,0,1,-70)
+ frame.Position = UDim2.new(0,0,0,20)
+ frame.Name = frameName
+ frame.BackgroundTransparency = 1
+ frame.Parent = parent
+ frame.Visible = false
+ return frame
+ end
+ local function makeZone( zoneName, image, size, position, parent )
+ local zone = Instance.new("ImageLabel")
+ zone.RobloxLocked = true
+ zone.Name = zoneName
+ zone.Image = image
+ zone.Size = size
+ zone.BackgroundTransparency = 1
+ zone.Position = position
+ zone.Parent = parent
+ return zone
+ end
+ local function makeStyledButton( buttonName, size, position, parent, buttonStyle )
+ local button = Instance.new("ImageButton")
+ button.RobloxLocked = true
+ button.Name = buttonName
+ button.Size = size
+ button.Position = position
+ if buttonStyle then
+ button.Style = buttonStyle
+ else
+ button.BackgroundColor3 = Color3.new(0,0,0)
+ button.BorderColor3 = Color3.new(1,1,1)
+ end
+ button.Parent = parent
+ return button
+ end
+ local function makeTextLabel( TextLabelName,text,position,parent )
+ local label = Instance.new("TextLabel")
+ label.RobloxLocked = true
+ label.BackgroundTransparency = 1
+ label.Size = UDim2.new(0,32,0,14)
+ label.Name = TextLabelName
+ label.Font = Enum.Font.Arial
+ label.TextColor3 = Color3.new(1,1,1)
+ label.FontSize = Enum.FontSize.Size14
+ label.Text = text
+ label.Position = position
+ label.Parent = parent
+ end
+
+
+ local Wardrobe = Instance.new("Frame")
+ Wardrobe.Name = "Wardrobe"
+ Wardrobe.RobloxLocked = true
+ Wardrobe.BackgroundTransparency = 1
+ Wardrobe.Visible = false
+ Wardrobe.Size = UDim2.new(1,0,1,0)
+ Wardrobe.Parent = Backpack
+
+ local AssetList = Instance.new("Frame")
+ AssetList.RobloxLocked = true
+ AssetList.Name = "AssetList"
+ AssetList.Position = UDim2.new(0,4,0,5)
+ AssetList.Size = UDim2.new(0,85,1,-5)
+ AssetList.BackgroundTransparency = 1
+ AssetList.Visible = true
+ AssetList.Parent = Wardrobe
+
+ local PreviewAssetFrame = Instance.new("Frame")
+ PreviewAssetFrame.RobloxLocked = true
+ PreviewAssetFrame.Name = "PreviewAssetFrame"
+ PreviewAssetFrame.BackgroundTransparency = 1
+ PreviewAssetFrame.Position = UDim2.new(1,-240,0,30)
+ PreviewAssetFrame.Size = UDim2.new(0,250,0,250)
+ PreviewAssetFrame.Parent = Wardrobe
+
+ local PreviewAssetBacking = Instance.new("TextButton")
+ PreviewAssetBacking.RobloxLocked = true
+ PreviewAssetBacking.Name = "PreviewAssetBacking"
+ PreviewAssetBacking.Active = false
+ PreviewAssetBacking.Text = ""
+ PreviewAssetBacking.AutoButtonColor = false
+ PreviewAssetBacking.Size = UDim2.new(1,0,1,0)
+ PreviewAssetBacking.Style = Enum.ButtonStyle.RobloxButton
+ PreviewAssetBacking.Visible = false
+ PreviewAssetBacking.ZIndex = 9
+ PreviewAssetBacking.Parent = PreviewAssetFrame
+
+ local PreviewAssetImage = Instance.new("ImageLabel")
+ PreviewAssetImage.RobloxLocked = true
+ PreviewAssetImage.Name = "PreviewAssetImage"
+ PreviewAssetImage.BackgroundTransparency = 0.8
+ PreviewAssetImage.Position = UDim2.new(0.5,-100,0,0)
+ PreviewAssetImage.Size = UDim2.new(0,200,0,200)
+ PreviewAssetImage.BorderSizePixel = 0
+ PreviewAssetImage.ZIndex = 10
+ PreviewAssetImage.Parent = PreviewAssetBacking
+
+ local AssetNameLabel = Instance.new("TextLabel")
+ AssetNameLabel.Name = "AssetNameLabel"
+ AssetNameLabel.RobloxLocked = true
+ AssetNameLabel.BackgroundTransparency = 1
+ AssetNameLabel.Position = UDim2.new(0,0,1,-20)
+ AssetNameLabel.Size = UDim2.new(0.5,0,0,24)
+ AssetNameLabel.ZIndex = 10
+ AssetNameLabel.Font = Enum.Font.Arial
+ AssetNameLabel.Text = ""
+ AssetNameLabel.TextColor3 = Color3.new(1,1,1)
+ AssetNameLabel.TextScaled = true
+ AssetNameLabel.Parent = PreviewAssetBacking
+
+ local AssetTypeLabel = AssetNameLabel:clone()
+ AssetTypeLabel.RobloxLocked = true
+ AssetTypeLabel.Name = "AssetTypeLabel"
+ AssetTypeLabel.TextScaled = false
+ AssetTypeLabel.FontSize = Enum.FontSize.Size18
+ AssetTypeLabel.Position = UDim2.new(0.5,3,1,-20)
+ AssetTypeLabel.Parent = PreviewAssetBacking
+
+
+
+ local PreviewButton = Instance.new("TextButton")
+ PreviewButton.RobloxLocked = true
+ PreviewButton.Name = "PreviewButton"
+ PreviewButton.Text = "Rotate"
+ PreviewButton.BackgroundColor3 = Color3.new(0,0,0)
+ PreviewButton.BackgroundTransparency = 0.5
+ PreviewButton.BorderColor3 = Color3.new(1,1,1)
+ PreviewButton.Position = UDim2.new(1.2,-62,1,-50)
+ PreviewButton.Size = UDim2.new(0,125,0,50)
+ PreviewButton.Font = Enum.Font.ArialBold
+ PreviewButton.FontSize = Enum.FontSize.Size24
+ PreviewButton.TextColor3 = Color3.new(1,1,1)
+ PreviewButton.TextWrapped = true
+ PreviewButton.TextStrokeTransparency = 0
+ PreviewButton.Parent = Wardrobe
+
+ local CharacterPane = Instance.new("Frame")
+ CharacterPane.RobloxLocked = true
+ CharacterPane.Name = "CharacterPane"
+ CharacterPane.Position = UDim2.new(1,-220,0,32)
+ CharacterPane.Size = UDim2.new(0,220,1,-40)
+ CharacterPane.BackgroundTransparency = 1
+ CharacterPane.Visible = true
+ CharacterPane.Parent = Wardrobe
+
+ --CharacterPane Children
+ local FaceFrame = makeCharFrame("FacesFrame", CharacterPane)
+ game:GetService("ContentProvider"):Preload("http://www.syntax.eco/asset/?id=75460621")
+ makeZone("FaceZone","http://www.syntax.eco/asset/?id=75460621",UDim2.new(0,157,0,137),UDim2.new(0.5,-78,0.5,-68),FaceFrame)
+ makeStyledButton("Face",UDim2.new(0,64,0,64),UDim2.new(0.5,-32,0.5,-135),FaceFrame)
+
+ local HeadFrame = makeCharFrame("HeadsFrame", CharacterPane)
+ makeZone("FaceZone","http://www.syntax.eco/asset/?id=75460621",UDim2.new(0,157,0,137),UDim2.new(0.5,-78,0.5,-68),HeadFrame)
+ makeStyledButton("Head",UDim2.new(0,64,0,64),UDim2.new(0.5,-32,0.5,-135),HeadFrame)
+
+ local HatsFrame = makeCharFrame("HatsFrame", CharacterPane)
+ game:GetService("ContentProvider"):Preload("http://www.syntax.eco/asset/?id=75457888")
+ local HatsZone = makeZone("HatsZone","http://www.syntax.eco/asset/?id=75457888",UDim2.new(0,186,0,184),UDim2.new(0.5,-93,0.5,-100), HatsFrame)
+ makeStyledButton("Hat1Button",UDim2.new(0,64,0,64),UDim2.new(0,-1,0,-1),HatsZone,Enum.ButtonStyle.RobloxButton)
+ makeStyledButton("Hat2Button",UDim2.new(0,64,0,64),UDim2.new(0,63,0,-1),HatsZone,Enum.ButtonStyle.RobloxButton)
+ makeStyledButton("Hat3Button",UDim2.new(0,64,0,64),UDim2.new(0,127,0,-1),HatsZone,Enum.ButtonStyle.RobloxButton)
+
+ local PantsFrame = makeCharFrame("PantsFrame", CharacterPane)
+ game:GetService("ContentProvider"):Preload("http://www.syntax.eco/asset/?id=75457920")
+ makeZone("PantsZone","http://www.syntax.eco/asset/?id=75457920",UDim2.new(0,121,0,99),UDim2.new(0.5,-60,0.5,-100),PantsFrame)
+
+ local pantFrame = Instance.new("Frame")
+ pantFrame.RobloxLocked = true
+ pantFrame.Size = UDim2.new(0,25,0,56)
+ pantFrame.Position = UDim2.new(0.5,-26,0.5,0)
+ pantFrame.BackgroundColor3 = Color3.new(0,0,0)
+ pantFrame.BorderColor3 = Color3.new(1,1,1)
+ pantFrame.Name = "PantFrame"
+ pantFrame.Parent = PantsFrame
+
+ local otherPantFrame = pantFrame:clone()
+ otherPantFrame.Position = UDim2.new(0.5,3,0.5,0)
+ otherPantFrame.RobloxLocked = true
+ otherPantFrame.Parent = PantsFrame
+
+ local CurrentPants = Instance.new("ImageButton")
+ CurrentPants.RobloxLocked = true
+ CurrentPants.BackgroundTransparency = 1
+ CurrentPants.ZIndex = 2
+ CurrentPants.Name = "CurrentPants"
+ CurrentPants.Position = UDim2.new(0.5,-31,0.5,-4)
+ CurrentPants.Size = UDim2.new(0,54,0,59)
+ CurrentPants.Parent = PantsFrame
+
+ local MeshFrame = makeCharFrame("PackagesFrame", CharacterPane)
+ local torsoButton = makeStyledButton("TorsoMeshButton", UDim2.new(0,64,0,64),UDim2.new(0.5,-32,0.5,-110),MeshFrame,Enum.ButtonStyle.RobloxButton)
+ makeTextLabel("TorsoLabel","Torso",UDim2.new(0.5,-16,0,-25),torsoButton)
+ local leftLegButton = makeStyledButton("LeftLegMeshButton", UDim2.new(0,64,0,64),UDim2.new(0.5,0,0.5,-25),MeshFrame,Enum.ButtonStyle.RobloxButton)
+ makeTextLabel("LeftLegLabel","Left Leg",UDim2.new(0.5,-16,0,-25),leftLegButton)
+ local rightLegButton = makeStyledButton("RightLegMeshButton", UDim2.new(0,64,0,64),UDim2.new(0.5,-64,0.5,-25),MeshFrame,Enum.ButtonStyle.RobloxButton)
+ makeTextLabel("RightLegLabel","Right Leg",UDim2.new(0.5,-16,0,-25),rightLegButton)
+ local rightArmButton = makeStyledButton("RightArmMeshButton", UDim2.new(0,64,0,64),UDim2.new(0.5,-96,0.5,-110),MeshFrame,Enum.ButtonStyle.RobloxButton)
+ makeTextLabel("RightArmLabel","Right Arm",UDim2.new(0.5,-16,0,-25),rightArmButton)
+ local leftArmButton = makeStyledButton("LeftArmMeshButton", UDim2.new(0,64,0,64),UDim2.new(0.5,32,0.5,-110),MeshFrame,Enum.ButtonStyle.RobloxButton)
+ makeTextLabel("LeftArmLabel","Left Arm",UDim2.new(0.5,-16,0,-25),leftArmButton)
+
+ local TShirtFrame = makeCharFrame("T-ShirtsFrame",CharacterPane)
+ game:GetService("ContentProvider"):Preload("http://www.syntax.eco/asset/?id=75460642")
+ makeZone("TShirtZone","http://www.syntax.eco/asset/?id=75460642",UDim2.new(0,121,0,154),UDim2.new(0.5,-60,0.5,-100),TShirtFrame)
+ makeStyledButton("TShirtButton", UDim2.new(0,64,0,64),UDim2.new(0.5,-32,0.5,-64),TShirtFrame)
+
+
+ local ShirtFrame = makeCharFrame("ShirtsFrame", CharacterPane)
+ makeZone("ShirtZone","http://www.syntax.eco/asset/?id=75460642",UDim2.new(0,121,0,154),UDim2.new(0.5,-60,0.5,-100),ShirtFrame)
+ makeStyledButton("ShirtButton", UDim2.new(0,64,0,64),UDim2.new(0.5,-32,0.5,-64),ShirtFrame)
+
+
+ local ColorFrame = makeCharFrame("ColorFrame", CharacterPane)
+ game:GetService("ContentProvider"):Preload("http://www.syntax.eco/asset/?id=76049888")
+ local ColorZone = makeZone("ColorZone","http://www.syntax.eco/asset/?id=76049888", UDim2.new(0,120,0,150),UDim2.new(0.5,-60,0.5,-100),ColorFrame)
+ makeStyledButton("Head",UDim2.new(0.26,0,0.19,0),UDim2.new(0.37,0,0.02,0),ColorZone).AutoButtonColor = false
+ makeStyledButton("LeftArm",UDim2.new(0.19,0,0.36,0),UDim2.new(0.78,0,0.26,0),ColorZone).AutoButtonColor = false
+ makeStyledButton("RightArm",UDim2.new(0.19,0,0.36,0),UDim2.new(0.025,0,0.26,0),ColorZone).AutoButtonColor = false
+ makeStyledButton("Torso",UDim2.new(0.43,0,0.36,0),UDim2.new(0.28,0,0.26,0),ColorZone).AutoButtonColor = false
+ makeStyledButton("RightLeg",UDim2.new(0.19,0,0.31,0),UDim2.new(0.275,0,0.67,0),ColorZone).AutoButtonColor = false
+ makeStyledButton("LeftLeg",UDim2.new(0.19,0,0.31,0),UDim2.new(0.525,0,0.67,0),ColorZone).AutoButtonColor = false
+
+ -- Character Panel label (shows what category we are currently browsing)
+ local CategoryLabel = Instance.new("TextLabel")
+ CategoryLabel.RobloxLocked = true
+ CategoryLabel.Name = "CategoryLabel"
+ CategoryLabel.BackgroundTransparency = 1
+ CategoryLabel.Font = Enum.Font.ArialBold
+ CategoryLabel.FontSize = Enum.FontSize.Size18
+ CategoryLabel.Position = UDim2.new(0,0,0,-7)
+ CategoryLabel.Size = UDim2.new(1,0,0,20)
+ CategoryLabel.TextXAlignment = Enum.TextXAlignment.Center
+ CategoryLabel.Text = "All"
+ CategoryLabel.TextColor3 = Color3.new(1,1,1)
+ CategoryLabel.Parent = CharacterPane
+
+ --Save Button
+ local SaveButton = Instance.new("TextButton")
+ SaveButton.RobloxLocked = true
+ SaveButton.Name = "SaveButton"
+ SaveButton.Size = UDim2.new(0.6,0,0,50)
+ SaveButton.Position = UDim2.new(0.2,0,1,-50)
+ SaveButton.Style = Enum.ButtonStyle.RobloxButton
+ SaveButton.Selected = false
+ SaveButton.Font = Enum.Font.ArialBold
+ SaveButton.FontSize = Enum.FontSize.Size18
+ SaveButton.Text = "Save"
+ SaveButton.TextColor3 = Color3.new(1,1,1)
+ SaveButton.Parent = CharacterPane
+
+-- no need for this to stick around
+
+script:Destroy()
\ No newline at end of file
diff --git a/app/files/CoreGui/53878048 b/app/files/CoreGui/53878048
new file mode 100644
index 0000000..4d3e7db
--- /dev/null
+++ b/app/files/CoreGui/53878048
@@ -0,0 +1,790 @@
+-- This script creates almost all gui elements found in the backpack (warning: there are a lot!)
+-- TODO: automate this process
+
+if game.CoreGui.Version < 3 then return end -- peace out if we aren't using the right client
+
+local gui = script.Parent
+
+-- A couple of necessary functions
+local function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+local function waitForProperty(instance, property)
+ while not instance[property] do
+ instance.Changed:wait()
+ end
+end
+
+waitForChild(game,"Players")
+waitForProperty(game.Players,"LocalPlayer")
+local player = game.Players.LocalPlayer
+
+-- First up is the current loadout
+local CurrentLoadout = Instance.new("Frame")
+CurrentLoadout.Name = "CurrentLoadout"
+CurrentLoadout.Position = UDim2.new(0.5, -240, 1, -85)
+CurrentLoadout.Size = UDim2.new(0, 480, 0, 48)
+CurrentLoadout.BackgroundTransparency = 1
+CurrentLoadout.RobloxLocked = true
+CurrentLoadout.Parent = gui
+
+local Debounce = Instance.new("BoolValue")
+Debounce.Name = "Debounce"
+Debounce.RobloxLocked = true
+Debounce.Parent = CurrentLoadout
+
+local BackpackButton = Instance.new("ImageButton")
+BackpackButton.RobloxLocked = true
+BackpackButton.Visible = false
+BackpackButton.Name = "BackpackButton"
+BackpackButton.BackgroundTransparency = 1
+BackpackButton.Image = "rbxasset://textures/ui/backpackButton.png"
+BackpackButton.Position = UDim2.new(0.5, -195, 1, -30)
+BackpackButton.Size = UDim2.new(0,107,0,26)
+waitForChild(gui,"ControlFrame")
+BackpackButton.Parent = gui.ControlFrame
+
+for i = 0, 9 do
+ local slotFrame = Instance.new("Frame")
+ slotFrame.RobloxLocked = true
+ slotFrame.BackgroundColor3 = Color3.new(0,0,0)
+ slotFrame.BackgroundTransparency = 1
+ slotFrame.BorderColor3 = Color3.new(1,1,1)
+ slotFrame.Name = "Slot" .. tostring(i)
+ if i == 0 then
+ slotFrame.Position = UDim2.new(0.9,0,0,0)
+ else
+ slotFrame.Position = UDim2.new((i - 1) * 0.1,0,0,0)
+ end
+ slotFrame.Size = UDim2.new(0.1,0,1,0)
+ slotFrame.Parent = CurrentLoadout
+end
+
+local TempSlot = Instance.new("ImageButton")
+TempSlot.Name = "TempSlot"
+TempSlot.Active = true
+TempSlot.Size = UDim2.new(1,0,1,0)
+TempSlot.Style = Enum.ButtonStyle.RobloxButton
+TempSlot.Visible = false
+TempSlot.RobloxLocked = true
+TempSlot.Parent = CurrentLoadout
+
+ -- TempSlot Children
+ local GearReference = Instance.new("ObjectValue")
+ GearReference.Name = "GearReference"
+ GearReference.RobloxLocked = true
+ GearReference.Parent = TempSlot
+
+ local ToolTipLabel = Instance.new("TextLabel")
+ ToolTipLabel.Name = "ToolTipLabel"
+ ToolTipLabel.RobloxLocked = true
+ ToolTipLabel.Text = ""
+ ToolTipLabel.BackgroundTransparency = 0.5
+ ToolTipLabel.BorderSizePixel = 0
+ ToolTipLabel.Visible = false
+ ToolTipLabel.TextColor3 = Color3.new(1,1,1)
+ ToolTipLabel.BackgroundColor3 = Color3.new(0,0,0)
+ ToolTipLabel.TextStrokeTransparency = 0
+ ToolTipLabel.Font = Enum.Font.ArialBold
+ ToolTipLabel.FontSize = Enum.FontSize.Size14
+ --ToolTipLabel.TextWrap = true
+ ToolTipLabel.Size = UDim2.new(1,60,0,20)
+ ToolTipLabel.Position = UDim2.new(0,-30,0,-30)
+ ToolTipLabel.Parent = TempSlot
+
+ local Kill = Instance.new("BoolValue")
+ Kill.Name = "Kill"
+ Kill.RobloxLocked = true
+ Kill.Parent = TempSlot
+
+ local GearImage = Instance.new("ImageLabel")
+ GearImage.Name = "GearImage"
+ GearImage.BackgroundTransparency = 1
+ GearImage.Position = UDim2.new(0,-7,0,-7)
+ GearImage.Size = UDim2.new(1,14,1,14)
+ GearImage.ZIndex = 2
+ GearImage.RobloxLocked = true
+ GearImage.Parent = TempSlot
+
+ local SlotNumber = Instance.new("TextLabel")
+ SlotNumber.Name = "SlotNumber"
+ SlotNumber.BackgroundTransparency = 1
+ SlotNumber.BorderSizePixel = 0
+ SlotNumber.Font = Enum.Font.ArialBold
+ SlotNumber.FontSize = Enum.FontSize.Size18
+ SlotNumber.Position = UDim2.new(0,-7,0,-7)
+ SlotNumber.Size = UDim2.new(0,10,0,15)
+ SlotNumber.TextColor3 = Color3.new(1,1,1)
+ SlotNumber.TextTransparency = 0
+ SlotNumber.TextXAlignment = Enum.TextXAlignment.Left
+ SlotNumber.TextYAlignment = Enum.TextYAlignment.Bottom
+ SlotNumber.ZIndex = 4
+ SlotNumber.RobloxLocked = true
+ SlotNumber.Parent = TempSlot
+
+ local SlotNumberDownShadow = SlotNumber:clone()
+ SlotNumberDownShadow.Name = "SlotNumberDownShadow"
+ SlotNumberDownShadow.TextColor3 = Color3.new(0,0,0)
+ SlotNumberDownShadow.ZIndex = 3
+ SlotNumberDownShadow.Position = UDim2.new(0,-6,0,-6)
+ SlotNumberDownShadow.Parent = TempSlot
+
+ local SlotNumberUpShadow = SlotNumberDownShadow:clone()
+ SlotNumberUpShadow.Name = "SlotNumberUpShadow"
+ SlotNumberUpShadow.Position = UDim2.new(0,-8,0,-8)
+ SlotNumberUpShadow.Parent = TempSlot
+
+ local GearText = Instance.new("TextLabel")
+ GearText.RobloxLocked = true
+ GearText.Name = "GearText"
+ GearText.BackgroundTransparency = 1
+ GearText.Font = Enum.Font.Arial
+ GearText.FontSize = Enum.FontSize.Size14
+ GearText.Position = UDim2.new(0,-8,0,-8)
+ GearText.ZIndex = 2
+ GearText.Size = UDim2.new(1,16,1,16)
+ GearText.Text = ""
+ GearText.TextColor3 = Color3.new(1,1,1)
+ GearText.TextWrap = true
+ GearText.Parent = TempSlot
+
+--- Great, now lets make the inventory!
+
+local Backpack = Instance.new("Frame")
+Backpack.RobloxLocked = true
+Backpack.Visible = false
+Backpack.Name = "Backpack"
+Backpack.Position = UDim2.new(0.5,0,0.5,0)
+Backpack.BackgroundColor3 = Color3.new(0,0,0)
+Backpack.BackgroundTransparency = 0.08
+Backpack.BorderSizePixel = 0
+Backpack.Parent = gui
+Backpack.Active = true
+
+ -- Backpack Children
+ local SwapSlot = Instance.new("BoolValue")
+ SwapSlot.RobloxLocked = true
+ SwapSlot.Name = "SwapSlot"
+ SwapSlot.Parent = Backpack
+
+ -- SwapSlot Children
+ local Slot = Instance.new("IntValue")
+ Slot.RobloxLocked = true
+ Slot.Name = "Slot"
+ Slot.Parent = SwapSlot
+
+ local GearButton = Instance.new("ObjectValue")
+ GearButton.RobloxLocked = true
+ GearButton.Name = "GearButton"
+ GearButton.Parent = SwapSlot
+
+ local Tabs = Instance.new("Frame")
+ Tabs.Name = "Tabs"
+ Tabs.Visible = true
+ Tabs.RobloxLocked = true
+ Tabs.BackgroundColor3 = Color3.new(0,0,0)
+ Tabs.BackgroundTransparency = 0.08
+ Tabs.BorderSizePixel = 0
+ Tabs.Position = UDim2.new(0,0,-0.1,-4)
+ Tabs.Size = UDim2.new(1,0,0.1,4)
+ Tabs.Parent = Backpack
+
+ -- Tabs Children
+
+ local tabLine = Instance.new("Frame")
+ tabLine.RobloxLocked = true
+ tabLine.Name = "TabLine"
+ tabLine.BackgroundColor3 = Color3.new(53/255, 53/255, 53/255)
+ tabLine.BorderSizePixel = 0
+ tabLine.Position = UDim2.new(0,5,1,-4)
+ tabLine.Size = UDim2.new(1,-10,0,4)
+ tabLine.ZIndex = 2
+ tabLine.Parent = Tabs
+
+ local InventoryButton = Instance.new("TextButton")
+ InventoryButton.RobloxLocked = true
+ InventoryButton.Name = "InventoryButton"
+ InventoryButton.Size = UDim2.new(0,60,0,30)
+ InventoryButton.Position = UDim2.new(0,7,1,-31)
+ InventoryButton.BackgroundColor3 = Color3.new(1,1,1)
+ InventoryButton.BorderColor3 = Color3.new(1,1,1)
+ InventoryButton.Font = Enum.Font.ArialBold
+ InventoryButton.FontSize = Enum.FontSize.Size18
+ InventoryButton.Text = "Gear"
+ InventoryButton.AutoButtonColor = false
+ InventoryButton.TextColor3 = Color3.new(0,0,0)
+ InventoryButton.Selected = true
+ InventoryButton.Active = true
+ InventoryButton.ZIndex = 3
+ InventoryButton.Parent = Tabs
+
+ if game.CoreGui.Version >= 8 then
+ local WardrobeButton = Instance.new("TextButton")
+ WardrobeButton.RobloxLocked = true
+ WardrobeButton.Name = "WardrobeButton"
+ WardrobeButton.Size = UDim2.new(0,90,0,30)
+ WardrobeButton.Position = UDim2.new(0,77,1,-31)
+ WardrobeButton.BackgroundColor3 = Color3.new(0,0,0)
+ WardrobeButton.BorderColor3 = Color3.new(1,1,1)
+ WardrobeButton.Font = Enum.Font.ArialBold
+ WardrobeButton.FontSize = Enum.FontSize.Size18
+ WardrobeButton.Text = "Wardrobe"
+ WardrobeButton.AutoButtonColor = false
+ WardrobeButton.TextColor3 = Color3.new(1,1,1)
+ WardrobeButton.Selected = false
+ WardrobeButton.Active = true
+ WardrobeButton.Parent = Tabs
+ end
+
+ local closeButton = Instance.new("TextButton")
+ closeButton.RobloxLocked = true
+ closeButton.Name = "CloseButton"
+ closeButton.Font = Enum.Font.ArialBold
+ closeButton.FontSize = Enum.FontSize.Size24
+ closeButton.Position = UDim2.new(1,-33,0,4)
+ closeButton.Size = UDim2.new(0,30,0,30)
+ closeButton.Style = Enum.ButtonStyle.RobloxButton
+ closeButton.Text = ""
+ closeButton.TextColor3 = Color3.new(1,1,1)
+ closeButton.Parent = Tabs
+ closeButton.Modal = true
+
+ --closeButton child
+ local XImage = Instance.new("ImageLabel")
+ XImage.RobloxLocked = true
+ XImage.Name = "XImage"
+ game:GetService("ContentProvider"):Preload("http://www.roblox.com/asset/?id=75547445")
+ XImage.Image = "http://www.roblox.com/asset/?id=75547445" --TODO: move to rbxasset
+ XImage.BackgroundTransparency = 1
+ XImage.Position = UDim2.new(-.25,-1,-.25,-1)
+ XImage.Size = UDim2.new(1.5,2,1.5,2)
+ XImage.ZIndex = 2
+ XImage.Parent = closeButton
+
+ -- Generic Search gui used across backpack
+ local SearchFrame = Instance.new("Frame")
+ SearchFrame.RobloxLocked = true
+ SearchFrame.Name = "SearchFrame"
+ SearchFrame.BackgroundTransparency = 1
+ SearchFrame.Position = UDim2.new(1,-220,0,2)
+ SearchFrame.Size = UDim2.new(0,220,0,24)
+ SearchFrame.Parent = Backpack
+
+ -- SearchFrame Children
+ local SearchButton = Instance.new("ImageButton")
+ SearchButton.RobloxLocked = true
+ SearchButton.Name = "SearchButton"
+ SearchButton.Size = UDim2.new(0,25,0,25)
+ SearchButton.BackgroundTransparency = 1
+ SearchButton.Image = "rbxasset://textures/ui/SearchIcon.png"
+ SearchButton.Parent = SearchFrame
+
+ local SearchBoxFrame = Instance.new("TextButton")
+ SearchBoxFrame.RobloxLocked = true
+ SearchBoxFrame.Position = UDim2.new(0,25,0,0)
+ SearchBoxFrame.Size = UDim2.new(1,-28,0,26)
+ SearchBoxFrame.Name = "SearchBoxFrame"
+ SearchBoxFrame.Text = ""
+ SearchBoxFrame.Style = Enum.ButtonStyle.RobloxButton
+ SearchBoxFrame.Parent = SearchFrame
+
+ -- SearchBoxFrame Children
+ local SearchBox = Instance.new("TextBox")
+ SearchBox.RobloxLocked = true
+ SearchBox.Name = "SearchBox"
+ SearchBox.BackgroundTransparency = 1
+ SearchBox.Font = Enum.Font.ArialBold
+ SearchBox.FontSize = Enum.FontSize.Size12
+ SearchBox.Position = UDim2.new(0,-5,0,-5)
+ SearchBox.Size = UDim2.new(1,10,1,10)
+ SearchBox.TextColor3 = Color3.new(1,1,1)
+ SearchBox.TextXAlignment = Enum.TextXAlignment.Left
+ SearchBox.ZIndex = 2
+ SearchBox.TextWrap = true
+ SearchBox.Text = "Search..."
+ SearchBox.Parent = SearchBoxFrame
+
+
+ local ResetButton = Instance.new("TextButton")
+ ResetButton.RobloxLocked = true
+ ResetButton.Visible = false
+ ResetButton.Name = "ResetButton"
+ ResetButton.Position = UDim2.new(1,-26,0,3)
+ ResetButton.Size = UDim2.new(0,20,0,20)
+ ResetButton.Style = Enum.ButtonStyle.RobloxButtonDefault
+ ResetButton.Text = "X"
+ ResetButton.TextColor3 = Color3.new(1,1,1)
+ ResetButton.Font = Enum.Font.ArialBold
+ ResetButton.FontSize = Enum.FontSize.Size18
+ ResetButton.ZIndex = 3
+ ResetButton.Parent = SearchFrame
+
+------------------------------- GEAR -------------------------------------------------------
+ local Gear = Instance.new("Frame")
+ Gear.Name = "Gear"
+ Gear.RobloxLocked = true
+ Gear.BackgroundTransparency = 1
+ Gear.Size = UDim2.new(1,0,1,0)
+ Gear.Parent = Backpack
+
+ -- Gear Children
+ local AssetsList = Instance.new("Frame")
+ AssetsList.RobloxLocked = true
+ AssetsList.Name = "AssetsList"
+ AssetsList.BackgroundTransparency = 1
+ AssetsList.Size = UDim2.new(0.2,0,1,0)
+ AssetsList.Style = Enum.FrameStyle.RobloxSquare
+ AssetsList.Visible = false
+ AssetsList.Parent = Gear
+
+ local GearGrid = Instance.new("Frame")
+ GearGrid.RobloxLocked = true
+ GearGrid.Name = "GearGrid"
+ GearGrid.Size = UDim2.new(0.69,0,1,0)
+ GearGrid.BackgroundTransparency = 1
+ GearGrid.Parent = Gear
+
+
+ local GearButton = Instance.new("ImageButton")
+ GearButton.RobloxLocked = true
+ GearButton.Visible = false
+ GearButton.Name = "GearButton"
+ GearButton.Size = UDim2.new(0,64,0,64)
+ GearButton.Style = Enum.ButtonStyle.RobloxButton
+ GearButton.Parent = GearGrid
+
+ -- GearButton Children
+ local GearReference = Instance.new("ObjectValue")
+ GearReference.RobloxLocked = true
+ GearReference.Name = "GearReference"
+ GearReference.Parent = GearButton
+
+ local GreyOutButton = Instance.new("Frame")
+ GreyOutButton.RobloxLocked = true
+ GreyOutButton.Name = "GreyOutButton"
+ GreyOutButton.BackgroundTransparency = 0.5
+ GreyOutButton.Size = UDim2.new(1,0,1,0)
+ GreyOutButton.Active = true
+ GreyOutButton.Visible = false
+ GreyOutButton.ZIndex = 3
+ GreyOutButton.Parent = GearButton
+
+ local GearText = Instance.new("TextLabel")
+ GearText.RobloxLocked = true
+ GearText.Name = "GearText"
+ GearText.BackgroundTransparency = 1
+ GearText.Font = Enum.Font.Arial
+ GearText.FontSize = Enum.FontSize.Size14
+ GearText.Position = UDim2.new(0,-8,0,-8)
+ GearText.Size = UDim2.new(1,16,1,16)
+ GearText.Text = ""
+ GearText.ZIndex = 2
+ GearText.TextColor3 = Color3.new(1,1,1)
+ GearText.TextWrap = true
+ GearText.Parent = GearButton
+
+ local GearGridScrollingArea = Instance.new("Frame")
+ GearGridScrollingArea.RobloxLocked = true
+ GearGridScrollingArea.Name = "GearGridScrollingArea"
+ GearGridScrollingArea.Position = UDim2.new(0.7,0,0,35)
+ GearGridScrollingArea.Size = UDim2.new(0,17,1,-45)
+ GearGridScrollingArea.BackgroundTransparency = 1
+ GearGridScrollingArea.Parent = Gear
+
+ local GearLoadouts = Instance.new("Frame")
+ GearLoadouts.RobloxLocked = true
+ GearLoadouts.Name = "GearLoadouts"
+ GearLoadouts.BackgroundTransparency = 1
+ GearLoadouts.Position = UDim2.new(0.7,23,0.5,1)
+ GearLoadouts.Size = UDim2.new(0.3,-23,0.5,-1)
+ GearLoadouts.Parent = Gear
+ GearLoadouts.Visible = false
+
+ -- GearLoadouts Children
+ local GearLoadoutsHeader = Instance.new("Frame")
+ GearLoadoutsHeader.RobloxLocked = true
+ GearLoadoutsHeader.Name = "GearLoadoutsHeader"
+ GearLoadoutsHeader.BackgroundColor3 = Color3.new(0,0,0)
+ GearLoadoutsHeader.BackgroundTransparency = 0.2
+ GearLoadoutsHeader.BorderColor3 = Color3.new(1,0,0)
+ GearLoadoutsHeader.Size = UDim2.new(1,2,0.15,-1)
+ GearLoadoutsHeader.Parent = GearLoadouts
+
+ -- GearLoadoutsHeader Children
+ local LoadoutsHeaderText = Instance.new("TextLabel")
+ LoadoutsHeaderText.RobloxLocked = true
+ LoadoutsHeaderText.Name = "LoadoutsHeaderText"
+ LoadoutsHeaderText.BackgroundTransparency = 1
+ LoadoutsHeaderText.Font = Enum.Font.ArialBold
+ LoadoutsHeaderText.FontSize = Enum.FontSize.Size18
+ LoadoutsHeaderText.Size = UDim2.new(1,0,1,0)
+ LoadoutsHeaderText.Text = "Loadouts"
+ LoadoutsHeaderText.TextColor3 = Color3.new(1,1,1)
+ LoadoutsHeaderText.Parent = GearLoadoutsHeader
+
+ local GearLoadoutsScrollingArea = GearGridScrollingArea:clone()
+ GearLoadoutsScrollingArea.RobloxLocked = true
+ GearLoadoutsScrollingArea.Name = "GearLoadoutsScrollingArea"
+ GearLoadoutsScrollingArea.Position = UDim2.new(1,-15,0.15,2)
+ GearLoadoutsScrollingArea.Size = UDim2.new(0,17,0.85,-2)
+ GearLoadoutsScrollingArea.Parent = GearLoadouts
+
+ local LoadoutsList = Instance.new("Frame")
+ LoadoutsList.RobloxLocked = true
+ LoadoutsList.Name = "LoadoutsList"
+ LoadoutsList.Position = UDim2.new(0,0,0.15,2)
+ LoadoutsList.Size = UDim2.new(1,-17,0.85,-2)
+ LoadoutsList.Style = Enum.FrameStyle.RobloxSquare
+ LoadoutsList.Parent = GearLoadouts
+
+ local GearPreview = Instance.new("Frame")
+ GearPreview.RobloxLocked = true
+ GearPreview.Name = "GearPreview"
+ GearPreview.Position = UDim2.new(0.7,23,0,0)
+ GearPreview.Size = UDim2.new(0.3,-28,0.5,-1)
+ GearPreview.BackgroundTransparency = 1
+ GearPreview.ZIndex = 7
+ GearPreview.Parent = Gear
+
+ -- GearPreview Children
+ local GearStats = Instance.new("Frame")
+ GearStats.RobloxLocked = true
+ GearStats.Name = "GearStats"
+ GearStats.BackgroundTransparency = 1
+ GearStats.Position = UDim2.new(0,0,0.75,0)
+ GearStats.Size = UDim2.new(1,0,0.25,0)
+ GearStats.ZIndex = 8
+ GearStats.Parent = GearPreview
+
+ -- GearStats Children
+ local GearName = Instance.new("TextLabel")
+ GearName.RobloxLocked = true
+ GearName.Name = "GearName"
+ GearName.BackgroundTransparency = 1
+ GearName.Font = Enum.Font.ArialBold
+ GearName.FontSize = Enum.FontSize.Size18
+ GearName.Position = UDim2.new(0,-3,0,0)
+ GearName.Size = UDim2.new(1,6,1,5)
+ GearName.Text = ""
+ GearName.TextColor3 = Color3.new(1,1,1)
+ GearName.TextWrap = true
+ GearName.ZIndex = 9
+ GearName.Parent = GearStats
+
+ local GearImage = Instance.new("ImageLabel")
+ GearImage.RobloxLocked = true
+ GearImage.Name = "GearImage"
+ GearImage.Image = ""
+ GearImage.BackgroundTransparency = 1
+ GearImage.Position = UDim2.new(0.125,0,0,0)
+ GearImage.Size = UDim2.new(0.75,0,0.75,0)
+ GearImage.ZIndex = 8
+ GearImage.Parent = GearPreview
+
+ --GearImage Children
+ local GearIcons = Instance.new("Frame")
+ GearIcons.BackgroundColor3 = Color3.new(0,0,0)
+ GearIcons.BackgroundTransparency = 0.5
+ GearIcons.BorderSizePixel = 0
+ GearIcons.RobloxLocked = true
+ GearIcons.Name = "GearIcons"
+ GearIcons.Position = UDim2.new(0.4,2,0.85,-2)
+ GearIcons.Size = UDim2.new(0.6,0,0.15,0)
+ GearIcons.Visible = false
+ GearIcons.ZIndex = 9
+ GearIcons.Parent = GearImage
+
+ -- GearIcons Children
+ local GenreImage = Instance.new("ImageLabel")
+ GenreImage.RobloxLocked = true
+ GenreImage.Name = "GenreImage"
+ GenreImage.BackgroundColor3 = Color3.new(102/255,153/255,1)
+ GenreImage.BackgroundTransparency = 0.5
+ GenreImage.BorderSizePixel = 0
+ GenreImage.Size = UDim2.new(0.25,0,1,0)
+ GenreImage.Parent = GearIcons
+
+ local AttributeOneImage = GenreImage:clone()
+ AttributeOneImage.RobloxLocked = true
+ AttributeOneImage.Name = "AttributeOneImage"
+ AttributeOneImage.BackgroundColor3 = Color3.new(1,51/255,0)
+ AttributeOneImage.Position = UDim2.new(0.25,0,0,0)
+ AttributeOneImage.Parent = GearIcons
+
+ local AttributeTwoImage = GenreImage:clone()
+ AttributeTwoImage.RobloxLocked = true
+ AttributeTwoImage.Name = "AttributeTwoImage"
+ AttributeTwoImage.BackgroundColor3 = Color3.new(153/255,1,153/255)
+ AttributeTwoImage.Position = UDim2.new(0.5,0,0,0)
+ AttributeTwoImage.Parent = GearIcons
+
+ local AttributeThreeImage = GenreImage:clone()
+ AttributeThreeImage.RobloxLocked = true
+ AttributeThreeImage.Name = "AttributeThreeImage"
+ AttributeThreeImage.BackgroundColor3 = Color3.new(0,0.5,0.5)
+ AttributeThreeImage.Position = UDim2.new(0.75,0,0,0)
+ AttributeThreeImage.Parent = GearIcons
+
+------------------------------- WARDROBE -------------------------------------------------------
+ if game.CoreGui.Version < 8 then
+ -- no need for this to stick around, we aren't ready for wardrobe
+ script:remove()
+ return
+ end
+
+ local function makeCharFrame(frameName, parent)
+ local frame = Instance.new("Frame")
+ frame.RobloxLocked = true
+ frame.Size = UDim2.new(1,0,1,-70)
+ frame.Position = UDim2.new(0,0,0,20)
+ frame.Name = frameName
+ frame.BackgroundTransparency = 1
+ frame.Parent = parent
+ frame.Visible = false
+ return frame
+ end
+ local function makeZone( zoneName, image, size, position, parent )
+ local zone = Instance.new("ImageLabel")
+ zone.RobloxLocked = true
+ zone.Name = zoneName
+ zone.Image = image
+ zone.Size = size
+ zone.BackgroundTransparency = 1
+ zone.Position = position
+ zone.Parent = parent
+ return zone
+ end
+ local function makeStyledButton( buttonName, size, position, parent, buttonStyle )
+ local button = Instance.new("ImageButton")
+ button.RobloxLocked = true
+ button.Name = buttonName
+ button.Size = size
+ button.Position = position
+ if buttonStyle then
+ button.Style = buttonStyle
+ else
+ button.BackgroundColor3 = Color3.new(0,0,0)
+ button.BorderColor3 = Color3.new(1,1,1)
+ end
+ button.Parent = parent
+ return button
+ end
+ local function makeTextLabel( TextLabelName,text,position,parent )
+ local label = Instance.new("TextLabel")
+ label.RobloxLocked = true
+ label.BackgroundTransparency = 1
+ label.Size = UDim2.new(0,32,0,14)
+ label.Name = TextLabelName
+ label.Font = Enum.Font.Arial
+ label.TextColor3 = Color3.new(1,1,1)
+ label.FontSize = Enum.FontSize.Size14
+ label.Text = text
+ label.Position = position
+ label.Parent = parent
+ end
+
+
+ local Wardrobe = Instance.new("Frame")
+ Wardrobe.Name = "Wardrobe"
+ Wardrobe.RobloxLocked = true
+ Wardrobe.BackgroundTransparency = 1
+ Wardrobe.Visible = false
+ Wardrobe.Size = UDim2.new(1,0,1,0)
+ Wardrobe.Parent = Backpack
+
+ local AssetList = Instance.new("Frame")
+ AssetList.RobloxLocked = true
+ AssetList.Name = "AssetList"
+ AssetList.Position = UDim2.new(0,4,0,5)
+ AssetList.Size = UDim2.new(0,85,1,-5)
+ AssetList.BackgroundTransparency = 1
+ AssetList.Visible = true
+ AssetList.Parent = Wardrobe
+
+ local PreviewAssetFrame = Instance.new("Frame")
+ PreviewAssetFrame.RobloxLocked = true
+ PreviewAssetFrame.Name = "PreviewAssetFrame"
+ PreviewAssetFrame.BackgroundTransparency = 1
+ PreviewAssetFrame.Position = UDim2.new(1,-240,0,30)
+ PreviewAssetFrame.Size = UDim2.new(0,250,0,250)
+ PreviewAssetFrame.Parent = Wardrobe
+
+ local PreviewAssetBacking = Instance.new("TextButton")
+ PreviewAssetBacking.RobloxLocked = true
+ PreviewAssetBacking.Name = "PreviewAssetBacking"
+ PreviewAssetBacking.Active = false
+ PreviewAssetBacking.Text = ""
+ PreviewAssetBacking.AutoButtonColor = false
+ PreviewAssetBacking.Size = UDim2.new(1,0,1,0)
+ PreviewAssetBacking.Style = Enum.ButtonStyle.RobloxButton
+ PreviewAssetBacking.Visible = false
+ PreviewAssetBacking.ZIndex = 9
+ PreviewAssetBacking.Parent = PreviewAssetFrame
+
+ local PreviewAssetImage = Instance.new("ImageLabel")
+ PreviewAssetImage.RobloxLocked = true
+ PreviewAssetImage.Name = "PreviewAssetImage"
+ PreviewAssetImage.BackgroundTransparency = 0.8
+ PreviewAssetImage.Position = UDim2.new(0.5,-100,0,0)
+ PreviewAssetImage.Size = UDim2.new(0,200,0,200)
+ PreviewAssetImage.BorderSizePixel = 0
+ PreviewAssetImage.ZIndex = 10
+ PreviewAssetImage.Parent = PreviewAssetBacking
+
+ local AssetNameLabel = Instance.new("TextLabel")
+ AssetNameLabel.Name = "AssetNameLabel"
+ AssetNameLabel.RobloxLocked = true
+ AssetNameLabel.BackgroundTransparency = 1
+ AssetNameLabel.Position = UDim2.new(0,0,1,-20)
+ AssetNameLabel.Size = UDim2.new(0.5,0,0,24)
+ AssetNameLabel.ZIndex = 10
+ AssetNameLabel.Font = Enum.Font.Arial
+ AssetNameLabel.Text = ""
+ AssetNameLabel.TextColor3 = Color3.new(1,1,1)
+ AssetNameLabel.TextScaled = true
+ AssetNameLabel.Parent = PreviewAssetBacking
+
+ local AssetTypeLabel = AssetNameLabel:clone()
+ AssetTypeLabel.RobloxLocked = true
+ AssetTypeLabel.Name = "AssetTypeLabel"
+ AssetTypeLabel.TextScaled = false
+ AssetTypeLabel.FontSize = Enum.FontSize.Size18
+ AssetTypeLabel.Position = UDim2.new(0.5,3,1,-20)
+ AssetTypeLabel.Parent = PreviewAssetBacking
+
+
+
+ local PreviewButton = Instance.new("TextButton")
+ PreviewButton.RobloxLocked = true
+ PreviewButton.Name = "PreviewButton"
+ PreviewButton.Text = "Rotate"
+ PreviewButton.BackgroundColor3 = Color3.new(0,0,0)
+ PreviewButton.BackgroundTransparency = 0.5
+ PreviewButton.BorderColor3 = Color3.new(1,1,1)
+ PreviewButton.Position = UDim2.new(1.2,-62,1,-50)
+ PreviewButton.Size = UDim2.new(0,125,0,50)
+ PreviewButton.Font = Enum.Font.ArialBold
+ PreviewButton.FontSize = Enum.FontSize.Size24
+ PreviewButton.TextColor3 = Color3.new(1,1,1)
+ PreviewButton.TextWrapped = true
+ PreviewButton.TextStrokeTransparency = 0
+ PreviewButton.Parent = Wardrobe
+
+ local CharacterPane = Instance.new("Frame")
+ CharacterPane.RobloxLocked = true
+ CharacterPane.Name = "CharacterPane"
+ CharacterPane.Position = UDim2.new(1,-220,0,32)
+ CharacterPane.Size = UDim2.new(0,220,1,-40)
+ CharacterPane.BackgroundTransparency = 1
+ CharacterPane.Visible = true
+ CharacterPane.Parent = Wardrobe
+
+ --CharacterPane Children
+ local FaceFrame = makeCharFrame("FacesFrame", CharacterPane)
+ game:GetService("ContentProvider"):Preload("http://www.roblox.com/asset/?id=75460621")
+ makeZone("FaceZone","http://www.roblox.com/asset/?id=75460621",UDim2.new(0,157,0,137),UDim2.new(0.5,-78,0.5,-68),FaceFrame)
+ makeStyledButton("Face",UDim2.new(0,64,0,64),UDim2.new(0.5,-32,0.5,-135),FaceFrame)
+
+ local HeadFrame = makeCharFrame("HeadsFrame", CharacterPane)
+ makeZone("FaceZone","http://www.roblox.com/asset/?id=75460621",UDim2.new(0,157,0,137),UDim2.new(0.5,-78,0.5,-68),HeadFrame)
+ makeStyledButton("Head",UDim2.new(0,64,0,64),UDim2.new(0.5,-32,0.5,-135),HeadFrame)
+
+ local HatsFrame = makeCharFrame("HatsFrame", CharacterPane)
+ game:GetService("ContentProvider"):Preload("http://www.roblox.com/asset/?id=75457888")
+ local HatsZone = makeZone("HatsZone","http://www.roblox.com/asset/?id=75457888",UDim2.new(0,186,0,184),UDim2.new(0.5,-93,0.5,-100), HatsFrame)
+ makeStyledButton("Hat1Button",UDim2.new(0,64,0,64),UDim2.new(0,-1,0,-1),HatsZone,Enum.ButtonStyle.RobloxButton)
+ makeStyledButton("Hat2Button",UDim2.new(0,64,0,64),UDim2.new(0,63,0,-1),HatsZone,Enum.ButtonStyle.RobloxButton)
+ makeStyledButton("Hat3Button",UDim2.new(0,64,0,64),UDim2.new(0,127,0,-1),HatsZone,Enum.ButtonStyle.RobloxButton)
+
+ local PantsFrame = makeCharFrame("PantsFrame", CharacterPane)
+ game:GetService("ContentProvider"):Preload("http://www.roblox.com/asset/?id=75457920")
+ makeZone("PantsZone","http://www.roblox.com/asset/?id=75457920",UDim2.new(0,121,0,99),UDim2.new(0.5,-60,0.5,-100),PantsFrame)
+
+ local pantFrame = Instance.new("Frame")
+ pantFrame.RobloxLocked = true
+ pantFrame.Size = UDim2.new(0,25,0,56)
+ pantFrame.Position = UDim2.new(0.5,-26,0.5,0)
+ pantFrame.BackgroundColor3 = Color3.new(0,0,0)
+ pantFrame.BorderColor3 = Color3.new(1,1,1)
+ pantFrame.Name = "PantFrame"
+ pantFrame.Parent = PantsFrame
+
+ local otherPantFrame = pantFrame:clone()
+ otherPantFrame.Position = UDim2.new(0.5,3,0.5,0)
+ otherPantFrame.RobloxLocked = true
+ otherPantFrame.Parent = PantsFrame
+
+ local CurrentPants = Instance.new("ImageButton")
+ CurrentPants.RobloxLocked = true
+ CurrentPants.BackgroundTransparency = 1
+ CurrentPants.ZIndex = 2
+ CurrentPants.Name = "CurrentPants"
+ CurrentPants.Position = UDim2.new(0.5,-31,0.5,-4)
+ CurrentPants.Size = UDim2.new(0,54,0,59)
+ CurrentPants.Parent = PantsFrame
+
+ local MeshFrame = makeCharFrame("PackagesFrame", CharacterPane)
+ local torsoButton = makeStyledButton("TorsoMeshButton", UDim2.new(0,64,0,64),UDim2.new(0.5,-32,0.5,-110),MeshFrame,Enum.ButtonStyle.RobloxButton)
+ makeTextLabel("TorsoLabel","Torso",UDim2.new(0.5,-16,0,-25),torsoButton)
+ local leftLegButton = makeStyledButton("LeftLegMeshButton", UDim2.new(0,64,0,64),UDim2.new(0.5,0,0.5,-25),MeshFrame,Enum.ButtonStyle.RobloxButton)
+ makeTextLabel("LeftLegLabel","Left Leg",UDim2.new(0.5,-16,0,-25),leftLegButton)
+ local rightLegButton = makeStyledButton("RightLegMeshButton", UDim2.new(0,64,0,64),UDim2.new(0.5,-64,0.5,-25),MeshFrame,Enum.ButtonStyle.RobloxButton)
+ makeTextLabel("RightLegLabel","Right Leg",UDim2.new(0.5,-16,0,-25),rightLegButton)
+ local rightArmButton = makeStyledButton("RightArmMeshButton", UDim2.new(0,64,0,64),UDim2.new(0.5,-96,0.5,-110),MeshFrame,Enum.ButtonStyle.RobloxButton)
+ makeTextLabel("RightArmLabel","Right Arm",UDim2.new(0.5,-16,0,-25),rightArmButton)
+ local leftArmButton = makeStyledButton("LeftArmMeshButton", UDim2.new(0,64,0,64),UDim2.new(0.5,32,0.5,-110),MeshFrame,Enum.ButtonStyle.RobloxButton)
+ makeTextLabel("LeftArmLabel","Left Arm",UDim2.new(0.5,-16,0,-25),leftArmButton)
+
+ local TShirtFrame = makeCharFrame("T-ShirtsFrame",CharacterPane)
+ game:GetService("ContentProvider"):Preload("http://www.roblox.com/asset/?id=75460642")
+ makeZone("TShirtZone","http://www.roblox.com/asset/?id=75460642",UDim2.new(0,121,0,154),UDim2.new(0.5,-60,0.5,-100),TShirtFrame)
+ makeStyledButton("TShirtButton", UDim2.new(0,64,0,64),UDim2.new(0.5,-32,0.5,-64),TShirtFrame)
+
+
+ local ShirtFrame = makeCharFrame("ShirtsFrame", CharacterPane)
+ makeZone("ShirtZone","http://www.roblox.com/asset/?id=75460642",UDim2.new(0,121,0,154),UDim2.new(0.5,-60,0.5,-100),ShirtFrame)
+ makeStyledButton("ShirtButton", UDim2.new(0,64,0,64),UDim2.new(0.5,-32,0.5,-64),ShirtFrame)
+
+
+ local ColorFrame = makeCharFrame("ColorFrame", CharacterPane)
+ game:GetService("ContentProvider"):Preload("http://www.roblox.com/asset/?id=76049888")
+ local ColorZone = makeZone("ColorZone","http://www.roblox.com/asset/?id=76049888", UDim2.new(0,120,0,150),UDim2.new(0.5,-60,0.5,-100),ColorFrame)
+ makeStyledButton("Head",UDim2.new(0.26,0,0.19,0),UDim2.new(0.37,0,0.02,0),ColorZone).AutoButtonColor = false
+ makeStyledButton("LeftArm",UDim2.new(0.19,0,0.36,0),UDim2.new(0.78,0,0.26,0),ColorZone).AutoButtonColor = false
+ makeStyledButton("RightArm",UDim2.new(0.19,0,0.36,0),UDim2.new(0.025,0,0.26,0),ColorZone).AutoButtonColor = false
+ makeStyledButton("Torso",UDim2.new(0.43,0,0.36,0),UDim2.new(0.28,0,0.26,0),ColorZone).AutoButtonColor = false
+ makeStyledButton("RightLeg",UDim2.new(0.19,0,0.31,0),UDim2.new(0.275,0,0.67,0),ColorZone).AutoButtonColor = false
+ makeStyledButton("LeftLeg",UDim2.new(0.19,0,0.31,0),UDim2.new(0.525,0,0.67,0),ColorZone).AutoButtonColor = false
+
+ -- Character Panel label (shows what category we are currently browsing)
+ local CategoryLabel = Instance.new("TextLabel")
+ CategoryLabel.RobloxLocked = true
+ CategoryLabel.Name = "CategoryLabel"
+ CategoryLabel.BackgroundTransparency = 1
+ CategoryLabel.Font = Enum.Font.ArialBold
+ CategoryLabel.FontSize = Enum.FontSize.Size18
+ CategoryLabel.Position = UDim2.new(0,0,0,-7)
+ CategoryLabel.Size = UDim2.new(1,0,0,20)
+ CategoryLabel.TextXAlignment = Enum.TextXAlignment.Center
+ CategoryLabel.Text = "All"
+ CategoryLabel.TextColor3 = Color3.new(1,1,1)
+ CategoryLabel.Parent = CharacterPane
+
+ --Save Button
+ local SaveButton = Instance.new("TextButton")
+ SaveButton.RobloxLocked = true
+ SaveButton.Name = "SaveButton"
+ SaveButton.Size = UDim2.new(0.6,0,0,50)
+ SaveButton.Position = UDim2.new(0.2,0,1,-50)
+ SaveButton.Style = Enum.ButtonStyle.RobloxButton
+ SaveButton.Selected = false
+ SaveButton.Font = Enum.Font.ArialBold
+ SaveButton.FontSize = Enum.FontSize.Size18
+ SaveButton.Text = "Save"
+ SaveButton.TextColor3 = Color3.new(1,1,1)
+ SaveButton.Parent = CharacterPane
+
+-- no need for this to stick around
+script:remove()
\ No newline at end of file
diff --git a/app/files/CoreGui/53878053 b/app/files/CoreGui/53878053
new file mode 100644
index 0000000..79b5c9d
--- /dev/null
+++ b/app/files/CoreGui/53878053
@@ -0,0 +1,968 @@
+if game.CoreGui.Version < 3 then return end -- peace out if we aren't using the right client
+
+-- A couple of necessary functions
+local function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+local function waitForProperty(instance, property)
+ while not instance[property] do
+ instance.Changed:wait()
+ end
+end
+
+waitForChild(game,"Players")
+waitForProperty(game.Players,"LocalPlayer")
+local player = game.Players.LocalPlayer
+
+local RbxGui,msg = LoadLibrary("RbxGui")
+if not RbxGui then print("could not find RbxGui!") return end
+
+--- Begin Locals
+waitForChild(game,"Players")
+
+-- don't do anything if we are in an empty game
+if #game.Players:GetChildren() < 1 then
+ game.Players.ChildAdded:wait()
+end
+
+local tilde = "~"
+local backquote = "`"
+game:GetService("GuiService"):AddKey(tilde) -- register our keys
+game:GetService("GuiService"):AddKey(backquote)
+
+local player = game.Players.LocalPlayer
+
+local backpack = script.Parent
+local screen = script.Parent.Parent
+local closeButton = backpack.Tabs.CloseButton
+
+local openCloseDebounce = false
+
+local backpackItems = {}
+
+local buttons = {}
+
+local debounce = false
+
+local guiTweenSpeed = 1
+
+local backpackOldStateVisible = false
+local browsingMenu = false
+
+local mouseEnterCons = {}
+local mouseClickCons = {}
+
+local characterChildAddedCon = nil
+local characterChildRemovedCon = nil
+local backpackAddCon = nil
+local humanoidDiedCon = nil
+local backpackButtonClickCon = nil
+local guiServiceKeyPressCon = nil
+
+waitForChild(player,"Backpack")
+local playerBackpack = player.Backpack
+
+waitForChild(backpack,"Gear")
+waitForChild(backpack.Gear,"GearPreview")
+local gearPreview = backpack.Gear.GearPreview
+
+waitForChild(backpack.Gear,"GearGridScrollingArea")
+local scroller = backpack.Gear.GearGridScrollingArea
+
+waitForChild(backpack.Parent,"CurrentLoadout")
+local currentLoadout = backpack.Parent.CurrentLoadout
+
+waitForChild(backpack.Parent,"ControlFrame")
+waitForChild(backpack.Parent.ControlFrame,"BackpackButton")
+local backpackButton = backpack.Parent.ControlFrame.BackpackButton
+
+waitForChild(backpack.Gear,"GearGrid")
+waitForChild(backpack.Gear.GearGrid,"GearButton")
+local gearButton = backpack.Gear.GearGrid.GearButton
+local grid = backpack.Gear.GearGrid
+
+waitForChild(backpack.Gear.GearGrid,"SearchFrame")
+waitForChild(backpack.Gear.GearGrid.SearchFrame,"SearchBoxFrame")
+waitForChild(backpack.Gear.GearGrid.SearchFrame.SearchBoxFrame,"SearchBox")
+local searchBox = backpack.Gear.GearGrid.SearchFrame.SearchBoxFrame.SearchBox
+
+waitForChild(backpack.Gear.GearGrid.SearchFrame,"SearchButton")
+local searchButton = backpack.Gear.GearGrid.SearchFrame.SearchButton
+
+waitForChild(backpack.Gear.GearGrid,"ResetFrame")
+local resetFrame = backpack.Gear.GearGrid.ResetFrame
+
+waitForChild(backpack.Gear.GearGrid.ResetFrame,"ResetButtonBorder")
+local resetButton = backpack.Gear.GearGrid.ResetFrame.ResetButtonBorder
+
+waitForChild(script.Parent,"SwapSlot")
+local swapSlot = script.Parent.SwapSlot
+
+
+-- creating scroll bar early as to make sure items get placed correctly
+local scrollFrame, scrollUp, scrollDown, recalculateScroll = RbxGui.CreateScrollingFrame(nil, "grid", Vector2.new(4, 4))
+
+scrollFrame.Position = UDim2.new(0,0,0,30)
+scrollFrame.Size = UDim2.new(1,0,1,-30)
+scrollFrame.Parent = backpack.Gear.GearGrid
+
+local scrollBar = Instance.new("Frame")
+scrollBar.Name = "ScrollBar"
+scrollBar.BackgroundTransparency = 0.9
+scrollBar.BackgroundColor3 = Color3.new(1,1,1)
+scrollBar.BorderSizePixel = 0
+scrollBar.Size = UDim2.new(0, 17, 1, -36)
+scrollBar.Position = UDim2.new(0,0,0,18)
+scrollBar.Parent = scroller
+
+scrollDown.Position = UDim2.new(0,0,1,-17)
+
+scrollUp.Parent = scroller
+scrollDown.Parent = scroller
+
+local scrollFrameLoadout, scrollUpLoadout, scrollDownLoadout, recalculateScrollLoadout = RbxGui.CreateScrollingFrame()
+
+scrollFrameLoadout.Position = UDim2.new(0,0,0,0)
+scrollFrameLoadout.Size = UDim2.new(1,0,1,0)
+scrollFrameLoadout.Parent = backpack.Gear.GearLoadouts.LoadoutsList
+
+local LoadoutButton = Instance.new("TextButton")
+LoadoutButton.RobloxLocked = true
+LoadoutButton.Name = "LoadoutButton"
+LoadoutButton.Font = Enum.Font.ArialBold
+LoadoutButton.FontSize = Enum.FontSize.Size14
+LoadoutButton.Position = UDim2.new(0,0,0,0)
+LoadoutButton.Size = UDim2.new(1,0,0,32)
+LoadoutButton.Style = Enum.ButtonStyle.RobloxButton
+LoadoutButton.Text = "Loadout #1"
+LoadoutButton.TextColor3 = Color3.new(1,1,1)
+LoadoutButton.Parent = scrollFrameLoadout
+
+local LoadoutButtonTwo = LoadoutButton:clone()
+LoadoutButtonTwo.Text = "Loadout #2"
+LoadoutButtonTwo.Parent = scrollFrameLoadout
+
+local LoadoutButtonThree = LoadoutButton:clone()
+LoadoutButtonThree.Text = "Loadout #3"
+LoadoutButtonThree.Parent = scrollFrameLoadout
+
+local LoadoutButtonFour = LoadoutButton:clone()
+LoadoutButtonFour.Text = "Loadout #4"
+LoadoutButtonFour.Parent = scrollFrameLoadout
+
+local scrollBarLoadout = Instance.new("Frame")
+scrollBarLoadout.Name = "ScrollBarLoadout"
+scrollBarLoadout.BackgroundTransparency = 0.9
+scrollBarLoadout.BackgroundColor3 = Color3.new(1,1,1)
+scrollBarLoadout.BorderSizePixel = 0
+scrollBarLoadout.Size = UDim2.new(0, 17, 1, -36)
+scrollBarLoadout.Position = UDim2.new(0,0,0,18)
+scrollBarLoadout.Parent = backpack.Gear.GearLoadouts.GearLoadoutsScrollingArea
+
+scrollDownLoadout.Position = UDim2.new(0,0,1,-17)
+
+scrollUpLoadout.Parent = backpack.Gear.GearLoadouts.GearLoadoutsScrollingArea
+scrollDownLoadout.Parent = backpack.Gear.GearLoadouts.GearLoadoutsScrollingArea
+
+
+-- Begin Functions
+function removeFromMap(map,object)
+ for i = 1, #map do
+ if map[i] == object then
+ table.remove(map,i)
+ break
+ end
+ end
+end
+
+function robloxLock(instance)
+ instance.RobloxLocked = true
+ children = instance:GetChildren()
+ if children then
+ for i, child in ipairs(children) do
+ robloxLock(child)
+ end
+ end
+end
+
+function resize()
+ local size = 0
+ if gearPreview.AbsoluteSize.Y > gearPreview.AbsoluteSize.X then
+ size = gearPreview.AbsoluteSize.X * 0.75
+ else
+ size = gearPreview.AbsoluteSize.Y * 0.75
+ end
+
+ gearPreview.GearImage.Size = UDim2.new(0,size,0,size)
+ gearPreview.GearImage.Position = UDim2.new(0,gearPreview.AbsoluteSize.X/2 - size/2,0.75,-size)
+
+ resizeGrid()
+end
+
+function addToGrid(child)
+ if not child:IsA("Tool") then
+ if not child:IsA("HopperBin") then
+ return
+ end
+ end
+ if child:FindFirstChild("RobloxBuildTool") then return end
+
+ for i,v in pairs(backpackItems) do -- check to see if we already have this gear registered
+ if v == child then return end
+ end
+
+ table.insert(backpackItems,child)
+
+ local changeCon = child.Changed:connect(function(prop)
+ if prop == "Name" then
+ if buttons[child] then
+ if buttons[child].Image == "" then
+ buttons[child].GearText.Text = child.Name
+ end
+ end
+ end
+ end)
+ local ancestryCon = nil
+ ancestryCon = child.AncestryChanged:connect(function(theChild,theParent)
+ local thisObject = nil
+ for k,v in pairs(backpackItems) do
+ if v == child then
+ thisObject = v
+ break
+ end
+ end
+
+ waitForProperty(player,"Character")
+ waitForChild(player,"Backpack")
+ if (child.Parent ~= player.Backpack and child.Parent ~= player.Character) then
+ if ancestryCon then ancestryCon:disconnect() end
+ if changeCon then changeCon:disconnect() end
+
+ for k,v in pairs(backpackItems) do
+ if v == thisObject then
+ if mouseEnterCons[buttons[v]] then mouseEnterCons[buttons[v]]:disconnect() end
+ if mouseClickCons[buttons[v]] then mouseClickCons[buttons[v]]:disconnect() end
+ buttons[v].Parent = nil
+ buttons[v] = nil
+ break
+ end
+ end
+
+ removeFromMap(backpackItems,thisObject)
+
+ resizeGrid()
+ else
+ resizeGrid()
+ end
+ updateGridActive()
+ end)
+ resizeGrid()
+end
+
+function buttonClick(button)
+ if button:FindFirstChild("UnequipContextMenu") and not button.Active then
+ button.UnequipContextMenu.Visible = true
+ browsingMenu = true
+ end
+end
+
+function previewGear(button)
+ if not browsingMenu then
+ gearPreview.GearImage.Image = button.Image
+ gearPreview.GearStats.GearName.Text = button.GearReference.Value.Name
+ end
+end
+
+function findEmptySlot()
+ local smallestNum = nil
+ local loadout = currentLoadout:GetChildren()
+ for i = 1, #loadout do
+ if loadout[i]:IsA("Frame") and #loadout[i]:GetChildren() <= 0 then
+ local frameNum = tonumber(string.sub(loadout[i].Name,5))
+ if frameNum == 0 then frameNum = 10 end
+ if not smallestNum or (smallestNum > frameNum) then
+ smallestNum = frameNum
+ end
+ end
+ end
+ if smallestNum == 10 then smallestNum = 0 end
+ return smallestNum
+end
+
+function checkForSwap(button,x,y)
+ local loadoutChildren = currentLoadout:GetChildren()
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") and string.find(loadoutChildren[i].Name,"Slot") then
+ if x >= loadoutChildren[i].AbsolutePosition.x and x <= (loadoutChildren[i].AbsolutePosition.x + loadoutChildren[i].AbsoluteSize.x) then
+ if y >= loadoutChildren[i].AbsolutePosition.y and y <= (loadoutChildren[i].AbsolutePosition.y + loadoutChildren[i].AbsoluteSize.y) then
+ local slot = tonumber(string.sub(loadoutChildren[i].Name,5))
+ swapGearSlot(slot,button)
+ return true
+ end
+ end
+ end
+ end
+ return false
+end
+
+function resizeGrid()
+ for k,v in pairs(backpackItems) do
+ if not v:FindFirstChild("RobloxBuildTool") then
+ if not buttons[v] then
+ local buttonClone = gearButton:clone()
+ buttonClone.Parent = grid.ScrollingFrame
+ buttonClone.Visible = true
+ buttonClone.Image = v.TextureId
+ if buttonClone.Image == "" then
+ buttonClone.GearText.Text = v.Name
+ end
+
+ buttonClone.GearReference.Value = v
+ buttonClone.Draggable = true
+ buttons[v] = buttonClone
+
+ local unequipMenu = getGearContextMenu()
+
+ unequipMenu.Visible = false
+ unequipMenu.Parent = buttonClone
+
+ local beginPos = nil
+ buttonClone.DragBegin:connect(function(value)
+ buttonClone.ZIndex = 9
+ beginPos = value
+ end)
+ buttonClone.DragStopped:connect(function(x,y)
+ buttonClone.ZIndex = 1
+ if beginPos ~= buttonClone.Position then
+ if not checkForSwap(buttonClone,x,y) then
+ buttonClone:TweenPosition(beginPos,Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.5, true)
+ buttonClone.Draggable = false
+ delay(0.5,function()
+ buttonClone.Draggable = true
+ end)
+ else
+ buttonClone.Position = beginPos
+ end
+ end
+ end)
+ local clickTime = tick()
+ mouseEnterCons[buttonClone] = buttonClone.MouseEnter:connect(function() previewGear(buttonClone) end)
+ mouseClickCons[buttonClone] = buttonClone.MouseButton1Click:connect(function()
+ local newClickTime = tick()
+ if buttonClone.Active and (newClickTime - clickTime) < 0.5 then
+ local slot = findEmptySlot()
+ if slot then
+ buttonClone.ZIndex = 1
+ swapGearSlot(slot,buttonClone)
+ end
+ else
+ buttonClick(buttonClone)
+ end
+ clickTime = newClickTime
+ end)
+ end
+ end
+ end
+ recalculateScroll()
+end
+
+function showPartialGrid(subset)
+
+ resetFrame.Visible = true
+
+ for k,v in pairs(buttons) do
+ v.Parent = nil
+ end
+ for k,v in pairs(subset) do
+ v.Parent = grid.ScrollingFrame
+ end
+ recalculateScroll()
+end
+
+function showEntireGrid()
+ resetFrame.Visible = false
+
+ for k,v in pairs(buttons) do
+ v.Parent = grid.ScrollingFrame
+ end
+ recalculateScroll()
+end
+
+function inLoadout(gear)
+ local children = currentLoadout:GetChildren()
+ for i = 1, #children do
+ if children[i]:IsA("Frame") then
+ local button = children[i]:GetChildren()
+ if #button > 0 then
+ if button[1].GearReference.Value and button[1].GearReference.Value == gear then
+ return true
+ end
+ end
+ end
+ end
+ return false
+end
+
+function updateGridActive()
+ for k,v in pairs(backpackItems) do
+ if buttons[v] then
+ local gear = nil
+ local gearRef = buttons[v]:FindFirstChild("GearReference")
+
+ if gearRef then gear = gearRef.Value end
+
+ if not gear then
+ buttons[v].Active = false
+ elseif inLoadout(gear) then
+ buttons[v].Active = false
+ else
+ buttons[v].Active = true
+ end
+ end
+ end
+end
+
+function centerGear(loadoutChildren)
+ local gearButtons = {}
+ local lastSlotAdd = nil
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") and #loadoutChildren[i]:GetChildren() > 0 then
+ if loadoutChildren[i].Name == "Slot0" then
+ lastSlotAdd = loadoutChildren[i]
+ else
+ table.insert(gearButtons, loadoutChildren[i])
+ end
+ end
+ end
+ if lastSlotAdd then table.insert(gearButtons,lastSlotAdd) end
+
+ local startPos = ( 1 - (#gearButtons * 0.1) ) / 2
+ for i = 1, #gearButtons do
+ gearButtons[i]:TweenPosition(UDim2.new(startPos + ((i - 1) * 0.1),0,0,0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25, true)
+ end
+end
+
+function spreadOutGear(loadoutChildren)
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") then
+ local slot = tonumber(string.sub(loadoutChildren[i].Name,5))
+ if slot == 0 then slot = 10 end
+ loadoutChildren[i]:TweenPosition(UDim2.new((slot - 1)/10,0,0,0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25, true)
+ end
+ end
+end
+
+function openCloseBackpack(close)
+ if openCloseDebounce then return end
+ openCloseDebounce = true
+
+ local visible = not backpack.Visible
+ if visible and not close then
+ updateGridActive()
+ local centerDialogSupported, msg = pcall(function() game.GuiService:AddCenterDialog(backpack, Enum.CenterDialogType.PlayerInitiatedDialog,
+ function()
+ backpack.Visible = true
+ loadoutChildren = currentLoadout:GetChildren()
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") then
+ loadoutChildren[i].BackgroundTransparency = 0.5
+ end
+ end
+ spreadOutGear(loadoutChildren)
+ end,
+ function()
+ backpack.Visible = false
+ end)
+ end)
+ backpackButton.Selected = true
+ backpack:TweenSizeAndPosition(UDim2.new(0.55, 0, 0.6, 0),UDim2.new(0.225, 0, 0.2, 0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, guiTweenSpeed/2, true)
+ delay(guiTweenSpeed/2 + 0.01,
+ function()
+ local children = backpack:GetChildren()
+ for i = 1, #children do
+ if children[i]:IsA("Frame") then
+ children[i].Visible = true
+ end
+ end
+ resizeGrid()
+ resize()
+ openCloseDebounce = false
+ end)
+ else
+ backpackButton.Selected = false
+ local children = backpack:GetChildren()
+ for i = 1, #children do
+ if children[i]:IsA("Frame") then
+ children[i].Visible = false
+ end
+ end
+ loadoutChildren = currentLoadout:GetChildren()
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") then
+ loadoutChildren[i].BackgroundTransparency = 1
+ end
+ end
+ centerGear(loadoutChildren)
+
+ backpack:TweenSizeAndPosition(UDim2.new(0,0,0,0),UDim2.new(0.5,0,0.5,0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, guiTweenSpeed/2, true)
+ delay(guiTweenSpeed/2 + 0.01,
+ function()
+ backpack.Visible = visible
+ resizeGrid()
+ resize()
+ pcall(function() game.GuiService:RemoveCenterDialog(backpack) end)
+ openCloseDebounce = false
+ end)
+ end
+end
+
+function loadoutCheck(child, selectState)
+ if not child:IsA("ImageButton") then return end
+ for k,v in pairs(backpackItems) do
+ if buttons[v] then
+ if child:FindFirstChild("GearReference") and buttons[v]:FindFirstChild("GearReference") then
+ if buttons[v].GearReference.Value == child.GearReference.Value then
+ buttons[v].Active = selectState
+ break
+ end
+ end
+ end
+ end
+end
+
+function clearPreview()
+ gearPreview.GearImage.Image = ""
+ gearPreview.GearStats.GearName.Text = ""
+end
+
+function removeAllEquippedGear(physGear)
+ local stuff = player.Character:GetChildren()
+ for i = 1, #stuff do
+ if ( stuff[i]:IsA("Tool") or stuff[i]:IsA("HopperBin") ) and stuff[i] ~= physGear then
+ stuff[i].Parent = playerBackpack
+ end
+ end
+end
+
+function equipGear(physGear)
+ removeAllEquippedGear(physGear)
+ physGear.Parent = player.Character
+ updateGridActive()
+end
+
+function unequipGear(physGear)
+ physGear.Parent = playerBackpack
+ updateGridActive()
+end
+
+function highlight(button)
+ button.TextColor3 = Color3.new(0,0,0)
+ button.BackgroundColor3 = Color3.new(0.8,0.8,0.8)
+end
+function clearHighlight(button)
+ button.TextColor3 = Color3.new(1,1,1)
+ button.BackgroundColor3 = Color3.new(0,0,0)
+end
+
+function swapGearSlot(slot,gearButton)
+ if not swapSlot.Value then -- signal loadout to swap a gear out
+ swapSlot.Slot.Value = slot
+ swapSlot.GearButton.Value = gearButton
+ swapSlot.Value = true
+ updateGridActive()
+ end
+end
+
+
+local UnequipGearMenuClick = function(element, menu)
+ if type(element.Action) ~= "number" then return end
+ local num = element.Action
+ if num == 1 then -- remove from loadout
+ unequipGear(menu.Parent.GearReference.Value)
+ local inventoryButton = menu.Parent
+ local gearToUnequip = inventoryButton.GearReference.Value
+ local loadoutChildren = currentLoadout:GetChildren()
+ local slot = -1
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") then
+ local button = loadoutChildren[i]:GetChildren()
+ if button[1] and button[1].GearReference.Value == gearToUnequip then
+ slot = button[1].SlotNumber.Text
+ break
+ end
+ end
+ end
+ swapGearSlot(slot,nil)
+ end
+end
+
+-- these next two functions are used to stop any use of backpack while the player is dead (can cause issues)
+function activateBackpack()
+ backpack.Visible = backpackOldStateVisible
+
+ loadoutChildren = currentLoadout:GetChildren()
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") then
+ loadoutChildren[i].BackgroundTransparency = 1
+ end
+ end
+
+ backpackButtonClickCon = backpackButton.MouseButton1Click:connect(function() openCloseBackpack() end)
+ guiServiceKeyPressCon = game:GetService("GuiService").KeyPressed:connect(function(key)
+ if key == tilde or key == backquote then
+ openCloseBackpack()
+ end
+ end)
+end
+function deactivateBackpack()
+ if backpackButtonClickCon then backpackButtonClickCon:disconnect() end
+ if guiServiceKeyPressCon then guiServiceKeyPressCon:disconnect() end
+
+ backpackOldStateVisible = backpack.Visible
+ backpack.Visible = false
+ openCloseBackpack(true)
+end
+
+function setupCharacterConnections()
+
+ if backpackAddCon then backpackAddCon:disconnect() end
+ backpackAddCon = game.Players.LocalPlayer.Backpack.ChildAdded:connect(function(child) addToGrid(child) end)
+
+ -- make sure we get all the children
+ local backpackChildren = game.Players.LocalPlayer.Backpack:GetChildren()
+ for i = 1, #backpackChildren do
+ addToGrid(backpackChildren[i])
+ end
+
+ if characterChildAddedCon then characterChildAddedCon:disconnect() end
+ characterChildAddedCon =
+ game.Players.LocalPlayer.Character.ChildAdded:connect(function(child)
+ addToGrid(child)
+ updateGridActive()
+ end)
+
+ if characterChildRemovedCon then characterChildRemovedCon:disconnect() end
+ characterChildRemovedCon =
+ game.Players.LocalPlayer.Character.ChildRemoved:connect(function(child)
+ updateGridActive()
+ end)
+
+
+ if humanoidDiedCon then humanoidDiedCon:disconnect() end
+ local localPlayer = game.Players.LocalPlayer
+ waitForProperty(localPlayer,"Character")
+ waitForChild(localPlayer.Character,"Humanoid")
+ humanoidDiedCon = game.Players.LocalPlayer.Character.Humanoid.Died:connect(function() deactivateBackpack() end)
+
+ activateBackpack()
+
+ wait()
+ centerGear(currentLoadout:GetChildren())
+end
+
+function removeCharacterConnections()
+ if characterChildAddedCon then characterChildAddedCon:disconnect() end
+ if characterChildRemovedCon then characterChildRemovedCon:disconnect() end
+ if backpackAddCon then backpackAddCon:disconnect() end
+end
+
+function trim(s)
+ return (s:gsub("^%s*(.-)%s*$", "%1"))
+end
+
+function splitByWhiteSpace(text)
+ if type(text) ~= "string" then return nil end
+
+ local terms = {}
+ for token in string.gmatch(text, "[^%s]+") do
+ if string.len(token) > 2 then
+ table.insert(terms,token)
+ end
+ end
+ return terms
+end
+
+function filterGear(searchTerm)
+ string.lower(searchTerm)
+ searchTerm = trim(searchTerm)
+ if string.len(searchTerm) < 2 then return nil end
+ local terms = splitByWhiteSpace(searchTerm)
+
+ local filteredGear = {}
+ for k,v in pairs(backpackItems) do
+ if buttons[v] then
+ local gearString = string.lower(buttons[v].GearReference.Value.Name)
+ gearString = trim(gearString)
+ for i = 1, #terms do
+ if string.match(gearString,terms[i]) then
+ table.insert(filteredGear,buttons[v])
+ break
+ end
+ end
+ end
+ end
+
+ return filteredGear
+end
+
+
+function showSearchGear()
+ local searchText = searchBox.Text
+ searchBox.Text = "Search..."
+ local filteredButtons = filterGear(searchText)
+ if filteredButtons and #filteredButtons > 0 then
+ showPartialGrid(filteredButtons)
+ else
+ showEntireGrid()
+ end
+end
+
+function nukeBackpack()
+ while #buttons > 0 do
+ table.remove(buttons)
+ end
+ buttons = {}
+ while #backpackItems > 0 do
+ table.remove(backpackItems)
+ end
+ backpackItems = {}
+ local scrollingFrameChildren = grid.ScrollingFrame:GetChildren()
+ for i = 1, #scrollingFrameChildren do
+ scrollingFrameChildren[i]:remove()
+ end
+end
+
+function getGearContextMenu()
+ local gearContextMenu = Instance.new("Frame")
+ gearContextMenu.Active = true
+ gearContextMenu.Name = "UnequipContextMenu"
+ gearContextMenu.Size = UDim2.new(0,115,0,70)
+ gearContextMenu.Position = UDim2.new(0,-16,0,-16)
+ gearContextMenu.BackgroundTransparency = 1
+ gearContextMenu.Visible = false
+
+ local gearContextMenuButton = Instance.new("TextButton")
+ gearContextMenuButton.Name = "UnequipContextMenuButton"
+ gearContextMenuButton.Text = ""
+ gearContextMenuButton.Style = Enum.ButtonStyle.RobloxButtonDefault
+ gearContextMenuButton.ZIndex = 8
+ gearContextMenuButton.Size = UDim2.new(1, 0, 1, -20)
+ gearContextMenuButton.Visible = true
+ gearContextMenuButton.Parent = gearContextMenu
+
+ local elementHeight = 12
+
+ local contextMenuElements = {}
+ local contextMenuElementsName = {"Remove Hotkey"}
+
+ for i = 1, #contextMenuElementsName do
+ local element = {}
+ element.Type = "Button"
+ element.Text = contextMenuElementsName[i]
+ element.Action = i
+ element.DoIt = UnequipGearMenuClick
+ table.insert(contextMenuElements,element)
+ end
+
+ for i, contextElement in ipairs(contextMenuElements) do
+ local element = contextElement
+ if element.Type == "Button" then
+ local button = Instance.new("TextButton")
+ button.Name = "UnequipContextButton" .. i
+ button.BackgroundColor3 = Color3.new(0,0,0)
+ button.BorderSizePixel = 0
+ button.TextXAlignment = Enum.TextXAlignment.Left
+ button.Text = " " .. contextElement.Text
+ button.Font = Enum.Font.Arial
+ button.FontSize = Enum.FontSize.Size14
+ button.Size = UDim2.new(1, 8, 0, elementHeight)
+ button.Position = UDim2.new(0,0,0,elementHeight * i)
+ button.TextColor3 = Color3.new(1,1,1)
+ button.ZIndex = 9
+ button.Parent = gearContextMenuButton
+
+ button.MouseButton1Click:connect(function()
+ if button.Active and not gearContextMenu.Parent.Active then
+ local success, result = pcall(function() element.DoIt(element, gearContextMenu) end)
+ browsingMenu = false
+ gearContextMenu.Visible = false
+ clearHighlight(button)
+ clearPreview()
+ end
+ end)
+
+ button.MouseEnter:connect(function()
+ if button.Active and gearContextMenu.Parent.Active then
+ highlight(button)
+ end
+ end)
+ button.MouseLeave:connect(function()
+ if button.Active and gearContextMenu.Parent.Active then
+ clearHighlight(button)
+ end
+ end)
+
+ contextElement.Button = button
+ contextElement.Element = button
+ elseif element.Type == "Label" then
+ local frame = Instance.new("Frame")
+ frame.Name = "ContextLabel" .. i
+ frame.BackgroundTransparency = 1
+ frame.Size = UDim2.new(1, 8, 0, elementHeight)
+
+ local label = Instance.new("TextLabel")
+ label.Name = "Text1"
+ label.BackgroundTransparency = 1
+ label.BackgroundColor3 = Color3.new(1,1,1)
+ label.BorderSizePixel = 0
+ label.TextXAlignment = Enum.TextXAlignment.Left
+ label.Font = Enum.Font.ArialBold
+ label.FontSize = Enum.FontSize.Size14
+ label.Position = UDim2.new(0.0, 0, 0, 0)
+ label.Size = UDim2.new(0.5, 0, 1, 0)
+ label.TextColor3 = Color3.new(1,1,1)
+ label.ZIndex = 9
+ label.Parent = frame
+ element.Label1 = label
+
+ if element.GetText2 then
+ label = Instance.new("TextLabel")
+ label.Name = "Text2"
+ label.BackgroundTransparency = 1
+ label.BackgroundColor3 = Color3.new(1,1,1)
+ label.BorderSizePixel = 0
+ label.TextXAlignment = Enum.TextXAlignment.Right
+ label.Font = Enum.Font.Arial
+ label.FontSize = Enum.FontSize.Size14
+ label.Position = UDim2.new(0.5, 0, 0, 0)
+ label.Size = UDim2.new(0.5, 0, 1, 0)
+ label.TextColor3 = Color3.new(1,1,1)
+ label.ZIndex = 9
+ label.Parent = frame
+ element.Label2 = label
+ end
+ frame.Parent = gearContextMenuButton
+ element.Label = frame
+ element.Element = frame
+ end
+ end
+
+ gearContextMenu.ZIndex = 4
+ gearContextMenu.MouseLeave:connect(function()
+ browsingMenu = false
+ gearContextMenu.Visible = false
+ clearPreview()
+ end)
+ robloxLock(gearContextMenu)
+
+ return gearContextMenu
+end
+
+local backpackChildren = player.Backpack:GetChildren()
+for i = 1, #backpackChildren do
+ addToGrid(backpackChildren[i])
+end
+
+------------------------- Start Lifelong Connections -----------------------
+screen.Changed:connect(function(prop)
+ if prop == "AbsoluteSize" then
+ if debounce then return end
+ debounce = true
+ wait()
+ resize()
+ resizeGrid()
+ debounce = false
+ end
+end)
+
+currentLoadout.ChildAdded:connect(function(child) loadoutCheck(child, false) end)
+currentLoadout.ChildRemoved:connect(function(child) loadoutCheck(child, true) end)
+
+currentLoadout.DescendantAdded:connect(function(descendant)
+ if not backpack.Visible and ( descendant:IsA("ImageButton") or descendant:IsA("TextButton") ) then
+ centerGear(currentLoadout:GetChildren())
+ end
+end)
+currentLoadout.DescendantRemoving:connect(function(descendant)
+ if not backpack.Visible and ( descendant:IsA("ImageButton") or descendant:IsA("TextButton") ) then
+ wait()
+ centerGear(currentLoadout:GetChildren())
+ end
+end)
+
+grid.MouseEnter:connect(function() clearPreview() end)
+grid.MouseLeave:connect(function() clearPreview() end)
+
+player.CharacterRemoving:connect(function()
+ removeCharacterConnections()
+ nukeBackpack()
+end)
+player.CharacterAdded:connect(function() setupCharacterConnections() end)
+
+player.ChildAdded:connect(function(child)
+ if child:IsA("Backpack") then
+ playerBackpack = child
+ if backpackAddCon then backpackAddCon:disconnect() end
+ backpackAddCon = game.Players.LocalPlayer.Backpack.ChildAdded:connect(function(child) addToGrid(child) end)
+ end
+end)
+
+swapSlot.Changed:connect(function()
+ if not swapSlot.Value then
+ updateGridActive()
+ end
+end)
+
+searchBox.FocusLost:connect(function(enterPressed)
+ if enterPressed then
+ showSearchGear()
+ end
+end)
+
+local loadoutChildren = currentLoadout:GetChildren()
+for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") and string.find(loadoutChildren[i].Name,"Slot") then
+ loadoutChildren[i].ChildRemoved:connect(function()
+ updateGridActive()
+ end)
+ loadoutChildren[i].ChildAdded:connect(function()
+ updateGridActive()
+ end)
+ end
+end
+
+pcall(function() closeButton.Modal = true end)
+closeButton.MouseButton1Click:connect(function() openCloseBackpack() end)
+
+searchButton.MouseButton1Click:connect(function() showSearchGear() end)
+resetButton.MouseButton1Click:connect(function() showEntireGrid() end)
+------------------------- End Lifelong Connections -----------------------
+
+resize()
+resizeGrid()
+
+-- make sure any items in the loadout are accounted for in inventory
+local loadoutChildren = currentLoadout:GetChildren()
+for i = 1, #loadoutChildren do
+ loadoutCheck(loadoutChildren[i], false)
+end
+if not backpack.Visible then centerGear(currentLoadout:GetChildren()) end
+
+-- make sure that inventory is listening to gear reparenting
+if characterChildAddedCon == nil and game.Players.LocalPlayer["Character"] then
+ setupCharacterConnections()
+end
+if not backpackAddCon then
+ backpackAddCon = game.Players.LocalPlayer.Backpack.ChildAdded:connect(function(child) addToGrid(child) end)
+end
+
+-- flip it on if we are good
+if game.CoreGui.Version >= 3 then
+ backpackButton.Visible = true
+end
+
+recalculateScrollLoadout()
+
+
diff --git a/app/files/CoreGui/53878057 b/app/files/CoreGui/53878057
new file mode 100644
index 0000000..f74cba0
--- /dev/null
+++ b/app/files/CoreGui/53878057
@@ -0,0 +1,1098 @@
+if game.CoreGui.Version < 3 then return end -- peace out if we aren't using the right client
+
+-- A couple of necessary functions
+local function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+ return instance:FindFirstChild(name)
+end
+local function waitForProperty(instance, property)
+ while not instance[property] do
+ instance.Changed:wait()
+ end
+end
+
+local useCoreHealthBar = false
+pcall(function() useCoreHealthBar = Game.Players:GetUseCoreScriptHealthBar() end)
+
+
+local currentLoadout = script.Parent
+local StaticTabName = "gear"
+local backpackEnabled = true
+
+local robloxGui = game:GetService("CoreGui"):FindFirstChild("RobloxGui")
+assert(robloxGui)
+local controlFrame = waitForChild(robloxGui, 'ControlFrame')
+local backpackButton = waitForChild(controlFrame, 'BackpackButton')
+local backpack = waitForChild(robloxGui, 'Backpack')
+waitForChild(robloxGui,"CurrentLoadout")
+waitForChild(robloxGui.CurrentLoadout,"TempSlot")
+waitForChild(robloxGui.CurrentLoadout.TempSlot,"SlotNumber")
+
+waitForChild(currentLoadout, 'Background')
+local clBackground = currentLoadout.Background
+
+local function IsTouchDevice()
+ return Game:GetService('UserInputService').TouchEnabled
+end
+
+local function IsSmallScreen()
+ return (robloxGui.AbsoluteSize.Y <= 500)
+end
+
+local function moveHealthBar(pGui)
+ waitForChild(pGui, 'HealthGUI')
+ waitForChild(pGui['HealthGUI'], 'tray')
+ local tray = pGui['HealthGUI']['tray']
+ tray.Position = UDim2.new(0.5, -85, 1, -26)
+end
+
+local function setHealthBarVisible( pGui, visible )
+ waitForChild(pGui, 'HealthGUI')
+ waitForChild(pGui['HealthGUI'], 'tray')
+ local tray = pGui['HealthGUI']['tray']
+ tray.Visible = visible
+end
+
+
+--- Begin Locals
+waitForChild(game,"Players")
+waitForProperty(game.Players,"LocalPlayer")
+local player = game.Players.LocalPlayer
+
+if not useCoreHealthBar then
+ waitForChild(player, 'PlayerGui')
+ Spawn(function()
+ moveHealthBar(player.PlayerGui)
+ end)
+end
+
+while player.Character == nil do wait(0.03) end
+local humanoid = waitForChild(player.Character, 'Humanoid')
+humanoid.Died:connect(function()
+ backpackButton.Visible = false
+end)
+
+waitForChild(game, "LocalBackpack")
+game.LocalBackpack:SetOldSchoolBackpack(false)
+
+waitForChild(currentLoadout.Parent,"Backpack")
+local guiBackpack = currentLoadout.Parent.Backpack
+
+local backpackManager = waitForChild(guiBackpack,"CoreScripts/BackpackScripts/BackpackManager")
+local backpackOpenEvent = waitForChild(backpackManager,"BackpackOpenEvent")
+local backpackCloseEvent = waitForChild(backpackManager,"BackpackCloseEvent")
+local tabClickedEvent = waitForChild(backpackManager,"TabClickedEvent")
+local resizeEvent = waitForChild(backpackManager,"ResizeEvent")
+
+local inGearTab = true
+
+local maxNumLoadoutItems = 10
+if IsSmallScreen() then
+ maxNumLoadoutItems = 4
+end
+
+
+local characterChildAddedCon = nil
+local backpackChildCon = nil
+
+local debounce = false
+
+local enlargeFactor = 1.18
+local buttonSizeEnlarge = UDim2.new(1 * enlargeFactor,0,1 * enlargeFactor,0)
+local buttonSizeNormal = UDim2.new(1,0,1,0)
+local enlargeOverride = true
+local guiTweenSpeed = 0.5
+
+local firstInstanceOfLoadout = false
+
+local inventory = {}
+
+local gearSlots = {}
+for i = 1, maxNumLoadoutItems do
+ gearSlots[i] = "empty"
+end
+
+local backpackWasOpened = false
+--- End Locals
+
+
+
+
+
+
+-- Begin Functions
+local function backpackIsOpen()
+ if guiBackpack then
+ return guiBackpack.Visible
+ end
+ return false
+end
+
+
+local function kill(prop,con,gear)
+ if con then con:disconnect() end
+ if prop == true and gear then
+ reorganizeLoadout(gear,false)
+ end
+end
+
+function registerNumberKeys()
+ for i = 0, 9 do
+ game:GetService("GuiService"):AddKey(tostring(i))
+ end
+end
+
+function unregisterNumberKeys()
+ for i = 0, 9 do
+ game:GetService("GuiService"):RemoveKey(tostring(i))
+ end
+end
+
+function characterInWorkspace()
+ if game.Players["LocalPlayer"] then
+ if game.Players.LocalPlayer["Character"] then
+ if game.Players.LocalPlayer.Character ~= nil then
+ if game.Players.LocalPlayer.Character.Parent ~= nil then
+ return true
+ end
+ end
+ end
+ end
+
+ return false
+end
+
+function removeGear(gear)
+ local emptySlot = nil
+ for i = 1, #gearSlots do
+ if gearSlots[i] == gear and gear.Parent ~= nil then
+ emptySlot = i
+ break
+ end
+ end
+ if emptySlot then
+ if gearSlots[emptySlot].GearReference.Value then
+ if gearSlots[emptySlot].GearReference.Value.Parent == game.Players.LocalPlayer.Character then -- if we currently have this equipped, unequip it
+ gearSlots[emptySlot].GearReference.Value.Parent = game.Players.LocalPlayer.Backpack
+ end
+
+ if gearSlots[emptySlot].GearReference.Value:IsA("HopperBin") and gearSlots[emptySlot].GearReference.Value.Active then -- this is an active hopperbin
+ gearSlots[emptySlot].GearReference.Value:Disable()
+ gearSlots[emptySlot].GearReference.Value.Active = false
+ end
+ end
+
+ gearSlots[emptySlot] = "empty"
+
+ local centerizeX = gear.Size.X.Scale/2
+ local centerizeY = gear.Size.Y.Scale/2
+ --[[gear:TweenSizeAndPosition(UDim2.new(0,0,0,0),
+ UDim2.new(gear.Position.X.Scale + centerizeX,gear.Position.X.Offset,gear.Position.Y.Scale + centerizeY,gear.Position.Y.Offset),
+ Enum.EasingDirection.Out, Enum.EasingStyle.Quad,guiTweenSpeed/4,true)]]
+ delay(0,
+ function()
+ gear:remove()
+ end)
+
+ Spawn(function()
+ while backpackIsOpen() do wait(0.03) end
+ waitForChild(player, 'Backpack')
+ local allEmpty = true
+ for i = 1, #gearSlots do
+ if gearSlots[i] ~= 'empty' then
+ allEmpty = false
+ end
+ end
+
+ if allEmpty then
+ if #player.Backpack:GetChildren() < 1 then
+ backpackButton.Visible = false
+ else
+ backpackButton.Position = UDim2.new(0.5, -60, 1, -44)
+ end
+ clBackground.Visible = false
+ end
+ end)
+ end
+end
+
+function insertGear(gear, addToSlot)
+ local pos = nil
+ if not addToSlot then
+ for i = 1, #gearSlots do
+ if gearSlots[i] == "empty" then
+ pos = i
+ break
+ end
+ end
+
+ if pos == 1 and gearSlots[1] ~= "empty" then gear:remove() return end -- we are currently full, can't add in
+ else
+ pos = addToSlot
+ -- push all gear down one slot
+ local start = 1
+ for i = 1, #gearSlots do
+ if gearSlots[i] == "empty" then
+ start = i
+ break
+ end
+ end
+ for i = start, pos + 1, -1 do
+ gearSlots[i] = gearSlots[i - 1]
+ if i == 10 then
+ gearSlots[i].SlotNumber.Text = "0"
+ gearSlots[i].SlotNumberDownShadow.Text = "0"
+ gearSlots[i].SlotNumberUpShadow.Text = "0"
+ else
+ gearSlots[i].SlotNumber.Text = i
+ gearSlots[i].SlotNumberDownShadow.Text = i
+ gearSlots[i].SlotNumberUpShadow.Text = i
+ end
+ end
+ end
+
+ gearSlots[pos] = gear
+ if pos ~= maxNumLoadoutItems then
+ if(type(tostring(pos)) == "string") then
+ local posString = tostring(pos)
+ gear.SlotNumber.Text = posString
+ gear.SlotNumberDownShadow.Text = posString
+ gear.SlotNumberUpShadow.Text = posString
+ end
+ else -- tenth gear doesn't follow mathematical pattern :(
+ gear.SlotNumber.Text = "0"
+ gear.SlotNumberDownShadow.Text = "0"
+ gear.SlotNumberUpShadow.Text = "0"
+ end
+ gear.Visible = true
+
+ local con = nil
+ con = gear.Kill.Changed:connect(function(prop) kill(prop,con,gear) end)
+end
+
+
+function reorganizeLoadout(gear, inserting, equipped, addToSlot)
+ if inserting then -- add in gear
+ insertGear(gear, addToSlot)
+ else
+ removeGear(gear)
+ end
+ if gear ~= "empty" then gear.ZIndex = 1 end
+end
+
+function checkToolAncestry(child,parent)
+ if child:FindFirstChild("RobloxBuildTool") then return end -- don't show roblox build tools
+ if child:IsA("Tool") or child:IsA("HopperBin") then
+ for i = 1, #gearSlots do
+ if gearSlots[i] ~= "empty" and gearSlots[i].GearReference.Value == child then
+ if parent == nil then
+ gearSlots[i].Kill.Value = true
+ return false
+ elseif child.Parent == player.Character then
+ gearSlots[i].Selected = true
+ return true
+ elseif child.Parent == player.Backpack then
+ if child:IsA("Tool") or child:IsA("HopperBin") then gearSlots[i].Selected = false end
+ return true
+ else
+ gearSlots[i].Kill.Value = true
+ return false
+ end
+ return true
+ end
+ end
+ end
+end
+
+function removeAllEquippedGear(physGear)
+ local stuff = player.Character:GetChildren()
+ for i = 1, #stuff do
+ if ( stuff[i]:IsA("Tool") or stuff[i]:IsA("HopperBin") ) and stuff[i] ~= physGear then
+ if stuff[i]:IsA("Tool") then stuff[i].Parent = player.Backpack end
+ if stuff[i]:IsA("HopperBin") then
+ stuff[i]:Disable()
+ end
+ end
+ end
+end
+
+function hopperBinSwitcher(numKey, physGear)
+ if not physGear then return end
+
+ physGear:ToggleSelect()
+
+ if gearSlots[numKey] == "empty" then return end
+
+ if not physGear.Active then
+ gearSlots[numKey].Selected = false
+ normalizeButton(gearSlots[numKey])
+ else
+ gearSlots[numKey].Selected = true
+ enlargeButton(gearSlots[numKey])
+ end
+end
+
+function toolSwitcher(numKey)
+
+ if not gearSlots[numKey] then return end
+ local physGear = gearSlots[numKey].GearReference.Value
+ if physGear == nil then return end
+
+ removeAllEquippedGear(physGear) -- we don't remove this gear, as then we get a double switcheroo
+
+ local key = numKey
+ if numKey == 0 then key = 10 end
+
+ for i = 1, #gearSlots do
+ if gearSlots[i] and gearSlots[i] ~= "empty" and i ~= key then
+ normalizeButton(gearSlots[i])
+ gearSlots[i].Selected = false
+ if gearSlots[i].GearReference and gearSlots[i].GearReference.Value and gearSlots[i].GearReference.Value:IsA("HopperBin") and gearSlots[i].GearReference.Value.Active then
+ gearSlots[i].GearReference.Value:ToggleSelect()
+ end
+ end
+ end
+
+ if physGear:IsA("HopperBin") then
+ hopperBinSwitcher(numKey,physGear)
+ else
+ if physGear.Parent == player.Character then
+ physGear.Parent = player.Backpack
+
+ if gearSlots[numKey] ~= "empty" then
+ gearSlots[numKey].Selected = false
+ normalizeButton(gearSlots[numKey])
+ end
+ else
+ --player.Character.Humanoid:EquipTool(physGear)
+
+ physGear.Parent = player.Character
+ gearSlots[numKey].Selected = true
+
+ enlargeButton(gearSlots[numKey])
+ end
+ end
+end
+
+
+function activateGear(num)
+ local numKey = nil
+ if num == "0" then
+ numKey = 10 -- why do lua indexes have to start at 1? :(
+ else
+ numKey = tonumber(num)
+ end
+
+ if(numKey == nil) then return end
+
+ if gearSlots[numKey] ~= "empty" then
+ toolSwitcher(numKey)
+ end
+end
+
+
+enlargeButton = function(button)
+ if button.Size.Y.Scale > 1 then return end
+ if not button.Parent then return end
+ if not button.Selected then return end
+
+ for i = 1, #gearSlots do
+ if gearSlots[i] == "empty" then break end
+ if gearSlots[i] ~= button then
+ normalizeButton(gearSlots[i])
+ end
+ end
+
+ if not enlargeOverride then
+ return
+ end
+
+ if button:FindFirstChild('Highlight') then
+ button.Highlight.Visible = true
+ end
+
+ if button:IsA("ImageButton") or button:IsA("TextButton") then
+ button.ZIndex = 5
+ local centerizeX = -(buttonSizeEnlarge.X.Scale - button.Size.X.Scale)/2
+ local centerizeY = -(buttonSizeEnlarge.Y.Scale - button.Size.Y.Scale)/2
+ button:TweenSizeAndPosition(buttonSizeEnlarge,
+ UDim2.new(button.Position.X.Scale + centerizeX,button.Position.X.Offset,button.Position.Y.Scale + centerizeY,button.Position.Y.Offset),
+ Enum.EasingDirection.Out, Enum.EasingStyle.Quad,guiTweenSpeed/5,enlargeOverride)
+ end
+end
+
+normalizeAllButtons = function()
+ for i = 1, #gearSlots do
+ if gearSlots[i] == "empty" then break end
+ if gearSlots[i] ~= button then
+ normalizeButton(gearSlots[i],0.1)
+ end
+ end
+end
+
+
+normalizeButton = function(button, speed)
+ if not button then return end
+ if button.Size.Y.Scale <= 1 then return end
+ if button.Selected then return end
+ if not button.Parent then return end
+
+ local moveSpeed = speed
+ if moveSpeed == nil or type(moveSpeed) ~= "number" then moveSpeed = guiTweenSpeed/5 end
+
+ if button:FindFirstChild('Highlight') then
+ button.Highlight.Visible = false
+ end
+
+ if button:IsA("ImageButton") or button:IsA("TextButton") then
+ button.ZIndex = 1
+ local inverseEnlarge = 1/enlargeFactor
+ local centerizeX = -(buttonSizeNormal.X.Scale - button.Size.X.Scale)/2
+ local centerizeY = -(buttonSizeNormal.Y.Scale - button.Size.Y.Scale)/2
+ button:TweenSizeAndPosition(buttonSizeNormal,
+ UDim2.new(button.Position.X.Scale + centerizeX,button.Position.X.Offset,button.Position.Y.Scale + centerizeY,button.Position.Y.Offset),
+ Enum.EasingDirection.Out, Enum.EasingStyle.Quad,moveSpeed,enlargeOverride)
+ end
+end
+
+local waitForDebounce = function()
+ while debounce do
+ wait()
+ end
+end
+
+function pointInRectangle(point,rectTopLeft,rectSize)
+ if point.x > rectTopLeft.x and point.x < (rectTopLeft.x + rectSize.x) then
+ if point.y > rectTopLeft.y and point.y < (rectTopLeft.y + rectSize.y) then
+ return true
+ end
+ end
+ return false
+end
+
+function swapGear(gearClone,toFrame)
+ local toFrameChildren = toFrame:GetChildren()
+ if #toFrameChildren == 1 then
+ if toFrameChildren[1]:FindFirstChild("SlotNumber") then
+
+ local toSlot = tonumber(toFrameChildren[1].SlotNumber.Text)
+ local gearCloneSlot = tonumber(gearClone.SlotNumber.Text)
+ if toSlot == 0 then toSlot = 10 end
+ if gearCloneSlot == 0 then gearCloneSlot = 10 end
+
+ gearSlots[toSlot] = gearClone
+ gearSlots[gearCloneSlot] = toFrameChildren[1]
+
+ toFrameChildren[1].SlotNumber.Text = gearClone.SlotNumber.Text
+ toFrameChildren[1].SlotNumberDownShadow.Text = gearClone.SlotNumber.Text
+ toFrameChildren[1].SlotNumberUpShadow.Text = gearClone.SlotNumber.Text
+
+ local subString = string.sub(toFrame.Name,5)
+ gearClone.SlotNumber.Text = subString
+ gearClone.SlotNumberDownShadow.Text = subString
+ gearClone.SlotNumberUpShadow.Text = subString
+
+ gearClone.Position = UDim2.new(gearClone.Position.X.Scale,0,gearClone.Position.Y.Scale,0)
+ toFrameChildren[1].Position = UDim2.new(toFrameChildren[1].Position.X.Scale,0,toFrameChildren[1].Position.Y.Scale,0)
+
+ toFrameChildren[1].Parent = gearClone.Parent
+ gearClone.Parent = toFrame
+ end
+ else
+ local slotNum = tonumber(gearClone.SlotNumber.Text)
+ if slotNum == 0 then slotNum = 10 end
+ gearSlots[slotNum] = "empty" -- reset this gear slot
+
+ local subString = string.sub(toFrame.Name,5)
+ gearClone.SlotNumber.Text = subString
+ gearClone.SlotNumberDownShadow.Text = subString
+ gearClone.SlotNumberUpShadow.Text = subString
+
+ local toSlotNum = tonumber(gearClone.SlotNumber.Text)
+ if toSlotNum == 0 then toSlotNum = 10 end
+ gearSlots[toSlotNum] = gearClone
+ gearClone.Position = UDim2.new(gearClone.Position.X.Scale,0,gearClone.Position.Y.Scale,0)
+ gearClone.Parent = toFrame
+ end
+end
+
+function resolveDrag(gearClone,x,y)
+ local mousePoint = Vector2.new(x,y)
+
+ local frame = gearClone.Parent
+ local frames = frame.Parent:GetChildren()
+
+ for i = 1, #frames do
+ if frames[i]:IsA("Frame") then
+ if pointInRectangle(mousePoint, frames[i].AbsolutePosition,frames[i].AbsoluteSize) then
+ swapGear(gearClone,frames[i])
+ return true
+ end
+ end
+ end
+
+ if x < frame.AbsolutePosition.x or x > ( frame.AbsolutePosition.x + frame.AbsoluteSize.x ) then
+ reorganizeLoadout(gearClone,false)
+ return false
+ elseif y < frame.AbsolutePosition.y or y > ( frame.AbsolutePosition.y + frame.AbsoluteSize.y ) then
+ reorganizeLoadout(gearClone,false)
+ return false
+ else
+ if dragBeginPos then gearClone.Position = dragBeginPos end
+ return -1
+ end
+end
+
+function unequipAllItems(dontEquipThis)
+ for i = 1, #gearSlots do
+ if gearSlots[i] == "empty" then break end
+ if gearSlots[i].GearReference.Value and gearSlots[i].GearReference.Value ~= dontEquipThis then
+ if gearSlots[i].GearReference.Value:IsA("HopperBin") then
+ gearSlots[i].GearReference.Value:Disable()
+ elseif gearSlots[i].GearReference.Value:IsA("Tool") then
+ gearSlots[i].GearReference.Value.Parent = game.Players.LocalPlayer.Backpack
+ end
+ gearSlots[i].Selected = false
+ end
+ end
+end
+
+function showToolTip(button, tip)
+ if button and button:FindFirstChild("ToolTipLabel") and button.ToolTipLabel:IsA("TextLabel") and not IsTouchDevice() then
+ button.ToolTipLabel.Text = tostring(tip)
+ local xSize = button.ToolTipLabel.TextBounds.X + 6
+ button.ToolTipLabel.Size = UDim2.new(0,xSize,0,20)
+ button.ToolTipLabel.Position = UDim2.new(0.5,-xSize/2,0,-30)
+ button.ToolTipLabel.Visible = true
+ end
+end
+
+function hideToolTip(button, tip)
+ if button and button:FindFirstChild("ToolTipLabel") and button.ToolTipLabel:IsA("TextLabel") then
+ button.ToolTipLabel.Visible = false
+ end
+end
+
+local addingPlayerChild = function(child, equipped, addToSlot, inventoryGearButton)
+ waitForDebounce()
+ debounce = true
+
+ if child:FindFirstChild("RobloxBuildTool") then debounce = false return end -- don't show roblox build tools
+ if not child:IsA("Tool") then
+ if not child:IsA("HopperBin") then
+ debounce = false
+ return -- we don't care about anything besides tools (sigh...)
+ end
+ end
+
+ if not addToSlot then
+ for i = 1, #gearSlots do
+ if gearSlots[i] ~= "empty" and gearSlots[i].GearReference.Value == child then -- we already have gear, do nothing
+ debounce = false
+ return
+ end
+ end
+ end
+
+ local gearClone = currentLoadout.TempSlot:clone()
+ gearClone.Name = child.Name
+ gearClone.GearImage.Image = child.TextureId
+ if gearClone.GearImage.Image == "" then
+ gearClone.GearText.Text = child.Name
+ end
+ gearClone.GearReference.Value = child
+
+ gearClone.MouseEnter:connect(function()
+ local gear = gearClone.GearReference and gearClone.GearReference.Value
+ if gear:IsA("Tool") and gear.ToolTip ~= "" then
+ showToolTip(gearClone, gear.ToolTip)
+ end
+ end)
+
+ gearClone.MouseLeave:connect(function()
+ local gear = gearClone.GearReference and gearClone.GearReference.Value
+ if gear:IsA("Tool") and gear.ToolTip ~= "" then
+ hideToolTip(gearClone, gear.ToolTip)
+ end
+ end)
+
+ gearClone.RobloxLocked = true
+
+ local slotToMod = -1
+
+ if not addToSlot then
+ for i = 1, #gearSlots do
+ if gearSlots[i] == "empty" then
+ slotToMod = i
+ break
+ end
+ end
+ else
+ slotToMod = addToSlot
+ end
+
+ if slotToMod == - 1 then -- No available slot to add in!
+ debounce = false
+ return
+ end
+
+ local slotNum = slotToMod % 10
+ local parent = currentLoadout:FindFirstChild("Slot"..tostring(slotNum))
+ gearClone.Parent = parent
+
+ if inventoryGearButton then
+ local absolutePositionFinal = inventoryGearButton.AbsolutePosition
+ local currentAbsolutePosition = gearClone.AbsolutePosition
+ local diff = absolutePositionFinal - currentAbsolutePosition
+ gearClone.Position = UDim2.new(gearClone.Position.X.Scale,diff.x,gearClone.Position.Y.Scale,diff.y)
+ gearClone.ZIndex = 4
+ end
+
+ if addToSlot then
+ reorganizeLoadout(gearClone, true, equipped, addToSlot)
+ else
+ reorganizeLoadout(gearClone, true)
+ end
+
+ if gearClone.Parent == nil then debounce = false return end -- couldn't fit in (hopper is full!)
+
+ if equipped then
+ gearClone.Selected = true
+ unequipAllItems(child)
+ delay(guiTweenSpeed + 0.01,function() -- if our gear is equipped, we will want to enlarge it when done moving
+ if gearClone:FindFirstChild("GearReference") and ( (gearClone.GearReference.Value:IsA("Tool") and gearClone.GearReference.Value.Parent == player.Character) or
+ (gearClone.GearReference.Value:IsA("HopperBin") and gearClone.GearReference.Value.Active == true) ) then
+ enlargeButton(gearClone)
+ end
+ end)
+ end
+
+ local dragBeginPos = nil
+ local clickCon, buttonDeleteCon, mouseEnterCon, mouseLeaveCon, dragStop, dragBegin = nil
+ clickCon = gearClone.MouseButton1Click:connect(function()
+ if characterInWorkspace() then
+ if not gearClone.Draggable then
+ activateGear(gearClone.SlotNumber.Text)
+ end
+ end
+ end)
+ mouseEnterCon = gearClone.MouseEnter:connect(function()
+ if guiBackpack.Visible then
+ gearClone.Draggable = true
+ end
+ end)
+ dragBegin = gearClone.DragBegin:connect(function(pos)
+ dragBeginPos = pos
+ gearClone.ZIndex = 7
+ local children = gearClone:GetChildren()
+ for i = 1, #children do
+ if children[i]:IsA("TextLabel") then
+ if string.find(children[i].Name,"Shadow") then
+ children[i].ZIndex = 8
+ else
+ children[i].ZIndex = 9
+ end
+ elseif children[i]:IsA("Frame") or children[i]:IsA("ImageLabel") then
+ children[i].ZIndex = 7
+ end
+ end
+ end)
+ dragStop = gearClone.DragStopped:connect(function(x,y)
+ if gearClone.Selected then
+ gearClone.ZIndex = 4
+ else
+ gearClone.ZIndex = 3
+ end
+ local children = gearClone:GetChildren()
+ for i = 1, #children do
+ if children[i]:IsA("TextLabel") then
+ if string.find(children[i].Name,"Shadow") then
+ children[i].ZIndex = 3
+ else
+ children[i].ZIndex = 4
+ end
+ elseif children[i]:IsA("Frame") or children[i]:IsA("ImageLabel") then
+ children[i].ZIndex = 2
+ end
+ end
+ resolveDrag(gearClone,x,y)
+ end)
+ mouseLeaveCon = gearClone.MouseLeave:connect(function()
+ gearClone.Draggable = false
+ end)
+ buttonDeleteCon = gearClone.AncestryChanged:connect(function()
+ if gearClone.Parent and gearClone.Parent.Parent == currentLoadout then return end
+ if clickCon then clickCon:disconnect() end
+ if buttonDeleteCon then buttonDeleteCon:disconnect() end
+ if mouseEnterCon then mouseEnterCon:disconnect() end
+ if mouseLeaveCon then mouseLeaveCon:disconnect() end
+ if dragStop then dragStop:disconnect() end
+ if dragBegin then dragBegin:disconnect() end
+ end) -- this probably isn't necessary since objects are being deleted (probably), but this might still leak just in case
+
+ local childCon = nil
+ local childChangeCon = nil
+ childCon = child.AncestryChanged:connect(function(newChild,parent)
+ if not checkToolAncestry(newChild,parent) then
+ if childCon then childCon:disconnect() end
+ if childChangeCon then childChangeCon:disconnect() end
+ removeFromInventory(child)
+ elseif parent == game.Players.LocalPlayer.Backpack then
+ normalizeButton(gearClone)
+ end
+ end)
+
+ childChangeCon = child.Changed:connect(function(prop)
+ if prop == "Name" then
+ if gearClone and gearClone.GearImage.Image == "" then
+ gearClone.GearText.Text = child.Name
+ end
+ elseif prop == "Active" then
+ if child and child:IsA("HopperBin") then
+ if not child.Active then
+ gearClone.Selected = false
+ normalizeButton(gearClone)
+ end
+ end
+ elseif prop == "TextureId" then
+ gearClone.GearImage.Image = child.TextureId
+ end
+ end)
+
+ debounce = false
+
+ Spawn(function()
+ while backpackIsOpen() do wait(0.03) end
+ for i = 1, #gearSlots do
+ if gearSlots[i] ~= 'empty' then
+ backpackButton.Position = UDim2.new(0.5, -60, 1, -108)
+ if backpackEnabled then
+ backpackButton.Visible = true
+ clBackground.Visible = true
+ end
+ end
+ end
+ end)
+end
+
+function addToInventory(child)
+ if not child:IsA("Tool") or not child:IsA("HopperBin") then return end
+
+ local slot = nil
+ for i = 1, #inventory do
+ if inventory[i] and inventory[i] == child then return end
+ if not inventory[i] then slot = i end
+ end
+ if slot then
+ inventory[slot] = child
+ elseif #inventory < 1 then
+ inventory[1] = child
+ else
+ inventory[#inventory + 1] = child
+ end
+end
+
+function removeFromInventory(child)
+ for i = 1, #inventory do
+ if inventory[i] == child then
+ table.remove(inventory,i)
+ inventory[i] = nil
+ end
+ end
+end
+
+local spreadOutGear = function()
+ loadoutChildren = currentLoadout:GetChildren()
+
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") then
+ loadoutChildren[i].BackgroundTransparency = 0.5
+ local slot = tonumber(string.sub(loadoutChildren[i].Name,5))
+ if slot == 0 then slot = 10 end
+ if IsSmallScreen() then
+ loadoutChildren[i]:TweenPosition(UDim2.new(0,(slot-1) * 60,0,0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25, true)
+ else
+ loadoutChildren[i]:TweenPosition(UDim2.new((slot - 1)/10,0,0,0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25, true)
+ end
+ end
+ end
+end
+
+local centerGear = function()
+ loadoutChildren = currentLoadout:GetChildren()
+ local gearButtons = {}
+ local lastSlotAdd = nil
+
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") then
+ if #loadoutChildren[i]:GetChildren() > 0 then
+ if loadoutChildren[i].Name == "Slot0" then
+ lastSlotAdd = loadoutChildren[i]
+ else
+ table.insert(gearButtons, loadoutChildren[i])
+ end
+ end
+ loadoutChildren[i].BackgroundTransparency = 1
+ end
+ end
+ if lastSlotAdd then table.insert(gearButtons,lastSlotAdd) end
+
+ local startPos = ( 1 - (#gearButtons * 0.1) ) / 2
+ for i = 1, #gearButtons do
+ if IsSmallScreen() then
+ startPos = ( 0.5 - (#gearButtons * 0.333)/2 )
+ gearButtons[i]:TweenPosition(UDim2.new(startPos + (i-1) * 0.33, 0, 0, 0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25, true)
+ else
+ gearButtons[i]:TweenPosition(UDim2.new(startPos + ((i - 1) * 0.1),0,0,0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25, true)
+ end
+ end
+end
+
+function editLoadout()
+ backpackWasOpened = true
+ if inGearTab then
+ spreadOutGear()
+ end
+end
+
+function readonlyLoadout()
+ if not inGearTab then
+ centerGear()
+ end
+end
+
+function setupBackpackListener()
+ if backpackChildCon then backpackChildCon:disconnect() backpackChildCon = nil end
+ backpackChildCon = player.Backpack.ChildAdded:connect(function(child)
+ if not firstInstanceOfLoadout then
+ firstInstanceOfLoadout = true
+ if backpackEnabled then
+ backpackButton.Visible = true
+ clBackground.Visible = true
+ end
+ end
+ addingPlayerChild(child)
+ addToInventory(child)
+ end)
+end
+
+function playerCharacterChildAdded(child)
+ addingPlayerChild(child,true)
+ addToInventory(child)
+end
+
+function activateLoadout()
+ currentLoadout.Visible = true
+end
+
+function deactivateLoadout()
+ currentLoadout.Visible = false
+end
+
+function tabHandler(inFocus)
+ inGearTab = inFocus
+ if inFocus then
+ editLoadout()
+ else
+ readonlyLoadout()
+ end
+end
+
+function coreGuiChanged(coreGuiType,enabled)
+ if coreGuiType == Enum.CoreGuiType.Backpack or coreGuiType == Enum.CoreGuiType.All then
+ backpackButton.Visible = enabled
+ clBackground.Visible = enabled
+ backpackEnabled = enabled
+
+ if enabled then
+ registerNumberKeys()
+ else
+ unregisterNumberKeys()
+ end
+ end
+
+ if not useCoreHealthBar and coreGuiType == Enum.CoreGuiType.Health or coreGuiType == Enum.CoreGuiType.All then
+ setHealthBarVisible(game.Players.LocalPlayer.PlayerGui, enabled)
+ end
+end
+-- End Functions
+
+
+
+
+
+
+-- Begin Script
+registerNumberKeys()
+
+coreGuiChanged(Enum.CoreGuiType.Backpack, Game.StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Backpack))
+if not useCoreHealthBar then
+ coreGuiChanged(Enum.CoreGuiType.Health, Game.StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Health))
+end
+Game.StarterGui.CoreGuiChangedSignal:connect(coreGuiChanged)
+
+wait() -- let stuff initialize incase this is first heartbeat...
+
+waitForChild(player,"Backpack")
+waitForProperty(player,"Character")
+
+-- not sure why this had no delay but the player.CharacterAdded one had one... this type of error would be easier to avoid with function reusage
+delay(1,function()
+ local backpackChildren = player.Backpack:GetChildren()
+ local size = math.min(10,#backpackChildren)
+ for i = 1, size do
+ if backpackEnabled then
+ backpackButton.Visible = true
+ clBackground.Visible = true
+ end
+ addingPlayerChild(backpackChildren[i],false)
+ end
+ setupBackpackListener()
+end)
+
+delay(2, function()
+ --while true do
+ if not backpackWasOpened then
+ if IsSmallScreen() then
+ local cChildren = currentLoadout:GetChildren()
+ for i = 1, #cChildren do
+ local slotNum = tonumber(string.sub(cChildren[i].Name, 5, string.len(cChildren[i].Name)))
+ if type(slotNum) == 'number' then
+ cChildren[i].Position = UDim2.new(0, (slotNum-1) * 60, 0, 0)
+ end
+ end
+ end
+ end
+ wait(0.25)
+ --end
+end)
+
+if not useCoreHealthBar then
+ player.ChildAdded:connect(function(child)
+ if child:IsA('PlayerGui') then
+ moveHealthBar(child)
+ end
+ end)
+end
+
+waitForProperty(player,"Character")
+for i,v in ipairs(player.Character:GetChildren()) do
+ playerCharacterChildAdded(v)
+end
+characterChildAddedCon = player.Character.ChildAdded:connect(function(child) playerCharacterChildAdded(child) end)
+
+waitForChild(player.Character,"Humanoid")
+humanoidDiedCon = player.Character.Humanoid.Died:connect(function()
+ if humanoidDiedCon then humanoidDiedCon:disconnect() humanoidDiedCon = nil end
+ deactivateLoadout()
+ if backpackChildCon then backpackChildCon:disconnect() backpackChildCon = nil end
+ backpackWasOpened = false
+end)
+
+player.CharacterRemoving:connect(function()
+ for i = 1, #gearSlots do
+ if gearSlots[i] ~= "empty" then
+ gearSlots[i].Parent = nil
+ gearSlots[i] = "empty"
+ end
+ end
+end)
+
+player.CharacterAdded:connect(function()
+ waitForProperty(game.Players,"LocalPlayer")
+ player = game.Players.LocalPlayer -- make sure we are still looking at the correct character
+ waitForChild(player,"Backpack")
+
+
+ delay(1,function()
+ local backpackChildren = player.Backpack:GetChildren()
+ local size = math.min(10,#backpackChildren)
+ for i = 1, size do
+ if backpackEnabled then
+ backpackButton.Visible = true
+ clBackground.Visible = true
+ end
+ addingPlayerChild(backpackChildren[i],false)
+ end
+ setupBackpackListener()
+ end)
+
+ activateLoadout()
+
+ if characterChildAddedCon then
+ characterChildAddedCon:disconnect()
+ characterChildAddedCon = nil
+ end
+
+ characterChildAddedCon =
+ player.Character.ChildAdded:connect(function(child)
+ addingPlayerChild(child,true)
+ end)
+
+ waitForChild(player.Character,"Humanoid")
+ if backpack.Visible then
+ backpackOpenEvent:Fire()
+ end
+ humanoidDiedCon =
+ player.Character.Humanoid.Died:connect(function()
+ if backpackEnabled then
+ backpackButton.Visible = false
+ clBackground.Visible = false
+ end
+ firstInstanceOfLoadout = false
+ deactivateLoadout()
+
+ if humanoidDiedCon then humanoidDiedCon:disconnect() humanoidDiedCon = nil end
+ if backpackChildCon then backpackChildCon:disconnect() backpackChildCon = nil end
+ end)
+ if not useCoreHealthBar then
+ waitForChild(player, 'PlayerGui')
+ moveHealthBar(player.PlayerGui)
+ end
+ delay(2, function()
+ --while true do
+ if not backpackWasOpened then
+ if IsSmallScreen() then
+ local cChildren = currentLoadout:GetChildren()
+ for i = 1, #cChildren do
+ local slotNum = tonumber(string.sub(cChildren[i].Name, 5, string.len(cChildren[i].Name)))
+ if type(slotNum) == 'number' then
+ cChildren[i].Position = UDim2.new(0, (slotNum-1) * 60, 0, 0)
+ end
+ end
+ end
+ end
+ wait(0.25)
+ --end
+ end)
+end)
+
+waitForChild(guiBackpack,"SwapSlot")
+guiBackpack.SwapSlot.Changed:connect(function()
+ if guiBackpack.SwapSlot.Value then
+ local swapSlot = guiBackpack.SwapSlot
+ local pos = swapSlot.Slot.Value
+ if pos == 0 then pos = 10 end
+ if gearSlots[pos] then
+ reorganizeLoadout(gearSlots[pos],false)
+ end
+ if swapSlot.GearButton.Value then
+ addingPlayerChild(swapSlot.GearButton.Value.GearReference.Value,false,pos)
+ end
+ guiBackpack.SwapSlot.Value = false
+ end
+end)
+
+game:GetService("GuiService").KeyPressed:connect(function(key)
+ if characterInWorkspace() then
+ activateGear(key)
+ end
+end)
+
+backpackOpenEvent.Event:connect(editLoadout)
+backpackCloseEvent.Event:connect(centerGear)
+tabClickedEvent.Event:connect(function ( tabName )
+ tabHandler(tabName == StaticTabName)
+end)
diff --git a/app/files/CoreGui/53878058 b/app/files/CoreGui/53878058
new file mode 100644
index 0000000..a763e8c
--- /dev/null
+++ b/app/files/CoreGui/53878058
@@ -0,0 +1,883 @@
+if game.CoreGui.Version < 3 then return end -- peace out if we aren't using the right client
+
+-- A couple of necessary functions
+local function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+ return instance:FindFirstChild(name)
+end
+local function waitForProperty(instance, property)
+ while not instance[property] do
+ instance.Changed:wait()
+ end
+end
+
+local currentLoadout = script.Parent
+local StaticTabName = "gear"
+
+local robloxGui = game:GetService("CoreGui"):FindFirstChild("RobloxGui")
+assert(robloxGui)
+waitForChild(robloxGui,"CurrentLoadout")
+waitForChild(robloxGui.CurrentLoadout,"TempSlot")
+waitForChild(robloxGui.CurrentLoadout.TempSlot,"SlotNumber")
+
+
+--- Begin Locals
+waitForChild(game,"Players")
+waitForProperty(game.Players,"LocalPlayer")
+local player = game.Players.LocalPlayer
+
+waitForChild(game, "LocalBackpack")
+game.LocalBackpack:SetOldSchoolBackpack(false)
+
+waitForChild(currentLoadout.Parent,"Backpack")
+local guiBackpack = currentLoadout.Parent.Backpack
+
+local backpackManager = waitForChild(guiBackpack,"CoreScripts/BackpackScripts/BackpackManager")
+local backpackOpenEvent = waitForChild(backpackManager,"BackpackOpenEvent")
+local backpackCloseEvent = waitForChild(backpackManager,"BackpackCloseEvent")
+local tabClickedEvent = waitForChild(backpackManager,"TabClickedEvent")
+local resizeEvent = waitForChild(backpackManager,"ResizeEvent")
+
+local inGearTab = true
+
+local maxNumLoadoutItems = 10
+
+local characterChildAddedCon = nil
+local backpackChildCon = nil
+
+local debounce = false
+
+local enlargeFactor = 1.18
+local buttonSizeEnlarge = UDim2.new(1 * enlargeFactor,0,1 * enlargeFactor,0)
+local buttonSizeNormal = UDim2.new(1,0,1,0)
+local enlargeOverride = true
+local guiTweenSpeed = 0.5
+
+local inventory = {}
+
+for i = 0, 9 do
+ game:GetService("GuiService"):AddKey(tostring(i)) -- register our keys
+end
+
+local gearSlots = {}
+for i = 1, maxNumLoadoutItems do
+ gearSlots[i] = "empty"
+end
+--- End Locals
+
+
+
+
+
+
+-- Begin Functions
+local function kill(prop,con,gear)
+ if con then con:disconnect() end
+ if prop == true and gear then
+ reorganizeLoadout(gear,false)
+ end
+end
+
+function characterInWorkspace()
+ if game.Players["LocalPlayer"] then
+ if game.Players.LocalPlayer["Character"] then
+ if game.Players.LocalPlayer.Character ~= nil then
+ if game.Players.LocalPlayer.Character.Parent ~= nil then
+ return true
+ end
+ end
+ end
+ end
+
+ return false
+end
+
+function removeGear(gear)
+ local emptySlot = nil
+ for i = 1, #gearSlots do
+ if gearSlots[i] == gear and gear.Parent ~= nil then
+ emptySlot = i
+ break
+ end
+ end
+ if emptySlot then
+ if gearSlots[emptySlot].GearReference.Value then
+ if gearSlots[emptySlot].GearReference.Value.Parent == game.Players.LocalPlayer.Character then -- if we currently have this equipped, unequip it
+ gearSlots[emptySlot].GearReference.Value.Parent = game.Players.LocalPlayer.Backpack
+ end
+
+ if gearSlots[emptySlot].GearReference.Value:IsA("HopperBin") and gearSlots[emptySlot].GearReference.Value.Active then -- this is an active hopperbin
+ gearSlots[emptySlot].GearReference.Value:Disable()
+ gearSlots[emptySlot].GearReference.Value.Active = false
+ end
+ end
+
+ gearSlots[emptySlot] = "empty"
+
+ local centerizeX = gear.Size.X.Scale/2
+ local centerizeY = gear.Size.Y.Scale/2
+ gear:TweenSizeAndPosition(UDim2.new(0,0,0,0),
+ UDim2.new(gear.Position.X.Scale + centerizeX,gear.Position.X.Offset,gear.Position.Y.Scale + centerizeY,gear.Position.Y.Offset),
+ Enum.EasingDirection.Out, Enum.EasingStyle.Quad,guiTweenSpeed/4,true)
+ delay(guiTweenSpeed/2,
+ function()
+ gear:remove()
+ end)
+ end
+end
+
+function insertGear(gear, addToSlot)
+ local pos = nil
+ if not addToSlot then
+ for i = 1, #gearSlots do
+ if gearSlots[i] == "empty" then
+ pos = i
+ break
+ end
+ end
+
+ if pos == 1 and gearSlots[1] ~= "empty" then gear:remove() return end -- we are currently full, can't add in
+ else
+ pos = addToSlot
+ -- push all gear down one slot
+ local start = 1
+ for i = 1, #gearSlots do
+ if gearSlots[i] == "empty" then
+ start = i
+ break
+ end
+ end
+ for i = start, pos + 1, -1 do
+ gearSlots[i] = gearSlots[i - 1]
+ if i == 10 then
+ gearSlots[i].SlotNumber.Text = "0"
+ gearSlots[i].SlotNumberDownShadow.Text = "0"
+ gearSlots[i].SlotNumberUpShadow.Text = "0"
+ else
+ gearSlots[i].SlotNumber.Text = i
+ gearSlots[i].SlotNumberDownShadow.Text = i
+ gearSlots[i].SlotNumberUpShadow.Text = i
+ end
+ end
+ end
+
+ gearSlots[pos] = gear
+ if pos ~= maxNumLoadoutItems then
+ if(type(tostring(pos)) == "string") then
+ local posString = tostring(pos)
+ gear.SlotNumber.Text = posString
+ gear.SlotNumberDownShadow.Text = posString
+ gear.SlotNumberUpShadow.Text = posString
+ end
+ else -- tenth gear doesn't follow mathematical pattern :(
+ gear.SlotNumber.Text = "0"
+ gear.SlotNumberDownShadow.Text = "0"
+ gear.SlotNumberUpShadow.Text = "0"
+ end
+ gear.Visible = true
+
+ local con = nil
+ con = gear.Kill.Changed:connect(function(prop) kill(prop,con,gear) end)
+end
+
+
+function reorganizeLoadout(gear, inserting, equipped, addToSlot)
+ if inserting then -- add in gear
+ insertGear(gear, addToSlot)
+ else
+ removeGear(gear)
+ end
+ if gear ~= "empty" then gear.ZIndex = 1 end
+end
+
+function checkToolAncestry(child,parent)
+ if child:FindFirstChild("RobloxBuildTool") then return end -- don't show roblox build tools
+ if child:IsA("Tool") or child:IsA("HopperBin") then
+ for i = 1, #gearSlots do
+ if gearSlots[i] ~= "empty" and gearSlots[i].GearReference.Value == child then
+ if parent == nil then
+ gearSlots[i].Kill.Value = true
+ return false
+ elseif child.Parent == player.Character then
+ gearSlots[i].Selected = true
+ return true
+ elseif child.Parent == player.Backpack then
+ if child:IsA("Tool") or child:IsA("HopperBin") then gearSlots[i].Selected = false end
+ return true
+ else
+ gearSlots[i].Kill.Value = true
+ return false
+ end
+ return true
+ end
+ end
+ end
+end
+
+function removeAllEquippedGear(physGear)
+ local stuff = player.Character:GetChildren()
+ for i = 1, #stuff do
+ if ( stuff[i]:IsA("Tool") or stuff[i]:IsA("HopperBin") ) and stuff[i] ~= physGear then
+ if stuff[i]:IsA("Tool") then stuff[i].Parent = player.Backpack end
+ if stuff[i]:IsA("HopperBin") then
+ stuff[i]:Disable()
+ end
+ end
+ end
+end
+
+function hopperBinSwitcher(numKey, physGear)
+ if not physGear then return end
+
+ physGear:ToggleSelect()
+
+ if gearSlots[numKey] == "empty" then return end
+
+ if not physGear.Active then
+ gearSlots[numKey].Selected = false
+ normalizeButton(gearSlots[numKey])
+ else
+ gearSlots[numKey].Selected = true
+ enlargeButton(gearSlots[numKey])
+ end
+end
+
+function toolSwitcher(numKey)
+
+ if not gearSlots[numKey] then return end
+ local physGear = gearSlots[numKey].GearReference.Value
+ if physGear == nil then return end
+
+ removeAllEquippedGear(physGear) -- we don't remove this gear, as then we get a double switcheroo
+
+ local key = numKey
+ if numKey == 0 then key = 10 end
+
+ for i = 1, #gearSlots do
+ if gearSlots[i] and gearSlots[i] ~= "empty" and i ~= key then
+ normalizeButton(gearSlots[i])
+ gearSlots[i].Selected = false
+ if gearSlots[i].GearReference and gearSlots[i].GearReference.Value and gearSlots[i].GearReference.Value:IsA("HopperBin") and gearSlots[i].GearReference.Value.Active then
+ gearSlots[i].GearReference.Value:ToggleSelect()
+ end
+ end
+ end
+
+ if physGear:IsA("HopperBin") then
+ hopperBinSwitcher(numKey,physGear)
+ else
+ if physGear.Parent == player.Character then
+ physGear.Parent = player.Backpack
+
+ if gearSlots[numKey] ~= "empty" then
+ gearSlots[numKey].Selected = false
+ normalizeButton(gearSlots[numKey])
+ end
+ else
+ --player.Character.Humanoid:EquipTool(physGear)
+
+ physGear.Parent = player.Character
+ gearSlots[numKey].Selected = true
+
+ enlargeButton(gearSlots[numKey])
+ end
+ end
+end
+
+
+function activateGear(num)
+ local numKey = nil
+ if num == "0" then
+ numKey = 10 -- why do lua indexes have to start at 1? :(
+ else
+ numKey = tonumber(num)
+ end
+
+ if(numKey == nil) then return end
+
+ if gearSlots[numKey] ~= "empty" then
+ toolSwitcher(numKey)
+ end
+end
+
+
+enlargeButton = function(button)
+ if button.Size.Y.Scale > 1 then return end
+ if not button.Parent then return end
+ if not button.Selected then return end
+
+ for i = 1, #gearSlots do
+ if gearSlots[i] == "empty" then break end
+ if gearSlots[i] ~= button then
+ normalizeButton(gearSlots[i])
+ end
+ end
+
+ if not enlargeOverride then
+ return
+ end
+
+ if button:IsA("ImageButton") or button:IsA("TextButton") then
+ button.ZIndex = 2
+ local centerizeX = -(buttonSizeEnlarge.X.Scale - button.Size.X.Scale)/2
+ local centerizeY = -(buttonSizeEnlarge.Y.Scale - button.Size.Y.Scale)/2
+ button:TweenSizeAndPosition(buttonSizeEnlarge,
+ UDim2.new(button.Position.X.Scale + centerizeX,button.Position.X.Offset,button.Position.Y.Scale + centerizeY,button.Position.Y.Offset),
+ Enum.EasingDirection.Out, Enum.EasingStyle.Quad,guiTweenSpeed/5,enlargeOverride)
+ end
+end
+
+normalizeAllButtons = function()
+ for i = 1, #gearSlots do
+ if gearSlots[i] == "empty" then break end
+ if gearSlots[i] ~= button then
+ normalizeButton(gearSlots[i],0.1)
+ end
+ end
+end
+
+
+normalizeButton = function(button, speed)
+ if not button then return end
+ if button.Size.Y.Scale <= 1 then return end
+ if button.Selected then return end
+ if not button.Parent then return end
+
+ local moveSpeed = speed
+ if moveSpeed == nil or type(moveSpeed) ~= "number" then moveSpeed = guiTweenSpeed/5 end
+
+ if button:IsA("ImageButton") or button:IsA("TextButton") then
+ button.ZIndex = 1
+ local inverseEnlarge = 1/enlargeFactor
+ local centerizeX = -(buttonSizeNormal.X.Scale - button.Size.X.Scale)/2
+ local centerizeY = -(buttonSizeNormal.Y.Scale - button.Size.Y.Scale)/2
+ button:TweenSizeAndPosition(buttonSizeNormal,
+ UDim2.new(button.Position.X.Scale + centerizeX,button.Position.X.Offset,button.Position.Y.Scale + centerizeY,button.Position.Y.Offset),
+ Enum.EasingDirection.Out, Enum.EasingStyle.Quad,moveSpeed,enlargeOverride)
+ end
+end
+
+local waitForDebounce = function()
+ while debounce do
+ wait()
+ end
+end
+
+function pointInRectangle(point,rectTopLeft,rectSize)
+ if point.x > rectTopLeft.x and point.x < (rectTopLeft.x + rectSize.x) then
+ if point.y > rectTopLeft.y and point.y < (rectTopLeft.y + rectSize.y) then
+ return true
+ end
+ end
+ return false
+end
+
+function swapGear(gearClone,toFrame)
+ local toFrameChildren = toFrame:GetChildren()
+ if #toFrameChildren == 1 then
+ if toFrameChildren[1]:FindFirstChild("SlotNumber") then
+
+ local toSlot = tonumber(toFrameChildren[1].SlotNumber.Text)
+ local gearCloneSlot = tonumber(gearClone.SlotNumber.Text)
+ if toSlot == 0 then toSlot = 10 end
+ if gearCloneSlot == 0 then gearCloneSlot = 10 end
+
+ gearSlots[toSlot] = gearClone
+ gearSlots[gearCloneSlot] = toFrameChildren[1]
+
+ toFrameChildren[1].SlotNumber.Text = gearClone.SlotNumber.Text
+ toFrameChildren[1].SlotNumberDownShadow.Text = gearClone.SlotNumber.Text
+ toFrameChildren[1].SlotNumberUpShadow.Text = gearClone.SlotNumber.Text
+
+ local subString = string.sub(toFrame.Name,5)
+ gearClone.SlotNumber.Text = subString
+ gearClone.SlotNumberDownShadow.Text = subString
+ gearClone.SlotNumberUpShadow.Text = subString
+
+ gearClone.Position = UDim2.new(gearClone.Position.X.Scale,0,gearClone.Position.Y.Scale,0)
+ toFrameChildren[1].Position = UDim2.new(toFrameChildren[1].Position.X.Scale,0,toFrameChildren[1].Position.Y.Scale,0)
+
+ toFrameChildren[1].Parent = gearClone.Parent
+ gearClone.Parent = toFrame
+ end
+ else
+ local slotNum = tonumber(gearClone.SlotNumber.Text)
+ if slotNum == 0 then slotNum = 10 end
+ gearSlots[slotNum] = "empty" -- reset this gear slot
+
+ local subString = string.sub(toFrame.Name,5)
+ gearClone.SlotNumber.Text = subString
+ gearClone.SlotNumberDownShadow.Text = subString
+ gearClone.SlotNumberUpShadow.Text = subString
+
+ local toSlotNum = tonumber(gearClone.SlotNumber.Text)
+ if toSlotNum == 0 then toSlotNum = 10 end
+ gearSlots[toSlotNum] = gearClone
+ gearClone.Position = UDim2.new(gearClone.Position.X.Scale,0,gearClone.Position.Y.Scale,0)
+ gearClone.Parent = toFrame
+ end
+end
+
+function resolveDrag(gearClone,x,y)
+ local mousePoint = Vector2.new(x,y)
+
+ local frame = gearClone.Parent
+ local frames = frame.Parent:GetChildren()
+
+ for i = 1, #frames do
+ if frames[i]:IsA("Frame") then
+ if pointInRectangle(mousePoint, frames[i].AbsolutePosition,frames[i].AbsoluteSize) then
+ swapGear(gearClone,frames[i])
+ return true
+ end
+ end
+ end
+
+ if x < frame.AbsolutePosition.x or x > ( frame.AbsolutePosition.x + frame.AbsoluteSize.x ) then
+ reorganizeLoadout(gearClone,false)
+ return false
+ elseif y < frame.AbsolutePosition.y or y > ( frame.AbsolutePosition.y + frame.AbsoluteSize.y ) then
+ reorganizeLoadout(gearClone,false)
+ return false
+ else
+ if dragBeginPos then gearClone.Position = dragBeginPos end
+ return -1
+ end
+end
+
+function unequipAllItems(dontEquipThis)
+ for i = 1, #gearSlots do
+ if gearSlots[i] == "empty" then break end
+ if gearSlots[i].GearReference.Value and gearSlots[i].GearReference.Value ~= dontEquipThis then
+ if gearSlots[i].GearReference.Value:IsA("HopperBin") then
+ gearSlots[i].GearReference.Value:Disable()
+ elseif gearSlots[i].GearReference.Value:IsA("Tool") then
+ gearSlots[i].GearReference.Value.Parent = game.Players.LocalPlayer.Backpack
+ end
+ gearSlots[i].Selected = false
+ end
+ end
+end
+
+function showToolTip(button, tip)
+ if button and button:FindFirstChild("ToolTipLabel") and button.ToolTipLabel:IsA("TextLabel") then
+ button.ToolTipLabel.Text = tostring(tip)
+ local xSize = button.ToolTipLabel.TextBounds.X + 6
+ button.ToolTipLabel.Size = UDim2.new(0,xSize,0,20)
+ button.ToolTipLabel.Position = UDim2.new(0.5,-xSize/2,0,-30)
+ button.ToolTipLabel.Visible = true
+ end
+end
+
+function hideToolTip(button, tip)
+ if button and button:FindFirstChild("ToolTipLabel") and button.ToolTipLabel:IsA("TextLabel") then
+ button.ToolTipLabel.Visible = false
+ end
+end
+
+local addingPlayerChild = function(child, equipped, addToSlot, inventoryGearButton)
+ waitForDebounce()
+ debounce = true
+
+ if child:FindFirstChild("RobloxBuildTool") then debounce = false return end -- don't show roblox build tools
+ if not child:IsA("Tool") then
+ if not child:IsA("HopperBin") then
+ debounce = false
+ return -- we don't care about anything besides tools (sigh...)
+ end
+ end
+
+ if not addToSlot then
+ for i = 1, #gearSlots do
+ if gearSlots[i] ~= "empty" and gearSlots[i].GearReference.Value == child then -- we already have gear, do nothing
+ debounce = false
+ return
+ end
+ end
+ end
+
+
+ local gearClone = currentLoadout.TempSlot:clone()
+ gearClone.Name = child.Name
+ gearClone.GearImage.Image = child.TextureId
+ if gearClone.GearImage.Image == "" then
+ gearClone.GearText.Text = child.Name
+ end
+ gearClone.GearReference.Value = child
+
+ gearClone.MouseEnter:connect(function()
+ if gearClone.GearReference and gearClone.GearReference.Value["ToolTip"] and gearClone.GearReference.Value.ToolTip ~= "" then
+ showToolTip(gearClone, gearClone.GearReference.Value.ToolTip)
+ end
+ end)
+
+ gearClone.MouseLeave:connect(function()
+ if gearClone.GearReference and gearClone.GearReference.Value["ToolTip"] and gearClone.GearReference.Value.ToolTip ~= "" then
+ hideToolTip(gearClone, gearClone.GearReference.Value.ToolTip)
+ end
+ end)
+
+ gearClone.RobloxLocked = true
+
+ local slotToMod = -1
+
+ if not addToSlot then
+ for i = 1, #gearSlots do
+ if gearSlots[i] == "empty" then
+ slotToMod = i
+ break
+ end
+ end
+ else
+ slotToMod = addToSlot
+ end
+
+ if slotToMod == - 1 then -- No available slot to add in!
+ debounce = false
+ return
+ end
+
+ local slotNum = slotToMod % 10
+ local parent = currentLoadout:FindFirstChild("Slot"..tostring(slotNum))
+ gearClone.Parent = parent
+
+ if inventoryGearButton then
+ local absolutePositionFinal = inventoryGearButton.AbsolutePosition
+ local currentAbsolutePosition = gearClone.AbsolutePosition
+ local diff = absolutePositionFinal - currentAbsolutePosition
+ gearClone.Position = UDim2.new(gearClone.Position.X.Scale,diff.x,gearClone.Position.Y.Scale,diff.y)
+ gearClone.ZIndex = 4
+ end
+
+ if addToSlot then
+ reorganizeLoadout(gearClone, true, equipped, addToSlot)
+ else
+ reorganizeLoadout(gearClone, true)
+ end
+
+ if gearClone.Parent == nil then debounce = false return end -- couldn't fit in (hopper is full!)
+
+ if equipped then
+ gearClone.Selected = true
+ unequipAllItems(child)
+ delay(guiTweenSpeed + 0.01,function() -- if our gear is equipped, we will want to enlarge it when done moving
+ if gearClone:FindFirstChild("GearReference") and ( (gearClone.GearReference.Value:IsA("Tool") and gearClone.GearReference.Value.Parent == player.Character) or
+ (gearClone.GearReference.Value:IsA("HopperBin") and gearClone.GearReference.Value.Active == true) ) then
+ enlargeButton(gearClone)
+ end
+ end)
+ end
+
+ local dragBeginPos = nil
+ local clickCon, buttonDeleteCon, mouseEnterCon, mouseLeaveCon, dragStop, dragBegin = nil
+ clickCon = gearClone.MouseButton1Click:connect(function()
+ if characterInWorkspace() then
+ if not gearClone.Draggable then
+ activateGear(gearClone.SlotNumber.Text)
+ end
+ end
+ end)
+ mouseEnterCon = gearClone.MouseEnter:connect(function()
+ if guiBackpack.Visible then
+ gearClone.Draggable = true
+ end
+ end)
+ dragBegin = gearClone.DragBegin:connect(function(pos)
+ dragBeginPos = pos
+ gearClone.ZIndex = 7
+ local children = gearClone:GetChildren()
+ for i = 1, #children do
+ if children[i]:IsA("TextLabel") then
+ if string.find(children[i].Name,"Shadow") then
+ children[i].ZIndex = 8
+ else
+ children[i].ZIndex = 9
+ end
+ elseif children[i]:IsA("Frame") or children[i]:IsA("ImageLabel") then
+ children[i].ZIndex = 7
+ end
+ end
+ end)
+ dragStop = gearClone.DragStopped:connect(function(x,y)
+ if gearClone.Selected then
+ gearClone.ZIndex = 2
+ else
+ gearClone.ZIndex = 1
+ end
+ local children = gearClone:GetChildren()
+ for i = 1, #children do
+ if children[i]:IsA("TextLabel") then
+ if string.find(children[i].Name,"Shadow") then
+ children[i].ZIndex = 3
+ else
+ children[i].ZIndex = 4
+ end
+ elseif children[i]:IsA("Frame") or children[i]:IsA("ImageLabel") then
+ children[i].ZIndex = 2
+ end
+ end
+ resolveDrag(gearClone,x,y)
+ end)
+ mouseLeaveCon = gearClone.MouseLeave:connect(function()
+ gearClone.Draggable = false
+ end)
+ buttonDeleteCon = gearClone.AncestryChanged:connect(function()
+ if gearClone.Parent and gearClone.Parent.Parent == currentLoadout then return end
+ if clickCon then clickCon:disconnect() end
+ if buttonDeleteCon then buttonDeleteCon:disconnect() end
+ if mouseEnterCon then mouseEnterCon:disconnect() end
+ if mouseLeaveCon then mouseLeaveCon:disconnect() end
+ if dragStop then dragStop:disconnect() end
+ if dragBegin then dragBegin:disconnect() end
+ end) -- this probably isn't necessary since objects are being deleted (probably), but this might still leak just in case
+
+ local childCon = nil
+ local childChangeCon = nil
+ childCon = child.AncestryChanged:connect(function(newChild,parent)
+ if not checkToolAncestry(newChild,parent) then
+ if childCon then childCon:disconnect() end
+ if childChangeCon then childChangeCon:disconnect() end
+ removeFromInventory(child)
+ elseif parent == game.Players.LocalPlayer.Backpack then
+ normalizeButton(gearClone)
+ end
+ end)
+
+ childChangeCon = child.Changed:connect(function(prop)
+ if prop == "Name" then
+ if gearClone and gearClone.GearImage.Image == "" then
+ gearClone.GearText.Text = child.Name
+ end
+ elseif prop == "Active" then
+ if child and child:IsA("HopperBin") then
+ if not child.Active then
+ gearClone.Selected = false
+ normalizeButton(gearClone)
+ end
+ end
+ elseif prop == "TextureId" then
+ gearClone.GearImage.Image = child.TextureId
+ end
+ end)
+
+ debounce = false
+
+end
+
+function addToInventory(child)
+ if not child:IsA("Tool") or not child:IsA("HopperBin") then return end
+
+ local slot = nil
+ for i = 1, #inventory do
+ if inventory[i] and inventory[i] == child then return end
+ if not inventory[i] then slot = i end
+ end
+ if slot then
+ inventory[slot] = child
+ elseif #inventory < 1 then
+ inventory[1] = child
+ else
+ inventory[#inventory + 1] = child
+ end
+end
+
+function removeFromInventory(child)
+ for i = 1, #inventory do
+ if inventory[i] == child then
+ table.remove(inventory,i)
+ inventory[i] = nil
+ end
+ end
+end
+
+local spreadOutGear = function()
+ loadoutChildren = currentLoadout:GetChildren()
+
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") then
+ loadoutChildren[i].BackgroundTransparency = 0.5
+ local slot = tonumber(string.sub(loadoutChildren[i].Name,5))
+ if slot == 0 then slot = 10 end
+ loadoutChildren[i]:TweenPosition(UDim2.new((slot - 1)/10,0,0,0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25, true)
+ end
+ end
+end
+
+local centerGear = function()
+ loadoutChildren = currentLoadout:GetChildren()
+ local gearButtons = {}
+ local lastSlotAdd = nlii
+
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") then
+ if #loadoutChildren[i]:GetChildren() > 0 then
+ if loadoutChildren[i].Name == "Slot0" then
+ lastSlotAdd = loadoutChildren[i]
+ else
+ table.insert(gearButtons, loadoutChildren[i])
+ end
+ end
+ loadoutChildren[i].BackgroundTransparency = 1
+ end
+ end
+ if lastSlotAdd then table.insert(gearButtons,lastSlotAdd) end
+
+ local startPos = ( 1 - (#gearButtons * 0.1) ) / 2
+ for i = 1, #gearButtons do
+ gearButtons[i]:TweenPosition(UDim2.new(startPos + ((i - 1) * 0.1),0,0,0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25, true)
+ end
+end
+
+function editLoadout()
+ if inGearTab then
+ spreadOutGear()
+ end
+end
+
+function readonlyLoadout()
+ if not inGearTab then
+ centerGear()
+ end
+end
+
+function setupBackpackListener()
+ if backpackChildCon then backpackChildCon:disconnect() backpackChildCon = nil end
+ backpackChildCon = player.Backpack.ChildAdded:connect(function(child)
+ addingPlayerChild(child)
+ addToInventory(child)
+ end)
+end
+
+function playerCharacterChildAdded(child)
+ addingPlayerChild(child,true)
+ addToInventory(child)
+end
+
+function activateLoadout()
+ currentLoadout.Visible = true
+end
+
+function deactivateLoadout()
+ currentLoadout.Visible = false
+end
+
+function tabHandler(inFocus)
+ inGearTab = inFocus
+ if inFocus then
+ editLoadout()
+ else
+ readonlyLoadout()
+ end
+end
+-- End Functions
+
+
+
+
+
+
+-- Begin Script
+wait() -- let stuff initialize incase this is first heartbeat...
+
+waitForChild(player,"Backpack")
+waitForProperty(player,"Character")
+
+-- not sure why this had no delay but the player.CharacterAdded one had one... this type of error would be easier to avoid with function reusage
+delay(1,function()
+ local backpackChildren = player.Backpack:GetChildren()
+ local size = math.min(10,#backpackChildren)
+ for i = 1, size do
+ addingPlayerChild(backpackChildren[i],false)
+ end
+ setupBackpackListener()
+end)
+
+waitForProperty(player,"Character")
+for i,v in ipairs(player.Character:GetChildren()) do
+ playerCharacterChildAdded(v)
+end
+characterChildAddedCon = player.Character.ChildAdded:connect(function(child) playerCharacterChildAdded(child) end)
+
+waitForChild(player.Character,"Humanoid")
+humanoidDiedCon = player.Character.Humanoid.Died:connect(function()
+ if humanoidDiedCon then humanoidDiedCon:disconnect() humanoidDiedCon = nil end
+ deactivateLoadout()
+ if backpackChildCon then backpackChildCon:disconnect() backpackChildCon = nil end
+end)
+
+player.CharacterRemoving:connect(function()
+ for i = 1, #gearSlots do
+ if gearSlots[i] ~= "empty" then
+ gearSlots[i].Parent = nil
+ gearSlots[i] = "empty"
+ end
+ end
+end)
+
+player.CharacterAdded:connect(function()
+ waitForProperty(game.Players,"LocalPlayer")
+ player = game.Players.LocalPlayer -- make sure we are still looking at the correct character
+ waitForChild(player,"Backpack")
+
+
+ delay(1,function()
+ local backpackChildren = player.Backpack:GetChildren()
+ local size = math.min(10,#backpackChildren)
+ for i = 1, size do
+ addingPlayerChild(backpackChildren[i],false)
+ end
+ setupBackpackListener()
+ end)
+
+ activateLoadout()
+
+ if characterChildAddedCon then
+ characterChildAddedCon:disconnect()
+ characterChildAddedCon = nil
+ end
+
+ characterChildAddedCon =
+ player.Character.ChildAdded:connect(function(child)
+ addingPlayerChild(child,true)
+ end)
+
+ waitForChild(player.Character,"Humanoid")
+ humanoidDiedCon =
+ player.Character.Humanoid.Died:connect(function()
+ deactivateLoadout()
+
+ if humanoidDiedCon then humanoidDiedCon:disconnect() humanoidDiedCon = nil end
+ if backpackChildCon then backpackChildCon:disconnect() backpackChildCon = nil end
+ end)
+
+end)
+
+waitForChild(guiBackpack,"SwapSlot")
+guiBackpack.SwapSlot.Changed:connect(function()
+ if guiBackpack.SwapSlot.Value then
+ local swapSlot = guiBackpack.SwapSlot
+ local pos = swapSlot.Slot.Value
+ if pos == 0 then pos = 10 end
+ if gearSlots[pos] then
+ reorganizeLoadout(gearSlots[pos],false)
+ end
+ if swapSlot.GearButton.Value then
+ addingPlayerChild(swapSlot.GearButton.Value.GearReference.Value,false,pos)
+ end
+ guiBackpack.SwapSlot.Value = false
+ end
+end)
+
+game:GetService("GuiService").KeyPressed:connect(function(key)
+ if characterInWorkspace() then
+ activateGear(key)
+ end
+end)
+
+backpackOpenEvent.Event:connect(editLoadout)
+backpackCloseEvent.Event:connect(centerGear)
+tabClickedEvent.Event:connect(function ( tabName )
+ tabHandler(tabName == StaticTabName)
+end)
\ No newline at end of file
diff --git a/app/files/CoreGui/59002209 b/app/files/CoreGui/59002209
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/app/files/CoreGui/59002209
@@ -0,0 +1 @@
+
diff --git a/app/files/CoreGui/59431535 b/app/files/CoreGui/59431535
new file mode 100644
index 0000000..5e2f30a
--- /dev/null
+++ b/app/files/CoreGui/59431535
@@ -0,0 +1,204 @@
+-- This script is responsible for loading in all build tools for build mode
+
+-- Script Globals
+local buildTools = {}
+local currentTools = {}
+
+local DeleteToolID = 73089190
+local PartSelectionID = 73089166
+local CloneToolID = 73089204
+local RecentPartToolID = 73089229
+local RotateToolID = 73089214
+local ConfigToolID = 73089239
+local WiringToolID = 73089259
+local classicToolID = 58921588
+
+local player = nil
+local backpack = nil
+
+-- Basic Functions
+local function waitForProperty(instance, name)
+ while not instance[name] do
+ instance.Changed:wait()
+ end
+end
+
+local function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+
+waitForProperty(game.Players,"LocalPlayer")
+waitForProperty(game.Players.LocalPlayer,"userId")
+
+-- we aren't in a true build mode session, don't give build tools and delete this script
+if game.Players.LocalPlayer.userId < 1 then
+ script:Destroy()
+ return -- this is probably not necessesary, doing it just in case
+end
+
+-- Functions
+function getLatestPlayer()
+ waitForProperty(game.Players,"LocalPlayer")
+ player = game.Players.LocalPlayer
+ waitForChild(player,"Backpack")
+ backpack = player.Backpack
+end
+
+function waitForCharacterLoad()
+
+ local startTick = tick()
+
+ local playerLoaded = false
+
+ local success = pcall(function() playerLoaded = player.AppearanceDidLoad end) --TODO: remove pcall once this in client on prod
+ if not success then return false end
+
+ while not playerLoaded do
+ player.Changed:wait()
+ playerLoaded = player.AppearanceDidLoad
+ end
+
+ return true
+end
+
+function showBuildToolsTutorial()
+ local tutorialKey = "BuildToolsTutorial"
+ if UserSettings().GameSettings:GetTutorialState(tutorialKey) == true then return end --already have shown tutorial
+
+ local RbxGui = LoadLibrary("RbxGui")
+
+ local frame, showTutorial, dismissTutorial, gotoPage = RbxGui.CreateTutorial("Build", tutorialKey, false)
+ local firstPage = RbxGui.CreateImageTutorialPage(" ", "http://www.syntax.eco/asset/?id=59162193", 359, 296, function() dismissTutorial() end, true)
+
+ RbxGui.AddTutorialPage(frame, firstPage)
+ frame.Parent = game:GetService("CoreGui"):FindFirstChild("RobloxGui")
+
+ game:GetService("GuiService"):AddCenterDialog(frame, Enum.CenterDialogType.UnsolicitedDialog,
+ --showFunction
+ function()
+ frame.Visible = true
+ showTutorial()
+ end,
+ --hideFunction
+ function()
+ frame.Visible = false
+ end
+ )
+
+ wait(1)
+ showTutorial()
+end
+
+function clearLoadout()
+ currentTools = {}
+
+ local backpackChildren = game.Players.LocalPlayer.Backpack:GetChildren()
+ for i = 1, #backpackChildren do
+ if backpackChildren[i]:IsA("Tool") or backpackChildren[i]:IsA("HopperBin") then
+ table.insert(currentTools,backpackChildren[i])
+ end
+ end
+
+ if game.Players.LocalPlayer["Character"] then
+ local characterChildren = game.Players.LocalPlayer.Character:GetChildren()
+ for i = 1, #characterChildren do
+ if characterChildren[i]:IsA("Tool") or characterChildren[i]:IsA("HopperBin") then
+ table.insert(currentTools,characterChildren[i])
+ end
+ end
+ end
+
+ for i = 1, #currentTools do
+ currentTools[i].Parent = nil
+ end
+end
+
+function giveToolsBack()
+ for i = 1, #currentTools do
+ currentTools[i].Parent = game.Players.LocalPlayer.Backpack
+ end
+end
+
+function backpackHasTool(tool)
+ local backpackChildren = backpack:GetChildren()
+ for i = 1, #backpackChildren do
+ if backpackChildren[i] == tool then
+ return true
+ end
+ end
+ return false
+end
+
+function getToolAssetID(assetID)
+ local newTool = game:GetService("InsertService"):LoadAsset(assetID)
+ local toolChildren = newTool:GetChildren()
+ for i = 1, #toolChildren do
+ if toolChildren[i]:IsA("Tool") then
+ return toolChildren[i]
+ end
+ end
+ return nil
+end
+
+-- remove legacy identifiers
+-- todo: determine if we still need this
+function removeBuildToolTag(tool)
+ if tool:FindFirstChild("RobloxBuildTool") then
+ tool.RobloxBuildTool:Destroy()
+ end
+end
+
+function giveAssetId(assetID,toolName)
+ local theTool = getToolAssetID(assetID,toolName)
+ if theTool and not backpackHasTool(theTool) then
+ removeBuildToolTag(theTool)
+ theTool.Parent = backpack
+ table.insert(buildTools,theTool)
+ end
+end
+
+function loadBuildTools()
+ giveAssetId(PartSelectionID)
+ giveAssetId(DeleteToolID)
+ giveAssetId(CloneToolID)
+ giveAssetId(RotateToolID)
+ giveAssetId(RecentPartToolID)
+ giveAssetId(WiringToolID)
+ giveAssetId(ConfigToolID)
+
+ -- deprecated tools
+ giveAssetId(classicToolID)
+end
+
+function givePlayerBuildTools()
+ getLatestPlayer()
+
+ clearLoadout()
+
+ loadBuildTools()
+
+ giveToolsBack()
+end
+
+function takePlayerBuildTools()
+ for k,v in ipairs(buildTools) do
+ v.Parent = nil
+ end
+ buildTools = {}
+end
+
+
+-- Script start
+getLatestPlayer()
+waitForCharacterLoad()
+givePlayerBuildTools()
+
+-- If player dies, we make sure to give them build tools again
+player.CharacterAdded:connect(function()
+ takePlayerBuildTools()
+ givePlayerBuildTools()
+end)
+
+showBuildToolsTutorial()
diff --git a/app/files/CoreGui/60595411 b/app/files/CoreGui/60595411
new file mode 100644
index 0000000..f8318ee
--- /dev/null
+++ b/app/files/CoreGui/60595411
@@ -0,0 +1,1115 @@
+local t = {}
+
+
+
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------JSON Functions Begin----------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+
+ --JSON Encoder and Parser for Lua 5.1
+ --
+ --Copyright 2007 Shaun Brown (http://www.chipmunkav.com)
+ --All Rights Reserved.
+
+ --Permission is hereby granted, free of charge, to any person
+ --obtaining a copy of this software to deal in the Software without
+ --restriction, including without limitation the rights to use,
+ --copy, modify, merge, publish, distribute, sublicense, and/or
+ --sell copies of the Software, and to permit persons to whom the
+ --Software is furnished to do so, subject to the following conditions:
+
+ --The above copyright notice and this permission notice shall be
+ --included in all copies or substantial portions of the Software.
+ --If you find this software useful please give www.chipmunkav.com a mention.
+
+ --THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ --EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ --OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ --IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ --ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ --CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ --CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+local string = string
+local math = math
+local table = table
+local error = error
+local tonumber = tonumber
+local tostring = tostring
+local type = type
+local setmetatable = setmetatable
+local pairs = pairs
+local ipairs = ipairs
+local assert = assert
+local Chipmunk = Chipmunk
+
+
+local StringBuilder = {
+ buffer = {}
+}
+
+function StringBuilder:New()
+ local o = {}
+ setmetatable(o, self)
+ self.__index = self
+ o.buffer = {}
+ return o
+end
+
+function StringBuilder:Append(s)
+ self.buffer[#self.buffer+1] = s
+end
+
+function StringBuilder:ToString()
+ return table.concat(self.buffer)
+end
+
+local JsonWriter = {
+ backslashes = {
+ ['\b'] = "\\b",
+ ['\t'] = "\\t",
+ ['\n'] = "\\n",
+ ['\f'] = "\\f",
+ ['\r'] = "\\r",
+ ['"'] = "\\\"",
+ ['\\'] = "\\\\",
+ ['/'] = "\\/"
+ }
+}
+
+function JsonWriter:New()
+ local o = {}
+ o.writer = StringBuilder:New()
+ setmetatable(o, self)
+ self.__index = self
+ return o
+end
+
+function JsonWriter:Append(s)
+ self.writer:Append(s)
+end
+
+function JsonWriter:ToString()
+ return self.writer:ToString()
+end
+
+function JsonWriter:Write(o)
+ local t = type(o)
+ if t == "nil" then
+ self:WriteNil()
+ elseif t == "boolean" then
+ self:WriteString(o)
+ elseif t == "number" then
+ self:WriteString(o)
+ elseif t == "string" then
+ self:ParseString(o)
+ elseif t == "table" then
+ self:WriteTable(o)
+ elseif t == "function" then
+ self:WriteFunction(o)
+ elseif t == "thread" then
+ self:WriteError(o)
+ elseif t == "userdata" then
+ self:WriteError(o)
+ end
+end
+
+function JsonWriter:WriteNil()
+ self:Append("null")
+end
+
+function JsonWriter:WriteString(o)
+ self:Append(tostring(o))
+end
+
+function JsonWriter:ParseString(s)
+ self:Append('"')
+ self:Append(string.gsub(s, "[%z%c\\\"/]", function(n)
+ local c = self.backslashes[n]
+ if c then return c end
+ return string.format("\\u%.4X", string.byte(n))
+ end))
+ self:Append('"')
+end
+
+function JsonWriter:IsArray(t)
+ local count = 0
+ local isindex = function(k)
+ if type(k) == "number" and k > 0 then
+ if math.floor(k) == k then
+ return true
+ end
+ end
+ return false
+ end
+ for k,v in pairs(t) do
+ if not isindex(k) then
+ return false, '{', '}'
+ else
+ count = math.max(count, k)
+ end
+ end
+ return true, '[', ']', count
+end
+
+function JsonWriter:WriteTable(t)
+ local ba, st, et, n = self:IsArray(t)
+ self:Append(st)
+ if ba then
+ for i = 1, n do
+ self:Write(t[i])
+ if i < n then
+ self:Append(',')
+ end
+ end
+ else
+ local first = true;
+ for k, v in pairs(t) do
+ if not first then
+ self:Append(',')
+ end
+ first = false;
+ self:ParseString(k)
+ self:Append(':')
+ self:Write(v)
+ end
+ end
+ self:Append(et)
+end
+
+function JsonWriter:WriteError(o)
+ error(string.format(
+ "Encoding of %s unsupported",
+ tostring(o)))
+end
+
+function JsonWriter:WriteFunction(o)
+ if o == Null then
+ self:WriteNil()
+ else
+ self:WriteError(o)
+ end
+end
+
+local StringReader = {
+ s = "",
+ i = 0
+}
+
+function StringReader:New(s)
+ local o = {}
+ setmetatable(o, self)
+ self.__index = self
+ o.s = s or o.s
+ return o
+end
+
+function StringReader:Peek()
+ local i = self.i + 1
+ if i <= #self.s then
+ return string.sub(self.s, i, i)
+ end
+ return nil
+end
+
+function StringReader:Next()
+ self.i = self.i+1
+ if self.i <= #self.s then
+ return string.sub(self.s, self.i, self.i)
+ end
+ return nil
+end
+
+function StringReader:All()
+ return self.s
+end
+
+local JsonReader = {
+ escapes = {
+ ['t'] = '\t',
+ ['n'] = '\n',
+ ['f'] = '\f',
+ ['r'] = '\r',
+ ['b'] = '\b',
+ }
+}
+
+function JsonReader:New(s)
+ local o = {}
+ o.reader = StringReader:New(s)
+ setmetatable(o, self)
+ self.__index = self
+ return o;
+end
+
+function JsonReader:Read()
+ self:SkipWhiteSpace()
+ local peek = self:Peek()
+ if peek == nil then
+ error(string.format(
+ "Nil string: '%s'",
+ self:All()))
+ elseif peek == '{' then
+ return self:ReadObject()
+ elseif peek == '[' then
+ return self:ReadArray()
+ elseif peek == '"' then
+ return self:ReadString()
+ elseif string.find(peek, "[%+%-%d]") then
+ return self:ReadNumber()
+ elseif peek == 't' then
+ return self:ReadTrue()
+ elseif peek == 'f' then
+ return self:ReadFalse()
+ elseif peek == 'n' then
+ return self:ReadNull()
+ elseif peek == '/' then
+ self:ReadComment()
+ return self:Read()
+ else
+ return nil
+ end
+end
+
+function JsonReader:ReadTrue()
+ self:TestReservedWord{'t','r','u','e'}
+ return true
+end
+
+function JsonReader:ReadFalse()
+ self:TestReservedWord{'f','a','l','s','e'}
+ return false
+end
+
+function JsonReader:ReadNull()
+ self:TestReservedWord{'n','u','l','l'}
+ return nil
+end
+
+function JsonReader:TestReservedWord(t)
+ for i, v in ipairs(t) do
+ if self:Next() ~= v then
+ error(string.format(
+ "Error reading '%s': %s",
+ table.concat(t),
+ self:All()))
+ end
+ end
+end
+
+function JsonReader:ReadNumber()
+ local result = self:Next()
+ local peek = self:Peek()
+ while peek ~= nil and string.find(
+ peek,
+ "[%+%-%d%.eE]") do
+ result = result .. self:Next()
+ peek = self:Peek()
+ end
+ result = tonumber(result)
+ if result == nil then
+ error(string.format(
+ "Invalid number: '%s'",
+ result))
+ else
+ return result
+ end
+end
+
+function JsonReader:ReadString()
+ local result = ""
+ assert(self:Next() == '"')
+ while self:Peek() ~= '"' do
+ local ch = self:Next()
+ if ch == '\\' then
+ ch = self:Next()
+ if self.escapes[ch] then
+ ch = self.escapes[ch]
+ end
+ end
+ result = result .. ch
+ end
+ assert(self:Next() == '"')
+ local fromunicode = function(m)
+ return string.char(tonumber(m, 16))
+ end
+ return string.gsub(
+ result,
+ "u%x%x(%x%x)",
+ fromunicode)
+end
+
+function JsonReader:ReadComment()
+ assert(self:Next() == '/')
+ local second = self:Next()
+ if second == '/' then
+ self:ReadSingleLineComment()
+ elseif second == '*' then
+ self:ReadBlockComment()
+ else
+ error(string.format(
+ "Invalid comment: %s",
+ self:All()))
+ end
+end
+
+function JsonReader:ReadBlockComment()
+ local done = false
+ while not done do
+ local ch = self:Next()
+ if ch == '*' and self:Peek() == '/' then
+ done = true
+ end
+ if not done and
+ ch == '/' and
+ self:Peek() == "*" then
+ error(string.format(
+ "Invalid comment: %s, '/*' illegal.",
+ self:All()))
+ end
+ end
+ self:Next()
+end
+
+function JsonReader:ReadSingleLineComment()
+ local ch = self:Next()
+ while ch ~= '\r' and ch ~= '\n' do
+ ch = self:Next()
+ end
+end
+
+function JsonReader:ReadArray()
+ local result = {}
+ assert(self:Next() == '[')
+ local done = false
+ if self:Peek() == ']' then
+ done = true;
+ end
+ while not done do
+ local item = self:Read()
+ result[#result+1] = item
+ self:SkipWhiteSpace()
+ if self:Peek() == ']' then
+ done = true
+ end
+ if not done then
+ local ch = self:Next()
+ if ch ~= ',' then
+ error(string.format(
+ "Invalid array: '%s' due to: '%s'",
+ self:All(), ch))
+ end
+ end
+ end
+ assert(']' == self:Next())
+ return result
+end
+
+function JsonReader:ReadObject()
+ local result = {}
+ assert(self:Next() == '{')
+ local done = false
+ if self:Peek() == '}' then
+ done = true
+ end
+ while not done do
+ local key = self:Read()
+ if type(key) ~= "string" then
+ error(string.format(
+ "Invalid non-string object key: %s",
+ key))
+ end
+ self:SkipWhiteSpace()
+ local ch = self:Next()
+ if ch ~= ':' then
+ error(string.format(
+ "Invalid object: '%s' due to: '%s'",
+ self:All(),
+ ch))
+ end
+ self:SkipWhiteSpace()
+ local val = self:Read()
+ result[key] = val
+ self:SkipWhiteSpace()
+ if self:Peek() == '}' then
+ done = true
+ end
+ if not done then
+ ch = self:Next()
+ if ch ~= ',' then
+ error(string.format(
+ "Invalid array: '%s' near: '%s'",
+ self:All(),
+ ch))
+ end
+ end
+ end
+ assert(self:Next() == "}")
+ return result
+end
+
+function JsonReader:SkipWhiteSpace()
+ local p = self:Peek()
+ while p ~= nil and string.find(p, "[%s/]") do
+ if p == '/' then
+ self:ReadComment()
+ else
+ self:Next()
+ end
+ p = self:Peek()
+ end
+end
+
+function JsonReader:Peek()
+ return self.reader:Peek()
+end
+
+function JsonReader:Next()
+ return self.reader:Next()
+end
+
+function JsonReader:All()
+ return self.reader:All()
+end
+
+function Encode(o)
+ local writer = JsonWriter:New()
+ writer:Write(o)
+ return writer:ToString()
+end
+
+function Decode(s)
+ local reader = JsonReader:New(s)
+ return reader:Read()
+end
+
+function Null()
+ return Null
+end
+-------------------- End JSON Parser ------------------------
+
+
+t.DecodeJSON = function(jsonString)
+ if type(jsonString) == "string" then
+ return Decode(jsonString)
+ end
+ print("RbxUtil.DecodeJSON expects string argument!")
+ return nil
+end
+
+t.EncodeJSON = function(jsonTable)
+ return Encode(jsonTable)
+end
+
+
+
+
+
+
+
+
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+--------------------------------------------Terrain Utilities Begin-----------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+--makes a wedge at location x, y, z
+--sets cell x, y, z to default material if parameter is provided, if not sets cell x, y, z to be whatever material it previously w
+--returns true if made a wedge, false if the cell remains a block
+t.MakeWedge = function(x, y, z, defaultmaterial)
+ return game:GetService("Terrain"):AutoWedgeCell(x,y,z)
+end
+
+t.SelectTerrainRegion = function(regionToSelect, color, selectEmptyCells, selectionParent)
+ local terrain = game.Workspace:FindFirstChild("Terrain")
+ if not terrain then return end
+
+ assert(regionToSelect)
+ assert(color)
+
+ if not type(regionToSelect) == "Region3" then
+ error("regionToSelect (first arg), should be of type Region3, but is type",type(regionToSelect))
+ end
+ if not type(color) == "BrickColor" then
+ error("color (second arg), should be of type BrickColor, but is type",type(color))
+ end
+
+ -- frequently used terrain calls (speeds up call, no lookup necessary)
+ local GetCell = terrain.GetCell
+ local WorldToCellPreferSolid = terrain.WorldToCellPreferSolid
+ local CellCenterToWorld = terrain.CellCenterToWorld
+ local emptyMaterial = Enum.CellMaterial.Empty
+
+ -- container for all adornments, passed back to user
+ local selectionContainer = Instance.new("Model")
+ selectionContainer.Name = "SelectionContainer"
+ selectionContainer.Archivable = false
+ if selectionParent then
+ selectionContainer.Parent = selectionParent
+ else
+ selectionContainer.Parent = game.Workspace
+ end
+
+ local updateSelection = nil -- function we return to allow user to update selection
+ local currentKeepAliveTag = nil -- a tag that determines whether adorns should be destroyed
+ local aliveCounter = 0 -- helper for currentKeepAliveTag
+ local lastRegion = nil -- used to stop updates that do nothing
+ local adornments = {} -- contains all adornments
+ local reusableAdorns = {}
+
+ local selectionPart = Instance.new("Part")
+ selectionPart.Name = "SelectionPart"
+ selectionPart.Transparency = 1
+ selectionPart.Anchored = true
+ selectionPart.Locked = true
+ selectionPart.CanCollide = false
+ selectionPart.FormFactor = Enum.FormFactor.Custom
+ selectionPart.Size = Vector3.new(4.2,4.2,4.2)
+
+ local selectionBox = Instance.new("SelectionBox")
+
+ -- srs translation from region3 to region3int16
+ function Region3ToRegion3int16(region3)
+ local theLowVec = region3.CFrame.p - (region3.Size/2) + Vector3.new(2,2,2)
+ local lowCell = WorldToCellPreferSolid(terrain,theLowVec)
+
+ local theHighVec = region3.CFrame.p + (region3.Size/2) - Vector3.new(2,2,2)
+ local highCell = WorldToCellPreferSolid(terrain, theHighVec)
+
+ local highIntVec = Vector3int16.new(highCell.x,highCell.y,highCell.z)
+ local lowIntVec = Vector3int16.new(lowCell.x,lowCell.y,lowCell.z)
+
+ return Region3int16.new(lowIntVec,highIntVec)
+ end
+
+ -- helper function that creates the basis for a selection box
+ function createAdornment(theColor)
+ local selectionPartClone = nil
+ local selectionBoxClone = nil
+
+ if #reusableAdorns > 0 then
+ selectionPartClone = reusableAdorns[1]["part"]
+ selectionBoxClone = reusableAdorns[1]["box"]
+ table.remove(reusableAdorns,1)
+
+ selectionBoxClone.Visible = true
+ else
+ selectionPartClone = selectionPart:Clone()
+ selectionPartClone.Archivable = false
+
+ selectionBoxClone = selectionBox:Clone()
+ selectionBoxClone.Archivable = false
+
+ selectionBoxClone.Adornee = selectionPartClone
+ selectionBoxClone.Parent = selectionContainer
+
+ selectionBoxClone.Adornee = selectionPartClone
+
+ selectionBoxClone.Parent = selectionContainer
+ end
+
+ if theColor then
+ selectionBoxClone.Color = theColor
+ end
+
+ return selectionPartClone, selectionBoxClone
+ end
+
+ -- iterates through all current adornments and deletes any that don't have latest tag
+ function cleanUpAdornments()
+ for cellPos, adornTable in pairs(adornments) do
+
+ if adornTable.KeepAlive ~= currentKeepAliveTag then -- old news, we should get rid of this
+ adornTable.SelectionBox.Visible = false
+ table.insert(reusableAdorns,{part = adornTable.SelectionPart, box = adornTable.SelectionBox})
+ adornments[cellPos] = nil
+ end
+ end
+ end
+
+ -- helper function to update tag
+ function incrementAliveCounter()
+ aliveCounter = aliveCounter + 1
+ if aliveCounter > 1000000 then
+ aliveCounter = 0
+ end
+ return aliveCounter
+ end
+
+ -- finds full cells in region and adorns each cell with a box, with the argument color
+ function adornFullCellsInRegion(region, color)
+ local regionBegin = region.CFrame.p - (region.Size/2) + Vector3.new(2,2,2)
+ local regionEnd = region.CFrame.p + (region.Size/2) - Vector3.new(2,2,2)
+
+ local cellPosBegin = WorldToCellPreferSolid(terrain, regionBegin)
+ local cellPosEnd = WorldToCellPreferSolid(terrain, regionEnd)
+
+ currentKeepAliveTag = incrementAliveCounter()
+ for y = cellPosBegin.y, cellPosEnd.y do
+ for z = cellPosBegin.z, cellPosEnd.z do
+ for x = cellPosBegin.x, cellPosEnd.x do
+ local cellMaterial = GetCell(terrain, x, y, z)
+
+ if cellMaterial ~= emptyMaterial then
+ local cframePos = CellCenterToWorld(terrain, x, y, z)
+ local cellPos = Vector3int16.new(x,y,z)
+
+ local updated = false
+ for cellPosAdorn, adornTable in pairs(adornments) do
+ if cellPosAdorn == cellPos then
+ adornTable.KeepAlive = currentKeepAliveTag
+ if color then
+ adornTable.SelectionBox.Color = color
+ end
+ updated = true
+ break
+ end
+ end
+
+ if not updated then
+ local selectionPart, selectionBox = createAdornment(color)
+ selectionPart.Size = Vector3.new(4,4,4)
+ selectionPart.CFrame = CFrame.new(cframePos)
+ local adornTable = {SelectionPart = selectionPart, SelectionBox = selectionBox, KeepAlive = currentKeepAliveTag}
+ adornments[cellPos] = adornTable
+ end
+ end
+ end
+ end
+ end
+ cleanUpAdornments()
+ end
+
+
+ ------------------------------------- setup code ------------------------------
+ lastRegion = regionToSelect
+
+ if selectEmptyCells then -- use one big selection to represent the area selected
+ local selectionPart, selectionBox = createAdornment(color)
+
+ selectionPart.Size = regionToSelect.Size
+ selectionPart.CFrame = regionToSelect.CFrame
+
+ adornments.SelectionPart = selectionPart
+ adornments.SelectionBox = selectionBox
+
+ updateSelection =
+ function (newRegion, color)
+ if newRegion and newRegion ~= lastRegion then
+ lastRegion = newRegion
+ selectionPart.Size = newRegion.Size
+ selectionPart.CFrame = newRegion.CFrame
+ end
+ if color then
+ selectionBox.Color = color
+ end
+ end
+ else -- use individual cell adorns to represent the area selected
+ adornFullCellsInRegion(regionToSelect, color)
+ updateSelection =
+ function (newRegion, color)
+ if newRegion and newRegion ~= lastRegion then
+ lastRegion = newRegion
+ adornFullCellsInRegion(newRegion, color)
+ end
+ end
+
+ end
+
+ local destroyFunc = function()
+ updateSelection = nil
+ if selectionContainer then selectionContainer:Destroy() end
+ adornments = nil
+ end
+
+ return updateSelection, destroyFunc
+end
+
+-----------------------------Terrain Utilities End-----------------------------
+
+
+
+
+
+
+
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------Signal class begin------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+--[[
+A 'Signal' object identical to the internal RBXScriptSignal object in it's public API and semantics. This function
+can be used to create "custom events" for user-made code.
+API:
+Method :connect( function handler )
+ Arguments: The function to connect to.
+ Returns: A new connection object which can be used to disconnect the connection
+ Description: Connects this signal to the function specified by |handler|. That is, when |fire( ... )| is called for
+ the signal the |handler| will be called with the arguments given to |fire( ... )|. Note, the functions
+ connected to a signal are called in NO PARTICULAR ORDER, so connecting one function after another does
+ NOT mean that the first will be called before the second as a result of a call to |fire|.
+
+Method :disconnect()
+ Arguments: None
+ Returns: None
+ Description: Disconnects all of the functions connected to this signal.
+
+Method :fire( ... )
+ Arguments: Any arguments are accepted
+ Returns: None
+ Description: Calls all of the currently connected functions with the given arguments.
+
+Method :wait()
+ Arguments: None
+ Returns: The arguments given to fire
+ Description: This call blocks until
+]]
+
+function t.CreateSignal()
+ local this = {}
+
+ local mBindableEvent = Instance.new('BindableEvent')
+ local mAllCns = {} --all connection objects returned by mBindableEvent::connect
+
+ --main functions
+ function this:connect(func)
+ if self ~= this then error("connect must be called with `:`, not `.`", 2) end
+ if type(func) ~= 'function' then
+ error("Argument #1 of connect must be a function, got a "..type(func), 2)
+ end
+ local cn = mBindableEvent.Event:connect(func)
+ mAllCns[cn] = true
+ local pubCn = {}
+ function pubCn:disconnect()
+ cn:disconnect()
+ mAllCns[cn] = nil
+ end
+ return pubCn
+ end
+ function this:disconnect()
+ if self ~= this then error("disconnect must be called with `:`, not `.`", 2) end
+ for cn, _ in pairs(mAllCns) do
+ cn:disconnect()
+ mAllCns[cn] = nil
+ end
+ end
+ function this:wait()
+ if self ~= this then error("wait must be called with `:`, not `.`", 2) end
+ return mBindableEvent.Event:wait()
+ end
+ function this:fire(...)
+ if self ~= this then error("fire must be called with `:`, not `.`", 2) end
+ mBindableEvent:Fire(...)
+ end
+
+ return this
+end
+
+------------------------------------------------- Sigal class End ------------------------------------------------------
+
+
+
+
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+-----------------------------------------------Create Function Begins---------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+--[[
+A "Create" function for easy creation of Roblox instances. The function accepts a string which is the classname of
+the object to be created. The function then returns another function which either accepts accepts no arguments, in
+which case it simply creates an object of the given type, or a table argument that may contain several types of data,
+in which case it mutates the object in varying ways depending on the nature of the aggregate data. These are the
+type of data and what operation each will perform:
+1) A string key mapping to some value:
+ Key-Value pairs in this form will be treated as properties of the object, and will be assigned in NO PARTICULAR
+ ORDER. If the order in which properties is assigned matter, then they must be assigned somewhere else than the
+ |Create| call's body.
+
+2) An integral key mapping to another Instance:
+ Normal numeric keys mapping to Instances will be treated as children if the object being created, and will be
+ parented to it. This allows nice recursive calls to Create to create a whole hierarchy of objects without a
+ need for temporary variables to store references to those objects.
+
+3) A key which is a value returned from Create.Event( eventname ), and a value which is a function function
+ The Create.E( string ) function provides a limited way to connect to signals inside of a Create hierarchy
+ for those who really want such a functionality. The name of the event whose name is passed to
+ Create.E( string )
+
+4) A key which is the Create function itself, and a value which is a function
+ The function will be run with the argument of the object itself after all other initialization of the object is
+ done by create. This provides a way to do arbitrary things involving the object from withing the create
+ hierarchy.
+ Note: This function is called SYNCHRONOUSLY, that means that you should only so initialization in
+ it, not stuff which requires waiting, as the Create call will block until it returns. While waiting in the
+ constructor callback function is possible, it is probably not a good design choice.
+ Note: Since the constructor function is called after all other initialization, a Create block cannot have two
+ constructor functions, as it would not be possible to call both of them last, also, this would be unnecessary.
+
+
+Some example usages:
+
+A simple example which uses the Create function to create a model object and assign two of it's properties.
+local model = Create'Model'{
+ Name = 'A New model',
+ Parent = game.Workspace,
+}
+
+
+An example where a larger hierarchy of object is made. After the call the hierarchy will look like this:
+Model_Container
+ |-ObjectValue
+ | |
+ | `-BoolValueChild
+ `-IntValue
+
+local model = Create'Model'{
+ Name = 'Model_Container',
+ Create'ObjectValue'{
+ Create'BoolValue'{
+ Name = 'BoolValueChild',
+ },
+ },
+ Create'IntValue'{},
+}
+
+
+An example using the event syntax:
+
+local part = Create'Part'{
+ [Create.E'Touched'] = function(part)
+ print("I was touched by "..part.Name)
+ end,
+}
+
+
+An example using the general constructor syntax:
+
+local model = Create'Part'{
+ [Create] = function(this)
+ print("Constructor running!")
+ this.Name = GetGlobalFoosAndBars(this)
+ end,
+}
+
+
+Note: It is also perfectly legal to save a reference to the function returned by a call Create, this will not cause
+ any unexpected behavior. EG:
+ local partCreatingFunction = Create'Part'
+ local part = partCreatingFunction()
+]]
+
+--the Create function need to be created as a functor, not a function, in order to support the Create.E syntax, so it
+--will be created in several steps rather than as a single function declaration.
+local function Create_PrivImpl(objectType)
+ if type(objectType) ~= 'string' then
+ error("Argument of Create must be a string", 2)
+ end
+ --return the proxy function that gives us the nice Create'string'{data} syntax
+ --The first function call is a function call using Lua's single-string-argument syntax
+ --The second function call is using Lua's single-table-argument syntax
+ --Both can be chained together for the nice effect.
+ return function(dat)
+ --default to nothing, to handle the no argument given case
+ dat = dat or {}
+
+ --make the object to mutate
+ local obj = Instance.new(objectType)
+
+ --stored constructor function to be called after other initialization
+ local ctor = nil
+
+ for k, v in pairs(dat) do
+ --add property
+ if type(k) == 'string' then
+ obj[k] = v
+
+
+ --add child
+ elseif type(k) == 'number' then
+ if type(v) ~= 'userdata' then
+ error("Bad entry in Create body: Numeric keys must be paired with children, got a: "..type(v), 2)
+ end
+ v.Parent = obj
+
+
+ --event connect
+ elseif type(k) == 'table' and k.__eventname then
+ if type(v) ~= 'function' then
+ error("Bad entry in Create body: Key `[Create.E\'"..k.__eventname.."\']` must have a function value\
+ got: "..tostring(v), 2)
+ end
+ obj[k.__eventname]:connect(v)
+
+
+ --define constructor function
+ elseif k == t.Create then
+ if type(v) ~= 'function' then
+ error("Bad entry in Create body: Key `[Create]` should be paired with a constructor function, \
+ got: "..tostring(v), 2)
+ elseif ctor then
+ --ctor already exists, only one allowed
+ error("Bad entry in Create body: Only one constructor function is allowed", 2)
+ end
+ ctor = v
+
+
+ else
+ error("Bad entry ("..tostring(k).." => "..tostring(v)..") in Create body", 2)
+ end
+ end
+
+ --apply constructor function if it exists
+ if ctor then
+ ctor(obj)
+ end
+
+ --return the completed object
+ return obj
+ end
+end
+
+--now, create the functor:
+t.Create = setmetatable({}, {__call = function(tb, ...) return Create_PrivImpl(...) end})
+
+--and create the "Event.E" syntax stub. Really it's just a stub to construct a table which our Create
+--function can recognize as special.
+t.Create.E = function(eventName)
+ return {__eventname = eventName}
+end
+
+-------------------------------------------------Create function End----------------------------------------------------
+
+
+
+
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------Documentation Begin-----------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+------------------------------------------------------------------------------------------------------------------------
+
+t.Help =
+ function(funcNameOrFunc)
+ --input argument can be a string or a function. Should return a description (of arguments and expected side effects)
+ if funcNameOrFunc == "DecodeJSON" or funcNameOrFunc == t.DecodeJSON then
+ return "Function DecodeJSON. " ..
+ "Arguments: (string). " ..
+ "Side effect: returns a table with all parsed JSON values"
+ end
+ if funcNameOrFunc == "EncodeJSON" or funcNameOrFunc == t.EncodeJSON then
+ return "Function EncodeJSON. " ..
+ "Arguments: (table). " ..
+ "Side effect: returns a string composed of argument table in JSON data format"
+ end
+ if funcNameOrFunc == "MakeWedge" or funcNameOrFunc == t.MakeWedge then
+ return "Function MakeWedge. " ..
+ "Arguments: (x, y, z, [default material]). " ..
+ "Description: Makes a wedge at location x, y, z. Sets cell x, y, z to default material if "..
+ "parameter is provided, if not sets cell x, y, z to be whatever material it previously was. "..
+ "Returns true if made a wedge, false if the cell remains a block "
+ end
+ if funcNameOrFunc == "SelectTerrainRegion" or funcNameOrFunc == t.SelectTerrainRegion then
+ return "Function SelectTerrainRegion. " ..
+ "Arguments: (regionToSelect, color, selectEmptyCells, selectionParent). " ..
+ "Description: Selects all terrain via a series of selection boxes within the regionToSelect " ..
+ "(this should be a region3 value). The selection box color is detemined by the color argument " ..
+ "(should be a brickcolor value). SelectionParent is the parent that the selection model gets placed to (optional)." ..
+ "SelectEmptyCells is bool, when true will select all cells in the " ..
+ "region, otherwise we only select non-empty cells. Returns a function that can update the selection," ..
+ "arguments to said function are a new region3 to select, and the adornment color (color arg is optional). " ..
+ "Also returns a second function that takes no arguments and destroys the selection"
+ end
+ if funcNameOrFunc == "CreateSignal" or funcNameOrFunc == t.CreateSignal then
+ return "Function CreateSignal. "..
+ "Arguments: None. "..
+ "Returns: The newly created Signal object. This object is identical to the RBXScriptSignal class "..
+ "used for events in Objects, but is a Lua-side object so it can be used to create custom events in"..
+ "Lua code. "..
+ "Methods of the Signal object: :connect, :wait, :fire, :disconnect. "..
+ "For more info you can pass the method name to the Help function, or view the wiki page "..
+ "for this library. EG: Help('Signal:connect')."
+ end
+ if funcNameOrFunc == "Signal:connect" then
+ return "Method Signal:connect. "..
+ "Arguments: (function handler). "..
+ "Return: A connection object which can be used to disconnect the connection to this handler. "..
+ "Description: Connectes a handler function to this Signal, so that when |fire| is called the "..
+ "handler function will be called with the arguments passed to |fire|."
+ end
+ if funcNameOrFunc == "Signal:wait" then
+ return "Method Signal:wait. "..
+ "Arguments: None. "..
+ "Returns: The arguments passed to the next call to |fire|. "..
+ "Description: This call does not return until the next call to |fire| is made, at which point it "..
+ "will return the values which were passed as arguments to that |fire| call."
+ end
+ if funcNameOrFunc == "Signal:fire" then
+ return "Method Signal:fire. "..
+ "Arguments: Any number of arguments of any type. "..
+ "Returns: None. "..
+ "Description: This call will invoke any connected handler functions, and notify any waiting code "..
+ "attached to this Signal to continue, with the arguments passed to this function. Note: The calls "..
+ "to handlers are made asynchronously, so this call will return immediately regardless of how long "..
+ "it takes the connected handler functions to complete."
+ end
+ if funcNameOrFunc == "Signal:disconnect" then
+ return "Method Signal:disconnect. "..
+ "Arguments: None. "..
+ "Returns: None. "..
+ "Description: This call disconnects all handlers attacched to this function, note however, it "..
+ "does NOT make waiting code continue, as is the behavior of normal Roblox events. This method "..
+ "can also be called on the connection object which is returned from Signal:connect to only "..
+ "disconnect a single handler, as opposed to this method, which will disconnect all handlers."
+ end
+ if funcNameOrFunc == "Create" then
+ return "Function Create. "..
+ "Arguments: A table containing information about how to construct a collection of objects. "..
+ "Returns: The constructed objects. "..
+ "Descrition: Create is a very powerfull function, whose description is too long to fit here, and "..
+ "is best described via example, please see the wiki page for a description of how to use it."
+ end
+ end
+
+--------------------------------------------Documentation Ends----------------------------------------------------------
+
+return t
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/files/CoreGui/60595695 b/app/files/CoreGui/60595695
new file mode 100644
index 0000000..dbec354
--- /dev/null
+++ b/app/files/CoreGui/60595695
@@ -0,0 +1,26 @@
+-- Library Registration Script
+-- This script is used to register RbxLua libraries on game servers, so game scripts have
+-- access to all of the libraries (otherwise only local scripts do)
+
+local deepakTestingPlace = 3569749
+local sc = game:GetService("ScriptContext")
+local tries = 0
+
+while not sc and tries < 3 do
+ tries = tries + 1
+ sc = game:GetService("ScriptContext")
+ wait(0.2)
+end
+
+if sc then
+ sc:RegisterLibrary("Libraries/RbxGui", "45284430")
+ sc:RegisterLibrary("Libraries/RbxGear", "45374389")
+ if game.PlaceId == deepakTestingPlace then
+ sc:RegisterLibrary("Libraries/RbxStatus", "52177566")
+ end
+ sc:RegisterLibrary("Libraries/RbxUtility", "60595411")
+ sc:RegisterLibrary("Libraries/RbxStamper", "73157242")
+ sc:LibraryRegistrationComplete()
+else
+ print("failed to find script context, libraries did not load")
+end
diff --git a/app/files/CoreGui/60595696 b/app/files/CoreGui/60595696
new file mode 100644
index 0000000..dbec354
--- /dev/null
+++ b/app/files/CoreGui/60595696
@@ -0,0 +1,26 @@
+-- Library Registration Script
+-- This script is used to register RbxLua libraries on game servers, so game scripts have
+-- access to all of the libraries (otherwise only local scripts do)
+
+local deepakTestingPlace = 3569749
+local sc = game:GetService("ScriptContext")
+local tries = 0
+
+while not sc and tries < 3 do
+ tries = tries + 1
+ sc = game:GetService("ScriptContext")
+ wait(0.2)
+end
+
+if sc then
+ sc:RegisterLibrary("Libraries/RbxGui", "45284430")
+ sc:RegisterLibrary("Libraries/RbxGear", "45374389")
+ if game.PlaceId == deepakTestingPlace then
+ sc:RegisterLibrary("Libraries/RbxStatus", "52177566")
+ end
+ sc:RegisterLibrary("Libraries/RbxUtility", "60595411")
+ sc:RegisterLibrary("Libraries/RbxStamper", "73157242")
+ sc:LibraryRegistrationComplete()
+else
+ print("failed to find script context, libraries did not load")
+end
diff --git a/app/files/CoreGui/64164692 b/app/files/CoreGui/64164692
new file mode 100644
index 0000000..4011edb
--- /dev/null
+++ b/app/files/CoreGui/64164692
@@ -0,0 +1,146 @@
+-- Responsible for giving out tools in personal servers
+
+-- first, lets see if buildTools have already been created
+-- create the object in lighting (TODO: move to some sort of "container" object when we have one)
+local toolsArray = game.Lighting:FindFirstChild("BuildToolsModel")
+local ownerArray = game.Lighting:FindFirstChild("OwnerToolsModel")
+local hasBuildTools = false
+
+function getIds(idTable, assetTable)
+ for i = 1, #idTable do
+ local model = game:GetService("InsertService"):LoadAsset(idTable[i])
+ if model then
+ local children = model:GetChildren()
+ for i = 1, #children do
+ if children[i]:IsA("Tool") then
+ table.insert(assetTable,children[i])
+ end
+ end
+ end
+ end
+end
+
+function storeInLighting(modelName, assetTable)
+ local model = Instance.new("Model")
+ model.Archivable = false
+ model.Name = modelName
+
+ for i = 1, #assetTable do
+ assetTable[i].Parent = model
+ end
+
+ if not game.Lighting:FindFirstChild(modelName) then -- no one beat us to it, we get to insert
+ model.Parent = game.Lighting
+ end
+end
+
+if not toolsArray then -- no one has made build tools yet, we get to!
+ local buildToolIds = {}
+ local ownerToolIds = {}
+
+ table.insert(buildToolIds,73089166) -- PartSelectionTool
+ table.insert(buildToolIds,73089190) -- DeleteTool
+ table.insert(buildToolIds,73089204) -- CloneTool
+ table.insert(buildToolIds,73089214) -- RotateTool
+ table.insert(buildToolIds,73089239) -- ConfigTool
+ table.insert(buildToolIds,73089259) -- WiringTool
+ table.insert(buildToolIds,58921588) -- ClassicTool
+
+ table.insert(ownerToolIds, 65347268)
+
+ -- next, create array of our tools
+ local buildTools = {}
+ local ownerTools = {}
+
+ getIds(buildToolIds, buildTools)
+ getIds(ownerToolIds, ownerTools)
+
+ storeInLighting("BuildToolsModel",buildTools)
+ storeInLighting("OwnerToolsModel",ownerTools)
+
+ toolsArray = game.Lighting:FindFirstChild("BuildToolsModel")
+ ownerArray = game.Lighting:FindFirstChild("OwnerToolsModel")
+end
+
+local localBuildTools = {}
+
+function giveBuildTools()
+ if not hasBuildTools then
+ hasBuildTools = true
+ local theTools = toolsArray:GetChildren()
+ for i = 1, #theTools do
+ local toolClone = theTools[i]:clone()
+ if toolClone then
+ toolClone.Parent = game.Players.LocalPlayer.Backpack
+ table.insert(localBuildTools,toolClone)
+ end
+ end
+ end
+end
+
+function giveOwnerTools()
+ local theOwnerTools = ownerArray:GetChildren()
+ for i = 1, #theOwnerTools do
+ local ownerToolClone = theOwnerTools[i]:clone()
+ if ownerToolClone then
+ ownerToolClone.Parent = game.Players.LocalPlayer.Backpack
+ table.insert(localBuildTools,ownerToolClone)
+ end
+ end
+end
+
+function removeBuildTools()
+ if hasBuildTools then
+ hasBuildTools = false
+ for i = 1, #localBuildTools do
+ localBuildTools[i].Parent = nil
+ end
+ localBuildTools = {}
+ end
+end
+
+if game.Players.LocalPlayer.HasBuildTools then
+ giveBuildTools()
+end
+if game.Players.LocalPlayer.PersonalServerRank >= 255 then
+ giveOwnerTools()
+end
+
+local debounce = false
+game.Players.LocalPlayer.Changed:connect(function(prop)
+ if prop == "HasBuildTools" then
+ while debounce do
+ wait(0.5)
+ end
+
+ debounce = true
+
+ if game.Players.LocalPlayer.HasBuildTools then
+ giveBuildTools()
+ else
+ removeBuildTools()
+ end
+
+ if game.Players.LocalPlayer.PersonalServerRank >= 255 then
+ giveOwnerTools()
+ end
+
+ debounce = false
+ elseif prop == "PersonalServerRank" then
+ if game.Players.LocalPlayer.PersonalServerRank >= 255 then
+ giveOwnerTools()
+ elseif game.Players.LocalPlayer.PersonalServerRank <= 0 then
+ game.Players.LocalPlayer:Remove() -- you're banned, goodbye!
+ end
+ end
+end)
+
+game.Players.LocalPlayer.CharacterAdded:connect(function()
+ hasBuildTools = false
+ if game.Players.LocalPlayer.HasBuildTools then
+ giveBuildTools()
+ end
+ if game.Players.LocalPlayer.PersonalServerRank >= 255 then
+ giveOwnerTools()
+ end
+end)
\ No newline at end of file
diff --git a/app/files/CoreGui/73157242 b/app/files/CoreGui/73157242
new file mode 100644
index 0000000..8d88b84
--- /dev/null
+++ b/app/files/CoreGui/73157242
@@ -0,0 +1,2207 @@
+local t = {}
+
+function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+end
+
+-- Do a line/plane intersection. The line starts at the camera. The plane is at y == 0, normal(0, 1, 0)
+--
+-- vectorPos - End point of the line.
+--
+-- Return:
+-- cellPos - The terrain cell intersection point if there is one, vectorPos if there isn't.
+-- hit - Whether there was a plane intersection. Value is true if there was, false if not.
+function PlaneIntersection(vectorPos)
+ local hit = false
+ local currCamera = game.Workspace.CurrentCamera
+ local startPos = Vector3.new(currCamera.CoordinateFrame.p.X, currCamera.CoordinateFrame.p.Y, currCamera.CoordinateFrame.p.Z)
+ local endPos = Vector3.new(vectorPos.X, vectorPos.Y, vectorPos.Z)
+ local normal = Vector3.new(0, 1, 0)
+ local p3 = Vector3.new(0, 0, 0)
+ local startEndDot = normal:Dot(endPos - startPos)
+ local cellPos = vectorPos
+ if startEndDot ~= 0 then
+ local t = normal:Dot(p3 - startPos) / startEndDot
+ if(t >=0 and t <=1) then
+ local intersection = ((endPos - startPos) * t) + startPos
+ cellPos = game.Workspace.Terrain:WorldToCell(intersection)
+ hit = true
+ end
+ end
+
+ return cellPos, hit
+end
+
+
+-- Purpose:
+-- Checks for terrain touched by the mouse hit.
+-- Will do a plane intersection if no terrain is touched.
+--
+-- mouse - Mouse to check the .hit for.
+--
+-- Return:
+-- cellPos - Cell position hit. Nil if none.
+function GetTerrainForMouse(mouse)
+ -- There was no target, so all it could be is a plane intersection.
+ -- Check for a plane intersection. If there isn't one then nothing will get hit.
+ local cell = game.Workspace.Terrain:WorldToCellPreferSolid(Vector3.new(mouse.hit.x, mouse.hit.y, mouse.hit.z))
+ local planeLoc = nil
+ -- If nothing was hit, do the plane intersection.
+ if 0 == game.Workspace.Terrain:GetCell(cell.X, cell.Y, cell.Z).Value then
+ cell = nil
+ planeLoc, hit = PlaneIntersection(Vector3.new(mouse.hit.x, mouse.hit.y, mouse.hit.z))
+ if hit then
+ cell = planeLoc
+ end
+ end
+ return cell
+end
+
+-- setup helper functions
+local insertBoundingBoxOverlapVector = Vector3.new(.3, .3, .3) -- we can still stamp if our character extrudes into the target stamping space by .3 or fewer units
+
+-- rotates a model by yAngle radians about the global y-axis
+local function rotatePartAndChildren(part, rotCF, offsetFromOrigin)
+ -- rotate this thing, if it's a part
+ if part:IsA("BasePart") then
+ part.CFrame = (rotCF * (part.CFrame - offsetFromOrigin)) + offsetFromOrigin
+ end
+
+ -- recursively do the same to all children
+ local partChildren = part:GetChildren()
+ for c = 1, #partChildren do rotatePartAndChildren(partChildren[c], rotCF, offsetFromOrigin) end
+end
+
+local function modelRotate(model, yAngle)
+ local rotCF = CFrame.Angles(0, yAngle, 0)
+ local offsetFromOrigin = model:GetModelCFrame().p
+
+ rotatePartAndChildren(model, rotCF, offsetFromOrigin)
+end
+
+
+local function collectParts(object, baseParts, scripts, decals)
+ if object:IsA("BasePart") then
+ baseParts[#baseParts+1] = object
+ elseif object:IsA("Script") then
+ scripts[#scripts+1] = object
+ elseif object:IsA("Decal") then
+ decals[#decals+1] = object
+ end
+
+ for index,child in pairs(object:GetChildren()) do
+ collectParts(child, baseParts, scripts, decals)
+ end
+end
+
+local function clusterPartsInRegion(startVector, endVector)
+ local cluster = game.Workspace:FindFirstChild("Terrain")
+
+ local startCell = cluster:WorldToCell(startVector)
+ local endCell = cluster:WorldToCell(endVector)
+
+ local startX = startCell.X
+ local startY = startCell.Y
+ local startZ = startCell.Z
+
+ local endX = endCell.X
+ local endY = endCell.Y
+ local endZ = endCell.Z
+
+ if startX < cluster.MaxExtents.Min.X then startX = cluster.MaxExtents.Min.X end
+ if startY < cluster.MaxExtents.Min.Y then startY = cluster.MaxExtents.Min.Y end
+ if startZ < cluster.MaxExtents.Min.Z then startZ = cluster.MaxExtents.Min.Z end
+
+ if endX > cluster.MaxExtents.Max.X then endX = cluster.MaxExtents.Max.X end
+ if endY > cluster.MaxExtents.Max.Y then endY = cluster.MaxExtents.Max.Y end
+ if endZ > cluster.MaxExtents.Max.Z then endZ = cluster.MaxExtents.Max.Z end
+
+ for x = startX, endX do
+ for y = startY, endY do
+ for z = startZ, endZ do
+ if (cluster:GetCell(x, y, z).Value) > 0 then return true end
+ end
+ end
+ end
+
+ return false
+end
+
+local function findSeatsInModel(parent, seatTable)
+ if not parent then return end
+
+ if parent.className == "Seat" or parent.className == "VehicleSeat" then
+ table.insert(seatTable, parent)
+ end
+ local myChildren = parent:GetChildren()
+ for j = 1, #myChildren do
+ findSeatsInModel(myChildren[j], seatTable)
+ end
+end
+
+local function setSeatEnabledStatus(model, isEnabled)
+ local seatList = {}
+ findSeatsInModel(model, seatList)
+
+ if isEnabled then
+ -- remove any welds called "SeatWeld" in seats
+ for i = 1, #seatList do
+ local nextSeat = seatList[i]:FindFirstChild("SeatWeld")
+ while nextSeat do nextSeat:Remove() nextSeat = seatList[i]:FindFirstChild("SeatWeld") end
+ end
+ else
+ -- put a weld called "SeatWeld" in every seat
+ -- this tricks it into thinking there's already someone sitting there, and it won't make you sit XD
+ for i = 1, #seatList do
+ local fakeWeld = Instance.new("Weld")
+ fakeWeld.Name = "SeatWeld"
+ fakeWeld.Parent = seatList[i]
+ end
+ end
+end
+
+local function autoAlignToFace(parts)
+ local aatf = parts:FindFirstChild("AutoAlignToFace")
+ if aatf then return aatf.Value else return false end
+end
+
+local function getClosestAlignedWorldDirection(aVector3InWorld)
+ local xDir = Vector3.new(1,0,0)
+ local yDir = Vector3.new(0,1,0)
+ local zDir = Vector3.new(0,0,1)
+ local xDot = aVector3InWorld.x * xDir.x + aVector3InWorld.y * xDir.y + aVector3InWorld.z * xDir.z
+ local yDot = aVector3InWorld.x * yDir.x + aVector3InWorld.y * yDir.y + aVector3InWorld.z * yDir.z
+ local zDot = aVector3InWorld.x * zDir.x + aVector3InWorld.y * zDir.y + aVector3InWorld.z * zDir.z
+
+ if math.abs(xDot) > math.abs(yDot) and math.abs(xDot) > math.abs(zDot) then
+ if xDot > 0 then
+ return 0
+ else
+ return 3
+ end
+ elseif math.abs(yDot) > math.abs(xDot) and math.abs(yDot) > math.abs(zDot) then
+ if yDot > 0 then
+ return 1
+ else
+ return 4
+ end
+ else
+ if zDot > 0 then
+ return 2
+ else
+ return 5
+ end
+ end
+end
+
+local function positionPartsAtCFrame3(aCFrame, currentParts)
+ local insertCFrame = nil
+ if not currentParts then return currentParts end
+ if currentParts and (currentParts:IsA("Model") or currentParts:IsA("Tool")) then
+ insertCFrame = currentParts:GetModelCFrame()
+ currentParts:TranslateBy(aCFrame.p - insertCFrame.p)
+ else
+ currentParts.CFrame = aCFrame
+ end
+ return currentParts
+end
+
+local function calcRayHitTime(rayStart, raySlope, intersectionPlane)
+ if math.abs(raySlope) < .01 then return 0 end -- 0 slope --> we just say intersection time is 0, and sidestep this dimension
+ return (intersectionPlane - rayStart) / raySlope
+end
+
+local function modelTargetSurface(partOrModel, rayStart, rayEnd)
+ if not partOrModel then
+ return 0
+ end
+
+ local modelCFrame = nil
+ local modelSize = nil
+ if partOrModel:IsA("Model") then
+ modelCFrame = partOrModel:GetModelCFrame()
+ modelSize = partOrModel:GetModelSize()
+ else
+ modelCFrame = partOrModel.CFrame
+ modelSize = partOrModel.Size
+ end
+
+ local mouseRayStart = modelCFrame:pointToObjectSpace(rayStart)
+ local mouseRayEnd = modelCFrame:pointToObjectSpace(rayEnd)
+ local mouseSlope = mouseRayEnd - mouseRayStart
+
+ local xPositive = 1
+ local yPositive = 1
+ local zPositive = 1
+ if mouseSlope.X > 0 then xPositive = -1 end
+ if mouseSlope.Y > 0 then yPositive = -1 end
+ if mouseSlope.Z > 0 then zPositive = -1 end
+
+ -- find which surface the transformed mouse ray hits (using modelSize):
+ local xHitTime = calcRayHitTime(mouseRayStart.X, mouseSlope.X, modelSize.X/2 * xPositive)
+ local yHitTime = calcRayHitTime(mouseRayStart.Y, mouseSlope.Y, modelSize.Y/2 * yPositive)
+ local zHitTime = calcRayHitTime(mouseRayStart.Z, mouseSlope.Z, modelSize.Z/2 * zPositive)
+
+ local hitFace = 0
+
+ --if xHitTime >= 0 and yHitTime >= 0 and zHitTime >= 0 then
+ if xHitTime > yHitTime then
+ if xHitTime > zHitTime then
+ -- xFace is hit
+ hitFace = 1*xPositive
+ else
+ -- zFace is hit
+ hitFace = 3*zPositive
+ end
+ else
+ if yHitTime > zHitTime then
+ -- yFace is hit
+ hitFace = 2*yPositive
+ else
+ -- zFace is hit
+ hitFace = 3*zPositive
+ end
+ end
+
+ return hitFace
+end
+
+local function getBoundingBox2(partOrModel)
+
+ -- for models, the bounding box is defined as the minimum and maximum individual part bounding boxes
+ -- relative to the first part's coordinate frame.
+ local minVec = Vector3.new(math.huge, math.huge, math.huge)
+ local maxVec = Vector3.new(-math.huge, -math.huge, -math.huge)
+
+ if partOrModel:IsA("Terrain") then
+ minVec = Vector3.new(-2, -2, -2)
+ maxVec = Vector3.new(2, 2, 2)
+ elseif partOrModel:IsA("BasePart") then
+ minVec = -0.5 * partOrModel.Size
+ maxVec = -minVec
+ else
+ maxVec = partOrModel:GetModelSize()*0.5
+ minVec = -maxVec
+ end
+
+ -- Adjust bounding box to reflect what the model or part author wants in terms of justification
+ local justifyValue = partOrModel:FindFirstChild("Justification")
+ if justifyValue ~= nil then
+ -- find the multiple of 4 that contains the model
+ justify = justifyValue.Value
+ two = Vector3.new(2, 2, 2)
+ actualBox = maxVec - minVec - Vector3.new(0.01, 0.01, 0.01)
+ containingGridBox = Vector3.new(4 * math.ceil(actualBox.x/4), 4 * math.ceil(actualBox.y/4), 4 * math.ceil(actualBox.z/4))
+ adjustment = containingGridBox - actualBox
+ minVec = minVec - 0.5 * adjustment * justify
+ maxVec = maxVec + 0.5 * adjustment * (two - justify)
+ end
+
+ return minVec, maxVec
+end
+
+local function getBoundingBoxInWorldCoordinates(partOrModel)
+ local minVec = Vector3.new(math.huge, math.huge, math.huge)
+ local maxVec = Vector3.new(-math.huge, -math.huge, -math.huge)
+
+ if partOrModel:IsA("BasePart") and not partOrModel:IsA("Terrain") then
+ vec1 = partOrModel.CFrame:pointToWorldSpace(-0.5 * partOrModel.Size)
+ vec2 = partOrModel.CFrame:pointToWorldSpace(0.5 * partOrModel.Size)
+ minVec = Vector3.new(math.min(vec1.X, vec2.X), math.min(vec1.Y, vec2.Y), math.min(vec1.Z, vec2.Z))
+ maxVec = Vector3.new(math.max(vec1.X, vec2.X), math.max(vec1.Y, vec2.Y), math.max(vec1.Z, vec2.Z))
+ elseif partOrModel:IsA("Terrain") then
+ -- we shouldn't have to deal with this case
+ --minVec = Vector3.new(-2, -2, -2)
+ --maxVec = Vector3.new(2, 2, 2)
+ else
+ vec1 = partOrModel:GetModelCFrame():pointToWorldSpace(-0.5 * partOrModel:GetModelSize())
+ vec2 = partOrModel:GetModelCFrame():pointToWorldSpace(0.5 * partOrModel:GetModelSize())
+ minVec = Vector3.new(math.min(vec1.X, vec2.X), math.min(vec1.Y, vec2.Y), math.min(vec1.Z, vec2.Z))
+ maxVec = Vector3.new(math.max(vec1.X, vec2.X), math.max(vec1.Y, vec2.Y), math.max(vec1.Z, vec2.Z))
+ end
+
+ return minVec, maxVec
+end
+
+local function getTargetPartBoundingBox(targetPart)
+ if targetPart.Parent:FindFirstChild("RobloxModel") ~= nil then
+ return getBoundingBox2(targetPart.Parent)
+ else
+ return getBoundingBox2(targetPart)
+ end
+end
+
+local function getMouseTargetCFrame(targetPart)
+ if targetPart.Parent:FindFirstChild("RobloxModel") ~= nil then
+ if targetPart.Parent:IsA("Tool") then return targetPart.Parent.Handle.CFrame
+ else return targetPart.Parent:GetModelCFrame() end
+ else
+ return targetPart.CFrame
+ end
+end
+
+local function isBlocker(part) -- returns whether or not we want to cancel the stamp because we're blocked by this part
+ if not part then return false end
+ if not part.Parent then return false end
+ if part:FindFirstChild("Humanoid") then return false end
+ if part:FindFirstChild("RobloxStamper") or part:FindFirstChild("RobloxModel") then return true end
+ if part:IsA("Part") and not part.CanCollide then return false end
+ if part == game.Lighting then return false end
+ return isBlocker(part.Parent)
+end
+
+-- helper function to determine if a character can be pushed upwards by a certain amount
+-- character is 5 studs tall, we'll check a 1.5 x 1.5 x 4.5 box around char, with center .5 studs below torsocenter
+local function spaceAboveCharacter(charTorso, newTorsoY, stampData)
+ local partsAboveChar = game.Workspace:FindPartsInRegion3(
+ Region3.new(Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(.75, 2.75, .75),
+ Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(.75, 1.75, .75)),
+ charTorso.Parent,
+ 100)
+
+ for j = 1, #partsAboveChar do
+ if partsAboveChar[j].CanCollide and not partsAboveChar[j]:IsDescendantOf(stampData.CurrentParts) then return false end
+ end
+
+ if clusterPartsInRegion(Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(.75, 2.75, .75),
+ Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(.75, 1.75, .75)) then
+ return false
+ end
+
+ return true
+end
+
+
+local function findConfigAtMouseTarget(Mouse, stampData)
+ -- *Critical Assumption* :
+ -- This function assumes the target CF axes are orthogonal with the target bounding box faces
+ -- And, it assumes the insert CF axes are orthongonal with the insert bounding box faces
+ -- Therefore, insertion will not work with angled faces on wedges or other "non-block" parts, nor
+ -- will it work for parts in a model that are not orthogonally aligned with the model's CF.
+
+ if not Mouse then return nil end -- This can happen sometimes, return if so
+ if not stampData then error("findConfigAtMouseTarget: stampData is nil") return nil end
+ if not stampData["CurrentParts"] then return nil end
+
+ local grid = 4.0
+ local admissibleConfig = false
+ local targetConfig = CFrame.new(0,0,0)
+
+ local minBB, maxBB = getBoundingBox2(stampData.CurrentParts)
+ local diagBB = maxBB - minBB
+
+ local insertCFrame
+ if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
+ insertCFrame = stampData.CurrentParts:GetModelCFrame()
+ else
+ insertCFrame = stampData.CurrentParts.CFrame
+ end
+
+ if Mouse then
+ if stampData.CurrentParts:IsA("Tool") then
+ Mouse.TargetFilter = stampData.CurrentParts.Handle
+ else
+ Mouse.TargetFilter = stampData.CurrentParts
+ end
+ end
+
+ local hitPlane = false
+ local targetPart = nil
+ local success = pcall(function() targetPart = Mouse.Target end)
+
+ if not success then-- or targetPart == nil then
+ return admissibleConfig, targetConfig
+ end
+
+ local mouseHitInWorld = Vector3.new(0, 0, 0)
+ if Mouse then
+ mouseHitInWorld = Vector3.new(Mouse.Hit.x, Mouse.Hit.y, Mouse.Hit.z)
+ end
+
+ local cellPos = nil
+
+ -- Nothing was hit, so check for the default plane.
+ if nil == targetPart then
+ cellPos = GetTerrainForMouse(Mouse)
+ if nil == cellPos then
+ hitPlane = false
+ return admissibleConfig, targetConfig
+ else
+ targetPart = game.Workspace.Terrain
+ hitPlane = true
+ -- Take into account error that will occur.
+ cellPos = Vector3.new(cellPos.X - 1, cellPos.Y, cellPos.Z)
+ mouseHitInWorld = game.Workspace.Terrain:CellCenterToWorld(cellPos.x, cellPos.y, cellPos.z)
+ end
+ end
+
+ -- test mouse hit location
+ local minBBTarget, maxBBTarget = getTargetPartBoundingBox(targetPart)
+ local diagBBTarget = maxBBTarget - minBBTarget
+ local targetCFrame = getMouseTargetCFrame(targetPart)
+
+ if targetPart:IsA("Terrain") then
+ if not cluster then cluster = game.Workspace:FindFirstChild("Terrain") end
+ local cellID = cluster:WorldToCellPreferSolid(mouseHitInWorld)
+ if hitPlane then
+ cellID = cellPos
+ end
+
+ targetCFrame = CFrame.new(game.Workspace.Terrain:CellCenterToWorld(cellID.x, cellID.y, cellID.z))
+ end
+
+ local mouseHitInTarget = targetCFrame:pointToObjectSpace(mouseHitInWorld)
+ local targetVectorInWorld = Vector3.new(0,0,0)
+ if Mouse then
+ -- DON'T WANT THIS IN TERMS OF THE MODEL CFRAME! (.TargetSurface is in terms of the part CFrame, so this would break, right? [HotThoth])
+ -- (ideally, we would want to make the Mouse.TargetSurface a model-targetsurface instead, but for testing will be using the converse)
+ --targetVectorInWorld = targetCFrame:vectorToWorldSpace(Vector3.FromNormalId(Mouse.TargetSurface))
+ targetVectorInWorld = targetPart.CFrame:vectorToWorldSpace(Vector3.FromNormalId(Mouse.TargetSurface)) -- better, but model cframe would be best
+ --[[if targetPart.Parent:IsA("Model") then
+ local hitFace = modelTargetSurface(targetPart.Parent, Mouse.Hit.p, game.Workspace.CurrentCamera.CoordinateFrame.p) -- best, if you get it right
+ local WORLD_AXES = {Vector3.new(1, 0, 0), Vector3.new(0, 1, 0), Vector3.new(0, 0, 1)}
+ if hitFace > 0 then
+ targetVectorInWorld = targetCFrame:vectorToWorldSpace(WORLD_AXES[hitFace])
+ elseif hitFace < 0 then
+ targetVectorInWorld = targetCFrame:vectorToWorldSpace(-WORLD_AXES[-hitFace])
+ end
+ end]]
+ end
+
+ local targetRefPointInTarget
+ local clampToSurface
+
+ if getClosestAlignedWorldDirection(targetVectorInWorld) == 0 then
+ targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(1, -1, 1))
+ insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
+ clampToSurface = Vector3.new(0,1,1)
+ elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 3 then
+ targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, -1))
+ insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(1, -1, -1))
+ clampToSurface = Vector3.new(0,1,1)
+ elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 1 then
+ targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, 1, 1))
+ insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
+ clampToSurface = Vector3.new(1,0,1)
+ elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 4 then
+ targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
+ insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, 1, 1))
+ clampToSurface = Vector3.new(1,0,1)
+ elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 2 then
+ targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
+ insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, -1))
+ clampToSurface = Vector3.new(1,1,0)
+ else
+ targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(1, -1, -1))
+ insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(1, -1, 1))
+ clampToSurface = Vector3.new(1,1,0)
+ end
+
+ targetRefPointInTarget = targetRefPointInTarget * (0.5 * diagBBTarget) + 0.5 * (maxBBTarget + minBBTarget)
+ insertRefPointInInsert = insertRefPointInInsert * (0.5 * diagBB) + 0.5 * (maxBB + minBB)
+
+ -- To Do: For cases that are not aligned to the world grid, account for the minimal rotation
+ -- needed to bring the Insert part(s) into alignment with the Target Part
+ -- Apply the rotation here
+
+ local delta = mouseHitInTarget - targetRefPointInTarget
+ local deltaClamped = Vector3.new(grid * math.modf(delta.x/grid), grid * math.modf(delta.y/grid), grid * math.modf(delta.z/grid))
+ deltaClamped = deltaClamped * clampToSurface
+ local targetTouchInTarget = deltaClamped + targetRefPointInTarget
+
+ local TargetTouchRelToWorld = targetCFrame:pointToWorldSpace(targetTouchInTarget)
+ local InsertTouchInWorld = insertCFrame:vectorToWorldSpace(insertRefPointInInsert)
+ local posInsertOriginInWorld = TargetTouchRelToWorld - InsertTouchInWorld
+
+ local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = insertCFrame:components()
+ targetConfig = CFrame.new(posInsertOriginInWorld.x, posInsertOriginInWorld.y, posInsertOriginInWorld.z, R00, R01, R02, R10, R11, R12, R20, R21, R22)
+ admissibleConfig = true
+
+ return admissibleConfig, targetConfig, getClosestAlignedWorldDirection(targetVectorInWorld)
+end
+
+local function truncateToCircleEighth(bigValue, littleValue)
+ local big = math.abs(bigValue)
+ local little = math.abs(littleValue)
+ local hypotenuse = math.sqrt(big*big + little*little)
+ local frac = little / hypotenuse
+
+ local bigSign = 1
+ local littleSign = 1
+ if bigValue < 0 then bigSign = -1 end
+ if littleValue < 0 then littleSign = -1 end
+
+ if frac > .382683432 then
+ -- between 22.5 and 45 degrees, so truncate to 45-degree tilt
+ return .707106781 * hypotenuse * bigSign, .707106781 * hypotenuse * littleSign
+ else
+ -- between 0 and 22.5 degrees, so truncate to 0-degree tilt
+ return hypotenuse * bigSign, 0
+ end
+end
+
+
+local function saveTheWelds(object, manualWeldTable, manualWeldParentTable)
+ if object:IsA("ManualWeld") or object:IsA("Rotate") then
+ table.insert(manualWeldTable, object)
+ table.insert(manualWeldParentTable, object.Parent)
+ else
+ local children = object:GetChildren()
+ for i = 1, #children do
+ saveTheWelds(children[i], manualWeldTable, manualWeldParentTable)
+ end
+ end
+end
+
+local function restoreTheWelds(manualWeldTable, manualWeldParentTable)
+ for i = 1, #manualWeldTable do
+ manualWeldTable[i].Parent = manualWeldParentTable[i]
+ end
+end
+
+t.CanEditRegion = function(partOrModel, EditRegion) -- todo: use model and stamper metadata
+ if not EditRegion then return true, false end
+
+ local minBB, maxBB = getBoundingBoxInWorldCoordinates(partOrModel)
+
+ if minBB.X < EditRegion.CFrame.p.X - EditRegion.Size.X/2 or
+ minBB.Y < EditRegion.CFrame.p.Y - EditRegion.Size.Y/2 or
+ minBB.Z < EditRegion.CFrame.p.Z - EditRegion.Size.Z/2 then
+ return false, false
+ end
+
+ if maxBB.X > EditRegion.CFrame.p.X + EditRegion.Size.X/2 or
+ maxBB.Y > EditRegion.CFrame.p.Y + EditRegion.Size.Y/2 or
+ maxBB.Z > EditRegion.CFrame.p.Z + EditRegion.Size.Z/2 then
+ return false, false
+ end
+
+ return true, false
+end
+
+t.GetStampModel = function(assetId, terrainShape, useAssetVersionId)
+ if assetId == 0 then
+ return nil, "No Asset"
+ end
+ if assetId < 0 then
+ return nil, "Negative Asset"
+ end
+
+ local function UnlockInstances(object)
+ if object:IsA("BasePart") then
+ object.Locked = false
+ end
+ for index,child in pairs(object:GetChildren()) do
+ UnlockInstances(child)
+ end
+ end
+
+ local function getClosestColorToTerrainMaterial(terrainValue)
+ if terrainValue == 1 then
+ return BrickColor.new("Bright green")
+ elseif terrainValue == 2 then
+ return BrickColor.new("Bright yellow")
+ elseif terrainValue == 3 then
+ return BrickColor.new("Bright red")
+ elseif terrainValue == 4 then
+ return BrickColor.new("Sand red")
+ elseif terrainValue == 5 then
+ return BrickColor.new("Black")
+ elseif terrainValue == 6 then
+ return BrickColor.new("Dark stone grey")
+ elseif terrainValue == 7 then
+ return BrickColor.new("Sand blue")
+ elseif terrainValue == 8 then
+ return BrickColor.new("Deep orange")
+ elseif terrainValue == 9 then
+ return BrickColor.new("Dark orange")
+ elseif terrainValue == 10 then
+ return BrickColor.new("Reddish brown")
+ elseif terrainValue == 11 then
+ return BrickColor.new("Light orange")
+ elseif terrainValue == 12 then
+ return BrickColor.new("Light stone grey")
+ elseif terrainValue == 13 then
+ return BrickColor.new("Sand green")
+ elseif terrainValue == 14 then
+ return BrickColor.new("Medium stone grey")
+ elseif terrainValue == 15 then
+ return BrickColor.new("Really red")
+ elseif terrainValue == 16 then
+ return BrickColor.new("Really blue")
+ elseif terrainValue == 17 then
+ return BrickColor.new("Bright blue")
+ else
+ return BrickColor.new("Bright green")
+ end
+ end
+
+ local function setupFakeTerrainPart(cellMat, cellType, cellOrient)
+ local newTerrainPiece = nil
+ if (cellType == 1 or cellType == 4) then newTerrainPiece = Instance.new("WedgePart") newTerrainPiece.formFactor = "Custom"
+ elseif (cellType == 2) then newTerrainPiece = Instance.new("CornerWedgePart")
+ else newTerrainPiece = Instance.new("Part") newTerrainPiece.formFactor = "Custom" end
+ newTerrainPiece.Name = "MegaClusterCube"
+ newTerrainPiece.Size = Vector3.new(4, 4, 4)
+ newTerrainPiece.BottomSurface = "Smooth"
+ newTerrainPiece.TopSurface = "Smooth"
+
+ -- can add decals or textures here if feeling particularly adventurous... for now, can make a table of look-up colors
+ newTerrainPiece.BrickColor = getClosestColorToTerrainMaterial(cellMat)
+
+ local sideways = 0
+ local flipped = math.pi
+ if cellType == 4 then sideways = -math.pi/2 end
+ if cellType == 2 or cellType == 3 then flipped = 0 end
+ newTerrainPiece.CFrame = CFrame.Angles(0, math.pi/2*cellOrient + flipped, sideways)
+
+ if cellType == 3 then
+ local inverseCornerWedgeMesh = Instance.new("SpecialMesh")
+ inverseCornerWedgeMesh.MeshType = "FileMesh"
+ inverseCornerWedgeMesh.MeshId = "http://www.syntax.eco/asset?id=66832495"
+ inverseCornerWedgeMesh.Scale = Vector3.new(2, 2, 2)
+ inverseCornerWedgeMesh.Parent = newTerrainPiece
+ end
+
+ local materialTag = Instance.new("Vector3Value")
+ materialTag.Value = Vector3.new(cellMat, cellType, cellOrient)
+ materialTag.Name = "ClusterMaterial"
+ materialTag.Parent = newTerrainPiece
+
+ return newTerrainPiece
+ end
+
+ -- This call will cause a "wait" until the data comes back
+ -- below we wait a max of 8 seconds before deciding to bail out on loading
+ local root
+ local loader
+ loading = true
+ if useAssetVersionId then
+ loader = coroutine.create(function()
+ root = game:GetService("InsertService"):LoadAssetVersion(assetId)
+ loading = false
+ end)
+ coroutine.resume(loader)
+ else
+ loader = coroutine.create(function()
+ root = game:GetService("InsertService"):LoadAsset(assetId)
+ loading = false
+ end)
+ coroutine.resume(loader)
+ end
+
+ local lastGameTime = 0
+ local totalTime = 0
+ local maxWait = 8
+ while loading and totalTime < maxWait do
+ lastGameTime = tick()
+ wait(1)
+ totalTime = totalTime + tick() - lastGameTime
+ end
+ loading = false
+
+ if totalTime >= maxWait then
+ return nil, "Load Time Fail"
+ end
+
+
+ if root == nil then
+ return nil, "Load Asset Fail"
+ end
+
+ if not root:IsA("Model") then
+ return nil, "Load Type Fail"
+ end
+
+ local instances = root:GetChildren()
+ if #instances == 0 then
+ return nil, "Empty Model Fail"
+ end
+
+ --Unlock all parts that are inserted, to make sure they are editable
+ UnlockInstances(root)
+
+ --Continue the insert process
+ root = root:GetChildren()[1]
+
+ --Examine the contents and decide what it looks like
+ for pos, instance in pairs(instances) do
+ if instance:IsA("Team") then
+ instance.Parent = game:GetService("Teams")
+ elseif instance:IsA("Sky") then
+ local lightingService = game:GetService("Lighting")
+ for index,child in pairs(lightingService:GetChildren()) do
+ if child:IsA("Sky") then
+ child:Remove();
+ end
+ end
+ instance.Parent = lightingService
+ return
+ end
+ end
+
+ -- ...and tag all inserted models for subsequent origin identification
+ -- if no RobloxModel tag already exists, then add it.
+ if root:FindFirstChild("RobloxModel") == nil then
+ local stringTag = Instance.new("BoolValue", root)
+ stringTag.Name = "RobloxModel"
+
+ if root:FindFirstChild("RobloxStamper") == nil then
+ local stringTag2 = Instance.new("BoolValue", root)
+ stringTag2.Name = "RobloxStamper"
+ end
+ end
+
+ if terrainShape then
+ if root.Name == "MegaClusterCube" then
+ if (terrainShape == 6) then -- insert an autowedging tag
+ local autowedgeTag = Instance.new("BoolValue")
+ autowedgeTag.Name = "AutoWedge"
+ autowedgeTag.Parent = root
+ else
+ local clusterTag = root:FindFirstChild("ClusterMaterial")
+ if clusterTag then
+ if clusterTag:IsA("Vector3Value") then
+ root = setupFakeTerrainPart(clusterTag.Value.X, terrainShape, clusterTag.Value.Z)
+ else
+ root = setupFakeTerrainPart(clusterTag.Value, terrainShape, 0)
+ end
+ else
+ root = setupFakeTerrainPart(1, terrainShape, 0)
+ end
+ end
+ end
+ end
+
+ return root
+end
+
+
+
+t.SetupStamperDragger = function(modelToStamp, Mouse, StampInModel, AllowedStampRegion, StampFailedFunc)
+ if not modelToStamp then
+ error("SetupStamperDragger: modelToStamp (first arg) is nil! Should be a stamper model")
+ return nil
+ end
+ if not modelToStamp:IsA("Model") and not modelToStamp:IsA("BasePart") then
+ error("SetupStamperDragger: modelToStamp (first arg) is neither a Model or Part!")
+ return nil
+ end
+ if not Mouse then
+ error("SetupStamperDragger: Mouse (second arg) is nil! Should be a mouse object")
+ return nil
+ end
+ if not Mouse:IsA("Mouse") then
+ error("SetupStamperDragger: Mouse (second arg) is not of type Mouse!")
+ return nil
+ end
+
+ local stampInModel = nil
+ local allowedStampRegion = nil
+ local stampFailedFunc = nil
+ if StampInModel then
+ if not StampInModel:IsA("Model") then
+ error("SetupStamperDragger: StampInModel (optional third arg) is not of type 'Model'")
+ return nil
+ end
+ if not AllowedStampRegion then
+ error("SetupStamperDragger: AllowedStampRegion (optional fourth arg) is nil when StampInModel (optional third arg) is defined")
+ return nil
+ end
+ stampFailedFunc = StampFailedFunc
+ stampInModel = StampInModel
+ allowedStampRegion = AllowedStampRegion
+ end
+
+ -- Init all state variables
+ local gInitial90DegreeRotations = 0
+ local stampData = nil
+ local mouseTarget = nil
+
+ local errorBox = Instance.new("SelectionBox")
+ errorBox.Color = BrickColor.new("Bright red")
+ errorBox.Transparency = 0
+ errorBox.Archivable = false
+
+ -- for megacluster MEGA STAMPING
+ local adornPart = Instance.new("Part")
+ adornPart.Parent = nil
+ adornPart.formFactor = "Custom"
+ adornPart.Size = Vector3.new(4, 4, 4)
+ adornPart.CFrame = CFrame.new()
+ adornPart.Archivable = false
+
+ local adorn = Instance.new("SelectionBox")
+ adorn.Color = BrickColor.new("Toothpaste")
+ adorn.Adornee = adornPart
+ adorn.Visible = true
+ adorn.Transparency = 0
+ adorn.Name = "HighScalabilityStamperLine"
+ adorn.Archivable = false
+
+ local HighScalabilityLine = {}
+ HighScalabilityLine.Start = nil
+ HighScalabilityLine.End = nil
+ HighScalabilityLine.Adorn = adorn
+ HighScalabilityLine.AdornPart = adornPart
+ HighScalabilityLine.InternalLine = nil
+ HighScalabilityLine.NewHint = true
+
+ HighScalabilityLine.MorePoints = {nil, nil}
+ HighScalabilityLine.MoreLines = {nil, nil}
+ HighScalabilityLine.Dimensions = 1
+
+ local control = {}
+ local movingLock = false
+ local stampUpLock = false
+ local unstampableSurface = false
+ local mouseCons = {}
+ local keyCon = nil
+
+ local stamped = Instance.new("BoolValue")
+ stamped.Archivable = false
+ stamped.Value = false
+
+ local lastTarget = {}
+ lastTarget.TerrainOrientation = 0
+ lastTarget.CFrame = 0
+
+ local cellInfo = {}
+ cellInfo.Material = 1
+ cellInfo.clusterType = 0
+ cellInfo.clusterOrientation = 0
+
+ local function isMegaClusterPart()
+ if not stampData then return false end
+ if not stampData.CurrentParts then return false end
+
+ return ( stampData.CurrentParts:FindFirstChild("ClusterMaterial",true) or (stampData.CurrentParts.Name == "MegaClusterCube") )
+ end
+
+ local function DoHighScalabilityRegionSelect()
+ local megaCube = stampData.CurrentParts:FindFirstChild("MegaClusterCube")
+ if not megaCube then
+ if not stampData.CurrentParts.Name == "MegaClusterCube" then
+ return
+ else
+ megaCube = stampData.CurrentParts
+ end
+ end
+
+ HighScalabilityLine.End = megaCube.CFrame.p
+ local line = nil
+ local line2 = Vector3.new(0, 0, 0)
+ local line3 = Vector3.new(0, 0, 0)
+
+ if HighScalabilityLine.Dimensions == 1 then
+ -- extract the line from these positions and limit to a 2D plane made from 2 of the world axes
+ -- then use dominating axis to limit line to be at 45-degree intervals
+ -- will use this internal representation of the line for the actual stamping
+ line = (HighScalabilityLine.End - HighScalabilityLine.Start)
+
+ if math.abs(line.X) < math.abs(line.Y) then
+ if math.abs(line.X) < math.abs(line.Z) then
+ -- limit to Y/Z plane, domination unknown
+ local newY, newZ
+ if (math.abs(line.Y) > math.abs(line.Z)) then
+ newY, newZ = truncateToCircleEighth(line.Y, line.Z)
+ else
+ newZ, newY = truncateToCircleEighth(line.Z, line.Y)
+ end
+ line = Vector3.new(0, newY, newZ)
+ else
+ -- limit to X/Y plane, with Y dominating
+ local newY, newX = truncateToCircleEighth(line.Y, line.X)
+ line = Vector3.new(newX, newY, 0)
+ end
+ else
+ if math.abs(line.Y) < math.abs(line.Z) then
+ -- limit to X/Z plane, domination unknown
+ local newX, newZ
+ if math.abs(line.X) > math.abs(line.Z) then
+ newX, newZ = truncateToCircleEighth(line.X, line.Z)
+ else
+ newZ, newX = truncateToCircleEighth(line.Z, line.X)
+ end
+ line = Vector3.new(newX, 0, newZ)
+ else
+ -- limit to X/Y plane, with X dominating
+ local newX, newY = truncateToCircleEighth(line.X, line.Y)
+ line = Vector3.new(newX, newY, 0)
+ end
+ end
+ HighScalabilityLine.InternalLine = line
+
+ elseif HighScalabilityLine.Dimensions == 2 then
+ line = HighScalabilityLine.MoreLines[1]
+ line2 = HighScalabilityLine.End - HighScalabilityLine.MorePoints[1]
+
+ -- take out any component of line2 along line1, so you get perpendicular to line1 component
+ line2 = line2 - line.unit*line.unit:Dot(line2)
+
+ tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line)
+
+ -- then zero out whichever is the smaller component
+ local yAxis = tempCFrame:vectorToWorldSpace(Vector3.new(0, 1, 0))
+ local xAxis = tempCFrame:vectorToWorldSpace(Vector3.new(1, 0, 0))
+
+ local xComp = xAxis:Dot(line2)
+ local yComp = yAxis:Dot(line2)
+
+ if math.abs(yComp) > math.abs(xComp) then
+ line2 = line2 - xAxis * xComp
+ else
+ line2 = line2 - yAxis * yComp
+ end
+
+ HighScalabilityLine.InternalLine = line2
+
+ elseif HighScalabilityLine.Dimensions == 3 then
+ line = HighScalabilityLine.MoreLines[1]
+ line2 = HighScalabilityLine.MoreLines[2]
+ line3 = HighScalabilityLine.End - HighScalabilityLine.MorePoints[2]
+
+ -- zero out all components of previous lines
+ line3 = line3 - line.unit * line.unit:Dot(line3)
+ line3 = line3 - line2.unit * line2.unit:Dot(line3)
+
+ HighScalabilityLine.InternalLine = line3
+ end
+
+ -- resize the "line" graphic to be the correct size and orientation
+ tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line)
+
+ if HighScalabilityLine.Dimensions == 1 then -- faster calculation for line
+ HighScalabilityLine.AdornPart.Size = Vector3.new(4, 4, line.magnitude + 4)
+ HighScalabilityLine.AdornPart.CFrame = tempCFrame + tempCFrame:vectorToWorldSpace(Vector3.new(2, 2, 2) - HighScalabilityLine.AdornPart.Size/2)
+ else
+ local boxSize = tempCFrame:vectorToObjectSpace(line + line2 + line3)
+ HighScalabilityLine.AdornPart.Size = Vector3.new(4, 4, 4) + Vector3.new(math.abs(boxSize.X), math.abs(boxSize.Y), math.abs(boxSize.Z))
+ HighScalabilityLine.AdornPart.CFrame = tempCFrame + tempCFrame:vectorToWorldSpace(boxSize/2)
+ end
+
+ -- make player able to see this ish
+
+ local gui = nil
+ if game.Players["LocalPlayer"] then
+ gui = game.Players.LocalPlayer:FindFirstChild("PlayerGui")
+ if gui and gui:IsA("PlayerGui") then
+ if HighScalabilityLine.Dimensions == 1 and line.magnitude > 3 then -- don't show if mouse hasn't moved enough
+ HighScalabilityLine.Adorn.Parent = gui
+ elseif HighScalabilityLine.Dimensions > 1 then
+ HighScalabilityLine.Adorn.Parent = gui
+ end
+ end
+ end
+
+ if gui == nil then -- we are in studio
+ gui = game:GetService("CoreGui")
+ if HighScalabilityLine.Dimensions == 1 and line.magnitude > 3 then -- don't show if mouse hasn't moved enough
+ HighScalabilityLine.Adorn.Parent = gui
+ elseif HighScalabilityLine.Dimensions > 1 then
+ HighScalabilityLine.Adorn.Parent = gui
+ end
+ end
+ end
+
+
+ local function DoStamperMouseMove(Mouse)
+ if not Mouse then
+ error("Error: RbxStamper.DoStamperMouseMove: Mouse is nil")
+ return
+ end
+ if not Mouse:IsA("Mouse") then
+ error("Error: RbxStamper.DoStamperMouseMove: Mouse is of type", Mouse.className,"should be of type Mouse")
+ return
+ end
+
+ -- There wasn't a target (no part or terrain), so check for plane intersection.
+ if not Mouse.Target then
+ local cellPos = GetTerrainForMouse(Mouse)
+ if nil == cellPos then
+ return
+ end
+ end
+
+ if not stampData then
+ return
+ end
+
+ -- don't move with dragger - will move in one step on mouse down
+ -- draw ghost at acceptable positions
+ configFound, targetCFrame, targetSurface = findConfigAtMouseTarget(Mouse, stampData)
+ if not configFound then
+ error("RbxStamper.DoStamperMouseMove No configFound, returning")
+ return
+ end
+
+ local numRotations = 0 -- update this according to how many rotations you need to get it to target surface
+ if autoAlignToFace(stampData.CurrentParts) and targetSurface ~= 1 and targetSurface ~= 4 then -- pre-rotate the flag or portrait so it's aligned correctly
+ if targetSurface == 3 then numRotations = 0 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
+ elseif targetSurface == 0 then numRotations = 2 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
+ elseif targetSurface == 5 then numRotations = 3 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
+ elseif targetSurface == 2 then numRotations = 1 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
+ end
+ end
+
+ local ry = math.pi/2
+ gInitial90DegreeRotations = gInitial90DegreeRotations + numRotations
+ if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
+ --stampData.CurrentParts:Rotate(0, ry*numRotations, 0)
+ modelRotate(stampData.CurrentParts, ry*numRotations)
+ else
+ stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry*numRotations, 0) * stampData.CurrentParts.CFrame
+ end
+
+ -- CODE TO CHECK FOR DRAGGING GHOST PART INTO A COLLIDING STATE
+ local minBB, maxBB = getBoundingBoxInWorldCoordinates(stampData.CurrentParts)
+
+ -- need to offset by distance to be dragged
+ local currModelCFrame = nil
+ if stampData.CurrentParts:IsA("Model") then
+ currModelCFrame = stampData.CurrentParts:GetModelCFrame()
+ else
+ currModelCFrame = stampData.CurrentParts.CFrame
+ end
+
+ minBB = minBB + targetCFrame.p - currModelCFrame.p
+ maxBB = maxBB + targetCFrame.p - currModelCFrame.p
+
+ -- don't drag into terrain
+ if clusterPartsInRegion(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector) then
+ if lastTarget.CFrame then
+ if (stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)) then
+ local theClusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
+ if theClusterMaterial:IsA("Vector3Value") then
+ local stampClusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
+ if stampClusterMaterial then
+ stampClusterMaterial = clusterMat
+ end
+ end
+ end
+ end
+ return
+ end
+
+ -- if we are stamping a terrain part, make sure it goes on the grid! Otherwise preview block could be placed off grid, but stamped on grid
+ if isMegaClusterPart() then
+ local cellToStamp = game.Workspace.Terrain:WorldToCell(targetCFrame.p)
+ local newCFramePosition = game.Workspace.Terrain:CellCenterToWorld(cellToStamp.X, cellToStamp.Y, cellToStamp.Z)
+ local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = targetCFrame:components()
+ targetCFrame = CFrame.new(newCFramePosition.X,newCFramePosition.Y,newCFramePosition.Z,R00, R01, R02, R10, R11, R12, R20, R21, R22)
+ end
+
+ positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts)
+ lastTarget.CFrame = targetCFrame -- successful positioning, so update 'dat cframe
+ if stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) then
+ local clusterMat = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
+ if clusterMat:IsA("Vector3Value") then
+ lastTarget.TerrainOrientation = clusterMat.Value.Z
+ end
+ end
+
+
+ -- auto break joints code
+ if Mouse and Mouse.Target and Mouse.Target.Parent then
+ local modelInfo = Mouse.Target:FindFirstChild("RobloxModel")
+ if not modelInfo then modelInfo = Mouse.Target.Parent:FindFirstChild("RobloxModel") end
+
+ local myModelInfo = stampData.CurrentParts:FindFirstChild("UnstampableFaces")
+
+ --if (modelInfo and modelInfo.Parent:FindFirstChild("UnstampableFaces")) or (modelInfo and myModelInfo) then -- need better targetSurface calcs
+ if (true) then
+ local breakingFaces = ""
+ local myBreakingFaces = ""
+ if modelInfo and modelInfo.Parent:FindFirstChild("UnstampableFaces") then breakingFaces = modelInfo.Parent.UnstampableFaces.Value end
+ if myModelInfo then myBreakingFaces = myModelInfo.Value end
+ local hitFace = 0
+
+ if modelInfo then hitFace = modelTargetSurface(modelInfo.Parent, game.Workspace.CurrentCamera.CoordinateFrame.p, Mouse.Hit.p) end
+
+ -- are we stamping TO an unstampable surface?
+ for bf in string.gmatch(breakingFaces, "[^,]+") do
+ if hitFace == tonumber(bf) then
+ -- return before we hit the JointsService code below!
+ unstampableSurface = true
+ game.JointsService:ClearJoinAfterMoveJoints() -- clear the JointsService cache
+ return
+ end
+ end
+
+ -- now we have to cast the ray back in the other direction to find the surface we're stamping FROM
+ hitFace = modelTargetSurface(stampData.CurrentParts, Mouse.Hit.p, game.Workspace.CurrentCamera.CoordinateFrame.p)
+
+ -- are we stamping WITH an unstampable surface?
+ for bf in string.gmatch(myBreakingFaces, "[^,]+") do
+ if hitFace == tonumber(bf) then
+ unstampableSurface = true
+ game.JointsService:ClearJoinAfterMoveJoints() -- clear the JointsService cache
+ return
+ end
+ end
+
+ -- just need to match breakingFace against targetSurface using rotation supplied by modelCFrame
+ -- targetSurface: 1 is top, 4 is bottom,
+ end
+ end
+
+ -- to show joints during the mouse move
+ unstampableSurface = false
+ game.JointsService:SetJoinAfterMoveInstance(stampData.CurrentParts)
+
+ -- most common mouse inactive error occurs here, so check mouse active one more time in a pcall
+ if not pcall(function()
+ if Mouse and Mouse.Target and Mouse.Target.Parent:FindFirstChild("RobloxModel") == nil then
+ return
+ else
+ return
+ end
+ end)
+ then
+ error("Error: RbxStamper.DoStamperMouseMove Mouse is nil on second check")
+ game.JointsService:ClearJoinAfterMoveJoints()
+ Mouse = nil
+ return
+ end
+
+ if Mouse and Mouse.Target and Mouse.Target.Parent:FindFirstChild("RobloxModel") == nil then
+ game.JointsService:SetJoinAfterMoveTarget(Mouse.Target)
+ else
+ game.JointsService:SetJoinAfterMoveTarget(nil)
+ end
+ game.JointsService:ShowPermissibleJoints()
+
+ -- here we allow for a line of high-scalability parts
+ if isMegaClusterPart() and HighScalabilityLine and HighScalabilityLine.Start then
+ DoHighScalabilityRegionSelect()
+ end
+ end
+
+ local function setupKeyListener(key, Mouse)
+ if control and control["Paused"] then return end -- don't do this if we have no stamp
+
+ key = string.lower(key)
+ if key == 'r' and not autoAlignToFace(stampData.CurrentParts) then -- rotate the model
+ gInitial90DegreeRotations = gInitial90DegreeRotations + 1
+
+ -- Update orientation value if this is a fake terrain part
+ local clusterValues = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
+ if clusterValues and clusterValues:IsA("Vector3Value") then
+ clusterValues.Value = Vector3.new(clusterValues.Value.X, clusterValues.Value.Y, (clusterValues.Value.Z + 1) % 4)
+ end
+
+ -- Rotate the parts or all the parts in the model
+ local ry = math.pi/2
+ if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
+ --stampData.CurrentParts:Rotate(0, ry, 0)
+ modelRotate(stampData.CurrentParts, ry)
+ else
+ stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry, 0) * stampData.CurrentParts.CFrame
+ end
+
+ -- After rotating, update the position
+ configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData)
+ if configFound then
+ positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts)
+
+ -- update everything else in MouseMove
+ DoStamperMouseMove(Mouse)
+ end
+ elseif key == 'c' then -- try to expand our high scalability dragger dimension
+ if HighScalabilityLine.InternalLine and HighScalabilityLine.InternalLine.magnitude > 0 and HighScalabilityLine.Dimensions < 3 then
+ HighScalabilityLine.MorePoints[HighScalabilityLine.Dimensions] = HighScalabilityLine.End
+ HighScalabilityLine.MoreLines[HighScalabilityLine.Dimensions] = HighScalabilityLine.InternalLine
+ HighScalabilityLine.Dimensions = HighScalabilityLine.Dimensions + 1
+ HighScalabilityLine.NewHint = true
+ end
+ end
+ end
+
+ keyCon = Mouse.KeyDown:connect(function(key) -- init key connection (keeping code close to func)
+ setupKeyListener(key, Mouse)
+ end)
+
+ local function resetHighScalabilityLine()
+ if HighScalabilityLine then
+ HighScalabilityLine.Start = nil
+ HighScalabilityLine.End = nil
+ HighScalabilityLine.InternalLine = nil
+ HighScalabilityLine.NewHint = true
+ end
+ end
+
+ local function flashRedBox()
+ local gui = game.CoreGui
+ if game:FindFirstChild("Players") then
+ if game.Players["LocalPlayer"] then
+ if game.Players.LocalPlayer:FindFirstChild("PlayerGui") then
+ gui = game.Players.LocalPlayer.PlayerGui
+ end
+ end
+ end
+ if not stampData["ErrorBox"] then return end
+
+ stampData.ErrorBox.Parent = gui
+ if stampData.CurrentParts:IsA("Tool") then
+ stampData.ErrorBox.Adornee = stampData.CurrentParts.Handle
+ else
+ stampData.ErrorBox.Adornee = stampData.CurrentParts
+ end
+
+ delay(0,function()
+ for i = 1, 3 do
+ if stampData["ErrorBox"] then stampData.ErrorBox.Visible = true end
+ wait(0.13)
+ if stampData["ErrorBox"] then stampData.ErrorBox.Visible = false end
+ wait(0.13)
+ end
+ if stampData["ErrorBox"] then
+ stampData.ErrorBox.Adornee = nil
+ stampData.ErrorBox.Parent = Tool
+ end
+ end)
+ end
+
+ local function DoStamperMouseDown(Mouse)
+ if not Mouse then
+ error("Error: RbxStamper.DoStamperMouseDown: Mouse is nil")
+ return
+ end
+ if not Mouse:IsA("Mouse") then
+ error("Error: RbxStamper.DoStamperMouseDown: Mouse is of type", Mouse.className,"should be of type Mouse")
+ return
+ end
+ if not stampData then
+ return
+ end
+
+ if isMegaClusterPart() then
+ if Mouse and HighScalabilityLine then
+ local megaCube = stampData.CurrentParts:FindFirstChild("MegaClusterCube", true)
+ local terrain = game.Workspace.Terrain
+ if megaCube then
+ HighScalabilityLine.Dimensions = 1
+ local tempCell = terrain:WorldToCell(megaCube.CFrame.p)
+ HighScalabilityLine.Start = terrain:CellCenterToWorld(tempCell.X, tempCell.Y, tempCell.Z)
+ return
+ else
+ HighScalabilityLine.Dimensions = 1
+ local tempCell = terrain:WorldToCell(stampData.CurrentParts.CFrame.p)
+ HighScalabilityLine.Start = terrain:CellCenterToWorld(tempCell.X, tempCell.Y, tempCell.Z)
+ return
+ end
+ end
+ end
+ end
+
+ local function loadSurfaceTypes(part, surfaces)
+ part.TopSurface = surfaces[1]
+ part.BottomSurface = surfaces[2]
+ part.LeftSurface = surfaces[3]
+ part.RightSurface = surfaces[4]
+ part.FrontSurface = surfaces[5]
+ part.BackSurface = surfaces[6]
+ end
+
+ local function saveSurfaceTypes(part, myTable)
+ local tempTable = {}
+ tempTable[1] = part.TopSurface
+ tempTable[2] = part.BottomSurface
+ tempTable[3] = part.LeftSurface
+ tempTable[4] = part.RightSurface
+ tempTable[5] = part.FrontSurface
+ tempTable[6] = part.BackSurface
+
+ myTable[part] = tempTable
+ end
+
+ local function makeSurfaceUnjoinable(part, surface)
+ -- TODO: FILL OUT!
+ end
+
+ local function prepareModel(model)
+ if not model then return nil end
+
+ local gDesiredTrans = 0.7
+ local gStaticTrans = 1
+
+ local clone = model:Clone()
+ local scripts = {}
+ local parts = {}
+ local decals = {}
+
+ stampData = {}
+ stampData.DisabledScripts = {}
+ stampData.TransparencyTable = {}
+ stampData.MaterialTable = {}
+ stampData.CanCollideTable = {}
+ stampData.AnchoredTable = {}
+ stampData.ArchivableTable = {}
+ stampData.DecalTransparencyTable = {}
+ stampData.SurfaceTypeTable = {}
+
+ collectParts(clone, parts, scripts, decals)
+
+ if #parts <= 0 then return nil, "no parts found in modelToStamp" end
+
+ for index,script in pairs(scripts) do
+ if not(script.Disabled) then
+ script.Disabled = true
+ stampData.DisabledScripts[#stampData.DisabledScripts + 1] = script
+ end
+ end
+ for index, part in pairs(parts) do
+ stampData.TransparencyTable[part] = part.Transparency
+ part.Transparency = gStaticTrans + (1 - gStaticTrans) * part.Transparency
+ stampData.MaterialTable[part] = part.Material
+ part.Material = Enum.Material.Plastic
+ stampData.CanCollideTable[part] = part.CanCollide
+ part.CanCollide = false
+ stampData.AnchoredTable[part] = part.Anchored
+ part.Anchored = true
+ stampData.ArchivableTable[part] = part.Archivable
+ part.Archivable = false
+
+ saveSurfaceTypes(part, stampData.SurfaceTypeTable)
+
+ local fadeInDelayTime = 0.5
+ local transFadeInTime = 0.5
+ delay(0,function()
+ wait(fadeInDelayTime) -- give it some time to be completely transparent
+
+ local begTime = tick()
+ local currTime = begTime
+ while (currTime - begTime) < transFadeInTime and part and part:IsA("BasePart") and part.Transparency > gDesiredTrans do
+ local newTrans = 1 - (((currTime - begTime)/transFadeInTime) * (gStaticTrans - gDesiredTrans))
+ if stampData["TransparencyTable"] and stampData.TransparencyTable[part] then
+ part.Transparency = newTrans + (1 - newTrans) * stampData.TransparencyTable[part]
+ end
+ wait(0.03)
+ currTime = tick()
+ end
+ if part and part:IsA("BasePart") then
+ if stampData["TransparencyTable"] and stampData.TransparencyTable[part] then
+ part.Transparency = gDesiredTrans + (1 - gDesiredTrans) * stampData.TransparencyTable[part]
+ end
+ end
+ end)
+ end
+
+ for index, decal in pairs(decals) do
+ stampData.DecalTransparencyTable[decal] = decal.Transparency
+ decal.Transparency = gDesiredTrans + (1 - gDesiredTrans) * decal.Transparency
+ end
+
+ -- disable all seats
+ setSeatEnabledStatus(clone, true)
+ setSeatEnabledStatus(clone, false)
+
+ stampData.CurrentParts = clone
+
+ -- if auto-alignable, we enforce a pre-rotation to the canonical "0-frame"
+ if autoAlignToFace(clone) then
+ stampData.CurrentParts:ResetOrientationToIdentity()
+ gInitial90DegreeRotations = 0
+ else -- pre-rotate if necessary
+ local ry = gInitial90DegreeRotations * math.pi/2
+ if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
+ --stampData.CurrentParts:Rotate(0, ry, 0)
+ modelRotate(stampData.CurrentParts, ry)
+ else
+ stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry, 0) * stampData.CurrentParts.CFrame
+ end
+ end
+
+ -- since we're cloning the old model instead of the new one, we will need to update the orientation based on the original value AND how many more
+ -- rotations we expect since then [either that or we need to store the just-stamped clusterMaterial.Value.Z somewhere]. This should fix the terrain rotation
+ -- issue (fingers crossed) [HotThoth]
+
+ local clusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
+ if clusterMaterial and clusterMaterial:IsA("Vector3Value") then
+ clusterMaterial.Value = Vector3.new(clusterMaterial.Value.X, clusterMaterial.Value.Y, (clusterMaterial.Value.Z + gInitial90DegreeRotations) % 4)
+ end
+
+ -- After rotating, update the position
+ local configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData)
+ if configFound then
+ stampData.CurrentParts = positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts)
+ end
+
+ -- to show joints during the mouse move
+ game.JointsService:SetJoinAfterMoveInstance(stampData.CurrentParts)
+
+ return clone, parts
+ end
+
+ local function checkTerrainBlockCollisions(cellPos, checkHighScalabilityStamp)
+ local cellCenterToWorld = game.Workspace.Terrain.CellCenterToWorld
+ local cellCenter = cellCenterToWorld(game.Workspace.Terrain, cellPos.X, cellPos.Y, cellPos.Z)
+ local cellBlockingParts = game.Workspace:FindPartsInRegion3(Region3.new(cellCenter - Vector3.new(2, 2, 2) + insertBoundingBoxOverlapVector, cellCenter + Vector3.new(2, 2, 2) - insertBoundingBoxOverlapVector), stampData.CurrentParts, 100)
+
+ local skipThisCell = false
+
+ for b = 1, #cellBlockingParts do
+ if isBlocker(cellBlockingParts[b]) then skipThisCell = true break end
+ end
+
+ if not skipThisCell then
+ -- pop players up above any set cells
+ local alreadyPushedUp = {}
+ -- if no blocking model below, then see if stamping on top of a character
+ for b = 1, #cellBlockingParts do
+ if cellBlockingParts[b].Parent and
+ not alreadyPushedUp[cellBlockingParts[b].Parent] and
+ cellBlockingParts[b].Parent:FindFirstChild("Humanoid") and
+ cellBlockingParts[b].Parent:FindFirstChild("Humanoid"):IsA("Humanoid") then
+ -----------------------------------------------------------------------------------
+ local blockingPersonTorso = cellBlockingParts[b].Parent:FindFirstChild("Torso")
+ alreadyPushedUp[cellBlockingParts[b].Parent] = true
+
+ if blockingPersonTorso then
+ -- if so, let's push the person upwards so they pop on top of the stamped model/part (but only if there's space above them)
+ local newY = cellCenter.Y + 5
+ if spaceAboveCharacter(blockingPersonTorso, newY, stampData) then
+ blockingPersonTorso.CFrame = blockingPersonTorso.CFrame + Vector3.new(0, newY - blockingPersonTorso.CFrame.p.Y, 0)
+ else
+ -- if no space, we just skip this one
+ skipThisCell = true
+ break
+ end
+ end
+ -----------------------------------------------------------------------------------
+ end
+ end
+ end
+
+ if not skipThisCell then -- if we STILL aren't skipping... then we're good to go!
+ local canSetCell = true
+
+ if checkHighScalabilityStamp then -- check to see if cell is in region, if not we'll skip set
+ if allowedStampRegion then
+ local cellPos = cellCenterToWorld(game.Workspace.Terrain, cellPos.X, cellPos.Y, cellPos.Z)
+ if cellPos.X + 2 > allowedStampRegion.CFrame.p.X + allowedStampRegion.Size.X/2 then
+ canSetCell = false
+ elseif cellPos.X - 2 < allowedStampRegion.CFrame.p.X - allowedStampRegion.Size.X/2 then
+ canSetCell = false
+ elseif cellPos.Y + 2 > allowedStampRegion.CFrame.p.Y + allowedStampRegion.Size.Y/2 then
+ canSetCell = false
+ elseif cellPos.Y - 2 < allowedStampRegion.CFrame.p.Y - allowedStampRegion.Size.Y/2 then
+ canSetCell = false
+ elseif cellPos.Z + 2 > allowedStampRegion.CFrame.p.Z + allowedStampRegion.Size.Z/2 then
+ canSetCell = false
+ elseif cellPos.Z - 2 < allowedStampRegion.CFrame.p.Z - allowedStampRegion.Size.Z/2 then
+ canSetCell = false
+ end
+ end
+ end
+
+ return canSetCell
+ end
+ return false
+ end
+
+
+ local function ResolveMegaClusterStamp(checkHighScalabilityStamp)
+ local cellSet = false
+
+ local cluser = game.Workspace.Terrain
+
+ local line = HighScalabilityLine.InternalLine
+ local cMax = game.Workspace.Terrain.MaxExtents.Max
+ local cMin = game.Workspace.Terrain.MaxExtents.Min
+
+ local clusterMaterial = 1 -- default is grass
+ local clusterType = 0 -- default is brick
+ local clusterOrientation = 0 -- default is 0 rotation
+
+ local autoWedgeClusterParts = false
+ if stampData.CurrentParts:FindFirstChild("AutoWedge") then autoWedgeClusterParts = true end
+
+ if stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) then
+ clusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
+ if clusterMaterial:IsA("Vector3Value") then
+ clusterType = clusterMaterial.Value.Y
+ clusterOrientation = clusterMaterial.Value.Z
+ clusterMaterial = clusterMaterial.Value.X
+ elseif clusterMaterial:IsA("IntValue") then
+ clusterMaterial = clusterMaterial.Value
+ end
+ end
+
+ if HighScalabilityLine.Adorn.Parent and HighScalabilityLine.Start and ((HighScalabilityLine.Dimensions > 1) or (line and line.magnitude > 0)) then
+ local startCell = game.Workspace.Terrain:WorldToCell(HighScalabilityLine.Start)
+ local xInc = {0,0,0}
+ local yInc = {0,0,0}
+ local zInc = {0,0,0}
+
+ local cluster = game.Workspace.Terrain
+
+ local incrementVect = {nil, nil, nil}
+ local stepVect = {Vector3.new(0, 0, 0), Vector3.new(0, 0, 0), Vector3.new(0, 0, 0)}
+
+ local worldAxes = {Vector3.new(1, 0, 0), Vector3.new(0, 1, 0), Vector3.new(0, 0, 1)}
+
+ local lines = {}
+ if HighScalabilityLine.Dimensions > 1 then table.insert(lines, HighScalabilityLine.MoreLines[1]) end
+ if line and line.magnitude > 0 then table.insert(lines, line) end
+ if HighScalabilityLine.Dimensions > 2 then table.insert(lines, HighScalabilityLine.MoreLines[2]) end
+
+ for i = 1, #lines do
+ lines[i] = Vector3.new(math.floor(lines[i].X+.5), math.floor(lines[i].Y+.5), math.floor(lines[i].Z+.5)) -- round to integers
+
+ if lines[i].X > 0 then xInc[i] = 1 elseif lines[i].X < 0 then xInc[i] = -1 end
+ if lines[i].Y > 0 then yInc[i] = 1 elseif lines[i].Y < 0 then yInc[i] = -1 end
+ if lines[i].Z > 0 then zInc[i] = 1 elseif lines[i].Z < 0 then zInc[i] = -1 end
+
+ incrementVect[i] = Vector3.new(xInc[i], yInc[i], zInc[i])
+ if incrementVect[i].magnitude < .9 then incrementVect[i] = nil end
+ end
+
+
+ if not lines[2] then lines[2] = Vector3.new(0, 0, 0) end
+ if not lines[3] then lines[3] = Vector3.new(0, 0, 0) end
+
+ local waterForceTag = stampData.CurrentParts:FindFirstChild("WaterForceTag", true)
+ local waterForceDirectionTag = stampData.CurrentParts:FindFirstChild("WaterForceDirectionTag", true)
+
+ while (stepVect[3].magnitude*4 <= lines[3].magnitude) do
+ local outerStepVectIndex = 1
+ while outerStepVectIndex < 4 do
+ stepVect[2] = Vector3.new(0, 0, 0)
+ while (stepVect[2].magnitude*4 <= lines[2].magnitude) do
+ local innerStepVectIndex = 1
+ while innerStepVectIndex < 4 do
+ stepVect[1] = Vector3.new(0, 0, 0)
+ while (stepVect[1].magnitude*4 <= lines[1].magnitude) do
+ local stepVectSum = stepVect[1] + stepVect[2] + stepVect[3]
+ local cellPos = Vector3int16.new(startCell.X + stepVectSum.X, startCell.Y + stepVectSum.Y, startCell.Z + stepVectSum.Z)
+ if cellPos.X >= cMin.X and cellPos.Y >= cMin.Y and cellPos.Z >= cMin.Z and cellPos.X < cMax.X and cellPos.Y < cMax.Y and cellPos.Z < cMax.Z then
+ -- check if overlaps player or part
+ local okToStampTerrainBlock = checkTerrainBlockCollisions(cellPos, checkHighScalabilityStamp)
+
+ if okToStampTerrainBlock then
+ if waterForceTag then
+ cluster:SetWaterCell(cellPos.X, cellPos.Y, cellPos.Z, Enum.WaterForce[waterForceTag.Value], Enum.WaterDirection[waterForceDirectionTag.Value])
+ else
+ cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterMaterial, clusterType, clusterOrientation)
+ end
+ cellSet = true
+
+ -- auto-wedge it?
+ if (autoWedgeClusterParts) then
+ game.Workspace.Terrain:AutowedgeCells(Region3int16.new(Vector3int16.new(cellPos.x - 1, cellPos.y - 1, cellPos.z - 1),
+ Vector3int16.new(cellPos.x + 1, cellPos.y + 1, cellPos.z + 1)))
+ end
+ end
+ end
+ stepVect[1] = stepVect[1] + incrementVect[1]
+ end
+ if incrementVect[2] then
+ while innerStepVectIndex < 4 and worldAxes[innerStepVectIndex]:Dot(incrementVect[2]) == 0 do
+ innerStepVectIndex = innerStepVectIndex + 1
+ end
+ if innerStepVectIndex < 4 then
+ stepVect[2] = stepVect[2] + worldAxes[innerStepVectIndex] * worldAxes[innerStepVectIndex]:Dot(incrementVect[2])
+ end
+ innerStepVectIndex = innerStepVectIndex + 1
+ else
+ stepVect[2] = Vector3.new(1, 0, 0)
+ innerStepVectIndex = 4 -- skip all remaining loops
+ end
+ if (stepVect[2].magnitude*4 > lines[2].magnitude) then innerStepVectIndex = 4 end
+ end
+ end
+ if incrementVect[3] then
+ while outerStepVectIndex < 4 and worldAxes[outerStepVectIndex]:Dot(incrementVect[3]) == 0 do
+ outerStepVectIndex = outerStepVectIndex + 1
+ end
+ if outerStepVectIndex < 4 then
+ stepVect[3] = stepVect[3] + worldAxes[outerStepVectIndex] * worldAxes[outerStepVectIndex]:Dot(incrementVect[3])
+ end
+ outerStepVectIndex = outerStepVectIndex + 1
+ else -- skip all remaining loops
+ stepVect[3] = Vector3.new(1, 0, 0) outerStepVectIndex = 4
+ end
+ if (stepVect[3].magnitude*4 > lines[3].magnitude) then outerStepVectIndex = 4 end
+ end
+ end
+ end
+
+ -- and also get rid of any HighScalabilityLine stuff if it's there
+ HighScalabilityLine.Start = nil
+ HighScalabilityLine.Adorn.Parent = nil
+
+ -- Mark for undo.
+ if cellSet then
+ stampData.CurrentParts.Parent = nil
+ pcall(function() game:GetService("ChangeHistoryService"): SetWaypoint("StamperMulti") end)
+ end
+
+ return cellSet
+ end
+
+ local function DoStamperMouseUp(Mouse)
+ if not Mouse then
+ error("Error: RbxStamper.DoStamperMouseUp: Mouse is nil")
+ return false
+ end
+ if not Mouse:IsA("Mouse") then
+ error("Error: RbxStamper.DoStamperMouseUp: Mouse is of type", Mouse.className,"should be of type Mouse")
+ return false
+ end
+
+ if not stampData.Dragger then
+ error("Error: RbxStamper.DoStamperMouseUp: stampData.Dragger is nil")
+ return false
+ end
+
+ if not HighScalabilityLine then
+ return false
+ end
+
+ local checkHighScalabilityStamp = nil
+ if stampInModel then
+ local canStamp = nil
+ local isHSLPart = isMegaClusterPart()
+
+ if isHSLPart and
+ HighScalabilityLine and
+ HighScalabilityLine.Start and
+ HighScalabilityLine.InternalLine and
+ HighScalabilityLine.InternalLine.magnitude > 0 then -- we have an HSL line, test later
+ canStamp = true
+ checkHighScalabilityStamp = true
+ else
+ canStamp, checkHighScalabilityStamp = t.CanEditRegion(stampData.CurrentParts, allowedStampRegion)
+ end
+
+ if not canStamp then
+ if stampFailedFunc then
+ stampFailedFunc()
+ end
+ return false
+ end
+ end
+
+ -- if unstampable face, then don't let us stamp there!
+ if unstampableSurface then
+ flashRedBox()
+ return false
+ end
+
+ -- recheck if we can stamp, as we just moved part
+ canStamp, checkHighScalabilityStamp = t.CanEditRegion(stampData.CurrentParts, allowedStampRegion)
+ if not canStamp then
+ if stampFailedFunc then
+ stampFailedFunc()
+ end
+ return false
+ end
+
+ -- Prevent part from being stamped on top of a player
+
+ local minBB, maxBB = getBoundingBoxInWorldCoordinates(stampData.CurrentParts)
+
+ -- HotThoth's note: Now that above CurrentParts positioning has been commented out, to be truly correct, we would need to use the
+ -- value of configFound from the previous onStamperMouseMove call which moved the CurrentParts
+ -- Shouldn't this be true when lastTargetCFrame has been set and false otherwise?
+ configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData)
+
+ if configFound and not HighScalabilityLine.Adorn.Parent then
+ if clusterPartsInRegion(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector) then
+ flashRedBox()
+ return false
+ end
+
+ local blockingParts = game.Workspace:FindPartsInRegion3(Region3.new(minBB + insertBoundingBoxOverlapVector,
+ maxBB - insertBoundingBoxOverlapVector),
+ stampData.CurrentParts,
+ 100)
+
+
+ for b = 1, #blockingParts do
+ if isBlocker(blockingParts[b]) then
+ flashRedBox()
+ return false
+ end
+ end
+
+ local alreadyPushedUp = {}
+ -- if no blocking model below, then see if stamping on top of a character
+ for b = 1, #blockingParts do
+ if blockingParts[b].Parent and
+ not alreadyPushedUp[blockingParts[b].Parent] and
+ blockingParts[b].Parent:FindFirstChild("Humanoid") and
+ blockingParts[b].Parent:FindFirstChild("Humanoid"):IsA("Humanoid") then
+ ---------------------------------------------------------------------------
+ local blockingPersonTorso = blockingParts[b].Parent:FindFirstChild("Torso")
+ alreadyPushedUp[blockingParts[b].Parent] = true
+
+ if blockingPersonTorso then
+ -- if so, let's push the person upwards so they pop on top of the stamped model/part (but only if there's space above them)
+ local newY = maxBB.Y + 3
+ if spaceAboveCharacter(blockingPersonTorso, newY, stampData) then
+ blockingPersonTorso.CFrame = blockingPersonTorso.CFrame + Vector3.new(0, newY - blockingPersonTorso.CFrame.p.Y, 0)
+ else
+ -- if no space, we just error
+ flashRedBox()
+ return false
+ end
+ end
+ ---------------------------------------------------------------------------
+ end
+ end
+
+ elseif (not configFound) and not (HighScalabilityLine.Start and HighScalabilityLine.Adorn.Parent) then -- if no config then only stamp if it's a real HSL!
+ resetHighScalabilityLine()
+ return false
+ end
+
+ -- something will be stamped! so set the "StampedSomething" toggle to true
+ if game:FindFirstChild("Players") then
+ if game.Players["LocalPlayer"] then
+ if game.Players.LocalPlayer["Character"] then
+ local localChar = game.Players.LocalPlayer.Character
+ local stampTracker = localChar:FindFirstChild("StampTracker")
+ if stampTracker and not stampTracker.Value then
+ stampTracker.Value = true
+ end
+ end
+ end
+ end
+
+ -- if we drew a line of mega parts, stamp them out
+ if HighScalabilityLine.Start and HighScalabilityLine.Adorn.Parent and isMegaClusterPart() then
+ if ResolveMegaClusterStamp(checkHighScalabilityStamp) or checkHighScalabilityStamp then
+ -- kill the ghost part
+ stampData.CurrentParts.Parent = nil
+ return true
+ end
+ end
+
+ -- not High-Scalability-Line-Based, so behave normally [and get rid of any HSL stuff]
+ HighScalabilityLine.Start = nil
+ HighScalabilityLine.Adorn.Parent = nil
+
+ local cluster = game.Workspace.Terrain
+
+ -- if target point is in cluster, just use cluster:SetCell
+ if isMegaClusterPart() then
+ -- if targetCFrame is inside cluster, just set that cell to 1 and return
+ --local cellPos = cluster:WorldToCell(targetCFrame.p)
+
+ local cellPos
+ if stampData.CurrentParts:IsA("Model") then cellPos = cluster:WorldToCell(stampData.CurrentParts:GetModelCFrame().p)
+ else cellPos = cluster:WorldToCell(stampData.CurrentParts.CFrame.p) end
+
+ local cMax = game.Workspace.Terrain.MaxExtents.Max
+ local cMin = game.Workspace.Terrain.MaxExtents.Min
+
+ if checkTerrainBlockCollisions(cellPos, false) then
+
+ local clusterValues = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
+ local waterForceTag = stampData.CurrentParts:FindFirstChild("WaterForceTag", true)
+ local waterForceDirectionTag = stampData.CurrentParts:FindFirstChild("WaterForceDirectionTag", true)
+
+ if cellPos.X >= cMin.X and cellPos.Y >= cMin.Y and cellPos.Z >= cMin.Z and cellPos.X < cMax.X and cellPos.Y < cMax.Y and cellPos.Z < cMax.Z then
+
+ if waterForceTag then
+ cluster:SetWaterCell(cellPos.X, cellPos.Y, cellPos.Z, Enum.WaterForce[waterForceTag.Value], Enum.WaterDirection[waterForceDirectionTag.Value])
+ elseif not clusterValues then
+ cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, cellInfo.Material, cellInfo.clusterType, gInitial90DegreeRotations % 4)
+ elseif clusterValues:IsA("Vector3Value") then
+ cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterValues.Value.X, clusterValues.Value.Y, clusterValues.Value.Z)
+ else
+ cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterValues.Value, 0, 0)
+ end
+
+ local autoWedgeClusterParts = false
+ if stampData.CurrentParts:FindFirstChild("AutoWedge") then autoWedgeClusterParts = true end
+
+ -- auto-wedge it
+ if (autoWedgeClusterParts) then
+ game.Workspace.Terrain:AutowedgeCells(
+ Region3int16.new(
+ Vector3int16.new(cellPos.x - 1, cellPos.y - 1, cellPos.z - 1),
+ Vector3int16.new(cellPos.x + 1, cellPos.y + 1, cellPos.z + 1)
+ )
+ )
+ end
+
+ -- kill the ghost part
+ stampData.CurrentParts.Parent = nil
+
+ -- Mark for undo. It has to happen here or the selection display will come back also.
+ pcall(function() game:GetService("ChangeHistoryService"):SetWaypoint("StamperSingle") end)
+ return true
+ end
+ else
+ -- you tried to stamp a HSL-single part where one does not belong!
+ flashRedBox()
+ return false
+ end
+ end
+
+ local function getPlayer()
+ if game:FindFirstChild("Players") then
+ if game.Players["LocalPlayer"] then
+ return game.Players.LocalPlayer
+ end
+ end
+ return nil
+ end
+
+
+ -- Post process: after positioning the part or model, restore transparency, material, anchored and collide states and create joints
+ if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
+ if stampData.CurrentParts:IsA("Model") then
+ -- Tyler's magical hack-code for allowing/preserving clones of both Surface and Manual Welds... just don't ask X<
+ local manualWeldTable = {}
+ local manualWeldParentTable = {}
+ saveTheWelds(stampData.CurrentParts, manualWeldTable, manualWeldParentTable)
+ stampData.CurrentParts:BreakJoints()
+ stampData.CurrentParts:MakeJoints()
+ restoreTheWelds(manualWeldTable, manualWeldParentTable)
+ end
+
+ -- if it's a model, we also want to fill in the playerID and playerName tags, if it has those (e.g. for the friend-only door)
+ playerIdTag = stampData.CurrentParts:FindFirstChild("PlayerIdTag")
+ playerNameTag = stampData.CurrentParts:FindFirstChild("PlayerNameTag")
+ if playerIdTag ~= nil then
+ tempPlayerValue = getPlayer()
+ if tempPlayerValue ~= nil then playerIdTag.Value = tempPlayerValue.userId end
+ end
+ if playerNameTag ~= nil then
+ if game:FindFirstChild("Players") and game.Players["LocalPlayer"] then
+ tempPlayerValue = game.Players.LocalPlayer
+ if tempPlayerValue ~= nil then playerNameTag.Value = tempPlayerValue.Name end
+ end
+ end
+ -- ...and tag all inserted models for subsequent origin identification
+ -- if no RobloxModel tag already exists, then add it.
+ if stampData.CurrentParts:FindFirstChild("RobloxModel") == nil then
+ local stringTag = Instance.new("BoolValue", stampData.CurrentParts)
+ stringTag.Name = "RobloxModel"
+
+ if stampData.CurrentParts:FindFirstChild("RobloxStamper") == nil then
+ local stringTag2 = Instance.new("BoolValue", stampData.CurrentParts)
+ stringTag2.Name = "RobloxStamper"
+ end
+ end
+
+ else
+ stampData.CurrentParts:BreakJoints()
+ if stampData.CurrentParts:FindFirstChild("RobloxStamper") == nil then
+ local stringTag2 = Instance.new("BoolValue", stampData.CurrentParts)
+ stringTag2.Name = "RobloxStamper"
+ end
+ end
+
+ -- make sure all the joints are activated before restoring anchor states
+ if not createJoints then game.JointsService:CreateJoinAfterMoveJoints() end
+
+ -- Restore the original properties for all parts being stamped
+ for part, transparency in pairs(stampData.TransparencyTable) do
+ part.Transparency = transparency
+ end
+ for part, archivable in pairs(stampData.ArchivableTable) do
+ part.Archivable = archivable
+ end
+ for part, material in pairs(stampData.MaterialTable) do
+ part.Material = material
+ end
+ for part, collide in pairs(stampData.CanCollideTable) do
+ part.CanCollide = collide
+ end
+ for part, anchored in pairs(stampData.AnchoredTable) do
+ part.Anchored = anchored
+ end
+ for decal, transparency in pairs(stampData.DecalTransparencyTable) do
+ decal.Transparency = transparency
+ end
+
+ for part, surfaces in pairs(stampData.SurfaceTypeTable) do
+ loadSurfaceTypes(part, surfaces)
+ end
+
+ if isMegaClusterPart() then
+ stampData.CurrentParts.Transparency = 0
+ end
+
+ -- re-enable all seats
+ setSeatEnabledStatus(stampData.CurrentParts, true)
+
+ stampData.TransparencyTable = nil
+ stampData.ArchivableTable = nil
+ stampData.MaterialTable = nil
+ stampData.CanCollideTable = nil
+ stampData.AnchoredTable = nil
+ stampData.SurfaceTypeTable = nil
+
+ -- ...and tag all inserted models for subsequent origin identification
+ -- if no RobloxModel tag already exists, then add it.
+ if stampData.CurrentParts:FindFirstChild("RobloxModel") == nil then
+ local stringTag = Instance.new("BoolValue", stampData.CurrentParts)
+ stringTag.Name = "RobloxModel"
+ end
+
+ -- and make sure we don't delete it, now that it's not a ghost part
+ if ghostRemovalScript then ghostRemovalScript.Parent = nil end
+
+ --Re-enable the scripts
+ for index,script in pairs(stampData.DisabledScripts) do
+ script.Disabled = false
+ end
+
+ --Now that they are all marked enabled, reinsert them into the world so they start running
+ for index,script in pairs(stampData.DisabledScripts) do
+ local oldParent = script.Parent
+ script.Parent = nil
+ script:Clone().Parent = oldParent
+ end
+
+ -- clear out more data
+ stampData.DisabledScripts = nil
+ stampData.Dragger = nil
+ stampData.CurrentParts = nil
+
+ pcall(function() game:GetService("ChangeHistoryService"): SetWaypoint("StampedObject") end)
+ return true
+ end
+
+ local function pauseStamper()
+ for i = 1, #mouseCons do -- stop the mouse from doing anything
+ mouseCons[i]:disconnect()
+ mouseCons[i] = nil
+ end
+ mouseCons = {}
+
+ if stampData and stampData.CurrentParts then -- remove our ghost part
+ stampData.CurrentParts.Parent = nil
+ stampData.CurrentParts:Remove()
+ end
+
+ resetHighScalabilityLine()
+
+ game.JointsService:ClearJoinAfterMoveJoints()
+ end
+
+
+ local function prepareUnjoinableSurfaces(modelCFrame, parts, whichSurface)
+ local AXIS_VECTORS = {Vector3.new(1, 0, 0), Vector3.new(0, 1, 0), Vector3.new(0, 0, 1)} -- maybe last one is negative? TODO: check this!
+ local isPositive = 1
+ if whichSurface < 0 then isPositive = isPositive * -1 whichSurface = whichSurface*-1 end
+ local surfaceNormal = isPositive * modelCFrame:vectorToWorldSpace(AXIS_VECTORS[whichSurface])
+
+ for i = 1, #parts do
+ local currPart = parts[i]
+
+ -- now just need to find which surface of currPart most closely match surfaceNormal and then set that to Unjoinable
+ local surfaceNormalInLocalCoords = currPart.CFrame:vectorToObjectSpace(surfaceNormal)
+ if math.abs(surfaceNormalInLocalCoords.X) > math.abs(surfaceNormalInLocalCoords.Y) then
+ if math.abs(surfaceNormalInLocalCoords.X) > math.abs(surfaceNormalInLocalCoords.Z) then
+ if surfaceNormalInLocalCoords.X > 0 then currPart.RightSurface = "Unjoinable" else currPart.LeftSurface = "Unjoinable" end
+ else
+ if surfaceNormalInLocalCoords.Z > 0 then currPart.BackSurface = "Unjoinable" else currPart.FrontSurface = "Unjoinable" end
+ end
+ else
+ if math.abs(surfaceNormalInLocalCoords.Y) > math.abs(surfaceNormalInLocalCoords.Z) then
+ if surfaceNormalInLocalCoords.Y > 0 then currPart.TopSurface = "Unjoinable" else currPart.BottomSurface = "Unjoinable" end
+ else
+ if surfaceNormalInLocalCoords.Z > 0 then currPart.BackSurface = "Unjoinable" else currPart.FrontSurface = "Unjoinable" end
+ end
+ end
+ end
+ end
+
+ local function resumeStamper()
+ clone, parts = prepareModel(modelToStamp)
+
+ if not clone or not parts then
+ return
+ end
+
+ -- if we have unjoinable faces, then we want to change those surfaces to be Unjoinable
+ local unjoinableTag = clone:FindFirstChild("UnjoinableFaces", true)
+ if unjoinableTag then
+ for unjoinableSurface in string.gmatch(unjoinableTag.Value, "[^,]*") do
+ if tonumber(unjoinableSurface) then
+ if clone:IsA("Model") then
+ prepareUnjoinableSurfaces(clone:GetModelCFrame(), parts, tonumber(unjoinableSurface))
+ else
+ prepareUnjoinableSurfaces(clone.CFrame, parts, tonumber(unjoinableSurface))
+ end
+ end
+ end
+ end
+
+ stampData.ErrorBox = errorBox
+ if stampInModel then
+ clone.Parent = stampInModel
+ else
+ clone.Parent = game.Workspace
+ end
+
+ if clone:FindFirstChild("ClusterMaterial", true) then -- extract all info from vector
+ clusterMaterial = clone:FindFirstChild("ClusterMaterial", true)
+ if (clusterMaterial:IsA("Vector3Value")) then
+ cellInfo.Material = clusterMaterial.Value.X
+ cellInfo.clusterType = clusterMaterial.Value.Y
+ cellInfo.clusterOrientation = clusterMaterial.Value.Z
+ elseif clusterMaterial:IsA("IntValue") then
+ cellInfo.Material = clusterMaterial.Value
+ end
+ end
+
+ pcall(function() mouseTarget = Mouse.Target end)
+
+ if mouseTarget and mouseTarget.Parent:FindFirstChild("RobloxModel") == nil then
+ game.JointsService:SetJoinAfterMoveTarget(mouseTarget)
+ else
+ game.JointsService:SetJoinAfterMoveTarget(nil)
+ end
+ game.JointsService:ShowPermissibleJoints()
+
+ for index, object in pairs(stampData.DisabledScripts) do
+ if object.Name == "GhostRemovalScript" then
+ object.Parent = stampData.CurrentParts
+ end
+ end
+
+ stampData.Dragger = Instance.new("Dragger")
+
+ --Begin a movement by faking a MouseDown signal
+ stampData.Dragger:MouseDown(parts[1], Vector3.new(0,0,0), parts)
+ stampData.Dragger:MouseUp()
+
+ DoStamperMouseMove(Mouse)
+
+ table.insert(mouseCons,Mouse.Move:connect(function()
+ if movingLock or stampUpLock then return end
+ movingLock = true
+ DoStamperMouseMove(Mouse)
+ movingLock = false
+ end))
+
+ table.insert(mouseCons,Mouse.Button1Down:connect(function()
+ DoStamperMouseDown(Mouse)
+ end))
+
+ table.insert(mouseCons,Mouse.Button1Up:connect(function()
+ stampUpLock = true
+ while movingLock do wait() end
+ stamped.Value = DoStamperMouseUp(Mouse)
+ resetHighScalabilityLine()
+ stampUpLock = false
+ end))
+
+ stamped.Value = false
+ end
+
+ local function resetStamperState(newModelToStamp)
+
+ -- if we have a new model, swap it out
+ if newModelToStamp then
+ if not newModelToStamp:IsA("Model") and not newModelToStamp:IsA("BasePart") then
+ error("resetStamperState: newModelToStamp (first arg) is not nil, but not a model or part!")
+ end
+ modelToStamp = newModelToStamp
+ end
+
+ -- first clear our state
+ pauseStamper()
+ -- now lets load in the new model
+ resumeStamper()
+
+ end
+
+ -- load the model initially
+ resetStamperState()
+
+
+ -- setup the control table we pass back to the user
+ control.Stamped = stamped -- BoolValue that fires when user stamps
+ control.Paused = false
+
+ control.LoadNewModel = function(newStampModel) -- allows us to specify a new stamper model to be used with this stamper
+ if newStampModel and not newStampModel:IsA("Model") and not newStampModel:IsA("BasePart") then
+ error("Control.LoadNewModel: newStampModel (first arg) is not a Model or Part!")
+ return nil
+ end
+ resetStamperState(newStampModel)
+ end
+
+ control.ReloadModel = function() -- will automatically set stamper to get a new model of current model and start stamping with new model
+ resetStamperState()
+ end
+
+ control.Pause = function() -- temporarily stops stamping, use resume to start up again
+ if not control.Paused then
+ pauseStamper()
+ control.Paused = true
+ else
+ print("RbxStamper Warning: Tried to call Control.Pause() when already paused")
+ end
+ end
+
+ control.Resume = function() -- resumes stamping, if currently paused
+ if control.Paused then
+ resumeStamper()
+ control.Paused = false
+ else
+ print("RbxStamper Warning: Tried to call Control.Resume() without Pausing First")
+ end
+ end
+
+ control.ResetRotation = function() -- resets the model rotation so new models are at default orientation
+ -- gInitial90DegreeRotations = 0
+ -- Note: This function will not always work quite the way we want it to; we will have to build this out further so it works with
+ -- High-Scalability and with the new model orientation setting methods (model:ResetOrientationToIdentity()) [HotThoth]
+ end
+
+ control.Destroy = function() -- Stops current Stamp operation and destroys control construct
+ for i = 1, #mouseCons do
+ mouseCons[i]:disconnect()
+ mouseCons[i] = nil
+ end
+
+ if keyCon then
+ keyCon:disconnect()
+ end
+
+ game.JointsService:ClearJoinAfterMoveJoints()
+
+ if adorn then adorn:Destroy() end
+ if adornPart then adornPart:Destroy() end
+ if errorBox then errorBox:Destroy() end
+ if stampData then
+ if stampData["Dragger"] then
+ stampData.Dragger:Destroy()
+ end
+ if stampData.CurrentParts then
+ stampData.CurrentParts:Destroy()
+ end
+ end
+ if control and control["Stamped"] then
+ control.Stamped:Destroy()
+ end
+ control = nil
+ end
+
+ return control
+end
+
+t.Help =
+ function(funcNameOrFunc)
+ --input argument can be a string or a function. Should return a description (of arguments and expected side effects)
+ if funcNameOrFunc == "GetStampModel" or funcNameOrFunc == t.GetStampModel then
+ return "Function GetStampModel. Arguments: assetId, useAssetVersionId. assetId is the asset to load in, define useAssetVersionId as true if assetId is a version id instead of a relative assetId. Side effect: returns a model of the assetId, or a string with error message if something fails"
+ end
+ if funcNameOrFunc == "SetupStamperDragger" or funcNameOrFunc == t.SetupStamperDragger then
+ return "Function SetupStamperDragger. Side Effect: Creates 4x4 stamping mechanism for building out parts quickly. Arguments: ModelToStamp, Mouse, LegalStampCheckFunction. ModelToStamp should be a Model or Part, preferrably loaded from RbxStamper.GetStampModel and should have extents that are multiples of 4. Mouse should be a mouse object (obtained from things such as Tool.OnEquipped), used to drag parts around 'stamp' them out. LegalStampCheckFunction is optional, used as a callback with a table argument (table is full of instances about to be stamped). Function should return either true or false, false stopping the stamp action."
+ end
+ end
+
+return t
diff --git a/app/files/CoreGui/89449008 b/app/files/CoreGui/89449008
new file mode 100644
index 0000000..1ecbd88
--- /dev/null
+++ b/app/files/CoreGui/89449008
@@ -0,0 +1,876 @@
+-- A couple of necessary functions
+local function waitForChild(instance, name)
+ assert(instance)
+ assert(name)
+ while not instance:FindFirstChild(name) do
+ print('Waiting for ...', instance, name)
+ instance.ChildAdded:wait()
+ end
+ return instance:FindFirstChild(name)
+end
+local function waitForProperty(instance, property)
+ assert(instance)
+ assert(property)
+ while not instance[property] do
+ instance.Changed:wait()
+ end
+end
+
+local function IsTouchDevice()
+ local touchEnabled = false
+ pcall(function() touchEnabled = Game:GetService('UserInputService').TouchEnabled end)
+ return touchEnabled
+end
+
+
+waitForChild(game,"Players")
+waitForProperty(game.Players,"LocalPlayer")
+local player = game.Players.LocalPlayer
+
+local RbxGui, msg = LoadLibrary("RbxGui")
+if not RbxGui then print("could not find RbxGui!") return end
+
+--- Begin Locals
+local StaticTabName = "gear"
+
+local backpack = script.Parent
+local screen = script.Parent.Parent
+
+local backpackItems = {}
+local buttons = {}
+
+local debounce = false
+local browsingMenu = false
+
+local mouseEnterCons = {}
+local mouseClickCons = {}
+
+local characterChildAddedCon = nil
+local characterChildRemovedCon = nil
+local backpackAddCon = nil
+
+local playerBackpack = waitForChild(player,"Backpack")
+
+waitForChild(backpack,"Tabs")
+
+waitForChild(backpack,"Gear")
+local gearPreview = waitForChild(backpack.Gear,"GearPreview")
+
+local scroller = waitForChild(backpack.Gear,"GearGridScrollingArea")
+
+local currentLoadout = waitForChild(backpack.Parent,"CurrentLoadout")
+
+local grid = waitForChild(backpack.Gear,"GearGrid")
+local gearButton = waitForChild(grid,"GearButton")
+
+local swapSlot = waitForChild(script.Parent,"SwapSlot")
+
+local backpackManager = waitForChild(script.Parent,"CoreScripts/BackpackScripts/BackpackManager")
+local backpackOpenEvent = waitForChild(backpackManager,"BackpackOpenEvent")
+local backpackCloseEvent = waitForChild(backpackManager,"BackpackCloseEvent")
+local tabClickedEvent = waitForChild(backpackManager,"TabClickedEvent")
+local resizeEvent = waitForChild(backpackManager,"ResizeEvent")
+local searchRequestedEvent = waitForChild(backpackManager,"SearchRequestedEvent")
+local tellBackpackReadyFunc = waitForChild(backpackManager,"BackpackReady")
+
+-- creating scroll bar early as to make sure items get placed correctly
+local scrollFrame, scrollUp, scrollDown, recalculateScroll = RbxGui.CreateScrollingFrame(nil, "grid", Vector2.new(6, 6))
+
+scrollFrame.Position = UDim2.new(0,0,0,30)
+scrollFrame.Size = UDim2.new(1,0,1,-30)
+scrollFrame.Parent = backpack.Gear.GearGrid
+
+local scrollBar = Instance.new("Frame")
+scrollBar.Name = "ScrollBar"
+scrollBar.BackgroundTransparency = 0.9
+scrollBar.BackgroundColor3 = Color3.new(1,1,1)
+scrollBar.BorderSizePixel = 0
+scrollBar.Size = UDim2.new(0, 17, 1, -36)
+scrollBar.Position = UDim2.new(0,0,0,18)
+scrollBar.Parent = scroller
+
+scrollDown.Position = UDim2.new(0,0,1,-17)
+
+scrollUp.Parent = scroller
+scrollDown.Parent = scroller
+
+local scrollFrameLoadout, scrollUpLoadout, scrollDownLoadout, recalculateScrollLoadout = RbxGui.CreateScrollingFrame()
+
+scrollFrameLoadout.Position = UDim2.new(0,0,0,0)
+scrollFrameLoadout.Size = UDim2.new(1,0,1,0)
+scrollFrameLoadout.Parent = backpack.Gear.GearLoadouts.LoadoutsList
+
+local LoadoutButton = Instance.new("TextButton")
+LoadoutButton.RobloxLocked = true
+LoadoutButton.Name = "LoadoutButton"
+LoadoutButton.Font = Enum.Font.ArialBold
+LoadoutButton.FontSize = Enum.FontSize.Size14
+LoadoutButton.Position = UDim2.new(0,0,0,0)
+LoadoutButton.Size = UDim2.new(1,0,0,32)
+LoadoutButton.Style = Enum.ButtonStyle.RobloxButton
+LoadoutButton.Text = "Loadout #1"
+LoadoutButton.TextColor3 = Color3.new(1,1,1)
+LoadoutButton.Parent = scrollFrameLoadout
+
+local LoadoutButtonTwo = LoadoutButton:clone()
+LoadoutButtonTwo.Text = "Loadout #2"
+LoadoutButtonTwo.Parent = scrollFrameLoadout
+
+local LoadoutButtonThree = LoadoutButton:clone()
+LoadoutButtonThree.Text = "Loadout #3"
+LoadoutButtonThree.Parent = scrollFrameLoadout
+
+local LoadoutButtonFour = LoadoutButton:clone()
+LoadoutButtonFour.Text = "Loadout #4"
+LoadoutButtonFour.Parent = scrollFrameLoadout
+
+local scrollBarLoadout = Instance.new("Frame")
+scrollBarLoadout.Name = "ScrollBarLoadout"
+scrollBarLoadout.BackgroundTransparency = 0.9
+scrollBarLoadout.BackgroundColor3 = Color3.new(1,1,1)
+scrollBarLoadout.BorderSizePixel = 0
+scrollBarLoadout.Size = UDim2.new(0, 17, 1, -36)
+scrollBarLoadout.Position = UDim2.new(0,0,0,18)
+scrollBarLoadout.Parent = backpack.Gear.GearLoadouts.GearLoadoutsScrollingArea
+
+scrollDownLoadout.Position = UDim2.new(0,0,1,-17)
+
+scrollUpLoadout.Parent = backpack.Gear.GearLoadouts.GearLoadoutsScrollingArea
+scrollDownLoadout.Parent = backpack.Gear.GearLoadouts.GearLoadoutsScrollingArea
+
+
+-- Begin Functions
+function removeFromMap(map,object)
+ for i = 1, #map do
+ if map[i] == object then
+ table.remove(map,i)
+ break
+ end
+ end
+end
+
+function robloxLock(instance)
+ instance.RobloxLocked = true
+ children = instance:GetChildren()
+ if children then
+ for i, child in ipairs(children) do
+ robloxLock(child)
+ end
+ end
+end
+
+function resize()
+ local size = 0
+ if gearPreview.AbsoluteSize.Y > gearPreview.AbsoluteSize.X then
+ size = gearPreview.AbsoluteSize.X * 0.75
+ else
+ size = gearPreview.AbsoluteSize.Y * 0.75
+ end
+
+ waitForChild(gearPreview,"GearImage")
+ gearPreview.GearImage.Size = UDim2.new(0,size,0,size)
+ gearPreview.GearImage.Position = UDim2.new(0,gearPreview.AbsoluteSize.X/2 - size/2,0.75,-size)
+
+ resizeGrid()
+end
+
+function addToGrid(child)
+ if not child:IsA("Tool") then
+ if not child:IsA("HopperBin") then
+ return
+ end
+ end
+ if child:FindFirstChild("RobloxBuildTool") then return end
+
+ for i,v in pairs(backpackItems) do -- check to see if we already have this gear registered
+ if v == child then return end
+ end
+
+ table.insert(backpackItems,child)
+
+ local changeCon = child.Changed:connect(function(prop)
+ if prop == "Name" then
+ if buttons[child] then
+ if buttons[child].Image == "" then
+ buttons[child].GearText.Text = child.Name
+ end
+ end
+ end
+ end)
+ local ancestryCon = nil
+ ancestryCon = child.AncestryChanged:connect(function(theChild,theParent)
+ local thisObject = nil
+ for k,v in pairs(backpackItems) do
+ if v == child then
+ thisObject = v
+ break
+ end
+ end
+
+ waitForProperty(player,"Character")
+ waitForChild(player,"Backpack")
+ if (child.Parent ~= player.Backpack and child.Parent ~= player.Character) then
+ if ancestryCon then ancestryCon:disconnect() end
+ if changeCon then changeCon:disconnect() end
+
+ for k,v in pairs(backpackItems) do
+ if v == thisObject then
+ if mouseEnterCons[buttons[v]] then mouseEnterCons[buttons[v]]:disconnect() end
+ if mouseClickCons[buttons[v]] then mouseClickCons[buttons[v]]:disconnect() end
+ buttons[v].Parent = nil
+ buttons[v] = nil
+ break
+ end
+ end
+
+ removeFromMap(backpackItems,thisObject)
+
+ resizeGrid()
+ else
+ resizeGrid()
+ end
+ updateGridActive()
+ end)
+ resizeGrid()
+end
+
+function buttonClick(button)
+ if button:FindFirstChild("UnequipContextMenu") and not button.Active then
+ button.UnequipContextMenu.Visible = true
+ browsingMenu = true
+ end
+end
+
+function previewGear(button)
+ if not browsingMenu then
+ gearPreview.Visible = false
+ gearPreview.GearImage.Image = button.Image
+ gearPreview.GearStats.GearName.Text = button.GearReference.Value.Name
+ end
+end
+
+function findEmptySlot()
+ local smallestNum = nil
+ local loadout = currentLoadout:GetChildren()
+ for i = 1, #loadout do
+ if loadout[i]:IsA("Frame") and #loadout[i]:GetChildren() <= 0 then
+ local frameNum = tonumber(string.sub(loadout[i].Name,5))
+ if frameNum == 0 then frameNum = 10 end
+ if not smallestNum or (smallestNum > frameNum) then
+ smallestNum = frameNum
+ end
+ end
+ end
+ if smallestNum == 10 then smallestNum = 0 end
+ return smallestNum
+end
+
+function checkForSwap(button,x,y)
+ local loadoutChildren = currentLoadout:GetChildren()
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") and string.find(loadoutChildren[i].Name,"Slot") then
+ if x >= loadoutChildren[i].AbsolutePosition.x and x <= (loadoutChildren[i].AbsolutePosition.x + loadoutChildren[i].AbsoluteSize.x) then
+ if y >= loadoutChildren[i].AbsolutePosition.y and y <= (loadoutChildren[i].AbsolutePosition.y + loadoutChildren[i].AbsoluteSize.y) then
+ local slot = tonumber(string.sub(loadoutChildren[i].Name,5))
+ swapGearSlot(slot,button)
+ return true
+ end
+ end
+ end
+ end
+ return false
+end
+
+function resizeGrid()
+ for k,v in pairs(backpackItems) do
+ if not v:FindFirstChild("RobloxBuildTool") then
+ if not buttons[v] then
+ local buttonClone = gearButton:clone()
+ buttonClone.Parent = grid.ScrollingFrame
+ buttonClone.Visible = true
+ buttonClone.Image = v.TextureId
+ if buttonClone.Image == "" then
+ buttonClone.GearText.Text = v.Name
+ end
+
+ buttonClone.GearReference.Value = v
+ buttonClone.Draggable = true
+ buttons[v] = buttonClone
+
+
+ if not IsTouchDevice() then
+ local unequipMenu = getGearContextMenu()
+
+
+ unequipMenu.Visible = false
+ unequipMenu.Parent = buttonClone
+ end
+
+ local beginPos = nil
+ buttonClone.DragBegin:connect(function(value)
+ waitForChild(buttonClone, 'Background')
+ buttonClone['Background'].ZIndex = 10
+ buttonClone.ZIndex = 10
+ beginPos = value
+ end)
+ buttonClone.DragStopped:connect(function(x,y)
+ waitForChild(buttonClone, 'Background')
+ buttonClone['Background'].ZIndex = 1.0
+ buttonClone.ZIndex = 2
+ if beginPos ~= buttonClone.Position then
+ if not checkForSwap(buttonClone,x,y) then
+ buttonClone:TweenPosition(beginPos,Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.5, true)
+ buttonClone.Draggable = false
+ delay(0.5,function()
+ buttonClone.Draggable = true
+ end)
+ else
+ buttonClone.Position = beginPos
+ end
+ end
+ end)
+ local clickTime = tick()
+ mouseEnterCons[buttonClone] = buttonClone.MouseEnter:connect(function() previewGear(buttonClone) end)
+ mouseClickCons[buttonClone] = buttonClone.MouseButton1Click:connect(function()
+ local newClickTime = tick()
+ if buttonClone.Active and (newClickTime - clickTime) < 0.5 then
+ local slot = findEmptySlot()
+ if slot then
+ buttonClone.ZIndex = 1
+ swapGearSlot(slot,buttonClone)
+ end
+ else
+ buttonClick(buttonClone)
+ end
+ clickTime = newClickTime
+ end)
+ end
+ end
+ end
+ recalculateScroll()
+end
+
+function showPartialGrid(subset)
+ for k,v in pairs(buttons) do
+ v.Parent = nil
+ end
+ if subset then
+ for k,v in pairs(subset) do
+ v.Parent = grid.ScrollingFrame
+ end
+ end
+ recalculateScroll()
+end
+
+function showEntireGrid()
+ for k,v in pairs(buttons) do
+ v.Parent = grid.ScrollingFrame
+ end
+ recalculateScroll()
+end
+
+function inLoadout(gear)
+ local children = currentLoadout:GetChildren()
+ for i = 1, #children do
+ if children[i]:IsA("Frame") then
+ local button = children[i]:GetChildren()
+ if #button > 0 then
+ if button[1].GearReference.Value and button[1].GearReference.Value == gear then
+ return true
+ end
+ end
+ end
+ end
+ return false
+end
+
+function updateGridActive()
+ for k,v in pairs(backpackItems) do
+ if buttons[v] then
+ local gear = nil
+ local gearRef = buttons[v]:FindFirstChild("GearReference")
+
+ if gearRef then gear = gearRef.Value end
+
+ if not gear then
+ buttons[v].Active = false
+ elseif inLoadout(gear) then
+ buttons[v].Active = false
+ else
+ buttons[v].Active = true
+ end
+ end
+ end
+end
+
+function centerGear(loadoutChildren)
+ local gearButtons = {}
+ local lastSlotAdd = nil
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") and #loadoutChildren[i]:GetChildren() > 0 then
+ if loadoutChildren[i].Name == "Slot0" then
+ lastSlotAdd = loadoutChildren[i]
+ else
+ table.insert(gearButtons, loadoutChildren[i])
+ end
+ end
+ end
+ if lastSlotAdd then table.insert(gearButtons,lastSlotAdd) end
+
+ local startPos = ( 1 - (#gearButtons * 0.1) ) / 2
+ for i = 1, #gearButtons do
+ gearButtons[i]:TweenPosition(UDim2.new(startPos + ((i - 1) * 0.1),0,0,0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25, true)
+ end
+end
+
+function tabClickHandler(tabName)
+ if tabName == StaticTabName then
+ backpackOpenHandler(tabName)
+ else
+ backpackCloseHandler(tabName)
+ end
+end
+
+function backpackOpenHandler(currentTab)
+ if currentTab and currentTab ~= StaticTabName then
+ backpack.Gear.Visible = false
+ return
+ end
+
+ backpack.Gear.Visible = true
+ updateGridActive()
+
+ resizeGrid()
+ resize()
+ tellBackpackReadyFunc:Invoke()
+end
+
+function backpackCloseHandler(currentTab)
+ if currentTab and currentTab ~= StaticTabName then
+ backpack.Gear.Visible = false
+ return
+ end
+
+ backpack.Gear.Visible = false
+
+ resizeGrid()
+ resize()
+ tellBackpackReadyFunc:Invoke()
+end
+
+function loadoutCheck(child, selectState)
+ if not child:IsA("ImageButton") then return end
+ for k,v in pairs(backpackItems) do
+ if buttons[v] then
+ if child:FindFirstChild("GearReference") and buttons[v]:FindFirstChild("GearReference") then
+ if buttons[v].GearReference.Value == child.GearReference.Value then
+ buttons[v].Active = selectState
+ break
+ end
+ end
+ end
+ end
+end
+
+function clearPreview()
+ gearPreview.GearImage.Image = ""
+ gearPreview.GearStats.GearName.Text = ""
+end
+
+function removeAllEquippedGear(physGear)
+ local stuff = player.Character:GetChildren()
+ for i = 1, #stuff do
+ if ( stuff[i]:IsA("Tool") or stuff[i]:IsA("HopperBin") ) and stuff[i] ~= physGear then
+ stuff[i].Parent = playerBackpack
+ end
+ end
+end
+
+function equipGear(physGear)
+ removeAllEquippedGear(physGear)
+ physGear.Parent = player.Character
+ updateGridActive()
+end
+
+function unequipGear(physGear)
+ physGear.Parent = playerBackpack
+ updateGridActive()
+end
+
+function highlight(button)
+ button.TextColor3 = Color3.new(0,0,0)
+ button.BackgroundColor3 = Color3.new(0.8,0.8,0.8)
+end
+function clearHighlight(button)
+ button.TextColor3 = Color3.new(1,1,1)
+ button.BackgroundColor3 = Color3.new(0,0,0)
+end
+
+function swapGearSlot(slot,gearButton)
+ if not swapSlot.Value then -- signal loadout to swap a gear out
+ swapSlot.Slot.Value = slot
+ swapSlot.GearButton.Value = gearButton
+ swapSlot.Value = true
+ updateGridActive()
+ end
+end
+
+
+local UnequipGearMenuClick = function(element, menu)
+ if type(element.Action) ~= "number" then return end
+ local num = element.Action
+ if num == 1 then -- remove from loadout
+ unequipGear(menu.Parent.GearReference.Value)
+ local inventoryButton = menu.Parent
+ local gearToUnequip = inventoryButton.GearReference.Value
+ local loadoutChildren = currentLoadout:GetChildren()
+ local slot = -1
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") then
+ local button = loadoutChildren[i]:GetChildren()
+ if button[1] and button[1].GearReference.Value == gearToUnequip then
+ slot = button[1].SlotNumber.Text
+ break
+ end
+ end
+ end
+ swapGearSlot(slot,nil)
+ end
+end
+
+function setupCharacterConnections()
+
+ if backpackAddCon then backpackAddCon:disconnect() end
+ backpackAddCon = game.Players.LocalPlayer.Backpack.ChildAdded:connect(function(child) addToGrid(child) end)
+
+ -- make sure we get all the children
+ local backpackChildren = game.Players.LocalPlayer.Backpack:GetChildren()
+ for i = 1, #backpackChildren do
+ addToGrid(backpackChildren[i])
+ end
+
+ if characterChildAddedCon then characterChildAddedCon:disconnect() end
+ characterChildAddedCon =
+ game.Players.LocalPlayer.Character.ChildAdded:connect(function(child)
+ addToGrid(child)
+ updateGridActive()
+ end)
+
+ if characterChildRemovedCon then characterChildRemovedCon:disconnect() end
+ characterChildRemovedCon =
+ game.Players.LocalPlayer.Character.ChildRemoved:connect(function(child)
+ updateGridActive()
+ end)
+
+ wait()
+ centerGear(currentLoadout:GetChildren())
+end
+
+function removeCharacterConnections()
+ if characterChildAddedCon then characterChildAddedCon:disconnect() end
+ if characterChildRemovedCon then characterChildRemovedCon:disconnect() end
+ if backpackAddCon then backpackAddCon:disconnect() end
+end
+
+function trim(s)
+ return (s:gsub("^%s*(.-)%s*$", "%1"))
+end
+
+function filterGear(terms)
+ local filteredGear = {}
+ for k,v in pairs(backpackItems) do
+ if buttons[v] then
+ local gearString = string.lower(buttons[v].GearReference.Value.Name)
+ gearString = trim(gearString)
+ for i = 1, #terms do
+ if string.match(gearString,terms[i]) then
+ table.insert(filteredGear,buttons[v])
+ break
+ end
+ end
+ end
+ end
+
+ return filteredGear
+end
+function splitByWhitespace(text)
+ if type(text) ~= "string" then return nil end
+
+ local terms = {}
+ for token in string.gmatch(text, "[^%s]+") do
+ if string.len(token) > 0 then
+ table.insert(terms,token)
+ end
+ end
+ return terms
+end
+function showSearchGear(searchTerms)
+ if not backpack.Gear.Visible then return end -- currently not active tab
+
+ local searchTermTable = splitByWhitespace(searchTerms)
+ if searchTermTable and (#searchTermTable > 0) then
+ currSearchTerms = searchTermTable
+ else
+ currSearchTerms = nil
+ end
+
+ if searchTermTable == nil then
+ showEntireGrid()
+ return
+ end
+
+ local filteredButtons = filterGear(currSearchTerms)
+ showPartialGrid(filteredButtons)
+end
+
+function nukeBackpack()
+ while #buttons > 0 do
+ table.remove(buttons)
+ end
+ buttons = {}
+ while #backpackItems > 0 do
+ table.remove(backpackItems)
+ end
+ backpackItems = {}
+ local scrollingFrameChildren = grid.ScrollingFrame:GetChildren()
+ for i = 1, #scrollingFrameChildren do
+ scrollingFrameChildren[i]:remove()
+ end
+end
+
+function getGearContextMenu()
+ local gearContextMenu = Instance.new("Frame")
+ gearContextMenu.Active = true
+ gearContextMenu.Name = "UnequipContextMenu"
+ gearContextMenu.Size = UDim2.new(0,115,0,70)
+ gearContextMenu.Position = UDim2.new(0,-16,0,-16)
+ gearContextMenu.BackgroundTransparency = 1
+ gearContextMenu.Visible = false
+
+ local gearContextMenuButton = Instance.new("TextButton")
+ gearContextMenuButton.Name = "UnequipContextMenuButton"
+ gearContextMenuButton.Text = ""
+ gearContextMenuButton.Style = Enum.ButtonStyle.RobloxButtonDefault
+ gearContextMenuButton.ZIndex = 8
+ gearContextMenuButton.Size = UDim2.new(1, 0, 1, -20)
+ gearContextMenuButton.Visible = true
+ gearContextMenuButton.Parent = gearContextMenu
+
+ local elementHeight = 12
+
+ local contextMenuElements = {}
+ local contextMenuElementsName = {"Remove Hotkey"}
+
+ for i = 1, #contextMenuElementsName do
+ local element = {}
+ element.Type = "Button"
+ element.Text = contextMenuElementsName[i]
+ element.Action = i
+ element.DoIt = UnequipGearMenuClick
+ table.insert(contextMenuElements,element)
+ end
+
+ for i, contextElement in ipairs(contextMenuElements) do
+ local element = contextElement
+ if element.Type == "Button" then
+ local button = Instance.new("TextButton")
+ button.Name = "UnequipContextButton" .. i
+ button.BackgroundColor3 = Color3.new(0,0,0)
+ button.BorderSizePixel = 0
+ button.TextXAlignment = Enum.TextXAlignment.Left
+ button.Text = " " .. contextElement.Text
+ button.Font = Enum.Font.Arial
+ button.FontSize = Enum.FontSize.Size14
+ button.Size = UDim2.new(1, 8, 0, elementHeight)
+ button.Position = UDim2.new(0,0,0,elementHeight * i)
+ button.TextColor3 = Color3.new(1,1,1)
+ button.ZIndex = 9
+ button.Parent = gearContextMenuButton
+
+ if not IsTouchDevice() then
+
+ button.MouseButton1Click:connect(function()
+ if button.Active and not gearContextMenu.Parent.Active then
+ local success, result = pcall(function() element.DoIt(element, gearContextMenu) end)
+ browsingMenu = false
+ gearContextMenu.Visible = false
+ clearHighlight(button)
+ clearPreview()
+ end
+ end)
+
+ button.MouseEnter:connect(function()
+ if button.Active and gearContextMenu.Parent.Active then
+ highlight(button)
+ end
+ end)
+ button.MouseLeave:connect(function()
+ if button.Active and gearContextMenu.Parent.Active then
+ clearHighlight(button)
+ end
+ end)
+ end
+
+ contextElement.Button = button
+ contextElement.Element = button
+ elseif element.Type == "Label" then
+ local frame = Instance.new("Frame")
+ frame.Name = "ContextLabel" .. i
+ frame.BackgroundTransparency = 1
+ frame.Size = UDim2.new(1, 8, 0, elementHeight)
+
+ local label = Instance.new("TextLabel")
+ label.Name = "Text1"
+ label.BackgroundTransparency = 1
+ label.BackgroundColor3 = Color3.new(1,1,1)
+ label.BorderSizePixel = 0
+ label.TextXAlignment = Enum.TextXAlignment.Left
+ label.Font = Enum.Font.ArialBold
+ label.FontSize = Enum.FontSize.Size14
+ label.Position = UDim2.new(0.0, 0, 0, 0)
+ label.Size = UDim2.new(0.5, 0, 1, 0)
+ label.TextColor3 = Color3.new(1,1,1)
+ label.ZIndex = 9
+ label.Parent = frame
+ element.Label1 = label
+
+ if element.GetText2 then
+ label = Instance.new("TextLabel")
+ label.Name = "Text2"
+ label.BackgroundTransparency = 1
+ label.BackgroundColor3 = Color3.new(1,1,1)
+ label.BorderSizePixel = 0
+ label.TextXAlignment = Enum.TextXAlignment.Right
+ label.Font = Enum.Font.Arial
+ label.FontSize = Enum.FontSize.Size14
+ label.Position = UDim2.new(0.5, 0, 0, 0)
+ label.Size = UDim2.new(0.5, 0, 1, 0)
+ label.TextColor3 = Color3.new(1,1,1)
+ label.ZIndex = 9
+ label.Parent = frame
+ element.Label2 = label
+ end
+ frame.Parent = gearContextMenuButton
+ element.Label = frame
+ element.Element = frame
+ end
+ end
+
+ gearContextMenu.ZIndex = 4
+ gearContextMenu.MouseLeave:connect(function()
+ browsingMenu = false
+ gearContextMenu.Visible = false
+ clearPreview()
+ end)
+ robloxLock(gearContextMenu)
+
+ return gearContextMenu
+end
+
+function coreGuiChanged(coreGuiType,enabled)
+ if coreGuiType == Enum.CoreGuiType.Backpack or coreGuiType == Enum.CoreGuiType.All then
+ if not enabled then
+ backpack.Gear.Visible = false
+ end
+ end
+end
+
+
+local backpackChildren = player.Backpack:GetChildren()
+for i = 1, #backpackChildren do
+ addToGrid(backpackChildren[i])
+end
+
+------------------------- Start Lifelong Connections -----------------------
+
+
+resizeEvent.Event:connect(function(absSize)
+ if debounce then return end
+
+ debounce = true
+ wait()
+ resize()
+ resizeGrid()
+ debounce = false
+end)
+
+currentLoadout.ChildAdded:connect(function(child) loadoutCheck(child, false) end)
+currentLoadout.ChildRemoved:connect(function(child) loadoutCheck(child, true) end)
+
+currentLoadout.DescendantAdded:connect(function(descendant)
+ if not backpack.Visible and ( descendant:IsA("ImageButton") or descendant:IsA("TextButton") ) then
+ centerGear(currentLoadout:GetChildren())
+ end
+end)
+currentLoadout.DescendantRemoving:connect(function(descendant)
+ if not backpack.Visible and ( descendant:IsA("ImageButton") or descendant:IsA("TextButton") ) then
+ wait()
+ centerGear(currentLoadout:GetChildren())
+ end
+end)
+
+grid.MouseEnter:connect(function() clearPreview() end)
+grid.MouseLeave:connect(function() clearPreview() end)
+
+player.CharacterRemoving:connect(function()
+ removeCharacterConnections()
+ nukeBackpack()
+end)
+player.CharacterAdded:connect(function() setupCharacterConnections() end)
+
+player.ChildAdded:connect(function(child)
+ if child:IsA("Backpack") then
+ playerBackpack = child
+ if backpackAddCon then backpackAddCon:disconnect() end
+ backpackAddCon = game.Players.LocalPlayer.Backpack.ChildAdded:connect(function(child) addToGrid(child) end)
+ end
+end)
+
+swapSlot.Changed:connect(function()
+ if not swapSlot.Value then
+ updateGridActive()
+ end
+end)
+
+local loadoutChildren = currentLoadout:GetChildren()
+for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") and string.find(loadoutChildren[i].Name,"Slot") then
+ loadoutChildren[i].ChildRemoved:connect(function()
+ updateGridActive()
+ end)
+ loadoutChildren[i].ChildAdded:connect(function()
+ updateGridActive()
+ end)
+ end
+end
+------------------------- End Lifelong Connections -----------------------
+
+pcall(function()
+ coreGuiChanged(Enum.CoreGuiType.Backpack, Game.StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Backpack))
+ Game.StarterGui.CoreGuiChangedSignal:connect(coreGuiChanged)
+end)
+
+resize()
+resizeGrid()
+
+-- make sure any items in the loadout are accounted for in inventory
+local loadoutChildren = currentLoadout:GetChildren()
+for i = 1, #loadoutChildren do
+ loadoutCheck(loadoutChildren[i], false)
+end
+if not backpack.Visible then centerGear(currentLoadout:GetChildren()) end
+
+-- make sure that inventory is listening to gear reparenting
+if characterChildAddedCon == nil and game.Players.LocalPlayer["Character"] then
+ setupCharacterConnections()
+end
+if not backpackAddCon then
+ backpackAddCon = game.Players.LocalPlayer.Backpack.ChildAdded:connect(function(child) addToGrid(child) end)
+end
+
+backpackOpenEvent.Event:connect(backpackOpenHandler)
+backpackCloseEvent.Event:connect(backpackCloseHandler)
+tabClickedEvent.Event:connect(tabClickHandler)
+searchRequestedEvent.Event:connect(showSearchGear)
+
+recalculateScrollLoadout()
\ No newline at end of file
diff --git a/app/files/CoreGui/89449009 b/app/files/CoreGui/89449009
new file mode 100644
index 0000000..2100eb3
--- /dev/null
+++ b/app/files/CoreGui/89449009
@@ -0,0 +1,842 @@
+-- A couple of necessary functions
+local function waitForChild(instance, name)
+ assert(instance)
+ assert(name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+ return instance:FindFirstChild(name)
+end
+local function waitForProperty(instance, property)
+ assert(instance)
+ assert(property)
+ while not instance[property] do
+ instance.Changed:wait()
+ end
+end
+
+waitForChild(game,"Players")
+waitForProperty(game.Players,"LocalPlayer")
+local player = game.Players.LocalPlayer
+
+local RbxGui, msg = LoadLibrary("RbxGui")
+if not RbxGui then print("could not find RbxGui!") return end
+
+--- Begin Locals
+local StaticTabName = "gear"
+
+local backpack = script.Parent
+local screen = script.Parent.Parent
+
+local backpackItems = {}
+local buttons = {}
+
+local debounce = false
+local browsingMenu = false
+
+local mouseEnterCons = {}
+local mouseClickCons = {}
+
+local characterChildAddedCon = nil
+local characterChildRemovedCon = nil
+local backpackAddCon = nil
+
+local playerBackpack = waitForChild(player,"Backpack")
+
+waitForChild(backpack,"Tabs")
+
+waitForChild(backpack,"Gear")
+local gearPreview = waitForChild(backpack.Gear,"GearPreview")
+
+local scroller = waitForChild(backpack.Gear,"GearGridScrollingArea")
+
+local currentLoadout = waitForChild(backpack.Parent,"CurrentLoadout")
+
+local grid = waitForChild(backpack.Gear,"GearGrid")
+local gearButton = waitForChild(grid,"GearButton")
+
+local swapSlot = waitForChild(script.Parent,"SwapSlot")
+
+local backpackManager = waitForChild(script.Parent,"CoreScripts/BackpackScripts/BackpackManager")
+local backpackOpenEvent = waitForChild(backpackManager,"BackpackOpenEvent")
+local backpackCloseEvent = waitForChild(backpackManager,"BackpackCloseEvent")
+local tabClickedEvent = waitForChild(backpackManager,"TabClickedEvent")
+local resizeEvent = waitForChild(backpackManager,"ResizeEvent")
+local searchRequestedEvent = waitForChild(backpackManager,"SearchRequestedEvent")
+local tellBackpackReadyFunc = waitForChild(backpackManager,"BackpackReady")
+
+-- creating scroll bar early as to make sure items get placed correctly
+local scrollFrame, scrollUp, scrollDown, recalculateScroll = RbxGui.CreateScrollingFrame(nil,"grid")
+
+scrollFrame.Position = UDim2.new(0,0,0,30)
+scrollFrame.Size = UDim2.new(1,0,1,-30)
+scrollFrame.Parent = backpack.Gear.GearGrid
+
+local scrollBar = Instance.new("Frame")
+scrollBar.Name = "ScrollBar"
+scrollBar.BackgroundTransparency = 0.9
+scrollBar.BackgroundColor3 = Color3.new(1,1,1)
+scrollBar.BorderSizePixel = 0
+scrollBar.Size = UDim2.new(0, 17, 1, -36)
+scrollBar.Position = UDim2.new(0,0,0,18)
+scrollBar.Parent = scroller
+
+scrollDown.Position = UDim2.new(0,0,1,-17)
+
+scrollUp.Parent = scroller
+scrollDown.Parent = scroller
+
+local scrollFrameLoadout, scrollUpLoadout, scrollDownLoadout, recalculateScrollLoadout = RbxGui.CreateScrollingFrame()
+
+scrollFrameLoadout.Position = UDim2.new(0,0,0,0)
+scrollFrameLoadout.Size = UDim2.new(1,0,1,0)
+scrollFrameLoadout.Parent = backpack.Gear.GearLoadouts.LoadoutsList
+
+local LoadoutButton = Instance.new("TextButton")
+LoadoutButton.RobloxLocked = true
+LoadoutButton.Name = "LoadoutButton"
+LoadoutButton.Font = Enum.Font.ArialBold
+LoadoutButton.FontSize = Enum.FontSize.Size14
+LoadoutButton.Position = UDim2.new(0,0,0,0)
+LoadoutButton.Size = UDim2.new(1,0,0,32)
+LoadoutButton.Style = Enum.ButtonStyle.RobloxButton
+LoadoutButton.Text = "Loadout #1"
+LoadoutButton.TextColor3 = Color3.new(1,1,1)
+LoadoutButton.Parent = scrollFrameLoadout
+
+local LoadoutButtonTwo = LoadoutButton:clone()
+LoadoutButtonTwo.Text = "Loadout #2"
+LoadoutButtonTwo.Parent = scrollFrameLoadout
+
+local LoadoutButtonThree = LoadoutButton:clone()
+LoadoutButtonThree.Text = "Loadout #3"
+LoadoutButtonThree.Parent = scrollFrameLoadout
+
+local LoadoutButtonFour = LoadoutButton:clone()
+LoadoutButtonFour.Text = "Loadout #4"
+LoadoutButtonFour.Parent = scrollFrameLoadout
+
+local scrollBarLoadout = Instance.new("Frame")
+scrollBarLoadout.Name = "ScrollBarLoadout"
+scrollBarLoadout.BackgroundTransparency = 0.9
+scrollBarLoadout.BackgroundColor3 = Color3.new(1,1,1)
+scrollBarLoadout.BorderSizePixel = 0
+scrollBarLoadout.Size = UDim2.new(0, 17, 1, -36)
+scrollBarLoadout.Position = UDim2.new(0,0,0,18)
+scrollBarLoadout.Parent = backpack.Gear.GearLoadouts.GearLoadoutsScrollingArea
+
+scrollDownLoadout.Position = UDim2.new(0,0,1,-17)
+
+scrollUpLoadout.Parent = backpack.Gear.GearLoadouts.GearLoadoutsScrollingArea
+scrollDownLoadout.Parent = backpack.Gear.GearLoadouts.GearLoadoutsScrollingArea
+
+
+-- Begin Functions
+function removeFromMap(map,object)
+ for i = 1, #map do
+ if map[i] == object then
+ table.remove(map,i)
+ break
+ end
+ end
+end
+
+function robloxLock(instance)
+ instance.RobloxLocked = true
+ children = instance:GetChildren()
+ if children then
+ for i, child in ipairs(children) do
+ robloxLock(child)
+ end
+ end
+end
+
+function resize()
+ local size = 0
+ if gearPreview.AbsoluteSize.Y > gearPreview.AbsoluteSize.X then
+ size = gearPreview.AbsoluteSize.X * 0.75
+ else
+ size = gearPreview.AbsoluteSize.Y * 0.75
+ end
+
+ waitForChild(gearPreview,"GearImage")
+ gearPreview.GearImage.Size = UDim2.new(0,size,0,size)
+ gearPreview.GearImage.Position = UDim2.new(0,gearPreview.AbsoluteSize.X/2 - size/2,0.75,-size)
+
+ resizeGrid()
+end
+
+function addToGrid(child)
+ if not child:IsA("Tool") then
+ if not child:IsA("HopperBin") then
+ return
+ end
+ end
+ if child:FindFirstChild("RobloxBuildTool") then return end
+
+ for i,v in pairs(backpackItems) do -- check to see if we already have this gear registered
+ if v == child then return end
+ end
+
+ table.insert(backpackItems,child)
+
+ local changeCon = child.Changed:connect(function(prop)
+ if prop == "Name" then
+ if buttons[child] then
+ if buttons[child].Image == "" then
+ buttons[child].GearText.Text = child.Name
+ end
+ end
+ end
+ end)
+ local ancestryCon = nil
+ ancestryCon = child.AncestryChanged:connect(function(theChild,theParent)
+ local thisObject = nil
+ for k,v in pairs(backpackItems) do
+ if v == child then
+ thisObject = v
+ break
+ end
+ end
+
+ waitForProperty(player,"Character")
+ waitForChild(player,"Backpack")
+ if (child.Parent ~= player.Backpack and child.Parent ~= player.Character) then
+ if ancestryCon then ancestryCon:disconnect() end
+ if changeCon then changeCon:disconnect() end
+
+ for k,v in pairs(backpackItems) do
+ if v == thisObject then
+ if mouseEnterCons[buttons[v]] then mouseEnterCons[buttons[v]]:disconnect() end
+ if mouseClickCons[buttons[v]] then mouseClickCons[buttons[v]]:disconnect() end
+ buttons[v].Parent = nil
+ buttons[v] = nil
+ break
+ end
+ end
+
+ removeFromMap(backpackItems,thisObject)
+
+ resizeGrid()
+ else
+ resizeGrid()
+ end
+ updateGridActive()
+ end)
+ resizeGrid()
+end
+
+function buttonClick(button)
+ if button:FindFirstChild("UnequipContextMenu") and not button.Active then
+ button.UnequipContextMenu.Visible = true
+ browsingMenu = true
+ end
+end
+
+function previewGear(button)
+ if not browsingMenu then
+ gearPreview.GearImage.Image = button.Image
+ gearPreview.GearStats.GearName.Text = button.GearReference.Value.Name
+ end
+end
+
+function findEmptySlot()
+ local smallestNum = nil
+ local loadout = currentLoadout:GetChildren()
+ for i = 1, #loadout do
+ if loadout[i]:IsA("Frame") and #loadout[i]:GetChildren() <= 0 then
+ local frameNum = tonumber(string.sub(loadout[i].Name,5))
+ if frameNum == 0 then frameNum = 10 end
+ if not smallestNum or (smallestNum > frameNum) then
+ smallestNum = frameNum
+ end
+ end
+ end
+ if smallestNum == 10 then smallestNum = 0 end
+ return smallestNum
+end
+
+function checkForSwap(button,x,y)
+ local loadoutChildren = currentLoadout:GetChildren()
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") and string.find(loadoutChildren[i].Name,"Slot") then
+ if x >= loadoutChildren[i].AbsolutePosition.x and x <= (loadoutChildren[i].AbsolutePosition.x + loadoutChildren[i].AbsoluteSize.x) then
+ if y >= loadoutChildren[i].AbsolutePosition.y and y <= (loadoutChildren[i].AbsolutePosition.y + loadoutChildren[i].AbsoluteSize.y) then
+ local slot = tonumber(string.sub(loadoutChildren[i].Name,5))
+ swapGearSlot(slot,button)
+ return true
+ end
+ end
+ end
+ end
+ return false
+end
+
+function resizeGrid()
+ for k,v in pairs(backpackItems) do
+ if not v:FindFirstChild("RobloxBuildTool") then
+ if not buttons[v] then
+ local buttonClone = gearButton:clone()
+ buttonClone.Parent = grid.ScrollingFrame
+ buttonClone.Visible = true
+ buttonClone.Image = v.TextureId
+ if buttonClone.Image == "" then
+ buttonClone.GearText.Text = v.Name
+ end
+
+ buttonClone.GearReference.Value = v
+ buttonClone.Draggable = true
+ buttons[v] = buttonClone
+
+ local unequipMenu = getGearContextMenu()
+
+ unequipMenu.Visible = false
+ unequipMenu.Parent = buttonClone
+
+ local beginPos = nil
+ buttonClone.DragBegin:connect(function(value)
+ buttonClone.ZIndex = 9
+ beginPos = value
+ end)
+ buttonClone.DragStopped:connect(function(x,y)
+ buttonClone.ZIndex = 1
+ if beginPos ~= buttonClone.Position then
+ if not checkForSwap(buttonClone,x,y) then
+ buttonClone:TweenPosition(beginPos,Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.5, true)
+ buttonClone.Draggable = false
+ delay(0.5,function()
+ buttonClone.Draggable = true
+ end)
+ else
+ buttonClone.Position = beginPos
+ end
+ end
+ end)
+ local clickTime = tick()
+ mouseEnterCons[buttonClone] = buttonClone.MouseEnter:connect(function() previewGear(buttonClone) end)
+ mouseClickCons[buttonClone] = buttonClone.MouseButton1Click:connect(function()
+ local newClickTime = tick()
+ if buttonClone.Active and (newClickTime - clickTime) < 0.5 then
+ local slot = findEmptySlot()
+ if slot then
+ buttonClone.ZIndex = 1
+ swapGearSlot(slot,buttonClone)
+ end
+ else
+ buttonClick(buttonClone)
+ end
+ clickTime = newClickTime
+ end)
+ end
+ end
+ end
+ recalculateScroll()
+end
+
+function showPartialGrid(subset)
+ for k,v in pairs(buttons) do
+ v.Parent = nil
+ end
+ if subset then
+ for k,v in pairs(subset) do
+ v.Parent = grid.ScrollingFrame
+ end
+ end
+ recalculateScroll()
+end
+
+function showEntireGrid()
+ for k,v in pairs(buttons) do
+ v.Parent = grid.ScrollingFrame
+ end
+ recalculateScroll()
+end
+
+function inLoadout(gear)
+ local children = currentLoadout:GetChildren()
+ for i = 1, #children do
+ if children[i]:IsA("Frame") then
+ local button = children[i]:GetChildren()
+ if #button > 0 then
+ if button[1].GearReference.Value and button[1].GearReference.Value == gear then
+ return true
+ end
+ end
+ end
+ end
+ return false
+end
+
+function updateGridActive()
+ for k,v in pairs(backpackItems) do
+ if buttons[v] then
+ local gear = nil
+ local gearRef = buttons[v]:FindFirstChild("GearReference")
+
+ if gearRef then gear = gearRef.Value end
+
+ if not gear then
+ buttons[v].Active = false
+ elseif inLoadout(gear) then
+ buttons[v].Active = false
+ else
+ buttons[v].Active = true
+ end
+ end
+ end
+end
+
+function centerGear(loadoutChildren)
+ local gearButtons = {}
+ local lastSlotAdd = nil
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") and #loadoutChildren[i]:GetChildren() > 0 then
+ if loadoutChildren[i].Name == "Slot0" then
+ lastSlotAdd = loadoutChildren[i]
+ else
+ table.insert(gearButtons, loadoutChildren[i])
+ end
+ end
+ end
+ if lastSlotAdd then table.insert(gearButtons,lastSlotAdd) end
+
+ local startPos = ( 1 - (#gearButtons * 0.1) ) / 2
+ for i = 1, #gearButtons do
+ gearButtons[i]:TweenPosition(UDim2.new(startPos + ((i - 1) * 0.1),0,0,0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, 0.25, true)
+ end
+end
+
+function tabClickHandler(tabName)
+ if tabName == StaticTabName then
+ backpackOpenHandler(tabName)
+ else
+ backpackCloseHandler(tabName)
+ end
+end
+
+function backpackOpenHandler(currentTab)
+ if currentTab and currentTab ~= StaticTabName then
+ backpack.Gear.Visible = false
+ return
+ end
+
+ backpack.Gear.Visible = true
+ updateGridActive()
+
+ resizeGrid()
+ resize()
+ tellBackpackReadyFunc:Invoke()
+end
+
+function backpackCloseHandler(currentTab)
+ if currentTab and currentTab ~= StaticTabName then
+ backpack.Gear.Visible = false
+ return
+ end
+
+ backpack.Gear.Visible = false
+
+ resizeGrid()
+ resize()
+ tellBackpackReadyFunc:Invoke()
+end
+
+function loadoutCheck(child, selectState)
+ if not child:IsA("ImageButton") then return end
+ for k,v in pairs(backpackItems) do
+ if buttons[v] then
+ if child:FindFirstChild("GearReference") and buttons[v]:FindFirstChild("GearReference") then
+ if buttons[v].GearReference.Value == child.GearReference.Value then
+ buttons[v].Active = selectState
+ break
+ end
+ end
+ end
+ end
+end
+
+function clearPreview()
+ gearPreview.GearImage.Image = ""
+ gearPreview.GearStats.GearName.Text = ""
+end
+
+function removeAllEquippedGear(physGear)
+ local stuff = player.Character:GetChildren()
+ for i = 1, #stuff do
+ if ( stuff[i]:IsA("Tool") or stuff[i]:IsA("HopperBin") ) and stuff[i] ~= physGear then
+ stuff[i].Parent = playerBackpack
+ end
+ end
+end
+
+function equipGear(physGear)
+ removeAllEquippedGear(physGear)
+ physGear.Parent = player.Character
+ updateGridActive()
+end
+
+function unequipGear(physGear)
+ physGear.Parent = playerBackpack
+ updateGridActive()
+end
+
+function highlight(button)
+ button.TextColor3 = Color3.new(0,0,0)
+ button.BackgroundColor3 = Color3.new(0.8,0.8,0.8)
+end
+function clearHighlight(button)
+ button.TextColor3 = Color3.new(1,1,1)
+ button.BackgroundColor3 = Color3.new(0,0,0)
+end
+
+function swapGearSlot(slot,gearButton)
+ if not swapSlot.Value then -- signal loadout to swap a gear out
+ swapSlot.Slot.Value = slot
+ swapSlot.GearButton.Value = gearButton
+ swapSlot.Value = true
+ updateGridActive()
+ end
+end
+
+
+local UnequipGearMenuClick = function(element, menu)
+ if type(element.Action) ~= "number" then return end
+ local num = element.Action
+ if num == 1 then -- remove from loadout
+ unequipGear(menu.Parent.GearReference.Value)
+ local inventoryButton = menu.Parent
+ local gearToUnequip = inventoryButton.GearReference.Value
+ local loadoutChildren = currentLoadout:GetChildren()
+ local slot = -1
+ for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") then
+ local button = loadoutChildren[i]:GetChildren()
+ if button[1] and button[1].GearReference.Value == gearToUnequip then
+ slot = button[1].SlotNumber.Text
+ break
+ end
+ end
+ end
+ swapGearSlot(slot,nil)
+ end
+end
+
+function setupCharacterConnections()
+
+ if backpackAddCon then backpackAddCon:disconnect() end
+ backpackAddCon = game.Players.LocalPlayer.Backpack.ChildAdded:connect(function(child) addToGrid(child) end)
+
+ -- make sure we get all the children
+ local backpackChildren = game.Players.LocalPlayer.Backpack:GetChildren()
+ for i = 1, #backpackChildren do
+ addToGrid(backpackChildren[i])
+ end
+
+ if characterChildAddedCon then characterChildAddedCon:disconnect() end
+ characterChildAddedCon =
+ game.Players.LocalPlayer.Character.ChildAdded:connect(function(child)
+ addToGrid(child)
+ updateGridActive()
+ end)
+
+ if characterChildRemovedCon then characterChildRemovedCon:disconnect() end
+ characterChildRemovedCon =
+ game.Players.LocalPlayer.Character.ChildRemoved:connect(function(child)
+ updateGridActive()
+ end)
+
+ wait()
+ centerGear(currentLoadout:GetChildren())
+end
+
+function removeCharacterConnections()
+ if characterChildAddedCon then characterChildAddedCon:disconnect() end
+ if characterChildRemovedCon then characterChildRemovedCon:disconnect() end
+ if backpackAddCon then backpackAddCon:disconnect() end
+end
+
+function trim(s)
+ return (s:gsub("^%s*(.-)%s*$", "%1"))
+end
+
+function filterGear(terms)
+ local filteredGear = {}
+ for k,v in pairs(backpackItems) do
+ if buttons[v] then
+ local gearString = string.lower(buttons[v].GearReference.Value.Name)
+ gearString = trim(gearString)
+ for i = 1, #terms do
+ if string.match(gearString,terms[i]) then
+ table.insert(filteredGear,buttons[v])
+ break
+ end
+ end
+ end
+ end
+
+ return filteredGear
+end
+function splitByWhitespace(text)
+ if type(text) ~= "string" then return nil end
+
+ local terms = {}
+ for token in string.gmatch(text, "[^%s]+") do
+ if string.len(token) > 0 then
+ table.insert(terms,token)
+ end
+ end
+ return terms
+end
+function showSearchGear(searchTerms)
+ if not backpack.Gear.Visible then return end -- currently not active tab
+
+ local searchTermTable = splitByWhitespace(searchTerms)
+ if searchTermTable and (#searchTermTable > 0) then
+ currSearchTerms = searchTermTable
+ else
+ currSearchTerms = nil
+ end
+
+ if searchTermTable == nil then
+ showEntireGrid()
+ return
+ end
+
+ local filteredButtons = filterGear(currSearchTerms)
+ showPartialGrid(filteredButtons)
+end
+
+function nukeBackpack()
+ while #buttons > 0 do
+ table.remove(buttons)
+ end
+ buttons = {}
+ while #backpackItems > 0 do
+ table.remove(backpackItems)
+ end
+ backpackItems = {}
+ local scrollingFrameChildren = grid.ScrollingFrame:GetChildren()
+ for i = 1, #scrollingFrameChildren do
+ scrollingFrameChildren[i]:remove()
+ end
+end
+
+function getGearContextMenu()
+ local gearContextMenu = Instance.new("Frame")
+ gearContextMenu.Active = true
+ gearContextMenu.Name = "UnequipContextMenu"
+ gearContextMenu.Size = UDim2.new(0,115,0,70)
+ gearContextMenu.Position = UDim2.new(0,-16,0,-16)
+ gearContextMenu.BackgroundTransparency = 1
+ gearContextMenu.Visible = false
+
+ local gearContextMenuButton = Instance.new("TextButton")
+ gearContextMenuButton.Name = "UnequipContextMenuButton"
+ gearContextMenuButton.Text = ""
+ gearContextMenuButton.Style = Enum.ButtonStyle.RobloxButtonDefault
+ gearContextMenuButton.ZIndex = 8
+ gearContextMenuButton.Size = UDim2.new(1, 0, 1, -20)
+ gearContextMenuButton.Visible = true
+ gearContextMenuButton.Parent = gearContextMenu
+
+ local elementHeight = 12
+
+ local contextMenuElements = {}
+ local contextMenuElementsName = {"Remove Hotkey"}
+
+ for i = 1, #contextMenuElementsName do
+ local element = {}
+ element.Type = "Button"
+ element.Text = contextMenuElementsName[i]
+ element.Action = i
+ element.DoIt = UnequipGearMenuClick
+ table.insert(contextMenuElements,element)
+ end
+
+ for i, contextElement in ipairs(contextMenuElements) do
+ local element = contextElement
+ if element.Type == "Button" then
+ local button = Instance.new("TextButton")
+ button.Name = "UnequipContextButton" .. i
+ button.BackgroundColor3 = Color3.new(0,0,0)
+ button.BorderSizePixel = 0
+ button.TextXAlignment = Enum.TextXAlignment.Left
+ button.Text = " " .. contextElement.Text
+ button.Font = Enum.Font.Arial
+ button.FontSize = Enum.FontSize.Size14
+ button.Size = UDim2.new(1, 8, 0, elementHeight)
+ button.Position = UDim2.new(0,0,0,elementHeight * i)
+ button.TextColor3 = Color3.new(1,1,1)
+ button.ZIndex = 9
+ button.Parent = gearContextMenuButton
+
+ button.MouseButton1Click:connect(function()
+ if button.Active and not gearContextMenu.Parent.Active then
+ local success, result = pcall(function() element.DoIt(element, gearContextMenu) end)
+ browsingMenu = false
+ gearContextMenu.Visible = false
+ clearHighlight(button)
+ clearPreview()
+ end
+ end)
+
+ button.MouseEnter:connect(function()
+ if button.Active and gearContextMenu.Parent.Active then
+ highlight(button)
+ end
+ end)
+ button.MouseLeave:connect(function()
+ if button.Active and gearContextMenu.Parent.Active then
+ clearHighlight(button)
+ end
+ end)
+
+ contextElement.Button = button
+ contextElement.Element = button
+ elseif element.Type == "Label" then
+ local frame = Instance.new("Frame")
+ frame.Name = "ContextLabel" .. i
+ frame.BackgroundTransparency = 1
+ frame.Size = UDim2.new(1, 8, 0, elementHeight)
+
+ local label = Instance.new("TextLabel")
+ label.Name = "Text1"
+ label.BackgroundTransparency = 1
+ label.BackgroundColor3 = Color3.new(1,1,1)
+ label.BorderSizePixel = 0
+ label.TextXAlignment = Enum.TextXAlignment.Left
+ label.Font = Enum.Font.ArialBold
+ label.FontSize = Enum.FontSize.Size14
+ label.Position = UDim2.new(0.0, 0, 0, 0)
+ label.Size = UDim2.new(0.5, 0, 1, 0)
+ label.TextColor3 = Color3.new(1,1,1)
+ label.ZIndex = 9
+ label.Parent = frame
+ element.Label1 = label
+
+ if element.GetText2 then
+ label = Instance.new("TextLabel")
+ label.Name = "Text2"
+ label.BackgroundTransparency = 1
+ label.BackgroundColor3 = Color3.new(1,1,1)
+ label.BorderSizePixel = 0
+ label.TextXAlignment = Enum.TextXAlignment.Right
+ label.Font = Enum.Font.Arial
+ label.FontSize = Enum.FontSize.Size14
+ label.Position = UDim2.new(0.5, 0, 0, 0)
+ label.Size = UDim2.new(0.5, 0, 1, 0)
+ label.TextColor3 = Color3.new(1,1,1)
+ label.ZIndex = 9
+ label.Parent = frame
+ element.Label2 = label
+ end
+ frame.Parent = gearContextMenuButton
+ element.Label = frame
+ element.Element = frame
+ end
+ end
+
+ gearContextMenu.ZIndex = 4
+ gearContextMenu.MouseLeave:connect(function()
+ browsingMenu = false
+ gearContextMenu.Visible = false
+ clearPreview()
+ end)
+ robloxLock(gearContextMenu)
+
+ return gearContextMenu
+end
+
+local backpackChildren = player.Backpack:GetChildren()
+for i = 1, #backpackChildren do
+ addToGrid(backpackChildren[i])
+end
+
+------------------------- Start Lifelong Connections -----------------------
+
+
+resizeEvent.Event:connect(function(absSize)
+ if debounce then return end
+
+ debounce = true
+ wait()
+ resize()
+ resizeGrid()
+ debounce = false
+end)
+
+currentLoadout.ChildAdded:connect(function(child) loadoutCheck(child, false) end)
+currentLoadout.ChildRemoved:connect(function(child) loadoutCheck(child, true) end)
+
+currentLoadout.DescendantAdded:connect(function(descendant)
+ if not backpack.Visible and ( descendant:IsA("ImageButton") or descendant:IsA("TextButton") ) then
+ centerGear(currentLoadout:GetChildren())
+ end
+end)
+currentLoadout.DescendantRemoving:connect(function(descendant)
+ if not backpack.Visible and ( descendant:IsA("ImageButton") or descendant:IsA("TextButton") ) then
+ wait()
+ centerGear(currentLoadout:GetChildren())
+ end
+end)
+
+grid.MouseEnter:connect(function() clearPreview() end)
+grid.MouseLeave:connect(function() clearPreview() end)
+
+player.CharacterRemoving:connect(function()
+ removeCharacterConnections()
+ nukeBackpack()
+end)
+player.CharacterAdded:connect(function() setupCharacterConnections() end)
+
+player.ChildAdded:connect(function(child)
+ if child:IsA("Backpack") then
+ playerBackpack = child
+ if backpackAddCon then backpackAddCon:disconnect() end
+ backpackAddCon = game.Players.LocalPlayer.Backpack.ChildAdded:connect(function(child) addToGrid(child) end)
+ end
+end)
+
+swapSlot.Changed:connect(function()
+ if not swapSlot.Value then
+ updateGridActive()
+ end
+end)
+
+local loadoutChildren = currentLoadout:GetChildren()
+for i = 1, #loadoutChildren do
+ if loadoutChildren[i]:IsA("Frame") and string.find(loadoutChildren[i].Name,"Slot") then
+ loadoutChildren[i].ChildRemoved:connect(function()
+ updateGridActive()
+ end)
+ loadoutChildren[i].ChildAdded:connect(function()
+ updateGridActive()
+ end)
+ end
+end
+------------------------- End Lifelong Connections -----------------------
+
+resize()
+resizeGrid()
+
+-- make sure any items in the loadout are accounted for in inventory
+local loadoutChildren = currentLoadout:GetChildren()
+for i = 1, #loadoutChildren do
+ loadoutCheck(loadoutChildren[i], false)
+end
+if not backpack.Visible then centerGear(currentLoadout:GetChildren()) end
+
+-- make sure that inventory is listening to gear reparenting
+if characterChildAddedCon == nil and game.Players.LocalPlayer["Character"] then
+ setupCharacterConnections()
+end
+if not backpackAddCon then
+ backpackAddCon = game.Players.LocalPlayer.Backpack.ChildAdded:connect(function(child) addToGrid(child) end)
+end
+
+backpackOpenEvent.Event:connect(backpackOpenHandler)
+backpackCloseEvent.Event:connect(backpackCloseHandler)
+tabClickedEvent.Event:connect(tabClickHandler)
+searchRequestedEvent.Event:connect(showSearchGear)
+
+recalculateScrollLoadout()
\ No newline at end of file
diff --git a/app/files/CoreGui/89449093 b/app/files/CoreGui/89449093
new file mode 100644
index 0000000..e0747a8
--- /dev/null
+++ b/app/files/CoreGui/89449093
@@ -0,0 +1,443 @@
+-- This script manages context switches in the backpack (Gear to Wardrobe, etc.) and player state changes. Also manages global functions across different tabs (currently only search)
+if game.CoreGui.Version < 7 then return end -- peace out if we aren't using the right client
+
+-- basic functions
+local function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+ return instance:FindFirstChild(name)
+end
+local function waitForProperty(instance, property)
+ while not instance[property] do
+ instance.Changed:wait()
+ end
+end
+
+-- don't do anything if we are in an empty game
+waitForChild(game,"Players")
+if #game.Players:GetChildren() < 1 then
+ game.Players.ChildAdded:wait()
+end
+-- make sure everything is loaded in before we do anything
+-- get our local player
+waitForProperty(game.Players,"LocalPlayer")
+local player = game.Players.LocalPlayer
+
+
+
+------------------------ Locals ------------------------------
+local backpack = script.Parent
+waitForChild(backpack,"Gear")
+
+local screen = script.Parent.Parent
+assert(screen:IsA("ScreenGui"))
+
+waitForChild(backpack, "Tabs")
+waitForChild(backpack.Tabs, "CloseButton")
+local closeButton = backpack.Tabs.CloseButton
+
+waitForChild(backpack.Tabs, "InventoryButton")
+local inventoryButton = backpack.Tabs.InventoryButton
+if game.CoreGui.Version >= 8 then
+ waitForChild(backpack.Tabs, "WardrobeButton")
+ local wardrobeButton = backpack.Tabs.WardrobeButton
+end
+waitForChild(backpack.Parent,"ControlFrame")
+local backpackButton = waitForChild(backpack.Parent.ControlFrame,"BackpackButton")
+local currentTab = "gear"
+
+local searchFrame = waitForChild(backpack,"SearchFrame")
+waitForChild(backpack.SearchFrame,"SearchBoxFrame")
+local searchBox = waitForChild(backpack.SearchFrame.SearchBoxFrame,"SearchBox")
+local searchButton = waitForChild(backpack.SearchFrame,"SearchButton")
+local resetButton = waitForChild(backpack.SearchFrame,"ResetButton")
+
+local robloxGui = waitForChild(Game.CoreGui, 'RobloxGui')
+local currentLoadout = waitForChild(robloxGui, 'CurrentLoadout')
+local loadoutBackground = waitForChild(currentLoadout, 'Background')
+
+local canToggle = true
+local readyForNextEvent = true
+local backpackIsOpen = false
+local active = true
+local disabledByDeveloper = false
+
+local humanoidDiedCon = nil
+
+local backpackButtonPos
+
+local guiTweenSpeed = 0.25 -- how quickly we open/close the backpack
+
+local searchDefaultText = "Search..."
+local tilde = "~"
+local backquote = "`"
+
+local backpackSize = UDim2.new(0, 600, 0, 400)
+
+if robloxGui.AbsoluteSize.Y <= 320 then
+ backpackSize = UDim2.new(0, 200, 0, 140)
+end
+
+
+------------------------ End Locals ---------------------------
+
+
+---------------------------------------- Public Event Setup ----------------------------------------
+
+function createPublicEvent(eventName)
+ assert(eventName, "eventName is nil")
+ assert(tostring(eventName),"eventName is not a string")
+
+ local newEvent = Instance.new("BindableEvent")
+ newEvent.Name = tostring(eventName)
+ newEvent.Parent = script
+
+ return newEvent
+end
+
+function createPublicFunction(funcName, invokeFunc)
+ assert(funcName, "funcName is nil")
+ assert(tostring(funcName), "funcName is not a string")
+ assert(invokeFunc, "invokeFunc is nil")
+ assert(type(invokeFunc) == "function", "invokeFunc should be of type 'function'")
+
+ local newFunction = Instance.new("BindableFunction")
+ newFunction.Name = tostring(funcName)
+ newFunction.OnInvoke = invokeFunc
+ newFunction.Parent = script
+
+ return newFunction
+end
+
+-- Events
+local resizeEvent = createPublicEvent("ResizeEvent")
+local backpackOpenEvent = createPublicEvent("BackpackOpenEvent")
+local backpackCloseEvent = createPublicEvent("BackpackCloseEvent")
+local tabClickedEvent = createPublicEvent("TabClickedEvent")
+local searchRequestedEvent = createPublicEvent("SearchRequestedEvent")
+---------------------------------------- End Public Event Setup ----------------------------------------
+
+
+
+--------------------------- Internal Functions ----------------------------------------
+
+function deactivateBackpack()
+ backpack.Visible = false
+ active = false
+end
+
+function activateBackpack()
+ initHumanoidDiedConnections()
+ active = true
+ backpack.Visible = backpackIsOpen
+ if backpackIsOpen then
+ toggleBackpack()
+ end
+end
+
+function initHumanoidDiedConnections()
+ if humanoidDiedCon then
+ humanoidDiedCon:disconnect()
+ end
+ waitForProperty(game.Players.LocalPlayer,"Character")
+ waitForChild(game.Players.LocalPlayer.Character,"Humanoid")
+ humanoidDiedCon = game.Players.LocalPlayer.Character.Humanoid.Died:connect(deactivateBackpack)
+end
+
+local hideBackpack = function()
+ backpackIsOpen = false
+ readyForNextEvent = false
+ backpackButton.Selected = false
+ resetSearch()
+ backpackCloseEvent:Fire(currentTab)
+ backpack.Tabs.Visible = false
+ searchFrame.Visible = false
+ backpack:TweenSizeAndPosition(UDim2.new(0, backpackSize.X.Offset,0, 0), UDim2.new(0.5, -backpackSize.X.Offset/2, 1, -85), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, guiTweenSpeed, true,
+ function()
+ game.GuiService:RemoveCenterDialog(backpack)
+ backpack.Visible = false
+ backpackButton.Selected = false
+ end)
+ delay(guiTweenSpeed,function()
+ game.GuiService:RemoveCenterDialog(backpack)
+ backpack.Visible = false
+ backpackButton.Selected = false
+ readyForNextEvent = true
+ canToggle = true
+ end)
+end
+
+function showBackpack()
+ game.GuiService:AddCenterDialog(backpack, Enum.CenterDialogType.PlayerInitiatedDialog,
+ function()
+ backpack.Visible = true
+ backpackButton.Selected = true
+ end,
+ function()
+ backpack.Visible = false
+ backpackButton.Selected = false
+ end)
+ backpack.Visible = true
+ backpackButton.Selected = true
+ backpack:TweenSizeAndPosition(backpackSize, UDim2.new(0.5, -backpackSize.X.Offset/2, 1, -backpackSize.Y.Offset - 88), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, guiTweenSpeed, true)
+ delay(guiTweenSpeed,function()
+ backpack.Tabs.Visible = false
+ searchFrame.Visible = true
+ backpackOpenEvent:Fire(currentTab)
+ canToggle = true
+ readyForNextEvent = true
+ backpackButton.Image = 'http://www.syntax.eco/asset/?id=97644093'
+ backpackButton.Position = UDim2.new(0.5, -60, 1, -backpackSize.Y.Offset - 103)
+ end)
+end
+
+function toggleBackpack()
+ if not game.Players.LocalPlayer then return end
+ if not game.Players.LocalPlayer["Character"] then return end
+ if not canToggle then return end
+ if not readyForNextEvent then return end
+ readyForNextEvent = false
+ canToggle = false
+
+ backpackIsOpen = not backpackIsOpen
+
+ if backpackIsOpen then
+ loadoutBackground.Image = 'http://www.syntax.eco/asset/?id=97623721'
+ loadoutBackground.Position = UDim2.new(-0.03, 0, -0.17, 0)
+ loadoutBackground.Size = UDim2.new(1.05, 0, 1.25, 0)
+ loadoutBackground.ZIndex = 2.0
+ loadoutBackground.Visible = true
+ showBackpack()
+ else
+ backpackButton.Position = UDim2.new(0.5, -60, 1, -44)
+ loadoutBackground.Visible = false
+ backpackButton.Selected = false
+ backpackButton.Image = "http://www.syntax.eco/asset/?id=97617958"
+ loadoutBackground.Image = 'http://www.syntax.eco/asset/?id=96536002'
+ loadoutBackground.Position = UDim2.new(-0.1, 0, -0.1, 0)
+ loadoutBackground.Size = UDim2.new(1.2, 0, 1.2, 0)
+ hideBackpack()
+
+
+ local clChildren = currentLoadout:GetChildren()
+ for i = 1, #clChildren do
+ if clChildren[i] and clChildren[i]:IsA('Frame') then
+ local frame = clChildren[i]
+ if #frame:GetChildren() > 0 then
+ backpackButton.Position = UDim2.new(0.5, -60, 1, -108)
+ backpackButton.Visible = true
+ loadoutBackground.Visible = true
+ if frame:GetChildren()[1]:IsA('ImageButton') then
+ local imgButton = frame:GetChildren()[1]
+ imgButton.Active = true
+ imgButton.Draggable = false
+ end
+ end
+ end
+ end
+
+ end
+end
+
+function closeBackpack()
+ if backpackIsOpen then
+ toggleBackpack()
+ end
+end
+
+function setSelected(tab)
+ assert(tab)
+ assert(tab:IsA("TextButton"))
+
+ tab.BackgroundColor3 = Color3.new(1,1,1)
+ tab.TextColor3 = Color3.new(0,0,0)
+ tab.Selected = true
+ tab.ZIndex = 3
+end
+
+function setUnselected(tab)
+ assert(tab)
+ assert(tab:IsA("TextButton"))
+
+ tab.BackgroundColor3 = Color3.new(0,0,0)
+ tab.TextColor3 = Color3.new(1,1,1)
+ tab.Selected = false
+ tab.ZIndex = 1
+end
+
+function updateTabGui(selectedTab)
+ assert(selectedTab)
+
+ if selectedTab == "gear" then
+ setSelected(inventoryButton)
+ setUnselected(wardrobeButton)
+ elseif selectedTab == "wardrobe" then
+ setSelected(wardrobeButton)
+ setUnselected(inventoryButton)
+ end
+end
+
+function mouseLeaveTab(button)
+ assert(button)
+ assert(button:IsA("TextButton"))
+
+ if button.Selected then return end
+
+ button.BackgroundColor3 = Color3.new(0,0,0)
+end
+
+function mouseOverTab(button)
+ assert(button)
+ assert(button:IsA("TextButton"))
+
+ if button.Selected then return end
+
+ button.BackgroundColor3 = Color3.new(39/255,39/255,39/255)
+end
+
+function newTabClicked(tabName)
+ assert(tabName)
+ tabName = string.lower(tabName)
+ currentTab = tabName
+
+ updateTabGui(tabName)
+ tabClickedEvent:Fire(tabName)
+ resetSearch()
+end
+
+function trim(s)
+ return (s:gsub("^%s*(.-)%s*$", "%1"))
+end
+
+function splitByWhitespace(text)
+ if type(text) ~= "string" then return nil end
+
+ local terms = {}
+ for token in string.gmatch(text, "[^%s]+") do
+ if string.len(token) > 0 then
+ table.insert(terms,token)
+ end
+ end
+ return terms
+end
+
+function resetSearchBoxGui()
+ resetButton.Visible = false
+ searchBox.Text = searchDefaultText
+end
+
+function doSearch()
+ local searchText = searchBox.Text
+ if searchText == "" then
+ resetSearch()
+ return
+ end
+ searchText = trim(searchText)
+ resetButton.Visible = true
+ termTable = splitByWhitespace(searchText)
+ searchRequestedEvent:Fire(searchText) -- todo: replace this with termtable when table passing is possible
+end
+
+function resetSearch()
+ resetSearchBoxGui()
+ searchRequestedEvent:Fire()
+end
+
+local backpackReady = function()
+ readyForNextEvent = true
+end
+
+function coreGuiChanged(coreGuiType,enabled)
+ if coreGuiType == Enum.CoreGuiType.Backpack or coreGuiType == Enum.CoreGuiType.All then
+ active = enabled
+ disabledByDeveloper = not enabled
+
+ if disabledByDeveloper then
+ pcall(function()
+ game:GetService("GuiService"):RemoveKey(tilde)
+ game:GetService("GuiService"):RemoveKey(backquote)
+ end)
+ else
+ game:GetService("GuiService"):AddKey(tilde)
+ game:GetService("GuiService"):AddKey(backquote)
+ end
+
+ resetSearch()
+ searchFrame.Visible = enabled and backpackIsOpen
+
+ currentLoadout.Visible = enabled
+ backpack.Visible = enabled
+ backpackButton.Visible = enabled
+ end
+end
+
+--------------------------- End Internal Functions -------------------------------------
+
+
+------------------------------ Public Functions Setup -------------------------------------
+createPublicFunction("CloseBackpack", hideBackpack)
+createPublicFunction("BackpackReady", backpackReady)
+------------------------------ End Public Functions Setup ---------------------------------
+
+
+------------------------ Connections/Script Main -------------------------------------------
+
+pcall(function()
+ coreGuiChanged(Enum.CoreGuiType.Backpack, Game.StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Backpack))
+ Game.StarterGui.CoreGuiChangedSignal:connect(coreGuiChanged)
+end)
+
+inventoryButton.MouseButton1Click:connect(function() newTabClicked("gear") end)
+inventoryButton.MouseEnter:connect(function() mouseOverTab(inventoryButton) end)
+inventoryButton.MouseLeave:connect(function() mouseLeaveTab(inventoryButton) end)
+
+if game.CoreGui.Version >= 8 then
+ wardrobeButton.MouseButton1Click:connect(function() newTabClicked("wardrobe") end)
+ wardrobeButton.MouseEnter:connect(function() mouseOverTab(wardrobeButton) end)
+ wardrobeButton.MouseLeave:connect(function() mouseLeaveTab(wardrobeButton) end)
+end
+
+closeButton.MouseButton1Click:connect(closeBackpack)
+
+screen.Changed:connect(function(prop)
+ if prop == "AbsoluteSize" then
+ resizeEvent:Fire(screen.AbsoluteSize)
+ end
+end)
+
+-- GuiService key setup
+game:GetService("GuiService"):AddKey(tilde)
+game:GetService("GuiService"):AddKey(backquote)
+game:GetService("GuiService").KeyPressed:connect(function(key)
+ if not active or disabledByDeveloper then return end
+ if key == tilde or key == backquote then
+ toggleBackpack()
+ end
+end)
+backpackButton.MouseButton1Click:connect(function()
+ if not active or disabledByDeveloper then return end
+ toggleBackpack()
+end)
+
+if game.Players.LocalPlayer["Character"] then
+ activateBackpack()
+end
+
+game.Players.LocalPlayer.CharacterAdded:connect(activateBackpack)
+
+-- search functions
+searchBox.FocusLost:connect(function(enterPressed)
+ if enterPressed or searchBox.Text ~= "" then
+ doSearch()
+ elseif searchBox.Text == "" then
+ resetSearch()
+ end
+end)
+searchButton.MouseButton1Click:connect(doSearch)
+resetButton.MouseButton1Click:connect(resetSearch)
+
+if searchFrame and robloxGui.AbsoluteSize.Y <= 320 then
+ searchFrame.RobloxLocked = false
+ searchFrame:Destroy()
+end
\ No newline at end of file
diff --git a/app/files/CoreGui/89449094 b/app/files/CoreGui/89449094
new file mode 100644
index 0000000..3c14c16
--- /dev/null
+++ b/app/files/CoreGui/89449094
@@ -0,0 +1,360 @@
+-- This script manages context switches in the backpack (Gear to Wardrobe, etc.) and player state changes. Also manages global functions across different tabs (currently only search)
+if game.CoreGui.Version < 7 then return end -- peace out if we aren't using the right client
+
+-- basic functions
+local function waitForChild(instance, name)
+ while not instance:FindFirstChild(name) do
+ instance.ChildAdded:wait()
+ end
+ return instance:FindFirstChild(name)
+end
+local function waitForProperty(instance, property)
+ while not instance[property] do
+ instance.Changed:wait()
+ end
+end
+
+-- don't do anything if we are in an empty game
+waitForChild(game,"Players")
+if #game.Players:GetChildren() < 1 then
+ game.Players.ChildAdded:wait()
+end
+-- make sure everything is loaded in before we do anything
+-- get our local player
+waitForProperty(game.Players,"LocalPlayer")
+local player = game.Players.LocalPlayer
+
+
+
+------------------------ Locals ------------------------------
+local backpack = script.Parent
+waitForChild(backpack,"Gear")
+
+local screen = script.Parent.Parent
+assert(screen:IsA("ScreenGui"))
+
+waitForChild(backpack, "Tabs")
+waitForChild(backpack.Tabs, "CloseButton")
+local closeButton = backpack.Tabs.CloseButton
+
+waitForChild(backpack.Tabs, "InventoryButton")
+local inventoryButton = backpack.Tabs.InventoryButton
+if game.CoreGui.Version >= 8 then
+ waitForChild(backpack.Tabs, "WardrobeButton")
+ local wardrobeButton = backpack.Tabs.WardrobeButton
+end
+waitForChild(backpack.Parent,"ControlFrame")
+local backpackButton = waitForChild(backpack.Parent.ControlFrame,"BackpackButton")
+local currentTab = "gear"
+
+local searchFrame = waitForChild(backpack,"SearchFrame")
+waitForChild(backpack.SearchFrame,"SearchBoxFrame")
+local searchBox = waitForChild(backpack.SearchFrame.SearchBoxFrame,"SearchBox")
+local searchButton = waitForChild(backpack.SearchFrame,"SearchButton")
+local resetButton = waitForChild(backpack.SearchFrame,"ResetButton")
+
+local canToggle = true
+local readyForNextEvent = true
+local backpackIsOpen = false
+local active = true
+
+local humanoidDiedCon = nil
+
+local guiTweenSpeed = 0.25 -- how quickly we open/close the backpack
+
+local searchDefaultText = "Search..."
+local tilde = "~"
+local backquote = "`"
+
+------------------------ End Locals ---------------------------
+
+
+---------------------------------------- Public Event Setup ----------------------------------------
+
+function createPublicEvent(eventName)
+ assert(eventName, "eventName is nil")
+ assert(tostring(eventName),"eventName is not a string")
+
+ local newEvent = Instance.new("BindableEvent")
+ newEvent.Name = tostring(eventName)
+ newEvent.Parent = script
+
+ return newEvent
+end
+
+function createPublicFunction(funcName, invokeFunc)
+ assert(funcName, "funcName is nil")
+ assert(tostring(funcName), "funcName is not a string")
+ assert(invokeFunc, "invokeFunc is nil")
+ assert(type(invokeFunc) == "function", "invokeFunc should be of type 'function'")
+
+ local newFunction = Instance.new("BindableFunction")
+ newFunction.Name = tostring(funcName)
+ newFunction.OnInvoke = invokeFunc
+ newFunction.Parent = script
+
+ return newFunction
+end
+
+-- Events
+local resizeEvent = createPublicEvent("ResizeEvent")
+local backpackOpenEvent = createPublicEvent("BackpackOpenEvent")
+local backpackCloseEvent = createPublicEvent("BackpackCloseEvent")
+local tabClickedEvent = createPublicEvent("TabClickedEvent")
+local searchRequestedEvent = createPublicEvent("SearchRequestedEvent")
+---------------------------------------- End Public Event Setup ----------------------------------------
+
+
+
+--------------------------- Internal Functions ----------------------------------------
+
+function deactivateBackpack()
+ backpack.Visible = false
+ active = false
+end
+
+function activateBackpack()
+ initHumanoidDiedConnections()
+ active = true
+ backpack.Visible = backpackIsOpen
+end
+
+function initHumanoidDiedConnections()
+ if humanoidDiedCon then
+ humanoidDiedCon:disconnect()
+ end
+ waitForProperty(game.Players.LocalPlayer,"Character")
+ waitForChild(game.Players.LocalPlayer.Character,"Humanoid")
+ humanoidDiedCon = game.Players.LocalPlayer.Character.Humanoid.Died:connect(deactivateBackpack)
+end
+
+local hideBackpack = function()
+ backpackIsOpen = false
+ readyForNextEvent = false
+ backpackButton.Selected = false
+ resetSearch()
+ backpackCloseEvent:Fire(currentTab)
+ backpack.Tabs.Visible = false
+ searchFrame.Visible = false
+ backpack:TweenSizeAndPosition(UDim2.new(0,0,0,0),UDim2.new(0.5,0,0.5,0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, guiTweenSpeed, true,
+ function()
+ game.GuiService:RemoveCenterDialog(backpack)
+ backpack.Visible = false
+ backpackButton.Selected = false
+ end)
+ delay(guiTweenSpeed,function()
+ game.GuiService:RemoveCenterDialog(backpack)
+ backpack.Visible = false
+ backpackButton.Selected = false
+ canToggle = true
+ end)
+end
+
+function showBackpack()
+ game.GuiService:AddCenterDialog(backpack, Enum.CenterDialogType.PlayerInitiatedDialog,
+ function()
+ backpack.Visible = true
+ backpackButton.Selected = true
+ end,
+ function()
+ backpack.Visible = false
+ backpackButton.Selected = false
+ end)
+ backpack.Visible = true
+ backpackButton.Selected = true
+ backpack:TweenSizeAndPosition(UDim2.new(0.55, 0, 0.76, 0),UDim2.new(0.225, 0, 0.09, 0), Enum.EasingDirection.Out, Enum.EasingStyle.Quad, guiTweenSpeed, true)
+ delay(guiTweenSpeed,function()
+ backpack.Tabs.Visible = true
+ searchFrame.Visible = true
+ backpackOpenEvent:Fire(currentTab)
+ canToggle = true
+ end)
+end
+
+function toggleBackpack()
+ if not game.Players.LocalPlayer then return end
+ if not game.Players.LocalPlayer["Character"] then return end
+
+ if not canToggle then return end
+ if not readyForNextEvent then return end
+ readyForNextEvent = false
+ canToggle = false
+
+ backpackIsOpen = not backpackIsOpen
+
+ if backpackIsOpen then
+ showBackpack()
+ else
+ backpackButton.Selected = false
+ hideBackpack()
+ end
+end
+
+function closeBackpack()
+ if backpackIsOpen then
+ toggleBackpack()
+ end
+end
+
+function setSelected(tab)
+ assert(tab)
+ assert(tab:IsA("TextButton"))
+
+ tab.BackgroundColor3 = Color3.new(1,1,1)
+ tab.TextColor3 = Color3.new(0,0,0)
+ tab.Selected = true
+ tab.ZIndex = 3
+end
+
+function setUnselected(tab)
+ assert(tab)
+ assert(tab:IsA("TextButton"))
+
+ tab.BackgroundColor3 = Color3.new(0,0,0)
+ tab.TextColor3 = Color3.new(1,1,1)
+ tab.Selected = false
+ tab.ZIndex = 1
+end
+
+function updateTabGui(selectedTab)
+ assert(selectedTab)
+
+ if selectedTab == "gear" then
+ setSelected(inventoryButton)
+ setUnselected(wardrobeButton)
+ elseif selectedTab == "wardrobe" then
+ setSelected(wardrobeButton)
+ setUnselected(inventoryButton)
+ end
+end
+
+function mouseLeaveTab(button)
+ assert(button)
+ assert(button:IsA("TextButton"))
+
+ if button.Selected then return end
+
+ button.BackgroundColor3 = Color3.new(0,0,0)
+end
+
+function mouseOverTab(button)
+ assert(button)
+ assert(button:IsA("TextButton"))
+
+ if button.Selected then return end
+
+ button.BackgroundColor3 = Color3.new(39/255,39/255,39/255)
+end
+
+function newTabClicked(tabName)
+ assert(tabName)
+ tabName = string.lower(tabName)
+ currentTab = tabName
+
+ updateTabGui(tabName)
+ tabClickedEvent:Fire(tabName)
+ resetSearch()
+end
+
+function trim(s)
+ return (s:gsub("^%s*(.-)%s*$", "%1"))
+end
+
+function splitByWhitespace(text)
+ if type(text) ~= "string" then return nil end
+
+ local terms = {}
+ for token in string.gmatch(text, "[^%s]+") do
+ if string.len(token) > 0 then
+ table.insert(terms,token)
+ end
+ end
+ return terms
+end
+
+function resetSearchBoxGui()
+ resetButton.Visible = false
+ searchBox.Text = searchDefaultText
+end
+
+function doSearch()
+ local searchText = searchBox.Text
+ if searchText == "" then
+ resetSearch()
+ return
+ end
+ searchText = trim(searchText)
+ resetButton.Visible = true
+ termTable = splitByWhitespace(searchText)
+ searchRequestedEvent:Fire(searchText) -- todo: replace this with termtable when table passing is possible
+end
+
+function resetSearch()
+ resetSearchBoxGui()
+ searchRequestedEvent:Fire()
+end
+
+local backpackReady = function()
+ readyForNextEvent = true
+end
+
+--------------------------- End Internal Functions -------------------------------------
+
+
+------------------------------ Public Functions Setup -------------------------------------
+createPublicFunction("CloseBackpack", hideBackpack)
+createPublicFunction("BackpackReady", backpackReady)
+------------------------------ End Public Functions Setup ---------------------------------
+
+
+------------------------ Connections/Script Main -------------------------------------------
+
+inventoryButton.MouseButton1Click:connect(function() newTabClicked("gear") end)
+inventoryButton.MouseEnter:connect(function() mouseOverTab(inventoryButton) end)
+inventoryButton.MouseLeave:connect(function() mouseLeaveTab(inventoryButton) end)
+
+if game.CoreGui.Version >= 8 then
+ wardrobeButton.MouseButton1Click:connect(function() newTabClicked("wardrobe") end)
+ wardrobeButton.MouseEnter:connect(function() mouseOverTab(wardrobeButton) end)
+ wardrobeButton.MouseLeave:connect(function() mouseLeaveTab(wardrobeButton) end)
+end
+
+closeButton.MouseButton1Click:connect(closeBackpack)
+
+screen.Changed:connect(function(prop)
+ if prop == "AbsoluteSize" then
+ resizeEvent:Fire(screen.AbsoluteSize)
+ end
+end)
+
+-- GuiService key setup
+game:GetService("GuiService"):AddKey(tilde)
+game:GetService("GuiService"):AddKey(backquote)
+game:GetService("GuiService").KeyPressed:connect(function(key)
+ if not active then return end
+ if key == tilde or key == backquote then
+ toggleBackpack()
+ end
+end)
+backpackButton.MouseButton1Click:connect(function()
+ if not active then return end
+ toggleBackpack()
+end)
+
+if game.Players.LocalPlayer["Character"] then
+ activateBackpack()
+end
+game.Players.LocalPlayer.CharacterAdded:connect(activateBackpack)
+
+-- search functions
+searchBox.FocusLost:connect(function(enterPressed)
+ if enterPressed or searchBox.Text ~= "" then
+ doSearch()
+ elseif searchBox.Text == "" then
+ resetSearch()
+ end
+end)
+searchButton.MouseButton1Click:connect(doSearch)
+resetButton.MouseButton1Click:connect(resetSearch)
+
+backpackButton.Visible = true
diff --git a/app/files/CoreGui/97188756 b/app/files/CoreGui/97188756
new file mode 100644
index 0000000..1b12424
--- /dev/null
+++ b/app/files/CoreGui/97188756
@@ -0,0 +1,1376 @@
+--[[
+ //FileName: ChatScript.LUA
+ //Written by: Sorcus
+ //Description: Code for lua side chat on ROBLOX. Supports Scrolling.
+ //NOTE: If you find any bugs or inaccuracies PM Sorcus on ROBLOX or @Canavus on Twitter
+]]
+
+local forceChatGUI = false
+
+-- Utility functions + Globals
+local function WaitForChild(parent, childName)
+ while parent:FindFirstChild(childName) == nil do
+ parent.ChildAdded:wait(0.03)
+ end
+ return parent[childName]
+end
+
+local function typedef(obj)
+ return obj
+end
+
+local function IsPhone()
+ local cGui = Game:GetService('CoreGui')
+ local rGui = WaitForChild(cGui, 'RobloxGui')
+ if rGui.AbsoluteSize.Y < 600 then
+ return true
+ end
+ return false
+end
+
+-- Users can use enough white spaces to spoof chatting as other players
+-- This function removes trailing and leading white spaces
+-- AFAIK, there is no reason for spam white spaces
+local function StringTrim(str)
+ -- %S is whitespaces
+ -- When we find the first non space character defined by ^%s
+ -- we yank out anything in between that and the end of the string
+ -- Everything else is replaced with %1 which is essentially nothing
+ return (str:gsub("^%s*(.-)%s*$", "%1"))
+end
+
+while Game.Players.LocalPlayer == nil do wait(0.03) end
+
+local Player = Game.Players.LocalPlayer
+while Player.Character == nil do wait(0.03) end
+local RbxUtility = LoadLibrary('RbxUtility')
+local Gui = typedef(RbxUtility)
+local Camera = Game.Workspace.CurrentCamera
+
+-- Services
+local CoreGuiService = Game:GetService('CoreGui')
+local PlayersService = Game:GetService('Players')
+local DebrisService= Game:GetService('Debris')
+local GuiService = Game:GetService('GuiService')
+local inputService = game:GetService("UserInputService")
+
+-- Lua Enums
+local Enums do
+ Enums = {}
+ local EnumName = {} -- used as unique key for enum name
+ local enum_mt = {
+ __call = function(self,value)
+ return self[value] or self[tonumber(value)]
+ end;
+ __index = {
+ GetEnumItems = function(self)
+ local t = {}
+ for i,item in pairs(self) do
+ if type(i) == 'number' then
+ t[#t+1] = item
+ end
+ end
+ table.sort(t,function(a,b) return a.Value < b.Value end)
+ return t
+ end;
+ };
+ __tostring = function(self)
+ return "Enum." .. self[EnumName]
+ end;
+ }
+ local item_mt = {
+ __call = function(self,value)
+ return value == self or value == self.Name or value == self.Value
+ end;
+ __tostring = function(self)
+ return "Enum." .. self[EnumName] .. "." .. self.Name
+ end;
+ }
+ function CreateEnum(enumName)
+ return function(t)
+ local e = {[EnumName] = enumName}
+ for i,name in pairs(t) do
+ local item = setmetatable({Name=name,Value=i,Enum=e,[EnumName]=enumName},item_mt)
+ e[i] = item
+ e[name] = item
+ e[item] = item
+ end
+ Enums[enumName] = e
+ return setmetatable(e, enum_mt)
+ end
+ end
+end
+---------------------------------------------------
+------------------ Input class --------------------
+local Input = {
+ Mouse = Player:GetMouse(),
+ Speed = 0,
+ Simulating = false,
+
+ Configuration = {
+ DefaultSpeed = 1
+ },
+ UserIsScrolling = false
+ }
+
+---------------------------------------------------
+------------------ Chat class --------------------
+local Chat = {
+
+ ChatColors = {
+ BrickColor.new("Bright red"),
+ BrickColor.new("Bright blue"),
+ BrickColor.new("Earth green"),
+ BrickColor.new("Bright violet"),
+ BrickColor.new("Bright orange"),
+ BrickColor.new("Bright yellow"),
+ BrickColor.new("Light reddish violet"),
+ BrickColor.new("Brick yellow"),
+ },
+
+ Gui = nil,
+ Frame = nil,
+ RenderFrame = nil,
+ TapToChatLabel = nil,
+ ClickToChatButton = nil,
+
+ ScrollingLock = false,
+ EventListener = nil,
+
+ -- This is actually a ring buffer
+ -- Meaning at hitting the historyLength it wraps around
+ -- Reuses the text objects, so chat atmost uses 100 text objects
+ MessageQueue = {},
+
+ -- Stores all the values for configuring chat
+ Configuration = {
+ FontSize = Enum.FontSize.Size12, -- 10 is good
+ -- Also change this when you are changing the above, this is suboptimal but so is our interface to find FontSize
+ NumFontSize = 12,
+ HistoryLength = 20, -- stores up to 50 of the last chat messages for you to scroll through,
+ Size = UDim2.new(0.38, 0, 0.20, 0),
+ MessageColor = Color3.new(1, 1, 1),
+ AdminMessageColor = Color3.new(1, 215/255, 0),
+ XScale = 0.025,
+ LifeTime = 45,
+ Position = UDim2.new(0, 2, 0.05, 0),
+ DefaultTweenSpeed = 0.15,
+ },
+
+ -- This could be redone by just using the previous and next fields of the Queue
+ -- But the iterators cause issues, will be optimized later
+ SlotPositions_List = {},
+ -- To precompute and store all player null strings since its an expensive process
+ CachedSpaceStrings_List = {},
+ MouseOnFrame = false,
+ GotFocus = false,
+
+ Messages_List = {},
+ MessageThread = nil,
+
+ Admins_List = {
+ 'Rbadam', 'Adamintygum', 'androidtest', 'RobloxFrenchie', 'JacksSmirkingRevenge', 'LindaPepita', 'vaiobot', 'Goddessnoob', 'effward', 'Blockhaak', 'Drewbda', '659223', 'Tone', 'fasterbuilder19', 'Zeuxcg', 'concol2',
+ 'ReeseMcBlox', 'Jeditkacheff', 'whkm1980', 'ChiefJustus', 'Ellissar', 'Arbolito', 'Noob007', 'Limon', 'cmed', 'hawkington', 'Tabemono', 'autoconfig', 'BrightEyes', 'Monsterinc3D', 'MrDoomBringer', 'IsolatedEvent',
+ 'CountOnConnor', 'Scubasomething', 'OnlyTwentyCharacters', 'LordRugdumph', 'bellavour', 'david.baszucki', 'ibanez2189', 'Sorcus', 'DeeAna00', 'TheLorekt', 'NiqueMonster', 'Thorasaur', 'MSE6', 'CorgiParade', 'Varia',
+ '4runningwolves', 'pulmoesflor', 'Olive71', 'groundcontroll2', 'GuruKrish', 'Countvelcro', 'IltaLumi', 'juanjuan23', 'OstrichSized', 'jackintheblox', 'SlingshotJunkie', 'gordonrox24', 'sharpnine', 'Motornerve', 'Motornerve',
+ 'watchmedogood', 'jmargh', 'JayKorean', 'Foyle', 'MajorTom4321', 'Shedletsky', 'supernovacaine', 'FFJosh', 'Sickenedmonkey', 'Doughtless', 'KBUX', 'totallynothere', 'ErzaStar', 'Keith', 'Chro', 'SolarCrane', 'GloriousSalt',
+ 'UristMcSparks', 'ITOlaurEN', 'Malcomso', 'Stickmasterluke', 'windlight13', 'yumyumcheerios', 'Stravant', 'ByteMe', 'imaginationsensation', 'Matt.Dusek', 'Mcrtest', 'Seranok', 'maxvee', 'Coatp0cketninja', 'Screenme',
+ 'b1tsh1ft', 'Totbl', 'Aquabot8', 'grossinger', 'Merely', 'CDakkar', 'Siekiera', 'Robloxkidsaccount', 'flotsamthespork', 'Soggoth', 'Phil', 'OrcaSparkles', 'skullgoblin', 'RickROSStheB0SS', 'ArgonPirate', 'NobleDragon',
+ 'Squidcod', 'Raeglyn', 'RobloxSai', 'Briarroze', 'hawkeyebandit', 'DapperBuffalo', 'Vukota', 'swiftstone', 'Gemlocker', 'Loopylens', 'Tarabyte', 'Timobius', 'Tobotrobot', 'Foster008', 'Twberg', 'DarthVaden', 'Khanovich',
+ 'CodeWriter', 'VladTheFirst', 'Phaedre', 'gorroth', 'SphinxShen', 'jynj1984', 'RoboYZ', 'ZodiacZak'
+ },
+
+
+ SafeChat_List = {
+ ['Use the Chat menu to talk to me.'] = {'/sc 0', true},
+ ['I can only see menu chats.'] = {'/sc 1', true},
+ ['Hello'] = {
+ ['Hi'] = {'/sc 2_0', true, ['Hi there!'] = true, ['Hi everyone'] = true},
+ ['Howdy'] = {'/sc 2_1', true, ['Howdy partner!'] = true},
+ ['Greetings'] = {'/sc 2_2', true, ['Greetings everyone'] = true, ['Greetings Robloxians!'] = true, ['Seasons greetings!'] = true},
+ ['Welcome'] = {'/sc 2_3', true, ['Welcome to my place'] = true, ['Welcome to my barbeque'] = true, ['Welcome to our base'] = true},
+ ['Hey there!'] = {'/sc 2_4', true},
+ ['What\'s up?'] = {'/sc 2_5', true, ['How are you doing?'] = true, ['How\'s it going?'] = true, ['What\'s new?'] = true},
+ ['Good day'] = {'/sc 2_6', true, ['Good morning'] = true, ['Good evening'] = true, ['Good afternoon'] = true, ['Good night'] = true},
+ ['Silly'] = {'/sc 2_7', true, ['Waaaaaaaz up?!'] = true, ['Hullo!'] = true, ['Behold greatness, mortals!'] = true, ['Pardon me, is this Sparta?'] = true, ['THIS IS SPARTAAAA!'] = true},
+ ['Happy Holidays!'] = {'/sc 2_8', true, ['Happy New Year!'] = true,
+ ['Happy Valentine\'s Day!'] = true,
+ ['Beware the Ides of March!'] = true,
+ ['Happy St. Patrick\'s Day!'] = true,
+ ['Happy Easter!'] = true,
+ ['Happy Earth Day!'] = true,
+ ['Happy 4th of July!'] = true,
+ ['Happy Thanksgiving!'] = true,
+ ['Happy Halloween!'] = true,
+ ['Happy Hanukkah!'] = true,
+ ['Merry Christmas!'] = true,
+ ['Happy Halloween!'] = true,
+ ['Happy Earth Day!'] = true,
+ ['Happy May Day!'] = true,
+ ['Happy Towel Day!'] = true,
+ ['Happy ROBLOX Day!'] = true,
+ ['Happy LOL Day!'] = true },
+
+ [1] = '/sc 2'
+ },
+ ['Goodbye'] = {
+ ['Good Night']= {'/sc 3_0', true,
+ ['Sweet dreams'] = true,
+ ['Go to sleep!'] = true,
+ ['Lights out!'] = true,
+ ['Bedtime'] = true,
+ ['Going to bed now'] = true},
+
+ ['Later']= {'/sc 3_1', true,
+ ['See ya later'] = true,
+ ['Later gator!'] = true,
+ ['See you tomorrow'] = true},
+
+ ['Bye'] = {'/sc 3_2', true, ['Hasta la bye bye!'] = true},
+ ['I\'ll be right back'] = {'/sc 3_3', true},
+ ['I have to go'] = {'/sc 3_4', true},
+ ['Farewell'] = {'/sc 3_5', true, ['Take care'] = true, ['Have a nice day'] = true, ['Goodluck!'] = true, ['Ta-ta for now!'] = true},
+ ['Peace'] = {'/sc 3_6', true, ['Peace out!'] = true, ['Peace dudes!'] = true, ['Rest in pieces!'] = true},
+ ['Silly'] = {'/sc 3_7', true,
+ ['To the batcave!'] = true,
+ ['Over and out!'] = true,
+ ['Happy trails!'] = true,
+ ['I\'ve got to book it!'] = true,
+ ['Tootles!'] = true,
+ ['Smell you later!'] = true,
+ ['GG!'] = true,
+ ['My house is on fire! gtg.'] = true},
+ [1] = '/sc 3'
+ },
+ ['Friend'] ={
+ ['Wanna be friends?'] = {'/sc 4_0', true},
+ ['Follow me'] = {'/sc 4_1', true, ['Come to my place!'] = true, ['Come to my base!'] = true, ['Follow me, team!'] = true, ['Follow me'] = true},
+ ['Your place is cool'] = {'/sc 4_2', true, ['Your place is fun'] = true, ['Your place is awesome'] = true, ['Your place looks good'] = true, ['This place is awesome!'] = true},
+ ['Thank you'] = {'/sc 4_3', true, ['Thanks for playing'] = true, ['Thanks for visiting'] = true, ['Thanks for everything'] = true, ['No, thank you'] = true, ['Thanx'] = true},
+ ['No problem'] = {'/sc 4_4', true, ['Don\'t worry'] = true, ['That\'s ok'] = true, ['np'] = true},
+ ['You are ...'] = {'/sc 4_5', true,
+ ['You are great!'] = true,
+ ['You are good!'] = true,
+ ['You are cool!'] = true,
+ ['You are funny!'] = true,
+ ['You are silly!'] = true,
+ ['You are awesome!'] = true,
+ ['You are doing something I don\'t like, please stop'] = true
+ },
+ ['I like ...'] = {'/sc 4_6', true, ['I like your name'] = true, ['I like your shirt'] = true, ['I like your place'] = true, ['I like your style'] = true,
+ ['I like you'] = true, ['I like items'] = true, ['I like money'] = true},
+ ['Sorry'] = {'/sc 4_7', true, ['My bad!'] = true, ['I\'m sorry'] = true, ['Whoops!'] = true, ['Please forgive me.'] = true, ['I forgive you.'] = true,
+ ['I didn\'t mean to do that.'] = true, ['Sorry, I\'ll stop now.'] = true},
+ [1] = '/sc 4'
+ },
+ ['Questions'] = {
+ ['Who?'] = {'/sc 5_0', true, ['Who wants to be my friend?'] = true, ['Who wants to be on my team?'] = true, ['Who made this brilliant game?'] = true},
+ ['What?'] = {'/sc 5_1', true, ['What is your favorite animal?'] = true, ['What is your favorite game?'] = true, ['What is your favorite movie?'] = true,
+ ['What is your favorite TV show?'] = true, ['What is your favorite music?'] = true, ['What are your hobbies?'] = true, ['LOLWUT?'] = true},
+ ['When?'] = {'/sc 5_2', true, ['When are you online?'] = true, ['When is the new version coming out?'] = true, ['When can we play again?'] = true, ['When will your place be done?'] = true},
+ ['Where?'] = {'/sc 5_3', true, ['Where do you want to go?'] = true, ['Where are you going?'] = true, ['Where am I?!'] = true, ['Where did you go?'] = true},
+ ['How?'] = {'/sc 5_4', true, ['How are you today?'] = true, ['How did you make this cool place?'] = true, ['LOLHOW?'] = true},
+ ['Can I...'] = {'/sc 5_5', true, ['Can I have a tour?'] = true, ['Can I be on your team?'] = true, ['Can I be your friend?'] = true, ['Can I try something?'] = true,
+ ['Can I have that please?'] = true, ['Can I have that back please?'] = true, ['Can I have borrow your hat?'] = true, ['Can I have borrow your gear?'] = true},
+ [1] = '/sc 5'
+ },
+ ['Answers'] = {
+ ['You need help?'] = {'/sc 6_0', true, ['Check out the news section'] = true, ['Check out the help section'] = true, ['Read the wiki!'] = true,
+ ['All the answers are in the wiki!'] = true, ['I will help you with this.'] = true},
+ ['Some people ...'] = {'/sc 6_1', true, ['Me'] = true, ['Not me'] = true, ['You'] = true, ['All of us'] = true, ['Everyone but you'] = true, ['Builderman!'] = true,
+ ['Telamon!'] = true, ['My team'] = true, ['My group'] = true, ['Mom'] = true, ['Dad'] = true, ['Sister'] = true, ['Brother'] = true, ['Cousin'] = true,
+ ['Grandparent'] = true, ['Friend'] = true},
+ ['Time ...'] = {'/sc 6_2', true, ['In the morning'] = true, ['In the afternoon'] = true, ['At night'] = true, ['Tomorrow'] = true, ['This week'] = true, ['This month'] = true,
+ ['Sometime'] = true, ['Sometimes'] = true, ['Whenever you want'] = true, ['Never'] = true, ['After this'] = true, ['In 10 minutes'] = true, ['In a couple hours'] = true,
+ ['In a couple days'] = true},
+ ['Animals'] = {'/sc 6_3', true,
+ ['Cats'] = {['Lion'] = true, ['Tiger'] = true, ['Leopard'] = true, ['Cheetah'] = true},
+ ['Dogs'] = {['Wolves'] = true, ['Beagle'] = true, ['Collie'] = true, ['Dalmatian'] = true, ['Poodle'] = true, ['Spaniel'] = true,
+ ['Shepherd'] = true, ['Terrier'] = true, ['Retriever'] = true},
+ ['Horses'] = {['Ponies'] = true, ['Stallions'] = true, ['Pwnyz'] = true},
+ ['Reptiles'] = {['Dinosaurs'] = true, ['Lizards'] = true, ['Snakes'] = true, ['Turtles!'] = true},
+ ['Hamster'] = true,
+ ['Monkey'] = true,
+ ['Bears'] = true,
+ ['Fish'] = {['Goldfish'] = true, ['Sharks'] = true, ['Sea Bass'] = true, ['Halibut'] = true, ['Tropical Fish'] = true},
+ ['Birds'] = {['Eagles'] = true, ['Penguins'] = true, ['Parakeets'] = true, ['Owls'] = true, ['Hawks'] = true, ['Pidgeons'] = true},
+ ['Elephants'] = true,
+ ['Mythical Beasts'] = {['Dragons'] = true, ['Unicorns'] = true, ['Sea Serpents'] = true, ['Sphinx'] = true, ['Cyclops'] = true,
+ ['Minotaurs'] = true, ['Goblins'] = true, ['Honest Politicians'] = true, ['Ghosts'] = true, ['Scylla and Charybdis'] = true}
+ },
+ ['Games'] = {'/sc 6_4', true,
+ ['Action'] = true, ['Puzzle'] = true, ['Strategy'] = true, ['Racing'] = true, ['RPG'] = true, ['Obstacle Course'] = true, ['Tycoon'] = true,
+ ['Roblox'] = { ['BrickBattle'] = true, ['Community Building'] = true, ['Roblox Minigames'] = true, ['Contest Place'] = true},
+ ['Board games'] = { ['Chess'] = true, ['Checkers'] = true, ['Settlers of Catan'] = true, ['Tigris and Euphrates'] = true, ['El Grande'] = true,
+ ['Stratego'] = true, ['Carcassonne'] = true}
+ },
+ ['Sports'] = {'/sc 6_5', true, ['Hockey'] = true, ['Soccer'] = true, ['Football'] = true, ['Baseball'] = true, ['Basketball'] = true,
+ ['Volleyball'] = true, ['Tennis'] = true, ['Sports team practice'] = true,
+ ['Watersports'] = { ['Surfing'] = true,['Swimming'] = true, ['Water Polo'] = true},
+ ['Winter sports'] = { ['Skiing'] = true, ['Snowboarding'] = true, ['Sledding'] = true, ['Skating'] = true},
+ ['Adventure'] = {['Rock climbing'] = true, ['Hiking'] = true, ['Fishing'] = true, ['Horseback riding'] = true},
+ ['Wacky'] = {['Foosball'] = true, ['Calvinball'] = true, ['Croquet'] = true, ['Cricket'] = true, ['Dodgeball'] = true,
+ ['Squash'] = true, ['Trampoline'] = true}
+ },
+ ['Movies/TV'] = {'/sc 6_6', true, ['Science Fiction'] = true, ['Animated'] = {['Anime'] = true}, ['Comedy'] = true, ['Romantic'] = true,
+ ['Action'] = true, ['Fantasy'] = true},
+ ['Music'] = {'/sc 6_7', true, ['Country'] = true, ['Jazz'] = true, ['Rap'] = true, ['Hip-hop'] = true, ['Techno'] = true, ['Classical'] = true,
+ ['Pop'] = true, ['Rock'] = true},
+ ['Hobbies'] = {'/sc 6_8', true,
+ ['Computers'] = { ['Building computers'] = true, ['Videogames'] = true, ['Coding'] = true, ['Hacking'] = true},
+ ['The Internet'] = { ['lol. teh internets!'] = true, ['Watching vids'] = true},
+ ['Dance'] = true, ['Gymnastics'] = true, ['Listening to music'] = true, ['Arts and crafts'] = true,
+ ['Martial Arts'] = {['Karate'] = true, ['Judo'] = true, ['Taikwon Do'] = true, ['Wushu'] = true, ['Street fighting'] = true},
+ ['Music lessons'] = {['Playing in my band'] = true, ['Playing piano'] = true, ['Playing guitar'] = true,
+ ['Playing violin'] = true, ['Playing drums'] = true, ['Playing a weird instrument'] = true}
+ },
+ ['Location'] = {'/sc 6_9', true,
+ ['USA'] = {
+ ['West'] = { ['Alaska'] = true, ['Arizona'] = true, ['California'] = true, ['Colorado'] = true, ['Hawaii'] = true,
+ ['Idaho'] = true, ['Montana'] = true, ['Nevada'] = true, ['New Mexico'] = true, ['Oregon'] = true,
+ ['Utah'] = true, ['Washington'] = true, ['Wyoming'] = true
+ },
+ ['South'] = { ['Alabama'] = true, ['Arkansas'] = true, ['Florida'] = true, ['Georgia'] = true, ['Kentucky'] = true,
+ ['Louisiana'] = true, ['Mississippi'] = true, ['North Carolina'] = true, ['Oklahoma'] = true,
+ ['South Carolina'] = true, ['Tennessee'] = true, ['Texas'] = true, ['Virginia'] = true, ['West Virginia'] = true
+ },
+ ['Northeast'] = {['Connecticut'] = true, ['Delaware'] = true, ['Maine'] = true, ['Maryland'] = true, ['Massachusetts'] = true,
+ ['New Hampshire'] = true, ['New Jersey'] = true, ['New York'] = true, ['Pennsylvania'] = true, ['Rhode Island'] = true,
+ ['Vermont'] = true
+ },
+ ['Midwest'] = {['Illinois'] = true, ['Indiana'] = true, ['Iowa'] = true, ['Kansas'] = true, ['Michigan'] = true, ['Minnesota'] = true,
+ ['Missouri'] = true, ['Nebraska'] = true, ['North Dakota'] = true, ['Ohio'] = true, ['South Dakota'] = true, ['Wisconsin'] = true}
+ },
+ ['Canada'] = {['Alberta'] = true, ['British Columbia'] = true, ['Manitoba'] = true, ['New Brunswick'] = true, ['Newfoundland'] = true,
+ ['Northwest Territories'] = true, ['Nova Scotia'] = true, ['Nunavut'] = true, ['Ontario'] = true, ['Prince Edward Island'] = true,
+ ['Quebec'] = true, ['Saskatchewan'] = true, ['Yukon'] = true},
+ ['Mexico'] = true,
+ ['Central America'] = true,
+ ['Europe'] = {['France'] = true, ['Germany'] = true, ['Spain'] = true, ['Italy'] = true, ['Poland'] = true, ['Switzerland'] = true,
+ ['Greece'] = true, ['Romania'] = true, ['Netherlands'] = true,
+ ['Great Britain'] = {['England'] = true, ['Scotland'] = true, ['Wales'] = true, ['Northern Ireland'] = true}
+ },
+ ['Asia'] = { ['China'] = true, ['India'] = true, ['Japan'] = true, ['Korea'] = true, ['Russia'] = true, ['Vietnam'] = true},
+ ['South America'] = { ['Argentina'] = true, ['Brazil'] = true},
+ ['Africa'] = { ['Eygpt'] = true, ['Swaziland'] = true},
+ ['Australia'] = true, ['Middle East'] = true, ['Antarctica'] = true, ['New Zealand'] = true
+ },
+ ['Age'] = {'/sc 6_10', true, ['Rugrat'] = true, ['Kid'] = true, ['Tween'] = true, ['Teen'] = true, ['Twenties'] = true,
+ ['Old'] = true, ['Ancient'] = true, ['Mesozoic'] = true, ['I don\'t want to say my age. Don\'t ask.'] = true},
+ ['Mood'] = {'/sc 6_11', true, ['Good'] = true, ['Great!'] = true, ['Not bad'] = true, ['Sad'] = true, ['Hyper'] = true,
+ ['Chill'] = true, ['Happy'] = true, ['Kind of mad'] = true},
+ ['Boy'] = {'/sc 6_12', true},
+ ['Girl'] = {'/sc 6_13', true},
+ ['I don\'t want to say boy or girl. Don\'t ask.'] = {'/sc 6_14', true},
+ [1] = '/sc 6'
+ },
+ ['Game'] = {
+ ['Let\'s build'] = {'/sc 7_0', true},
+ ['Let\'s battle'] = {'/sc 7_1', true},
+ ['Nice one!'] = {'/sc 7_2', true},
+ ['So far so good'] = {'/sc 7_3', true},
+ ['Lucky shot!'] = {'/sc 7_4', true},
+ ['Oh man!'] = {'/sc 7_5', true},
+ ['I challenge you to a fight!'] = {'/sc 7_6', true},
+ ['Help me with this'] = {'/sc 7_7', true},
+ ['Let\'s go to your game'] = {'/sc 7_8', true},
+ ['Can you show me how do to that?'] = {'/sc 7_9', true},
+ ['Backflip!'] = {'/sc 7_10', true},
+ ['Frontflip!'] = {'/sc 7_11', true},
+ ['Dance!'] = {'/sc 7_12', true},
+ ['I\'m on your side!'] = {'/sc 7_13', true},
+ ['Game Commands'] = {'/sc 7_14', true, ['regen'] = true, ['reset'] = true, ['go'] = true, ['fix'] = true, ['respawn'] = true},
+ [1] = '/sc 7'
+ };
+ ['Silly'] = {
+ ['Muahahahaha!'] = true,
+ ['all your base are belong to me!'] = true,
+ ['GET OFF MAH LAWN'] = true,
+ ['TEH EPIK DUCK IS COMING!!!'] = true,
+ ['ROFL'] = true,
+ ['1337'] = {true, ['i r teh pwnz0r!'] = true, ['w00t!'] = true, ['z0mg h4x!'] = true, ['ub3rR0xXorzage!'] = true}
+ },
+ ['Yes'] = {
+ ['Absolutely!'] = true,
+ ['Rock on!'] = true,
+ ['Totally!'] = true,
+ ['Juice!'] = true,
+ ['Yay!'] = true,
+ ['Yesh'] = true
+ },
+ ['No'] = {
+ ['Ummm. No.'] = true,
+ ['...'] = true,
+ ['Stop!'] = true,
+ ['Go away!'] = true,
+ ['Don\'t do that'] = true,
+ ['Stop breaking the rules'] = true,
+ ['I don\'t want to'] = true
+ },
+ ['Ok'] = {
+ ['Well... ok'] = true,
+ ['Sure'] = true
+ },
+ ['Uncertain'] = {
+ ['Maybe'] = true,
+ ['I don\'t know'] = true,
+ ['idk'] = true,
+ ['I can\'t decide'] = true,
+ ['Hmm...'] = true
+ },
+ [':-)'] = {
+ [':-('] = true,
+ [':D'] = true,
+ [':-O'] = true,
+ ['lol'] = true,
+ ['=D'] = true,
+ ['D='] = true,
+ ['XD'] = true,
+ [';D'] = true,
+ [';)'] = true,
+ ['O_O'] = true,
+ ['=)'] = true,
+ ['@_@'] = true,
+ ['>_<'] = true,
+ ['T_T'] = true,
+ ['^_^'] = true,
+ ['<(0_0<) <(0_0)> (>0_0)> KIRBY DANCE'] = true,
+ [')\';'] = true,
+ [':3'] = true
+ },
+ ['Ratings'] = {
+ ['Rate it!'] = true,
+ ['I give it a 1 out of 10'] = true,
+ ['I give it a 2 out of 10'] = true,
+ ['I give it a 3 out of 10'] = true,
+ ['I give it a 4 out of 10'] = true,
+ ['I give it a 5 out of 10'] = true,
+ ['I give it a 6 out of 10'] = true,
+ ['I give it a 7 out of 10'] = true,
+ ['I give it a 8 out of 10'] = true,
+ ['I give it a 9 out of 10'] = true,
+ ['I give it a 10 out of 10!'] = true,
+ }
+ },
+ CreateEnum('SafeChat'){'Level1', 'Level2', 'Level3'},
+ SafeChatTree = {},
+ TempSpaceLabel = nil
+ }
+---------------------------------------------------
+
+local function GetNameValue(pName)
+ local value = 0
+ for index = 1, #pName do
+ local cValue = string.byte(string.sub(pName, index, index))
+ local reverseIndex = #pName - index + 1
+ if #pName%2 == 1 then
+ reverseIndex = reverseIndex - 1
+ end
+ if reverseIndex%4 >= 2 then
+ cValue = -cValue
+ end
+ value = value + cValue
+ end
+ return value%8
+end
+
+function Chat:ComputeChatColor(pName)
+ return self.ChatColors[GetNameValue(pName) + 1].Color
+end
+
+-- This is context based scrolling
+function Chat:EnableScrolling(toggle)
+ -- Genius idea gone to fail, if we switch the camera type we can effectively lock the
+ -- camera and do no click scrolling
+ self.MouseOnFrame = false
+ if self.RenderFrame then
+ self.RenderFrame.MouseEnter:connect(function()
+ local character = Player.Character
+ local torso = WaitForChild(character, 'Torso')
+ local humanoid = WaitForChild(character, 'Humanoid')
+ local head = WaitForChild(character, 'Head')
+ if toggle then
+ self.MouseOnFrame = true
+ Camera.CameraType = 'Scriptable'
+ -- Get relative position of camera and keep to it
+ Spawn(function()
+ local currentRelativePos = Camera.CoordinateFrame.p - torso.Position
+ while Chat.MouseOnFrame do
+ Camera.CoordinateFrame = CFrame.new(torso.Position + currentRelativePos, head.Position)
+ wait(0.015)
+ end
+ end)
+ end
+ end)
+
+ self.RenderFrame.MouseLeave:connect(function()
+ Camera.CameraType = 'Custom'
+ self.MouseOnFrame = false
+ end)
+ end
+end
+
+-- TODO: Scrolling using Mouse wheel
+function Chat:OnScroll(speed)
+ if self.MouseOnFrame then
+ --
+ end
+end
+
+-- Check if we are running on a touch device
+function Chat:IsTouchDevice()
+ local touchEnabled = false
+ pcall(function() touchEnabled = inputService.TouchEnabled end)
+ return touchEnabled
+end
+
+-- Scrolling
+function Chat:ScrollQueue(value)
+ --[[for i = 1, #self.MessageQueue do
+ if self.MessageQueue[i] then
+ for _, label in pairs(self.MessageQueue[i]) do
+ local next = self.MessageQueue[i].Next
+ local previous = self.MessageQueue[i].Previous
+ if label and label:IsA('TextLabel') or label:IsA('TextButton') then
+ if value > 0 and previous and previous['Message'] then
+ label.Position = previous['Message'].Position
+ elseif value < 1 and next['Message'] then
+ label.Position = previous['Message'].Position
+ end
+ end
+ end
+ end
+ end ]]
+end
+
+-- Handles the rendering of the text objects in their appropriate places
+function Chat:UpdateQueue(field, diff)
+ -- Have to do some sort of correction here
+ for i = #self.MessageQueue, 1, -1 do
+ if self.MessageQueue[i] then
+ for _, label in pairs(self.MessageQueue[i]) do
+ if label and type(label) ~= 'table' and type(label) ~= 'number' then
+ if label:IsA('TextLabel') or label:IsA('TextButton') then
+ if diff then
+ label.Position = label.Position - UDim2.new(0, 0, diff, 0)
+ else
+ if field == self.MessageQueue[i] then
+ label.Position = UDim2.new(self.Configuration.XScale, 0, label.Position.Y.Scale - field['Message'].Size.Y.Scale , 0)
+ -- Just to show up popping effect for the latest message in chat
+ Spawn(function()
+ wait(0.05)
+ while label.TextTransparency >= 0 do
+ label.TextTransparency = label.TextTransparency - 0.2
+ wait(0.03)
+ end
+ if label == field['Message'] then
+ label.TextStrokeTransparency = 0.8
+ else
+ label.TextStrokeTransparency = 1.0
+ end
+ end)
+ else
+ label.Position = UDim2.new(self.Configuration.XScale, 0, label.Position.Y.Scale - field['Message'].Size.Y.Scale, 0)
+ end
+ if label.Position.Y.Scale < -0.01 then
+ -- NOTE: Remove this fix when Textbounds is fixed
+ label.Visible = false
+ label:Destroy()
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+function Chat:CreateScrollBar()
+ -- Code for scrolling is in here, partially, but scroll bar drawing isn't drawn
+ -- TODO: Implement
+end
+
+-- For scrolling, to see if we hit the bounds so that we can stop it from scrolling anymore
+function Chat:CheckIfInBounds(value)
+ if #Chat.MessageQueue < 3 then
+ return true
+ end
+
+ if value > 0 and Chat.MessageQueue[1] and Chat.MessageQueue[1]['Player'] and Chat.MessageQueue[1]['Player'].Position.Y.Scale == 0 then
+ return true
+ elseif value < 0 and Chat.MessageQueue[1] and Chat.MessageQueue[1]['Player'] and Chat.MessageQueue[1]['Player'].Position.Y.Scale < 0 then
+ return true
+ else
+ return false
+ end
+ return false
+end
+
+-- This is to precompute all playerName space strings
+-- This is used to offset the message by exactly this + 2 spacestrings
+function Chat:ComputeSpaceString(pLabel)
+ local nString = " "
+ if not self.TempSpaceLabel then
+ self.TempSpaceLabel = Gui.Create'TextButton'
+ {
+ Size = UDim2.new(0, pLabel.AbsoluteSize.X, 0, pLabel.AbsoluteSize.Y);
+ FontSize = self.Configuration.FontSize;
+ Parent = self.RenderFrame;
+ BackgroundTransparency = 1.0;
+ Text = nString;
+ Name = 'SpaceButton'
+ };
+ else
+ self.TempSpaceLabel.Text = nString
+ end
+
+ while self.TempSpaceLabel.TextBounds.X < pLabel.TextBounds.X do
+ nString = nString .. " "
+ self.TempSpaceLabel.Text = nString
+ end
+ nString = nString .. " "
+ self.CachedSpaceStrings_List[pLabel.Text] = nString
+ self.TempSpaceLabel.Text = ""
+ return nString
+end
+
+-- When the playerChatted event fires
+-- The message is what the player chatted
+function Chat:UpdateChat(cPlayer, message)
+ local messageField = {
+ ['Player'] = cPlayer,
+ ['Message'] = message
+ }
+ if coroutine.status(Chat.MessageThread) == 'dead' then
+ --Chat.Messages_List = {}
+ table.insert(Chat.Messages_List, messageField)
+ Chat.MessageThread = coroutine.create(function()
+ for i = 1, #Chat.Messages_List do
+ local field = Chat.Messages_List[i]
+ Chat:CreateMessage(field['Player'], field['Message'])
+ end
+ Chat.Messages_List = {}
+ end)
+ coroutine.resume(Chat.MessageThread)
+ else
+ table.insert(Chat.Messages_List, messageField)
+ end
+end
+
+function Chat:RecalculateSpacing()
+ --[[for i = 1, #self.MessageQueue do
+ local pLabel = self.MessageQueue[i]['Player']
+ local mLabel = self.MessageQueue[i]['Message']
+
+ local prevYScale = mLabel.Size.Y.Scale
+ local prevText = mLabel.Text
+ mLabel.Text = prevText
+
+ local heightField = mLabel.TextBounds.Y
+
+ mLabel.Size = UDim2.new(1, 0, heightField/self.RenderFrame.AbsoluteSize.Y, 0)
+ pLabel.Size = mLabel.Size
+
+ local diff = mLabel.Size.Y.Scale - prevYScale
+
+ Chat:UpdateQueue(self.MessageQueue[i], diff)
+ end ]]
+end
+
+function Chat:ApplyFilter(str)
+ --[[for _, word in pair(self.Filter_List) do
+ if string.find(str, word) then
+ str:gsub(word, '@#$^')
+ end
+ end ]]
+end
+
+-- NOTE: Temporarily disabled ring buffer to allow for chat to always wrap around
+function Chat:CreateMessage(cPlayer, message)
+ local pName
+ if not cPlayer then
+ pName = ''
+ else
+ pName = cPlayer.Name
+ end
+ message = StringTrim(message)
+ local pLabel
+ local mLabel
+ -- Our history stores upto 50 messages that is 100 textlabels
+ -- If we ever hit the mark, which would be in every popular game btw
+ -- we wrap around and reuse the labels
+ if #self.MessageQueue > self.Configuration.HistoryLength then
+ --[[pLabel = self.MessageQueue[#self.MessageQueue]['Player']
+ mLabel = self.MessageQueue[#self.MessageQueue]['Message']
+
+ pLabel.Text = pName .. ':'
+ pLabel.Name = pName
+
+ local pColor
+ if cPlayer.Neutral then
+ pLabel.TextColor3 = Chat:ComputeChatColor(pName)
+ else
+ pLabel.TextColor3 = cPlayer.TeamColor.Color
+ end
+
+ local nString
+
+ if not self.CachedSpaceStrings_List[pName] then
+ nString = Chat:ComputeSpaceString(pLabel)
+ else
+ nString = self.CachedSpaceStrings_List[pName]
+ end
+
+ mLabel.Text = ""
+ mLabel.Name = pName .. " - message"
+ mLabel.Text = nString .. message;
+
+ mLabel.Parent = nil
+ mLabel.Parent = self.RenderFrame
+
+ mLabel.Position = UDim2.new(0, 0, 1, 0);
+ pLabel.Position = UDim2.new(0, 0, 1, 0);]]
+
+ -- Reinserted at the beginning, ring buffer
+ self.MessageQueue[#self.MessageQueue] = nil
+ end
+ --else
+ -- Haven't hit the mark yet, so keep creating
+ pLabel = Gui.Create'TextLabel'
+ {
+ Name = pName;
+ Text = pName .. ":";
+ TextColor3 = pColor;
+ FontSize = Chat.Configuration.FontSize;
+ TextXAlignment = Enum.TextXAlignment.Left;
+ TextYAlignment = Enum.TextYAlignment.Top;
+ Parent = self.RenderFrame;
+ TextWrapped = false;
+ Size = UDim2.new(1, 0, 0.1, 0);
+ BackgroundTransparency = 1.0;
+ TextTransparency = 1.0;
+ Position = UDim2.new(0, 0, 1, 0);
+ BorderSizePixel = 0.0;
+ TextStrokeColor3 = Color3.new(0.5, 0.5, 0.5);
+ TextStrokeTransparency = 0.75;
+ --Active = false;
+ };
+ local pColor
+ if cPlayer.Neutral then
+ pLabel.TextColor3 = Chat:ComputeChatColor(pName)
+ else
+ pLabel.TextColor3 = cPlayer.TeamColor.Color
+ end
+
+ local nString
+
+ if not self.CachedSpaceStrings_List[pName] then
+ nString = Chat:ComputeSpaceString(pLabel)
+ else
+ nString = self.CachedSpaceStrings_List[pName]
+ end
+
+ mLabel = Gui.Create'TextLabel'
+ {
+ Name = pName .. ' - message';
+ -- Max is 3 lines
+ Size = UDim2.new(1, 0, 0.5, 0);
+ TextColor3 = Chat.Configuration.MessageColor;
+ FontSize = Chat.Configuration.FontSize;
+ TextXAlignment = Enum.TextXAlignment.Left;
+ TextYAlignment = Enum.TextYAlignment.Top;
+ Text = ""; -- this is to stop when the engine reverts the swear words to default, which is button, ugh
+ Parent = self.RenderFrame;
+ TextWrapped = true;
+ BackgroundTransparency = 1.0;
+ TextTransparency = 1.0;
+ Position = UDim2.new(0, 0, 1, 0);
+ BorderSizePixel = 0.0;
+ TextStrokeColor3 = Color3.new(0, 0, 0);
+ --TextStrokeTransparency = 0.8;
+ --Active = false;
+ };
+ mLabel.Text = nString .. message;
+
+ if not pName then
+ pLabel.Text = ''
+ mLabel.TextColor3 = Color3.new(0, 0.4, 1.0)
+ end
+ --end
+
+ for _, adminName in pairs(self.Admins_List) do
+ if string.lower(adminName) == string.lower(pName) then
+ mLabel.TextColor3 = self.Configuration.AdminMessageColor
+ end
+ end
+
+ pLabel.Visible = true
+ mLabel.Visible = true
+
+ -- This will give beautiful multilines as well
+ local heightField = mLabel.TextBounds.Y
+
+ mLabel.Size = UDim2.new(1, 0, heightField/self.RenderFrame.AbsoluteSize.Y, 0)
+ pLabel.Size = mLabel.Size
+
+ local yPixels = self.RenderFrame.AbsoluteSize.Y
+ local yFieldSize = mLabel.TextBounds.Y
+
+ local queueField = {}
+ queueField['Player'] = pLabel
+ queueField['Message'] = mLabel
+ queueField['SpawnTime'] = tick() -- Used for identifying when to make the message invisible
+
+ table.insert(self.MessageQueue, 1, queueField)
+ Chat:UpdateQueue(queueField)
+end
+
+function Chat:ScreenSizeChanged()
+ wait()
+ while self.Frame.AbsoluteSize.Y > 120 do
+ self.Frame.Size = self.Frame.Size - UDim2.new(0, 0, 0.005, 0)
+ end
+ Chat:RecalculateSpacing()
+end
+
+
+function Chat:FindButtonTree(scButton, rootList)
+ local list = {}
+ rootList = rootList or self.SafeChatTree
+ for button, _ in pairs(rootList) do
+ if button == scButton then
+ list = rootList[button]
+ elseif type(rootList[button]) == 'table' then
+ list = Chat:FindButtonTree(scButton, rootList[button])
+ end
+ end
+ return list
+end
+
+function Chat:ToggleSafeChatMenu(scButton)
+ local list = Chat:FindButtonTree(scButton, self.SafeChatTree)
+ if list then
+ for button, _ in pairs(list) do
+ if button:IsA('TextButton') or button:IsA('ImageButton') then
+ button.Visible = not button.Visible
+ end
+ end
+ return true
+ end
+ return false
+end
+
+function Chat:CreateSafeChatOptions(list, rootButton)
+ local text_List = {}
+ level = level or 0
+ local count = 0
+ text_List[rootButton] = {}
+ text_List[rootButton][1] = list[1]
+ rootButton = rootButton or self.SafeChatButton
+ for msg, _ in pairs(list) do
+ if type(msg) == 'string' then
+ local chatText = Gui.Create'TextButton'
+ {
+ Name = msg;
+ Text = msg;
+ Size = UDim2.new(0, 100, 0, 20);
+ TextXAlignment = Enum.TextXAlignment.Center;
+ TextColor3 = Color3.new(0.2, 0.1, 0.1);
+ BackgroundTransparency = 0.5;
+ BackgroundColor3 = Color3.new(1, 1, 1);
+ Parent = self.SafeChatFrame;
+ Visible = false;
+ Position = UDim2.new(0, rootButton.Position.X.Scale + 105, 0, rootButton.Position.Y.Scale - ((count - 3) * 100));
+ };
+
+ count = count + 1
+
+ if type(list[msg]) == 'table' then
+ text_List[rootButton][chatText] = Chat:CreateSafeChatOptions(list[msg], chatText)
+ else
+ --table.insert(text_List[chatText], true)
+ end
+ chatText.MouseEnter:connect(function()
+ Chat:ToggleSafeChatMenu(chatText)
+ end)
+
+ chatText.MouseLeave:connect(function()
+ Chat:ToggleSafeChatMenu(chatText)
+ end)
+
+ chatText.MouseButton1Click:connect(function()
+ local lList = Chat:FindButtonTree(chatText)
+ if lList then
+ for i, v in pairs(lList) do
+ end
+ else
+ end
+ pcall(function() PlayersService:Chat(lList[1]) end)
+ end)
+ end
+ end
+ return text_List
+end
+
+function Chat:CreateSafeChatGui()
+ self.SafeChatFrame = Gui.Create'Frame'
+ {
+ Name = 'SafeChatFrame';
+ Size = UDim2.new(1, 0, 1, 0);
+ Parent = self.Gui;
+ BackgroundTransparency = 1.0;
+
+ Gui.Create'ImageButton'
+ {
+ Name = 'SafeChatButton';
+ Size = UDim2.new(0, 44, 0, 31);
+ Position = UDim2.new(0, 1, 0.35, 0);
+ BackgroundTransparency = 1.0;
+ Image = 'http://www.syntax.eco/asset/?id=97080365';
+ };
+ }
+
+ self.SafeChatButton = self.SafeChatFrame.SafeChatButton
+ -- safe chat button is the root of this tree
+ self.SafeChatTree[self.SafeChatButton] = Chat:CreateSafeChatOptions(self.SafeChat_List, self.SafeChatButton)
+
+ self.SafeChatButton.MouseButton1Click:connect(function()
+ Chat:ToggleSafeChatMenu(self.SafeChatButton)
+ end)
+end
+
+
+function Chat:FocusOnChatBar()
+ if self.ClickToChatButton then
+ self.ClickToChatButton.Visible = false
+ end
+
+ self.GotFocus = true
+ if self.Frame['Background'] then
+ self.Frame.Background.Visible = false
+ end
+ self.ChatBar:CaptureFocus()
+end
+
+-- For touch devices we create a button instead
+function Chat:CreateTouchButton()
+ self.ChatTouchFrame = Gui.Create'Frame'
+ {
+ Name = 'ChatTouchFrame';
+ Size = UDim2.new(0, 128, 0, 32);
+ Position = UDim2.new(0, 88, 0, 0);
+ BackgroundTransparency = 1.0;
+ Parent = self.Gui;
+
+ Gui.Create'ImageButton'
+ {
+ Name = 'ChatLabel';
+ Size = UDim2.new(0, 74, 0, 28);
+ Position = UDim2.new(0, 0, 0, 0);
+ BackgroundTransparency = 1.0;
+ ZIndex = 2.0;
+ };
+ Gui.Create'ImageLabel'
+ {
+ Name = 'Background';
+ Size = UDim2.new(1, 0, 1, 0);
+ Position = UDim2.new(0, 0, 0, 0);
+ BackgroundTransparency = 1.0;
+ Image = 'http://www.syntax.eco/asset/?id=97078724'
+ };
+
+ }
+ self.TapToChatLabel = self.ChatTouchFrame.ChatLabel
+ self.TouchLabelBackground = self.ChatTouchFrame.Background
+
+ self.ChatBar = Gui.Create'TextBox'
+ {
+ Name = 'ChatBar';
+ Size = UDim2.new(1, 0, 0.2, 0);
+ Position = UDim2.new(0, 0, 0.8, 800);
+ Text = "";
+ ZIndex = 1.0;
+ BackgroundTransparency = 1.0;
+ Parent = self.Frame;
+ TextXAlignment = Enum.TextXAlignment.Left;
+ TextColor3 = Color3.new(1, 1, 1);
+ ClearTextOnFocus = false;
+ };
+
+ self.TapToChatLabel.MouseButton1Click:connect(function()
+ self.TapToChatLabel.Visible = false
+ --self.ChatBar.Visible = true
+ --self.Frame.Background.Visible = true
+ self.ChatBar:CaptureFocus()
+ self.GotFocus = true
+ if self.TouchLabelBackground then
+ self.TouchLabelBackground.Visible = false
+ end
+ end)
+end
+
+-- Non touch devices, create the bottom chat bar
+function Chat:CreateChatBar()
+ -- okay now we do
+ local status, result = pcall(function() return GuiService.UseLuaChat end)
+ if forceChatGUI or (status and result) then
+ self.ClickToChatButton = Gui.Create'TextButton'
+ {
+ Name = 'ClickToChat';
+ Size = UDim2.new(1, 0, 0, 20);
+ BackgroundTransparency = 1.0;
+ ZIndex = 2.0;
+ Parent = self.Gui;
+ Text = "To chat click here or press \"/\" key";
+ TextColor3 = Color3.new(1, 1, 0.9);
+ Position = UDim2.new(0, 0, 1, 0);
+ TextXAlignment = Enum.TextXAlignment.Left;
+ FontSize = Enum.FontSize.Size12;
+ }
+
+ self.ChatBar = Gui.Create'TextBox'
+ {
+ Name = 'ChatBar';
+ Size = UDim2.new(1, 0, 0, 20);
+ Position = UDim2.new(0, 0, 1, 0);
+ Text = "";
+ ZIndex = 1.0;
+ BackgroundColor3 = Color3.new(0, 0, 0);
+ BackgroundTransparency = 0.25;
+ Parent = self.Gui;
+ TextXAlignment = Enum.TextXAlignment.Left;
+ TextColor3 = Color3.new(1, 1, 1);
+ FontSize = Enum.FontSize.Size12;
+ ClearTextOnFocus = false;
+ Text = '';
+ };
+
+ -- Engine has code to offset the entire world, so if we do it by -20 pixels nothing gets in our chat's way
+ --GuiService:SetGlobalSizeOffsetPixel(0, -20)
+ local success, error = pcall(function() GuiService:SetGlobalGuiInset(0, 0, 0, 20) end)
+ if not success then
+ pcall(function() GuiService:SetGlobalSizeOffsetPixel(0, -20) end) -- Doesn't hurt to throw a non-existent function into a pcall
+ end
+ -- ChatHotKey is '/'
+ GuiService:AddSpecialKey(Enum.SpecialKey.ChatHotkey)
+ GuiService.SpecialKeyPressed:connect(function(key)
+ if key == Enum.SpecialKey.ChatHotkey then
+ Chat:FocusOnChatBar()
+ end
+ end)
+
+ self.ClickToChatButton.MouseButton1Click:connect(function()
+ Chat:FocusOnChatBar()
+ end)
+ end
+end
+
+-- Create the initial Chat stuff
+-- Done only once
+function Chat:CreateGui()
+ self.Gui = WaitForChild(CoreGuiService, 'RobloxGui')
+ self.Frame = Gui.Create'Frame'
+ {
+ Name = 'ChatFrame';
+ --Size = self.Configuration.Size;
+ Size = UDim2.new(0, 500, 0, 120);
+ Position = UDim2.new(0, 0, 0, 5);
+ BackgroundTransparency = 1.0;
+ --ClipsDescendants = true;
+ ZIndex = 0.0;
+ Parent = self.Gui;
+ Active = false;
+
+ Gui.Create'ImageLabel'
+ {
+ Name = 'Background';
+ Image = 'http://www.syntax.eco/asset/?id=97120937'; --96551212';
+ Size = UDim2.new(1.3, 0, 1.64, 0);
+ Position = UDim2.new(0, 0, 0, 0);
+ BackgroundTransparency = 1.0;
+ ZIndex = 0.0;
+ Visible = false
+ };
+
+ Gui.Create'Frame'
+ {
+ Name = 'Border';
+ Size = UDim2.new(1, 0, 0, 1);
+ Position = UDim2.new(0, 0, 0.8, 0);
+ BackgroundTransparency = 0.0;
+ BackgroundColor3 = Color3.new(236/255, 236/255, 236/255);
+ BorderSizePixel = 0.0;
+ Visible = false;
+ };
+
+ Gui.Create'Frame'
+ {
+ Name = 'ChatRenderFrame';
+ Size = UDim2.new(1.02, 0, 1.01, 0);
+ Position = UDim2.new(0, 0, 0, 0);
+ BackgroundTransparency = 1.0;
+ --ClipsDescendants = true;
+ ZIndex = 0.0;
+ Active = false;
+
+ };
+ };
+
+ Spawn(function()
+ wait(0.5)
+ if IsPhone() then
+ self.Frame.Size = UDim2.new(0, 280, 0, 120)
+ end
+ end)
+
+ self.RenderFrame = self.Frame.ChatRenderFrame
+ if Chat:IsTouchDevice() then
+ self.Frame.Position = self.Configuration.Position;
+ self.RenderFrame.Size = UDim2.new(1, 0, 1, 0)
+ elseif self.Frame.AbsoluteSize.Y > 120 then
+ Chat:ScreenSizeChanged()
+ self.Gui.Changed:connect(function(property)
+ if property == 'AbsoluteSize' then
+ Chat:ScreenSizeChanged()
+ end
+ end)
+ end
+
+ if forceChatGUI or Player.ChatMode == Enum.ChatMode.TextAndMenu then
+ if Chat:IsTouchDevice() then
+ Chat:CreateTouchButton()
+ else
+ Chat:CreateChatBar()
+ --Chat:CreateSafeChatGui()
+ end
+
+ if self.ChatBar then
+ self.ChatBar.FocusLost:connect(function(enterPressed)
+ Chat.GotFocus = false
+ if Chat:IsTouchDevice() then
+ self.ChatBar.Visible = false
+ self.TapToChatLabel.Visible = true
+
+ if self.TouchLabelBackground then
+ self.TouchLabelBackground.Visible = true
+ end
+ end
+ if enterPressed and self.ChatBar.Text ~= "" then
+
+ local cText = self.ChatBar.Text
+ if string.sub(self.ChatBar.Text, 1, 1) == '%' then
+ cText = '(TEAM) ' .. string.sub(cText, 2, #cText)
+ pcall(function() PlayersService:TeamChat(cText) end)
+ else
+ pcall(function() PlayersService:Chat(cText) end)
+ end
+
+ if self.ClickToChatButton then
+ self.ClickToChatButton.Visible = true
+ end
+ self.ChatBar.Text = ""
+ end
+ Spawn(function()
+ wait(5.0)
+ if not Chat.GotFocus then
+ Chat.Frame.Background.Visible = false
+ end
+ end)
+ end)
+
+ -- Make the escape key clear the chat box (like it used to)
+ inputService.InputBegan:connect(function(input)
+ if (input.KeyCode == Enum.KeyCode.Escape) then
+ if self.ClickToChatButton then
+ self.ClickToChatButton.Visible = true
+ end
+
+ self.ChatBar.Text = ""
+ end
+ end)
+ end
+ end
+end
+
+-- Scrolling function
+-- Applies a speed(velocity) to have nice scrolling effect
+function Input:OnMouseScroll()
+ Spawn(function()
+ -- How long should the speed last?
+ while Input.Speed ~=0 do
+ if Input.Speed > 1 then
+ while Input.Speed > 0 do
+ Input.Speed = Input.Speed - 1
+ wait(0.25)
+ end
+ elseif Input.Speed < 0 then
+ while Input.Speed < 0 do
+ Input.Speed = Input.Speed + 1
+ wait(0.25)
+ end
+ end
+ wait(0.03)
+ end
+ end)
+ if Chat:CheckIfInBounds(Input.Speed) then
+ return
+ end
+ Chat:ScrollQueue()
+end
+
+function Input:ApplySpeed(value)
+ Input.Speed = Input.Speed + value
+ if not self.Simulating then
+ Input:OnMouseScroll()
+ end
+end
+
+function Input:Initialize()
+ self.Mouse.WheelBackward:connect(function()
+ Input:ApplySpeed(self.Configuration.DefaultSpeed)
+ end)
+
+ self.Mouse.WheelForward:connect(function()
+ Input:ApplySpeed(self.Configuration.DefaultSpeed)
+ end)
+end
+
+function Chat:FindMessageInSafeChat(message, list)
+ local foundMessage = false
+ for msg, _ in pairs(list) do
+ if msg == message then
+ return true
+ end
+ if type(list[msg]) == 'table' then
+ foundMessage = Chat:FindMessageInSafeChat(message, list[msg])
+ if foundMessage then
+ return true
+ end
+ end
+ end
+ return foundMessage
+end
+
+-- Just a wrapper around our PlayerChatted event
+function Chat:PlayerChatted(...)
+ local args = {...}
+ local argCount = select('#', ...)
+ local player
+ local message
+ -- This doesn't look very good, but what else to do?
+ if args[2] then
+ player = args[2]
+ end
+ if args[3] then
+ message = args[3]
+ if string.sub(message, 1, 1) == '%' then
+ message = '(TEAM) ' .. string.sub(message, 2, #message)
+ end
+ end
+
+ if PlayersService.ClassicChat then
+ if string.sub(message, 1, 3) == '/e ' or string.sub(message, 1, 7) == '/emote ' then
+ -- don't do anything right now
+ elseif forceChatGUI or Player.ChatMode == Enum.ChatMode.TextAndMenu then
+ Chat:UpdateChat(player, message)
+ elseif Player.ChatMode == Enum.ChatMode.Menu and string.sub(message, 1, 3) == '/sc' then
+ Chat:UpdateChat(player, message)
+ else
+ if Chat:FindMessageInSafeChat(message, self.SafeChat_List) then
+ Chat:UpdateChat(player, message)
+ end
+ end
+ end
+end
+
+-- After Chat.Configuration.Lifetime seconds of existence, the labels become invisible
+-- Runs only every 5 seconds and has to loop through 50 values
+-- Shouldn't be too expensive
+function Chat:CullThread()
+ while true do
+ if #self.MessageQueue > 0 then
+ for _, field in pairs(self.MessageQueue) do
+ if field['SpawnTime'] and field['Player'] and field['Message'] and tick() - field['SpawnTime'] > self.Configuration.LifeTime then
+ field['Player'].Visible = false
+ field['Message'].Visible = false
+ end
+ end
+ end
+ wait(5.0)
+ end
+end
+
+-- RobloxLock everything so users can't delete them(?)
+function Chat:LockAllFields(gui)
+ local children = gui:GetChildren()
+ for i = 1, #children do
+ children[i].RobloxLocked = true
+ if #children[i]:GetChildren() > 0 then
+ Chat:LockAllFields(children[i])
+ end
+ end
+end
+
+function Chat:CoreGuiChanged(coreGuiType,enabled)
+ if coreGuiType == Enum.CoreGuiType.Chat or coreGuiType == Enum.CoreGuiType.All then
+ if self.Frame then self.Frame.Visible = enabled end
+ if self.TapToChatLabel then self.TapToChatLabel.Visible = enabled end
+
+ if not Chat:IsTouchDevice() and self.ChatBar then
+ self.ChatBar.Visible = enabled
+ if enabled then
+ GuiService:SetGlobalGuiInset(0, 0, 0, 20)
+ else
+ GuiService:SetGlobalGuiInset(0, 0, 0, 0)
+ end
+ end
+ end
+end
+
+-- Constructor
+-- This function initializes everything
+function Chat:Initialize()
+
+ Chat:CreateGui()
+
+ pcall(function()
+ Chat:CoreGuiChanged(Enum.CoreGuiType.Chat, Game.StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.Chat))
+ Game.StarterGui.CoreGuiChangedSignal:connect(function(coreGuiType,enabled) Chat:CoreGuiChanged(coreGuiType,enabled) end)
+ end)
+
+ self.EventListener = PlayersService.PlayerChatted:connect(function(...)
+ -- This event has 4 callback arguments
+ -- Enum.PlayerChatType.All, chatPlayer, message, targetPlayer
+ Chat:PlayerChatted(...)
+
+ end)
+
+ self.MessageThread = coroutine.create(function() end)
+ coroutine.resume(self.MessageThread)
+
+ -- Initialize input for us
+ Input:Initialize()
+ -- Eww, everytime a player is added, you have to redo the connection
+ -- Seems this is not automatic
+ -- NOTE: PlayerAdded only fires on the server, hence ChildAdded is used here
+ PlayersService.ChildAdded:connect(function()
+ Chat.EventListener:disconnect()
+ self.EventListener = PlayersService.PlayerChatted:connect(function(...)
+ -- This event has 4 callback arguments
+ -- Enum.PlayerChatType.All, chatPlayer, message, targetPlayer
+ Chat:PlayerChatted(...)
+ end)
+ end)
+
+ Spawn(function()
+ Chat:CullThread()
+ end)
+
+ self.Frame.RobloxLocked = true
+ Chat:LockAllFields(self.Frame)
+ self.Frame.DescendantAdded:connect(function(descendant)
+ Chat:LockAllFields(descendant)
+ end)
+end
+
+Chat:Initialize()
diff --git a/app/files/CoreGui/97188757 b/app/files/CoreGui/97188757
new file mode 100644
index 0000000..7b3dfa5
--- /dev/null
+++ b/app/files/CoreGui/97188757
@@ -0,0 +1,1211 @@
+--[[
+ //FileName: ChatScript.LUA
+ //Written by: Sorcus
+ //Description: Code for lua side chat on ROBLOX. Supports Scrolling.
+ //NOTE: If you find any bugs or inaccuracies PM Sorcus on ROBLOX or @Canavus on Twitter
+]]
+
+-- Utility functions + Globals
+local function WaitForChild(parent, childName)
+ while parent:FindFirstChild(childName) == nil do
+ wait()
+ end
+ return parent[childName]
+end
+
+local function typedef(obj)
+ return obj
+end
+
+while Game.Players.LocalPlayer == nil do wait() end
+
+local Player = Game.Players.LocalPlayer
+while Player.Character == nil do wait() end
+local RbxUtility = LoadLibrary('RbxUtility')
+local Gui = typedef(RbxUtility)
+local Camera = Game.Workspace.CurrentCamera
+
+-- Services
+local CoreGuiService = Game:GetService('CoreGui')
+local PlayersService = Game:GetService('Players')
+local DebrisService= Game:GetService('Debris')
+local GuiService = Game:GetService('GuiService')
+
+-- Lua Enums
+local Enums do
+ Enums = {}
+ local EnumName = {} -- used as unique key for enum name
+ local enum_mt = {
+ __call = function(self,value)
+ return self[value] or self[tonumber(value)]
+ end;
+ __index = {
+ GetEnumItems = function(self)
+ local t = {}
+ for i,item in pairs(self) do
+ if type(i) == 'number' then
+ t[#t+1] = item
+ end
+ end
+ table.sort(t,function(a,b) return a.Value < b.Value end)
+ return t
+ end;
+ };
+ __tostring = function(self)
+ return "Enum." .. self[EnumName]
+ end;
+ }
+ local item_mt = {
+ __call = function(self,value)
+ return value == self or value == self.Name or value == self.Value
+ end;
+ __tostring = function(self)
+ return "Enum." .. self[EnumName] .. "." .. self.Name
+ end;
+ }
+ function CreateEnum(enumName)
+ return function(t)
+ local e = {[EnumName] = enumName}
+ for i,name in pairs(t) do
+ local item = setmetatable({Name=name,Value=i,Enum=e,[EnumName]=enumName},item_mt)
+ e[i] = item
+ e[name] = item
+ e[item] = item
+ end
+ Enums[enumName] = e
+ return setmetatable(e, enum_mt)
+ end
+ end
+end
+---------------------------------------------------
+------------------ Input class --------------------
+local Input = {
+ Mouse = Player:GetMouse(),
+ Speed = 0,
+ Simulating = false,
+
+ Configuration = {
+ DefaultSpeed = 1
+ },
+ UserIsScrolling = false
+ }
+
+---------------------------------------------------
+------------------ Chat class --------------------
+local Chat = {
+
+ ChatColors = {
+ BrickColor.new("Bright red"),
+ BrickColor.new("Bright blue"),
+ BrickColor.new("Earth green"),
+ BrickColor.new("Bright violet"),
+ BrickColor.new("Bright orange"),
+ BrickColor.new("Bright yellow"),
+ BrickColor.new("Light reddish violet"),
+ BrickColor.new("Brick yellow"),
+ },
+
+ Gui = nil,
+ Frame = nil,
+ RenderFrame = nil,
+ TapToChatLabel = nil,
+ ClickToChatButton = nil,
+
+ ScrollingLock = false,
+ EventListener = nil,
+
+ -- This is actually a ring buffer
+ -- Meaning at hitting the historyLength it wraps around
+ -- Reuses the text objects, so chat atmost uses 100 text objects
+ MessageQueue = {},
+
+ -- Stores all the values for configuring chat
+ Configuration = {
+ FontSize = Enum.FontSize.Size10, -- 10 is good
+ -- Also change this when you are changing the above, this is suboptimal but so is our interface to find FontSize
+ NumFontSize = 10,
+ HistoryLength = 50, -- stores up to 50 of the last chat messages for you to scroll through,
+ Size = UDim2.new(0.38, 0, 0.20, 0),
+ MessageColor = Color3.new(1, 1, 1),
+ XScale = 0.025,
+ LifeTime = 45,
+ Position = UDim2.new(0, 2, 0.05, 0),
+ DefaultTweenSpeed = 0.15,
+ },
+
+ -- This could be redone by just using the previous and next fields of the Queue
+ -- But the iterators cause issues, will be optimized later
+ SlotPositions_List = {},
+ -- To precompute and store all player null strings since its an expensive process
+ CachedSpaceStrings_List = {},
+ MouseOnFrame = false,
+ GotFocus = false,
+
+ SafeChat_List = {
+ ['Use the Chat menu to talk to me.'] = {'/sc 0', true},
+ ['I can only see menu chats.'] = {'/sc 1', true},
+ ['Hello'] = {
+ ['Hi'] = {'/sc 2_0', true, ['Hi there!'] = true, ['Hi everyone'] = true},
+ ['Howdy'] = {'/sc 2_1', true, ['Howdy partner!'] = true},
+ ['Greetings'] = {'/sc 2_2', true, ['Greetings everyone'] = true, ['Greetings Robloxians!'] = true, ['Seasons greetings!'] = true},
+ ['Welcome'] = {'/sc 2_3', true, ['Welcome to my place'] = true, ['Welcome to my barbeque'] = true, ['Welcome to our base'] = true},
+ ['Hey there!'] = {'/sc 2_4', true},
+ ['What\'s up?'] = {'/sc 2_5', true, ['How are you doing?'] = true, ['How\'s it going?'] = true, ['What\'s new?'] = true},
+ ['Good day'] = {'/sc 2_6', true, ['Good morning'] = true, ['Good evening'] = true, ['Good afternoon'] = true, ['Good night'] = true},
+ ['Silly'] = {'/sc 2_7', true, ['Waaaaaaaz up?!'] = true, ['Hullo!'] = true, ['Behold greatness, mortals!'] = true, ['Pardon me, is this Sparta?'] = true, ['THIS IS SPARTAAAA!'] = true},
+ ['Happy Holidays!'] = {'/sc 2_8', true, ['Happy New Year!'] = true,
+ ['Happy Valentine\'s Day!'] = true,
+ ['Beware the Ides of March!'] = true,
+ ['Happy St. Patrick\'s Day!'] = true,
+ ['Happy Easter!'] = true,
+ ['Happy Earth Day!'] = true,
+ ['Happy 4th of July!'] = true,
+ ['Happy Thanksgiving!'] = true,
+ ['Happy Halloween!'] = true,
+ ['Happy Hanukkah!'] = true,
+ ['Merry Christmas!'] = true,
+ ['Happy Halloween!'] = true,
+ ['Happy Earth Day!'] = true,
+ ['Happy May Day!'] = true,
+ ['Happy Towel Day!'] = true,
+ ['Happy ROBLOX Day!'] = true,
+ ['Happy LOL Day!'] = true },
+
+ [1] = '/sc 2'
+ },
+ ['Goodbye'] = {
+ ['Good Night']= {'/sc 3_0', true,
+ ['Sweet dreams'] = true,
+ ['Go to sleep!'] = true,
+ ['Lights out!'] = true,
+ ['Bedtime'] = true,
+ ['Going to bed now'] = true},
+
+ ['Later']= {'/sc 3_1', true,
+ ['See ya later'] = true,
+ ['Later gator!'] = true,
+ ['See you tomorrow'] = true},
+
+ ['Bye'] = {'/sc 3_2', true, ['Hasta la bye bye!'] = true},
+ ['I\'ll be right back'] = {'/sc 3_3', true},
+ ['I have to go'] = {'/sc 3_4', true},
+ ['Farewell'] = {'/sc 3_5', true, ['Take care'] = true, ['Have a nice day'] = true, ['Goodluck!'] = true, ['Ta-ta for now!'] = true},
+ ['Peace'] = {'/sc 3_6', true, ['Peace out!'] = true, ['Peace dudes!'] = true, ['Rest in pieces!'] = true},
+ ['Silly'] = {'/sc 3_7', true,
+ ['To the batcave!'] = true,
+ ['Over and out!'] = true,
+ ['Happy trails!'] = true,
+ ['I\'ve got to book it!'] = true,
+ ['Tootles!'] = true,
+ ['Smell you later!'] = true,
+ ['GG!'] = true,
+ ['My house is on fire! gtg.'] = true},
+ [1] = '/sc 3'
+ },
+ ['Friend'] ={
+ ['Wanna be friends?'] = {'/sc 4_0', true},
+ ['Follow me'] = {'/sc 4_1', true, ['Come to my place!'] = true, ['Come to my base!'] = true, ['Follow me, team!'] = true, ['Follow me'] = true},
+ ['Your place is cool'] = {'/sc 4_2', true, ['Your place is fun'] = true, ['Your place is awesome'] = true, ['Your place looks good'] = true, ['This place is awesome!'] = true},
+ ['Thank you'] = {'/sc 4_3', true, ['Thanks for playing'] = true, ['Thanks for visiting'] = true, ['Thanks for everything'] = true, ['No, thank you'] = true, ['Thanx'] = true},
+ ['No problem'] = {'/sc 4_4', true, ['Don\'t worry'] = true, ['That\'s ok'] = true, ['np'] = true},
+ ['You are ...'] = {'/sc 4_5', true,
+ ['You are great!'] = true,
+ ['You are good!'] = true,
+ ['You are cool!'] = true,
+ ['You are funny!'] = true,
+ ['You are silly!'] = true,
+ ['You are awesome!'] = true,
+ ['You are doing something I don\'t like, please stop'] = true
+ },
+ ['I like ...'] = {'/sc 4_6', true, ['I like your name'] = true, ['I like your shirt'] = true, ['I like your place'] = true, ['I like your style'] = true,
+ ['I like you'] = true, ['I like items'] = true, ['I like money'] = true},
+ ['Sorry'] = {'/sc 4_7', true, ['My bad!'] = true, ['I\'m sorry'] = true, ['Whoops!'] = true, ['Please forgive me.'] = true, ['I forgive you.'] = true,
+ ['I didn\'t mean to do that.'] = true, ['Sorry, I\'ll stop now.'] = true},
+ [1] = '/sc 4'
+ },
+ ['Questions'] = {
+ ['Who?'] = {'/sc 5_0', true, ['Who wants to be my friend?'] = true, ['Who wants to be on my team?'] = true, ['Who made this brilliant game?'] = true},
+ ['What?'] = {'/sc 5_1', true, ['What is your favorite animal?'] = true, ['What is your favorite game?'] = true, ['What is your favorite movie?'] = true,
+ ['What is your favorite TV show?'] = true, ['What is your favorite music?'] = true, ['What are your hobbies?'] = true, ['LOLWUT?'] = true},
+ ['When?'] = {'/sc 5_2', true, ['When are you online?'] = true, ['When is the new version coming out?'] = true, ['When can we play again?'] = true, ['When will your place be done?'] = true},
+ ['Where?'] = {'/sc 5_3', true, ['Where do you want to go?'] = true, ['Where are you going?'] = true, ['Where am I?!'] = true, ['Where did you go?'] = true},
+ ['How?'] = {'/sc 5_4', true, ['How are you today?'] = true, ['How did you make this cool place?'] = true, ['LOLHOW?'] = true},
+ ['Can I...'] = {'/sc 5_5', true, ['Can I have a tour?'] = true, ['Can I be on your team?'] = true, ['Can I be your friend?'] = true, ['Can I try something?'] = true,
+ ['Can I have that please?'] = true, ['Can I have that back please?'] = true, ['Can I have borrow your hat?'] = true, ['Can I have borrow your gear?'] = true},
+ [1] = '/sc 5'
+ },
+ ['Answers'] = {
+ ['You need help?'] = {'/sc 6_0', true, ['Check out the news section'] = true, ['Check out the help section'] = true, ['Read the wiki!'] = true,
+ ['All the answers are in the wiki!'] = true, ['I will help you with this.'] = true},
+ ['Some people ...'] = {'/sc 6_1', true, ['Me'] = true, ['Not me'] = true, ['You'] = true, ['All of us'] = true, ['Everyone but you'] = true, ['Builderman!'] = true,
+ ['Telamon!'] = true, ['My team'] = true, ['My group'] = true, ['Mom'] = true, ['Dad'] = true, ['Sister'] = true, ['Brother'] = true, ['Cousin'] = true,
+ ['Grandparent'] = true, ['Friend'] = true},
+ ['Time ...'] = {'/sc 6_2', true, ['In the morning'] = true, ['In the afternoon'] = true, ['At night'] = true, ['Tomorrow'] = true, ['This week'] = true, ['This month'] = true,
+ ['Sometime'] = true, ['Sometimes'] = true, ['Whenever you want'] = true, ['Never'] = true, ['After this'] = true, ['In 10 minutes'] = true, ['In a couple hours'] = true,
+ ['In a couple days'] = true},
+ ['Animals'] = {'/sc 6_3', true,
+ ['Cats'] = {['Lion'] = true, ['Tiger'] = true, ['Leopard'] = true, ['Cheetah'] = true},
+ ['Dogs'] = {['Wolves'] = true, ['Beagle'] = true, ['Collie'] = true, ['Dalmatian'] = true, ['Poodle'] = true, ['Spaniel'] = true,
+ ['Shepherd'] = true, ['Terrier'] = true, ['Retriever'] = true},
+ ['Horses'] = {['Ponies'] = true, ['Stallions'] = true, ['Pwnyz'] = true},
+ ['Reptiles'] = {['Dinosaurs'] = true, ['Lizards'] = true, ['Snakes'] = true, ['Turtles!'] = true},
+ ['Hamster'] = true,
+ ['Monkey'] = true,
+ ['Bears'] = true,
+ ['Fish'] = {['Goldfish'] = true, ['Sharks'] = true, ['Sea Bass'] = true, ['Halibut'] = true, ['Tropical Fish'] = true},
+ ['Birds'] = {['Eagles'] = true, ['Penguins'] = true, ['Parakeets'] = true, ['Owls'] = true, ['Hawks'] = true, ['Pidgeons'] = true},
+ ['Elephants'] = true,
+ ['Mythical Beasts'] = {['Dragons'] = true, ['Unicorns'] = true, ['Sea Serpents'] = true, ['Sphinx'] = true, ['Cyclops'] = true,
+ ['Minotaurs'] = true, ['Goblins'] = true, ['Honest Politicians'] = true, ['Ghosts'] = true, ['Scylla and Charybdis'] = true}
+ },
+ ['Games'] = {'/sc 6_4', true,
+ ['Action'] = true, ['Puzzle'] = true, ['Strategy'] = true, ['Racing'] = true, ['RPG'] = true, ['Obstacle Course'] = true, ['Tycoon'] = true,
+ ['Roblox'] = { ['BrickBattle'] = true, ['Community Building'] = true, ['Roblox Minigames'] = true, ['Contest Place'] = true},
+ ['Board games'] = { ['Chess'] = true, ['Checkers'] = true, ['Settlers of Catan'] = true, ['Tigris and Euphrates'] = true, ['El Grande'] = true,
+ ['Stratego'] = true, ['Carcassonne'] = true}
+ },
+ ['Sports'] = {'/sc 6_5', true, ['Hockey'] = true, ['Soccer'] = true, ['Football'] = true, ['Baseball'] = true, ['Basketball'] = true,
+ ['Volleyball'] = true, ['Tennis'] = true, ['Sports team practice'] = true,
+ ['Watersports'] = { ['Surfing'] = true,['Swimming'] = true, ['Water Polo'] = true},
+ ['Winter sports'] = { ['Skiing'] = true, ['Snowboarding'] = true, ['Sledding'] = true, ['Skating'] = true},
+ ['Adventure'] = {['Rock climbing'] = true, ['Hiking'] = true, ['Fishing'] = true, ['Horseback riding'] = true},
+ ['Wacky'] = {['Foosball'] = true, ['Calvinball'] = true, ['Croquet'] = true, ['Cricket'] = true, ['Dodgeball'] = true,
+ ['Squash'] = true, ['Trampoline'] = true}
+ },
+ ['Movies/TV'] = {'/sc 6_6', true, ['Science Fiction'] = true, ['Animated'] = {['Anime'] = true}, ['Comedy'] = true, ['Romantic'] = true,
+ ['Action'] = true, ['Fantasy'] = true},
+ ['Music'] = {'/sc 6_7', true, ['Country'] = true, ['Jazz'] = true, ['Rap'] = true, ['Hip-hop'] = true, ['Techno'] = true, ['Classical'] = true,
+ ['Pop'] = true, ['Rock'] = true},
+ ['Hobbies'] = {'/sc 6_8', true,
+ ['Computers'] = { ['Building computers'] = true, ['Videogames'] = true, ['Coding'] = true, ['Hacking'] = true},
+ ['The Internet'] = { ['lol. teh internets!'] = true, ['Watching vids'] = true},
+ ['Dance'] = true, ['Gymnastics'] = true, ['Listening to music'] = true, ['Arts and crafts'] = true,
+ ['Martial Arts'] = {['Karate'] = true, ['Judo'] = true, ['Taikwon Do'] = true, ['Wushu'] = true, ['Street fighting'] = true},
+ ['Music lessons'] = {['Playing in my band'] = true, ['Playing piano'] = true, ['Playing guitar'] = true,
+ ['Playing violin'] = true, ['Playing drums'] = true, ['Playing a weird instrument'] = true}
+ },
+ ['Location'] = {'/sc 6_9', true,
+ ['USA'] = {
+ ['West'] = { ['Alaska'] = true, ['Arizona'] = true, ['California'] = true, ['Colorado'] = true, ['Hawaii'] = true,
+ ['Idaho'] = true, ['Montana'] = true, ['Nevada'] = true, ['New Mexico'] = true, ['Oregon'] = true,
+ ['Utah'] = true, ['Washington'] = true, ['Wyoming'] = true
+ },
+ ['South'] = { ['Alabama'] = true, ['Arkansas'] = true, ['Florida'] = true, ['Georgia'] = true, ['Kentucky'] = true,
+ ['Louisiana'] = true, ['Mississippi'] = true, ['North Carolina'] = true, ['Oklahoma'] = true,
+ ['South Carolina'] = true, ['Tennessee'] = true, ['Texas'] = true, ['Virginia'] = true, ['West Virginia'] = true
+ },
+ ['Northeast'] = {['Connecticut'] = true, ['Delaware'] = true, ['Maine'] = true, ['Maryland'] = true, ['Massachusetts'] = true,
+ ['New Hampshire'] = true, ['New Jersey'] = true, ['New York'] = true, ['Pennsylvania'] = true, ['Rhode Island'] = true,
+ ['Vermont'] = true
+ },
+ ['Midwest'] = {['Illinois'] = true, ['Indiana'] = true, ['Iowa'] = true, ['Kansas'] = true, ['Michigan'] = true, ['Minnesota'] = true,
+ ['Missouri'] = true, ['Nebraska'] = true, ['North Dakota'] = true, ['Ohio'] = true, ['South Dakota'] = true, ['Wisconsin'] = true}
+ },
+ ['Canada'] = {['Alberta'] = true, ['British Columbia'] = true, ['Manitoba'] = true, ['New Brunswick'] = true, ['Newfoundland'] = true,
+ ['Northwest Territories'] = true, ['Nova Scotia'] = true, ['Nunavut'] = true, ['Ontario'] = true, ['Prince Edward Island'] = true,
+ ['Quebec'] = true, ['Saskatchewan'] = true, ['Yukon'] = true},
+ ['Mexico'] = true,
+ ['Central America'] = true,
+ ['Europe'] = {['France'] = true, ['Germany'] = true, ['Spain'] = true, ['Italy'] = true, ['Poland'] = true, ['Switzerland'] = true,
+ ['Greece'] = true, ['Romania'] = true, ['Netherlands'] = true,
+ ['Great Britain'] = {['England'] = true, ['Scotland'] = true, ['Wales'] = true, ['Northern Ireland'] = true}
+ },
+ ['Asia'] = { ['China'] = true, ['India'] = true, ['Japan'] = true, ['Korea'] = true, ['Russia'] = true, ['Vietnam'] = true},
+ ['South America'] = { ['Argentina'] = true, ['Brazil'] = true},
+ ['Africa'] = { ['Eygpt'] = true, ['Swaziland'] = true},
+ ['Australia'] = true, ['Middle East'] = true, ['Antarctica'] = true, ['New Zealand'] = true
+ },
+ ['Age'] = {'/sc 6_10', true, ['Rugrat'] = true, ['Kid'] = true, ['Tween'] = true, ['Teen'] = true, ['Twenties'] = true,
+ ['Old'] = true, ['Ancient'] = true, ['Mesozoic'] = true, ['I don\'t want to say my age. Don\'t ask.'] = true},
+ ['Mood'] = {'/sc 6_11', true, ['Good'] = true, ['Great!'] = true, ['Not bad'] = true, ['Sad'] = true, ['Hyper'] = true,
+ ['Chill'] = true, ['Happy'] = true, ['Kind of mad'] = true},
+ ['Boy'] = {'/sc 6_12', true},
+ ['Girl'] = {'/sc 6_13', true},
+ ['I don\'t want to say boy or girl. Don\'t ask.'] = {'/sc 6_14', true},
+ [1] = '/sc 6'
+ },
+ ['Game'] = {
+ ['Let\'s build'] = {'/sc 7_0', true},
+ ['Let\'s battle'] = {'/sc 7_1', true},
+ ['Nice one!'] = {'/sc 7_2', true},
+ ['So far so good'] = {'/sc 7_3', true},
+ ['Lucky shot!'] = {'/sc 7_4', true},
+ ['Oh man!'] = {'/sc 7_5', true},
+ ['I challenge you to a fight!'] = {'/sc 7_6', true},
+ ['Help me with this'] = {'/sc 7_7', true},
+ ['Let\'s go to your game'] = {'/sc 7_8', true},
+ ['Can you show me how do to that?'] = {'/sc 7_9', true},
+ ['Backflip!'] = {'/sc 7_10', true},
+ ['Frontflip!'] = {'/sc 7_11', true},
+ ['Dance!'] = {'/sc 7_12', true},
+ ['I\'m on your side!'] = {'/sc 7_13', true},
+ ['Game Commands'] = {'/sc 7_14', true, ['regen'] = true, ['reset'] = true, ['go'] = true, ['fix'] = true, ['respawn'] = true},
+ [1] = '/sc 7'
+ };
+ ['Silly'] = {
+ ['Muahahahaha!'] = true,
+ ['all your base are belong to me!'] = true,
+ ['GET OFF MAH LAWN'] = true,
+ ['TEH EPIK DUCK IS COMING!!!'] = true,
+ ['ROFL'] = true,
+ ['1337'] = {true, ['i r teh pwnz0r!'] = true, ['w00t!'] = true, ['z0mg h4x!'] = true, ['ub3rR0xXorzage!'] = true}
+ },
+ ['Yes'] = {
+ ['Absolutely!'] = true,
+ ['Rock on!'] = true,
+ ['Totally!'] = true,
+ ['Juice!'] = true,
+ ['Yay!'] = true,
+ ['Yesh'] = true
+ },
+ ['No'] = {
+ ['Ummm. No.'] = true,
+ ['...'] = true,
+ ['Stop!'] = true,
+ ['Go away!'] = true,
+ ['Don\'t do that'] = true,
+ ['Stop breaking the rules'] = true,
+ ['I don\'t want to'] = true
+ },
+ ['Ok'] = {
+ ['Well... ok'] = true,
+ ['Sure'] = true
+ },
+ ['Uncertain'] = {
+ ['Maybe'] = true,
+ ['I don\'t know'] = true,
+ ['idk'] = true,
+ ['I can\'t decide'] = true,
+ ['Hmm...'] = true
+ },
+ [':-)'] = {
+ [':-('] = true,
+ [':D'] = true,
+ [':-O'] = true,
+ ['lol'] = true,
+ ['=D'] = true,
+ ['D='] = true,
+ ['XD'] = true,
+ [';D'] = true,
+ [';)'] = true,
+ ['O_O'] = true,
+ ['=)'] = true,
+ ['@_@'] = true,
+ ['>_<'] = true,
+ ['T_T'] = true,
+ ['^_^'] = true,
+ ['<(0_0<) <(0_0)> (>0_0)> KIRBY DANCE'] = true,
+ [')\';'] = true,
+ [':3'] = true
+ },
+ ['Ratings'] = {
+ ['Rate it!'] = true,
+ ['I give it a 1 out of 10'] = true,
+ ['I give it a 2 out of 10'] = true,
+ ['I give it a 3 out of 10'] = true,
+ ['I give it a 4 out of 10'] = true,
+ ['I give it a 5 out of 10'] = true,
+ ['I give it a 6 out of 10'] = true,
+ ['I give it a 7 out of 10'] = true,
+ ['I give it a 8 out of 10'] = true,
+ ['I give it a 9 out of 10'] = true,
+ ['I give it a 10 out of 10!'] = true,
+ }
+ },
+ CreateEnum('SafeChat'){'Level1', 'Level2', 'Level3'},
+ SafeChatTree = {}
+ }
+---------------------------------------------------
+
+local function GetNameValue(pName)
+ local value = 0
+ for index = 1, #pName do
+ local cValue = string.byte(string.sub(pName, index, index))
+ local reverseIndex = #pName - index + 1
+ if #pName%2 == 1 then
+ reverseIndex = reverseIndex - 1
+ end
+ if reverseIndex%4 >= 2 then
+ cValue = -cValue
+ end
+ value = value + cValue
+ end
+ return value%8
+end
+
+function Chat:ComputeChatColor(pName)
+ return self.ChatColors[GetNameValue(pName) + 1].Color
+end
+
+-- This is context based scrolling
+function Chat:EnableScrolling(toggle)
+ -- Genius idea gone to fail, if we switch the camera type we can effectively lock the
+ -- camera and do no click scrolling
+ self.MouseOnFrame = false
+ if self.RenderFrame then
+ self.RenderFrame.MouseEnter:connect(function()
+ local character = Player.Character
+ local torso = WaitForChild(character, 'Torso')
+ local humanoid = WaitForChild(character, 'Humanoid')
+ local head = WaitForChild(character, 'Head')
+ if toggle then
+ self.MouseOnFrame = true
+ Camera.CameraType = 'Scriptable'
+ -- Get relative position of camera and keep to it
+ Spawn(function()
+ local currentRelativePos = Camera.CoordinateFrame.p - torso.Position
+ while Chat.MouseOnFrame do
+ Camera.CoordinateFrame = CFrame.new(torso.Position + currentRelativePos, head.Position)
+ wait(0.015)
+ end
+ end)
+ end
+ end)
+
+ self.RenderFrame.MouseLeave:connect(function()
+ Camera.CameraType = 'Custom'
+ self.MouseOnFrame = false
+ end)
+ end
+end
+
+-- TODO: Scrolling using Mouse wheel
+function Chat:OnScroll(speed)
+ if self.MouseOnFrame then
+ --
+ end
+end
+
+-- Check if we are running on a touch device
+function Chat:IsTouchDevice()
+ local touchEnabled = false
+ pcall(function() touchEnabled = Game:GetService('UserInputService').TouchEnabled end)
+ return touchEnabled
+end
+
+-- Scrolling
+function Chat:ScrollQueue(value)
+ for i = 1, #self.MessageQueue do
+ if self.MessageQueue[i] then
+ for _, label in pairs(self.MessageQueue[i]) do
+ local next = self.MessageQueue[i].Next
+ local previous = self.MessageQueue[i].Previous
+ if label and label:IsA('TextLabel') or label:IsA('TextButton') then
+ if value > 0 and previous and previous['Message'] then
+ label.Position = previous['Message'].Position
+ elseif value < 1 and next['Message'] then
+ label.Position = previous['Message'].Position
+ end
+ end
+ end
+ end
+ end
+end
+
+-- Handles the rendering of the text objects in their appropriate places
+function Chat:UpdateQueue(field)
+ -- Have to do some sort of correction here
+ for i = #self.MessageQueue, 1, -1 do
+ if self.MessageQueue[i] then
+ for _, label in pairs(self.MessageQueue[i]) do
+ if label and type(label) ~= 'table' and type(label) ~= 'number' then
+ if label:IsA('TextLabel') or label:IsA('TextButton') then
+ if self.SlotPositions_List[i] and self.MessageQueue[i] ~= field then
+ label.Position = self.SlotPositions_List[i][1] - UDim2.new(0, 0, field['Message'].Size.Y.Scale, 0)
+ label.Position = UDim2.new(self.Configuration.XScale, 0, label.Position.Y.Scale, 0)
+ else
+ label.Position = UDim2.new(self.Configuration.XScale, 0, label.Position.Y.Scale - field['Message'].Size.Y.Scale , 0)
+ end
+ if field == self.MessageQueue[i] then
+ -- Just to show up popping effect for the latest message in chat
+ Spawn(function()
+ wait(0.15)
+ while label.TextTransparency > 0 do
+ label.TextTransparency = label.TextTransparency - 0.2
+ wait()
+ end
+ end)
+ end
+ if label.Position.Y.Scale < -0.05 or label.Position.Y.Scale > 1.0 then
+ label.TextTransparency = 1.0
+ else
+ label.TextTransparency = 0.0
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+function Chat:InsertIntoQueue(field)
+ -- This is a ring buffer where insertions happen at the beginning
+ -- One optimization here would be to not copy the queue by reference
+ -- And reuse the same queue and do the insertion in place
+ -- But rendering code must be changed accordingly and this becomes rather hard to manage
+ -- plus the effects are minimal
+ -- TODO: Revist if this becomes a problem for optimization
+ local tmpQueue = self.MessageQueue
+ self.MessageQueue = {}
+ self.MessageQueue[1] = field
+ self.MessageQueue[1].Previous = tmpQueue[i]
+ self.MessageQueue[1].Next = nil
+
+ self.SlotPositions_List = {}
+ self.SlotPositions_List[1] = {field.Position, field.Size}
+
+ if tmpQueue[self.Configuration.HistoryLength] then
+ if tmpQueue[self.Configuration.HistoryLength]['Player'] then
+ tmpQueue[self.Configuration.HistoryLength]['Player']:Destroy()
+ end
+ if tmpQueue[self.Configuration.HistoryLength]['Message'] then
+ tmpQueue[self.Configuration.HistoryLength]['Message']:Destroy()
+ end
+ tmpQueue[self.Configuration.HistoryLength] = nil
+ end
+
+ for i = 1, self.Configuration.HistoryLength - 1 do
+ if tmpQueue[i] and tmpQueue[i]['Player'] and tmpQueue[i]['Player'].Parent then
+ table.insert(self.MessageQueue, tmpQueue[i])
+ self.MessageQueue[#self.MessageQueue].Previous = tmpQueue[i+1] or nil
+ self.MessageQueue[#self.MessageQueue].Next = tmpQueue[i-1] or nil
+
+ self.SlotPositions_List[#self.MessageQueue] = {tmpQueue[i]['Message'].Position, tmpQueue[i]['Message'].Size}
+ end
+ end
+ -- Now, render the queue
+ Chat:UpdateQueue(field)
+end
+
+function Chat:CreateScrollBar()
+ -- Code for scrolling is in here, partially, but scroll bar drawing isn't drawn
+ -- TODO: Implement
+end
+
+function Chat:FocusOnChatBar()
+ if self.ClickToChatButton then
+ self.ClickToChatButton.Visible = false
+ end
+
+ self.GotFocus = true
+ if self.Frame['Background'] then
+ self.Frame.Background.Visible = false
+ end
+ self.ChatBar:CaptureFocus()
+end
+
+-- For touch devices we create a button instead
+function Chat:CreateTouchButton()
+ self.ChatTouchFrame = Gui.Create'Frame'
+ {
+ Name = 'ChatTouchFrame';
+ Size = UDim2.new(0, 128, 0, 32);
+ Position = UDim2.new(0, 88, 0, 0);
+ BackgroundTransparency = 1.0;
+ Parent = self.Gui;
+
+ Gui.Create'ImageButton'
+ {
+ Name = 'ChatLabel';
+ Size = UDim2.new(0, 74, 0, 28);
+ Position = UDim2.new(0, 0, 0, 0);
+ BackgroundTransparency = 1.0;
+ ZIndex = 2.0;
+ };
+
+ Gui.Create'ImageLabel'
+ {
+ Name = 'Background';
+ Size = UDim2.new(1, 0, 1, 0);
+ Position = UDim2.new(0, 0, 0, 0);
+ BackgroundTransparency = 1.0;
+ Image = 'http://www.roblox.com/asset/?id=97078724'
+ };
+ }
+ self.TapToChatLabel = self.ChatTouchFrame.ChatLabel
+ self.TouchLabelBackground = self.ChatTouchFrame.Background
+
+ self.ChatBar = Gui.Create'TextBox'
+ {
+ Name = 'ChatBar';
+ Size = UDim2.new(1, 0, 0.2, 0);
+ Position = UDim2.new(0, 0, 0.8, 0);
+ Text = "";
+ ZIndex = 1.0;
+ BackgroundTransparency = 1.0;
+ Parent = self.Frame;
+ TextXAlignment = Enum.TextXAlignment.Left;
+ TextColor3 = Color3.new(1, 1, 1);
+ ClearTextOnFocus = false;
+ };
+
+ self.TapToChatLabel.MouseButton1Click:connect(function()
+ self.TapToChatLabel.Visible = false
+ --self.ChatBar.Visible = true
+ self.Frame.Background.Visible = true
+ self.ChatBar:CaptureFocus()
+ self.GotFocus = true
+ if self.TouchLabelBackground then
+ self.TouchLabelBackground.Visible = false
+ end
+ end)
+end
+
+-- Non touch devices, create the bottom chat bar
+function Chat:CreateChatBar()
+ -- okay now we do
+ local status, result = pcall(function() return GuiService.UseLuaChat end)
+ if status and result then
+ self.ClickToChatButton = Gui.Create'TextButton'
+ {
+ Name = 'ClickToChat';
+ Size = UDim2.new(1, 0, 0, 20);
+ BackgroundTransparency = 1.0;
+ ZIndex = 2.0;
+ Parent = self.Gui;
+ Text = "To chat click here or press \"/\" key";
+ TextColor3 = Color3.new(1, 1, 0.9);
+ Position = UDim2.new(0, 0, 1, 0);
+ TextXAlignment = Enum.TextXAlignment.Left;
+ FontSize = Enum.FontSize.Size12;
+ }
+
+ self.ChatBar = Gui.Create'TextBox'
+ {
+ Name = 'ChatBar';
+ Size = UDim2.new(1, 0, 0, 20);
+ Position = UDim2.new(0, 0, 1, 0);
+ Text = "";
+ ZIndex = 1.0;
+ BackgroundColor3 = Color3.new(0, 0, 0);
+ BackgroundTransparency = 0.25;
+ Parent = self.Gui;
+ TextXAlignment = Enum.TextXAlignment.Left;
+ TextColor3 = Color3.new(1, 1, 1);
+ FontSize = Enum.FontSize.Size12;
+ ClearTextOnFocus = false;
+ Text = '';
+ };
+
+ -- Engine has code to offset the entire world, so if we do it by -20 pixels nothing gets in our chat's way
+ GuiService:SetGlobalSizeOffsetPixel(0, -20)
+ -- CHatHotKey is '/'
+ GuiService:AddSpecialKey(Enum.SpecialKey.ChatHotkey)
+ GuiService.SpecialKeyPressed:connect(function(key)
+ if key == Enum.SpecialKey.ChatHotkey then
+ Chat:FocusOnChatBar()
+ end
+ end)
+
+ self.ClickToChatButton.MouseButton1Click:connect(function()
+ Chat:FocusOnChatBar()
+ end)
+ end
+end
+
+-- For scrolling, to see if we hit the bounds so that we can stop it from scrolling anymore
+function Chat:CheckIfInBounds(value)
+ if #Chat.MessageQueue < 3 then
+ return true
+ end
+
+ if value > 0 and Chat.MessageQueue[1] and Chat.MessageQueue[1]['Player'] and Chat.MessageQueue[1]['Player'].Position.Y.Scale == 0 then
+ return true
+ elseif value < 0 and Chat.MessageQueue[1] and Chat.MessageQueue[1]['Player'] and Chat.MessageQueue[1]['Player'].Position.Y.Scale < 0 then
+ return true
+ else
+ return false
+ end
+ return false
+end
+
+-- This is to precompute all playerName space strings
+-- This is used to offset the message by exactly this + 2 spacestrings
+function Chat:ComputeSpaceString(pLabel)
+ local tLabel = Gui.Create'TextButton'
+ {
+ Size = UDim2.new(0, pLabel.AbsoluteSize.X, 0, pLabel.AbsoluteSize.Y);
+ FontSize = self.Configuration.FontSize;
+ Parent = self.RenderFrame;
+ BackgroundTransparency = 1.0;
+ Text = nString;
+ };
+ local nString = " "
+ while tLabel.TextBounds.X < pLabel.TextBounds.X do
+ nString = nString .. " "
+ tLabel.Text = nString
+ end
+ self.CachedSpaceStrings_List[pLabel.Text] = nString
+ return nString
+end
+
+-- When the playerChatted event fires
+-- The message is what the player chatted
+function Chat:UpdateChat(cPlayer, message)
+ local pName = cPlayer.Name
+ local pLabel
+ local mLabel
+ -- Our history stores upto 50 messages that is 100 textlabels
+ -- If we ever hit the mark, which would be in every popular game btw
+ -- we wrap around and reuse the labels
+ if #self.MessageQueue > self.Configuration.HistoryLength then
+ pLabel = self.MessageQueue[#self.MessageQueue]['Player']
+ mLabel = self.MessageQueue[#self.MessageQueue]['Message']
+
+ pLabel.Text = pName
+ pLabel.Name = pName
+ pLabel.TextColor3 = Chat:ComputeChatColor(pName)
+
+ local nString
+
+ if not self.CachedSpaceStrings_List[pName] then
+ nString = Chat:ComputeSpaceString(pLabel)
+ else
+ nString = self.CachedSpaceStrings_List[pName]
+ end
+
+ mLabel.Name = pName .. " - message"
+ mLabel.Text = nString .. message;
+
+ -- Reinserted at the beginning, ring buffer
+ self.MessageQueue[#self.MessageQueue] = nil
+ else
+ -- Haven't hit the mark yet, so keep creating
+ pLabel = Gui.Create'TextButton'
+ {
+ Name = pName;
+ Text = pName .. ":";
+ TextColor3 = Chat:ComputeChatColor(pName);
+ FontSize = Chat.Configuration.FontSize;
+ TextXAlignment = Enum.TextXAlignment.Left;
+ TextYAlignment = Enum.TextYAlignment.Top;
+ Parent = self.RenderFrame;
+ TextWrapped = false;
+ Size = UDim2.new(1, 0, 0.1, 0);
+ BackgroundTransparency = 1.0;
+ TextTransparency = 1.0;
+ Position = UDim2.new(0, 0, 1, 0);
+ BorderSizePixel = 0.0;
+ };
+
+ local nString
+
+ if not self.CachedSpaceStrings_List[pName] then
+ nString = Chat:ComputeSpaceString(pLabel)
+ else
+ nString = self.CachedSpaceStrings_List[pName]
+ end
+
+ mLabel = Gui.Create'TextButton'
+ {
+ Name = pName .. ' - message';
+ -- Max is 3 lines
+ Size = UDim2.new(1, 0, 0.5, 0);
+ Text = nString .. message;
+ TextColor3 = Chat.Configuration.MessageColor;
+ FontSize = Chat.Configuration.FontSize;
+ TextXAlignment = Enum.TextXAlignment.Left;
+ TextYAlignment = Enum.TextYAlignment.Top;
+ Parent = self.RenderFrame;
+ TextWrapped = true;
+ BackgroundTransparency = 1.0;
+ TextTransparency = 1.0;
+ Position = UDim2.new(0, 0, 1, 0);
+ BorderSizePixel = 0.0;
+ };
+ end
+
+ -- This is to find if we need to wrap the text around
+ -- Expensive, but works well and worthwhile
+ -- This will give beautiful multilines as well
+ local heightField = 0.1 * self.RenderFrame.AbsoluteSize.Y
+
+ while heightField < mLabel.TextBounds.Y do
+ heightField = heightField + 1
+ end
+
+ pLabel.Visible = true
+ mLabel.Visible = true
+
+ mLabel.Size = UDim2.new(1, 0, heightField/self.RenderFrame.AbsoluteSize.Y, 0)
+ pLabel.Size = mLabel.Size
+ local yPixels = self.RenderFrame.AbsoluteSize.Y
+ local yFieldSize = mLabel.TextBounds.Y
+
+ local queueField = {}
+ queueField['Player'] = pLabel
+ queueField['Message'] = mLabel
+ queueField['SpawnTime'] = tick() -- Used for identifying when to make the message invisible
+
+ Chat:InsertIntoQueue(queueField)
+end
+
+function Chat:ScreenSizeChanged()
+ while self.Frame.AbsoluteSize.Y > 120 do
+ self.Frame.Size = self.Frame.Size - UDim2.new(0, 0, 0.005, 0)
+ end
+end
+
+-- Create the initial Chat stuff
+-- Done only once
+function Chat:CreateGui()
+ self.Gui = WaitForChild(CoreGuiService, 'RobloxGui')
+ self.Frame = Gui.Create'Frame'
+ {
+ Name = 'ChatFrame';
+ Size = self.Configuration.Size;
+ Position = UDim2.new(0, 0, 0, 5);
+ BackgroundTransparency = 1.0;
+ --ClipsDescendants = true;
+ ZIndex = 0.0;
+ Parent = self.Gui;
+
+ Gui.Create'ImageLabel'
+ {
+ Name = 'Background';
+ Image = 'http://www.roblox.com/asset/?id=97120937'; --96551212';
+ Size = UDim2.new(1.3, 0, 1.64, 0);
+ Position = UDim2.new(0, 0, 0, 0);
+ BackgroundTransparency = 1.0;
+ ZIndex = 0.0;
+ Visible = false
+ };
+
+ Gui.Create'Frame'
+ {
+ Name = 'Border';
+ Size = UDim2.new(1, 0, 0, 1);
+ Position = UDim2.new(0, 0, 0.8, 0);
+ BackgroundTransparency = 0.0;
+ BackgroundColor3 = Color3.new(236/255, 236/255, 236/255);
+ BorderSizePixel = 0.0;
+ Visible = false;
+ };
+
+ Gui.Create'Frame'
+ {
+ Name = 'ChatRenderFrame';
+ Size = UDim2.new(1.02, 0, 1.01, 0);
+ Position = UDim2.new(0, 0, 0, 0);
+ BackgroundTransparency = 1.0;
+ ClipsDescendants = true;
+ ZIndex = 0.0;
+
+ };
+ };
+
+
+ self.RenderFrame = self.Frame.ChatRenderFrame
+ if Chat:IsTouchDevice() then
+ self.Frame.Position = self.Configuration.Position;
+ self.RenderFrame.Size = UDim2.new(1, 0, 1, 0)
+ elseif self.Frame.AbsoluteSize.Y > 120 then
+ Chat:ScreenSizeChanged()
+ self.Gui.Changed:connect(function(property)
+ if property == 'AbsoluteSize' then
+ Chat:ScreenSizeChanged()
+ end
+ end)
+ end
+
+ if Player.ChatMode == Enum.ChatMode.TextAndMenu then
+ if Chat:IsTouchDevice() then
+ Chat:CreateTouchButton()
+ else
+ Chat:CreateChatBar()
+ --Chat:CreateSafeChatGui()
+ end
+
+ if self.ChatBar then
+ self.ChatBar.FocusLost:connect(function(enterPressed)
+ Chat.GotFocus = false
+ if Chat:IsTouchDevice() then
+ self.ChatBar.Visible = false
+ self.TapToChatLabel.Visible = true
+
+ if self.TouchLabelBackground then
+ self.TouchLabelBackground.Visible = true
+ end
+ end
+ if enterPressed and self.ChatBar.Text ~= "" then
+ if PlayersService.ClassicChat then
+ pcall(function() PlayersService:Chat(self.ChatBar.Text) end)
+ elseif PlayersService.BubbleChat then
+ -- do nothing
+ end
+ if self.ClickToChatButton then
+ self.ClickToChatButton.Visible = true
+ end
+ self.ChatBar.Text = ""
+ end
+ Spawn(function()
+ wait(5.0)
+ if not Chat.GotFocus then
+ Chat.Frame.Background.Visible = false
+ end
+ end)
+ end)
+ end
+ end
+end
+
+function Chat:FindButtonTree(scButton, rootList)
+ local list = {}
+ rootList = rootList or self.SafeChatTree
+ for button, _ in pairs(rootList) do
+ if button == scButton then
+ list = rootList[button]
+ elseif type(rootList[button]) == 'table' then
+ list = Chat:FindButtonTree(scButton, rootList[button])
+ end
+ end
+ return list
+end
+
+function Chat:ToggleSafeChatMenu(scButton)
+ local list = Chat:FindButtonTree(scButton, self.SafeChatTree)
+ if list then
+ for button, _ in pairs(list) do
+ if button:IsA('TextButton') or button:IsA('ImageButton') then
+ button.Visible = not button.Visible
+ end
+ end
+ return true
+ end
+ return false
+end
+
+function Chat:CreateSafeChatOptions(list, rootButton)
+ local text_List = {}
+ level = level or 0
+ local count = 0
+ text_List[rootButton] = {}
+ text_List[rootButton][1] = list[1]
+ rootButton = rootButton or self.SafeChatButton
+ for msg, _ in pairs(list) do
+ if type(msg) == 'string' then
+ local chatText = Gui.Create'TextButton'
+ {
+ Name = msg;
+ Text = msg;
+ Size = UDim2.new(0, 100, 0, 20);
+ TextXAlignment = Enum.TextXAlignment.Center;
+ TextColor3 = Color3.new(0.2, 0.1, 0.1);
+ BackgroundTransparency = 0.5;
+ BackgroundColor3 = Color3.new(1, 1, 1);
+ Parent = self.SafeChatFrame;
+ Visible = false;
+ Position = UDim2.new(0, rootButton.Position.X.Scale + 105, 0, rootButton.Position.Y.Scale - ((count - 3) * 100));
+ };
+
+ count = count + 1
+
+ if type(list[msg]) == 'table' then
+ text_List[rootButton][chatText] = Chat:CreateSafeChatOptions(list[msg], chatText)
+ else
+ --table.insert(text_List[chatText], true)
+ end
+ chatText.MouseEnter:connect(function()
+ Chat:ToggleSafeChatMenu(chatText)
+ end)
+
+ chatText.MouseLeave:connect(function()
+ Chat:ToggleSafeChatMenu(chatText)
+ end)
+
+ chatText.MouseButton1Click:connect(function()
+ local lList = Chat:FindButtonTree(chatText)
+ if lList then
+ for i, v in pairs(lList) do
+ end
+ else
+ end
+ pcall(function() PlayersService:Chat(lList[1]) end)
+ end)
+ end
+ end
+ return text_List
+end
+
+function Chat:CreateSafeChatGui()
+ self.SafeChatFrame = Gui.Create'Frame'
+ {
+ Name = 'SafeChatFrame';
+ Size = UDim2.new(1, 0, 1, 0);
+ Parent = self.Gui;
+ BackgroundTransparency = 1.0;
+
+ Gui.Create'ImageButton'
+ {
+ Name = 'SafeChatButton';
+ Size = UDim2.new(0, 44, 0, 31);
+ Position = UDim2.new(0, 1, 0.35, 0);
+ BackgroundTransparency = 1.0;
+ Image = 'http://www.roblox.com/asset/?id=97080365';
+ };
+ }
+
+ self.SafeChatButton = self.SafeChatFrame.SafeChatButton
+ -- safe chat button is the root of this tree
+ self.SafeChatTree[self.SafeChatButton] = Chat:CreateSafeChatOptions(self.SafeChat_List, self.SafeChatButton)
+
+ self.SafeChatButton.MouseButton1Click:connect(function()
+ Chat:ToggleSafeChatMenu(self.SafeChatButton)
+ end)
+
+end
+
+-- Scrolling function
+-- Applies a speed(velocity) to have nice scrolling effect
+function Input:OnMouseScroll()
+ Spawn(function()
+ -- How long should the speed last?
+ while Input.Speed ~=0 do
+ if Input.Speed > 1 then
+ while Input.Speed > 0 do
+ Input.Speed = Input.Speed - 1
+ wait(0.25)
+ end
+ elseif Input.Speed < 0 then
+ while Input.Speed < 0 do
+ Input.Speed = Input.Speed + 1
+ wait(0.25)
+ end
+ end
+ wait()
+ end
+ end)
+ if Chat:CheckIfInBounds(Input.Speed) then
+ return
+ end
+ Chat:ScrollQueue()
+end
+
+function Input:ApplySpeed(value)
+ Input.Speed = Input.Speed + value
+ if not self.Simulating then
+ Input:OnMouseScroll()
+ end
+end
+
+function Input:Initialize()
+ self.Mouse.WheelBackward:connect(function()
+ Input:ApplySpeed(self.Configuration.DefaultSpeed)
+ end)
+
+ self.Mouse.WheelForward:connect(function()
+ Input:ApplySpeed(self.Configuration.DefaultSpeed)
+ end)
+end
+
+function Chat:FindMessageInSafeChat(message, list)
+ local foundMessage = false
+ for msg, _ in pairs(list) do
+ if msg == message then
+ return true
+ end
+ if type(list[msg]) == 'table' then
+ foundMessage = Chat:FindMessageInSafeChat(message, list[msg])
+ if foundMessage then
+ return true
+ end
+ end
+ end
+ return foundMessage
+end
+
+-- Just a wrapper around our PlayerChatted event
+function Chat:PlayerChatted(...)
+ local args = {...}
+ local argCount = select('#', ...)
+ local player
+ local message
+ -- This doesn't look very good, but what else to do?
+ if args[2] then
+ player = args[2]
+ end
+ if args[3] then
+ message = args[3]
+ end
+ if PlayersService.ClassicChat then
+ if Player.ChatMode == Enum.ChatMode.TextAndMenu then
+ Chat:UpdateChat(player, message)
+ elseif Player.ChatMode == Enum.ChatMode.Menu and string.sub(message, 3) == '/sc' then
+ Chat:UpdateChat(player, message)
+ else
+ if Chat:FindMessageInSafeChat(message, self.SafeChat_List) then
+ Chat:UpdateChat(player, message)
+ end
+ end
+ end
+end
+
+-- After 5 minutes of existence, the labels become invisible
+-- Runs only every 5 seconds and has to loop through 50 values
+-- Shouldn't be too expensive
+function Chat:CullThread()
+ while true do
+ if #self.MessageQueue > 0 then
+ for _, field in pairs(self.MessageQueue) do
+ if tick() - field['SpawnTime'] > self.Configuration.LifeTime and field['Player'] and field['Message'] then
+ field['Player'].Visible = false
+ field['Message'].Visible = false
+ end
+ end
+ end
+ wait(5.0)
+ end
+end
+
+-- RobloxLock everything so users can't delete them(?)
+function Chat:LockAllFields(gui)
+ local children = gui:GetChildren()
+ for i = 1, #children do
+ children[i].RobloxLocked = true
+ if #children[i]:GetChildren() > 0 then
+ Chat:LockAllFields(children[i])
+ end
+ end
+end
+
+-- Constructor
+-- This function initializes everything
+function Chat:Initialize()
+ Chat:CreateGui()
+ self.EventListener = PlayersService.PlayerChatted:connect(function(...)
+ -- This event has 4 callback arguments
+ -- Enum.PlayerChatType.All, chatPlayer, message, targetPlayer
+ Chat:PlayerChatted(...)
+
+ end)
+
+ -- Initialize input for us
+ Input:Initialize()
+ -- Eww, everytime a player is added, you have to redo the connection
+ -- Seems this is not automatic
+ Game.Players.ChildAdded:connect(function()
+ Chat.EventListener:disconnect()
+ self.EventListener = PlayersService.PlayerChatted:connect(function(...)
+ -- This event has 4 callback arguments
+ -- Enum.PlayerChatType.All, chatPlayer, message, targetPlayer
+ Chat:PlayerChatted(...)
+ end)
+ end)
+
+ Spawn(function()
+ Chat:CullThread()
+ end)
+
+ self.Frame.RobloxLocked = true
+ Chat:LockAllFields(self.Frame)
+ self.Frame.DescendantAdded:connect(function(descendant)
+ Chat:LockAllFields(descendant)
+ end)
+end
+
+Chat:Initialize()
+
+
+
+
+
diff --git a/app/files/LuaThumbnail.png b/app/files/LuaThumbnail.png
new file mode 100644
index 0000000..b481fdf
Binary files /dev/null and b/app/files/LuaThumbnail.png differ
diff --git a/app/files/NoRender.png b/app/files/NoRender.png
new file mode 100644
index 0000000..f2fda92
Binary files /dev/null and b/app/files/NoRender.png differ
diff --git a/app/files/Old2014Join.lua b/app/files/Old2014Join.lua
new file mode 100644
index 0000000..d4debbc
--- /dev/null
+++ b/app/files/Old2014Join.lua
@@ -0,0 +1,418 @@
+
+-- functions --------------------------
+function onPlayerAdded(player)
+ -- override
+end
+-- MultiplayerSharedScript.lua inserted here ------ Prepended to GroupBuild.lua and Join.lua --
+
+-- log app init time
+pcall(function()
+ local t = ElapsedTime()
+ local platform = settings().Diagnostics.OsPlatform
+ game:HttpGet("http://www.syntax.eco/Game/JoinRate.ashx?st=0&i=0&p=-1&c=GameAppInit&r=Success&d=" .. (math.floor(t*1000)) .. "&ip=localhost&errorType=&platform=" .. platform, false)
+end)
+
+pcall(function() game:SetPlaceID(-1, false) end)
+
+local startTime = tick()
+local connectResolved = false
+local loadResolved = false
+local joinResolved = false
+local playResolved = true
+local playStartTime = 0
+
+local cdnSuccess = 0
+local cdnFailure = 0
+
+settings()["Game Options"].CollisionSoundEnabled = true
+pcall(function() settings().Rendering.EnableFRM = true end)
+pcall(function() settings().Physics.Is30FpsThrottleEnabled = true end)
+pcall(function() settings()["Task Scheduler"].PriorityMethod = Enum.PriorityMethod.AccumulatedError end)
+pcall(function() settings().Physics.PhysicsEnvironmentalThrottle = Enum.EnviromentalPhysicsThrottle.DefaultAuto end)
+
+function reportContentProvider(time, queueLength, blocking)
+ pcall(function()
+ game:HttpGet("http://www.syntax.eco/Analytics/ContentProvider.ashx?t=" .. time .. "&ql=" .. queueLength, blocking)
+ end)
+end
+function reportCdn(blocking)
+ pcall(function()
+ local newCdnSuccess = settings().Diagnostics.CdnSuccessCount
+ local newCdnFailure = settings().Diagnostics.CdnFailureCount
+ local successDelta = newCdnSuccess - cdnSuccess
+ local failureDelta = newCdnFailure - cdnFailure
+ cdnSuccess = newCdnSuccess
+ cdnFailure = newCdnFailure
+ if successDelta > 0 or failureDelta > 0 then
+ game:HttpGet("http://www.syntax.eco/Game/Cdn.ashx?source=client&success=" .. successDelta .. "&failure=" .. failureDelta, blocking)
+ end
+ end)
+end
+
+function reportDuration(category, result, duration, blocking,errorType)
+ if not errorType then
+ errorType = ''
+ end
+ local platform = settings().Diagnostics.OsPlatform
+ local bytesReceived = -1
+ if stats().Network:getChildren()[2] ~= nil then
+ bytesReceived = stats().Network:getChildren()[2].Stats.totalBytesReceived:GetValue()
+ end
+ pcall(function() game:HttpGet("http://www.syntax.eco/Game/JoinRate.ashx?st=0&i=0&p=-1&c=" .. category .. "&r=" .. result .. "&d=" .. (math.floor(duration*1000)) .. "&b=" .. bytesReceived .. "&ip=localhost&errorType=" .. errorType .. "&platform=" .. platform, blocking) end)
+end
+-- arguments ---------------------------------------
+local threadSleepTime = ...
+
+if threadSleepTime==nil then
+ threadSleepTime = 15
+end
+
+local test = true
+
+print("! Joining game '' place -1 at localhost")
+local closeConnection = game.Close:connect(function()
+ if 0 then
+ reportCdn(true)
+ if not connectResolved then
+ local duration = tick() - startTime;
+ reportDuration("GameConnect", "Failure", duration, true)
+ elseif (not loadResolved) or (not joinResolved) then
+ local duration = tick() - startTime;
+ if not loadResolved then
+ loadResolved = true
+ reportDuration("GameLoad","Cancel", duration, true)
+ end
+ if not joinResolved then
+ joinResolved = true
+ reportDuration("GameJoin","Cancel", duration, true)
+ end
+ elseif not playResolved then
+ local duration = tick() - playStartTime;
+ playResolved = true
+ reportDuration("GameDuration","Success", duration, true)
+ end
+ pcall(function() game:HttpGet("&disconnect=true", true) end)
+ if true then pcall(function() game:HttpPost("https://api.syntax.eco/auth/invalidate", "invalidate") end) end
+ end
+end)
+
+game:GetService("ChangeHistoryService"):SetEnabled(false)
+game:GetService("ContentProvider"):SetThreadPool(16)
+game:GetService("InsertService"):SetBaseSetsUrl("http://www.syntax.eco/Game/Tools/InsertAsset.ashx?nsets=10&type=base")
+game:GetService("InsertService"):SetUserSetsUrl("http://www.syntax.eco/Game/Tools/InsertAsset.ashx?nsets=20&type=user&userid=%d")
+game:GetService("InsertService"):SetCollectionUrl("http://www.syntax.eco/Game/Tools/InsertAsset.ashx?sid=%d")
+game:GetService("InsertService"):SetAssetUrl("http://www.syntax.eco/Asset/?id=%d")
+game:GetService("InsertService"):SetAssetVersionUrl("http://www.syntax.eco/Asset/?assetversionid=%d")
+
+pcall(function() game:GetService("SocialService"):SetFriendUrl("http://www.syntax.eco/Game/LuaWebService/HandleSocialRequest.ashx?method=IsFriendsWith&playerid=%d&userid=%d") end)
+pcall(function() game:GetService("SocialService"):SetBestFriendUrl("http://www.syntax.eco/Game/LuaWebService/HandleSocialRequest.ashx?method=IsBestFriendsWith&playerid=%d&userid=%d") end)
+pcall(function() game:GetService("SocialService"):SetGroupUrl("http://www.syntax.eco/Game/LuaWebService/HandleSocialRequest.ashx?method=IsInGroup&playerid=%d&groupid=%d") end)
+pcall(function() game:GetService("SocialService"):SetGroupRankUrl("http://www.syntax.eco/Game/LuaWebService/HandleSocialRequest.ashx?method=GetGroupRank&playerid=%d&groupid=%d") end)
+pcall(function() game:GetService("SocialService"):SetGroupRoleUrl("http://www.syntax.eco/Game/LuaWebService/HandleSocialRequest.ashx?method=GetGroupRole&playerid=%d&groupid=%d") end)
+pcall(function() game:GetService("GamePassService"):SetPlayerHasPassUrl("http://www.syntax.eco/Game/GamePass/GamePassHandler.ashx?Action=HasPass&UserID=%d&PassID=%d") end)
+pcall(function() game:GetService("MarketplaceService"):SetProductInfoUrl("https://api.syntax.eco/marketplace/productinfo?assetId=%d") end)
+pcall(function() game:GetService("MarketplaceService"):SetPlayerOwnsAssetUrl("https://api.syntax.eco/ownership/hasasset?userId=%d&assetId=%d") end)
+pcall(function() game:SetCreatorID(0, Enum.CreatorType.User) end)
+
+-- Bubble chat. This is all-encapsulated to allow us to turn it off with a config setting
+pcall(function() game:GetService("Players"):SetChatStyle(Enum.ChatStyle.Classic) end)
+
+local waitingForCharacter = false
+local waitingForCharacterGuid = "26c3de03-3381-4ab6-8e60-e415fa757eba";
+pcall( function()
+ if settings().Network.MtuOverride == 0 then
+ settings().Network.MtuOverride = 1400
+ end
+end)
+
+
+-- globals -----------------------------------------
+
+client = game:GetService("NetworkClient")
+visit = game:GetService("Visit")
+
+-- functions ---------------------------------------
+function ifSeleniumThenSetCookie(key, value)
+ if false then
+ game:GetService("CookiesService"):SetCookieValue(key, value)
+ end
+end
+
+function setMessage(message)
+ -- todo: animated "..."
+ if not false then
+ game:SetMessage(message)
+ else
+ -- hack, good enought for now
+ game:SetMessage("Teleporting ...")
+ end
+end
+setMessage("Connecting to SYNTAX...")
+function showErrorWindow(message, errorType, errorCategory)
+ if 0 then
+ if (not loadResolved) or (not joinResolved) then
+ local duration = tick() - startTime;
+ if not loadResolved then
+ loadResolved = true
+ reportDuration("GameLoad","Failure", duration, false,errorType)
+ end
+ if not joinResolved then
+ joinResolved = true
+ reportDuration("GameJoin",errorCategory, duration, false,errorType)
+ end
+
+ pcall(function() game:HttpGet("?FilterName=Type&FilterValue=" .. errorType .. "&Type=JoinFailure", false) end)
+ elseif not playResolved then
+ local duration = tick() - playStartTime;
+ playResolved = true
+ reportDuration("GameDuration",errorCategory, duration, false,errorType)
+
+ pcall(function() game:HttpGet("?FilterName=Type&FilterValue=" .. errorType .. "&Type=GameDisconnect", false) end)
+ end
+ end
+
+ game:SetMessage(message)
+end
+
+function registerPlay(key)
+ return
+end
+
+function analytics(name)
+ return
+end
+
+function analyticsGuid(name, guid)
+ return
+end
+
+function reportError(err, message)
+ print("***ERROR*** " .. err)
+ if not test then visit:SetUploadUrl("") end
+ client:Disconnect()
+ wait(4)
+ showErrorWindow("Error: " .. err, message, "Other")
+end
+
+-- called when the client connection closes
+function onDisconnection(peer, lostConnection)
+ if lostConnection then
+ if waitingForCharacter then analyticsGuid("Waiting for Character Lost Connection",waitingForCharacterGuid) end
+ showErrorWindow("You have lost the connection to the game", "LostConnection", "LostConnection")
+ else
+ if waitingForCharacter then analyticsGuid("Waiting for Character Game Shutdown",waitingForCharacterGuid) end
+ showErrorWindow("This game has shut down", "Kick", "Kick")
+ end
+ pcall(function() game:HttpGet("&disconnect=true", true) end)
+ if true then pcall(function() game:HttpPost("https://api.syntax.eco/auth/invalidate", "invalidate") end) end
+end
+
+function requestCharacter(replicator)
+
+ -- prepare code for when the Character appears
+ local connection
+ connection = player.Changed:connect(function (property)
+ if property=="Character" then
+ game:ClearMessage()
+ waitingForCharacter = false
+ analyticsGuid("Waiting for Character Success", waitingForCharacterGuid)
+
+ connection:disconnect()
+
+ if 0 then
+ if not joinResolved then
+ local duration = tick() - startTime;
+ joinResolved = true
+ reportDuration("GameJoin","Success", duration, false)
+
+ playStartTime = tick()
+ playResolved = false
+ end
+ end
+ end
+ end)
+
+ setMessage("Requesting character")
+
+ if 0 and not loadResolved then
+ local duration = tick() - startTime;
+ loadResolved = true
+ reportDuration("GameLoad","Success", duration, false)
+ end
+
+ local success, err = pcall(function()
+ replicator:RequestCharacter()
+ setMessage("Waiting for character")
+ waitingForCharacter = true
+ analyticsGuid("Waiting for Character Begin",waitingForCharacterGuid);
+ end)
+ if not success then
+ reportError(err,"W4C")
+ return
+ end
+end
+
+-- called when the client connection is established
+function onConnectionAccepted(url, replicator)
+ connectResolved = true
+ --reportDuration("GameConnect", "Success", tick() - startTime, false)
+
+ local waitingForMarker = true
+
+ local success, err = pcall(function()
+ if not test then
+ visit:SetPing("", 300)
+ end
+
+ if not false then
+ game:SetMessageBrickCount()
+ else
+ setMessage("Teleporting ...")
+ end
+
+ replicator.Disconnection:connect(onDisconnection)
+
+ -- Wait for a marker to return before creating the Player
+ local marker = replicator:SendMarker()
+
+ marker.Received:connect(function()
+ waitingForMarker = false
+ requestCharacter(replicator)
+ end)
+ end)
+
+ if not success then
+ reportError(err,"ConnectionAccepted")
+ return
+ end
+
+ -- TODO: report marker progress
+
+ while waitingForMarker do
+ workspace:ZoomToExtents()
+ wait(0.5)
+ end
+end
+
+-- called when the client connection fails
+function onConnectionFailed(_, error)
+ showErrorWindow("Failed to connect to the Game. (ID=" .. error .. ")", "ID" .. error, "Other")
+end
+
+-- called when the client connection is rejected
+function onConnectionRejected()
+ connectionFailed:disconnect()
+ showErrorWindow("This game is not available. Please try another", "WrongVersion", "WrongVersion")
+end
+
+idled = false
+function onPlayerIdled(time)
+ if time > 20*60 then
+ showErrorWindow(string.format("You were disconnected for being idle %d minutes", time/60), "Idle", "Idle")
+ client:Disconnect()
+ if not idled then
+ idled = true
+ end
+ end
+end
+
+
+-- main ------------------------------------------------------------
+
+--analytics("Start Join Script")
+
+--ifSeleniumThenSetCookie("SeleniumTest1", "Started join script")
+
+pcall(function() settings().Diagnostics:LegacyScriptMode() end)
+local success, err = pcall(function()
+
+ game:SetRemoteBuildMode(true)
+
+ setMessage("Connecting to SYNTAX...")
+ client.ConnectionAccepted:connect(onConnectionAccepted)
+ client.ConnectionRejected:connect(onConnectionRejected)
+ connectionFailed = client.ConnectionFailed:connect(onConnectionFailed)
+ client.Ticket = ""
+ ifSeleniumThenSetCookie("SeleniumTest2", "Successfully connected to server")
+
+ playerConnectSucces, player = pcall(function() return client:PlayerConnect(0, "localhost", 53640, 0, threadSleepTime) end)
+ if not playerConnectSucces then
+ --Old player connection scheme
+ player = game:GetService("Players"):CreateLocalPlayer(0)
+ analytics("Created Player")
+ client:Connect("localhost", 53640, 0, threadSleepTime)
+ else
+ analytics("Created Player")
+ end
+
+ pcall(function()
+ registerPlay("rbx_evt_ftp")
+ delay(60*5, function() registerPlay("rbx_evt_fmp") end)
+ end)
+
+ -- negotiate an auth token
+ if true then
+ pcall(function() game:HttpPost("https://api.syntax.eco/auth/negotiate?ticket=", "negotiate") end)
+ delay(300, function()
+ while true do
+ pcall(function() game:HttpPost("https://api.syntax.eco/auth/renew", "renew") end)
+ wait(300)
+ end
+ end)
+ end
+
+ player:SetSuperSafeChat(false)
+ pcall(function() player:SetUnder13(false) end)
+ pcall(function() player:SetMembershipType(Enum.MembershipType.OutrageousBuildersClub) end)
+ pcall(function() player:SetAccountAge(365) end)
+ player.Idled:connect(onPlayerIdled)
+
+ -- Overriden
+ onPlayerAdded(player)
+
+ pcall(function() player.Name = [========[Player]========] end)
+ player.CharacterAppearance = "http://www.syntax.eco/Asset/CharacterFetch.ashx?userId=1"
+ if not test then visit:SetUploadUrl("")end
+
+ analytics("Connect Client")
+
+end)
+
+if not success then
+ reportError(err,"CreatePlayer")
+end
+
+--[[
+ifSeleniumThenSetCookie("SeleniumTest3", "Successfully created player")
+
+if not test then
+ -- TODO: Async get?
+ loadfile("")("", -1, 0)
+end
+
+if 0 then
+ delay(60*5, function()
+ while true do
+ reportCdn(false)
+ wait(60*5)
+ end
+ end)
+ local cpTime = 30
+ delay(cpTime, function()
+ while cpTime <= 480 do
+ reportContentProvider(cpTime, game:GetService("ContentProvider").RequestQueueSize, false)
+ wait(cpTime)
+ cpTime = cpTime * 2
+ end
+ end)
+end
+]]
+
+pcall(function() game:SetScreenshotInfo("") end)
+pcall(function() game:SetVideoInfo('GamesROBLOX, video, free game, online virtual world') end)
+-- use single quotes here because the video info string may have unescaped double quotes
+--[[
+analytics("Join Finished")
+
+ifSeleniumThenSetCookie("SeleniumTest4", "Finished join")
+]]
\ No newline at end of file
diff --git a/app/files/Pants.rbxmx b/app/files/Pants.rbxmx
new file mode 100644
index 0000000..8f151da
--- /dev/null
+++ b/app/files/Pants.rbxmx
@@ -0,0 +1,13 @@
+
+ null
+ nil
+
+
+
+ http://www.syntax.eco/asset/?id={PantsImageId}
+
+ Pants
+ true
+
+
+
\ No newline at end of file
diff --git a/app/files/Shirt.rbxmx b/app/files/Shirt.rbxmx
new file mode 100644
index 0000000..d6ca36a
--- /dev/null
+++ b/app/files/Shirt.rbxmx
@@ -0,0 +1,13 @@
+
+ null
+ nil
+
+
+
+ http://www.syntax.eco/asset/?id={ShirtImageId}
+
+ Shirt
+ true
+
+
+
\ No newline at end of file
diff --git a/app/files/ShutdownServer.lua b/app/files/ShutdownServer.lua
new file mode 100644
index 0000000..b49473b
--- /dev/null
+++ b/app/files/ShutdownServer.lua
@@ -0,0 +1,37 @@
+local AuthToken, PlaceId = ...
+local Players = game:GetService("Players")
+
+warn("Server shutdown requested by website")
+
+local function ReportServerShutdown()
+ local success, message = pcall(function()
+ local MessagePayload = HttpService:JSONEncode({
+ ["AuthToken"] = AuthToken,
+ ["JobId"] = game.JobId,
+ ["PlaceId"] = PlaceId,
+ ["ServerAliveTime"] = workspace.DistributedGameTime
+ })
+ game:HttpPost(BaseURL.."/internal/gameserver/reportshutdown", MessagePayload, false, "application/json")
+ end)
+end
+
+Players.PlayerAdded:Connect(function(player)
+ coroutine.wrap(function()
+ local success, message = pcall(function()
+ player:Kick("This game has shut down.")
+ end)
+ end)()
+end)
+
+for _, Player in pairs(Players:GetPlayers()) do
+ coroutine.wrap(function()
+ local success, message = pcall(function()
+ Player:Kick("This game has shut down.")
+ end)
+ end)()
+end
+ReportServerShutdown()
+ScriptContext.ScriptsDisabled = true
+game:HttpPost("http://127.0.0.1:3000/CloseJob?RCCReturnAuth="..AuthToken, HttpService:JSONEncode({
+ ["jobid"] = game.JobId
+}), false, "application/json")
diff --git a/app/files/Studio.lua b/app/files/Studio.lua
new file mode 100644
index 0000000..c2f1740
--- /dev/null
+++ b/app/files/Studio.lua
@@ -0,0 +1,27 @@
+-- Setup studio cmd bar & load core scripts
+local baseUrl = "http://www.syntax.eco"
+
+pcall(function() game:GetService("InsertService"):SetFreeModelUrl(baseUrl.."/Game/Tools/InsertAsset.ashx?type=fm&q=%s&pg=%d&rs=%d") end)
+pcall(function() game:GetService("InsertService"):SetFreeDecalUrl(baseUrl.."/Game/Tools/InsertAsset.ashx?type=fd&q=%s&pg=%d&rs=%d") end)
+
+game:GetService("ScriptInformationProvider"):SetAssetUrl(baseUrl.."/Asset/")
+game:GetService("InsertService"):SetBaseSetsUrl(baseUrl.."/Game/Tools/InsertAsset.ashx?nsets=10&type=base")
+game:GetService("InsertService"):SetUserSetsUrl(baseUrl.."/Game/Tools/InsertAsset.ashx?nsets=20&type=user&userid=%d")
+game:GetService("InsertService"):SetCollectionUrl(baseUrl.."/Game/Tools/InsertAsset.ashx?sid=%d")
+game:GetService("InsertService"):SetAssetUrl(baseUrl.."/Asset/?id=%d")
+game:GetService("InsertService"):SetAssetVersionUrl(baseUrl.."/Asset/?assetversionid=%d")
+
+pcall(function() game:GetService("SocialService"):SetFriendUrl(baseUrl.."/Game/LuaWebService/HandleSocialRequest.ashx?method=IsFriendsWith&playerid=%d&userid=%d") end)
+pcall(function() game:GetService("SocialService"):SetBestFriendUrl(baseUrl.."/Game/LuaWebService/HandleSocialRequest.ashx?method=IsBestFriendsWith&playerid=%d&userid=%d") end)
+pcall(function() game:GetService("SocialService"):SetGroupUrl(baseUrl.."/Game/LuaWebService/HandleSocialRequest.ashx?method=IsInGroup&playerid=%d&groupid=%d") end)
+pcall(function() game:GetService("SocialService"):SetGroupRankUrl(baseUrl.."/Game/LuaWebService/HandleSocialRequest.ashx?method=GetGroupRank&playerid=%d&groupid=%d") end)
+pcall(function() game:GetService("SocialService"):SetGroupRoleUrl(baseUrl.."/Game/LuaWebService/HandleSocialRequest.ashx?method=GetGroupRole&playerid=%d&groupid=%d") end)
+pcall(function() game:GetService("GamePassService"):SetPlayerHasPassUrl(baseUrl.."/Game/GamePass/GamePassHandler.ashx?Action=HasPass&UserID=%d&PassID=%d") end)
+pcall(function() game:GetService("MarketplaceService"):SetProductInfoUrl(baseUrl.."/marketplace/productinfo?assetId=%d") end)
+pcall(function() game:GetService("MarketplaceService"):SetDevProductInfoUrl(baseUrl.."/marketplace/productDetails?productId=%d") end)
+pcall(function() game:GetService("MarketplaceService"):SetPlayerOwnsAssetUrl(baseUrl.."/ownership/hasasset?userId=%d&assetId=%d") end)
+
+local result = pcall(function() game:GetService("ScriptContext"):AddStarterScript(37801172) end)
+if not result then
+ pcall(function() game:GetService("ScriptContext"):AddCoreScript(37801172,game:GetService("ScriptContext"),"StarterScript") end)
+end
\ No newline at end of file
diff --git a/app/files/TShirt.rbxmx b/app/files/TShirt.rbxmx
new file mode 100644
index 0000000..895a1b8
--- /dev/null
+++ b/app/files/TShirt.rbxmx
@@ -0,0 +1,13 @@
+
+ null
+ nil
+
+
+
+ http://www.syntax.eco/asset/?id={TShirtImageId}
+
+ Shirt Graphic
+ true
+
+
+
\ No newline at end of file
diff --git a/app/files/Visit.lua b/app/files/Visit.lua
new file mode 100644
index 0000000..e1085af
--- /dev/null
+++ b/app/files/Visit.lua
@@ -0,0 +1,132 @@
+function ifSeleniumThenSetCookie(key, value)
+ if false then
+ game:GetService("CookiesService"):SetCookieValue(key, value)
+ end
+end
+
+ifSeleniumThenSetCookie("SeleniumTest1", "Inside the visit lua script")
+
+pcall(function() game:SetPlaceID({PlaceId}) end)
+pcall(function() game:SetUniverseId({UniverseId}) end)
+
+visit = game:GetService("Visit")
+
+local message = Instance.new("Message")
+message.Parent = workspace
+message.archivable = false
+
+game:GetService("ScriptInformationProvider"):SetAssetUrl("https://www.syntax.eco/Asset/")
+game:GetService("ContentProvider"):SetThreadPool(16)
+pcall(function() game:GetService("InsertService"):SetFreeModelUrl("https://www.syntax.eco/Game/Tools/InsertAsset.ashx?type=fm&q=%s&pg=%d&rs=%d") end) -- Used for free model search (insert tool)
+pcall(function() game:GetService("InsertService"):SetFreeDecalUrl("https://www.syntax.eco/Game/Tools/InsertAsset.ashx?type=fd&q=%s&pg=%d&rs=%d") end) -- Used for free decal search (insert tool)
+
+ifSeleniumThenSetCookie("SeleniumTest2", "Set URL service")
+
+settings().Diagnostics:LegacyScriptMode()
+
+game:GetService("InsertService"):SetBaseSetsUrl("https://www.syntax.eco/Game/Tools/InsertAsset.ashx?nsets=10&type=base")
+game:GetService("InsertService"):SetUserSetsUrl("https://www.syntax.eco/Game/Tools/InsertAsset.ashx?nsets=20&type=user&userid=%d")
+game:GetService("InsertService"):SetCollectionUrl("https://www.syntax.eco/Game/Tools/InsertAsset.ashx?sid=%d")
+game:GetService("InsertService"):SetAssetUrl("https://www.syntax.eco/Asset/?id=%d")
+game:GetService("InsertService"):SetAssetVersionUrl("https://www.syntax.eco/Asset/?assetversionid=%d")
+
+pcall(function() game:GetService("SocialService"):SetFriendUrl("https://www.syntax.eco/Game/LuaWebService/HandleSocialRequest.ashx?method=IsFriendsWith&playerid=%d&userid=%d") end)
+pcall(function() game:GetService("SocialService"):SetBestFriendUrl("https://www.syntax.eco/Game/LuaWebService/HandleSocialRequest.ashx?method=IsBestFriendsWith&playerid=%d&userid=%d") end)
+pcall(function() game:GetService("SocialService"):SetGroupUrl("https://www.syntax.eco/Game/LuaWebService/HandleSocialRequest.ashx?method=IsInGroup&playerid=%d&groupid=%d") end)
+pcall(function() game:GetService("SocialService"):SetGroupRankUrl("https://www.syntax.eco/Game/LuaWebService/HandleSocialRequest.ashx?method=GetGroupRank&playerid=%d&groupid=%d") end)
+pcall(function() game:GetService("SocialService"):SetGroupRoleUrl("https://www.syntax.eco/Game/LuaWebService/HandleSocialRequest.ashx?method=GetGroupRole&playerid=%d&groupid=%d") end)
+pcall(function() game:GetService("GamePassService"):SetPlayerHasPassUrl("https://www.syntax.eco/Game/GamePass/GamePassHandler.ashx?Action=HasPass&UserID=%d&PassID=%d") end)
+pcall(function() game:GetService("MarketplaceService"):SetProductInfoUrl("https://www.syntax.eco/marketplace/productinfo?assetId=%d") end)
+pcall(function() game:GetService("MarketplaceService"):SetDevProductInfoUrl("https://www.syntax.eco/marketplace/productDetails?productId=%d") end)
+pcall(function() game:GetService("MarketplaceService"):SetPlayerOwnsAssetUrl("https://www.syntax.eco/ownership/hasasset?userId=%d&assetId=%d") end)
+pcall(function() game:SetCreatorID(0, Enum.CreatorType.User) end)
+
+ifSeleniumThenSetCookie("SeleniumTest3", "Set creator ID")
+
+pcall(function() game:SetScreenshotInfo("") end)
+pcall(function() game:SetVideoInfo("") end)
+
+ifSeleniumThenSetCookie("SeleniumTest4", "Exiting SingleplayerSharedScript")-- SingleplayerSharedScript.lua inserted here --
+
+pcall(function() settings().Rendering.EnableFRM = true end)
+pcall(function() settings()["Task Scheduler"].PriorityMethod = Enum.PriorityMethod.AccumulatedError end)
+
+game:GetService("ChangeHistoryService"):SetEnabled(false)
+pcall(function() game:GetService("Players"):SetBuildUserPermissionsUrl("https://www.syntax.eco/Game/BuildActionPermissionCheck.ashx?assetId=0&userId=%d&isSolo=true") end)
+
+workspace:SetPhysicsThrottleEnabled(true)
+
+local addedBuildTools = false
+local screenGui = game:GetService("CoreGui"):FindFirstChild("RobloxGui")
+
+local inStudio = true
+
+function doVisit()
+ message.Text = "Loading Game"
+ if false then
+ if false then
+ success, err = pcall(function() game:Load("") end)
+ if not success then
+ message.Text = "Could not teleport"
+ return
+ end
+ end
+ else
+ if false then
+ game:Load("")
+ pcall(function() visit:SetUploadUrl("") end)
+ else
+ pcall(function() visit:SetUploadUrl("") end)
+ end
+ end
+
+ message.Text = "Running"
+ game:GetService("RunService"):Run()
+
+ message.Text = "Creating Player"
+ player = game:GetService("Players"):CreateLocalPlayer(0)
+ player.CharacterAppearance = "https://www.syntax.eco/Asset/CharacterFetch.ashx?userId={UserId}&placeId={PlaceId}"
+ local propExists, canAutoLoadChar = false
+ propExists = pcall(function() canAutoLoadChar = game.Players.CharacterAutoLoads end)
+
+ if (propExists and canAutoLoadChar) or (not propExists) then
+ player:LoadCharacter()
+ end
+
+ message.Text = "Setting GUI"
+ player:SetSuperSafeChat(false)
+ pcall(function() player:SetUnder13(false) end)
+ pcall(function() player:SetMembershipType(None) end)
+ pcall(function() player:SetAccountAge(0) end)
+end
+
+success, err = pcall(doVisit)
+
+if not inStudio and not addedBuildTools then
+ local playerName = Instance.new("StringValue")
+ playerName.Name = "PlayerName"
+ playerName.Value = player.Name
+ playerName.RobloxLocked = true
+ playerName.Parent = screenGui
+
+ pcall(function() game:GetService("ScriptContext"):AddCoreScript(59431535,screenGui,"BuildToolsScript") end)
+ addedBuildTools = true
+end
+
+if success then
+ message.Parent = nil
+else
+ print(err)
+ if not inStudio then
+ if false then
+ pcall(function() visit:SetUploadUrl("") end)
+ end
+ end
+ wait(5)
+ message.Text = "Error on visit: " .. err
+ if not inStudio then
+ if false then
+ game:HttpPost("https://www.syntax.eco/Error/Lua.ashx", "Visit.lua: " .. err)
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/models/admin_permissions.py b/app/models/admin_permissions.py
new file mode 100644
index 0000000..d93f594
--- /dev/null
+++ b/app/models/admin_permissions.py
@@ -0,0 +1,16 @@
+from app.extensions import db
+
+class AdminPermissions(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, nullable=False, unique=True, autoincrement=True)
+ userid = db.Column(db.BigInteger, nullable=False, index=True)
+ permission = db.Column(db.String(128), nullable=False, index=True)
+
+ def __init__(self, userid, permission):
+ self.userid = userid
+ self.permission = permission
+
+ def __repr__(self):
+ return "".format(
+ userid=self.userid,
+ permission=self.permission
+ )
\ No newline at end of file
diff --git a/app/models/asset.py b/app/models/asset.py
new file mode 100644
index 0000000..7398a40
--- /dev/null
+++ b/app/models/asset.py
@@ -0,0 +1,83 @@
+from app.extensions import db
+from app.enums.AssetType import AssetType
+from sqlalchemy import Enum
+from datetime import datetime
+
+class Asset(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, unique=True, nullable=False, autoincrement=True)
+ roblox_asset_id = db.Column(db.BigInteger, nullable=True, default=None, index=True) # Roblox asset ID
+ name = db.Column(db.Text, nullable=False) # Asset name
+ description = db.Column(db.String(4096), nullable=False) # Asset description
+ created_at = db.Column(db.DateTime, nullable=False) # Asset creation date
+ updated_at = db.Column(db.DateTime, nullable=False) # Asset last update date
+ asset_type = db.Column(Enum(AssetType), nullable=False, index=True) # Asset type (e.g. shirt, pants, etc.)
+ asset_genre = db.Column(db.SmallInteger, nullable=False) # Asset genre (e.g. all, town and city, etc.)
+ creator_type = db.Column(db.SmallInteger, nullable=False) # Asset creator type (e.g. user, group, etc.)
+ creator_id = db.Column(db.BigInteger, nullable=False, index=True) # Asset creator ID
+
+ moderation_status = db.Column(db.SmallInteger, nullable=False, index=True) # Asset moderation status ( 0 = Approved, 1 = Pending, 2 = Declined )
+
+ # Economy
+ is_for_sale = db.Column(db.Boolean, nullable=False, default=False, index=True) # Is the asset for sale?
+ price_robux = db.Column(db.BigInteger, nullable=False, default=0) # Asset price in Robux
+ price_tix = db.Column(db.BigInteger, nullable=False, default=0) # Asset price in Tix
+ is_limited = db.Column(db.Boolean, nullable=False, default=False, index=True) # Is the asset limited?
+ is_limited_unique = db.Column(db.Boolean, nullable=False, default=False) # Is the asset limited unique?
+ serial_count = db.Column(db.BigInteger, nullable=False, default=0) # Asset serial count
+ sale_count = db.Column(db.BigInteger, nullable=False, default=0) # Asset sale count
+ offsale_at = db.Column(db.DateTime, nullable=True, default=None) # Asset offsale date
+
+ def __init__(
+ self,
+ roblox_asset_id=None,
+ name="Asset",
+ description="",
+ created_at=None,
+ updated_at=None,
+ asset_type=AssetType.Image,
+ asset_genre=0,
+ creator_type=0,
+ creator_id=1,
+ moderation_status=1,
+
+ is_for_sale=False,
+ price_robux=0,
+ price_tix=0,
+ is_limited=False,
+ is_limited_unique=False,
+ serial_count=0,
+ sale_count=0,
+ offsale_at=None,
+
+ force_asset_id=None
+ ):
+ if created_at is None:
+ created_at = datetime.utcnow()
+ if updated_at is None:
+ updated_at = datetime.utcnow()
+
+ self.roblox_asset_id = roblox_asset_id
+ self.name = name
+ self.description = description
+ self.created_at = created_at
+ self.updated_at = updated_at
+ self.asset_type = asset_type
+ self.asset_genre = asset_genre
+ self.creator_type = creator_type
+ self.creator_id = creator_id
+ self.moderation_status = moderation_status
+
+ self.is_for_sale = is_for_sale
+ self.price_robux = price_robux
+ self.price_tix = price_tix
+ self.is_limited = is_limited
+ self.is_limited_unique = is_limited_unique
+ self.serial_count = serial_count
+ self.sale_count = sale_count
+ self.offsale_at = offsale_at
+
+ if force_asset_id is not None:
+ self.id = force_asset_id
+
+ def __repr__(self):
+ return f""
\ No newline at end of file
diff --git a/app/models/asset_favorite.py b/app/models/asset_favorite.py
new file mode 100644
index 0000000..f9adeb8
--- /dev/null
+++ b/app/models/asset_favorite.py
@@ -0,0 +1,13 @@
+from app.extensions import db
+
+class AssetFavorite( db.Model ):
+ id = db.Column( db.BigInteger, primary_key=True, autoincrement=True )
+ assetid = db.Column( db.BigInteger, nullable=False, index=True )
+ userid = db.Column( db.BigInteger, nullable=False, index=True )
+
+ def __init__(self, assetid : int, userid : int):
+ self.assetid = assetid
+ self.userid = userid
+
+ def __repr__(self):
+ return f""
\ No newline at end of file
diff --git a/app/models/asset_moderation_link.py b/app/models/asset_moderation_link.py
new file mode 100644
index 0000000..e001d5a
--- /dev/null
+++ b/app/models/asset_moderation_link.py
@@ -0,0 +1,27 @@
+from app.extensions import db
+
+# This is used for the asset moderation system to link two assets together, eg. a Image and Shirt
+# So the moderator does not need to approve both assets
+
+class AssetModerationLink(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, unique=True, nullable=False, autoincrement=True)
+ ParentAssetId = db.Column(db.BigInteger, db.ForeignKey('asset.id'), nullable=False, index=True) # Parent asset ID, eg. Shirt
+ ChildAssetId = db.Column(db.BigInteger, db.ForeignKey('asset.id'), nullable=False, index=True) # Child asset ID eg. Image
+
+ ParentAsset = db.relationship("Asset", foreign_keys=[ParentAssetId], uselist=False, lazy="joined")
+ ChildAsset = db.relationship("Asset", foreign_keys=[ChildAssetId], uselist=False, lazy="joined")
+
+ def __init__(
+ self,
+ ParentAssetId,
+ ChildAssetId
+ ):
+ self.ParentAssetId = ParentAssetId
+ self.ChildAssetId = ChildAssetId
+
+ def __repr__(self):
+ return "".format(
+ id=self.id,
+ ParentAssetId=self.ParentAssetId,
+ ChildAssetId=self.ChildAssetId
+ )
\ No newline at end of file
diff --git a/app/models/asset_rap.py b/app/models/asset_rap.py
new file mode 100644
index 0000000..547a47a
--- /dev/null
+++ b/app/models/asset_rap.py
@@ -0,0 +1,23 @@
+from app.extensions import db
+from datetime import datetime
+
+class AssetRap(db.Model):
+ assetid = db.Column(db.BigInteger, primary_key=True, nullable=False, unique=True)
+ rap = db.Column(db.BigInteger, nullable=False, default=0)
+ updated = db.Column(db.DateTime, nullable=False)
+
+ def __init__(
+ self,
+ assetid,
+ rap=0
+ ):
+ self.assetid = assetid
+ self.rap = rap
+ self.updated = datetime.utcnow()
+
+ def __repr__(self):
+ return "".format(
+ assetid=self.assetid,
+ rap=self.rap,
+ updated=self.updated
+ )
\ No newline at end of file
diff --git a/app/models/asset_thumbnail.py b/app/models/asset_thumbnail.py
new file mode 100644
index 0000000..e098d15
--- /dev/null
+++ b/app/models/asset_thumbnail.py
@@ -0,0 +1,36 @@
+from app.extensions import db
+
+class AssetThumbnail(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, unique=True, nullable=False, autoincrement=True)
+ asset_id = db.Column(db.BigInteger, db.ForeignKey('asset.id'), nullable=False, index=True) # Asset ID
+ asset_version_id = db.Column(db.BigInteger, nullable=False) # Asset version ID
+ content_hash = db.Column(db.String(512), nullable=False) # Asset thumbnail content hash
+ created_at = db.Column(db.DateTime, nullable=False) # Asset thumbnail creation date
+ updated_at = db.Column(db.DateTime, nullable=False) # Asset thumbnail last update date
+ moderation_status = db.Column(db.SmallInteger, nullable=False) # Asset thumbnail moderation status ( 0 = Approved, 1 = Pending, 2 = Declined )
+
+ asset = db.relationship('Asset', backref=db.backref('thumbnails', lazy=True, uselist=True), uselist=False)
+ def __init__(
+ self,
+ asset_id,
+ asset_version_id,
+ content_hash,
+ created_at,
+ moderation_status
+ ):
+ self.asset_id = asset_id
+ self.asset_version_id = asset_version_id
+ self.content_hash = content_hash
+ self.created_at = created_at
+ self.updated_at = created_at
+ self.moderation_status = moderation_status
+
+ def __repr__(self):
+ return "".format(
+ asset_id=self.asset_id,
+ asset_version_id=self.asset_version_id,
+ content_hash=self.content_hash,
+ created_at=self.created_at,
+ updated_at=self.updated_at,
+ moderation_status=self.moderation_status
+ )
\ No newline at end of file
diff --git a/app/models/asset_version.py b/app/models/asset_version.py
new file mode 100644
index 0000000..d305f1d
--- /dev/null
+++ b/app/models/asset_version.py
@@ -0,0 +1,35 @@
+from app.extensions import db
+
+class AssetVersion(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, unique=True, nullable=False, autoincrement=True)
+ asset_id = db.Column(db.BigInteger, nullable=False, index=True)
+ version = db.Column(db.BigInteger, nullable=False)
+ content_hash = db.Column(db.String(512), nullable=False)
+ created_at = db.Column(db.DateTime, nullable=False)
+ updated_at = db.Column(db.DateTime, nullable=False)
+ uploaded_by = db.Column(db.BigInteger, db.ForeignKey('user.id'), nullable=True, index=True)
+
+ def __init__(
+ self,
+ asset_id,
+ version,
+ content_hash,
+ created_at,
+ uploaded_by=None
+ ):
+ self.asset_id = asset_id
+ self.version = version
+ self.content_hash = content_hash
+ self.created_at = created_at
+ self.updated_at = created_at
+ self.uploaded_by = uploaded_by
+
+ def __repr__(self):
+ return "".format(
+ id=self.id,
+ asset_id=self.asset_id,
+ version=self.version,
+ content_hash=self.content_hash,
+ created_at=self.created_at,
+ updated_at=self.updated_at
+ )
\ No newline at end of file
diff --git a/app/models/asset_votes.py b/app/models/asset_votes.py
new file mode 100644
index 0000000..0721622
--- /dev/null
+++ b/app/models/asset_votes.py
@@ -0,0 +1,15 @@
+from app.extensions import db
+
+class AssetVote( db.Model ):
+ id = db.Column( db.BigInteger, primary_key=True )
+ assetid = db.Column( db.BigInteger, nullable=False, index=True )
+ userid = db.Column( db.BigInteger, nullable=False, index=True )
+ vote = db.Column( db.Boolean, nullable=False, default=True )
+
+ def __init__(self, assetid : int, userid : int, vote : bool):
+ self.assetid = assetid
+ self.userid = userid
+ self.vote = vote
+
+ def __repr__(self):
+ return f""
\ No newline at end of file
diff --git a/app/models/cryptomus_invoice.py b/app/models/cryptomus_invoice.py
new file mode 100644
index 0000000..fed0e3f
--- /dev/null
+++ b/app/models/cryptomus_invoice.py
@@ -0,0 +1,52 @@
+from app.extensions import db
+from datetime import datetime
+from app.enums.CryptomusPaymentStatus import CryptomusPaymentStatus
+
+class CryptomusInvoice(db.Model):
+ id = db.Column( db.Text, primary_key=True, unique=True, nullable=False, index = True )
+ cryptomus_invoice_id = db.Column( db.Text, nullable=False, index = True )
+
+ initiator_id = db.Column( db.Integer, db.ForeignKey('user.id'), nullable=False, index = True )
+
+ required_amount = db.Column( db.Float, nullable=False )
+ paid_amount_usd = db.Column( db.Float, nullable=False, default=0 )
+ currency = db.Column( db.Text, nullable=False )
+ status = db.Column( db.Enum(CryptomusPaymentStatus), nullable=False )
+ is_final = db.Column( db.Boolean, nullable=False, default=False )
+
+ extra_data = db.Column( db.Text, nullable=True )
+
+ created_at = db.Column( db.DateTime, nullable=False )
+ updated_at = db.Column( db.DateTime, nullable=False )
+ expires_at = db.Column( db.DateTime, nullable=False )
+
+ assigned_key = db.Column( db.Text, nullable=True )
+
+ def __init__(
+ self,
+ id : str,
+ cryptomus_invoice_id : str,
+
+ initiator_id : int,
+
+ required_amount : float,
+ currency : str,
+ status : CryptomusPaymentStatus,
+
+ expires_at : datetime,
+ created_at : datetime = None,
+ updated_at : datetime = None,
+ paid_amount_usd : float = 0,
+ extra_data : str = None
+ ):
+ self.id = id
+ self.cryptomus_invoice_id = cryptomus_invoice_id
+ self.initiator_id = initiator_id
+ self.required_amount = required_amount
+ self.currency = currency
+ self.status = status
+ self.expires_at = expires_at
+ self.created_at = created_at if created_at is not None else datetime.utcnow()
+ self.updated_at = updated_at if updated_at is not None else datetime.utcnow()
+ self.paid_amount_usd = paid_amount_usd
+ self.extra_data = extra_data
\ No newline at end of file
diff --git a/app/models/exchange_offer.py b/app/models/exchange_offer.py
new file mode 100644
index 0000000..6a2681d
--- /dev/null
+++ b/app/models/exchange_offer.py
@@ -0,0 +1,54 @@
+from app.extensions import db
+from datetime import datetime, timedelta
+
+class ExchangeOffer(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+ creator_id = db.Column(db.BigInteger, db.ForeignKey('user.id'), nullable=False, index=True)
+ creator = db.relationship('User', backref=db.backref('exchange_offers', lazy=True), foreign_keys=[creator_id], uselist=False)
+
+ offer_value = db.Column(db.BigInteger, nullable=False) # Amount of currency the creator is sending
+ receive_value = db.Column(db.BigInteger, nullable=False) # Amount of currency the creator is receiving
+
+ offer_currency_type = db.Column(db.SmallInteger, nullable=False) # Currency type of the offer value, 0 = Robux, 1 = Tix
+ # Recieve currency type is the opposite of the offer currency type
+
+ reciever_id = db.Column(db.BigInteger, db.ForeignKey('user.id'), nullable=True, index=True) # If the offer is accepted by someone, this will be set to the user's id
+ reciever = db.relationship('User', backref=db.backref('exchange_offers_recieved', lazy=True), foreign_keys=[reciever_id], uselist=False)
+
+ created_at = db.Column(db.DateTime, nullable=False)
+ expires_at = db.Column(db.DateTime, nullable=False)
+
+ ratio = db.Column(db.Float, nullable=False, default=0) # The ratio of the offer value to the receive value
+ worth = db.Column(db.Float, nullable=False, default=0, index = True)
+
+ def __init___(
+ self,
+ creator_id,
+ offer_value,
+ receive_value,
+ offer_currency_type=0, # Robux
+ reciever_id=None,
+ ratio = None,
+ created_at=None,
+ expires_at=None
+ ):
+ if created_at is None:
+ created_at = datetime.utcnow()
+
+ if expires_at is None:
+ expires_at = datetime.utcnow() + timedelta(days=31)
+
+ self.creator_id = creator_id
+ self.offer_value = offer_value
+ self.receive_value = receive_value
+ self.offer_currency_type = offer_currency_type
+ self.reciever_id = reciever_id
+ self.created_at = created_at
+ self.expires_at = expires_at
+
+ if ratio is None:
+ self.ratio = (offer_value / receive_value) if offer_currency_type == 0 else (receive_value / offer_value)
+ else:
+ self.ratio = ratio
+ def __repr__(self):
+ return '' % self.id
\ No newline at end of file
diff --git a/app/models/fflag_group.py b/app/models/fflag_group.py
new file mode 100644
index 0000000..d053ea1
--- /dev/null
+++ b/app/models/fflag_group.py
@@ -0,0 +1,35 @@
+from app.extensions import db
+
+class FflagGroup(db.Model):
+ group_id = db.Column(db.BigInteger, primary_key=True, nullable=False, index=True) # Group ID
+ name = db.Column(db.String(128), nullable=False) # Group name
+ description = db.Column(db.String(512), nullable=False) # Group description
+ created_at = db.Column(db.DateTime, nullable=False) # Group creation date
+ updated_at = db.Column(db.DateTime, nullable=False) # Group last update date
+ enabled = db.Column(db.Boolean, nullable=False) # Group enabled
+ apikey = db.Column(db.String(128), nullable=True) # Group API key ( optional )
+ gameserver_only = db.Column(db.Boolean, nullable=False) # Group is for gameserver only
+
+ def __init__(
+ self,
+ group_id,
+ name,
+ description,
+ created_at,
+ enabled,
+ apikey
+ ):
+ self.group_id = group_id
+ self.name = name
+ self.description = description
+ self.created_at = created_at
+ self.updated_at = created_at
+ self.enabled = enabled
+ self.apikey = apikey
+
+ def __repr__(self):
+ return "".format(
+ group_id=self.group_id,
+ name=self.name,
+ enabled=self.enabled
+ )
\ No newline at end of file
diff --git a/app/models/fflag_value.py b/app/models/fflag_value.py
new file mode 100644
index 0000000..d5504cb
--- /dev/null
+++ b/app/models/fflag_value.py
@@ -0,0 +1,29 @@
+from app.extensions import db
+
+class FflagValue(db.Model):
+ flag_id = db.Column(db.BigInteger, primary_key=True, nullable=False, autoincrement=True) # Flag ID
+ group_id = db.Column(db.BigInteger, nullable=False, index=True) # Group ID
+ name = db.Column(db.String(128), nullable=False) # Flag name
+
+ flag_type = db.Column(db.Integer, nullable=False, default=1) # Flag type ( 1 = bool, 2 = number, 3 = string)
+ flag_value = db.Column(db.Text, nullable=False) # Flag value ( base64 )
+
+ def __init__(
+ self,
+ group_id,
+ name,
+ flag_type,
+ flag_value
+ ):
+ self.group_id = group_id
+ self.name = name
+ self.flag_type = flag_type
+ self.flag_value = flag_value
+
+ def __repr__(self):
+ return "".format(
+ flag_id=self.flag_id,
+ group_id=self.group_id,
+ name=self.name,
+ flag_type=self.flag_type
+ )
diff --git a/app/models/follow_relationship.py b/app/models/follow_relationship.py
new file mode 100644
index 0000000..9c36517
--- /dev/null
+++ b/app/models/follow_relationship.py
@@ -0,0 +1,20 @@
+from app.extensions import db
+from datetime import datetime
+
+class FollowRelationship(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, nullable=False, unique=True, autoincrement=True)
+ followerUserId = db.Column(db.BigInteger, nullable=False, index=True)
+ followeeUserId = db.Column(db.BigInteger, nullable=False, index=True)
+ created = db.Column(db.DateTime, nullable=False)
+
+ def __init__(self, followerUserId, followeeUserId):
+ self.followerUserId = followerUserId
+ self.followeeUserId = followeeUserId
+ self.created = datetime.utcnow()
+
+ def __repr__(self):
+ return "".format(
+ followerUserId=self.followerUserId,
+ followeeUserId=self.followeeUserId,
+ created=self.created
+ )
\ No newline at end of file
diff --git a/app/models/friend_relationship.py b/app/models/friend_relationship.py
new file mode 100644
index 0000000..c5b35cb
--- /dev/null
+++ b/app/models/friend_relationship.py
@@ -0,0 +1,16 @@
+from app.extensions import db
+from datetime import datetime
+
+class FriendRelationship(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True, nullable=False)
+ user_id = db.Column(db.BigInteger, nullable=False, index=True)
+ friend_id = db.Column(db.BigInteger, nullable=False, index=True)
+ created_at = db.Column(db.DateTime, nullable=False)
+
+ def __init__(self, user_id, friend_id):
+ self.user_id = user_id
+ self.friend_id = friend_id
+ self.created_at = datetime.utcnow()
+
+ def __repr__(self):
+ return '' % self.id
\ No newline at end of file
diff --git a/app/models/friend_request.py b/app/models/friend_request.py
new file mode 100644
index 0000000..90257b6
--- /dev/null
+++ b/app/models/friend_request.py
@@ -0,0 +1,17 @@
+from app.extensions import db
+from datetime import datetime
+
+class FriendRequest(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True, nullable=False)
+ requester_id = db.Column(db.BigInteger, db.ForeignKey("user.id") ,nullable=False, index=True) # The person who sent the friend request
+ requestee_id = db.Column(db.BigInteger, nullable=False, index=True) # The person who received the friend request
+ created_at = db.Column(db.DateTime, nullable=False)
+
+ requester = db.relationship("User", foreign_keys=[requester_id], backref="friend_requests_sent", uselist=False)
+
+ def __init__(self, requester_id, requestee_id):
+ self.requester_id = requester_id
+ self.requestee_id = requestee_id
+ self.created_at = datetime.utcnow()
+ def __repr__(self):
+ return '' % self.id
\ No newline at end of file
diff --git a/app/models/game_session_log.py b/app/models/game_session_log.py
new file mode 100644
index 0000000..6611711
--- /dev/null
+++ b/app/models/game_session_log.py
@@ -0,0 +1,36 @@
+from app.extensions import db
+from sqlalchemy.dialects.postgresql import UUID
+from datetime import datetime, timedelta
+
+class GameSessionLog( db.Model ):
+ id = db.Column( db.BigInteger, primary_key = True, autoincrement = True, nullable = False )
+ user_id = db.Column( db.BigInteger, db.ForeignKey( "user.id" ), nullable = False, index=True)
+ serveruuid = db.Column( UUID(as_uuid=True), nullable=False, index=True )
+ joined_at = db.Column( db.DateTime, nullable = False, index = True )
+ left_at = db.Column( db.DateTime, nullable = True )
+ place_id = db.Column( db.BigInteger, nullable = False, index=True )
+
+ def __init__(
+ self,
+ user_id,
+ serveruuid,
+ place_id,
+ joined_at = None,
+ left_at = None
+ ):
+ self.user_id = user_id
+ self.serveruuid = serveruuid
+ self.place_id = place_id
+ self.joined_at = joined_at or datetime.utcnow()
+ self.left_at = left_at
+
+ def __repr__(self):
+ return "".format(
+ user_id=self.user_id,
+ serveruuid=self.serveruuid,
+ place_id=self.place_id,
+ joined_at=self.joined_at,
+ left_at=self.left_at
+ )
+
+
\ No newline at end of file
diff --git a/app/models/gamepass_link.py b/app/models/gamepass_link.py
new file mode 100644
index 0000000..eddd619
--- /dev/null
+++ b/app/models/gamepass_link.py
@@ -0,0 +1,24 @@
+from app.extensions import db
+import datetime
+
+class GamepassLink(db.Model):
+ gamepass_id = db.Column(db.BigInteger, db.ForeignKey("asset.id"), primary_key=True, nullable=False)
+ place_id = db.Column(db.BigInteger, db.ForeignKey("asset.id"), primary_key=True, nullable=False, index=True) # DEPRECATED
+ creator_id = db.Column(db.BigInteger, db.ForeignKey("user.id"), nullable=False, index=True)
+ created_at = db.Column(db.DateTime, nullable=False)
+
+ gamepass = db.relationship("Asset", foreign_keys=[gamepass_id], uselist=False, lazy="joined")
+ place = db.relationship("Asset", foreign_keys=[place_id], uselist=False, lazy="joined")
+ creator = db.relationship("User", foreign_keys=[creator_id], uselist=False, lazy="joined")
+
+ universe_id = db.Column(db.BigInteger, db.ForeignKey("universe.id"), nullable=True, index=True)
+
+ def __init__(self, place_id, gamepass_id, universe_id, creator_id):
+ self.place_id = place_id
+ self.gamepass_id = gamepass_id
+ self.universe_id = universe_id
+ self.creator_id = creator_id
+ self.created_at = datetime.datetime.utcnow()
+
+ def __repr__(self):
+ return '' % self.id
\ No newline at end of file
diff --git a/app/models/gameservers.py b/app/models/gameservers.py
new file mode 100644
index 0000000..6d84ea8
--- /dev/null
+++ b/app/models/gameservers.py
@@ -0,0 +1,45 @@
+from app.extensions import db
+from sqlalchemy.dialects.postgresql import UUID
+import uuid
+
+class GameServer(db.Model):
+ serverId = db.Column(UUID(as_uuid=True), primary_key=True, nullable=False, unique=True, default=uuid.uuid4)
+ serverName = db.Column(db.String(128), nullable=False)
+ serverIP = db.Column(db.String(128), nullable=False)
+ serverPort = db.Column(db.Integer, nullable=False)
+ accessKey = db.Column(db.String(128), nullable=False)
+
+ lastHeartbeat = db.Column(db.DateTime, nullable=True, default=None)
+ heartbeatResponseTime = db.Column(db.Float, nullable=True, default=0)
+ isRCCOnline = db.Column(db.Boolean, nullable=False, default=False)
+ thumbnailQueueSize = db.Column(db.Integer, nullable=False, default=0)
+ RCCmemoryUsage = db.Column(db.BigInteger, nullable=False, default=0)
+
+ allowThumbnailGen = db.Column(db.Boolean, nullable=False, default=True) # If false, the server will not be able to generate thumbnails
+ allowGameServerHost = db.Column(db.Boolean, nullable=False, default=True) # If false, the server will not be able to host game servers
+
+ def __init__(
+ self,
+ serverId,
+ serverName,
+ serverIP,
+ serverPort,
+ accessKey,
+ allowThumbnailGen=True,
+ allowGameServerHost=True
+ ):
+ self.serverId = serverId
+ self.serverName = serverName
+ self.serverIP = serverIP
+ self.serverPort = serverPort
+ self.accessKey = accessKey
+ self.allowThumbnailGen = allowThumbnailGen
+ self.allowGameServerHost = allowGameServerHost
+
+ def __repr__(self):
+ return "".format(
+ serverId=self.serverId,
+ serverName=self.serverName,
+ serverIP=self.serverIP,
+ serverPort=self.serverPort
+ )
\ No newline at end of file
diff --git a/app/models/giftcard_key.py b/app/models/giftcard_key.py
new file mode 100644
index 0000000..0407664
--- /dev/null
+++ b/app/models/giftcard_key.py
@@ -0,0 +1,31 @@
+from app.extensions import db
+from app.enums.GiftcardType import GiftcardType
+from datetime import datetime
+
+class GiftcardKey(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True)
+ key = db.Column(db.String(255), nullable=False)
+ type = db.Column(db.Enum(GiftcardType), nullable=False)
+ value = db.Column(db.BigInteger, nullable=True)
+
+ created_at = db.Column(db.DateTime, nullable=False)
+ redeemed_at = db.Column(db.DateTime, nullable=True)
+ redeemed_by = db.Column(db.BigInteger, nullable=True)
+
+ def __init__(
+ self,
+ key: str,
+ type: GiftcardType,
+ value: int = None,
+ created_at: datetime = None,
+ ):
+ if created_at is None:
+ created_at = datetime.utcnow()
+
+ self.key = key
+ self.type = type
+ self.value = value
+ self.created_at = created_at
+
+ def __repr__(self):
+ return f''
diff --git a/app/models/groups.py b/app/models/groups.py
new file mode 100644
index 0000000..5b62f47
--- /dev/null
+++ b/app/models/groups.py
@@ -0,0 +1,296 @@
+from app.extensions import db
+from datetime import datetime
+
+class Group(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+ owner_id = db.Column(db.BigInteger, db.ForeignKey('user.id'), nullable=True, index=True) # Can be nullable since owners can leave groups
+ name = db.Column(db.String(255), nullable=False, index=True)
+ description = db.Column(db.String(1024), nullable=False)
+ created_at = db.Column(db.DateTime, nullable=False)
+ updated_at = db.Column(db.DateTime, nullable=False)
+ locked = db.Column(db.Boolean, nullable=False, default=False)
+
+ owner = db.relationship('User', backref=db.backref('groups', lazy=True))
+
+ def __init__(
+ self,
+ owner_id,
+ name,
+ description,
+ created_at=None,
+ updated_at=None,
+ ):
+ if created_at is None:
+ created_at = datetime.utcnow()
+ if updated_at is None:
+ updated_at = datetime.utcnow()
+
+ self.owner_id = owner_id
+ self.name = name
+ self.description = description
+ self.created_at = created_at
+ self.updated_at = updated_at
+
+ def __repr__(self):
+ return '' % self.name
+
+class GroupIcon(db.Model):
+ group_id = db.Column(db.BigInteger, db.ForeignKey('group.id'), primary_key=True)
+ content_hash = db.Column(db.String(512), nullable=False, index=True)
+ moderation_status = db.Column(db.Integer, nullable=False, default=1)
+ creator_id = db.Column(db.BigInteger, db.ForeignKey('user.id'), nullable=False, index=True)
+
+ created_at = db.Column(db.DateTime, nullable=False)
+
+ group = db.relationship('Group', backref=db.backref('icon', lazy=True, uselist=False), uselist=False)
+ creator = db.relationship('User', backref=db.backref('group_icons', lazy=True))
+
+ def __init__(
+ self,
+ group_id,
+ content_hash,
+ moderation_status,
+ creator_id,
+ created_at=None
+ ):
+ if created_at is None:
+ created_at = datetime.utcnow()
+
+ self.group_id = group_id
+ self.content_hash = content_hash
+ self.moderation_status = moderation_status
+ self.creator_id = creator_id
+ self.created_at = created_at
+
+ def __repr__(self):
+ return '' % self.group_id
+
+class GroupSettings(db.Model):
+ group_id = db.Column(db.BigInteger, db.ForeignKey('group.id'), primary_key=True)
+ approval_required = db.Column(db.Boolean, nullable=False, default=False)
+ enemies_allowed = db.Column(db.Boolean, nullable=False, default=False)
+ funds_visible = db.Column(db.Boolean, nullable=False, default=False)
+ games_visible = db.Column(db.Boolean, nullable=False, default=False)
+ membership_required = db.Column(db.Boolean, nullable=False, default=False)
+
+ last_updated = db.Column(db.DateTime, nullable=False)
+
+ group = db.relationship('Group', backref=db.backref('settings', lazy=True, uselist=False), uselist=False)
+
+ def __init__(
+ self,
+ group_id,
+ approval_required = False,
+ enemies_allowed = False,
+ funds_visible = False,
+ games_visible = False,
+ membership_required = False,
+ last_updated=None
+ ):
+ if last_updated is None:
+ last_updated = datetime.utcnow()
+
+ self.group_id = group_id
+ self.approval_required = approval_required
+ self.enemies_allowed = enemies_allowed
+ self.funds_visible = funds_visible
+ self.games_visible = games_visible
+ self.membership_required = membership_required
+ self.last_updated = last_updated
+
+ def __repr__(self):
+ return '' % self.group_id
+
+class GroupRole(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+ group_id = db.Column(db.BigInteger, db.ForeignKey('group.id'), nullable=False, index=True)
+ name = db.Column(db.String(255), nullable=False)
+ description = db.Column(db.String(255), nullable=False)
+ rank = db.Column(db.Integer, nullable=False, default=1)
+ member_count = db.Column(db.Integer, nullable=False, default=0)
+
+ created_at = db.Column(db.DateTime, nullable=False)
+ updated_at = db.Column(db.DateTime, nullable=False)
+
+ group = db.relationship('Group', backref=db.backref('roles', lazy=True))
+
+ def __init__(
+ self,
+ group_id,
+ name,
+ description,
+ rank,
+ member_count,
+ created_at=None,
+ updated_at=None
+ ):
+ if created_at is None:
+ created_at = datetime.utcnow()
+ if updated_at is None:
+ updated_at = datetime.utcnow()
+
+ self.group_id = group_id
+ self.name = name
+ self.description = description
+ self.rank = rank
+ self.member_count = member_count
+ self.created_at = created_at
+ self.updated_at = updated_at
+
+ def __repr__(self):
+ return '' % self.name
+
+class GroupRolePermission(db.Model):
+ group_role_id = db.Column(db.BigInteger, db.ForeignKey('group_role.id'), primary_key=True)
+ # One to one relationship
+ group_roleset = db.relationship('GroupRole', backref=db.backref('permissions', uselist=False, lazy=True), lazy=True, uselist=False)
+
+ delete_from_wall = db.Column(db.Boolean, nullable=False, default=False)
+ post_to_wall = db.Column(db.Boolean, nullable=False, default=False)
+ invite_members = db.Column(db.Boolean, nullable=False, default=False)
+ post_to_status = db.Column(db.Boolean, nullable=False, default=False)
+ remove_members = db.Column(db.Boolean, nullable=False, default=False)
+ view_status = db.Column(db.Boolean, nullable=False, default=False)
+ view_wall = db.Column(db.Boolean, nullable=False, default=False)
+ change_rank = db.Column(db.Boolean, nullable=False, default=False)
+ advertise_group = db.Column(db.Boolean, nullable=False, default=False)
+ manage_relationships = db.Column(db.Boolean, nullable=False, default=False)
+ add_group_places = db.Column(db.Boolean, nullable=False, default=False)
+ view_audit_logs = db.Column(db.Boolean, nullable=False, default=False)
+ create_items = db.Column(db.Boolean, nullable=False, default=False)
+ manage_items = db.Column(db.Boolean, nullable=False, default=False)
+ spend_group_funds = db.Column(db.Boolean, nullable=False, default=False)
+ manage_clan = db.Column(db.Boolean, nullable=False, default=False)
+ manage_group_games = db.Column(db.Boolean, nullable=False, default=False)
+
+ def __init__(
+ self,
+ group_role_id
+ ):
+ self.group_role_id = group_role_id
+
+ def __repr__(self):
+ return '' % self.group_role_id
+
+class GroupMember(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+ group_id = db.Column(db.BigInteger, db.ForeignKey('group.id'), nullable=False, index=True)
+ user_id = db.Column(db.BigInteger, db.ForeignKey('user.id'), nullable=False, index=True)
+ group_role_id = db.Column(db.BigInteger, db.ForeignKey('group_role.id'), nullable=False, index=True)
+
+ created_at = db.Column(db.DateTime, nullable=False)
+
+ group = db.relationship('Group', backref=db.backref('members', lazy=True))
+ user = db.relationship('User', backref=db.backref('group_members', lazy=True))
+ group_role = db.relationship('GroupRole', backref=db.backref('members', lazy=True))
+
+ def __init__(
+ self,
+ group_id,
+ user_id,
+ group_role_id,
+ created_at=None
+ ):
+ if created_at is None:
+ created_at = datetime.utcnow()
+ self.created_at = created_at
+ self.group_id = group_id
+ self.user_id = user_id
+ self.group_role_id = group_role_id
+
+ def __repr__(self):
+ return '' % self.id
+
+class GroupEconomy(db.Model):
+ group_id = db.Column(db.BigInteger, db.ForeignKey('group.id'), primary_key=True)
+ robux_balance = db.Column(db.BigInteger, nullable=False, default=0)
+ tix_balance = db.Column(db.BigInteger, nullable=False, default=0)
+
+ group = db.relationship('Group', backref=db.backref('economy', lazy=True, uselist=False), uselist=False)
+
+ def __init__(
+ self,
+ group_id
+ ):
+ self.group_id = group_id
+
+ def __repr__(self):
+ return '' % self.group_id
+
+class GroupJoinRequest(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+ group_id = db.Column(db.BigInteger, db.ForeignKey('group.id'), nullable=False, index=True)
+ user_id = db.Column(db.BigInteger, db.ForeignKey('user.id'), nullable=False, index=True)
+ created_at = db.Column(db.DateTime, nullable=False)
+
+ group = db.relationship('Group', backref=db.backref('join_requests', lazy=True))
+ user = db.relationship('User', backref=db.backref('group_join_requests', lazy=True))
+
+ def __init__(
+ self,
+ group_id,
+ user_id,
+ created_at=None
+ ):
+ if created_at is None:
+ created_at = datetime.utcnow()
+ self.group_id = group_id
+ self.user_id = user_id
+ self.created_at = created_at
+
+ def __repr__(self):
+ return '' % self.id
+
+class GroupStatus(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+ group_id = db.Column(db.BigInteger, db.ForeignKey('group.id'), nullable=False, index=True)
+ poster_id = db.Column(db.BigInteger, db.ForeignKey('user.id'), nullable=False, index=True)
+ content = db.Column(db.String(1024), nullable=False)
+ created_at = db.Column(db.DateTime, nullable=False)
+
+ group = db.relationship('Group', backref=db.backref('statuses', lazy=True))
+ poster = db.relationship('User', backref=db.backref('group_statuses', lazy=True))
+
+ def __init__(
+ self,
+ group_id,
+ poster_id,
+ content,
+ created_at=None
+ ):
+ self.group_id = group_id
+ self.poster_id = poster_id
+ self.content = content
+ if created_at is None:
+ created_at = datetime.utcnow()
+ self.created_at = created_at
+
+ def __repr__(self):
+ return '' % self.id
+
+class GroupWallPost(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+ group_id = db.Column(db.BigInteger, db.ForeignKey('group.id'), nullable=False, index=True)
+ poster_id = db.Column(db.BigInteger, db.ForeignKey('user.id'), nullable=False, index=True)
+ content = db.Column(db.String(1024), nullable=False)
+ created_at = db.Column(db.DateTime, nullable=False)
+
+ group = db.relationship('Group', backref=db.backref('wall_posts', lazy=True))
+ poster = db.relationship('User', backref=db.backref('group_wall_posts', lazy=True, uselist=False), uselist=False)
+
+ def __init__(
+ self,
+ group_id,
+ poster_id,
+ content,
+ created_at=None
+ ):
+ self.group_id = group_id
+ self.poster_id = poster_id
+ self.content = content
+ if created_at is None:
+ created_at = datetime.utcnow()
+ self.created_at = created_at
+
+ def __repr__(self):
+ return '' % self.id
\ No newline at end of file
diff --git a/app/models/invite_key.py b/app/models/invite_key.py
new file mode 100644
index 0000000..e93f34d
--- /dev/null
+++ b/app/models/invite_key.py
@@ -0,0 +1,25 @@
+from app.extensions import db
+from datetime import datetime
+
+class InviteKey(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True, nullable=False)
+ key = db.Column(db.String(255), nullable=False) # syntax-randomstring
+ created_at = db.Column(db.DateTime, nullable=False)
+ created_by = db.Column(db.BigInteger, db.ForeignKey('user.id') ,nullable=True, index=True)
+ used_by = db.Column(db.BigInteger, db.ForeignKey('user.id'), nullable=True)
+ used_on = db.Column(db.DateTime, nullable=True)
+
+ creator = db.relationship('User', backref=db.backref('invite_keys', lazy=True), foreign_keys=[created_by], uselist=False)
+ user = db.relationship('User', backref=db.backref('used_invite_keys', lazy=True), foreign_keys=[used_by], uselist=False)
+
+ def __init__(
+ self,
+ key,
+ created_by
+ ):
+ self.key = key
+ self.created_by = created_by
+ self.created_at = datetime.utcnow()
+
+ def __repr__(self):
+ return '' % self.id
\ No newline at end of file
diff --git a/app/models/kofi_transaction.py b/app/models/kofi_transaction.py
new file mode 100644
index 0000000..3310cb3
--- /dev/null
+++ b/app/models/kofi_transaction.py
@@ -0,0 +1,41 @@
+from app.extensions import db
+
+class KofiTransaction(db.Model):
+ kofi_transaction_id = db.Column(db.Text, primary_key=True)
+ timestamp = db.Column(db.DateTime, nullable=False)
+ donation_type = db.Column(db.Text, nullable=False)
+ amount = db.Column(db.Float, nullable=False)
+ currency = db.Column(db.Text, nullable=False)
+ is_subscription_payment = db.Column(db.Boolean, nullable=False)
+ message = db.Column(db.Text, nullable=False)
+ from_name = db.Column(db.Text, nullable=False)
+ from_email = db.Column(db.Text, nullable=False)
+
+ assigned_key = db.Column(db.Text ,nullable=True)
+
+ def __init__(
+ self,
+ kofi_transaction_id,
+ timestamp,
+ donation_type,
+ amount,
+ currency,
+ is_subscription_payment,
+ message,
+ from_name,
+ from_email,
+ assigned_key=None
+ ):
+ self.kofi_transaction_id = kofi_transaction_id
+ self.timestamp = timestamp
+ self.donation_type = donation_type
+ self.amount = amount
+ self.currency = currency
+ self.is_subscription_payment = is_subscription_payment
+ self.message = message
+ self.from_name = from_name
+ self.from_email = from_email
+ self.assigned_key = assigned_key
+
+ def __repr__(self):
+ return f""
\ No newline at end of file
diff --git a/app/models/legacy_data_persistence.py b/app/models/legacy_data_persistence.py
new file mode 100644
index 0000000..0c2ba71
--- /dev/null
+++ b/app/models/legacy_data_persistence.py
@@ -0,0 +1,31 @@
+from app.extensions import db
+from datetime import datetime
+
+class LegacyDataPersistence( db.Model ):
+ id = db.Column( db.BigInteger, primary_key=True, nullable=False, unique=True, autoincrement=True )
+ userid = db.Column( db.BigInteger, nullable=False, index=True )
+ placeid = db.Column( db.BigInteger, nullable=False, index=True ) # DEPRECATED
+ data = db.Column( db.LargeBinary, nullable=False )
+
+ last_updated = db.Column( db.DateTime, nullable=False )
+
+ universe_id = db.Column( db.BigInteger, db.ForeignKey('universe.id'), nullable=True, index=True )
+
+ def __init__(
+ self,
+ userid,
+ placeid,
+ universe_id
+ ):
+ self.userid = userid
+ self.placeid = placeid
+ self.last_updated = datetime.utcnow()
+ self.universe_id = universe_id
+
+ def __repr__(self):
+ return "".format(
+ id=self.id,
+ userid=self.userid,
+ placeid=self.placeid
+ )
+
diff --git a/app/models/limited_item_transfers.py b/app/models/limited_item_transfers.py
new file mode 100644
index 0000000..bda0190
--- /dev/null
+++ b/app/models/limited_item_transfers.py
@@ -0,0 +1,33 @@
+from app.extensions import db
+from app.enums.LimitedItemTransferMethod import LimitedItemTransferMethod
+
+from datetime import datetime
+
+class LimitedItemTransfer(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+
+ original_owner_id = db.Column(db.BigInteger, nullable=False, index=True)
+ new_owner_id = db.Column(db.BigInteger, nullable=False, index=True)
+
+ asset_id = db.Column(db.BigInteger, nullable=False, index=True)
+ user_asset_id = db.Column(db.BigInteger, nullable=False, index=True)
+ transferred_at = db.Column(db.DateTime, nullable=False)
+
+ transfer_method = db.Column(db.Enum(LimitedItemTransferMethod), nullable=False, index=True)
+ purchased_price = db.Column(db.BigInteger, nullable=True, default=None)
+ associated_trade_id = db.Column(db.BigInteger, nullable=True, default=None)
+
+ def __init__(self, original_owner_id, new_owner_id, asset_id, user_asset_id, transfer_method=LimitedItemTransferMethod.Purchase, transferred_at=None, purchased_price=None, associated_trade_id = None):
+ self.original_owner_id = original_owner_id
+ self.new_owner_id = new_owner_id
+ self.asset_id = asset_id
+ self.user_asset_id = user_asset_id
+ self.transfer_method = transfer_method
+
+ if transferred_at is None:
+ self.transferred_at = datetime.utcnow()
+ else:
+ self.transferred_at = transferred_at
+
+ self.purchased_price = purchased_price
+ self.associated_trade_id = associated_trade_id
\ No newline at end of file
diff --git a/app/models/linked_discord.py b/app/models/linked_discord.py
new file mode 100644
index 0000000..c401c09
--- /dev/null
+++ b/app/models/linked_discord.py
@@ -0,0 +1,49 @@
+from app.extensions import db
+from datetime import datetime
+
+class LinkedDiscord(db.Model):
+ user_id = db.Column(db.BigInteger, db.ForeignKey('user.id'), primary_key=True)
+ discord_id = db.Column(db.BigInteger, nullable=False)
+ discord_username = db.Column(db.String(255), nullable=False)
+ discord_discriminator = db.Column(db.String(255), nullable=True)
+ discord_avatar = db.Column(db.Text, nullable=True)
+ discord_access_token = db.Column(db.String(512), nullable=True)
+ discord_refresh_token = db.Column(db.String(512), nullable=True)
+ discord_expiry = db.Column(db.DateTime, nullable=True)
+
+ last_updated = db.Column(db.DateTime, nullable=False)
+ linked_on = db.Column(db.DateTime, nullable=False)
+
+ user = db.relationship('User', backref=db.backref('discord', lazy=True))
+
+ def __init__(self,
+ user_id,
+ discord_id,
+ discord_username,
+ discord_discriminator,
+ discord_avatar,
+ discord_access_token,
+ discord_refresh_token,
+ discord_expiry,
+ last_updated=None,
+ linked_on=None
+ ):
+ self.user_id = user_id
+ self.discord_id = discord_id
+ self.discord_username = discord_username
+ self.discord_discriminator = discord_discriminator
+ self.discord_avatar = discord_avatar
+ self.discord_access_token = discord_access_token
+ self.discord_refresh_token = discord_refresh_token,
+ self.discord_expiry = discord_expiry
+
+ if last_updated is None:
+ self.last_updated = datetime.utcnow()
+ else:
+ self.last_updated = last_updated
+ if linked_on is None:
+ self.linked_on = datetime.utcnow()
+ else:
+ self.linked_on = linked_on
+ def __repr__(self):
+ return '' % self.user_id
\ No newline at end of file
diff --git a/app/models/login_records.py b/app/models/login_records.py
new file mode 100644
index 0000000..c51b705
--- /dev/null
+++ b/app/models/login_records.py
@@ -0,0 +1,21 @@
+from app.extensions import db
+
+class LoginRecord(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True, nullable=False)
+ userid = db.Column(db.BigInteger, db.ForeignKey('user.id'), nullable=False, index=True)
+ ip = db.Column(db.Text, nullable=False, index=True)
+ useragent = db.Column(db.Text, nullable=False)
+ timestamp = db.Column(db.DateTime, nullable=False)
+ session_token = db.Column(db.Text, nullable=True, index=True)
+
+ User = db.relationship('User', backref=db.backref('login_records', lazy=True), uselist=False)
+
+ def __init__(self, userid, ip, useragent, timestamp, session_token = None):
+ self.userid = userid
+ self.ip = ip
+ self.useragent = useragent
+ self.timestamp = timestamp
+ self.session_token = session_token
+
+ def __repr__(self):
+ return ''.format(self.id, self.userid, self.ip)
\ No newline at end of file
diff --git a/app/models/messages.py b/app/models/messages.py
new file mode 100644
index 0000000..839b0ed
--- /dev/null
+++ b/app/models/messages.py
@@ -0,0 +1,33 @@
+from app.extensions import db
+from datetime import datetime
+
+class Message( db.Model ):
+ id = db.Column( db.BigInteger, primary_key=True, autoincrement=True, nullable=False )
+ sender_id = db.Column( db.BigInteger, db.ForeignKey("user.id"), nullable=False, index=True )
+ recipient_id = db.Column( db.BigInteger, db.ForeignKey("user.id"), nullable=False, index=True )
+ created = db.Column( db.DateTime, nullable=False )
+ read = db.Column( db.Boolean, nullable=False )
+ subject = db.Column( db.String( 128 ), nullable=False )
+ content = db.Column( db.Text, nullable=False )
+
+ sender = db.relationship( "User", foreign_keys=[sender_id], uselist=False)
+ recipient = db.relationship( "User", foreign_keys=[recipient_id], uselist=False)
+
+ def __init__(
+ self,
+ sender_id,
+ recipient_id,
+ content,
+ subject,
+
+ read=False
+ ):
+ self.sender_id = sender_id
+ self.recipient_id = recipient_id
+ self.content = content
+ self.read = read
+ self.subject = subject
+ self.created = datetime.utcnow()
+
+ def __repr__(self):
+ return '' % self.id
\ No newline at end of file
diff --git a/app/models/moderator_note.py b/app/models/moderator_note.py
new file mode 100644
index 0000000..9cac09b
--- /dev/null
+++ b/app/models/moderator_note.py
@@ -0,0 +1,36 @@
+from app.extensions import db
+from datetime import datetime
+
+class ModeratorNote( db.Model ):
+ id = db.Column( db.BigInteger, primary_key = True, autoincrement = True )
+ user_id = db.Column( db.BigInteger, db.ForeignKey( 'user.id' ), index = True)
+ note_creator_id = db.Column( db.BigInteger, db.ForeignKey( 'user.id' ), index = True)
+ note = db.Column( db.Text )
+
+ created_at = db.Column( db.DateTime, index=True, nullable=False )
+ updated_at = db.Column( db.DateTime, index=True, nullable=False )
+
+ related_action_id = db.Column( db.BigInteger, db.ForeignKey('user_ban.id'), nullable = True, index = True)
+
+ def __init__( self, user_id, note_creator_id, note, related_action_id = None ):
+ self.user_id = user_id
+ self.note_creator_id = note_creator_id
+ self.note = note
+ self.related_action_id = related_action_id
+
+ self.created_at = datetime.utcnow()
+ self.updated_at = datetime.utcnow()
+
+ def __repr__( self ):
+ return '' % self.id
+
+class ModeratorNoteAttachment( db.Model ):
+ id = db.Column( db.BigInteger, primary_key = True, autoincrement = True )
+ moderator_note_id = db.Column( db.BigInteger, db.ForeignKey( 'moderator_note.id' ), index = True)
+ attachment_hash = db.Column( db.String( 512 ), index = True, nullable = False )
+ attachment_name = db.Column( db.String( 512 ), index = True, nullable = False )
+
+ def __init__( self, moderator_note_id, attachment_hash, attachment_name ):
+ self.moderator_note_id = moderator_note_id
+ self.attachment_hash = attachment_hash
+ self.attachment_name = attachment_name
\ No newline at end of file
diff --git a/app/models/package_asset.py b/app/models/package_asset.py
new file mode 100644
index 0000000..2fd3f32
--- /dev/null
+++ b/app/models/package_asset.py
@@ -0,0 +1,13 @@
+from app.extensions import db
+
+class PackageAsset( db.Model ):
+ id = db.Column( db.BigInteger, primary_key=True, autoincrement=True)
+ package_asset_id = db.Column( db.BigInteger, nullable=False, index=True )
+ asset_id = db.Column( db.BigInteger, nullable=False )
+
+ def __init__( self, package_asset_id, asset_id ):
+ self.package_asset_id = package_asset_id
+ self.asset_id = asset_id
+
+ def __repr__( self ):
+ return '' % self.package_asset_id
\ No newline at end of file
diff --git a/app/models/past_usernames.py b/app/models/past_usernames.py
new file mode 100644
index 0000000..0c7b031
--- /dev/null
+++ b/app/models/past_usernames.py
@@ -0,0 +1,16 @@
+from app.extensions import db
+from datetime import datetime
+
+class PastUsername(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True, nullable=False)
+ user_id = db.Column(db.BigInteger, db.ForeignKey('user.id'), nullable=False, index=True)
+ username = db.Column(db.Text, nullable=False)
+ created = db.Column(db.DateTime, nullable=False)
+
+ def __init__(self, user_id, username):
+ self.user_id = user_id
+ self.username = username
+ self.created = datetime.utcnow()
+
+ def __repr__(self):
+ return ''.format(self.username, str(self.id))
diff --git a/app/models/place.py b/app/models/place.py
new file mode 100644
index 0000000..6382adb
--- /dev/null
+++ b/app/models/place.py
@@ -0,0 +1,51 @@
+from app.extensions import db
+from app.models.asset import Asset
+from app.enums.PlaceYear import PlaceYear
+from app.enums.PlaceRigChoice import PlaceRigChoice
+from app.enums.ChatStyle import ChatStyle
+
+class Place( db.Model ):
+ placeid = db.Column( db.BigInteger, db.ForeignKey('asset.id') ,primary_key=True, nullable=False, unique=True ) # Should be the same as the asset id
+ visitcount = db.Column( db.BigInteger, nullable=False, default=0 )
+ is_public = db.Column( db.Boolean, nullable=False, default=True )
+ maxplayers = db.Column( db.BigInteger, nullable=False, default=10 )
+ placeyear = db.Column( db.Enum( PlaceYear ), nullable=False, default=PlaceYear.Sixteen )
+ featured = db.Column( db.Boolean, nullable=False, default=False, index=True)
+ bc_required = db.Column( db.Boolean, nullable=False, default=False, index=True)
+ rig_choice = db.Column( db.Enum( PlaceRigChoice ), nullable=False, default=PlaceRigChoice.UserChoice )
+ chat_style = db.Column( db.Enum( ChatStyle ), nullable=False, default=ChatStyle.ClassicAndBubble )
+ min_account_age = db.Column( db.Integer, nullable=False, default=0)
+
+ parent_universe_id = db.Column( db.BigInteger, db.ForeignKey('universe.id'), nullable=True, index=True )
+
+ assetObj = db.relationship( 'Asset', backref=db.backref('place', lazy=True, uselist=False), uselist=False )
+
+ def __init__(
+ self,
+ placeid,
+ visitcount = 0,
+ is_public = True,
+ maxplayers = 10,
+ placeyear = PlaceYear.Sixteen,
+ rig_choice = PlaceRigChoice.UserChoice,
+ chat_style = ChatStyle.ClassicAndBubble,
+ min_account_age = 0,
+ parent_universe_id = None
+ ):
+ self.placeid = placeid
+ self.visitcount = visitcount
+ self.is_public = is_public
+ self.maxplayers = maxplayers
+ self.placeyear = placeyear
+ self.rig_choice = rig_choice
+ self.chat_style = chat_style
+ self.min_account_age = min_account_age
+ self.parent_universe_id = parent_universe_id
+
+ def __repr__(self):
+ return "".format(
+ placeid=self.placeid,
+ visitcount=self.visitcount,
+ is_public=self.is_public,
+ maxplayers=self.maxplayers
+ )
\ No newline at end of file
diff --git a/app/models/place_badge.py b/app/models/place_badge.py
new file mode 100644
index 0000000..3a2a6e8
--- /dev/null
+++ b/app/models/place_badge.py
@@ -0,0 +1,48 @@
+from app.extensions import db
+from datetime import datetime
+
+class PlaceBadge( db.Model ):
+ id = db.Column( db.BigInteger, primary_key=True )
+ associated_place_id = db.Column( db.BigInteger, db.ForeignKey( 'place.placeid' ), index = True ) # DEPRECATED
+ icon_image_id = db.Column( db.BigInteger, db.ForeignKey( 'asset.id' ), nullable = False )
+
+ name = db.Column( db.String( 255 ), nullable = False )
+ description = db.Column( db.String( 1024 ), nullable = False )
+ created_at = db.Column( db.DateTime, nullable = False, index = True )
+ updated_at = db.Column( db.DateTime, nullable = False, index = True )
+
+ enabled = db.Column( db.Boolean, nullable = False, default = True )
+ asset_reward = db.Column( db.BigInteger, nullable = True, default = None )
+
+ universe_id = db.Column( db.BigInteger, db.ForeignKey( 'universe.id' ), nullable = True, index = True )
+
+ def __init__( self, name, description, icon_image_id, associated_place_id, universe_id):
+ self.name = name
+ self.description = description
+ self.icon_image_id = icon_image_id
+ self.associated_place_id = associated_place_id
+ self.universe_id = universe_id
+
+ self.created_at = datetime.utcnow()
+ self.updated_at = datetime.utcnow()
+
+ def __repr__( self ):
+ return '' % self.id
+
+class UserBadge( db.Model ):
+ id = db.Column( db.BigInteger, primary_key=True )
+ badge_id = db.Column( db.BigInteger, db.ForeignKey( 'place_badge.id' ), index = True )
+ user_id = db.Column( db.BigInteger, db.ForeignKey( 'user.id' ), index = True )
+
+ awarded_at = db.Column( db.DateTime, nullable = False, index = True )
+
+ badge = db.relationship( 'PlaceBadge', backref = db.backref( 'user_badges', lazy = 'dynamic' ) )
+
+ def __init__( self, badge_id, user_id ):
+ self.badge_id = badge_id
+ self.user_id = user_id
+
+ self.awarded_at = datetime.utcnow()
+
+ def __repr__( self ):
+ return '' % self.id
\ No newline at end of file
diff --git a/app/models/place_datastore.py b/app/models/place_datastore.py
new file mode 100644
index 0000000..08752d5
--- /dev/null
+++ b/app/models/place_datastore.py
@@ -0,0 +1,34 @@
+from app.extensions import db
+from datetime import datetime
+
+class PlaceDatastore( db.Model ):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+ placeid = db.Column(db.BigInteger, nullable=False, index=True) # DEPRECATED
+
+ scope = db.Column(db.String(255), nullable=False, index=True)
+ key = db.Column(db.String(255), nullable=False, index=True)
+ name = db.Column(db.String(255), nullable=False)
+ value = db.Column(db.String(1024 * 1024), nullable=False)
+
+ created_at = db.Column(db.DateTime, nullable=False)
+ updated_at = db.Column(db.DateTime, nullable=False)
+
+ universe_id = db.Column(db.BigInteger, db.ForeignKey('universe.id'), nullable=True, index=True)
+
+ def __init__(self, placeid, scope, key, name, value, created_at=None, updated_at=None, universe_id = None):
+ self.placeid = placeid
+ self.scope = scope
+ self.key = key
+ self.name = name
+ self.value = value
+ self.universe_id = universe_id
+
+ if created_at is None:
+ created_at = datetime.utcnow()
+ if updated_at is None:
+ updated_at = datetime.utcnow()
+ self.created_at = created_at
+ self.updated_at = updated_at
+
+ def __repr__(self):
+ return '' % self.id
\ No newline at end of file
diff --git a/app/models/place_developer_product.py b/app/models/place_developer_product.py
new file mode 100644
index 0000000..22a0542
--- /dev/null
+++ b/app/models/place_developer_product.py
@@ -0,0 +1,48 @@
+from app.extensions import db
+from datetime import datetime
+
+class DeveloperProduct( db.Model ):
+ productid = db.Column( db.BigInteger, primary_key=True, nullable=False, unique=True, autoincrement=True )
+ placeid = db.Column( db.BigInteger, db.ForeignKey( 'place.placeid' ), nullable=True, index=True ) # DEPRECATED
+ name = db.Column( db.String( 256 ), nullable=False )
+ description = db.Column( db.String( 1024 ), nullable=False )
+ iconimage_assetid = db.Column( db.BigInteger, db.ForeignKey( 'asset.id' ), nullable=True, index=True )
+
+ robux_price = db.Column( db.BigInteger, nullable=False, default = 0 )
+ sales_count = db.Column( db.BigInteger, nullable=False, default = 0 )
+ is_for_sale = db.Column( db.Boolean, nullable=False, default = False )
+
+ created_at = db.Column( db.DateTime, nullable=False )
+ updated_at = db.Column( db.DateTime, nullable=False )
+
+ creator_id = db.Column( db.BigInteger, db.ForeignKey( 'user.id' ), nullable=False, index=True )
+ universe_id = db.Column( db.BigInteger, db.ForeignKey( 'universe.id' ), nullable=True, index=True )
+
+ placeObj = db.relationship( 'Place', lazy='joined', uselist=False )
+ creatorObj = db.relationship( 'User', lazy='joined', uselist=False )
+ iconimageObj = db.relationship( 'Asset', lazy='joined', uselist=False )
+
+ def __init__( self, placeid, name, description, iconimage_assetid, creator_id, universe_id ):
+ self.placeid = placeid
+ self.name = name
+ self.description = description
+ self.iconimage_assetid = iconimage_assetid
+ self.creator_id = creator_id
+ self.universe_id = universe_id
+
+ self.created_at = datetime.utcnow()
+ self.updated_at = datetime.utcnow()
+
+ def __repr__( self ):
+ return "".format(
+ productid=self.productid,
+ placeid=self.placeid,
+ name=self.name,
+ description=self.description,
+ iconimage_assetid=self.iconimage_assetid,
+ robux_price=self.robux_price,
+ sales_count=self.sales_count,
+ created_at=self.created_at,
+ updated_at=self.updated_at,
+ creator_id=self.creator_id
+ )
\ No newline at end of file
diff --git a/app/models/place_icon.py b/app/models/place_icon.py
new file mode 100644
index 0000000..cd4c2cc
--- /dev/null
+++ b/app/models/place_icon.py
@@ -0,0 +1,22 @@
+from app.extensions import db
+
+class PlaceIcon(db.Model):
+ placeid = db.Column(db.BigInteger, db.ForeignKey('asset.id'), primary_key=True, nullable=False, unique=True)
+ contenthash = db.Column(db.String(512), nullable=True)
+ updated_at = db.Column(db.DateTime, nullable=True)
+ moderation_status = db.Column(db.SmallInteger, nullable=False, default=1) # Asset thumbnail moderation status ( 0 = Approved, 1 = Pending, 2 = Declined )
+
+ asset = db.relationship('Asset', backref=db.backref('place_icon', lazy=True, uselist=False), uselist=False)
+
+ def __init__(self, placeid, contenthash, updated_at, moderation_status=1):
+ self.placeid = placeid
+ self.contenthash = contenthash
+ self.updated_at = updated_at
+ self.moderation_status = moderation_status
+
+ def __repr__(self):
+ return "".format(
+ placeid=self.placeid,
+ contenthash=self.contenthash,
+ updated_at=self.updated_at
+ )
\ No newline at end of file
diff --git a/app/models/place_ordered_datastore.py b/app/models/place_ordered_datastore.py
new file mode 100644
index 0000000..fa6975b
--- /dev/null
+++ b/app/models/place_ordered_datastore.py
@@ -0,0 +1,33 @@
+from app.extensions import db
+from datetime import datetime
+
+class PlaceOrderedDatastore( db.Model ):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+ placeid = db.Column(db.BigInteger, nullable=False)
+
+ scope = db.Column(db.String(255), nullable=False)
+ key = db.Column(db.String(255), nullable=False)
+ name = db.Column(db.String(255), nullable=False)
+ value = db.Column(db.BigInteger, nullable=False)
+
+ created_at = db.Column(db.DateTime, nullable=False)
+ updated_at = db.Column(db.DateTime, nullable=False)
+
+ universe_id = db.Column(db.BigInteger, db.ForeignKey('universe.id'), nullable=True, index=True)
+
+ def __init__(self, placeid, scope, key, name, value, created_at=None, updated_at=None, universe_id=None):
+ self.placeid = placeid
+ self.scope = scope
+ self.key = key
+ self.name = name
+ self.value = value
+
+ if created_at is None:
+ created_at = datetime.utcnow()
+ if updated_at is None:
+ updated_at = datetime.utcnow()
+ self.created_at = created_at
+ self.updated_at = updated_at
+
+ def __repr__(self):
+ return '' % self.id
\ No newline at end of file
diff --git a/app/models/placeserver_players.py b/app/models/placeserver_players.py
new file mode 100644
index 0000000..86f8577
--- /dev/null
+++ b/app/models/placeserver_players.py
@@ -0,0 +1,23 @@
+from app.extensions import db
+from sqlalchemy.dialects.postgresql import UUID
+
+class PlaceServerPlayer(db.Model):
+ userid = db.Column(db.BigInteger, db.ForeignKey('user.id'), primary_key=True, nullable=False, unique=True)
+ serveruuid = db.Column(UUID(as_uuid=True), nullable=False, index=True)
+ joinTime = db.Column(db.DateTime, nullable=False)
+ lastHeartbeat = db.Column(db.DateTime, nullable=True, default=None)
+
+ user = db.relationship('User', backref=db.backref('placeserver_players', lazy=True), uselist=False)
+
+ def __init__(self, userid, serveruuid, joinTime):
+ self.userid = userid
+ self.serveruuid = serveruuid
+ self.joinTime = joinTime
+ self.lastHeartbeat = joinTime
+
+ def __repr__(self):
+ return "".format(
+ userid=self.userid,
+ serveruuid=self.serveruuid,
+ joinTime=self.joinTime
+ )
diff --git a/app/models/placeservers.py b/app/models/placeservers.py
new file mode 100644
index 0000000..8c715df
--- /dev/null
+++ b/app/models/placeservers.py
@@ -0,0 +1,52 @@
+from app.extensions import db
+from sqlalchemy.dialects.postgresql import UUID
+from datetime import datetime, timedelta
+
+class PlaceServer(db.Model):
+ serveruuid = db.Column(UUID(as_uuid=True), primary_key=True, nullable=False, unique=True)
+ originServerId = db.Column(UUID(as_uuid=True), nullable=False, index=True)
+ serverIP = db.Column(db.String(128), nullable=False)
+ serverPort = db.Column(db.Integer, nullable=False)
+
+ serverPlaceId = db.Column(db.BigInteger, nullable=False, index=True)
+ serverRunningTime = db.Column(db.BigInteger, nullable=False, default=0)
+
+ playerCount = db.Column(db.Integer, nullable=False, default=0)
+ maxPlayerCount = db.Column(db.Integer, nullable=False, default=20)
+
+ lastping = db.Column(db.DateTime, nullable=True)
+
+ reservedServerAccessCode = db.Column(db.Text, nullable=True)
+
+ def __init__(
+ self,
+ serveruuid,
+ originServerId,
+ serverIP,
+ serverPort,
+ serverPlaceId,
+
+ serverRunningTime=0,
+ playerCount=0,
+ maxPlayerCount=20,
+ reservedServerAccessCode=None
+ ):
+ self.serveruuid = serveruuid
+ self.originServerId = originServerId
+ self.serverIP = serverIP
+ self.serverPort = serverPort
+ self.serverPlaceId = serverPlaceId
+ self.serverRunningTime = serverRunningTime
+ self.playerCount = playerCount
+ self.maxPlayerCount = maxPlayerCount
+ self.lastping = datetime.utcnow()
+ self.reservedServerAccessCode = reservedServerAccessCode
+
+ def __repr__(self):
+ return "".format(
+ serveruuid=self.serveruuid,
+ originServerId=self.originServerId,
+ serverIP=self.serverIP,
+ serverPort=self.serverPort,
+ serverPlaceId=self.serverPlaceId
+ )
diff --git a/app/models/pointsservice.py b/app/models/pointsservice.py
new file mode 100644
index 0000000..5b7a480
--- /dev/null
+++ b/app/models/pointsservice.py
@@ -0,0 +1,15 @@
+from app.extensions import db
+
+class PointsService(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+ placeId = db.Column(db.BigInteger, nullable=False, index=True)
+ userId = db.Column(db.BigInteger, nullable=False, index=True)
+ points = db.Column(db.BigInteger, nullable=False, default=0)
+
+ def __init__(self, placeId, userId, points):
+ self.placeId = placeId
+ self.userId = userId
+ self.points = points
+
+ def __repr__(self):
+ return f""
\ No newline at end of file
diff --git a/app/models/previously_played.py b/app/models/previously_played.py
new file mode 100644
index 0000000..3663743
--- /dev/null
+++ b/app/models/previously_played.py
@@ -0,0 +1,16 @@
+from app.extensions import db
+from datetime import datetime
+
+class PreviouslyPlayed(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True, unique=True)
+ userid = db.Column(db.BigInteger, nullable=False, index=True)
+ lastplayed = db.Column(db.DateTime, nullable=False)
+ placeid = db.Column(db.BigInteger, nullable=False, index=True)
+
+ def __init__(self, userid, placeid):
+ self.userid = userid
+ self.placeid = placeid
+ self.lastplayed = datetime.utcnow()
+
+ def __repr__(self):
+ return f""
\ No newline at end of file
diff --git a/app/models/product_receipt.py b/app/models/product_receipt.py
new file mode 100644
index 0000000..918418f
--- /dev/null
+++ b/app/models/product_receipt.py
@@ -0,0 +1,31 @@
+from app.extensions import db
+from datetime import datetime
+
+class ProductReceipt( db.Model ):
+ receipt_id = db.Column( db.BigInteger, primary_key=True, nullable=False, unique=True, autoincrement=True )
+ user_id = db.Column( db.BigInteger, db.ForeignKey( 'user.id' ), nullable=False, index=True )
+ product_id = db.Column( db.BigInteger, db.ForeignKey( 'developer_product.productid' ), nullable=False, index=True )
+ robux_amount = db.Column( db.BigInteger, nullable=False )
+ is_processed = db.Column( db.Boolean, nullable=False, default = False )
+
+ created_at = db.Column( db.DateTime, nullable=False )
+
+ userObj = db.relationship( 'User', lazy='joined', uselist=False )
+ productObj = db.relationship( 'DeveloperProduct', lazy='joined', uselist=False )
+
+ def __init__( self, user_id, product_id, robux_amount ):
+ self.user_id = user_id
+ self.product_id = product_id
+ self.robux_amount = robux_amount
+
+ self.created_at = datetime.utcnow()
+
+ def __repr__( self ):
+ return "".format(
+ receipt_id=self.receipt_id,
+ user_id=self.user_id,
+ product_id=self.product_id,
+ robux_amount=self.robux_amount,
+ is_processed=self.is_processed,
+ created_at=self.created_at
+ )
\ No newline at end of file
diff --git a/app/models/universe.py b/app/models/universe.py
new file mode 100644
index 0000000..69c343d
--- /dev/null
+++ b/app/models/universe.py
@@ -0,0 +1,56 @@
+from app.extensions import db
+from datetime import datetime
+from app.enums.PlaceRigChoice import PlaceRigChoice
+from app.enums.PlaceYear import PlaceYear
+
+class Universe( db.Model ):
+ id = db.Column( db.BigInteger, primary_key=True, autoincrement=True, nullable=False )
+ root_place_id = db.Column( db.BigInteger, nullable=False, index=True, unique=True )
+ creator_id = db.Column( db.BigInteger, nullable=False, index=True )
+ creator_type = db.Column( db.SmallInteger, nullable=False )
+
+ created_at = db.Column( db.DateTime, nullable=False )
+ updated_at = db.Column( db.DateTime, nullable=False )
+
+ place_rig_choice = db.Column( db.Enum( PlaceRigChoice ), nullable=False, default = PlaceRigChoice.UserChoice )
+ place_year = db.Column( db.Enum( PlaceYear ), nullable=False, default = PlaceYear.Sixteen )
+ is_featured = db.Column( db.Boolean, nullable=False, default = False, index=True )
+ minimum_account_age = db.Column( db.Integer, nullable=False, default = 0 )
+ bc_required = db.Column( db.Boolean, nullable=False, default = False, index=True )
+ allow_direct_join = db.Column( db.Boolean, nullable=False, default = False, index=True )
+ is_public = db.Column( db.Boolean, nullable=False, default = True, index=True )
+ moderation_status = db.Column( db.SmallInteger, nullable=False, default = 0, index=True )
+ visit_count = db.Column( db.BigInteger, nullable=False, default = 0, index=True )
+
+ def __init__(
+ self,
+ root_place_id : int,
+ creator_id : int,
+ creator_type : int,
+
+ place_rig_choice : PlaceRigChoice = PlaceRigChoice.UserChoice,
+ place_year : PlaceYear = PlaceYear.Sixteen,
+ is_featured : bool = False,
+ minimum_account_age : int = 0,
+ bc_required : bool = False,
+ allow_direct_join : bool = False,
+ is_public : bool = True,
+
+ updated_at : datetime = None,
+ created_at : datetime = None
+ ):
+ self.root_place_id = root_place_id
+ self.creator_id = creator_id
+ self.creator_type = creator_type
+
+ self.place_rig_choice = place_rig_choice
+ self.place_year = place_year
+
+ self.is_featured = is_featured
+ self.minimum_account_age = minimum_account_age
+ self.bc_required = bc_required
+ self.allow_direct_join = allow_direct_join
+ self.is_public = is_public
+
+ self.created_at = datetime.utcnow() if created_at is None else created_at
+ self.updated_at = datetime.utcnow() if updated_at is None else updated_at
\ No newline at end of file
diff --git a/app/models/user.py b/app/models/user.py
new file mode 100644
index 0000000..f5a3fed
--- /dev/null
+++ b/app/models/user.py
@@ -0,0 +1,25 @@
+from app.extensions import db
+
+class User(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True, unique=True, nullable=False)
+ username = db.Column(db.Text, index=True, unique=True, nullable=False)
+ password = db.Column(db.Text, nullable=False)
+
+ created = db.Column(db.DateTime, nullable=False)
+ description = db.Column(db.Text, nullable=False, default="")
+ lastonline = db.Column(db.DateTime, nullable=False)
+
+ # 1 = OK, 2 = Banned, 3 = Deleted, 4 = Forgotten
+ accountstatus = db.Column(db.Integer, nullable=False, default=1)
+ TOTPEnabled = db.Column(db.Boolean, nullable=False, default=False)
+
+ def __init__( self, username, password, created, lastonline):
+ self.username = username
+ self.password = password
+ self.created = created
+ self.lastonline = lastonline
+ self.description = "Hi! I just joined Syntax!"
+
+ def __repr__(self):
+ return ''.format(self.username, str(self.id))
+
diff --git a/app/models/user_avatar.py b/app/models/user_avatar.py
new file mode 100644
index 0000000..696e671
--- /dev/null
+++ b/app/models/user_avatar.py
@@ -0,0 +1,43 @@
+from app.extensions import db
+
+class UserAvatar(db.Model):
+ user_id = db.Column(db.BigInteger, primary_key=True, nullable=False, unique=True) # User ID
+ content_hash = db.Column(db.String(512), nullable=True, default=None) # User avatar content hash
+ avatar_type = db.Column(db.SmallInteger, nullable=False, default=1) # Deprecated
+
+ # Refer to https://create.roblox.com/docs/reference/engine/datatypes/BrickColor
+ # for BrickColor IDs and names
+ # The only IDs that should be used is 1001-1032
+ head_color_id = db.Column(db.BigInteger, nullable=False, default=1001) # User avatar head color ID
+ torso_color_id = db.Column(db.BigInteger, nullable=False, default=1001) # User avatar torso color ID
+ right_arm_color_id = db.Column(db.BigInteger, nullable=False, default=1001) # User avatar right arm color ID
+ left_arm_color_id = db.Column(db.BigInteger, nullable=False, default=1001) # User avatar left arm color ID
+ right_leg_color_id = db.Column(db.BigInteger, nullable=False, default=1001) # User avatar right leg color ID
+ left_leg_color_id = db.Column(db.BigInteger, nullable=False, default=1001) # User avatar left leg color ID
+
+ r15 = db.Column(db.Boolean, nullable=True, default=False) # Is the user using R15?
+
+ height_scale = db.Column(db.Float, nullable=False, default=1.0) # User avatar height scale
+ width_scale = db.Column(db.Float, nullable=False, default=1.0) # User avatar width scale
+ head_scale = db.Column(db.Float, nullable=False, default=1.0) # User avatar head scale
+ proportion_scale = db.Column(db.Float, nullable=False, default=1.0) # User avatar proportion scale
+ body_type_scale = db.Column(db.Float, nullable=False, default=1.0) # User avatar body type scale
+
+ def __init__(
+ self,
+ user_id
+ ):
+ self.user_id = user_id
+
+ def __repr__(self):
+ return "".format(
+ user_id=self.user_id,
+ content_hash=self.content_hash,
+ avatar_type=self.avatar_type,
+ head_color_id=self.head_color_id,
+ torso_color_id=self.torso_color_id,
+ right_arm_color_id=self.right_arm_color_id,
+ left_arm_color_id=self.left_arm_color_id,
+ right_leg_color_id=self.right_leg_color_id,
+ left_leg_color_id=self.left_leg_color_id
+ )
\ No newline at end of file
diff --git a/app/models/user_avatar_asset.py b/app/models/user_avatar_asset.py
new file mode 100644
index 0000000..313f68b
--- /dev/null
+++ b/app/models/user_avatar_asset.py
@@ -0,0 +1,22 @@
+from app.extensions import db
+
+class UserAvatarAsset(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True) # ID
+ user_id = db.Column(db.BigInteger, nullable=False, index=True) # User ID
+ asset_id = db.Column(db.BigInteger, db.ForeignKey('asset.id'), nullable=False, index=True) # Asset ID
+
+ asset = db.relationship('Asset', uselist=False)
+
+ def __init__(
+ self,
+ user_id,
+ asset_id
+ ):
+ self.user_id = user_id
+ self.asset_id = asset_id
+
+ def __repr__(self):
+ return "".format(
+ user_id=self.user_id,
+ asset_id=self.asset_id
+ )
\ No newline at end of file
diff --git a/app/models/user_ban.py b/app/models/user_ban.py
new file mode 100644
index 0000000..3764491
--- /dev/null
+++ b/app/models/user_ban.py
@@ -0,0 +1,40 @@
+from app.extensions import db
+from datetime import datetime, timedelta
+from app.enums.BanType import BanType
+
+class UserBan(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+ userid = db.Column(db.BigInteger, db.ForeignKey('user.id'), nullable=False)
+ author_userid = db.Column(db.BigInteger, db.ForeignKey('user.id'), nullable=False)
+ reason = db.Column(db.String(512), nullable=False)
+ ban_type = db.Column(db.Enum(BanType), nullable=False)
+ moderator_note = db.Column(db.String(512), nullable=True)
+
+ created_at = db.Column(db.DateTime, nullable=False)
+ expires_at = db.Column(db.DateTime, nullable=True)
+
+ acknowledged = db.Column(db.Boolean, nullable=False, default=False)
+
+ def __init__(
+ self,
+ userid: int,
+ author_userid: int,
+ reason: str,
+ ban_type: BanType,
+ moderator_note: str,
+ expires_at: datetime,
+ created_at: datetime = None
+ ):
+ self.userid = userid
+ self.author_userid = author_userid
+ self.reason = reason
+ self.ban_type = ban_type
+ self.moderator_note = moderator_note
+ self.expires_at = expires_at
+
+ if created_at is None:
+ created_at = datetime.utcnow()
+ self.created_at = created_at
+
+ def __repr__(self):
+ return f''
\ No newline at end of file
diff --git a/app/models/user_email.py b/app/models/user_email.py
new file mode 100644
index 0000000..eb2e932
--- /dev/null
+++ b/app/models/user_email.py
@@ -0,0 +1,19 @@
+from app.extensions import db
+from datetime import datetime
+
+class UserEmail( db.Model ):
+ user_id = db.Column(db.BigInteger, db.ForeignKey('user.id'), primary_key=True)
+ email = db.Column(db.String(256), nullable=False, primary_key=True)
+ verified = db.Column(db.Boolean, nullable=False, default=False)
+ updated_at = db.Column(db.DateTime, nullable=False)
+
+ user = db.relationship("User", foreign_keys=[user_id], uselist=False, lazy="joined")
+
+ def __init__(self, user_id, email, verified):
+ self.user_id = user_id
+ self.email = email
+ self.verified = verified
+ self.updated_at = datetime.utcnow()
+
+ def __repr__(self):
+ return f""
\ No newline at end of file
diff --git a/app/models/user_hwid_log.py b/app/models/user_hwid_log.py
new file mode 100644
index 0000000..3b16973
--- /dev/null
+++ b/app/models/user_hwid_log.py
@@ -0,0 +1,18 @@
+from app.extensions import db
+from datetime import datetime, timedelta
+
+class UserHWIDLog( db.Model ):
+ id = db.Column( db.BigInteger, primary_key = True, autoincrement = True, nullable = False )
+ user_id = db.Column( db.BigInteger, db.ForeignKey( "user.id" ), nullable = False, index=True)
+ hwid = db.Column( db.Text, nullable = False, index=True )
+ created_at = db.Column( db.DateTime, nullable = False )
+
+ def __init__(
+ self,
+ user_id,
+ hwid,
+ created_at = None
+ ):
+ self.user_id = user_id
+ self.hwid = hwid
+ self.created_at = created_at or datetime.utcnow()
\ No newline at end of file
diff --git a/app/models/user_membership.py b/app/models/user_membership.py
new file mode 100644
index 0000000..a49b2ba
--- /dev/null
+++ b/app/models/user_membership.py
@@ -0,0 +1,24 @@
+from app.extensions import db
+from app.enums.MembershipType import MembershipType
+from datetime import datetime, timedelta
+
+class UserMembership(db.Model):
+ user_id = db.Column(db.BigInteger, db.ForeignKey('user.id'), primary_key=True)
+ membership_type = db.Column(db.Enum(MembershipType), nullable=False)
+ created = db.Column(db.DateTime, nullable=True)
+ expiration = db.Column(db.DateTime, nullable=True)
+ next_stipend = db.Column(db.DateTime, nullable=True)
+
+ def __init__(self, user_id, membership_type, created, expiration):
+ self.user_id = user_id
+ self.membership_type = membership_type
+ self.created = created
+ self.expiration = expiration
+ if created is not None:
+ self.next_stipend = created + timedelta(hours=24)
+ else:
+ self.next_stipend = None
+
+ def __repr__(self):
+ return '' % self.user_id
+
\ No newline at end of file
diff --git a/app/models/user_thumbnail.py b/app/models/user_thumbnail.py
new file mode 100644
index 0000000..ec23456
--- /dev/null
+++ b/app/models/user_thumbnail.py
@@ -0,0 +1,22 @@
+from app.extensions import db
+
+class UserThumbnail(db.Model):
+ userid = db.Column(db.BigInteger, primary_key=True, nullable=False, unique=True)
+ full_contenthash = db.Column(db.String(512), nullable=True)
+ headshot_contenthash = db.Column(db.String(512), nullable=True)
+ updated_at = db.Column(db.DateTime, nullable=True)
+
+ def __init__(self, userid, full_contenthash, headshot_contenthash, updated_at):
+ self.userid = userid
+ self.full_contenthash = full_contenthash
+ self.headshot_contenthash = headshot_contenthash
+ self.updated_at = updated_at
+
+ def __repr__(self):
+ return "".format(
+ userid=self.userid,
+ full_contenthash=self.full_contenthash,
+ headshot_contenthash=self.headshot_contenthash,
+ updated_at=self.updated_at
+ )
+
diff --git a/app/models/user_trade_items.py b/app/models/user_trade_items.py
new file mode 100644
index 0000000..6af6013
--- /dev/null
+++ b/app/models/user_trade_items.py
@@ -0,0 +1,17 @@
+from app.extensions import db
+
+class UserTradeItem(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+ tradeid = db.Column(db.BigInteger, nullable=False, index=True)
+ userid = db.Column(db.BigInteger, nullable=False, index=True)
+ user_asset_id = db.Column(db.BigInteger, db.ForeignKey("user_asset.id"), nullable=False)
+
+ userasset = db.relationship("UserAsset", backref="tradeitems", lazy=True)
+
+ def __init__(self, tradeid : int, userid : int, user_asset_id : int):
+ self.tradeid = tradeid
+ self.userid = userid
+ self.user_asset_id = user_asset_id
+
+ def __repr__(self):
+ return f""
\ No newline at end of file
diff --git a/app/models/user_trades.py b/app/models/user_trades.py
new file mode 100644
index 0000000..a8d52df
--- /dev/null
+++ b/app/models/user_trades.py
@@ -0,0 +1,39 @@
+from app.extensions import db
+from app.enums.TradeStatus import TradeStatus
+from datetime import datetime, timedelta
+class UserTrade(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+ sender_userid = db.Column(db.BigInteger, nullable=False, index=True)
+ recipient_userid = db.Column(db.BigInteger, nullable=False, index=True)
+
+ sender_userid_robux = db.Column(db.BigInteger, nullable=False, default=0) # Robux the sender is offering
+ recipient_userid_robux = db.Column(db.BigInteger, nullable=False, default=0) # Robux the recipient is offering
+
+ status = db.Column(db.Enum(TradeStatus), nullable=False, default=TradeStatus.Pending)
+
+ created_at = db.Column(db.DateTime, nullable=False)
+ updated_at = db.Column(db.DateTime, nullable=False)
+ expires_at = db.Column(db.DateTime, nullable=False)
+
+ def __init__(
+ self,
+ sender_userid : int,
+ recipient_userid : int,
+ sender_userid_robux : int = 0,
+ recipient_userid_robux : int = 0,
+ status : TradeStatus = TradeStatus.Pending,
+ created_at : datetime = None,
+ updated_at : datetime = None,
+ expires_at : datetime = None
+ ):
+ self.sender_userid = sender_userid
+ self.recipient_userid = recipient_userid
+ self.sender_userid_robux = sender_userid_robux
+ self.recipient_userid_robux = recipient_userid_robux
+ self.status = status
+ self.created_at = created_at or datetime.utcnow()
+ self.updated_at = updated_at or datetime.utcnow()
+ self.expires_at = expires_at or datetime.utcnow() + timedelta(days=7)
+
+ def __repr__(self):
+ return f""
\ No newline at end of file
diff --git a/app/models/user_transactions.py b/app/models/user_transactions.py
new file mode 100644
index 0000000..3662f38
--- /dev/null
+++ b/app/models/user_transactions.py
@@ -0,0 +1,51 @@
+from app.extensions import db
+from app.enums.TransactionType import TransactionType
+from sqlalchemy import Enum
+from datetime import datetime
+
+class UserTransaction(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, autoincrement=True)
+
+ reciever_id = db.Column(db.BigInteger, nullable=False, index=True)
+ reciever_type = db.Column(db.Integer, nullable=False, default=0) # 0 = User, 1 = Group
+ sender_id = db.Column(db.BigInteger, nullable=False, index=True)
+ sender_type = db.Column(db.Integer, nullable=False, default=0) # 0 = User, 1 = Group
+
+ currency_amount = db.Column(db.BigInteger, nullable=False)
+ currency_type = db.Column(db.Integer, nullable=False, default=0) # 0 = Robux, 1 = Tix
+
+ assetId = db.Column(db.BigInteger, nullable=True, default=None, index=True)
+ custom_text = db.Column(db.Text, nullable=True, default=None)
+
+ transaction_type = db.Column(Enum(TransactionType), nullable=False, index=True)
+ created_at = db.Column(db.DateTime, nullable=False)
+
+ def __init__(
+ self,
+ reciever_id,
+ reciever_type,
+ sender_id,
+ sender_type,
+ currency_amount,
+ currency_type,
+ transaction_type,
+ asset_id=None,
+ custom_text=None,
+ created_at=None
+ ):
+ self.reciever_id = reciever_id
+ self.reciever_type = reciever_type
+ self.sender_id = sender_id
+ self.sender_type = sender_type
+ self.currency_amount = currency_amount
+ self.currency_type = currency_type
+ self.transaction_type = transaction_type
+ self.assetId = asset_id
+ self.custom_text = custom_text
+
+ if created_at is None:
+ self.created_at = datetime.utcnow()
+ else:
+ self.created_at = created_at
+ def __repr__(self):
+ return "" % self.id
\ No newline at end of file
diff --git a/app/models/userassets.py b/app/models/userassets.py
new file mode 100644
index 0000000..448727a
--- /dev/null
+++ b/app/models/userassets.py
@@ -0,0 +1,43 @@
+from app.extensions import db
+from datetime import datetime
+
+class UserAsset(db.Model):
+ id = db.Column(db.BigInteger, primary_key=True, nullable=False, unique=True, autoincrement=True)
+ userid = db.Column(db.BigInteger, nullable=False, index=True)
+ assetid = db.Column(db.BigInteger, db.ForeignKey("asset.id"), nullable=False, index=True)
+
+ serial = db.Column(db.BigInteger, nullable=True, default=None)
+ price = db.Column(db.BigInteger, nullable=False, default=0)
+
+ created = db.Column(db.DateTime, nullable=False)
+ updated = db.Column(db.DateTime, nullable=False)
+
+ is_for_sale = db.Column(db.Boolean, nullable=False, default=False)
+
+ asset = db.relationship("Asset", foreign_keys=[assetid], uselist=False, lazy="joined")
+
+ def __init__(
+ self,
+ userid,
+ assetid,
+
+ serial=None,
+ price=0
+ ):
+ self.userid = userid
+ self.assetid = assetid
+ self.serial = serial
+ self.price = price
+ self.created = datetime.utcnow()
+ self.updated = datetime.utcnow()
+
+ def __repr__(self):
+ return "".format(
+ id=self.id,
+ userid=self.userid,
+ assetid=self.assetid,
+ serial=self.serial,
+ price=self.price,
+ created=self.created,
+ updated=self.updated
+ )
\ No newline at end of file
diff --git a/app/models/usereconomy.py b/app/models/usereconomy.py
new file mode 100644
index 0000000..3aaffa0
--- /dev/null
+++ b/app/models/usereconomy.py
@@ -0,0 +1,16 @@
+from app.extensions import db
+
+class UserEconomy(db.Model):
+ userid = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True, unique=True, nullable=False )
+ robux = db.Column(db.Integer, nullable=False, default=0)
+ tix = db.Column(db.Integer, nullable=False, default=0)
+
+ user = db.relationship('User', backref=db.backref('economy', lazy=True, uselist=False), uselist=False)
+
+ def __init__(self, userid, robux, tix):
+ self.userid = userid
+ self.robux = robux
+ self.tix = tix
+
+ def __repr__(self):
+ return ''.format(self.userid, str(self.robux), str(self.tix))
\ No newline at end of file
diff --git a/app/pages/403.html b/app/pages/403.html
new file mode 100644
index 0000000..9bdf834
--- /dev/null
+++ b/app/pages/403.html
@@ -0,0 +1,12 @@
+{% extends '__layout__.html' %}
+{% block title %}Not Found{% endblock %}
+{% block head %}
+{% endblock %}
+{% block content %}
+
+
+
403 Forbidden
+
You do not have the required permission to access this page, please contact an administrator if you think this is a mistake.
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/pages/404.html b/app/pages/404.html
new file mode 100644
index 0000000..887a1f9
--- /dev/null
+++ b/app/pages/404.html
@@ -0,0 +1,12 @@
+{% extends '__layout__.html' %}
+{% block title %}Not Found{% endblock %}
+{% block head %}
+{% endblock %}
+{% block content %}
+
+
+
404 Not Found
+
This page does not exist or has been removed, we apologise for any inconvenience caused.
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/pages/405.html b/app/pages/405.html
new file mode 100644
index 0000000..56ce04c
--- /dev/null
+++ b/app/pages/405.html
@@ -0,0 +1,12 @@
+{% extends '__layout__.html' %}
+{% block title %}Method Not Allowed{% endblock %}
+{% block head %}
+{% endblock %}
+{% block content %}
+
+
+
405 Method Not Allowed
+
The method is not allowed for the requested URL.
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/pages/500.html b/app/pages/500.html
new file mode 100644
index 0000000..77a3e39
--- /dev/null
+++ b/app/pages/500.html
@@ -0,0 +1,25 @@
+{% extends '__layout__.html' %}
+{% block title %}Not Found{% endblock %}
+{% block head %}
+{% endblock %}
+{% block content %}
+
+
+
500 Internal Server Error
+
Uh oh, something went wrong on our end, we apologise for any inconvenience caused.
+ {% if error %}
+
+
+
+
+ {% endif %}
+
Route: {{page}} | Please report this error in our Discord Server
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/pages/__layout__.html b/app/pages/__layout__.html
new file mode 100644
index 0000000..c37fb43
--- /dev/null
+++ b/app/pages/__layout__.html
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% if currentuser: %}{% endif %}
+
+
+ {% block title %}{% endblock %} - Syntax
+
+ {% block head %}{% endblock %}
+
+
+
+ {% with messages = get_flashed_messages(with_categories=true) %}
+ {% if messages %}
+
+ {% for category, message in messages %}
+ {% if category == 'error': %}
+
+ {{ message }}
+
+ {% endif %}
+ {% if category == 'success': %}
+
+ {{ message }}
+
+ {% endif %}
+ {% endfor %}
+
+ {% endif %}
+ {% endwith %}
+
Do not abuse this tool, some extra modifications may be needed when cloning a bundle as it is not always perfect once cloned. Also it might take awhile for it copy all assets, and we recommend to not use it when anyone is in game.
+
WARNING: PLEASE make sure the bundle you are copying has not already been uploaded
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/pages/admin/createasset.html b/app/pages/admin/createasset.html
new file mode 100644
index 0000000..4dfea7c
--- /dev/null
+++ b/app/pages/admin/createasset.html
@@ -0,0 +1,59 @@
+{% extends '__layout__.html' %}
+{% block title %}Admin{% endblock %}
+{% block head %}
+
+{% endblock %}
+{% block content %}
+
You can create an offer to be placed onto the currency exchange system, this will allow other users to trade for example Robux to Tickets or vice versa.
+
+
Note: The minimum for Robux to Tix is 1:2 while the maximum is 1:20
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/pages/currencyexchange/index.html b/app/pages/currencyexchange/index.html
new file mode 100644
index 0000000..0285a47
--- /dev/null
+++ b/app/pages/currencyexchange/index.html
@@ -0,0 +1,114 @@
+{% extends '__layout__.html' %}
+{% block title %}Currency Exchange{% endblock %}
+{% block head %}
+
+{% endblock %}
+{% block content %}
+
+ {% with messages = get_flashed_messages(with_categories=true) %}
+ {% if messages %}
+
+ {% for category, message in messages %}
+ {% if category == 'error': %}
+
+
+{%endblock%}
\ No newline at end of file
diff --git a/app/pages/groups/admin_subpage/payout.html b/app/pages/groups/admin_subpage/payout.html
new file mode 100644
index 0000000..e864f70
--- /dev/null
+++ b/app/pages/groups/admin_subpage/payout.html
@@ -0,0 +1,42 @@
+{% extends 'groups/admin_template.html' %}
+{% block page_content%}
+
+
+
One-time Payout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Enter the username of the recipient you want to pay! This is CaSe SeNsItIvE and please double check before clicking payout, all payments are final and cannot be reversed
+
+
+
+
+
+
+{%endblock%}
\ No newline at end of file
diff --git a/app/pages/groups/admin_subpage/roles.html b/app/pages/groups/admin_subpage/roles.html
new file mode 100644
index 0000000..c65a1fe
--- /dev/null
+++ b/app/pages/groups/admin_subpage/roles.html
@@ -0,0 +1,198 @@
+{% extends 'groups/admin_template.html' %}
+{% block page_content%}
+
+
+
+
+
Roles
+
+ {% for role in roles %}
+ {{role.name}}
+ {% endfor %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/pages/notapproved/notapproved.py b/app/pages/notapproved/notapproved.py
new file mode 100644
index 0000000..d52d53b
--- /dev/null
+++ b/app/pages/notapproved/notapproved.py
@@ -0,0 +1,88 @@
+from flask import Blueprint, render_template, request, redirect, url_for, flash, session, abort
+from app.models.user_ban import UserBan
+from app.extensions import db, csrf, limiter, redis_controller
+from app.models.user import User
+from app.enums.BanType import BanType
+from app.util import auth
+from datetime import datetime, timedelta
+
+NotApprovedRoute = Blueprint('notapproved', __name__, template_folder="pages")
+
+BanTypeToText = {
+ BanType.Warning: "Warning",
+ BanType.Day1Ban: "Banned for 1 day",
+ BanType.Day3Ban: "Banned for 3 days",
+ BanType.Day7Ban: "Banned for 7 days",
+ BanType.Day14Ban: "Banned for 14 days",
+ BanType.Day30Ban: "Banned for 30 days",
+ BanType.Deleted: "Account Deleted",
+}
+
+@NotApprovedRoute.route("/not-approved", methods=["GET"])
+def not_approved():
+ CurrentUser : User = auth.GetCurrentUser()
+ if CurrentUser is not None:
+ return redirect("/")
+
+ if "not-approved-viewer" not in session:
+ abort(404)
+
+ UserObj : User = User.query.filter_by(id=session["not-approved-viewer"]).first()
+ if UserObj is None:
+ del session["not-approved-viewer"]
+ abort(404)
+
+ if UserObj.accountstatus == 4:
+ del session["not-approved-viewer"]
+ return redirect("/")
+
+ if UserObj.accountstatus == 1:
+ del session["not-approved-viewer"]
+ return redirect("/")
+
+ LatestUserBanObj : UserBan = UserBan.query.filter_by(userid=UserObj.id, acknowledged = False).order_by(UserBan.id.desc()).first()
+ if LatestUserBanObj is None:
+ del session["not-approved-viewer"]
+ UserObj.accountstatus = 1
+ db.session.commit()
+ return redirect("/")
+ hasBanExpired = False
+ if LatestUserBanObj.expires_at is not None:
+ if LatestUserBanObj.expires_at < datetime.utcnow():
+ hasBanExpired = True
+
+ return render_template("notapproved/banpage.html", user=UserObj,
+ userban=LatestUserBanObj,
+ banText=BanTypeToText[LatestUserBanObj.ban_type],
+ hasBanExpired=hasBanExpired)
+
+@NotApprovedRoute.route("/not-approved", methods=["POST"])
+def not_approved_post():
+ CurrentUser : User = auth.GetCurrentUser()
+ if CurrentUser is not None:
+ return redirect("/")
+ if "not-approved-viewer" not in session:
+ abort(404)
+
+ UserObj : User = User.query.filter_by(id=session["not-approved-viewer"]).first()
+ if UserObj is None:
+ del session["not-approved-viewer"]
+ abort(404)
+
+ if UserObj.accountstatus == 1:
+ del session["not-approved-viewer"]
+ return redirect("/")
+
+ LatestUserBanObj : UserBan = UserBan.query.filter_by(userid=UserObj.id, acknowledged = False).order_by(UserBan.id.desc()).first()
+ if LatestUserBanObj is None:
+ del session["not-approved-viewer"]
+ UserObj.accountstatus = 1
+ db.session.commit()
+ return redirect("/")
+
+ LatestUserBanObj.acknowledged = True
+ UserObj.accountstatus = 1
+ db.session.commit()
+ del session["not-approved-viewer"]
+ flash("Your account has been reactivated", "success")
+ return redirect("/login")
\ No newline at end of file
diff --git a/app/pages/privacy.html b/app/pages/privacy.html
new file mode 100644
index 0000000..7a824f8
--- /dev/null
+++ b/app/pages/privacy.html
@@ -0,0 +1,43 @@
+{% extends '__layout__.html' %}
+{% block title %}Privacy Policy{% endblock %}
+{% block head %}
+
+{% endblock %}
+{% block content %}
+
+
Privacy Policy
+
Last amended: 22/10/2023
+
+
This Privacy Policy ("Policy") describes how Syntax ("we," "us," or "our") collects, uses, and protects the personal information you provide when accessing or using the Syntax website (the "Website").
+
1. Information Collection and Use
+
1.1. We may collect and store your IP address when you access or use the Website. This information is collected to prevent any misuse of our systems and ensure the security and integrity of the Website.
+
1.2. In order to prevent malicious activities and protect the Website, we may also collect and store hardware information about your machine, such as device type, operating system, and browser version.
+
+
2. Information Sharing and Disclosure
+
2.1. We do not sell, trade, or otherwise transfer your personal information to third parties for marketing or advertising purposes.
+
2.2. We may share your personal information with trusted third-party service providers who assist us in operating the Website, conducting our business, or servicing you, as long as they agree to keep this information confidential.
+
2.3. We may disclose your personal information if required by law or if we believe that such action is necessary to comply with legal obligations, protect our rights and property, or ensure the safety of our users or the public.
+
+
3. Cookies and Tracking Technologies
+
3.1. We use cookies and similar tracking technologies to enhance your experience on the Website. Cookies are small files stored on your device that allow us to recognize and remember your preferences.
+
3.2. We also use third-party services, such as Cloudflare, Google Analytics and Hcaptcha, which may use cookies and other tracking technologies. Please review their privacy policies for more information on how they handle your data.
+
+
4. Data Security
+
4.1. We implement reasonable security measures to protect your personal information against unauthorized access, alteration, disclosure, or destruction.
+
4.2. However, please note that no method of transmission over the Internet or electronic storage is 100% secure. Therefore, while we strive to protect your personal information, we cannot guarantee its absolute security.
+
+
5. Children's Privacy
+
5.1. The Website is not intended for use by individuals under the age of 13. We do not knowingly collect personal information from children. If we become aware that we have collected personal information from a child without parental consent, we will take steps to remove that information from our servers.
+
+
6. Links to Third-Party Websites
+
6.1. The Website may contain links to third-party websites or services. We are not responsible for the privacy practices or the content of those third-party websites. We encourage you to review the privacy policies of those websites before providing any personal information.
+
+
7. Changes to the Privacy Policy
+
7.1. We reserve the right to update or modify this Privacy Policy at any time. Any changes will be effective immediately upon posting the updated Policy on the Website. Your continued use of the Website after any such changes constitutes your acceptance of the modified Privacy Policy.
+
+
8. Contact Us
+
8.1. If you have any questions or concerns about this Privacy Policy or our privacy practices, please contact us on our discord server.
+
+
By accessing or using the Syntax Website, you acknowledge that you have read, understood, and agreed to the terms and conditions of this Privacy Policy.
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/pages/profiles/followers.html b/app/pages/profiles/followers.html
new file mode 100644
index 0000000..d0533da
--- /dev/null
+++ b/app/pages/profiles/followers.html
@@ -0,0 +1,64 @@
+{% extends '__layout__.html' %}
+{% block title %}{{profile.username}}{% endblock %}
+{% block head %}
+
+
+{% endblock %}
+{% block content %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/pages/signup/signup.py b/app/pages/signup/signup.py
new file mode 100644
index 0000000..0f4faa6
--- /dev/null
+++ b/app/pages/signup/signup.py
@@ -0,0 +1,263 @@
+from flask import Blueprint, render_template, request, redirect, url_for, session, flash, redirect, make_response, abort
+from app.models.user import User
+from app.models.invite_key import InviteKey
+from app.models.usereconomy import UserEconomy
+from app.models.user_avatar import UserAvatar
+from app.models.login_records import LoginRecord
+from app.util.textfilter import FilterText, TextNotAllowedException
+from app.models.past_usernames import PastUsername
+from app.pages.login.login import CreateLoginRecord
+from app.extensions import db, limiter, csrf, redis_controller, get_remote_address
+from app.pages.avatar.avatar import AllowedBodyColors
+from app.routes.thumbnailer import TakeUserThumbnail
+import logging
+import hashlib
+from datetime import datetime
+from app.util import websiteFeatures, auth, turnstile, redislock
+from app.services import invitekeys, proxydetection
+from sqlalchemy import func
+import random
+from datetime import datetime, timedelta
+from config import Config
+import string
+
+config = Config()
+signup = Blueprint("signup", __name__, template_folder="pages")
+
+@signup.route("/signup", methods=["GET"])
+@limiter.exempt()
+def signup_page():
+ if auth.isAuthenticated():
+ return redirect("/home")
+ SignupEnabled = True
+ if not websiteFeatures.GetWebsiteFeature("WebSignup"):
+ flash("Signups are temporarily disabled")
+ SignupEnabled = False
+ return render_template("signup/signup.html", SignupEnabled=SignupEnabled)
+
+def RateLimitReached(e):
+ flash("You are being rate limited. Please try again later.")
+ return redirect("/signup")
+
+def isUsernameAllowed( username : str ):
+ if len(username) < 3 or len(username) > 20:
+ return False, "Usernames must be between 3 and 20 characters long"
+
+ if username[0] == "_" or username[-1] == "_":
+ return False, "Usernames must not start or end with an underscore"
+
+ if username.count("_") > 1:
+ return False, "Usernames must not contain more than one underscore"
+
+ if username.count(" ") > 0:
+ return False, "Usernames must not contain spaces"
+
+ allowedCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
+ hasLetter = False
+ for char in username:
+ if char not in allowedCharacters:
+ return False, "Usernames must only contain letters, numbers and underscores"
+ if char in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":
+ hasLetter = True
+
+ if not hasLetter:
+ return False, "Usernames must contain at least one letter"
+
+ return True, "Allowed"
+
+@signup.route("/signup", methods=["POST"])
+@limiter.limit("10/minute", on_breach=RateLimitReached)
+def signup_post():
+ if auth.isAuthenticated():
+ return redirect("/home")
+ username = request.form.get("username", default=None, type=str)
+ password = request.form.get("password", default=None, type=str)
+ agreedtoTerms = request.form.get("agreetoTermsandPrivacy", default="off", type=str) == "on"
+ #invitekey = request.form.get("invite-key", default=None, type=str)
+
+ if not websiteFeatures.GetWebsiteFeature("WebSignup"):
+ flash("Signups are temporarily disabled")
+ return redirect("/signup")
+ if 'cf-turnstile-response' not in request.form or request.form.get('cf-turnstile-response') == '':
+ flash("Please complete the captcha", "error")
+ return redirect("/signup")
+
+ if username is None or password is None: #or invitekey is None:
+ flash("Please fill in all fields")
+ return redirect("/signup")
+
+ if not agreedtoTerms:
+ flash("You must agree to the Terms of service and privacy policy")
+ return redirect("/signup")
+
+ allowed, reason = isUsernameAllowed(username)
+ if not allowed:
+ flash(reason)
+ return redirect("/signup")
+
+ if len(password) < 8:
+ flash("Password must be at least 8 characters long")
+ return redirect("/signup")
+
+ try:
+ FilterText( Text = username, ThrowException = True, UseExtendedBadWords=True)
+ except TextNotAllowedException:
+ flash("Username is not friendly for Syntax")
+ return redirect("/signup")
+
+ RequestAddressRisk : int = proxydetection.fetch_address_risk( get_remote_address() )
+ if RequestAddressRisk == 1:
+ flash("Please disable your VPN or proxy and try again")
+ return redirect("/signup")
+
+ if RequestAddressRisk == 3:
+ flash("Error 255, please report in our discord.") # We want to be as vague as possible
+ return redirect("/signup")
+ redisResultIP = redis_controller.get("signupcooldown:" + get_remote_address())
+ redisResultToken = None
+ if request.cookies.get("t", default=None, type=str) is not None:
+ redisResultToken = redis_controller.get("signupcooldown:token:" + str(request.cookies.get(key="t", default=None, type=str)))
+
+ if redisResultIP is not None or redisResultToken is not None:
+ if redisResultIP is None:
+ redis_controller.set("signupcooldown:" + get_remote_address(), "1", ex=60*60*24*7)
+ if redisResultToken is None and request.cookies.get("t", default=None, type=str) is not None:
+ redis_controller.set("signupcooldown:token:" + str(request.cookies.get(key="t", default=None, type=str)), "1", ex=60*60*24*7)
+
+ flash("You have recently made an account in the past 7 days.")
+ return redirect("/signup")
+
+ SessionToken = request.cookies.get(key="t", default=None, type=str)
+ if SessionToken is None:
+ flash("An issue occured while creating your account. Please try again later.")
+ return redirect("/signup")
+ if len(SessionToken) != 128:
+ flash("An issue occured while creating your account. Please try again later.")
+ return redirect("/signup")
+ for char in SessionToken:
+ if char not in (string.ascii_letters + string.digits):
+ flash("An issue occured while creating your account. Please try again later.")
+ return redirect("/signup")
+
+ UserIPHash = hashlib.md5(get_remote_address().encode("utf-8")).hexdigest()
+ LoginRecords : list[LoginRecord] = LoginRecord.query.filter(LoginRecord.ip == UserIPHash).distinct(LoginRecord.userid).all()
+ for record in LoginRecords:
+ if record.User.accountstatus != 1:
+ if record.session_token != SessionToken:
+ NewLoginRecord = LoginRecord(
+ userid = record.userid,
+ ip = UserIPHash,
+ session_token = SessionToken,
+ useragent = request.headers.get("User-Agent"),
+ timestamp = datetime.utcnow()
+ )
+ db.session.add(NewLoginRecord)
+ db.session.commit()
+
+ flash("An issue occured while creating your account. Please try again later.")
+ return redirect("/signup")
+ LoginRecords : list[LoginRecord] = LoginRecord.query.filter(LoginRecord.session_token == SessionToken).distinct(LoginRecord.userid).all()
+ for record in LoginRecords:
+ if record.User.accountstatus != 1:
+ NewLoginRecord = LoginRecord(
+ userid = record.userid,
+ ip = UserIPHash,
+ session_token = SessionToken,
+ useragent = request.headers.get("User-Agent"),
+ timestamp = datetime.utcnow()
+ )
+ db.session.add(NewLoginRecord)
+ db.session.commit()
+
+ flash("An issue occured while creating your account. Please try again later.")
+ return redirect("/signup")
+
+ UserSignupLock = redislock.acquire_lock("UserSignupLock", acquire_timeout = 20, lock_timeout=1)
+ if not UserSignupLock:
+ flash("An issue occured while creating your account. Please try again later.")
+ return redirect("/signup")
+
+ user = User.query.filter(func.lower(User.username) == func.lower(username)).first()
+ if user is not None:
+ redislock.release_lock("UserSignupLock", UserSignupLock)
+ flash("Username already taken")
+ return redirect("/signup")
+
+ pastUsername = PastUsername.query.filter(func.lower(PastUsername.username) == func.lower(username)).first()
+ if pastUsername is not None:
+ redislock.release_lock("UserSignupLock", UserSignupLock)
+ flash("Username already taken")
+ return redirect("/signup")
+
+ # try:
+ # inviteKey : InviteKey = invitekeys.GetInviteKey(invitekey)
+ # except invitekeys.InviteExceptions.InvalidInviteKey:
+ # redislock.release_lock("UserSignupLock", UserSignupLock)
+ # flash("Invalid invite key")
+ # return redirect("/signup")
+ # if inviteKey.used_by is not None:
+ # redislock.release_lock("UserSignupLock", UserSignupLock)
+ # flash("Invite key already used")
+ # return redirect("/signup")
+ # InviteKeyCreator : User = User.query.filter_by(id=inviteKey.created_by).first()
+ # if InviteKeyCreator is not None:
+ # if InviteKeyCreator.accountstatus != 1:
+ # redislock.release_lock("UserSignupLock", UserSignupLock)
+ # flash("Invite key creator's account is banned")
+ # return redirect("/signup")
+
+ if not turnstile.VerifyToken(request.form.get('cf-turnstile-response')):
+ redislock.release_lock("UserSignupLock", UserSignupLock)
+ flash("Invalid captcha", "error")
+
+ if request.cookies.get(key="t", default=None, type=str) is None:
+ abort(400)
+
+ redis_controller.set("signupcooldown:" + get_remote_address(), "1", ex=60*60*24*7)
+ if request.cookies.get("t", default=None, type=str) is not None:
+ redis_controller.set("signupcooldown:token:" + str(request.cookies.get(key="t", default=None, type=str)), "1", ex=60*60*24*7)
+
+ NewRegisteredUser : User = User(
+ username = username,
+ password = "",
+ created = datetime.utcnow(),
+ lastonline = datetime.utcnow(),
+ )
+
+ db.session.add(NewRegisteredUser)
+ db.session.commit()
+
+ auth.SetPassword(NewRegisteredUser, password)
+
+ userEconomy = UserEconomy(
+ userid = NewRegisteredUser.id,
+ robux = 0,
+ tix = 0
+ )
+ db.session.add(userEconomy)
+
+ userAvatar : UserAvatar = UserAvatar(
+ user_id=NewRegisteredUser.id
+ )
+ userAvatar.torso_color_id = random.choice(AllowedBodyColors)
+ db.session.add(userAvatar)
+
+ #invitekeys.UseInviteKey(invitekey, NewRegisteredUser)
+
+ db.session.commit()
+ redislock.release_lock("UserSignupLock", UserSignupLock)
+ logging.info(f"New user registered: {NewRegisteredUser.username} ({NewRegisteredUser.id})")
+
+ TakeUserThumbnail(NewRegisteredUser.id)
+ sessionToken = auth.CreateToken(userid=NewRegisteredUser.id, ip=get_remote_address())
+ resp = make_response(redirect("/home"))
+ resp.set_cookie(".ROBLOSECURITY", sessionToken, expires=datetime.utcnow() + timedelta(days=365), domain=f".{config.BaseDomain}")
+
+ if request.cookies.get("t", default=None, type=str) is not None:
+ NewToken = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(128))
+ resp.set_cookie("t", NewToken, expires=datetime.utcnow() + timedelta(days=365), domain=f".{config.BaseDomain}")
+ CreateLoginRecord( NewRegisteredUser.id, NewToken )
+ else:
+ CreateLoginRecord( NewRegisteredUser.id )
+
+ return resp
\ No newline at end of file
diff --git a/app/pages/static.py b/app/pages/static.py
new file mode 100644
index 0000000..d878aae
--- /dev/null
+++ b/app/pages/static.py
@@ -0,0 +1,28 @@
+# Pages which has no function but to display static content
+from flask import Blueprint, render_template, redirect
+from app.util import auth
+
+static = Blueprint("static", __name__, template_folder="pages")
+
+@static.route("/terms")
+def terms():
+ return render_template("terms.html")
+
+@static.route("/privacy")
+def privacy():
+ return render_template("privacy.html")
+
+@static.route("/download")
+@auth.authenticated_required
+def download():
+ return render_template("downloads.html")
+
+@static.route("/Games.aspx")
+@auth.authenticated_required
+def gamesaspx():
+ return redirect("/games")
+
+@static.route("/drivers")
+@auth.authenticated_required
+def drivers():
+ return render_template("drivers.html")
diff --git a/app/pages/studio/myPlaces.html b/app/pages/studio/myPlaces.html
new file mode 100644
index 0000000..fa9ecda
--- /dev/null
+++ b/app/pages/studio/myPlaces.html
@@ -0,0 +1,94 @@
+
+
+
+
+ SYNTAX
+
+
+
+
+
+ {% if not currentuser: %}
+
+
+
+
SYNTAX
+
relive those childhood memories
+ {% with messages = get_flashed_messages() %}
+ {% if messages %}
+
+ {% for message in messages %}
+
+ {{ message }}
+
+ {% endfor %}
+
+ {% endif %}
+ {% endwith %}
+
+
+
+
+
+
+
+
+
+
+ {%else%}
+
Logged in
+ {%endif%}
+
+
\ No newline at end of file
diff --git a/app/pages/studio/studiopages.py b/app/pages/studio/studiopages.py
new file mode 100644
index 0000000..ec27af4
--- /dev/null
+++ b/app/pages/studio/studiopages.py
@@ -0,0 +1,15 @@
+from flask import Blueprint, render_template, request, redirect, url_for, flash, make_response, jsonify, abort
+
+StudioPagesRoute = Blueprint('studiopages', __name__, url_prefix='/')
+
+@StudioPagesRoute.before_request
+def before_request():
+ BrowserUserAgent = request.headers.get('User-Agent')
+ if BrowserUserAgent is None:
+ return abort(404)
+ if "RobloxStudio" not in BrowserUserAgent:
+ return abort(404)
+
+@StudioPagesRoute.route("/ide/welcome", methods=['GET'])
+def myPlaces():
+ return render_template("studio/myPlaces.html")
\ No newline at end of file
diff --git a/app/pages/swaggerdocs.html b/app/pages/swaggerdocs.html
new file mode 100644
index 0000000..2695da9
--- /dev/null
+++ b/app/pages/swaggerdocs.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+ Swagger UI
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/pages/terms.html b/app/pages/terms.html
new file mode 100644
index 0000000..ba486db
--- /dev/null
+++ b/app/pages/terms.html
@@ -0,0 +1,52 @@
+{% extends '__layout__.html' %}
+{% block title %}Terms of Service{% endblock %}
+{% block head %}
+
+{% endblock %}
+{% block content %}
+
+
Terms of Service
+
Last amended: 30/5/2023
+
+
Please read these Terms of Service ("Terms") carefully before using the Syntax website (the "Website") operated by Syntax ("we," "us," or "our").
+
By accessing or using the Website, you agree to be bound by these Terms. If you disagree with any part of the Terms, please do not access or use the Website.
+
+
1. Acceptance of Terms
+
1.1. By accessing or using the Website, you acknowledge that you have read, understood, and agree to be bound by these Terms, as well as any additional guidelines, rules, or terms of service that may be provided by us.
+
1.2. These Terms constitute a legally binding agreement between you and Syntax regarding your use of the Website.
+
+
2. Website Usage Restrictions
+
2.1. You agree to use the Website only for lawful purposes and in a manner consistent with all applicable laws and regulations.
+
2.2. You agree not to post, upload, transmit, distribute, or otherwise publish through the Website any content that violates or infringes upon the rights of others, including but not limited to NSFW (Not Safe for Work) content, racist content, or content that promotes hatred or discrimination.
+
2.3. You agree not to engage in any activities that may disrupt or interfere with the proper functioning of the Website, including but not limited to exploiting game servers, misusing web APIs, scamming, or impersonating others.
+
2.4. Users under the age of 13 are prohibited from accessing the Website, and their access will be terminated if discovered.
+
+
3. Intellectual Property Rights
+
3.1. The Website and its original content, features, and functionality are owned by Syntax and are protected by international copyright, trademark, patent, trade secret, and other intellectual property or proprietary rights laws.
+
3.2. You may not copy, reproduce, modify, distribute, transmit, display, perform, or create derivative works from or exploit in any way the Website or any of its content without the prior written consent of Syntax.
+
+
4. Privacy
+
4.1. We respect your privacy and handle your personal information in accordance with our Privacy Policy. By using the Website, you consent to the collection, use, and disclosure of your information as described in the Privacy Policy.
+
+
5. Limitation of Liability
+
5.1. You agree that Syntax shall not be liable for any direct, indirect, incidental, special, consequential, or exemplary damages, including but not limited to damages for loss of profits, goodwill, use, data, or other intangible losses, resulting from your use or inability to use the Website.
+
+
6. Indemnification
+
6.1. You agree to indemnify and hold Syntax harmless from any claim or demand, including reasonable attorneys' fees, made by any third party due to or arising out of your breach of these Terms or your violation of any law or the rights of a third party.
+
+
7. Modifications to Terms of Service
+
7.1. Syntax reserves the right to modify or replace these Terms at any time. Any changes will be effective immediately upon posting the updated Terms on the Website. Your continued use of the Website after any such changes constitutes your acceptance of the modified Terms.
+
+
8. Termination
+
8.1. We may terminate or suspend your access to the Website, without prior notice or liability, for any reason whatsoever, including but not limited to a breach of these Terms.
+
8.2. All provisions of these Terms, which by their nature should survive termination, shall survive termination, including but not limited to intellectual property rights, limitation of liability, and indemnification.
+
+
9. Governing Law
+
9.1. These Terms shall be governed and construed in accordance with the laws of Singapore. Any dispute arising out of or relating to these Terms or the Website shall be exclusively resolved in the courts located in Singapore.
+
+
10. Contact Us
+
10.1. If you have any questions or concerns regarding these Terms, please contact us on our discord server.
+
+
By accessing or using the Syntax Website, you acknowledge that you have read, understood, and agreed to these Terms of Service.
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/pages/trades/create.html b/app/pages/trades/create.html
new file mode 100644
index 0000000..beec637
--- /dev/null
+++ b/app/pages/trades/create.html
@@ -0,0 +1,133 @@
+{% extends '__layout__.html' %}
+{% block title %}New Trade{% endblock %}
+{% block head %}
+
+
+{% endblock %}
+{% block content %}
+