From b71280a62add7b719fd4817154a4e7263fb720bc Mon Sep 17 00:00:00 2001
From: Nick Gasson <nick@nickg.me.uk>
Date: Sun, 17 May 2009 13:55:20 +0100
Subject: [PATCH] Add a few more stats meters

---
 include/IController.hpp   |   1 +
 include/gui/IControl.hpp  |   6 ++
 src/Engine.cpp            |  13 +++-
 src/Game.cpp              |  25 ++++++++
 src/gui/FuelMeter.cpp     | 129 ++++++++++++++++++++++++++++++++++++++
 src/gui/ThrottleMeter.cpp |  19 ++++--
 6 files changed, 186 insertions(+), 7 deletions(-)
 create mode 100644 src/gui/FuelMeter.cpp

diff --git a/include/IController.hpp b/include/IController.hpp
index 9bb0a57..d275cf0 100644
--- a/include/IController.hpp
+++ b/include/IController.hpp
@@ -37,6 +37,7 @@ struct IController {
    // Get current values for the display
    virtual int throttle() const = 0;
    virtual bool brakeOn() const = 0;
+   virtual double pressure() const = 0;
 };
 
 typedef std::tr1::shared_ptr<IController> IControllerPtr;
diff --git a/include/gui/IControl.hpp b/include/gui/IControl.hpp
index 890acbc..dc0501d 100644
--- a/include/gui/IControl.hpp
+++ b/include/gui/IControl.hpp
@@ -21,12 +21,15 @@
 #include "IFont.hpp"
 
 #include <tr1/memory>
+#include <tr1/tuple>
 #include <string>
 
 #include <boost/cast.hpp>
 
 namespace gui {
 
+   typedef std::tr1::tuple<float, float, float> Colour;
+
    // Interface to any UI control
    struct IControl {
       virtual ~IControl() {}
@@ -61,6 +64,7 @@ namespace gui {
       virtual ~IMeterControl() {}
 
       virtual void setValue(int aValue) = 0;
+      virtual void setRange(int aLowValue, int aHighValue) = 0;
    };
 
    typedef std::tr1::shared_ptr<IMeterControl> IMeterControlPtr;
@@ -69,6 +73,8 @@ namespace gui {
    IControlPtr makeButton(const std::string& aGlyphFile);
    ITextControlPtr makeLabel(IFontPtr aFont, const std::string& aString="");
    IMeterControlPtr makeThrottleMeter(IFontPtr aFont);
+   IMeterControlPtr makeFuelMeter(IFontPtr aFont, const std::string& aCaption,
+                                  const Colour& aColour);
 }
 
 #endif
diff --git a/src/Engine.cpp b/src/Engine.cpp
index 767e74f..8293003 100644
--- a/src/Engine.cpp
+++ b/src/Engine.cpp
@@ -48,6 +48,9 @@ using namespace std::tr1;
 // Run models.gnuplot to see an example of these curves. The functions
 // in that file should match the code here!!
 //
+// Currently pressure varies between 0 and 1. <0.1 and >1.0 are bad, but
+// currently don't correspond to real values
+//
 
 // Concrete implementation of a steam engine
 class Engine : public IRollingStock,
@@ -68,6 +71,7 @@ public:
    void actOn(Action anAction);
    int throttle() const { return myThrottle; }
    bool brakeOn() const { return isBrakeOn; }
+   double pressure() const { return myPressure; }
 private:
    double tractiveEffort() const;
    double resistance() const;
@@ -79,20 +83,25 @@ private:
    double myFuelOnFire;
    double myStatTractiveEffort;
    bool isBrakeOn;
-   int myThrottle;   // Ratio measured in tenths
+   int myThrottle;     // Ratio measured in tenths
+   double myPressure;  // Boiler pressure
    
    static const double MODEL_SCALE;
    static const double TRACTIVE_EFFORT_KNEE;
+
+   static const double INIT_PRESSURE;
 };
 
 const double Engine::MODEL_SCALE(0.4);
 const double Engine::TRACTIVE_EFFORT_KNEE(10.0);
+const double Engine::INIT_PRESSURE(0.2);
 
 Engine::Engine()
    : mySpeed(0.0), myMass(29.0), myBoilerPressure(1.0),
      myFireTemp(0.0), myFuelOnFire(0.0),
      myStatTractiveEffort(34.7),
-     isBrakeOn(true), myThrottle(0)
+     isBrakeOn(true), myThrottle(0),
+     myPressure(INIT_PRESSURE)
 {
    myModel = loadModel("pclass.obj", MODEL_SCALE);
 }
diff --git a/src/Game.cpp b/src/Game.cpp
index afd4da9..8d21ab4 100644
--- a/src/Game.cpp
+++ b/src/Game.cpp
@@ -27,6 +27,7 @@
 #include <GL/gl.h>
 
 using namespace std;
+using namespace std::tr1;
 using namespace gui;
 
 // Implementation of the main play screen
@@ -57,6 +58,8 @@ private:
    IContainerPtr myStatsPanel;
    ITextControlPtr mySpeedLabel, myBrakeLabel;
    IMeterControlPtr myThrottleMeter;
+   IMeterControlPtr myCoalMeter, myWaterMeter;
+   IMeterControlPtr myTempMeter, myPressureMeter;
 };
 
 Game::Game(IMapPtr aMap)
