#include "stdafx.h" #include "VMProtectSDK.h" #include "V8DataModel/GuiObject.h" #include "V8DataModel/GuiService.h" #include "V8DataModel/PlayerGui.h" #include "v8datamodel/ScreenGui.h" #include "V8DataModel/JointsService.h" #include "v8datamodel/Workspace.h" #include "v8datamodel/MegaCluster.h" #include "v8datamodel/ContextActionService.h" #include "v8datamodel/GamepadService.h" #include "Script/ScriptContext.h" #include "Network/Players.h" #include "V8DataModel/CollectionService.h" #include "V8DataModel/DialogRoot.h" #include "V8DataModel/PartInstance.h" #include "V8DataModel/Bindable.h" #include FASTFLAGVARIABLE(NewInGameDevConsole, false) FASTFLAGVARIABLE(UseNewSubdomainsInCoreScripts, false) DYNAMIC_FASTFLAG(EnableShowStatsLua) FASTFLAGVARIABLE(UseGameLoadedInLoadingScript, true) FASTFLAGVARIABLE(UseUserListMenu, false) FASTFLAGVARIABLE(EnableSetCoreTopbarEnabled, false) FASTFLAGVARIABLE(Durango3DBackground, true) // this is Xbox flag. Defined here so it is accessible in studio and xbox client namespace RBX { const char* const sGuiService = "GuiService"; REFLECTION_BEGIN(); static const Reflection::PropDescriptor prop_IsModalDialog("IsModalDialog", category_Data, &GuiService::getModalDialogStatus, NULL, Reflection::PropertyDescriptor::Attributes::deprecated()); static const Reflection::PropDescriptor prop_IsWindows("IsWindows", category_Data, &GuiService::getIsWindows, NULL, Reflection::PropertyDescriptor::Attributes::deprecated()); static Reflection::EventDesc event_keypressed(&GuiService::keyPressed, "KeyPressed", "key", "modifiers", Security::RobloxScript); static Reflection::EventDesc event_specialKeypressed(&GuiService::specialKeyPressed, "SpecialKeyPressed", "key", "modifiers", Security::RobloxScript); static Reflection::BoundFuncDesc func_addKey(&GuiService::addKey, "AddKey", "key", Security::RobloxScript); static Reflection::BoundFuncDesc func_removeKey(&GuiService::removeKey, "RemoveKey", "key", Security::RobloxScript); static Reflection::BoundFuncDesc func_addSpecialKey(&GuiService::addSpecialKey, "AddSpecialKey", "key", Security::RobloxScript); static Reflection::BoundFuncDesc func_removeSpecialKey(&GuiService::removeSpecialKey, "RemoveSpecialKey", "key", Security::RobloxScript); static Reflection::EventDesc event_escapeKeypressed(&GuiService::escapeKeyPressed, "EscapeKeyPressed", Security::RobloxScript); static Reflection::BoundFuncDesc, GuiService::CenterDialogType, Lua::WeakFunctionRef, Lua::WeakFunctionRef)> func_showCenterDialog(&GuiService::addCenterDialog, "AddCenterDialog", "dialog", "centerDialogType", "showFunction", "hideFunction", Security::RobloxScript); static Reflection::BoundFuncDesc)> func_hideCenterDialog(&GuiService::removeCenterDialog, "RemoveCenterDialog", "dialog", Security::RobloxScript); static Reflection::BoundFuncDesc func_globalGuiInset(&GuiService::setGlobalGuiInset, "SetGlobalGuiInset", "x1", "y1", "x2", "y2", Security::RobloxScript); static Reflection::BoundYieldFuncDesc func_getScreenResolution(&GuiService::getScreenResolutionLua, "GetScreenResolution", Security::RobloxScript); static Reflection::BoundFuncDesc func_openBrowserWindow(&GuiService::openBrowserWindow, "OpenBrowserWindow", "url", Security::RobloxScript); static Reflection::EventDesc event_urlWindowClosed(&GuiService::urlWindowClosed, "BrowserWindowClosed", Security::RobloxScript); static Reflection::BoundFuncDesc(Vector3)> func_GetClosestDialogToPosition(&GuiService::getClosestDialogToPosition, "GetClosestDialogToPosition", "position", Security::RobloxScript); static Reflection::BoundFuncDesc func_GetBrickCount(&GuiService::getBrickCount, "GetBrickCount", Security::RobloxScript); static Reflection::BoundFuncDesc func_setUiMessage(&GuiService::setUiMessage, "SetUiMessage", "msgType", "uiMessage", Security::LocalUser); static Reflection::BoundFuncDesc func_getUiMessage(&GuiService::getUiMessage, "GetUiMessage", Security::RobloxScript); static Reflection::EventDesc event_newUiMessage(&GuiService::newUiMessageSignal, "UiMessageChanged", "msgType", "newUiMessage", Security::RobloxScript); static Reflection::BoundFuncDesc func_setErrorMessage(&GuiService::setErrorMessage, "SetErrorMessage", "errorMessage", Security::LocalUser, Reflection::Descriptor::Attributes::deprecated(func_setUiMessage)); static Reflection::BoundFuncDesc func_getErrorMessage(&GuiService::getErrorMessage, "GetErrorMessage", Security::RobloxScript, Reflection::Descriptor::Attributes::deprecated(func_getUiMessage)); static Reflection::EventDesc event_newError(&GuiService::newErrorSignal, "ErrorMessageChanged", "newErrorMessage", Security::RobloxScript, Reflection::Descriptor::Attributes::deprecated(event_newUiMessage)); static Reflection::EventDesc event_showLeaveConfirmation(&GuiService::showLeaveConfirmationSignal, "ShowLeaveConfirmation", Security::RobloxScript); static Reflection::BoundFuncDesc func_toggleFullscreen(&GuiService::toggleFullscreen, "ToggleFullscreen", Security::RobloxScript); static Reflection::BoundFuncDesc func_isTenFootInterface(&GuiService::isTenFootInterface, "IsTenFootInterface", Security::RobloxScript); static Reflection::BoundFuncDesc)> func_addSelectionGroup(&GuiService::addSelectionGroup, "AddSelectionParent", "selectionName", "selectionParent", Security::None); static Reflection::BoundFuncDesc)> func_addSelectionGroupTuple(&GuiService::addSelectionGroup, "AddSelectionTuple", "selectionName", "selections", Security::None); static Reflection::BoundFuncDesc func_removeSelectionGroup(&GuiService::removeSelectionGroup, "RemoveSelectionGroup", "selectionName", Security::None); Reflection::RefPropDescriptor GuiService::prop_selectedGuiObject("SelectedObject", category_Data, &GuiService::getSelectedGuiObjectLua, &GuiService::setSelectedGuiObjectLua); Reflection::RefPropDescriptor GuiService::prop_selectedCoreGuiObject("SelectedCoreObject", category_Data, &GuiService::getSelectedCoreGuiObjectLua, &GuiService::setSelectedCoreGuiObjectLua, Reflection::PropertyDescriptor::UI, Security::RobloxScript); static const Reflection::PropDescriptor prop_autoGuiSelectionAllowed("AutoSelectGuiEnabled", category_Data, &GuiService::getAutoGuiSelectionAllowed, &GuiService::setAutoGuiSelectionAllowed); static const Reflection::PropDescriptor prop_gamepadNavigationEnabled("GuiNavigationEnabled", category_Data, &GuiService::getGamepadNavEnabled, &GuiService::setGamepadNavEnabled); static const Reflection::PropDescriptor prop_coreGamepadNavigationEnabled("CoreGuiNavigationEnabled", category_Data, &GuiService::getCoreGamepadNavEnabled, &GuiService::setCoreGamepadNavEnabled); static Reflection::BoundAsyncCallbackDesc callback_notificationCallback("SendCoreUiNotification", &GuiService::notificationCallback, "title", "text", Security::RobloxScript); static const Reflection::PropDescriptor prop_menuOpen("MenuIsOpen", category_Data, &GuiService::getMenuOpen, NULL); static const Reflection::BoundFuncDesc func_setMenuOpen(&GuiService::setMenuOpen, "SetMenuIsOpen", "open", Security::RobloxScript); static Reflection::EventDesc event_menuOpened(&GuiService::menuOpenedSignal, "MenuOpened", Security::None); static Reflection::EventDesc event_menuClosed(&GuiService::menuClosedSignal, "MenuClosed", Security::None); static Reflection::BoundFuncDesc func_ShowStatsBasedOnInputString(&GuiService::showStatsBasedOnInputString, "ShowStatsBasedOnInputString","input", Security::RobloxScript); REFLECTION_END(); namespace Reflection { template<> EnumDesc::EnumDesc() :EnumDescriptor("SpecialKey") { addPair(GuiService::KEY_INSERT, "Insert"); addPair(GuiService::KEY_HOME, "Home"); addPair(GuiService::KEY_END, "End"); addPair(GuiService::KEY_PAGEUP, "PageUp"); addPair(GuiService::KEY_PAGEDOWN, "PageDown"); addPair(GuiService::KEY_CHATHOTKEY, "ChatHotkey"); } template<> GuiService::SpecialKey& Variant::convert(void) { return genericConvert(); } template<> EnumDesc::EnumDesc() :EnumDescriptor("CenterDialogType") { addPair(GuiService::CENTER_DIALOG_UNSOLICITED_DIALOG , "UnsolicitedDialog"); addPair(GuiService::CENTER_DIALOG_PLAYER_INITIATED_DIALOG , "PlayerInitiatedDialog"); addPair(GuiService::CENTER_DIALOG_MODAL_DIALOG , "ModalDialog"); addPair(GuiService::CENTER_DIALOG_QUIT_DIALOG , "QuitDialog"); } template<> GuiService::CenterDialogType& Variant::convert(void) { return genericConvert(); } template<> EnumDesc::EnumDesc() :EnumDescriptor("UiMessageType") { addPair(GuiService::UIMESSAGE_ERROR , "UiMessageError"); addPair(GuiService::UIMESSAGE_INFO , "UiMessageInfo"); } template<> GuiService::UiMessageType& Variant::convert(void) { return genericConvert(); } }//namespace Reflection template<> bool StringConverter::convertToValue(const std::string& text, GuiService::SpecialKey& value) { return Reflection::EnumDesc::singleton().convertToValue(text.c_str(),value); } template<> bool StringConverter::convertToValue(const std::string& text, GuiService::CenterDialogType& value) { return Reflection::EnumDesc::singleton().convertToValue(text.c_str(),value); } template<> bool StringConverter::convertToValue(const std::string& text, GuiService::UiMessageType& value) { return Reflection::EnumDesc::singleton().convertToValue(text.c_str(),value); } GuiService::GuiService() :Super("GuiService") ,currentDialog(NULL) ,subscribedChars() ,subscribedSpecials() ,errorMessage("") ,gamepadNavEnabled(true) ,coreGamepadNavEnabled(true) ,menuOpen(false) { } void GuiService::setMenuOpen(bool value) { if (value != menuOpen) { menuOpen = value; menuOpen ? fireMenuOpenedSignal() : fireMenuClosedSignal(); raisePropertyChanged(prop_menuOpen); } } int GuiService::getBrickCount() { return DataModel::get(this)->getNumPartInstances(); } bool GuiService::shouldPreemptCurrentDialog(DialogWrapper* newDialog) const { if(currentDialog == NULL) return true; if(newDialog->dialogType > currentDialog->dialogType) return true; if(newDialog->dialogType < currentDialog->dialogType) return false; RBXASSERT(newDialog->dialogType == currentDialog->dialogType); switch(newDialog->dialogType){ case CENTER_DIALOG_UNSOLICITED_DIALOG: return false; case CENTER_DIALOG_PLAYER_INITIATED_DIALOG: case CENTER_DIALOG_MODAL_DIALOG: case CENTER_DIALOG_QUIT_DIALOG: return true; default: RBXASSERT(0); return false; } } bool GuiService::getModalDialogStatus() const { if(currentDialog == NULL) return false; switch(currentDialog->dialogType) { case CENTER_DIALOG_UNSOLICITED_DIALOG: case CENTER_DIALOG_PLAYER_INITIATED_DIALOG: return false; case CENTER_DIALOG_MODAL_DIALOG: case CENTER_DIALOG_QUIT_DIALOG: return true; default: RBXASSERT(0); return false; } } Vector2 GuiService::getScreenResolution() { if (CoreGuiService* coreGui = ServiceProvider::create(this)) { if (ScreenGui* screenGui = coreGui->findFirstChildOfType()) { return screenGui->getAbsoluteSize(); } } return Vector2::zero(); } void GuiService::getScreenResolutionNoRetry(boost::function resumeFunction, boost::function errorFunction) { screenGuiConnection.disconnect(); resumeFunction(getScreenResolution()); } void GuiService::getScreenResolutionLua(boost::function resumeFunction, boost::function errorFunction) { // if screen res is zero then we haven't done a heartbeat yet, try one more time after heartbeat if (getScreenResolution() == Vector2::zero()) { if (RunService* runService = ServiceProvider::create(this)) screenGuiConnection = runService->heartbeatSignal.connect(boost::bind(&GuiService::getScreenResolutionNoRetry,this,resumeFunction,errorFunction)); } else { getScreenResolutionNoRetry(resumeFunction,errorFunction); } } void GuiService::queueDialogWrapper(DialogWrapper* newDialog, bool preempted) { switch(newDialog->dialogType){ case CENTER_DIALOG_UNSOLICITED_DIALOG: if(preempted) dialogQueue[newDialog->dialogType].push_front(newDialog); else dialogQueue[newDialog->dialogType].push_back(newDialog); break; case CENTER_DIALOG_PLAYER_INITIATED_DIALOG: case CENTER_DIALOG_MODAL_DIALOG: case CENTER_DIALOG_QUIT_DIALOG: dialogQueue[newDialog->dialogType].push_front(newDialog); break; } } static void InvokeCallback(boost::weak_ptr weakThis, Lua::WeakFunctionRef callback, bool visible) { if (shared_ptr strongThis = weakThis.lock()) { if (Lua::ThreadRef threadRef = callback.lock()) { if (ScriptContext* sc = ServiceProvider::create(strongThis.get())) { Reflection::Tuple args; try { sc->callInNewThread(callback, args); return; } catch (RBX::base_exception& e) { StandardOut::singleton()->printf(MESSAGE_ERROR, "Unexpected error while invoking callback: %s", e.what()); } } } strongThis->setVisible(visible); } } void GuiService::addCenterDialog(shared_ptr dialogInstance, GuiService::CenterDialogType type, Lua::WeakFunctionRef show, Lua::WeakFunctionRef hide) { shared_ptr dialog = Instance::fastSharedDynamicCast(dialogInstance); if(!dialog) throw RBX::runtime_error("dialog must be a GuiObject instance"); weak_ptr weakDialog = weak_from(dialog.get()); std::map, DialogWrapper*>::iterator iter = dialogWrapperMap.find(weakDialog); if(iter != dialogWrapperMap.end()){ if( DialogWrapper* wrapper = iter->second){ if(wrapper->dialogType != type){ throw RBX::runtime_error("A dialogInstance should not change CenterDialogTypes"); } if(wrapper == currentDialog){ //Already on top -- ignore the request return; } else{ //Not on top -- remove it, so we can readd it removeCenterDialog(dialogInstance); } } else{ dialogWrapperMap.erase(iter); } } RBXASSERT(dialogWrapperMap.find(weakDialog) == dialogWrapperMap.end()); std::list > callbacks; DialogWrapper* wrapper = new DialogWrapper(); wrapper->dialog = weakDialog; wrapper->dialogType = type; wrapper->showFunction = boost::bind(&InvokeCallback, weakDialog, show, true); wrapper->hideFunction = boost::bind(&InvokeCallback, weakDialog, hide, false); dialogWrapperMap[weakDialog] = wrapper; if(shouldPreemptCurrentDialog(wrapper)){ if(currentDialog){ //Hide/Cleanup old dialog DataModel::get(this)->submitTask(boost::bind(currentDialog->hideFunction), DataModelJob::Write); queueDialogWrapper(currentDialog, true); currentDialog = NULL; } currentDialog = wrapper; //Show new Dialog DataModel::get(this)->submitTask(boost::bind(currentDialog->showFunction), DataModelJob::Write); } else { queueDialogWrapper(wrapper, false); } } bool GuiService::showWaitingDialog(CenterDialogType type) { RBXASSERT(currentDialog == NULL); if(!dialogQueue[type].empty()){ DialogWrapper* result = dialogQueue[type].front(); dialogQueue[type].pop_front(); currentDialog = result; return true; } return false; } void GuiService::removeCenterDialog(shared_ptr dialogInstance) { shared_ptr dialog = Instance::fastSharedDynamicCast(dialogInstance); if(!dialog) throw RBX::runtime_error("dialog must be a GuiObject instance"); weak_ptr weakDialog = weak_from(dialog.get()); if(DialogWrapper* wrapper = dialogWrapperMap[weakDialog]){ dialogWrapperMap.erase(weakDialog); dialogQueue[wrapper->dialogType].remove(wrapper); if(wrapper == currentDialog){ currentDialog = NULL; if(showWaitingDialog(CENTER_DIALOG_QUIT_DIALOG) || showWaitingDialog(CENTER_DIALOG_MODAL_DIALOG) || showWaitingDialog(CENTER_DIALOG_PLAYER_INITIATED_DIALOG) || showWaitingDialog(CENTER_DIALOG_UNSOLICITED_DIALOG)) { //Make it show DataModel::get(this)->submitTask(boost::bind(currentDialog->showFunction), DataModelJob::Write); } } delete wrapper; } } bool GuiService::dispatchKey(GuiService::SpecialKey special) { if(hasSpecial(special)){ specialKeyPressed(special, ""); return true; } return false; } bool GuiService::processKeyDown(const shared_ptr& event) { // ensure we are dealing with a key down RBXASSERT(event->isKeyDownEvent()); if(event->isTextCharacterKey() || (UserInputService::IsUsingNewKeyboardEvents() && event->getUserInputType() == RBX::InputObject::TYPE_KEYBOARD)) { if(!event->isAltEvent() && !event->isCtrlEvent()) { if(event->modifiedKey == '/' || (UserInputService::IsUsingNewKeyboardEvents() && event->getKeyCode() == SDLK_SLASH) ) { if(dispatchKey(KEY_CHATHOTKEY)) return true; } else { char character = tolower(event->modifiedKey); if(subscribedChars.find(character) != subscribedChars.end()) { keyPressed(std::string() + character,""); return true; } } } } else switch(event->getKeyCode()) { case SDLK_INSERT: if(dispatchKey(KEY_INSERT)) return true; break; case SDLK_HOME: if(dispatchKey(KEY_HOME)) return true; break; case SDLK_END: if(dispatchKey(KEY_END)) return true; break; case SDLK_PAGEUP: if(dispatchKey(KEY_PAGEUP)) return true; break; case SDLK_PAGEDOWN: if(dispatchKey(KEY_PAGEDOWN)) return true; break; default: break; } return false; } void GuiService::setGlobalGuiInset(int x1, int y1, int x2, int y2) { guiInset = Vector4(x1, y1, x2, y2); } void GuiService::addKey(std::string key) { if(key.size() != 1) throw RBX::runtime_error("GuiService:AddKey requires a string with a single character"); char character = tolower(key[0]); if(subscribedChars.find(character) == subscribedChars.end()){ subscribedChars.insert(character); } } void GuiService::removeKey(std::string key) { if(key.size() != 1) throw RBX::runtime_error("GuiService:RemoveKey requires a string with a single character"); char character = tolower(key[0]); std::set::iterator it = subscribedChars.find(character); if (it != subscribedChars.end()) subscribedChars.erase(it); } bool GuiService::hasSpecial(SpecialKey key) { return subscribedSpecials.find(key) != subscribedSpecials.end(); } void GuiService::addSpecialKey(SpecialKey key) { if(!hasSpecial(key)){ subscribedSpecials.insert(key); } } void GuiService::removeSpecialKey(SpecialKey key) { std::set::iterator iter = subscribedSpecials.find(key); if (iter != subscribedSpecials.end()) { subscribedSpecials.erase(iter); } } void GuiService::openBrowserWindow(std::string url) { if( !RBX::Http::isRobloxSite(url.c_str()) ) { StandardOut::singleton()->print(MESSAGE_WARNING, "GuiService::OpenBrowserWindow() was called on non-Roblox url."); return; } if(!RBX::Network::Players::frontendProcessing(this)) { StandardOut::singleton()->print(MESSAGE_WARNING, "GuiService::OpenBrowserWindow() was called on not a client (use local scripts on this call)."); return; } openUrlWindow(url); } void GuiService::setErrorMessage(std::string newErrorMessage) { if (newErrorMessage.compare(errorMessage) != 0) { errorMessage = newErrorMessage; newErrorSignal(errorMessage); } } std::string GuiService::getErrorMessage() { return errorMessage; } void GuiService::setUiMessage(GuiService::UiMessageType msgType, std::string newUiMessage) { if (newUiMessage.compare(uiMessage) != 0) { uiMessage = newUiMessage; newUiMessageSignal(msgType, uiMessage); } } std::string GuiService::getUiMessage() { return uiMessage; } void GuiService::toggleFullscreen() { // People were modifying verbString when it was loaded into a register by using CE. VMProtectBeginMutation(NULL); std::string verbString = "ToggleFullScreen"; Verb* verb = NULL; if(Workspace* workspace = ServiceProvider::find(this)) { #ifdef RBX_STUDIO_BUILD verb = workspace->getVerb(verbString); #else verb = workspace->getWhitelistVerb(verbString); if (verb && verb->getVerbSecurity()) { RBX::Tokens::simpleToken |= HATE_VERB_SNATCH; verb = NULL; } #endif } if (verb && verb->isEnabled()) { Verb::doItWithChecks(verb, RBX::DataModel::get(this)); } VMProtectEnd(); } bool GuiService::isTenFootInterface() { return UserInputService::isTenFootInterface(); } void GuiService::setSelectedGuiObjectLua(GuiObject* value) { if (RBX::Network::Players* players = ServiceProvider::create(this)) { if (RBX::Network::Player* player = players->getLocalPlayer()) { if (PlayerGui* playerGui = player->findFirstChildOfType()) { if (value != playerGui->getSelectedObject().get()) { playerGui->setSelectedObject(value); } } } } } void GuiService::setSelectedCoreGuiObjectLua(GuiObject* value) { if (CoreGuiService* coreGuiService = ServiceProvider::find(this)) { if (value != coreGuiService->getSelectedObject().get()) { coreGuiService->setSelectedObject(value); } } } GuiObject* GuiService::getSelectedGuiObjectLua() const { if (RBX::Network::Players* players = ServiceProvider::create(this)) { if (RBX::Network::Player* player = players->getLocalPlayer()) { if (PlayerGui* playerGui = player->findFirstChildOfType()) { if (shared_ptr selectedObject = playerGui->getSelectedObject()) { return selectedObject.get(); } } } } return NULL; } GuiObject* GuiService::getSelectedCoreGuiObjectLua() const { if (CoreGuiService* coreGuiService = ServiceProvider::find(this)) { if (shared_ptr selectedCoreObject = coreGuiService->getSelectedObject()) { return selectedCoreObject.get(); } } return NULL; } GuiObject* GuiService::getSelectedGuiObject() { if (GuiObject* selectedCoreObject = getSelectedCoreGuiObjectLua()) { return selectedCoreObject; } if (GuiObject* selectedObject = getSelectedGuiObjectLua()) { return selectedObject; } return NULL; } void GuiService::setCoreGamepadNavEnabled(bool value) { if (value != coreGamepadNavEnabled) { coreGamepadNavEnabled = value; raisePropertyChanged(prop_coreGamepadNavigationEnabled); } } void GuiService::setGamepadNavEnabled(bool value) { if (value != gamepadNavEnabled) { gamepadNavEnabled = value; raisePropertyChanged(prop_gamepadNavigationEnabled); } } bool GuiService::getAutoGuiSelectionAllowed() const { if (GamepadService* gamepadService = RBX::ServiceProvider::create(this)) { return gamepadService->getAutoGuiSelectionAllowed(); } return false; } void GuiService::setAutoGuiSelectionAllowed(bool value) { if (GamepadService* gamepadService = RBX::ServiceProvider::create(this)) { if (gamepadService->setAutoGuiSelectionAllowed(value)) { raisePropertyChanged(prop_autoGuiSelectionAllowed); } } } void GuiService::addSelectionGroup(std::string selectionGroupName, shared_ptr selectionParent) { shared_ptr guiObjectParent = shared_from( Instance::fastDynamicCast(selectionParent.get()) ); if (!guiObjectParent) { RBX::StandardOut::singleton()->printf(MESSAGE_ERROR, "GuiService:AddSelectionParent for group name %s: parent is not a GuiObject.", selectionGroupName.c_str()); return; } if (selectionMap.find(selectionGroupName) != selectionMap.end()) { RBX::StandardOut::singleton()->printf(MESSAGE_WARNING, "GuiService:AddSelectionParent already has selection group with name %s, overwriting selection group.", selectionGroupName.c_str()); removeSelectionGroup(selectionGroupName); } selectionMap[selectionGroupName] = SelectionGroupPair(guiObjectParent, shared_ptr()); } void GuiService::addSelectionGroup(std::string selectionGroupName, shared_ptr selectionTuple) { if (selectionMap.find(selectionGroupName) != selectionMap.end()) { RBX::StandardOut::singleton()->printf(MESSAGE_WARNING, "GuiService:AddSelectionTuple already has selection group with name %s, overwriting selection group.", selectionGroupName.c_str()); removeSelectionGroup(selectionGroupName); } selectionMap[selectionGroupName] = SelectionGroupPair(weak_ptr(), selectionTuple); } void GuiService::removeSelectionGroup(std::string selectionGroupName) { SelectionMap::iterator iter = selectionMap.find(selectionGroupName); if (iter != selectionMap.end()) { selectionMap.erase(iter); } } GuiService::SelectionGroupPair GuiService::getSelectedObjectGroup(shared_ptr object) { for (SelectionMap::iterator iter = selectionMap.begin(); iter != selectionMap.end(); ++iter) { if (shared_ptr parentSelection = (*iter).second.first.lock()) { if (object == parentSelection || object->isDescendantOf2(parentSelection)) { return (*iter).second; } } if (shared_ptr guiObjects = (*iter).second.second) { if (guiObjects->values.size() > 0 ) { Reflection::ValueArray guiObjectValues = guiObjects->values; for (Reflection::ValueArray::iterator tupleIter = guiObjectValues.begin(); tupleIter != guiObjectValues.end(); ++tupleIter) { if (tupleIter->isType >()) { shared_ptr selectableObject = tupleIter->cast >(); if (selectableObject == object) { return (*iter).second; } } } } } } return SelectionGroupPair(); } shared_ptr GuiService::getClosestDialogToPosition(Vector3 position) { float smallestDistance = Math::inf(); shared_ptr currentClosest = shared_ptr(); if (CollectionService* collectionService = ServiceProvider::create(this)) { if (shared_ptr collection = collectionService->getCollection("Dialog")) { for (Instances::const_iterator iter = collection->begin(); iter != collection->end(); ++iter) { if (shared_ptr dialog = Instance::fastSharedDynamicCast(*iter)) { if (shared_ptr dialogParent = Instance::fastSharedDynamicCast(shared_from(dialog->getParent()))) { float distanceFromPosition = (dialogParent->getCoordinateFrame().translation - position).magnitude(); if (distanceFromPosition < smallestDistance && distanceFromPosition < dialog->getConversationDistance()) { smallestDistance = distanceFromPosition; currentClosest = dialog; } } } } } } return currentClosest; } static void doNothing(Reflection::Variant var) {} bool GuiService::showStatsBasedOnInputString(std::string inputText) { if (!DFFlag::EnableShowStatsLua) return false; const char * text = inputText.c_str(); bool retVal = false; shared_ptr dataModel = shared_from(DataModel::get(this)); if (!dataModel) return retVal; if (strcmp(text, "Genstats") == 0) { dataModel->getGuiBuilder().toggleGeneralStats(); retVal = true; } else if (strcmp(text, "Renstats") == 0) { dataModel->getGuiBuilder().toggleRenderStats(); retVal = true; } else if (strcmp(text, "Netstats") == 0) { dataModel->getGuiBuilder().toggleNetworkStats(); retVal = true; } else if (strcmp(text, "Phystats") == 0) { dataModel->getGuiBuilder().togglePhysicsStats(); retVal = true; } else if (strcmp(text, "Sumstats") == 0) { dataModel->getGuiBuilder().toggleSummaryStats(); retVal = true; } else if (strcmp(text, "Cusstats") == 0) { dataModel->getGuiBuilder().toggleCustomStats(); retVal = true; } else if (boost::iequals(text, "/console")) { if (CoreGuiService* cgs = dataModel->find()) { if (Instance* parent = cgs->findFirstChildByName("RobloxGui")) { if (Instance* controlFrame = parent->findFirstChildByName("ControlFrame")) { parent = controlFrame; } if (BindableFunction* toggle = Instance::fastDynamicCast(parent->findFirstChildByName("ToggleDevConsole"))) { toggle->invoke(rbx::make_shared(), boost::bind(&doNothing, _1), boost::bind(&doNothing, _1)); } } } retVal = true; } return retVal; } }