//======== (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 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(); }