@@ -77,6 +80,23 @@ Game::Game(IMapPtr aMap)
    myThrottleMeter = makeThrottleMeter(stdFont);
    myStatsPanel->addChild(myThrottleMeter);
 
+   myCoalMeter = makeFuelMeter(stdFont, "Coal",
+                               make_tuple(0.1f, 0.1f, 0.1f));
+   myStatsPanel->addChild(myCoalMeter);
+   
+   myWaterMeter = makeFuelMeter(stdFont, "Water",
+                                make_tuple(0.1f, 0.1f, 0.8f));
+   myStatsPanel->addChild(myWaterMeter);
+
+   myTempMeter = makeFuelMeter(stdFont, "Temp",
+                               make_tuple(0.8f, 0.1f, 0.1f));
+   myStatsPanel->addChild(myTempMeter);
+
+   myPressureMeter = makeFuelMeter(stdFont, "Pressure",
+                                   make_tuple(0.1f, 0.3f, 0.5f));
+   myPressureMeter->setRange(0, 60);
+   myStatsPanel->addChild(myPressureMeter);
+
    myBrakeLabel = makeLabel(stdFont, "Brake on");
    myBrakeLabel->setColour(1.0f, 0.0f, 0.0f);
    myStatsPanel->addChild(myBrakeLabel);
@@ -121,6 +141,11 @@ void Game::update(IPickBufferPtr aPickBuffer, int aDelta)
    mySpeedLabel->setText("Speed: %.1lfmph\n", myTrain->speed() * msToMPH);
    myThrottleMeter->setValue(myTrain->controller()->throttle());
    myBrakeLabel->setVisible(myTrain->controller()->brakeOn());
+
+   const double pressure = myTrain->controller()->pressure();
+   myPressureMeter->setValue(static_cast<int>(pressure * 100.0));
+
+   myWaterMeter->setValue(8);
 }
 
 void Game::onKeyDown(SDLKey aKey)
