mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-05 21:27:48 +00:00
GEEKING
This commit is contained in:
@@ -0,0 +1,768 @@
|
||||
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "Gui/ChatOutput.h"
|
||||
#include "Network/Player.h"
|
||||
#include "Network/Players.h"
|
||||
#include "Util/Hash.h"
|
||||
#include "V8DataModel/GameSettings.h"
|
||||
#include "V8DataModel/Teams.h"
|
||||
#include "V8DataModel/Scale9Frame.h"
|
||||
#include "V8DataModel/ImageLabel.h"
|
||||
#include "V8DataModel/GuiObject.h"
|
||||
#include "V8DataModel/BillboardGui.h"
|
||||
#include "V8DataModel/PlayerGui.h"
|
||||
#include "V8DataModel/TextLabel.h"
|
||||
#include "Gui/ProfanityFilter.h"
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "GfxBase/ViewportBillboarder.h"
|
||||
#include "GfxBase/AdornBillboarder.h"
|
||||
#include "FastLog.h"
|
||||
|
||||
namespace RBX {
|
||||
|
||||
using RBX::Network::Players;
|
||||
|
||||
const char* PlayerChatLine::ROBLOXNAME = "(ROBLOX)";
|
||||
const char* ChatLine::ELIPSES = "...";
|
||||
const int ChatOutput::MaxChatBubblesPerPlayer = 10;
|
||||
const int ChatOutput::MaxChatLinesPerBubble = 5; // the number of lines each bubble can display
|
||||
const int ChatLine::CchMaxChatMessageLength = 128; // max chat message length, including null terminator and elipses.
|
||||
const int CchMaxChatMessageLengthExclusive = ChatLine::CchMaxChatMessageLength - strlen(ChatLine::ELIPSES) - 1;
|
||||
|
||||
static float lerpLength(const std::string& msg, float min, float max)
|
||||
{
|
||||
return min + (max-min)*std::min(msg.length()/75.0f, 1.0f);
|
||||
}
|
||||
|
||||
float ChatLine::ComputeBubbleLifetime(const std::string& msg, bool isSelf)
|
||||
{
|
||||
if(isSelf)
|
||||
return lerpLength(msg,8,15);
|
||||
else
|
||||
return lerpLength(msg,12,20);
|
||||
}
|
||||
|
||||
ChatLine::ChatLine(ChatType chatType, const std::string& message, float startTime, BubbleColor bubbleColor, bool isLocalPlayer)
|
||||
: chatType(chatType)
|
||||
, origin()
|
||||
, message(message)
|
||||
, startTime(startTime)
|
||||
, bubbleDieDelay(ChatLine::ComputeBubbleLifetime(message, isLocalPlayer)) // mutable.
|
||||
, bubbleColor(bubbleColor)
|
||||
, isLocalPlayer(isLocalPlayer)
|
||||
{}
|
||||
|
||||
PlayerChatLine::PlayerChatLine(ChatType chatType, boost::shared_ptr<Network::Player> player, const std::string& message, float startTime, bool isLocalPlayer)
|
||||
: ChatLine(chatType, message, startTime, ChatLine::WHITE, isLocalPlayer)
|
||||
, user("")
|
||||
, historyDieDelay(60.0f)
|
||||
{
|
||||
if (player)
|
||||
{
|
||||
user = player->getName();
|
||||
origin = player->getSharedCharacter();
|
||||
|
||||
// If this is a team game, make the color of the text be the team color of the speaker
|
||||
Teams *teams = ServiceProvider::find<Teams>(player.get());
|
||||
|
||||
if(teams != NULL && teams->isTeamGame())
|
||||
{
|
||||
if(player->getNeutral() == false)
|
||||
{
|
||||
Team *t = teams->getTeamFromPlayer(player.get());
|
||||
if (t != NULL)
|
||||
userColor = t->getTeamColor().color3();
|
||||
else
|
||||
userColor = G3D::Color3::black();
|
||||
}
|
||||
else
|
||||
userColor = G3D::Color3::white();
|
||||
|
||||
} else {
|
||||
unsigned int hash = Hash::hash(user);
|
||||
userColor = Color::colorFromIndex8(hash % 8);
|
||||
}
|
||||
}
|
||||
else{
|
||||
user = ROBLOXNAME;
|
||||
userColor = Color::black();
|
||||
}
|
||||
}
|
||||
|
||||
GameChatLine::GameChatLine(boost::shared_ptr<Instance> origin, const std::string& message, float startTime, bool isLocalPlayer, BubbleColor bubbleColor)
|
||||
:ChatLine(Instance::fastDynamicCast<ModelInstance>(origin.get()) ? ChatLine::PLAYER_GAME_CHAT : ChatLine::BOT_CHAT, message, startTime, bubbleColor, isLocalPlayer)
|
||||
{
|
||||
this->origin = origin;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static shared_ptr<Scale9Frame> createChatBubbleMain(const std::string& filePrefix)
|
||||
{
|
||||
shared_ptr<Scale9Frame> chatBubbleMain = Creatable<Instance>::create<Scale9Frame>();
|
||||
chatBubbleMain->setName("ChatBubble");
|
||||
chatBubbleMain->setScaleEdgeSize(Vector2int16(8,8));
|
||||
chatBubbleMain->setSlicePrefix("rbxasset://textures/"+filePrefix+".png");
|
||||
chatBubbleMain->setBackgroundTransparency(0.0f);
|
||||
chatBubbleMain->setBorderSizePixel(0);
|
||||
chatBubbleMain->setSize(UDim2(1.0f, 0, 1.0f, 0));
|
||||
chatBubbleMain->setPosition(UDim2(0,0,0,-30));
|
||||
|
||||
return chatBubbleMain;
|
||||
}
|
||||
|
||||
static shared_ptr<Scale9Frame> createChatBubbleWithTail(const std::string& filePrefix, const UDim2& position, const UDim2& size)
|
||||
{
|
||||
shared_ptr<Scale9Frame> chatBubbleMain;
|
||||
chatBubbleMain = createChatBubbleMain(filePrefix);
|
||||
shared_ptr<ImageLabel> chatBubbleTail = Creatable<Instance>::create<ImageLabel>();
|
||||
chatBubbleTail->setName("ChatBubbleTail");
|
||||
chatBubbleTail->setImage("rbxasset://textures/ui/dialog_tail.png");
|
||||
chatBubbleTail->setBackgroundTransparency(1.0f);
|
||||
chatBubbleTail->setBorderSizePixel(0);
|
||||
chatBubbleTail->setPosition(position);
|
||||
chatBubbleTail->setSize(size);
|
||||
chatBubbleTail->setParent(chatBubbleMain.get());
|
||||
|
||||
return chatBubbleMain;
|
||||
}
|
||||
|
||||
static shared_ptr<Scale9Frame> createScaledChatBubbleWithTail(const std::string& filePrefix, float frameScaleSize, const UDim2& position)
|
||||
{
|
||||
shared_ptr<Scale9Frame> chatBubbleMain;
|
||||
chatBubbleMain = createChatBubbleMain(filePrefix);
|
||||
|
||||
shared_ptr<Frame> frame = Creatable<Instance>::create<Frame>();
|
||||
frame->setName("ChatBubbleTailFrame");
|
||||
frame->setBackgroundTransparency(1.0f);
|
||||
frame->setSizeConstraint(GuiObject::RELATIVE_XX);
|
||||
frame->setPosition(UDim2(0.5f, 0, 1, 0));
|
||||
frame->setSize(UDim2(frameScaleSize, 0, frameScaleSize, 0));
|
||||
frame->setParent(chatBubbleMain.get());
|
||||
|
||||
shared_ptr<ImageLabel> chatBubbleTail = Creatable<Instance>::create<ImageLabel>();
|
||||
chatBubbleTail->setName("ChatBubbleTail");
|
||||
chatBubbleTail->setImage("rbxasset://textures/ui/dialog_tail.png");
|
||||
chatBubbleTail->setBackgroundTransparency(1.0f);
|
||||
chatBubbleTail->setBorderSizePixel(0);
|
||||
chatBubbleTail->setPosition(position);
|
||||
chatBubbleTail->setSize(UDim2(1,0,0.5,0));
|
||||
chatBubbleTail->setParent(frame.get());
|
||||
|
||||
return chatBubbleMain;
|
||||
}
|
||||
|
||||
static shared_ptr<GuiObject> createChatImposter(const std::string& filePrefix, const std::string& dotDotDot, float yOffset)
|
||||
{
|
||||
shared_ptr<ImageLabel> result = Creatable<Instance>::create<ImageLabel>();
|
||||
result->setName("DialogPlaceholder");
|
||||
result->setImage("rbxasset://textures/" + filePrefix + ".png");
|
||||
result->setBackgroundTransparency(1.0f);
|
||||
result->setBorderSizePixel(0);
|
||||
result->setPosition(UDim2(0, 0, -1.25, 0));
|
||||
result->setSize(UDim2(1, 0, 1, 0));
|
||||
|
||||
shared_ptr<ImageLabel> image = Creatable<Instance>::create<ImageLabel>();
|
||||
image->setName("DotDotDot");
|
||||
image->setImage("rbxasset://textures/"+dotDotDot+".png");
|
||||
image->setBackgroundTransparency(1.0f);
|
||||
image->setBorderSizePixel(0);
|
||||
image->setPosition(UDim2(0.001, 0, yOffset, 0));
|
||||
image->setSize(UDim2(1, 0, 0.7, 0));
|
||||
image->setParent(result.get());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
ChatOutput::ChatOutput()
|
||||
: time(0.0)
|
||||
, players(NULL)
|
||||
{
|
||||
{
|
||||
chatBubble[ChatLine::WHITE] = createChatBubbleMain("ui/dialog_white");
|
||||
chatBubbleWithTail[ChatLine::WHITE] = createChatBubbleWithTail("ui/dialog_white", UDim2(0.5f, -14, 1.0f, 0), UDim2(0.0f, 30, 0.0f, 14));
|
||||
scalingChatBubbleWithTail[ChatLine::WHITE] = createScaledChatBubbleWithTail("ui/dialog_white", 0.5f, UDim2(-0.5f, 0, 0.0f, 0));
|
||||
}
|
||||
|
||||
{
|
||||
chatBubble[ChatLine::BLUE] = createChatBubbleMain("ui/dialog_blue");
|
||||
chatBubbleWithTail[ChatLine::BLUE] = createChatBubbleWithTail("ui/dialog_blue", UDim2(0.5f, -14, 1.0f, -1), UDim2(0.0f, 30, 0.0f, 14));
|
||||
scalingChatBubbleWithTail[ChatLine::BLUE] = createScaledChatBubbleWithTail("ui/dialog_blue", 0.5f, UDim2(-0.5f, 0, 0.0f, -1));
|
||||
}
|
||||
|
||||
{
|
||||
chatBubble[ChatLine::RED] = createChatBubbleMain("ui/dialog_red");
|
||||
chatBubbleWithTail[ChatLine::RED] = createChatBubbleWithTail("ui/dialog_red", UDim2(0.5f, -14, 1.0f, -1), UDim2(0.0f, 30, 0.0f, 14));
|
||||
scalingChatBubbleWithTail[ChatLine::RED] = createScaledChatBubbleWithTail("ui/dialog_red", 0.5f, UDim2(-0.5f, 0, 0.0f, -1));
|
||||
}
|
||||
|
||||
{
|
||||
chatBubble[ChatLine::GREEN] = createChatBubbleMain("ui/dialog_green");
|
||||
chatBubbleWithTail[ChatLine::GREEN] = createChatBubbleWithTail("ui/dialog_green", UDim2(0.5f, -14, 1.0f, -1), UDim2(0.0f, 30, 0.0f, 14));
|
||||
scalingChatBubbleWithTail[ChatLine::GREEN] = createScaledChatBubbleWithTail("ui/dialog_green", 0.5f, UDim2(-0.5f, 0, 0.0f, -1));
|
||||
}
|
||||
|
||||
chatPlaceholder[ChatLine::WHITE] = createChatImposter("ui/chatBubble_white_notify_bkg", "chatBubble_bot_notifyGray_dotDotDot", -0.05f);
|
||||
chatPlaceholder[ChatLine::BLUE] = createChatImposter("ui/chatBubble_blue_notify_bkg", "chatBubble_bot_notifyGray_dotDotDot", -0.12);
|
||||
chatPlaceholder[ChatLine::GREEN] = createChatImposter("ui/chatBubble_green_notify_bkg", "chatBubble_bot_notifyGray_dotDotDot",-0.12);
|
||||
chatPlaceholder[ChatLine::RED] = createChatImposter("ui/chatBubble_red_notify_bkg", "chatBubble_bot_notifyGray_dotDotDot", -0.12);
|
||||
}
|
||||
|
||||
|
||||
ChatOutput::~ChatOutput()
|
||||
{
|
||||
while (!fifo.empty()) {
|
||||
removeOldest();
|
||||
}
|
||||
}
|
||||
|
||||
const ModelInstance* PlayerChatLine::getCharacter() const
|
||||
{
|
||||
return Instance::fastDynamicCast<ModelInstance>(getOrigin());
|
||||
}
|
||||
void ChatOutput::acceleratedBubbleDecay(ChatLine* line, float wallStep, bool isMoving, bool isVisible)
|
||||
{
|
||||
if(line->isLocalPlayer && isMoving)
|
||||
{
|
||||
line->bubbleDieDelay -= 3* wallStep; // effectively quarters delay time.
|
||||
}
|
||||
else if(isVisible)
|
||||
{
|
||||
line->bubbleDieDelay -= wallStep; // effectively halfs delay time.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string ChatOutput::SanitizeChatLine(const std::string& msg)
|
||||
{
|
||||
if(msg.size() > (size_t)CchMaxChatMessageLengthExclusive)
|
||||
{
|
||||
return msg.substr(0, CchMaxChatMessageLengthExclusive) + ChatLine::ELIPSES;
|
||||
}
|
||||
else
|
||||
{
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
void ChatOutput::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
||||
{
|
||||
heartbeatConnection.disconnect();
|
||||
playerChatMessageConnection.disconnect();
|
||||
gameChatMessageConnection.disconnect();
|
||||
|
||||
Super::onServiceProvider(oldProvider, newProvider);
|
||||
|
||||
if (RunService* runService = ServiceProvider::create<RunService>(newProvider))
|
||||
heartbeatConnection = runService->heartbeatSignal.connect(boost::bind(&ChatOutput::onHeartbeat, this, _1));
|
||||
|
||||
players = ServiceProvider::create<Network::Players>(newProvider);
|
||||
|
||||
if(newProvider)
|
||||
{
|
||||
if (players)
|
||||
playerChatMessageConnection = players->chatMessageSignal.connect(boost::bind(&ChatOutput::onPlayerChatMessage, this, _1));
|
||||
|
||||
if(ChatService* chatService = ServiceProvider::create<ChatService>(newProvider))
|
||||
gameChatMessageConnection = chatService->chattedSignal.connect(boost::bind(&ChatOutput::onGameChatMessage, this, _1, _2, _3));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ChatOutput::removeOldest()
|
||||
{
|
||||
fifo.pop_front();
|
||||
}
|
||||
|
||||
bool ChatOutput::removeExpired()
|
||||
{
|
||||
bool bRemovedSomething = false;
|
||||
int maxBubblesPerPlayer = std::min(RBX::GameSettings::singleton().bubbleChatMaxBubbles, MaxChatBubblesPerPlayer);
|
||||
|
||||
if (!fifo.empty())
|
||||
{
|
||||
// fifo contains only PlayerChatLine objects, its only using generic ChatLine for use in helper functions
|
||||
PlayerChatLine* line = boost::polymorphic_downcast<PlayerChatLine*>(fifo.front().get());
|
||||
if((line->historyDieDelay + line->startTime) < time)
|
||||
{
|
||||
fifo.pop_front();
|
||||
bRemovedSomething = true;
|
||||
}
|
||||
}
|
||||
for(CharacterChatMap::iterator it = characterSortedMsg.begin(); it != characterSortedMsg.end();)
|
||||
{
|
||||
std::deque<boost::shared_ptr<ChatLine> >& playerfifo(it->second.fifo);
|
||||
if(!playerfifo.empty())
|
||||
{
|
||||
ChatLine* line = playerfifo.front().get();
|
||||
if((line->bubbleDieDelay + line->startTime) < time || (int) playerfifo.size() > maxBubblesPerPlayer)
|
||||
{
|
||||
playerfifo.pop_front();
|
||||
bRemovedSomething = true;
|
||||
}
|
||||
}
|
||||
// remove if empty
|
||||
if(playerfifo.empty())
|
||||
{
|
||||
if(shared_ptr<BillboardGui> billlboardGui = it->second.billboardGui.lock())
|
||||
billlboardGui->setParent(NULL);
|
||||
|
||||
characterSortedMsg.erase(it++);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
return bRemovedSomething;
|
||||
}
|
||||
|
||||
void ChatOutput::onHeartbeat(const Heartbeat& heartbeat)
|
||||
{
|
||||
float wallStep = (float)heartbeat.wallStep;
|
||||
time += wallStep;
|
||||
|
||||
|
||||
for(CharacterChatMap::iterator it = characterSortedMsg.begin(); it != characterSortedMsg.end(); ++it)
|
||||
{
|
||||
std::deque<boost::shared_ptr<ChatLine> >& playerfifo(it->second.fifo);
|
||||
for (size_t i = 0; i < playerfifo.size(); ++i)
|
||||
{
|
||||
acceleratedBubbleDecay(playerfifo[i].get(), wallStep, it->second.isMoving, it->second.isVisible);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// delete chatLines with dieTime younger than heartbeat time;
|
||||
while(removeExpired());
|
||||
}
|
||||
|
||||
void ChatOutput::createBillboardGuiHelper(Instance* instance, bool onlyCharacter)
|
||||
{
|
||||
if(!characterSortedMsg[instance].billboardGui.lock())
|
||||
{
|
||||
if(CoreGuiService* coreGui = ServiceProvider::create<CoreGuiService>(instance))
|
||||
{
|
||||
if(!onlyCharacter)
|
||||
{
|
||||
if(PartInstance* part = instance->fastDynamicCast<PartInstance>())
|
||||
{
|
||||
//Create a new billboardGui object attached to this player
|
||||
shared_ptr<BillboardGui> billboardGui = Creatable<Instance>::create<BillboardGui>();
|
||||
billboardGui->setAdornee(part);
|
||||
billboardGui->setRobloxLocked(true);
|
||||
billboardGui->setParent(coreGui);
|
||||
|
||||
characterSortedMsg[instance].billboardGui = billboardGui;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(ModelInstance* character = instance->fastDynamicCast<ModelInstance>())
|
||||
{
|
||||
if(PartInstance* head = Instance::fastDynamicCast<PartInstance>(character->findFirstChildByName("Head")))
|
||||
{
|
||||
//Create a new billboardGui object attached to this player
|
||||
shared_ptr<BillboardGui> billboardGui = Creatable<Instance>::create<BillboardGui>();
|
||||
billboardGui->setAdornee(head);
|
||||
billboardGui->setRobloxLocked(true);
|
||||
billboardGui->setParent(coreGui);
|
||||
|
||||
characterSortedMsg[instance].billboardGui = billboardGui;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChatOutput::onGameChatMessage(boost::shared_ptr<Instance> origin, const std::string& message, ChatService::ChatColor color)
|
||||
{
|
||||
if (ProfanityFilter::ContainsProfanity(message))
|
||||
return;
|
||||
|
||||
Network::Player* local = players->getLocalPlayer();
|
||||
bool fromOthers = local != NULL && (local->getCharacter() != origin.get());
|
||||
|
||||
ChatLine::BubbleColor bubbleColor = ChatLine::WHITE;
|
||||
switch(color)
|
||||
{
|
||||
case ChatService::CHAT_BLUE: bubbleColor = ChatLine::BLUE; break;
|
||||
case ChatService::CHAT_GREEN: bubbleColor = ChatLine::GREEN; break;
|
||||
case ChatService::CHAT_RED: bubbleColor = ChatLine::RED; break;
|
||||
}
|
||||
std::string safemessage = SanitizeChatLine(message);
|
||||
shared_ptr<ChatLine> line(new GameChatLine(origin, safemessage, time, !fromOthers, bubbleColor));
|
||||
characterSortedMsg[line->getOrigin()].fifo.push_back(line);
|
||||
createBillboardGuiHelper(origin.get(), false);
|
||||
}
|
||||
|
||||
void ChatOutput::onPlayerChatMessage(const Network::ChatMessage& event)
|
||||
{
|
||||
// eliminate display of emotes
|
||||
if (boost::starts_with(event.message, "/e ") || boost::starts_with(event.message, "/emote "))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (fifo.size() > (size_t)RBX::GameSettings::singleton().chatScrollLength)
|
||||
{
|
||||
removeOldest();
|
||||
}
|
||||
|
||||
Network::Player* local = players->getLocalPlayer();
|
||||
bool fromOthers = local != NULL && (event.source.get() != local);
|
||||
|
||||
ChatLine::ChatType chatType = ChatLine::PLAYER_CHAT;
|
||||
switch(event.chatType)
|
||||
{
|
||||
case Network::ChatMessage::CHAT_TYPE_TEAM:
|
||||
chatType = ChatLine::PLAYER_TEAM_CHAT;
|
||||
break;
|
||||
case Network::ChatMessage::CHAT_TYPE_ALL:
|
||||
chatType = ChatLine::PLAYER_CHAT;
|
||||
break;
|
||||
case Network::ChatMessage::CHAT_TYPE_WHISPER:
|
||||
chatType = ChatLine::PLAYER_WHISPER_CHAT;
|
||||
break;
|
||||
case Network::ChatMessage::CHAT_TYPE_GAME:
|
||||
chatType = ChatLine::GAME_MESSAGE;
|
||||
break;
|
||||
}
|
||||
|
||||
std::string safemessage = SanitizeChatLine(event.message);
|
||||
|
||||
shared_ptr<PlayerChatLine> line(new PlayerChatLine(chatType, event.source, safemessage, time, !fromOthers));
|
||||
fifo.push_back(line);
|
||||
characterSortedMsg[line->getOrigin()].fifo.push_back(line);
|
||||
|
||||
if(event.source)
|
||||
{
|
||||
//Game chat (badges) won't show up here
|
||||
createBillboardGuiHelper(event.source->getCharacter(), true);
|
||||
}
|
||||
}
|
||||
|
||||
bool ChatOutput::bubbleChatEnabled()
|
||||
{
|
||||
if(players)
|
||||
{
|
||||
return players->getBubbleChat();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ChatOutput::render2d(Adorn* adorn)
|
||||
{
|
||||
render2d_bubbleStyle(adorn, bubbleChatEnabled());
|
||||
}
|
||||
|
||||
void ChatOutput::renderBubbleImposters(Adorn* adorn, weak_ptr<const Instance> weakOwner, weak_ptr<PartInstance> weakHead)
|
||||
{
|
||||
shared_ptr<PartInstance> head = weakHead.lock();
|
||||
if(!head)
|
||||
return;
|
||||
|
||||
shared_ptr<const Instance> owner = weakOwner.lock();
|
||||
if(!owner)
|
||||
return;
|
||||
|
||||
Workspace* workspace = ServiceProvider::find<Workspace>(head.get());
|
||||
if(!workspace)
|
||||
return;
|
||||
|
||||
ViewportBillboarder viewportBillboarder(Vector3(0,0,0), Vector3(0,0,0), Vector2(0,0), UDim2(3,0, 3.6, 0), NULL /*work in actual pixel space*/);
|
||||
viewportBillboarder.update(adorn->getViewport(), *workspace->getConstCamera(), head.get()->getPartSizeXml(), head.get()->calcRenderingCoordinateFrame());
|
||||
if(!viewportBillboarder.isVisibleAndValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AdornBillboarder adornView(adorn, viewportBillboarder);
|
||||
|
||||
std::deque<boost::shared_ptr<ChatLine> >& playerfifo(characterSortedMsg[owner.get()].fifo);
|
||||
for (size_t i = playerfifo.size()-1; i != ~0; --i)
|
||||
{
|
||||
if(playerfifo[i]->chatType != ChatLine::GAME_MESSAGE)
|
||||
{
|
||||
chatPlaceholder[playerfifo[i]->bubbleColor]->legacyRender2d(&adornView, adornView.getViewport());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChatOutput::renderBubbles(Adorn* adorn, weak_ptr<const Instance> weakOwner, weak_ptr<PartInstance> weakHead, bool playerBubbleChat,
|
||||
Vector3 extentsOffset, Vector3 studsOffset)
|
||||
{
|
||||
shared_ptr<PartInstance> head = weakHead.lock();
|
||||
if(!head)
|
||||
return;
|
||||
|
||||
shared_ptr<const Instance> owner = weakOwner.lock();
|
||||
if(!owner)
|
||||
return;
|
||||
|
||||
Workspace* workspace = ServiceProvider::find<Workspace>(head.get());
|
||||
if(!workspace)
|
||||
return;
|
||||
|
||||
Text::Font font;
|
||||
TextService::Font tsFont;
|
||||
tsFont = TextService::FONT_SOURCESANS;
|
||||
font = Text::FONT_SOURCESANS;
|
||||
|
||||
const UDim2 size(UDim(22, 25), UDim(3, 0)); /// we need some size, but we are not constrained by this size.
|
||||
/// we will use billboard witdh as maximum width, but we will allow ourselves to spill out upwards.
|
||||
/// note also: bubble is drawn in actual pixel space. but size of bubble is set using some stud size contribution (will get smaller
|
||||
/// as we get farther). We will maintain character width so that maximum width always corresponds to same number of chars. this
|
||||
/// is to ensure that there is no re-layouting of text as we move away
|
||||
/// we will align bottom of text to top of viewport. this means that the vertical dimention is only usefull for adjusting the size of the bubble arrow.
|
||||
|
||||
ViewportBillboarder viewportBillboarder(extentsOffset, studsOffset, Vector2::zero(), size, NULL /*work in actual pixel space*/);
|
||||
viewportBillboarder.update(adorn->getViewport(), *workspace->getConstCamera(), head.get()->getPartSizeXml(), head.get()->calcRenderingCoordinateFrame());
|
||||
|
||||
AdornBillboarder adornView(adorn, viewportBillboarder);
|
||||
|
||||
std::deque<boost::shared_ptr<ChatLine> >& playerfifo(characterSortedMsg[owner.get()].fifo);
|
||||
|
||||
Rect2D viewsize = adornView.getViewport();
|
||||
// clamp max size of bubble.
|
||||
float viewsizewidth = std::min(viewsize.width(), (float)kMaxTextSize * (kMaxCharsInLine + 1));
|
||||
float sizeadjust = viewsize.width() - viewsizewidth;
|
||||
viewsize = Rect2D::xywh(viewsize.x0() + sizeadjust * 0.5f, viewsize.y0(), viewsize.width() - sizeadjust, viewsize.height());
|
||||
|
||||
double textSize = floor(viewsize.width() + 0.5f) / (kMaxCharsInLine + 1); // margin is half a char each side.
|
||||
double textSizeHeight = textSize *1.5f; //magic number, we just know this.
|
||||
double textMargin = textSize /2;
|
||||
double spaceBetweenBubbles = 2;
|
||||
Vector2 availableTextSpace((float) (viewsize.width() - textMargin * 2), (float)(textSizeHeight * MaxChatLinesPerBubble + 0.01f));
|
||||
double lineCursor = -textSizeHeight; // start at the top of the viewport + textsize, (note: we go up from there!)
|
||||
Vector2 pointOfBubble(viewsize.center().x, 0); // point at the top of the viewport
|
||||
|
||||
textSize *= 1.8f; // source sans is smaller than legacy
|
||||
spaceBetweenBubbles = 4.0f;
|
||||
lineCursor = 0.0f;
|
||||
|
||||
|
||||
//Allocate memory on STACK for this data structure
|
||||
//Rect2D* textrect = (Rect2D*)alloca(sizeof(Rect2D) * playerfifo.size());
|
||||
Rect2D* bubbleTextRect = (Rect2D*)alloca(sizeof(Rect2D) * playerfifo.size());
|
||||
|
||||
float newBubbleBorder = 5.0f;
|
||||
unsigned chatCount = 0;
|
||||
for (size_t i = playerfifo.size()-1; i != ~0; --i)
|
||||
{
|
||||
if(!playerBubbleChat && playerfifo[i]->isPlayerChat())
|
||||
continue;
|
||||
|
||||
// grab head, and try to get the workspace from the first one we find.
|
||||
TextService* textService = ServiceProvider::create<TextService>(this);
|
||||
if(!textService)
|
||||
continue;
|
||||
|
||||
Vector2 textBounds = textService->getTypesetter(tsFont)->measure(playerfifo[i]->message, (float)textSize, availableTextSpace);
|
||||
|
||||
float scale = 1.0f;
|
||||
scale = std::min(scale, textBounds.x / 60);
|
||||
scale = std::min(scale, textBounds.y / 60);
|
||||
|
||||
float border = 7 + 13*scale;
|
||||
float yborder = border;
|
||||
float olderChatScale = 3;
|
||||
border = yborder = newBubbleBorder;
|
||||
yborder /= olderChatScale;
|
||||
float x = (float)(viewsize.center().x - textBounds.x /2);
|
||||
float y = (float)(lineCursor - textBounds.y);
|
||||
bubbleTextRect[chatCount] = Rect2D::xywh(x-(2.5f*border), y-yborder, textBounds.x+5*border, textBounds.y+2*yborder);
|
||||
//textrect[chatCount] = Rect2D::xywh((float)(viewsize.center().x - textBounds.x /2), (float)(lineCursor - textBounds.y), textBounds.x, textBounds.y).border((float)-textMargin);
|
||||
lineCursor -= (bubbleTextRect[chatCount].height() + spaceBetweenBubbles);
|
||||
|
||||
chatCount++;
|
||||
}
|
||||
|
||||
|
||||
for (size_t i = 0; i < playerfifo.size(); ++i)
|
||||
{
|
||||
if(!playerBubbleChat && playerfifo[i]->isPlayerChat())
|
||||
continue;
|
||||
|
||||
Color3 textColor = Color3::black();
|
||||
Text::XAlign xAlin = Text::XALIGN_CENTER;
|
||||
|
||||
ChatLine::BubbleColor bubbleColor = playerfifo[i]->bubbleColor;
|
||||
const Rect2D& rect = bubbleTextRect[chatCount-1];
|
||||
if(chatCount == 1)
|
||||
{
|
||||
if(rect.width() < 60 || rect.height() < 60)
|
||||
{
|
||||
if(GuiObject* tail = Instance::fastDynamicCast<GuiObject>(scalingChatBubbleWithTail[bubbleColor]->findFirstChildByName("ChatBubbleTailFrame"))){
|
||||
tail->setSizeConstraint(rect.width() < rect.height() ? GuiObject::RELATIVE_XX : GuiObject::RELATIVE_YY);
|
||||
scalingChatBubbleWithTail[bubbleColor]->legacyRender2d(&adornView, rect);
|
||||
}
|
||||
}
|
||||
else{
|
||||
//straight up render, no scaling funniness
|
||||
chatBubbleWithTail[bubbleColor]->legacyRender2d(&adornView, rect);
|
||||
}
|
||||
textColor = Color3(75/255.0f, 75/255.0f, 75/255.0f);
|
||||
xAlin = Text::XALIGN_LEFT;
|
||||
chatBubble[bubbleColor]->setBackgroundTransparency(0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
chatBubble[bubbleColor]->legacyRender2d(&adornView, rect);
|
||||
|
||||
textColor = Color3(184/255.0f, 184/255.0f, 184/255.0f);
|
||||
xAlin = Text::XALIGN_LEFT;
|
||||
}
|
||||
|
||||
Vector2 center = rect.center() + Vector2(0, -30);
|
||||
Color4 tColor = textColor;
|
||||
center.x -= (rect.width() / 2) - (newBubbleBorder * 2.5f);
|
||||
center.y -= 2;
|
||||
tColor = Color4(textColor, 1.0f - chatBubble[bubbleColor]->getBackgroundTransparency());
|
||||
adornView.drawFont2D( playerfifo[i]->message,
|
||||
center,
|
||||
(float)textSize, // size
|
||||
false,
|
||||
tColor,
|
||||
Color4(Color3::black(), 0), // no outline
|
||||
font,
|
||||
xAlin,
|
||||
Text::YALIGN_CENTER,
|
||||
availableTextSpace,
|
||||
Rect2D::xyxy(-1,-1,-1,-1));
|
||||
|
||||
chatCount--;
|
||||
}
|
||||
}
|
||||
void ChatOutput::render2d_bubbleStyle(Adorn* adorn, bool playerBubbleChat)
|
||||
{
|
||||
Vector3 extentsOffset;
|
||||
Vector3 studsOffset;
|
||||
|
||||
Workspace* workspace = NULL;
|
||||
|
||||
for(CharacterChatMap::iterator it = characterSortedMsg.begin(); it != characterSortedMsg.end(); ++it)
|
||||
{
|
||||
std::deque<boost::shared_ptr<ChatLine> >& playerfifo(it->second.fifo);
|
||||
|
||||
if(shared_ptr<BillboardGui> billboardGui = it->second.billboardGui.lock())
|
||||
{
|
||||
billboardGui->setRenderFunction(NULL);
|
||||
}
|
||||
|
||||
it->second.isVisible = false;
|
||||
it->second.isMoving = false;
|
||||
|
||||
if(playerfifo.empty())
|
||||
continue;
|
||||
|
||||
PartInstance* head = NULL;
|
||||
if(boost::shared_ptr<Instance> instance = playerfifo[0]->origin.lock())
|
||||
{
|
||||
switch(playerfifo[0]->chatType)
|
||||
{
|
||||
case ChatLine::PLAYER_CHAT:
|
||||
case ChatLine::PLAYER_TEAM_CHAT:
|
||||
case ChatLine::PLAYER_WHISPER_CHAT:
|
||||
case ChatLine::PLAYER_GAME_CHAT:
|
||||
extentsOffset = Vector3(0,0,0);
|
||||
|
||||
if(playerfifo[0]->chatType == ChatLine::PLAYER_GAME_CHAT)
|
||||
studsOffset = Vector3(0, 0, 2); //towards camera (so that gear doesn't block readability)
|
||||
else
|
||||
studsOffset = Vector3(0,0,2); //towards camera (so that gear doesn't block readability)
|
||||
if(ModelInstance* character = instance->fastDynamicCast<ModelInstance>())
|
||||
{
|
||||
head = Instance::fastDynamicCast<PartInstance>(character->findFirstChildByName("Head"));
|
||||
|
||||
if(head && !workspace)
|
||||
{
|
||||
workspace = ServiceProvider::find<Workspace>(head);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RBXASSERT(0);
|
||||
}
|
||||
break;
|
||||
case ChatLine::GAME_MESSAGE:
|
||||
RBXASSERT(0);
|
||||
break;
|
||||
case ChatLine::BOT_CHAT:
|
||||
extentsOffset = Vector3(0,1,0);
|
||||
|
||||
head = instance->fastDynamicCast<PartInstance>();
|
||||
if(head)
|
||||
{
|
||||
if(head->getName() == "Head" && Instance::fastDynamicCast<ModelInstance>(head->getParent()))
|
||||
studsOffset = Vector3(0, 0, 0); //towards camera (so that gear doesn't block readability)
|
||||
else
|
||||
studsOffset = Vector3(0.0, 0, 0);
|
||||
|
||||
if(!workspace)
|
||||
{
|
||||
workspace = ServiceProvider::find<Workspace>(head);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!workspace || !head)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!playerBubbleChat)
|
||||
{
|
||||
bool foundGameChat = false;
|
||||
//Game chat only
|
||||
for (size_t i = playerfifo.size()-1; i != ~0; --i)
|
||||
{
|
||||
if(!playerfifo[0]->isPlayerChat())
|
||||
{
|
||||
foundGameChat = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!foundGameChat)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
CoordinateFrame headCFrame = head->calcRenderingCoordinateFrame();
|
||||
|
||||
Vector3 distanceToObjectCenter = workspace->getCamera()->coordinateFrame().pointToObjectSpace(headCFrame.translation);
|
||||
if(distanceToObjectCenter.z > 0)
|
||||
continue;
|
||||
|
||||
if(distanceToObjectCenter.z < -90.0f)
|
||||
{
|
||||
if(shared_ptr<BillboardGui> billboardGui = it->second.billboardGui.lock())
|
||||
{
|
||||
billboardGui->setRenderFunction(boost::bind(&ChatOutput::renderBubbleImposters, this, _2, weak_from(it->first), weak_from(head)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RBX::Frustum frustum(workspace->getCamera()->frustum());
|
||||
it->second.isVisible = frustum.containsPoint(headCFrame.translation);
|
||||
it->second.isMoving = !head->getVelocity().linear.isZero();
|
||||
if(shared_ptr<BillboardGui> billboardGui = it->second.billboardGui.lock())
|
||||
{
|
||||
billboardGui->setRenderFunction(boost::bind(&ChatOutput::renderBubbles, this, _2, weak_from(it->first), weak_from(head), playerBubbleChat, extentsOffset, studsOffset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "Gui/ChatWidget.h"
|
||||
#include "Network/Players.h"
|
||||
#include "Util/SoundService.h"
|
||||
|
||||
namespace RBX {
|
||||
|
||||
Gui::WidgetState UnifiedImageWidget::getWidgetState() const
|
||||
{
|
||||
switch (getMenuState())
|
||||
{
|
||||
default:
|
||||
case NOTHING: return Gui::NOTHING;
|
||||
|
||||
case HOVER: return Gui::HOVER;
|
||||
|
||||
case SHOWN_APPEARING:
|
||||
case SHOWN: return Gui::DOWN_OVER;
|
||||
}
|
||||
}
|
||||
|
||||
void UnifiedImageWidget::render2dMe(Adorn* adorn)
|
||||
{
|
||||
if (isVisible()) {
|
||||
if (guiImageDraw.setImageFromName(adorn, imageName, imageState)) {
|
||||
guiImageDraw.render2d(adorn, true, getMyRect(adorn->getCanvas()), getWidgetState(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
bool ChatButton::isVisible() const
|
||||
{
|
||||
RBX::Network::Players* players = ServiceProvider::find<RBX::Network::Players>(this);
|
||||
|
||||
return (players && players->getLocalPlayer() && RBX::Network::Players::clientIsPresent(this));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
ChatWidget::ChatWidget(const std::string& text, std::string code)
|
||||
{
|
||||
setName(text);
|
||||
this->code = code;
|
||||
}
|
||||
|
||||
std::string ChatWidget::findMenuString(GuiItem* item)
|
||||
{
|
||||
if (Instance::fastDynamicCast<ChatWidget>(item))
|
||||
{
|
||||
RBXASSERT(item->getParent());
|
||||
|
||||
int childIndex = item->getParent()->findChildIndex(item);
|
||||
|
||||
return findMenuString(item->getGuiParent()) + "_" + StringConverter<int>::convertToString(childIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ChatWidget::onMenuStateChanged()
|
||||
{
|
||||
if (getMenuState() == HOVER)
|
||||
{
|
||||
setMenuState(SHOWN);
|
||||
}
|
||||
}
|
||||
|
||||
GuiResponse ChatWidget::process(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
if ( isVisible()
|
||||
&& event->isMouseEvent()
|
||||
&& (event->isLeftMouseUpEvent())
|
||||
&& getMyRect(Canvas(event->getWindowSize())).pointInRect(event->get2DPosition()))
|
||||
{
|
||||
if (Network::Players* players = ServiceProvider::find<Network::Players>(this))
|
||||
{
|
||||
ServiceProvider::create<Soundscape::SoundService>(this)->playSound(SoundWorld::ClickSound());
|
||||
//std::string chatString = "Chat" + findMenuString(this);
|
||||
std::string chatString = "/sc " + this->code;
|
||||
players->chat(chatString);
|
||||
return GuiResponse::sunkAndFinished();
|
||||
}
|
||||
}
|
||||
return Super::process(event);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "stdafx.h"
|
||||
#include "Gui/EquationDisplay.h"
|
||||
#include "Util/IMetric.h"
|
||||
|
||||
namespace RBX {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// EquationDisplay
|
||||
//
|
||||
|
||||
EquationDisplay::EquationDisplay(
|
||||
const std::string& title,
|
||||
const std::string& eqText)
|
||||
:
|
||||
TextDisplay(title, title),
|
||||
equation(eqText)
|
||||
{}
|
||||
|
||||
|
||||
EquationDisplay::EquationDisplay(
|
||||
const std::string& title,
|
||||
const std::string& label,
|
||||
const std::string& eqText)
|
||||
: TextDisplay(title, label)
|
||||
, equation(eqText)
|
||||
{}
|
||||
|
||||
|
||||
std::string EquationDisplay::getLabel() const
|
||||
{
|
||||
const Instance* root = Instance::getRootAncestor(this);
|
||||
const IMetric* metric = dynamic_cast<const IMetric*>(root);
|
||||
if (metric) {
|
||||
RBXASSERT(metric);
|
||||
std::string answer = metric->getMetric(equation);
|
||||
return label + " " + answer;
|
||||
}
|
||||
else {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
void EquationDisplay::render2d(Adorn* adorn)
|
||||
{
|
||||
if (isVisible())
|
||||
{
|
||||
label2d(
|
||||
adorn,
|
||||
getLabel(),
|
||||
fontColor,
|
||||
borderColor,
|
||||
align);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace
|
||||
+707
@@ -0,0 +1,707 @@
|
||||
#include "stdafx.h"
|
||||
#include "Gui/GUI.h"
|
||||
|
||||
namespace RBX {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// GuiItem
|
||||
//
|
||||
|
||||
const char* const sGuiItem = "GuiItem";
|
||||
|
||||
const Color4& GuiItem::disabledFill() {static Color4 c(.7f,.7f,.7f,.5f); return c;}
|
||||
const Color4& GuiItem::translucentBackdrop(){static Color4 c(.6f,.6f,.6f,.6f); return c;}
|
||||
const Color4& GuiItem::menuSelect() {static Color4 c(.7f,.7f,.7f,1.0f); return c;}
|
||||
|
||||
const char* const sGuiRoot = "GuiRoot";
|
||||
|
||||
GuiItem::GuiItem()
|
||||
{
|
||||
setName("Unnamed GuiItem");
|
||||
FASTLOG1(FLog::GuiTargetLifetime, "GuiItem created: %p", this);
|
||||
}
|
||||
|
||||
GuiItem::~GuiItem()
|
||||
{
|
||||
FASTLOG1(FLog::GuiTargetLifetime, "GuiItem destroyed: %p", this);
|
||||
}
|
||||
|
||||
|
||||
GuiItem* GuiItem::getGuiParent()
|
||||
{
|
||||
return Instance::fastDynamicCast<GuiItem>(this->getParent());
|
||||
}
|
||||
|
||||
const GuiItem* GuiItem::getGuiParent() const
|
||||
{
|
||||
return Instance::fastDynamicCast<GuiItem>(this->getParent());
|
||||
}
|
||||
|
||||
GuiItem* GuiItem::getGuiItem(int index)
|
||||
{
|
||||
return Instance::fastDynamicCast<GuiItem>(this->getChild(index));
|
||||
}
|
||||
|
||||
const GuiItem* GuiItem::getGuiItem(int index) const
|
||||
{
|
||||
return Instance::fastDynamicCast<GuiItem>(this->getChild(index));
|
||||
}
|
||||
|
||||
void GuiItem::onDescendantRemoving(const shared_ptr<Instance>& instance)
|
||||
{
|
||||
if (instance == focus)
|
||||
loseFocus();
|
||||
|
||||
Super::onDescendantRemoving(instance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Rect GuiItem::getMyRect(Canvas canvas) const
|
||||
{
|
||||
Vector2 pos = getPosition(canvas);
|
||||
return Rect(pos, pos + this->getSize(canvas));
|
||||
}
|
||||
|
||||
|
||||
GuiResponse GuiItem::processNonFocus(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
for (size_t i = 0; i < numChildren(); ++i) {
|
||||
if (GuiItem* item = getGuiItem(i)) {
|
||||
if (item != focus.get() && item != NULL) {
|
||||
GuiResponse itemResponse = item->process(event);
|
||||
if (itemResponse.wasSunk())
|
||||
{
|
||||
this->loseFocus();
|
||||
focus = shared_from(item);
|
||||
focus->loseFocus(); // make sure no focus has no focus of its own
|
||||
return itemResponse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Throw away idle events
|
||||
GuiResponse GuiItem::process(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
// flush idle events
|
||||
if (event->isMouseEvent() && event->getUserInputType() == InputObject::TYPE_MOUSEIDLE)
|
||||
{
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
|
||||
// if focus.....
|
||||
if (focus)
|
||||
{
|
||||
if (focus->canLoseFocus())
|
||||
{
|
||||
GuiResponse nonFocus = processNonFocus(event);
|
||||
if (nonFocus.wasSunk())
|
||||
return nonFocus;
|
||||
}
|
||||
|
||||
GuiResponse focusResponse = focus->process(event);
|
||||
if (focusResponse.wasSunk())
|
||||
{
|
||||
return focusResponse;
|
||||
}
|
||||
else
|
||||
{
|
||||
loseFocus();
|
||||
}
|
||||
}
|
||||
|
||||
RBXASSERT(focus == NULL);
|
||||
return processNonFocus(event);
|
||||
}
|
||||
|
||||
|
||||
void GuiItem::label2d(
|
||||
Adorn* adorn,
|
||||
const std::string& label,
|
||||
const Color4& fill,
|
||||
const Color4& border,
|
||||
Text::XAlign align) const
|
||||
{
|
||||
if (label.length()) {
|
||||
Rect myRect = this->getMyRect(adorn->getCanvas());
|
||||
Vector2 s = myRect.size();
|
||||
Vector2 pos = myRect.center();
|
||||
switch (align)
|
||||
{
|
||||
default:
|
||||
case Text::XALIGN_LEFT:
|
||||
pos.x = myRect.low.x + (0.1f * s.x);
|
||||
break;
|
||||
|
||||
case Text::XALIGN_RIGHT:
|
||||
pos.x = myRect.high.x - (0.1f * s.x);
|
||||
break;
|
||||
|
||||
case Text::XALIGN_CENTER: // pos.x == center
|
||||
break;
|
||||
}
|
||||
|
||||
adorn->drawFont2D( label,
|
||||
pos,
|
||||
(float)getFontSize(), // size
|
||||
false,
|
||||
fill,
|
||||
border,
|
||||
Text::FONT_LEGACY,
|
||||
align,
|
||||
Text::YALIGN_CENTER );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// GuiRoot
|
||||
//
|
||||
|
||||
GuiRoot::GuiRoot()
|
||||
{
|
||||
setName("GuiRoot");
|
||||
}
|
||||
|
||||
Vector2 Canvas::toPixelSize(const Vector2& percent) const // std screen is 100% wide and 75% tall
|
||||
{
|
||||
Vector2 answer;
|
||||
if (size.y > (size.x * 0.75f)) // bound by x - y too big
|
||||
{
|
||||
|
||||
answer = (0.01f * percent) * Vector2(size.x, size.x * 0.75f);
|
||||
}
|
||||
else
|
||||
{
|
||||
answer = (0.01f * percent) * Vector2(size.y * 1.33f, size.y);
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
int Canvas::normalizedFontSize(int fontSize) const
|
||||
{
|
||||
static Vector2 percentSize(100.0f, 75.0f);
|
||||
|
||||
return Math::iFloor(fontSize * toPixelSize(percentSize).x / 1000.0f);
|
||||
}
|
||||
|
||||
void GuiRoot::render2d(Adorn* adorn)
|
||||
{
|
||||
for (size_t i = 0; i < numChildren(); i++) {
|
||||
if (GuiItem* item = getGuiItem(i))
|
||||
item->render2d(adorn);
|
||||
}
|
||||
}
|
||||
|
||||
void GuiRoot::render2dItem(Adorn* adorn, GuiItem* guiItem)
|
||||
{
|
||||
guiItem->render2d(adorn);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// ScreenPanel
|
||||
//
|
||||
|
||||
void RelativePanel::init(const Layout& layout)
|
||||
{
|
||||
setName("RelativePanel");
|
||||
layoutStyle = layout.layoutStyle;
|
||||
backdropColor = layout.backdropColor;
|
||||
xLocation = layout.xLocation;
|
||||
yLocation = layout.yLocation;
|
||||
offset = layout.offset;
|
||||
}
|
||||
|
||||
|
||||
Vector2 RelativePanel::getPosition(Canvas canvas) const
|
||||
{
|
||||
Rect canvasRect(Vector2::zero(), canvas.size);
|
||||
Rect me(Vector2::zero(), getSize(canvas));
|
||||
|
||||
canvasRect = canvasRect.inset(offset);
|
||||
return canvasRect.positionChild(me, xLocation, yLocation).low;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TopMenuBar
|
||||
//
|
||||
void TopMenuBar::init()
|
||||
{
|
||||
layoutStyle = Layout::HORIZONTAL;
|
||||
backdropColor = Color4::clear();
|
||||
visible = true;
|
||||
}
|
||||
|
||||
TopMenuBar::TopMenuBar(
|
||||
const std::string& _title,
|
||||
Layout::Style _layoutStyle,
|
||||
bool translucentBackdrop)
|
||||
{
|
||||
init();
|
||||
setName(_title);
|
||||
layoutStyle = _layoutStyle;
|
||||
if (translucentBackdrop) {
|
||||
backdropColor = GuiItem::translucentBackdrop();
|
||||
}
|
||||
}
|
||||
|
||||
TopMenuBar::TopMenuBar(
|
||||
const std::string& _title,
|
||||
Layout::Style _layoutStyle,
|
||||
Color4 _backdropColor)
|
||||
{
|
||||
init();
|
||||
setName(_title);
|
||||
layoutStyle = _layoutStyle;
|
||||
backdropColor = _backdropColor;
|
||||
}
|
||||
|
||||
GuiResponse TopMenuBar::process(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
if (isVisible())
|
||||
{
|
||||
GuiResponse childResponse = GuiItem::process(event);
|
||||
if (childResponse.wasSunk()) {
|
||||
return childResponse;
|
||||
}
|
||||
|
||||
// if not a transparent background, grab all mouse events
|
||||
else if ( (backdropColor.a > 0.0)
|
||||
&& event->isMouseEvent()
|
||||
&& getMyRect(Canvas(event->getWindowSize())).pointInRect(event->get2DPosition()))
|
||||
{
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
}
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
|
||||
|
||||
Vector2 TopMenuBar::getSize(Canvas canvas) const
|
||||
{
|
||||
Vector2 answer; // set to zero
|
||||
|
||||
for (size_t i = 0; i < numChildren(); ++i) {
|
||||
if (const GuiItem* item = getGuiItem(i))
|
||||
{
|
||||
Vector2 childSize = item->getSize(canvas);
|
||||
switch (layoutStyle)
|
||||
{
|
||||
case Layout::HORIZONTAL:
|
||||
answer.x += childSize.x;
|
||||
answer.y = std::max(answer.y, childSize.y);
|
||||
break;
|
||||
case Layout::VERTICAL:
|
||||
answer.x = std::max(answer.x, childSize.x);
|
||||
answer.y += childSize.y;
|
||||
break;
|
||||
default:
|
||||
RBXASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
|
||||
Vector2 TopMenuBar::getChildPosition(const GuiItem* child, Canvas canvas) const
|
||||
{
|
||||
Vector2 childPosition = this->getPosition(canvas);
|
||||
Vector2 mySize = this->getSize(canvas);
|
||||
|
||||
for (size_t i = 0; i < numChildren(); ++i) {
|
||||
const GuiItem* myChild = getGuiItem(i);
|
||||
if (myChild)
|
||||
{
|
||||
Vector2 myChildSize = myChild->getSize(canvas);
|
||||
|
||||
if (myChild == child) {
|
||||
int perp = (layoutStyle + 1) % 2;
|
||||
// center it
|
||||
childPosition[perp] += 0.5f * (mySize[perp] - myChildSize[perp]);
|
||||
return childPosition;
|
||||
}
|
||||
switch (layoutStyle)
|
||||
{
|
||||
case Layout::HORIZONTAL:
|
||||
case Layout::VERTICAL:
|
||||
childPosition[layoutStyle] += myChild->getSize(canvas)[layoutStyle]; // x, y
|
||||
break;
|
||||
|
||||
default:
|
||||
RBXASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RBXASSERT(0);
|
||||
return childPosition;
|
||||
}
|
||||
|
||||
void TopMenuBar::render2d(Adorn* adorn)
|
||||
{
|
||||
if (isVisible()) {
|
||||
if (backdropColor != Color4::clear()) {
|
||||
adorn->rect2d(getMyRect2D(adorn->getCanvas()), backdropColor);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < numChildren(); i++) {
|
||||
if (GuiItem* item = getGuiItem(i))
|
||||
item->render2d(adorn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// UnifiedWidget
|
||||
//
|
||||
void UnifiedWidget::init()
|
||||
{
|
||||
setGuiSize(Vector2(140, 24));
|
||||
menuState = NOTHING;
|
||||
}
|
||||
|
||||
UnifiedWidget::UnifiedWidget(const std::string& title)
|
||||
{
|
||||
init();
|
||||
setName(title);
|
||||
}
|
||||
|
||||
void UnifiedWidget::render2dMe(Adorn* adorn)
|
||||
{
|
||||
adorn->rect2d(getMyRect2D(adorn->getCanvas()), (menuState != NOTHING) ? menuSelect() : Color3::white());
|
||||
|
||||
adorn->outlineRect2d(getMyRect2D(adorn->getCanvas()), 1, Color3::black());
|
||||
|
||||
label2d(
|
||||
adorn,
|
||||
getName(),
|
||||
Color3::black(),
|
||||
Color4::clear(),
|
||||
Text::XALIGN_CENTER
|
||||
);
|
||||
}
|
||||
|
||||
void UnifiedWidget::render2dChildren(Adorn* adorn)
|
||||
{
|
||||
if (showChildren())
|
||||
{
|
||||
for (size_t i = 0; i < numChildren(); i++) {
|
||||
if (GuiItem* item = getGuiItem(i)) {
|
||||
item->render2d(adorn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UnifiedWidget::render2d(Adorn* adorn)
|
||||
{
|
||||
if (isVisible())
|
||||
{
|
||||
render2dMe(adorn);
|
||||
render2dChildren(adorn);
|
||||
}
|
||||
}
|
||||
|
||||
// each item is 24 units high
|
||||
|
||||
Vector2 UnifiedWidget::firstChildPosition(Canvas canvas) const
|
||||
{
|
||||
Vector2 childPosition = getPosition(canvas);
|
||||
Vector2 mySize = getSize(canvas);
|
||||
|
||||
childPosition.x += (mySize.x + 4);
|
||||
|
||||
int canvasHeight = (int)canvas.size.y;
|
||||
int minY = 20;
|
||||
int maxY = canvasHeight - 100;
|
||||
int slotHeight = (int)mySize.y + 4;
|
||||
int slots = (maxY - minY) / slotHeight;
|
||||
int desiredCenter = (int)childPosition.y;
|
||||
int desiredCenterSlot = (desiredCenter - minY) / slotHeight;
|
||||
|
||||
int desiredSlots = this->numChildren();
|
||||
int topCenterSlot = desiredSlots / 2;
|
||||
int bottomCenterSlot = (slots - topCenterSlot);
|
||||
|
||||
desiredCenterSlot = std::max(topCenterSlot, desiredCenterSlot);
|
||||
desiredCenterSlot = std::min(bottomCenterSlot, desiredCenterSlot);
|
||||
|
||||
childPosition.y = (float)(((desiredCenterSlot - topCenterSlot)* slotHeight) + minY);
|
||||
return childPosition;
|
||||
}
|
||||
|
||||
Vector2 UnifiedWidget::childOffset() const
|
||||
{
|
||||
// return Vector2(0, -28);
|
||||
return Vector2(0, 28);
|
||||
}
|
||||
|
||||
Vector2 UnifiedWidget::getChildPosition(const GuiItem* child, Canvas canvas) const
|
||||
{
|
||||
Vector2 position = firstChildPosition(canvas);
|
||||
|
||||
for (size_t i = 0; i < numChildren(); ++i) {
|
||||
if (const GuiItem* item = getGuiItem(i)) {
|
||||
if (item == child) {
|
||||
return position;
|
||||
}
|
||||
position += childOffset();
|
||||
}
|
||||
}
|
||||
|
||||
RBXASSERT(0);
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
void UnifiedWidget::onLoseFocus()
|
||||
{
|
||||
menuState = NOTHING;
|
||||
Super::onLoseFocus();
|
||||
}
|
||||
|
||||
|
||||
void UnifiedWidget::setMenuState(MenuState value)
|
||||
{
|
||||
if (menuState != value)
|
||||
{
|
||||
menuState = value;
|
||||
onMenuStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
GuiResponse UnifiedWidget::processKey(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
return GuiResponse::notSunk();;
|
||||
}
|
||||
|
||||
|
||||
GuiResponse UnifiedWidget::processShown_InTitle(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
loseFocus();
|
||||
|
||||
if (menuState == SHOWN)
|
||||
{
|
||||
if (event->isLeftMouseUpEvent())
|
||||
{
|
||||
setMenuState(HOVER);
|
||||
}
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
|
||||
RBXASSERT(menuState == SHOWN_APPEARING);
|
||||
|
||||
if (event->isLeftMouseUpEvent())
|
||||
{
|
||||
setMenuState(SHOWN);
|
||||
}
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
|
||||
|
||||
|
||||
GuiResponse UnifiedWidget::processShown(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
if (getMyRect(Canvas(event->getWindowSize())).pointInRect(event->get2DPosition()))
|
||||
{
|
||||
return processShown_InTitle(event);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return processShown_OutOfTitle(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
GuiResponse UnifiedWidget::processHover(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
RBXASSERT(menuState == HOVER);
|
||||
loseFocus();
|
||||
|
||||
if (getMyRect(Canvas(event->getWindowSize())).pointInRect(event->get2DPosition()))
|
||||
{
|
||||
if (event->isLeftMouseDownEvent())
|
||||
{
|
||||
setMenuState(SHOWN_APPEARING);
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
else if(event->isLeftMouseUpEvent())
|
||||
{
|
||||
setMenuState(SHOWN);
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
else
|
||||
{
|
||||
setMenuState(NOTHING);
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GuiResponse UnifiedWidget::processNothing(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
RBXASSERT(menuState == NOTHING);
|
||||
loseFocus();
|
||||
|
||||
if (getMyRect(Canvas(event->getWindowSize())).pointInRect(event->get2DPosition()))
|
||||
{
|
||||
switch (event->getUserInputType())
|
||||
{
|
||||
case InputObject::TYPE_MOUSEMOVEMENT: // coming in with a mouse down or up?
|
||||
setMenuState(HOVER);
|
||||
return GuiResponse::sunk();
|
||||
case InputObject::TYPE_MOUSEBUTTON1:
|
||||
RBXASSERT(event->isLeftMouseDownEvent() || event->isLeftMouseUpEvent());
|
||||
setMenuState(SHOWN_APPEARING);
|
||||
return GuiResponse::sunk();
|
||||
default:
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setMenuState(NOTHING);
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GuiResponse UnifiedWidget::processShown_OutOfTitle(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
UnifiedWidget* oldMenuChild = Instance::fastDynamicCast<UnifiedWidget>(getFocus());
|
||||
|
||||
switch (event->getUserInputType())
|
||||
{
|
||||
// Mouse move - child can handle - always used
|
||||
case InputObject::TYPE_MOUSEMOVEMENT:
|
||||
{
|
||||
MenuState oldState = oldMenuChild ? oldMenuChild->getMenuState() : NOTHING;
|
||||
|
||||
GuiItem::process(event);
|
||||
|
||||
if (oldMenuChild) {
|
||||
if (UnifiedWidget* newMenuChild = Instance::fastDynamicCast<UnifiedWidget>(getFocus())) {
|
||||
if (newMenuChild != oldMenuChild) {
|
||||
newMenuChild->setMenuState(oldState);
|
||||
}
|
||||
}
|
||||
}
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
|
||||
// UP or Down - if not used, lose focus and hide our menu
|
||||
case InputObject::TYPE_MOUSEBUTTON1:
|
||||
{
|
||||
RBXASSERT(event->isLeftMouseDownEvent() || event->isLeftMouseUpEvent());
|
||||
GuiResponse answer = GuiItem::process(event);
|
||||
|
||||
if (!answer.wasSunk()) {
|
||||
RBXASSERT(getFocus() == NULL);
|
||||
setMenuState(NOTHING);
|
||||
}
|
||||
|
||||
if (answer.wasSunkAndFinished()) {
|
||||
loseFocus();
|
||||
setMenuState(NOTHING);
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
RBXASSERT(0);
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
|
||||
|
||||
|
||||
GuiResponse UnifiedWidget::process(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
if (isVisible())
|
||||
{
|
||||
if (event->isMouseEvent())
|
||||
{
|
||||
switch (menuState)
|
||||
{
|
||||
case UnifiedWidget::NOTHING: return processNothing(event);
|
||||
case UnifiedWidget::HOVER: return processHover(event);
|
||||
case UnifiedWidget::SHOWN_APPEARING:
|
||||
case UnifiedWidget::SHOWN: return processShown(event); // with or without focus....
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return processKey(event);
|
||||
}
|
||||
}
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TextDisplay
|
||||
//
|
||||
void TextDisplay::init()
|
||||
{
|
||||
setGuiSize(Vector2(180, 30));
|
||||
fontSize = 18;
|
||||
fontColor = Color3::purple();
|
||||
borderColor = Color4::clear();
|
||||
align = Text::XALIGN_LEFT;
|
||||
visible = true;
|
||||
}
|
||||
|
||||
TextDisplay::TextDisplay(const std::string& title, const std::string& _label)
|
||||
{
|
||||
init();
|
||||
setName(title);
|
||||
label = _label;
|
||||
}
|
||||
|
||||
|
||||
void TextDisplay::render2d(Adorn* adorn)
|
||||
{
|
||||
if (isVisible())
|
||||
{
|
||||
label2d(
|
||||
adorn,
|
||||
getLabel(),
|
||||
fontColor,
|
||||
borderColor,
|
||||
align);
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 TextDisplay::getSize(Canvas canvas) const
|
||||
{
|
||||
if (isVisible())
|
||||
{
|
||||
return Super::getSize(canvas);
|
||||
}
|
||||
return Vector2::zero();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,274 @@
|
||||
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "Gui/GuiDraw.h"
|
||||
#include "V8Kernel/Constants.h"
|
||||
#include "V8DataModel/PVInstance.h"
|
||||
#include "AppDraw/Draw.h"
|
||||
#include "Util/Utilities.h"
|
||||
#include "Util/Math.h"
|
||||
#include "AppDraw/DrawPrimitives.h"
|
||||
|
||||
#include "GfxBase/Adorn.h"
|
||||
#include "v8datamodel/contentprovider.h"
|
||||
|
||||
namespace RBX {
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// GuiDrawImage
|
||||
//
|
||||
|
||||
void GuiDrawImage::tryCreateTextureProxy(Adorn *adorn, const std::string& contentString, const std::string& context, RBX::TextureProxyBaseRef& textureRef, bool& isWaiting)
|
||||
{
|
||||
ContentId content(contentString);
|
||||
|
||||
if ( (content.isAsset() && !RBX::ContentProvider::findAsset(content).empty()) || !content.isAsset())
|
||||
{
|
||||
textureRef = adorn->createTextureProxy(content, isWaiting, false, context);
|
||||
}
|
||||
}
|
||||
|
||||
bool GuiDrawImage::setImage(Adorn *adorn, const TextureId& textureId, unsigned imageState, Vector2* outSize, Instance* contextInstance, const char* context)
|
||||
{
|
||||
if (textureId != currentTexture) {
|
||||
std::string contextString = "";
|
||||
if (contextInstance != NULL)
|
||||
{
|
||||
contextString = contextInstance->getFullName() + context;
|
||||
}
|
||||
currentTexture = TextureId::nullTexture();
|
||||
|
||||
// lazy connect to signal
|
||||
if(!unbindResourceSignalHint.connected())
|
||||
{
|
||||
unbindResourceSignalHint = adorn->getUnbindResourcesSignal().connect(boost::bind(&GuiDrawImage::OnUnbindResourceSignalHint, this));
|
||||
}
|
||||
|
||||
if(textureId != loadingTexture){
|
||||
normal.reset();
|
||||
hover.reset();
|
||||
down.reset();
|
||||
disable.reset();
|
||||
selected.reset();
|
||||
selectedHover.reset();
|
||||
selectedDown.reset();
|
||||
|
||||
size = Vector2(0, 0);
|
||||
loadingTexture = textureId;
|
||||
}
|
||||
|
||||
if (textureId.isNull()){
|
||||
currentTexture = textureId;
|
||||
}
|
||||
else{
|
||||
bool waitingNormal = false;
|
||||
if(!normal)
|
||||
{
|
||||
normal = adorn->createTextureProxy(textureId, waitingNormal, false, contextString);
|
||||
}
|
||||
if(textureId.isHttp() || imageState == NORMAL || textureId.isAssetId() || textureId.isNamedAsset()) {
|
||||
if (normal) {
|
||||
currentTexture = textureId;
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::string id = textureId.toString(); // hack - thrash createTextureProxy for old gui stuff if not all four are present
|
||||
std::string base = id.substr(0, id.size() - 4);
|
||||
bool waitingHover = false;
|
||||
bool waitingDown = false;
|
||||
bool waitingDisable = false;
|
||||
bool waitingSelected = false;
|
||||
bool waitingSelectedHover = false;
|
||||
bool waitingSelectedDown = false;
|
||||
|
||||
//TODO: eliminate this polling
|
||||
if(!hover && (imageState & HOVER))
|
||||
{
|
||||
tryCreateTextureProxy(adorn, base + "_ovr.png", contextString, hover, waitingHover);
|
||||
}
|
||||
if(!down && (imageState & DOWN))
|
||||
{
|
||||
tryCreateTextureProxy(adorn, base + "_dn.png", contextString, down, waitingDown);
|
||||
}
|
||||
if(!disable && (imageState & DISABLE))
|
||||
{
|
||||
tryCreateTextureProxy(adorn, base + "_ds.png", contextString, disable, waitingDisable);
|
||||
}
|
||||
if(!selected && (imageState & SELECTED))
|
||||
{
|
||||
tryCreateTextureProxy(adorn, base + "_sel.png", contextString, selected, waitingSelected);
|
||||
}
|
||||
if(!selectedHover && (imageState & SELECTED_HOVER))
|
||||
{
|
||||
tryCreateTextureProxy(adorn, base + "_sovr.png", contextString, selectedHover, waitingSelectedHover);
|
||||
}
|
||||
if(!selectedDown && (imageState & SELECTED_DOWN))
|
||||
{
|
||||
tryCreateTextureProxy(adorn, base + "_sdn.png", contextString, selectedDown, waitingSelectedDown);
|
||||
}
|
||||
|
||||
if (normal &&
|
||||
(hover || !waitingHover) &&
|
||||
(down || !waitingDown) &&
|
||||
(disable || !waitingDisable) &&
|
||||
(selected || !waitingSelected) &&
|
||||
(selectedHover || !waitingSelectedHover) &&
|
||||
(selectedDown || !waitingSelectedDown)) {
|
||||
currentTexture = textureId;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (outSize && normal)
|
||||
{
|
||||
*outSize = normal->getOriginalSize();
|
||||
}
|
||||
|
||||
return !!normal;
|
||||
}
|
||||
|
||||
bool GuiDrawImage::setImageFromName(Adorn *adorn, const std::string& textureName, unsigned imageState, Instance* contextInstance, const char* context)
|
||||
{
|
||||
std::string asset = "Textures/" + textureName + ".png";
|
||||
ContentId contentId = ContentId::fromAssets(asset.c_str());
|
||||
return setImage(adorn, contentId, imageState, NULL, contextInstance, context);
|
||||
}
|
||||
|
||||
|
||||
void GuiDrawImage::setImageSize(const Vector2& _size)
|
||||
{
|
||||
size = _size;
|
||||
}
|
||||
|
||||
Vector2 GuiDrawImage::getImageSize() const
|
||||
{
|
||||
if (size.isZero() && normal)
|
||||
{
|
||||
RBX::Vector2 sz = normal->getOriginalSize();
|
||||
size.x = sz.x;
|
||||
size.y = sz.y;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void GuiDrawImage::draw(Adorn* adorn, const RBX::TextureProxyBaseRef& texture, const Rect& rect, const Vector2& texul, const Vector2& texbr, const Color4& color,
|
||||
const Rect& clipRect, const Color4& behind, const Color4& inFront)
|
||||
{
|
||||
Rect2D rect2D = rect.toRect2D();
|
||||
Rect2D clipRect2D = clipRect.toRect2D();
|
||||
|
||||
if( clipRect2D.intersects(rect2D) )
|
||||
{
|
||||
if (texture) {
|
||||
adorn->setTexture(0, texture);
|
||||
adorn->rect2d(rect2D, texul, texbr, color, clipRect2D);
|
||||
adorn->setTexture(0, TextureProxyBaseRef());
|
||||
}
|
||||
else if (normal) { // backup - use the normal texture
|
||||
RBX::Rect2D intersectRect = clipRect2D.intersect(rect2D);
|
||||
adorn->rect2d(intersectRect, texul, texbr, behind);
|
||||
draw(adorn, normal, rect, texul, texbr, color, clipRect, Color4::clear(), Color4::clear());
|
||||
adorn->rect2d(intersectRect, texul, texbr, inFront);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuiDrawImage::draw(Adorn* adorn, const RBX::TextureProxyBaseRef& texture, const Rect& rect, const Vector2& texul, const Vector2& texbr, const Color4& color,
|
||||
const Rotation2D& rotation, const Color4& behind, const Color4& inFront)
|
||||
{
|
||||
Rect2D rect2D = rect.toRect2D();
|
||||
|
||||
if (texture) {
|
||||
adorn->setTexture(0, texture);
|
||||
adorn->rect2d(rect2D, texul, texbr, color, rotation);
|
||||
adorn->setTexture(0, TextureProxyBaseRef());
|
||||
}
|
||||
else if (normal) { // backup - use the normal texture
|
||||
adorn->rect2d(rect2D, texul, texbr, behind);
|
||||
draw(adorn, normal, rect, texul, texbr, color, rotation, Color4::clear(), Color4::clear());
|
||||
adorn->rect2d(rect2D, texul, texbr, inFront);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Modifier>
|
||||
void GuiDrawImage::render2dImpl(Adorn* adorn, bool enabled, const Rect& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, const Modifier& modifier,
|
||||
Gui::WidgetState state, bool isSelected)
|
||||
{
|
||||
if (!enabled) {
|
||||
draw(adorn, disable, rect, texul, texbr, color, modifier, Color4::clear(), Color4(1,1,1,0.5)); // disabled: draw gray over it
|
||||
}
|
||||
else if (!isSelected) {
|
||||
if (state == Gui::NOTHING) {
|
||||
draw(adorn, normal, rect, texul, texbr, color, modifier, Color4::clear(), Color4::clear()); // no special state, no special outline
|
||||
}
|
||||
else if (state == Gui::HOVER || state == Gui::DOWN_AWAY) {
|
||||
draw(adorn, hover, rect, texul, texbr, color, modifier, Color4::clear(), Color4::clear());
|
||||
}
|
||||
else if (state == Gui::DOWN_OVER) {
|
||||
draw(adorn, down, rect, texul, texbr, color, modifier, Color4::clear(), Color4::clear()); // Depressed: draw blue
|
||||
}
|
||||
} else {
|
||||
if (state == Gui::NOTHING) {
|
||||
draw(adorn, selected, rect, texul, texbr, color, modifier, Color4::clear(), Color4::clear()); // no special state, no special outline
|
||||
}
|
||||
else if (state == Gui::HOVER) {
|
||||
draw(adorn, hover, rect, texul, texbr, color, modifier, Color4::clear(), Color4::clear());
|
||||
}
|
||||
else if (state == Gui::DOWN_AWAY) { // hover/depressed+away: draw yellow
|
||||
draw(adorn, selectedHover, rect, texul, texbr, color, modifier, Color4::clear(), Color4::clear());
|
||||
}
|
||||
else if (state == Gui::DOWN_OVER) {
|
||||
draw(adorn, selectedDown, rect, texul, texbr, color, modifier, Color4::clear(), Color4::clear()); // Depressed: draw blue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GuiDrawImage::render2d(Adorn* adorn, bool enabled, const Rect& rect,
|
||||
Gui::WidgetState state, bool isSelected)
|
||||
{
|
||||
render2dImpl(adorn, enabled, rect, Vector2(0, 0), Vector2(1, 1), Color3::white(), Rotation2D(), state, isSelected);
|
||||
}
|
||||
|
||||
void GuiDrawImage::render2d(Adorn* adorn, bool enabled, const Rect& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, const Rotation2D& rotation,
|
||||
Gui::WidgetState state, bool isSelected)
|
||||
{
|
||||
render2dImpl(adorn, enabled, rect, texul, texbr, color, rotation, state, isSelected);
|
||||
}
|
||||
|
||||
void GuiDrawImage::render2d(Adorn* adorn, bool enabled, const Rect& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, const Rect& clipRect,
|
||||
Gui::WidgetState state, bool isSelected)
|
||||
{
|
||||
render2dImpl(adorn, enabled, rect, texul, texbr, color, clipRect, state, isSelected);
|
||||
}
|
||||
|
||||
void GuiDrawImage::OnUnbindResourceSignalHint()
|
||||
{
|
||||
currentTexture = TextureId::nullTexture();
|
||||
normal.reset();
|
||||
hover.reset();
|
||||
down.reset();
|
||||
disable.reset();
|
||||
selected.reset();
|
||||
selectedHover.reset();
|
||||
selectedDown.reset();
|
||||
|
||||
unbindResourceSignalHint.disconnect();
|
||||
}
|
||||
|
||||
void GuiDrawImage::computeUV(Vector2& uvtl, Vector2& uvbr, const Vector2& imageRectOffset, const Vector2& imageRectSize, const Vector2& imageSize)
|
||||
{
|
||||
if ((imageRectOffset.isZero() && imageRectSize.isZero()) || imageSize.isZero())
|
||||
{
|
||||
uvtl = Vector2(0, 0);
|
||||
uvbr = Vector2(1, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
uvtl = G3D::clamp(imageRectOffset / imageSize, Vector2(0, 0), Vector2(1, 1));
|
||||
uvbr = G3D::clamp((imageRectOffset + imageRectSize) / imageSize, Vector2(0, 0), Vector2(1, 1));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,123 @@
|
||||
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "Network/Player.h"
|
||||
#include "Util/Hash.h"
|
||||
#include "Util/Color.h"
|
||||
#include "Gui/ProfanityFilter.h"
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "v8datamodel/contentprovider.h"
|
||||
#include "Util/SafeToLower.h"
|
||||
#include "StringConv.h"
|
||||
|
||||
namespace RBX {
|
||||
|
||||
|
||||
void WordList::decrypt(std::string& str)
|
||||
{
|
||||
for(unsigned i = 0; i<str.size(); ++i){
|
||||
str[i] = 0x55 ^ str[i];
|
||||
}
|
||||
}
|
||||
|
||||
WordList::WordList()
|
||||
{
|
||||
/* Load blacklist
|
||||
|
||||
|
||||
*/
|
||||
|
||||
std::string filename = ContentProvider::getAssetFile("Fonts\\diogenes.fnt");
|
||||
|
||||
std::ifstream infile(utf8_decode(filename).c_str());
|
||||
std::string line;
|
||||
while( std::getline(infile, line, '\n'))
|
||||
{
|
||||
safeToLower(line);
|
||||
boost::trim(line);
|
||||
if (line.length() > 2){
|
||||
decrypt(line);
|
||||
blacklist.insert(line);
|
||||
}
|
||||
}
|
||||
|
||||
infile.close();
|
||||
}
|
||||
WordList::~WordList()
|
||||
{
|
||||
|
||||
}
|
||||
bool WordList::ContainsProfanity(std::string str)
|
||||
{
|
||||
/*
|
||||
1. Convert query to lower case
|
||||
2. Check for in black list
|
||||
*/
|
||||
|
||||
safeToLower(str);
|
||||
|
||||
return blacklist.find(str) != blacklist.end();
|
||||
}
|
||||
|
||||
|
||||
ProfanityFilter::ProfanityFilter()
|
||||
: wordlist(0)
|
||||
{
|
||||
}
|
||||
|
||||
ProfanityFilter::~ProfanityFilter()
|
||||
{
|
||||
if(wordlist)
|
||||
{
|
||||
delete wordlist;
|
||||
wordlist = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ProfanityFilter::ContainsProfanity(const std::string& str)
|
||||
{
|
||||
shared_ptr<ProfanityFilter> instance = getInstance();
|
||||
RBXASSERT(instance->getInitCount() <= 1); // if you hit this, you are using the ScopedSingleton improperly. Save a shared_ptr on a long-lived instance.
|
||||
return instance->ContainsProfanityWorker(str);
|
||||
}
|
||||
|
||||
|
||||
bool ProfanityFilter::ContainsProfanityWorker(std::string str)
|
||||
{
|
||||
/*
|
||||
1. Convert string to lower case
|
||||
2. Break string into testable "words" (a word is a potential entry in the blacklist)
|
||||
3. Test each word
|
||||
4. Return false if any fail, true otherwise
|
||||
|
||||
TODO - doesn't currently handle bigrams, so bigrams in word list effectively ignored
|
||||
*/
|
||||
|
||||
// You might be thinking "wtf". We don't want to lock if we have a wordlist. We need a check after we get the lock that the wordlist is still NULL.
|
||||
if (wordlist == NULL)
|
||||
{
|
||||
boost::mutex sync;
|
||||
boost::mutex::scoped_lock lock(sync);
|
||||
if (wordlist == NULL)
|
||||
wordlist = new WordList();
|
||||
}
|
||||
|
||||
|
||||
safeToLower(str);
|
||||
|
||||
std::vector<std::string> words;
|
||||
|
||||
boost::split(words, str, boost::is_any_of(" !.?,:;><[]{}|\\/@#$%^&*()-+=\"")); // TODO: add more delimiters
|
||||
|
||||
for(unsigned int i = 0; i < words.size(); i++)
|
||||
{
|
||||
if (wordlist->ContainsProfanity(words[i]))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
/* Copyright 2003-2008 ROBLOX Corporation, All Rights Reserved */
|
||||
|
||||
#include "Gui/ScoreHud.h"
|
||||
@@ -0,0 +1,177 @@
|
||||
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "Gui/Widget.h"
|
||||
#include "AppDraw/Draw.h"
|
||||
#include "AppDraw/DrawPrimitives.h"
|
||||
|
||||
namespace RBX {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Widget
|
||||
//
|
||||
|
||||
|
||||
Widget::Widget() : widgetState(Gui::NOTHING)
|
||||
{
|
||||
setGuiSize(Vector2(100,24));
|
||||
}
|
||||
|
||||
|
||||
GuiResponse Widget::processMouse(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
bool mouseOver = getMyRect(Canvas(event->getWindowSize())).pointInRect(event->get2DPosition());
|
||||
|
||||
switch (widgetState)
|
||||
{
|
||||
case Gui::NOTHING:
|
||||
case Gui::HOVER:
|
||||
if (mouseOver)
|
||||
{
|
||||
if (!isEnabled())
|
||||
{
|
||||
widgetState = Gui::NOTHING;
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (event->getUserInputType())
|
||||
{
|
||||
case InputObject::TYPE_MOUSEMOVEMENT:
|
||||
{
|
||||
widgetState = Gui::HOVER;
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
case InputObject::TYPE_MOUSEBUTTON1:
|
||||
{
|
||||
if(event->isLeftMouseDownEvent())
|
||||
{
|
||||
widgetState = Gui::DOWN_OVER;
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
}
|
||||
default:
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
widgetState = Gui::NOTHING;
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
|
||||
case Gui::DOWN_OVER:
|
||||
switch (event->getUserInputType())
|
||||
{
|
||||
case InputObject::TYPE_MOUSEBUTTON1:
|
||||
{
|
||||
if(event->isLeftMouseDownEvent())
|
||||
return GuiResponse::sunk();
|
||||
else if (event->isLeftMouseUpEvent())
|
||||
{
|
||||
widgetState = Gui::NOTHING;
|
||||
if (mouseOver)
|
||||
{
|
||||
onClick(event);
|
||||
return GuiResponse::sunkAndFinished();
|
||||
}
|
||||
else
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case InputObject::TYPE_MOUSEMOVEMENT:
|
||||
{
|
||||
if (!mouseOver) // mouse was over, no longer over
|
||||
widgetState = Gui::DOWN_AWAY;
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case Gui::DOWN_AWAY:
|
||||
switch (event->getUserInputType())
|
||||
{
|
||||
case InputObject::TYPE_MOUSEMOVEMENT:
|
||||
{
|
||||
widgetState = mouseOver ? Gui::DOWN_OVER : Gui::DOWN_AWAY;
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
case InputObject::TYPE_MOUSEBUTTON1:
|
||||
{
|
||||
// left button down
|
||||
if(event->isLeftMouseDownEvent())
|
||||
return GuiResponse::sunk();
|
||||
else if(event->isLeftMouseUpEvent())
|
||||
{
|
||||
widgetState = Gui::NOTHING;
|
||||
if (mouseOver)
|
||||
{
|
||||
onClick(event);
|
||||
return GuiResponse::sunk();
|
||||
}
|
||||
else
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
RBXASSERT(0);
|
||||
break;
|
||||
}
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
|
||||
GuiResponse Widget::processKey(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
|
||||
GuiResponse Widget::process(const shared_ptr<InputObject>& event)
|
||||
{
|
||||
if (!isEnabled()) {
|
||||
return GuiResponse::notSunk();
|
||||
}
|
||||
|
||||
if (event->isMouseEvent()) {
|
||||
return processMouse(event);
|
||||
}
|
||||
else {
|
||||
return processKey(event);
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::render2d(Adorn* adorn)
|
||||
{
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (widgetState == Gui::HOVER || widgetState == Gui::DOWN_AWAY) {
|
||||
adorn->rect2d(getMyRect2D(adorn->getCanvas()), Color3::gray());
|
||||
}
|
||||
else if (widgetState == Gui::DOWN_OVER) {
|
||||
adorn->rect2d(getMyRect2D(adorn->getCanvas()), Color3::yellow());
|
||||
}
|
||||
|
||||
label2d(
|
||||
adorn,
|
||||
getTitle(),
|
||||
isEnabled() ? getFontColor() : disabledFill(),
|
||||
G3D::Color4(.5,.5,.5,.25) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user