ns/releases/3.01/source/ui/PieNode.cpp

1423 lines
39 KiB
C++
Raw Normal View History

//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. =========
//
// The copyright to the contents herein is the property of Charles G. Cleveland.
// The contents may be used and/or copied only with the written permission of
// Charles G. Cleveland, or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose:
//
// $Workfile: PieNode.cpp $
// $Date: 2002/08/31 18:04:32 $
//
//-------------------------------------------------------------------------------
// $Log: PieNode.cpp,v $
// Revision 1.18 2002/08/31 18:04:32 Flayra
// - Work at VALVe
//
// Revision 1.17 2002/08/16 02:29:19 Flayra
// - Added document headers
// - Started to add support for pie nodes with both images and text
//
// Revision 1.16 2002/07/08 16:18:52 Flayra
// - Tried to turn off mouse capture so menu can be binded to keyboard and mouse properly
//
//===============================================================================
#include "ui/PieNode.h"
#include "util/Tokenizer.h"
#include "cl_dll/vgui_int.h"
#include "ui/UIUtil.h"
const float kTwoPI = 2.0f*3.141519f;
int PieNode::mDegrees[kNumNodes] = {90, 135, 180, 225, 270, 315, 0, 45};
PieNode* gNodeToTrack = NULL;
const int kDarkGreenColor = 130;
const int kDisabledColorComponent = 180;
PieNode::PieNode(const string& inNodeString, int inMessageID) : FadingImageLabel(0, 0) //, 640, 480)
{
memset(this->mArray, 0, kNumNodes*sizeof(PieNode*));
this->mBaseText = inNodeString;
this->mPointCost = 0;
this->setText(inNodeString.c_str());
this->mNodeName = inNodeString;
this->mParentPieNode = NULL;
this->mMessageID = inMessageID;
this->mDrawSelected = false;
this->setVisible(false);
this->mEnabled = true;
this->mColorBias = 1.0f;
this->ComputeAndSetLocalizedText();
// Save defaults for resetting back to
this->mDefaultText = this->mBaseText;
this->mDefaultID = this->mMessageID;
this->mConnectorSprite = 0;
//this->SetFadeState(false);
}
PieNode::~PieNode()
{
for(int i = 0; i < kNumNodes; i++)
{
delete this->mArray[i];
this->mArray[i] = NULL;
}
}
void PieNode::AddInputSignalForNodes(InputSignal* s)
{
FadingImageLabel::addInputSignal(s);
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->AddInputSignalForNodes(s);
}
}
}
bool PieNode::AttachNode(PieNode* inNode, int inAngularOffset, float inNodeXDistance, float inNodeYDistance)
{
bool theSuccess = false;
if((inAngularOffset >= 0) && (inAngularOffset < kNumNodes))
{
if(this->mArray[inAngularOffset] == NULL)
{
this->mArray[inAngularOffset] = inNode;
inNode->SetPosFromOffset(inAngularOffset, inNodeXDistance, inNodeYDistance);
//this->addChild(inNode);
//inNode->setParent(this);
inNode->mParentPieNode = this;
theSuccess = true;
}
}
return theSuccess;
}
void PieNode::ChangeNode(int inMessageID, int inNewMessageID, const string& inNewText)
{
if(this->mMessageID == inMessageID)
{
this->mMessageID = inNewMessageID;
if(inNewText != "")
{
this->mBaseText = inNewText;
this->ComputeAndSetLocalizedText();
}
}
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->ChangeNode(inMessageID, inNewMessageID, inNewText);
}
}
}
void PieNode::ChangeNode(int inNewMessageID, const string& inNewText)
{
this->mMessageID = inNewMessageID;
if(inNewText != "")
{
this->mBaseText = inNewText;
this->ComputeAndSetLocalizedText();
}
}
void PieNode::ComputeAndSetLocalizedText()
{
string theString = this->mBaseText;
char theLocalizedString[128];
if(CHudTextMessage::LocaliseTextString(theString.c_str(), theLocalizedString, 128))
{
// Remove newlines and junk
for(int i = 0; i < 128; i++)
{
char theCurrentChar = theLocalizedString[i];
if((theCurrentChar == '\n') || (theCurrentChar == '\r'))
{
theLocalizedString[i] = '\0';
break;
}
}
theString = theLocalizedString;
}
// Save it
this->mLocalizedText = theString;
this->setText(theString.c_str());
}
void PieNode::DisableNodesNotInMessageList(const MessageIDList& inList)
{
this->mEnabled = false;
// If node is in list, enable it
MessageIDList::const_iterator theFindIter = std::find(inList.begin(), inList.end(), this->mMessageID);
if(theFindIter != inList.end())
{
this->mEnabled = true;
}
// Call recursively on children
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->DisableNodesNotInMessageList(inList);
}
}
}
void PieNode::DisableNodesWhoseChildrenAreDisabled()
{
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->DisableNodesWhoseChildrenAreDisabled();
}
}
// Now disable ourself we have children and they are all disabled
bool theHasChildren = false;
bool theHasEnabledChild = false;
for(i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theHasChildren = true;
if(theCurrentPieNode->GetEnabled())
{
theHasEnabledChild = true;
break;
}
}
}
if(theHasChildren && !theHasEnabledChild)
{
this->mEnabled = false;
}
}
//vgui::Color PieNode::GetColor() const
//{
// return vgui::Color(0, (this->GetDrawSelected() ? 255 : kDarkGreenColor), 0, this->GetValveAlpha());
// //return vgui::Color(0, kDarkGreenColor, 0, this->GetValveAlpha());
//}
void PieNode::getBgColor(int& r, int& g,int& b, int& a)
{
Color theColor;
this->getBgColor(theColor);
theColor.getColor(r, g, b, a);
}
void PieNode::getBgColor(Color& outColor)
{
Color theBGColor;
FadingImageLabel::getBgColor(theBGColor);
if(this->mEnabled)
{
if(this->mDrawSelected)
{
// Brighten color slightly
int theR, theG, theB, theA;
theBGColor.getColor(theR, theG, theB, theA);
float theScalar = 1.4f;
theBGColor.setColor(min(theScalar*theR, 255), min(theScalar*theG, 255), min(theScalar*theB, 255), theA);
}
}
else
{
int theR, theG, theB, theA;
theBGColor.getColor(theR, theG, theB, theA);
theBGColor.setColor(kDisabledColorComponent, kDisabledColorComponent, kDisabledColorComponent, theA);
}
outColor = theBGColor;
}
// Override and provide no behavior so node visibility isn't changed. This is needed so
// the node can still receive input signal events and be faded in when the mouse moves
// over it
//void PieNode::FadedIn()
//{
//}
//void PieNode::FadedOut()
//{
//}
bool PieNode::GetDrawHighlighted() const
{
return this->GetDrawSelected();
}
bool PieNode::GetDrawSelected() const
{
return this->mDrawSelected;
}
bool PieNode::GetEnabled() const
{
return this->mEnabled;
}
bool PieNode::GetHasChild(const PieNode* inNode) const
{
bool theHasChild = false;
for(int i = 0; i < kNumNodes; i++)
{
if(this->mArray[i] == inNode)
{
theHasChild = true;
break;
}
}
return theHasChild;
}
bool PieNode::GetIsAbove(const PieNode* inNode) const
{
bool thisIsAbove = false;
const PieNode* thePieNode = inNode;
while(thePieNode != NULL)
{
if(thePieNode == this)
{
thisIsAbove = true;
break;
}
else
{
thePieNode = thePieNode->mParentPieNode;
}
}
return thisIsAbove;
}
int PieNode::GetMessageID() const
{
return this->mMessageID;
}
PieNode* PieNode::GetNodeAtAngularOffset(int inAngularOffset)
{
PieNode* thePieNode = NULL;
if((inAngularOffset >= 0) && (inAngularOffset < kNumNodes))
{
thePieNode = this->mArray[inAngularOffset];
}
return thePieNode;
}
const string& PieNode::GetNodeName() const
{
return this->mNodeName;
}
int PieNode::GetPointCost() const
{
return this->mPointCost;
}
PieNode* PieNode::GetRoot()
{
PieNode* theRoot = this;
while(theRoot->mParentPieNode != NULL)
{
theRoot = theRoot->mParentPieNode;
}
return theRoot;
}
bool PieNode::HasChildren(void) const
{
bool theHasChildren = false;
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theHasChildren = true;
break;
}
}
return theHasChildren;
}
bool PieNode::HighlightNode(void)
{
bool theSuccess = false;
// Only valid if parent is NULL, or parent is already selected
// This prevents selecting from the edge, you must follow track
//if(!this->mParentPieNode || this->mParentPieNode->GetFadeState())
//{
// Get root then set all of it's children as faded out
PieNode* theRoot = this->GetRoot();
theRoot->SetNodeAndAllChildrenFadeState(false);
theRoot->SetFadeState(true);
// For each non-root parent, set it's fade out state to true
PieNode* theCurrentPieNode = this->mParentPieNode;
while(theCurrentPieNode && (theCurrentPieNode != theRoot))
{
theCurrentPieNode->SetFadeState(true);
theCurrentPieNode = theCurrentPieNode->mParentPieNode;
}
// Set self and adjacent children to fade in
// If there are no nodes below us, parent node and its adjacents (comment
// this out if you don't believe it looks better)
PieNode* theBottomFullNode = /*this->HasChildren() ?*/ this/* : this->mParentPieNode*/;
if(theBottomFullNode)
{
theBottomFullNode->SetNodeAndAdjacentChildrenFadeState(true);
}
theSuccess = true;
//}
this->setAsMouseCapture(false);
return theSuccess;
}
bool PieNode::IsAdjacentTo(const PieNode* inNode)
{
bool isAdjacent = false;
if(inNode == this->mParentPieNode)
{
isAdjacent = true;
}
else
{
isAdjacent = this->GetHasChild(inNode);
// Check siblings
if(!isAdjacent && (this->mParentPieNode))
{
isAdjacent = this->mParentPieNode->GetHasChild(inNode);
}
}
return isAdjacent;
}
bool PieNode::IsChildOf(const PieNode* inNode)
{
bool isChild = false;
if(inNode && (inNode == this->mParentPieNode))
{
isChild = true;
}
return isChild;
}
bool PieNode::isVisible()
{
//return (this->GetValveAlpha() < 255);
return FadingImageLabel::isVisible();
}
// Important: Assumes both pie nodes are the same size
// The first coords it returns are the start of the connector, relative to this node
// The second coords it returns are the end of the connector (designated by inChildPieNode), relative to this node
bool PieNode::ComputeRelativeConnectorCoordinates(PieNode* inChildPieNode, int& theOutX0, int& theOutY0, int& theOutX1, int& theOutY1)
{
ASSERT(this != inChildPieNode);
// Check position of dest pie node relative to us
bool theSuccess = false;
int x0, y0;
this->getPos(x0, y0);
int x1, y1;
inChildPieNode->getPos(x1, y1);
int theFirstQuadrant, theSecondQuadrant;
int theDX = x1 - x0;
int theDY = y1 - y0;
// directly above/below or left/right
if((theDX == 0) || (theDY == 0))
{
if(theDY == 0)
{
// If left
if(x1 < x0)
{
// x0/y0 -> left edge
theFirstQuadrant = 2;
// x1/y1 -> right edge
theSecondQuadrant = 6;
}
// If right
else
{
// x0/y0 -> right edge
theFirstQuadrant = 6;
// x1/y1 -> left edge
theSecondQuadrant = 2;
}
}
else
{
// If below
if(y1 > y0)
{
// x0/y0 -> bottom edge
theFirstQuadrant = 4;
// x1/y1 -> top edge
theSecondQuadrant = 0;
}
// If above
else
{
// x0/y0 -> top edge
theFirstQuadrant = 0;
// x1/y1 -> bottom edge
theSecondQuadrant = 4;
}
}
theSuccess = true;
}
// otherwise draw from middle of nearest edge (not required yet)
//else
//{
// ASSERT(false);
//}
//ASSERT(theSuccess);
// Don't draw lines if we failed above
if(theSuccess)
{
// Now compute the coords relative to each
//this->GetCenteredCoordinatesFromQuadrant(theFirstQuadrant, 0, 0, theOutX0, theOutY0);
//this->GetCenteredCoordinatesFromQuadrant(theSecondQuadrant, theDX, theDY, theOutX1, theOutY1);
this->GetCenteredCoordinatesFromQuadrant(theFirstQuadrant, theOutX0, theOutY0);
//this->GetCenteredCoordinatesFromQuadrant(theSecondQuadrant, theOutX1, theOutY1);
inChildPieNode->GetCenteredCoordinatesFromQuadrant(theSecondQuadrant, theOutX1, theOutY1);
theOutX1 += theDX;
theOutY1 += theDY;
}
return theSuccess;
}
// inQuadrant = 0 -> top edge
// inQuadrant = 2 -> left edge
// inQuadrant = 4 -> bottom edge
// inQuadrant = 6 -> right edge
void PieNode::GetCenteredCoordinatesFromQuadrant(int inQuadrant, int& outX, int& outY)
{
int theWidth, theHeight;
this->getSize(theWidth, theHeight);
int theVisibleWidth, theVisibleHeight;
this->GetVisibleSize(theVisibleWidth, theVisibleHeight);
int theVisibleXOffset, theVisibleYOffset;
theVisibleXOffset = (theWidth - theVisibleWidth)/2;
theVisibleYOffset = (theHeight - theVisibleHeight)/2;
// Corner nodes no longer supported
ASSERT(inQuadrant != 1);
ASSERT(inQuadrant != 3);
ASSERT(inQuadrant != 5);
ASSERT(inQuadrant != 7);
switch(inQuadrant)
{
case 0:
outX = theWidth/2;
outY = theVisibleYOffset;
break;
case 2:
outX = theVisibleXOffset;
outY = theHeight/2;
break;
case 4:
outX = theWidth/2;
outY = theVisibleYOffset + theVisibleHeight;
break;
case 6:
outX = theVisibleXOffset + theVisibleWidth;
outY = theHeight/2;
break;
}
}
// Draw nodes
void PieNode::DoPaint()
{
FadingImageLabel::DoPaint();
}
// Draw node lines
void PieNode::paintBackground()
{
// For each child
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
// Draw line from us to them indicating pie node in that direction
if(theCurrentPieNode->GetValveAlpha() < 255)
{
// If node above us isn't highlighted, don't draw line to us
//if(theCurrentPieNode->HasSelectedNodeAbove() || theCurrentPieNode->HasSelectedNodeBelow())
// All those lines are getting confusing. Only draw the lines around this pie node that
// connect to a selected parent or selected child
if(theCurrentPieNode->HasSelectedNodeBelow() || this->GetDrawSelected())
{
// Find correct line to draw from center of edge of source pie node to center
// of edge of dest pie node
int theRelativeLineX0, theRelativeLineY0, theRelativeLineX1, theRelativeLineY1;
if(this->ComputeRelativeConnectorCoordinates(theCurrentPieNode, theRelativeLineX0, theRelativeLineY0, theRelativeLineX1, theRelativeLineY1))
{
// What color to draw the line in?
//vgui::Color theCurrentColor = this->GetColor();
vgui::Color theCurrentColor;
//theCurrentPieNode->getBgColor(theCurrentColor);
if(this->mEnabled)
{
theCurrentPieNode->getFgColor(theCurrentColor);
}
else
{
theCurrentPieNode->getBgColor(theCurrentColor);
}
int r, g, b, a;
theCurrentColor.getColor(r, g, b, a);
// Take gamma into account
//r *= this->mColorBias;
//g *= this->mColorBias;
//b *= this->mColorBias;
a = this->GetValveAlpha();
// //if(theCurrentPieNode->GetEnabled())
// //{
// g = (theCurrentPieNode->HasSelectedNodeBelow() ? 255 : kDarkGreenColor);
// //g = (theCurrentPieNode->HasSelectedNodeAbove() ? 255 : kDarkGreenColor);
// //}
// // ...else this will draw lines in disabled color too, using current fade alpha
// Stupid Valve-alpha, vguiSimpleLine wants normal alpha of course
a = 255 - a;
a *= this->mColorBias;
// Only draw if child isn't invisible. Draw line
// relative from current node.
if(this->mConnectorSprite == 0 && (this->mConnectorSpriteName != ""))
{
this->mConnectorSprite = Safe_SPR_Load(this->mConnectorSpriteName.c_str());
}
if(this->mConnectorSprite > 0)
{
// Approximate alpha
float theAlpha = a/255.0f;
int theSpriteRed = theAlpha*r;
int theSpriteGreen = theAlpha*g;
int theSpriteBlue = theAlpha*b;
SPR_Set(this->mConnectorSprite, theSpriteRed, theSpriteGreen, theSpriteBlue);
int theLineWidth = abs(theRelativeLineX1 - theRelativeLineX0);
int theLineHeight = abs(theRelativeLineY1 - theRelativeLineY0);
// Use second frame if vertical
int theConnectorFrame = 0;
if(theLineHeight > 0)
{
theConnectorFrame = 1;
}
int theConnectorSpriteWidth = SPR_Width(this->mConnectorSprite, theConnectorFrame);
int theConnectorSpriteHeight = SPR_Height(this->mConnectorSprite, theConnectorFrame);
int thePieNodeWidth, thePieNodeHeight;
this->getContentSize(thePieNodeWidth, thePieNodeHeight);
int thePieNodeX, thePieNodeY;
this->getPos(thePieNodeX, thePieNodeY);
int theStartX = 0;
int theStartY = 0;
if(theConnectorFrame == 0)
{
int theXOffset = (theLineWidth - theConnectorSpriteWidth)/2;
//if(theXOffset < 0)
// theXOffset = 0;
theStartX = min(theRelativeLineX0, theRelativeLineX1) + theXOffset;
theStartY = min(theRelativeLineY0, theRelativeLineY1) - theConnectorSpriteHeight/2;
// int theScissorStartX = thePieNodeX + min(theRelativeLineX0, theRelativeLineX1) + thePieNodeWidth/2;
// int theScissorStartY = thePieNodeY + theStartY;
// int theScissorEndX = theScissorStartX + theLineWidth - thePieNodeWidth/2;
// int theScissorEndY = theScissorStartY + theConnectorSpriteHeight;
//vguiSimpleBox(theScissorStartX - thePieNodeX, theScissorStartY - thePieNodeY, theScissorEndX - thePieNodeX, theScissorEndY - thePieNodeY, 255, 255, 0, 128);
// SPR_EnableScissor(theScissorStartX, theScissorStartY, theLineWidth, theConnectorSpriteHeight);
}
else
{
int theYOffset = (theLineHeight - theConnectorSpriteHeight)/2;
//if(theYOffset < 0)
// theYOffset = 0;
theStartX = min(theRelativeLineX0, theRelativeLineX1) - theConnectorSpriteWidth/2;
theStartY = min(theRelativeLineY0, theRelativeLineY1) + theYOffset;
// int theScissorStartX = thePieNodeX + theStartX;
// int theScissorStartY = thePieNodeY + min(theRelativeLineY0, theRelativeLineY1);
// int theScissorEndX = theScissorStartX + theConnectorSpriteWidth;
// int theScissorEndY = theScissorStartY + theLineHeight - thePieNodeHeight;
//vguiSimpleBox(theScissorStartX - thePieNodeX, theScissorStartY - thePieNodeY, theScissorEndX - thePieNodeX, theScissorEndY - thePieNodeY, 255, 255, 0, 128);
// SPR_EnableScissor(theScissorStartX, theScissorStartY, theConnectorSpriteWidth, theLineHeight);
}
// Take into account our position, including our parent(s), when drawing
// Panel* theParent = this->getParent();
// while(theParent)
// {
// int theX, theY;
// theParent->getPos(theX, theY);
// theStartX += theX;
// theStartY += theY;
// theParent = theParent->getParent();
// }
// Draw it
//SPR_DrawAdditive(theConnectorFrame, theStartX, theStartY, NULL);
SPR_DrawHoles(theConnectorFrame, theStartX, theStartY, NULL);
// gEngfuncs.pfnSPR_DisableScissor();
}
else
{
vguiSimpleLine(theRelativeLineX0, theRelativeLineY0, theRelativeLineX1, theRelativeLineY1, r, g, b, a);
}
vguiSimpleBox(theRelativeLineX0, theRelativeLineY0, theRelativeLineX1, theRelativeLineY1, 255, 255, 0, 128);
}
}
}
}
}
}
void PieNode::GetMaxTextSize(int& outWidth, int& outHeight)
{
int theTempWidth, theTempHeight;
const float kTextHeightBias = 1.5f;
// Use the text extents to dictate desired node size
// Bias text size a bit for usability
this->getTextSize(theTempWidth, theTempHeight);
theTempHeight *= kTextHeightBias;
if(theTempWidth > outWidth)
{
outWidth = theTempWidth;
}
if(theTempHeight > outHeight)
{
outHeight = theTempHeight;
}
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentNode = this->mArray[i];
if(theCurrentNode)
{
theCurrentNode->GetMaxTextSize(theTempWidth, theTempHeight);
if(theTempWidth > outWidth)
{
outWidth = theTempWidth;
}
if(theTempHeight > outHeight)
{
outHeight = theTempHeight;
}
}
}
}
bool PieNode::GetSelectedNode(PieNode*& outNode)
{
bool theFoundNode = false;
if(this->mDrawSelected)
{
outNode = this;
theFoundNode = true;
}
else
{
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentNode = this->mArray[i];
if(theCurrentNode)
{
theFoundNode = theCurrentNode->GetSelectedNode(outNode);
if(theFoundNode)
{
break;
}
}
}
}
return theFoundNode;
}
bool PieNode::HasSelectedNodeAbove(void) const
{
bool hasSelectedNodeAbove = false;
const PieNode* thePieNode = this;
while(thePieNode != NULL)
{
if(thePieNode->GetDrawSelected())
{
hasSelectedNodeAbove = true;
break;
}
else
{
thePieNode = thePieNode->mParentPieNode;
}
}
return hasSelectedNodeAbove;
}
bool PieNode::HasSelectedNodeBelow(void) const
{
bool hasSelectedNodeBelow = false;
// For each of our children
if(this->GetDrawSelected())
{
hasSelectedNodeBelow = true;
}
else
{
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentNode = this->mArray[i];
if(theCurrentNode)
{
// If child has selected node below
if(theCurrentNode->HasSelectedNodeBelow())
{
// break
hasSelectedNodeBelow = true;
break;
}
}
}
}
return hasSelectedNodeBelow;
}
void PieNode::MoveTree(int inDeltaX, int inDeltaY)
{
int theCurrentX, theCurrentY;
this->getPos(theCurrentX, theCurrentY);
this->setPos(theCurrentX + inDeltaX, theCurrentY + inDeltaY);
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->MoveTree(inDeltaX, inDeltaY);
}
}
}
void PieNode::ResetToDefaults()
{
this->mMessageID = this->mDefaultID;
this->mBaseText = this->mDefaultText;
this->ComputeAndSetLocalizedText();
}
void PieNode::SetColorBias(float inBias)
{
this->mColorBias = inBias;
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->SetColorBias(this->mColorBias);
}
}
}
void PieNode::SetConnectorName(const string& inConnectorName)
{
string theNewSpriteName = UINameToSprite(inConnectorName, ScreenWidth());
if(theNewSpriteName != this->mConnectorSpriteName)
{
// Mark it to be reloaded
this->mConnectorSpriteName = theNewSpriteName;
this->mConnectorSprite = 0;
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->SetConnectorName(inConnectorName);
}
}
}
}
void PieNode::SetDefaultImage(const string& inDefaultImage)
{
this->mDefaultImage = inDefaultImage;
this->SetSpriteName(this->mDefaultImage);
}
void PieNode::SetDrawSelected(bool inDrawSelected)
{
if(this->mEnabled)
{
this->mDrawSelected = inDrawSelected;
}
}
void PieNode::SetNodeAndAllChildrenFadeState(bool inNewFadeState)
{
this->SetFadeState(inNewFadeState);
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->SetNodeAndAllChildrenFadeState(inNewFadeState);
}
}
}
void PieNode::SetNodeDistance(float inNewXDistance, float inNewYDistance)
{
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->SetPosFromOffset(i, inNewXDistance, inNewYDistance);
}
}
}
bool PieNode::SetPosFromOffset(int inAngularOffset, float inNodeXDistance, float inNodeYDistance)
{
bool theSuccess = false;
if((inAngularOffset >= 0) && (inAngularOffset < kNumNodes))
{
// 0 corresponds to north (90 degrees), 90 corresponds to west (180 degrees)
// Invert y because half-life uses +Y towards top of screen
float theDegrees = kTwoPI*(this->mDegrees[inAngularOffset]/360.0f);
// Calculate spacing in pixels from inNodeDistance. Use same number of pixels
// as base for both x and y offset so the spacing is even (as opposed to using
// inNodeDistance*ScreenHeight for the y-component)
float theNodeXPixelDist = inNodeXDistance*ScreenWidth();
int xDiff = (int)(cos(theDegrees)*theNodeXPixelDist);
float theNodeYPixelDist = inNodeYDistance*ScreenHeight();
int yDiff = -(int)(sin(theDegrees)*theNodeYPixelDist);
this->setPos(xDiff, yDiff);
theSuccess = true;
}
return theSuccess;
}
//void PieNode::SetTolerance(int inPercentage)
//{
//}
//void PieNode::setSize(int wide, int tall)
//{
// // set size for self
// FadingImageLabel::setSize(wide, tall);
//
// // set size for all children
// for(int i = 0; i < kNumNodes; i++)
// {
// PieNode* theCurrentPieNode = this->mArray[i];
// if(theCurrentPieNode)
// {
// theCurrentPieNode->setSize(wide, tall);
// }
// }
//}
void PieNode::SetSizeKeepCenter(int inWidth, int inHeight)
{
// set size for self
FadingImageLabel::SetSizeKeepCenter(inWidth, inHeight);
// set size for all children
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->SetSizeKeepCenter(inWidth, inHeight);
}
}
}
void PieNode::SetNodeAndAdjacentChildrenFadeState(bool inFadeState)
{
this->SetFadeState(inFadeState);
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->SetFadeState(inFadeState);
}
}
}
void PieNode::SetFadeState(bool inNewFadeState)
{
FadingImageLabel::SetFadeState(inNewFadeState);
}
void PieNode::SetVisibleSize(int inVisWidth, int inVisHeight)
{
FadingImageLabel::SetVisibleSize(inVisWidth, inVisHeight);
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->SetVisibleSize(inVisWidth, inVisHeight);
}
}
}
void PieNode::SetNodeAndChildrenVisible(bool inVisibilityState)
{
this->setVisible(inVisibilityState);
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->SetNodeAndChildrenVisible(inVisibilityState);
}
}
}
void PieNode::Update(float theCurrentTime)
{
FadingImageLabel::Update(theCurrentTime);
// Update all children
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->Update(theCurrentTime);
}
}
}
void PieNode::UpdateMenuFromTech(const AvHTechNodes& inMenuCosts, int inPurchaseLevel)
{
// Set our next text
//string theNewText = this->mBaseText;
string theNewText = this->mLocalizedText;
this->mPointCost = 0;
AvHMessageID theMessageID = (AvHMessageID)this->mMessageID;
// Lookup message id and append onto it if it exists
bool theResearchable = false;
int theCost = 0;
float theTime = 0;
if(inMenuCosts.GetResearchInfo(theMessageID, theResearchable, theCost, theTime))
{
// Only append point cost if it isn't a sprite and there is a point cost greater than 0
this->mPointCost = theCost;
if(this->mPointCost > 0 && (theNewText[0] != '!'))
{
char theCharArray[6];
sprintf(theCharArray, " (%d)", this->mPointCost);
theNewText += string(theCharArray);
}
}
// Set it
this->setText(theNewText.c_str());
// Disable or enable node
bool theMessageIsAvailable = inMenuCosts.GetIsMessageAvailable(theMessageID);
this->mEnabled = true;
if((theCost >= 0) && ((this->mPointCost > inPurchaseLevel) /*|| (this->mPointCost == -1)*/ || !theResearchable || !theMessageIsAvailable))
{
this->mEnabled = false;
}
// do the same for our children
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->UpdateMenuFromTech(inMenuCosts, inPurchaseLevel);
}
}
}
void PieNode::VidInit(void)
{
FadingImageLabel::VidInit();
if(this->mConnectorSprite > 0)
{
// TODO: Can we unload it?
}
// Mark it to be reloaded next draw
this->mConnectorSprite = 0;
// do the same for our children
for(int i = 0; i < kNumNodes; i++)
{
PieNode* theCurrentPieNode = this->mArray[i];
if(theCurrentPieNode)
{
theCurrentPieNode->VidInit();
}
}
}
PieNodeList::PieNodeList(const string& inRootName, float inDist)
{
this->mRoot = new PieNode(inRootName, 0);
this->mRoot->setVisible(false);
this->mNodeXDistance = this->mNodeYDistance = inDist;
}
PieNodeList::~PieNodeList()
{
delete this->mRoot;
}
void PieNodeList::AddInputSignalForNodes(InputSignal* s)
{
this->mRoot->AddInputSignalForNodes(s);
}
// Accepts input in the form of:
// #/#/#/#/nodename/msgID
bool PieNodeList::AddNode(const string& inNodeString, Font* inFont, const Color& inBGColor, const Color& inFGColor, const string& inDefaultImage)
{
bool theSuccess = false;
// Parse inNodeString to get node offsets and node name
StringVector theTokens;
string theDelimiters("/");
Tokenizer::split(inNodeString, theDelimiters, theTokens);
if(theTokens.size() > 1)
{
// Grab directional indicies
typedef vector<int> DirectionalListType;
DirectionalListType theDirectionalList;
// For each direction until the last, get the sub node (skip the last two nodes, should be the node name and msg id)
for(StringVector::iterator theStringIter = theTokens.begin(); ((theStringIter != theTokens.end()) && (theStringIter + 2 != theTokens.end())) ; theStringIter++)
{
string theCurrentToken = *theStringIter;
int theCurrentOffset;
if(sscanf(theCurrentToken.c_str(), "%d", &theCurrentOffset) == 1)
{
theDirectionalList.push_back(theCurrentOffset);
}
}
// Grab node name (second to last in the list)
string theNodeName = *(theTokens.end() - 2);
// Grab the message id (last in list)
string theCurrentToken = *(theTokens.end() - 1);
int theMessageID;
if(sscanf(theCurrentToken.c_str(), "%d", &theMessageID) != 1)
{
}
// Build the new node
PieNode* theCurrentNode = this->mRoot;
ASSERT(theCurrentNode);
for(DirectionalListType::iterator theIter = theDirectionalList.begin(); ((theIter != theDirectionalList.end()) && (theIter + 1 != theDirectionalList.end()) && (theCurrentNode != NULL)); theIter++)
{
theCurrentNode = theCurrentNode->GetNodeAtAngularOffset(*theIter);
}
if(theCurrentNode != NULL)
{
int theLastOffset = *(theDirectionalList.end() - 1);
// At this last node, add the new node with the last index
PieNode* theNewNode = new PieNode(theNodeName, theMessageID);
if(inFont)
{
theNewNode->setFont(inFont);
}
theNewNode->setFgColor(inFGColor);
theNewNode->setBgColor(inBGColor);
theNewNode->setAsMouseCapture(false);
theNewNode->SetDefaultImage(inDefaultImage);
// Set size to be the default size for the rest of the nodes
//int theNodeListSizeX, theNodeListSizeY;
//this->mRoot->getSize(theNodeListSizeX, theNodeListSizeY);
//theNewNode->setSize(theNodeListSizeX, theNodeListSizeY);
theCurrentNode->AttachNode(theNewNode, theLastOffset, this->mNodeXDistance, this->mNodeYDistance);
// Set x/y as it's current x/y plus the node "before" it in the pie hierarchy
int thePreviousMenuX, thePreviousMenuY;
theCurrentNode->getPos(thePreviousMenuX, thePreviousMenuY);
int theNewNodeRelX, theNewNodeRelY;
theNewNode->getPos(theNewNodeRelX, theNewNodeRelY);
theNewNode->setPos(thePreviousMenuX + theNewNodeRelX, thePreviousMenuY + theNewNodeRelY);
// TODO: Just added this.
theNewNode->setSize(this->mNodeXDistance*ScreenWidth(), this->mNodeYDistance*ScreenHeight());
// Set new node's parent as root node's parent
theNewNode->setParent(this->mRoot->getParent());
theSuccess = true;
}
}
return theSuccess;
}
void PieNodeList::ChangeNode(int inMessageID, int inNewMessageID, const string& inNewText)
{
this->mRoot->ChangeNode(inMessageID, inNewMessageID, inNewText);
this->RecomputeVisibleSize();
}
void PieNodeList::DisableNodesNotInMessageList(const MessageIDList& inList)
{
this->mRoot->DisableNodesNotInMessageList(inList);
}
void PieNodeList::DisableNodesWhoseChildrenAreDisabled()
{
this->mRoot->DisableNodesWhoseChildrenAreDisabled();
}
PieNode* PieNodeList::GetRoot()
{
return this->mRoot;
}
bool PieNodeList::GetSelectedNode(PieNode*& outNode)
{
return this->mRoot->GetSelectedNode(outNode);
}
void PieNodeList::RecomputeVisibleSize(void)
{
int theMaxWidth = 0;
int theMaxHeight = 0;
// run through all pie nodes and find the max text extents of any of them
this->mRoot->GetMaxTextSize(theMaxWidth, theMaxHeight);
// set all sizes to accomodate that max size
//this->mRoot->SetSizeKeepCenter(theMaxWidth, theMaxHeight);
this->mRoot->SetVisibleSize(theMaxWidth, theMaxHeight);
}
void PieNodeList::ResetToDefaults()
{
this->mRoot->ResetToDefaults();
}
void PieNodeList::SetBasePosition(int x, int y, int inPieWidth, int inPieHeight)
{
// Center root in middle of pie menu, take into account image size so the image is centered.
// All children are relative to its parent, so they assume the same image width/height
//int xImage = this->mRoot->getImageWide();
//int yImage = this->mRoot->getImageTall();
//int theTextWidth, theTextHeight;
//this->mRoot->getTextSize(theTextWidth, theTextHeight);
int theWidth, theHeight;
this->mRoot->getSize(theWidth, theHeight);
//this->mRoot->setPos(inPieWidth/2 - xImage/2, inPieHeight/2 - yImage/2);
int theCurrentX, theCurrentY;
this->mRoot->getPos(theCurrentX, theCurrentY);
int theDeltaX = (inPieWidth/2 - theWidth/2) - theCurrentX;
int theDeltaY = (inPieHeight/2 - theHeight/2) - theCurrentY;
//this->mRoot->setPos(inPieWidth/2 - theWidth/2, inPieHeight/2 - theHeight/2);
// Run through child nodes and move them all by this amount
this->mRoot->MoveTree(theDeltaX, theDeltaY);
// Make sure the nodes are always as big as the pie so they are free to
// draw child nodes outside their own extents
//this->mRoot->setSize(inPieWidth, inPieHeight);
}
void PieNodeList::SetConnectorName(const string& inConnectorName)
{
this->mRoot->SetConnectorName(inConnectorName);
}
void PieNodeList::SetConstructionComplete()
{
}
void PieNodeList::setParent(vgui::Panel* inPanel)
{
this->mRoot->setParent(inPanel);
}
void PieNodeList::SetNodeDistance(float inNewXDistance, float inNewYDistance)
{
this->mRoot->SetNodeDistance(inNewXDistance, inNewYDistance);
this->mNodeXDistance = inNewXDistance;
this->mNodeYDistance = inNewYDistance;
}
//bool PieNodeList::SetNodeEnabled(const string& inNodeName, bool inNewState)
//{
// return false;
//}
//void PieNodeList::SetRootVisible(bool inNewVisibility)
//{
// // Set just the root not visibility
//}
//
//void PieNodeList::SetTolerance(int inPercentage)
//{
// // Loop through all nodes
// // Call SetTolerance on each
//}
void PieNodeList::SetFadeState(bool inNewFadeState)
{
if(inNewFadeState)
{
this->mRoot->SetNodeAndAdjacentChildrenFadeState(true);
}
else
{
this->mRoot->SetNodeAndAllChildrenFadeState(false);
}
}
void PieNodeList::SetSizeKeepCenter(int inWidth, int inHeight)
{
this->mRoot->SetSizeKeepCenter(inWidth, inHeight);
}
void PieNodeList::Update(float inTime)
{
this->mRoot->Update(inTime);
}
void PieNodeList::UpdateMenuFromTech(const AvHTechNodes& inMenuCosts, int inPurchaseLevel)
{
this->mRoot->UpdateMenuFromTech(inMenuCosts, inPurchaseLevel);
}
void PieNodeList::VidInit()
{
this->mRoot->VidInit();
}