diff --git a/src/gui/FuelMeter.cpp b/src/gui/FuelMeter.cpp
new file mode 100644
index 0000000..d2badab
--- /dev/null
+++ b/src/gui/FuelMeter.cpp
@@ -0,0 +1,129 @@
+//
+//  Copyright (C) 2009  Nick Gasson
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "gui/IControl.hpp"
+
+#include <stdexcept>
+
+#include <GL/gl.h>
+#include <boost/lexical_cast.hpp>
+
+using namespace gui;
+using namespace std;
+using namespace std::tr1;
+using namespace boost;
+
+class FuelMeter : public IMeterControl {
+public:
+   FuelMeter(IFontPtr aFont, const string& aCaption,
+             const Colour& aColour);
+   ~FuelMeter() {}
+
+   // IControl interface   
+   void render(int x, int y) const;
+   int width() const;
+   int height() const;
+   void setVisible(bool visible) { amVisible = visible; }
+
+   // IMeterControl interface
+   void setValue(int aValue);
+   void setRange(int aLowValue, int aHighValue);
+private:
+   int myValue;
+   IFontPtr myFont;
+   const string myCaption;
+   const Colour myColour;
+   const int myTextWidth;
+   bool amVisible;
+   int myMin, myMax;
+
+   static const int METER_HEIGHT, METER_WIDTH;
+};
+
+const int FuelMeter::METER_HEIGHT(16);
+const int FuelMeter::METER_WIDTH(100);
+
+FuelMeter::FuelMeter(IFontPtr aFont, const string& aCaption,
+                     const Colour& aColour)
+   : myValue(0), myFont(aFont), myCaption(aCaption + ": "),
+     myColour(aColour),
+     myTextWidth(myFont->stringWidth(myCaption.c_str())),
+     amVisible(true), myMin(0), myMax(10)
+{
+   
+}
+
+int FuelMeter::height() const
+{
+   return max(myFont->maxHeight(), METER_HEIGHT);
+}
+
+int FuelMeter::width() const
+{
+   return myTextWidth + METER_WIDTH;
+}
+
+void FuelMeter::setValue(int aValue)
+{
+   if (aValue < myMin)
+      throw runtime_error("Fuel meter underflow: "
+                          + lexical_cast<string>(aValue));
+   else if (aValue > myMax)
+      throw runtime_error("Fuel meter overflow: "
+                          + lexical_cast<string>(aValue));
+   
+   myValue = aValue;
+}
+
+void FuelMeter::setRange(int aLowValue, int aHighValue)
+{
+   myMin = aLowValue;
+   myMax = aHighValue;
+}
+
+void FuelMeter::render(int x, int y) const
+{
+   if (!amVisible)
+      return;
+
+   myFont->print(x, y, myCaption.c_str());
+
+   glPushMatrix();
+
+   const int off = height() - METER_HEIGHT + 1;
+   
+   glTranslatef(static_cast<float>(myTextWidth),
+                static_cast<float>(y + off), 0.0f);
+
+   const int unit = METER_WIDTH / (myMax + 1);
+
+   glColor3f(get<0>(myColour), get<1>(myColour), get<2>(myColour));
+   glBegin(GL_QUADS);
+   glVertex2i(0, 0);
+   glVertex2i(0, METER_HEIGHT);
+   glVertex2i(unit * myValue, METER_HEIGHT);
+   glVertex2i(unit * myValue, 0);
+   glEnd();
+
+   glPopMatrix();
+}
+
+IMeterControlPtr gui::makeFuelMeter(IFontPtr aFont, const string& aCaption,
+                                    const Colour& aColour)
+{
+   return IMeterControlPtr(new FuelMeter(aFont, aCaption, aColour));
+}
diff --git a/src/gui/ThrottleMeter.cpp b/src/gui/ThrottleMeter.cpp
index 09d0a56..e7a8511 100644
--- a/src/gui/ThrottleMeter.cpp
+++ b/src/gui/ThrottleMeter.cpp
@@ -37,11 +37,13 @@ public:
 
    // IMeterControl interface
    void setValue(int aValue);
+   void setRange(int aLowValue, int aHighValue);
 private:
    int myValue;
    IFontPtr myFont;
    const int myTextWidth;
    bool amVisible;
+   int myMin, myMax;
 
    static const int THROTTLE_MAX = 10;
    static const int THROTTLE_MIN = 0;
@@ -55,7 +57,8 @@ const int ThrottleMeter::METER_WIDTH(100);
 ThrottleMeter::ThrottleMeter(IFontPtr aFont)
    : myValue(0), myFont(aFont),
      myTextWidth(myFont->stringWidth("Throttle: ")),
-     amVisible(true)
+     amVisible(true),
+     myMin(THROTTLE_MIN), myMax(THROTTLE_MAX)
 {
    
 }
@@ -75,6 +78,12 @@ void ThrottleMeter::setValue(int aValue)
    myValue = aValue;
 }
 
+void ThrottleMeter::setRange(int aLowValue, int aHighValue)
+{
+   myMin = aLowValue;
+   myMax = aHighValue;
+}
+
 void ThrottleMeter::render(int x, int y) const
 {
    if (!amVisible)
@@ -89,7 +98,7 @@ void ThrottleMeter::render(int x, int y) const
    glTranslatef(static_cast<float>(myTextWidth),
                 static_cast<float>(y + off), 0.0f);
 
-   const int unit = METER_WIDTH / (THROTTLE_MAX + 1);
+   const int unit = METER_WIDTH / (myMax + 1);
 
    // Neutral bit
    glColor3f(1.0f, 1.0f, 0.0f);
@@ -100,8 +109,8 @@ void ThrottleMeter::render(int x, int y) const
    glVertex2i(unit, 0);
    glEnd();
 
-   int squareLen = myValue >= THROTTLE_MAX
-      ? (THROTTLE_MAX - 1) * unit
+   int squareLen = myValue >= myMax
+      ? (myMax - 1) * unit
       : (myValue > 0 ? unit * (myValue - 1) : 0);
 
    glTranslatef(static_cast<float>(unit), 0.0f, 0.0f);
@@ -117,7 +126,7 @@ void ThrottleMeter::render(int x, int y) const
       glEnd();
    }
    
-   const bool wantTriangle = myValue < THROTTLE_MAX && myValue > 0;
+   const bool wantTriangle = myValue < myMax && myValue > 0;
    if (wantTriangle) {
       // Triangle bit
       glBegin(GL_TRIANGLES);
-- 
2.39.5