Initial commit
This commit is contained in:
commit
fcf92829e8
|
|
@ -0,0 +1,10 @@
|
|||
config.py
|
||||
__pycache__/
|
||||
assets/
|
||||
proxies.txt
|
||||
*.pem
|
||||
logs/*.log*
|
||||
download_cache/*
|
||||
*.key
|
||||
*.crt
|
||||
!download_cache/KEEPME
|
||||
|
|
@ -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
|
||||
|
|
@ -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'<a href="\1">\1</a>', 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 "<h1 style='margin:0;'>404 - Page not found</h1><br><a style='margin:0;' href='/ide/welcome'>Return to homepage</a>"
|
||||
#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 "<h1 style='margin:0;'>403 - Forbidden</h1><br><a style='margin:0;' href='/ide/welcome'>Return to homepage</a>"
|
||||
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 "<h1 style='margin:0;'>405 - Method Not Allowed</h1><br><a style='margin:0;' href='/ide/welcome'>Return to homepage</a>"
|
||||
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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
from enum import Enum
|
||||
|
||||
class ChatStyle(Enum):
|
||||
Classic = 0
|
||||
Bubble = 1
|
||||
ClassicAndBubble = 2
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from enum import Enum
|
||||
|
||||
class GiftcardType(Enum):
|
||||
Outrageous_BuildersClub = 0
|
||||
Turbo_BuildersClub = 1
|
||||
RobuxCurrency = 2
|
||||
TixCurrency = 3
|
||||
Item = 4
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
from enum import Enum
|
||||
|
||||
class LimitedItemTransferMethod( Enum ):
|
||||
Purchase = 0
|
||||
Trade = 1
|
||||
WonByLottery = 2
|
||||
ForcedTransfer = 3
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
from enum import Enum
|
||||
|
||||
class MembershipType( Enum ):
|
||||
NonBuildersClub = 0
|
||||
BuildersClub = 1
|
||||
TurboBuildersClub = 2
|
||||
OutrageousBuildersClub = 3
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
from enum import Enum
|
||||
|
||||
class PlaceRigChoice( Enum ):
|
||||
UserChoice = 0
|
||||
ForceR6 = 1
|
||||
ForceR15 = 2
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from enum import Enum
|
||||
|
||||
class TradeStatus( Enum ):
|
||||
Pending = 0
|
||||
Accepted = 1
|
||||
Declined = 2
|
||||
Expired = 3
|
||||
Cancelled = 4
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
from enum import Enum
|
||||
|
||||
class TransactionType( Enum ):
|
||||
Purchase = 0
|
||||
Sale = 1
|
||||
GroupPayout = 2
|
||||
BuildersClubStipend = 3
|
||||
Commisions = 4
|
||||
Trade = 5
|
||||
|
|
@ -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"<t:{int(AssetObj.offsale_at.timestamp())}:R>",
|
||||
"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")
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
@ -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('<?xml version="1.0"?><entry xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:yt="http://gdata.youtube.com/schemas/2007"><media:group><media:title type="plain"><![CDATA[ROBLOX Place]]></media:title><media:description type="plain"><![CDATA[ For more games visit http://www.syntax.eco]]></media:description><media:category scheme="http://gdata.youtube.com/schemas/2007/categories.cat">Games</media:category><media:keywords>ROBLOX, video, free game, online virtual world</media:keywords></media:group></entry>') end)
|
||||
end)()
|
||||
|
|
@ -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)()
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
|
|
@ -0,0 +1,415 @@
|
|||
<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
|
||||
<External>null</External>
|
||||
<External>nil</External>
|
||||
<Item class="Workspace" referent="RBXC7F18B7C66F748608ECF68C721F975E7">
|
||||
<Properties>
|
||||
<bool name="AllowThirdPartySales">false</bool>
|
||||
<Ref name="CurrentCamera">RBX1512699E848543EC96B3B41C9AAF63F2</Ref>
|
||||
<double name="DistributedGameTime">0</double>
|
||||
<bool name="ExpSolverEnabled_Replicate">false</bool>
|
||||
<float name="FallenPartsDestroyHeight">-500</float>
|
||||
<bool name="FilteringEnabled">true</bool>
|
||||
<CoordinateFrame name="ModelInPrimary">
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
<Z>0</Z>
|
||||
<R00>1</R00>
|
||||
<R01>0</R01>
|
||||
<R02>0</R02>
|
||||
<R10>0</R10>
|
||||
<R11>1</R11>
|
||||
<R12>0</R12>
|
||||
<R20>0</R20>
|
||||
<R21>0</R21>
|
||||
<R22>1</R22>
|
||||
</CoordinateFrame>
|
||||
<string name="Name">Workspace</string>
|
||||
<bool name="PGSPhysicsSolverEnabled">false</bool>
|
||||
<token name="PhysicalPropertiesMode">1</token>
|
||||
<Ref name="PrimaryPart">null</Ref>
|
||||
<bool name="StreamingEnabled">false</bool>
|
||||
</Properties>
|
||||
<Item class="Camera" referent="RBX1512699E848543EC96B3B41C9AAF63F2">
|
||||
<Properties>
|
||||
<CoordinateFrame name="CFrame">
|
||||
<X>0</X>
|
||||
<Y>18.7408524</Y>
|
||||
<Z>23.4260654</Z>
|
||||
<R00>1</R00>
|
||||
<R01>0</R01>
|
||||
<R02>-0</R02>
|
||||
<R10>-0</R10>
|
||||
<R11>0.707106829</R11>
|
||||
<R12>0.707106829</R12>
|
||||
<R20>0</R20>
|
||||
<R21>-0.707106829</R21>
|
||||
<R22>0.707106829</R22>
|
||||
</CoordinateFrame>
|
||||
<Ref name="CameraSubject">null</Ref>
|
||||
<token name="CameraType">0</token>
|
||||
<float name="FieldOfView">70</float>
|
||||
<CoordinateFrame name="Focus">
|
||||
<X>0</X>
|
||||
<Y>17.3266392</Y>
|
||||
<Z>22.0118523</Z>
|
||||
<R00>1</R00>
|
||||
<R01>0</R01>
|
||||
<R02>0</R02>
|
||||
<R10>0</R10>
|
||||
<R11>1</R11>
|
||||
<R12>0</R12>
|
||||
<R20>0</R20>
|
||||
<R21>0</R21>
|
||||
<R22>1</R22>
|
||||
</CoordinateFrame>
|
||||
<bool name="HeadLocked">true</bool>
|
||||
<string name="Name">Camera</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="Terrain" referent="RBX962DC37307CB49EE949C2B4641DC8869">
|
||||
<Properties>
|
||||
<bool name="Anchored">true</bool>
|
||||
<float name="BackParamA">-0.5</float>
|
||||
<float name="BackParamB">0.5</float>
|
||||
<token name="BackSurface">0</token>
|
||||
<token name="BackSurfaceInput">0</token>
|
||||
<float name="BottomParamA">-0.5</float>
|
||||
<float name="BottomParamB">0.5</float>
|
||||
<token name="BottomSurface">4</token>
|
||||
<token name="BottomSurfaceInput">0</token>
|
||||
<int name="BrickColor">194</int>
|
||||
<CoordinateFrame name="CFrame">
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
<Z>0</Z>
|
||||
<R00>1</R00>
|
||||
<R01>0</R01>
|
||||
<R02>0</R02>
|
||||
<R10>0</R10>
|
||||
<R11>1</R11>
|
||||
<R12>0</R12>
|
||||
<R20>0</R20>
|
||||
<R21>0</R21>
|
||||
<R22>1</R22>
|
||||
</CoordinateFrame>
|
||||
<bool name="CanCollide">true</bool>
|
||||
<BinaryString name="ClusterGridV3"></BinaryString>
|
||||
<Color3 name="Color">4288914085</Color3>
|
||||
<PhysicalProperties name="CustomPhysicalProperties">
|
||||
<CustomPhysics>false</CustomPhysics>
|
||||
</PhysicalProperties>
|
||||
<float name="Elasticity">0.300000012</float>
|
||||
<float name="Friction">0.5</float>
|
||||
<float name="FrontParamA">-0.5</float>
|
||||
<float name="FrontParamB">0.5</float>
|
||||
<token name="FrontSurface">0</token>
|
||||
<token name="FrontSurfaceInput">0</token>
|
||||
<float name="LeftParamA">-0.5</float>
|
||||
<float name="LeftParamB">0.5</float>
|
||||
<token name="LeftSurface">0</token>
|
||||
<token name="LeftSurfaceInput">0</token>
|
||||
<bool name="Locked">true</bool>
|
||||
<token name="Material">256</token>
|
||||
<string name="Name">Terrain</string>
|
||||
<float name="Reflectance">0</float>
|
||||
<float name="RightParamA">-0.5</float>
|
||||
<float name="RightParamB">0.5</float>
|
||||
<token name="RightSurface">0</token>
|
||||
<token name="RightSurfaceInput">0</token>
|
||||
<Vector3 name="RotVelocity">
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
<Z>0</Z>
|
||||
</Vector3>
|
||||
<BinaryString name="SmoothGrid">AQU=</BinaryString>
|
||||
<float name="TopParamA">-0.5</float>
|
||||
<float name="TopParamB">0.5</float>
|
||||
<token name="TopSurface">3</token>
|
||||
<token name="TopSurfaceInput">0</token>
|
||||
<float name="Transparency">0</float>
|
||||
<Vector3 name="Velocity">
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
<Z>0</Z>
|
||||
</Vector3>
|
||||
<Color3 name="WaterColor">4278998108</Color3>
|
||||
<float name="WaterTransparency">0.300000012</float>
|
||||
<float name="WaterWaveSize">0.150000006</float>
|
||||
<float name="WaterWaveSpeed">10</float>
|
||||
<Vector3 name="size">
|
||||
<X>2044</X>
|
||||
<Y>252</Y>
|
||||
<Z>2044</Z>
|
||||
</Vector3>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="Part" referent="RBXD9661C66155D4480B1C60243AD55DEA5">
|
||||
<Properties>
|
||||
<bool name="Anchored">true</bool>
|
||||
<float name="BackParamA">-0.5</float>
|
||||
<float name="BackParamB">0.5</float>
|
||||
<token name="BackSurface">0</token>
|
||||
<token name="BackSurfaceInput">0</token>
|
||||
<float name="BottomParamA">-0.5</float>
|
||||
<float name="BottomParamB">0.5</float>
|
||||
<token name="BottomSurface">4</token>
|
||||
<token name="BottomSurfaceInput">0</token>
|
||||
<int name="BrickColor">199</int>
|
||||
<CoordinateFrame name="CFrame">
|
||||
<X>0</X>
|
||||
<Y>-10</Y>
|
||||
<Z>0</Z>
|
||||
<R00>1</R00>
|
||||
<R01>0</R01>
|
||||
<R02>0</R02>
|
||||
<R10>0</R10>
|
||||
<R11>1</R11>
|
||||
<R12>0</R12>
|
||||
<R20>0</R20>
|
||||
<R21>0</R21>
|
||||
<R22>1</R22>
|
||||
</CoordinateFrame>
|
||||
<bool name="CanCollide">true</bool>
|
||||
<Color3 name="Color">4284702562</Color3>
|
||||
<PhysicalProperties name="CustomPhysicalProperties">
|
||||
<CustomPhysics>false</CustomPhysics>
|
||||
</PhysicalProperties>
|
||||
<float name="Elasticity">0.5</float>
|
||||
<float name="Friction">0.300000012</float>
|
||||
<float name="FrontParamA">-0.5</float>
|
||||
<float name="FrontParamB">0.5</float>
|
||||
<token name="FrontSurface">0</token>
|
||||
<token name="FrontSurfaceInput">0</token>
|
||||
<float name="LeftParamA">-0.5</float>
|
||||
<float name="LeftParamB">0.5</float>
|
||||
<token name="LeftSurface">0</token>
|
||||
<token name="LeftSurfaceInput">0</token>
|
||||
<bool name="Locked">true</bool>
|
||||
<token name="Material">256</token>
|
||||
<string name="Name">BasePlate</string>
|
||||
<float name="Reflectance">0</float>
|
||||
<float name="RightParamA">-0.5</float>
|
||||
<float name="RightParamB">0.5</float>
|
||||
<token name="RightSurface">0</token>
|
||||
<token name="RightSurfaceInput">0</token>
|
||||
<Vector3 name="RotVelocity">
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
<Z>0</Z>
|
||||
</Vector3>
|
||||
<float name="TopParamA">-0.5</float>
|
||||
<float name="TopParamB">0.5</float>
|
||||
<token name="TopSurface">3</token>
|
||||
<token name="TopSurfaceInput">0</token>
|
||||
<float name="Transparency">0</float>
|
||||
<Vector3 name="Velocity">
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
<Z>0</Z>
|
||||
</Vector3>
|
||||
<token name="formFactorRaw">0</token>
|
||||
<token name="shape">1</token>
|
||||
<Vector3 name="size">
|
||||
<X>512</X>
|
||||
<Y>20</Y>
|
||||
<Z>512</Z>
|
||||
</Vector3>
|
||||
</Properties>
|
||||
</Item>
|
||||
</Item>
|
||||
<Item class="NonReplicatedCSGDictionaryService" referent="RBX067CE0D023DD497B8B9EE5961977CD31">
|
||||
<Properties>
|
||||
<string name="Name">NonReplicatedCSGDictionaryService</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="CSGDictionaryService" referent="RBXE0159F0B3BF24CBA8EA8E9A60AB80E0F">
|
||||
<Properties>
|
||||
<string name="Name">CSGDictionaryService</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="Players" referent="RBX8A99EC5033DB43D69BEA8B98350E671D">
|
||||
<Properties>
|
||||
<bool name="CharacterAutoLoads">true</bool>
|
||||
<int name="MaxPlayersInternal">12</int>
|
||||
<string name="Name">Players</string>
|
||||
<int name="PreferredPlayersInternal">7536745</int>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="ReplicatedFirst" referent="RBX343D15FAE3B14EC5B01DEA5B584C79F5">
|
||||
<Properties>
|
||||
<string name="Name">ReplicatedFirst</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="StarterPlayer" referent="RBXE9CF635E34094B729D64B946C7FB9F42">
|
||||
<Properties>
|
||||
<bool name="AutoJumpEnabled">true</bool>
|
||||
<float name="CameraMaxZoomDistance">400</float>
|
||||
<float name="CameraMinZoomDistance">0.5</float>
|
||||
<token name="CameraMode">0</token>
|
||||
<token name="DevCameraOcclusionMode">0</token>
|
||||
<token name="DevComputerCameraMovementMode">0</token>
|
||||
<token name="DevComputerMovementMode">0</token>
|
||||
<token name="DevTouchCameraMovementMode">0</token>
|
||||
<token name="DevTouchMovementMode">0</token>
|
||||
<bool name="EnableMouseLockOption">true</bool>
|
||||
<float name="HealthDisplayDistance">100</float>
|
||||
<bool name="LoadCharacterAppearance">true</bool>
|
||||
<string name="Name">StarterPlayer</string>
|
||||
<float name="NameDisplayDistance">100</float>
|
||||
</Properties>
|
||||
<Item class="StarterPlayerScripts" referent="RBX307AE5152BDC4BA58565D2ABCE5F7DB8">
|
||||
<Properties>
|
||||
<string name="Name">StarterPlayerScripts</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
</Item>
|
||||
<Item class="StarterPack" referent="RBX5B2912C78BAE4E7CBD812F2528B41C4A">
|
||||
<Properties>
|
||||
<string name="Name">StarterPack</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="StarterGui" referent="RBXCCEE4F7509994477820DAE2433E0D50C">
|
||||
<Properties>
|
||||
<string name="Name">StarterGui</string>
|
||||
<bool name="ResetPlayerGuiOnSpawn">true</bool>
|
||||
<bool name="ShowDevelopmentGui">true</bool>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="TeleportService" referent="RBX1C0D1B1D684943C9BD32C32F03B22DBC">
|
||||
<Properties>
|
||||
<string name="Name">Teleport Service</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="SoundService" referent="RBX05CFF11CF7804B63A7B9A87AFF717C90">
|
||||
<Properties>
|
||||
<token name="AmbientReverb">0</token>
|
||||
<float name="DistanceFactor">10</float>
|
||||
<float name="DopplerScale">1</float>
|
||||
<string name="Name">SoundService</string>
|
||||
<float name="RolloffScale">1</float>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="CollectionService" referent="RBX8D87D906F59B4804A229412EE38676EE">
|
||||
<Properties>
|
||||
<string name="Name">CollectionService</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="PhysicsService" referent="RBX68BBBC05359049F3BD5E6B5EE18A79D1">
|
||||
<Properties>
|
||||
<string name="Name">PhysicsService</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="Geometry" referent="RBX5D2ED4CB99F4425E857A8C8D60906137">
|
||||
<Properties>
|
||||
<string name="Name">Geometry</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="RenderHooksService" referent="RBXAE16C6653AF3438D9CF3019235FDC39A">
|
||||
<Properties>
|
||||
<string name="Name">RenderHooksService</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="InsertService" referent="RBXBAA293F59345438BA906BF0A46E84109">
|
||||
<Properties>
|
||||
<bool name="AllowInsertFreeModels">false</bool>
|
||||
<string name="Name">InsertService</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="SocialService" referent="RBX4A68FBCE25944173ADE26B5628ADD22E">
|
||||
<Properties>
|
||||
<string name="Name">SocialService</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="GamePassService" referent="RBX903E91343393412285AC46B20AEACE07">
|
||||
<Properties>
|
||||
<string name="Name">GamePassService</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="Debris" referent="RBX5566F5623A1C4C68AF0450FD10E2B277">
|
||||
<Properties>
|
||||
<int name="MaxItems">1000</int>
|
||||
<string name="Name">Debris</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="TimerService" referent="RBXD39BE6C8787645A592810B59B37F29DF">
|
||||
<Properties>
|
||||
<string name="Name">Instance</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="ScriptInformationProvider" referent="RBX795281AD9D034336A9A3AC60F0960A51">
|
||||
<Properties>
|
||||
<string name="Name">Instance</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="CookiesService" referent="RBXAE4ABDBBEAAE47138714681AB603AA8E">
|
||||
<Properties>
|
||||
<string name="Name">CookiesService</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="ContextActionService" referent="RBX0CD406A832244F80AF59D08650F23871">
|
||||
<Properties>
|
||||
<string name="Name">ContextActionService</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="ScriptService" referent="RBXBF257B38E3524ABD89D43BF8B308C06C">
|
||||
<Properties>
|
||||
<string name="Name">Instance</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="AssetService" referent="RBXC8F2AF8A58604E7CA2F15D67B0FA04E9">
|
||||
<Properties>
|
||||
<string name="Name">AssetService</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="Selection" referent="RBX88557E83E45A42D49F2D6960F4BA2079">
|
||||
<Properties>
|
||||
<string name="Name">Selection</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="ServerScriptService" referent="RBX8646DBC0047A4B418CFDC7A2F5194188">
|
||||
<Properties>
|
||||
<bool name="LoadStringEnabled">false</bool>
|
||||
<string name="Name">ServerScriptService</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="ServerStorage" referent="RBXF71D33A93A534CFB8E2C02B5044482C0">
|
||||
<Properties>
|
||||
<string name="Name">ServerStorage</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="ReplicatedStorage" referent="RBX9333FC8788C3495694590852FF7AB205">
|
||||
<Properties>
|
||||
<string name="Name">ReplicatedStorage</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="LuaWebService" referent="RBX4B2D942315DF48CA878ADA0362641048">
|
||||
<Properties>
|
||||
<string name="Name">Instance</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="Lighting" referent="RBX169D2E7A960249C7A46E5717B943F5B8">
|
||||
<Properties>
|
||||
<Color3 name="Ambient">4278190080</Color3>
|
||||
<float name="Brightness">1</float>
|
||||
<float name="ClockTime">14</float>
|
||||
<Color3 name="ColorShift_Bottom">4278190080</Color3>
|
||||
<Color3 name="ColorShift_Top">4278190080</Color3>
|
||||
<Color3 name="FogColor">4290822336</Color3>
|
||||
<float name="FogEnd">100000</float>
|
||||
<float name="FogStart">0</float>
|
||||
<float name="GeographicLatitude">41.7332993</float>
|
||||
<bool name="GlobalShadows">true</bool>
|
||||
<string name="Name">Lighting</string>
|
||||
<Color3 name="OutdoorAmbient">4286611584</Color3>
|
||||
<bool name="Outlines">true</bool>
|
||||
<Color3 name="ShadowColor">4289967032</Color3>
|
||||
<string name="TimeOfDay">14:00:00</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="HttpService" referent="RBX8D90633C9AD74BC59FE18FA9636536A7">
|
||||
<Properties>
|
||||
<bool name="HttpEnabled">false</bool>
|
||||
<string name="Name">HttpService</string>
|
||||
</Properties>
|
||||
</Item>
|
||||
</roblox>
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -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)
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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)
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -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()
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
|
|
@ -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('<?xml version="1.0"?><entry xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:yt="http://gdata.youtube.com/schemas/2007"><media:group><media:title type="plain"><![CDATA[ROBLOX Place]]></media:title><media:description type="plain"><![CDATA[ For more games visit http://www.syntax.eco]]></media:description><media:category scheme="http://gdata.youtube.com/schemas/2007/categories.cat">Games</media:category><media:keywords>ROBLOX, video, free game, online virtual world</media:keywords></media:group></entry>') end)
|
||||
-- use single quotes here because the video info string may have unescaped double quotes
|
||||
--[[
|
||||
analytics("Join Finished")
|
||||
|
||||
ifSeleniumThenSetCookie("SeleniumTest4", "Finished join")
|
||||
]]
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
|
||||
<External>null</External>
|
||||
<External>nil</External>
|
||||
<Item class="Pants" referent="RBX0">
|
||||
<Properties>
|
||||
<Content name="PantsTemplate">
|
||||
<url>http://www.syntax.eco/asset/?id={PantsImageId}</url>
|
||||
</Content>
|
||||
<string name="Name">Pants</string>
|
||||
<bool name="archivable">true</bool>
|
||||
</Properties>
|
||||
</Item>
|
||||
</roblox>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
|
||||
<External>null</External>
|
||||
<External>nil</External>
|
||||
<Item class="Shirt" referent="RBX0">
|
||||
<Properties>
|
||||
<Content name="ShirtTemplate">
|
||||
<url>http://www.syntax.eco/asset/?id={ShirtImageId}</url>
|
||||
</Content>
|
||||
<string name="Name">Shirt</string>
|
||||
<bool name="archivable">true</bool>
|
||||
</Properties>
|
||||
</Item>
|
||||
</roblox>
|
||||
|
|
@ -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")
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
|
||||
<External>null</External>
|
||||
<External>nil</External>
|
||||
<Item class="ShirtGraphic" referent="RBX0">
|
||||
<Properties>
|
||||
<Content name="Graphic">
|
||||
<url>http://www.syntax.eco/asset/?id={TShirtImageId}</url>
|
||||
</Content>
|
||||
<string name="Name">Shirt Graphic</string>
|
||||
<bool name="archivable">true</bool>
|
||||
</Properties>
|
||||
</Item>
|
||||
</roblox>
|
||||
|
|
@ -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
|
||||
|
|
@ -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 "<AdminPermissions userid={userid}, permission={permission}>".format(
|
||||
userid=self.userid,
|
||||
permission=self.permission
|
||||
)
|
||||
|
|
@ -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"<Asset {self.id}>"
|
||||
|
|
@ -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"<AssetFavorite {self.id} {self.assetid} {self.userid}>"
|
||||
|
|
@ -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 "<AssetModerationLink id={id} ParentAssetId={ParentAssetId} ChildAssetId={ChildAssetId}>".format(
|
||||
id=self.id,
|
||||
ParentAssetId=self.ParentAssetId,
|
||||
ChildAssetId=self.ChildAssetId
|
||||
)
|
||||
|
|
@ -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 "<AssetRap assetid={assetid}, rap={rap}, updated={updated}>".format(
|
||||
assetid=self.assetid,
|
||||
rap=self.rap,
|
||||
updated=self.updated
|
||||
)
|
||||
|
|
@ -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 "<AssetThumbnail asset_id={asset_id} asset_version_id={asset_version_id} content_hash={content_hash} created_at={created_at} updated_at={updated_at} moderation_status={moderation_status}>".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
|
||||
)
|
||||
|
|
@ -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 "<AssetVersion id={id} asset_id={asset_id} version={version} content_hash={content_hash} created_at={created_at} updated_at={updated_at}>".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
|
||||
)
|
||||
|
|
@ -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"<AssetVote {self.id} {self.assetid} {self.userid} {self.vote}>"
|
||||
|
|
@ -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
|
||||
|
|
@ -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 '<ExchangeOffer %r>' % self.id
|
||||
|
|
@ -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 "<FflagGroup group_id={group_id}, name={name}, enabled={enabled}>".format(
|
||||
group_id=self.group_id,
|
||||
name=self.name,
|
||||
enabled=self.enabled
|
||||
)
|
||||
|
|
@ -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 "<FflagValue flag_id={flag_id}, group_id={group_id}, name={name}, flag_type={flag_type}>".format(
|
||||
flag_id=self.flag_id,
|
||||
group_id=self.group_id,
|
||||
name=self.name,
|
||||
flag_type=self.flag_type
|
||||
)
|
||||
|
|
@ -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 "<FollowRelationship followerUserId={followerUserId}, followeeUserId={followeeUserId}, created={created}>".format(
|
||||
followerUserId=self.followerUserId,
|
||||
followeeUserId=self.followeeUserId,
|
||||
created=self.created
|
||||
)
|
||||
|
|
@ -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 '<FriendRelationship %r>' % self.id
|
||||
|
|
@ -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 '<FriendRequest %r>' % self.id
|
||||
|
|
@ -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 "<GameSessionLog user_id={user_id}, serveruuid={serveruuid}, place_id={place_id}, joined_at={joined_at}, left_at={left_at}>".format(
|
||||
user_id=self.user_id,
|
||||
serveruuid=self.serveruuid,
|
||||
place_id=self.place_id,
|
||||
joined_at=self.joined_at,
|
||||
left_at=self.left_at
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -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 '<GamepassLink %r>' % self.id
|
||||
|
|
@ -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 "<GameServers serverId={serverId}, serverName={serverName}, serverIP={serverIP}, serverPort={serverPort}>".format(
|
||||
serverId=self.serverId,
|
||||
serverName=self.serverName,
|
||||
serverIP=self.serverIP,
|
||||
serverPort=self.serverPort
|
||||
)
|
||||
|
|
@ -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'<GiftcardKey {self.id}>'
|
||||
|
|
@ -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 '<Group %r>' % 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 '<GroupIcon %r>' % 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 '<GroupSettings %r>' % 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 '<GroupRole %r>' % 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 '<GroupRolePermission %r>' % 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 '<GroupMember %r>' % 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 '<GroupEconomy %r>' % 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 '<GroupJoinRequest %r>' % 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 '<GroupStatus %r>' % 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 '<GroupWallPost %r>' % self.id
|
||||
|
|
@ -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 '<InviteKey %r>' % self.id
|
||||
|
|
@ -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"<KofiTransaction {self.kofi_transaction_id}>"
|
||||
|
|
@ -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 "<LegacyDataPersistence id={id}, userid={userid}, placeid={placeid}>".format(
|
||||
id=self.id,
|
||||
userid=self.userid,
|
||||
placeid=self.placeid
|
||||
)
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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 '<LinkedDiscord %r>' % self.user_id
|
||||
|
|
@ -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 '<LoginRecord {} ({} - {})>'.format(self.id, self.userid, self.ip)
|
||||
|
|
@ -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 '<Message %r>' % self.id
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue