Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/console/InGameConsole.cc @ 1578

Last change on this file since 1578 was 1578, checked in by rgrieder, 16 years ago

gcc and msvc don't always see eye to eye, fortunately…
Also created new config value for cursor symbol.

  • Property svn:eol-style set to native
File size: 21.1 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Felix Schulthess
24 *   Co-authors:
25 *      Fabian 'x3n' Landau
26 *
27 */
28
29#include "OrxonoxStableHeaders.h"
30
31#include "InGameConsole.h"
32
33#include <string>
34#include <OgreOverlay.h>
35#include <OgreOverlayElement.h>
36#include <OgreOverlayManager.h>
37#include <OgreOverlayContainer.h>
38#include <OgreStringConverter.h>
39
40#include "core/Debug.h"
41#include "core/CoreIncludes.h"
42#include "core/ConfigValueIncludes.h"
43#include "core/ConsoleCommand.h"
44#include "core/input/InputManager.h"
45#include "util/Math.h"
46#include "GraphicsEngine.h"
47
48#define LINES 30
49#define CHAR_WIDTH 7.45 // fix this please - determine the char-width dynamically
50
51namespace orxonox
52{
53    SetConsoleCommand(InGameConsole, openConsole, true);
54    SetConsoleCommand(InGameConsole, closeConsole, true);
55
56    using namespace Ogre;
57
58    /**
59        @brief Constructor: Creates and initializes the InGameConsole.
60    */
61    InGameConsole::InGameConsole() :
62        consoleOverlay_(0), consoleOverlayContainer_(0),
63        consoleOverlayNoise_(0), consoleOverlayCursor_(0), consoleOverlayBorder_(0),
64        consoleOverlayTextAreas_(0)
65    {
66        RegisterObject(InGameConsole);
67
68        this->bActive_ = false;
69        this->cursor_ = 0.0f;
70        this->cursorSymbol_ = '|';
71        this->inputWindowStart_ = 0;
72        this->numLinesShifted_ = LINES - 1;
73        // for the beginning, don't scroll
74        this->scroll_ = 0;
75
76        this->setConfigValues();
77    }
78
79    /**
80        @brief Destructor: Destroys the TextAreas.
81    */
82    InGameConsole::~InGameConsole(void)
83    {
84        this->destroy();
85    }
86
87    /**
88        @brief Returns a reference to the only existing instance of InGameConsole.
89    */
90    InGameConsole& InGameConsole::getInstance()
91    {
92        static InGameConsole instance;
93        return instance;
94    }
95
96    /**
97        @brief Sets the config values, describing the size of the console.
98    */
99    void InGameConsole::setConfigValues()
100    {
101        SetConfigValue(relativeWidth, 0.8);
102        SetConfigValue(relativeHeight, 0.4);
103        SetConfigValue(blinkTime, 0.5);
104        SetConfigValue(scrollSpeed_, 3.0f);
105        SetConfigValue(noiseSize_, 1.0f);
106        SetConfigValue(cursorSymbol_, '|');
107    }
108
109    /**
110        @brief Initializes the InGameConsole.
111    */
112    void InGameConsole::initialise()
113    {
114        // create overlay and elements
115        Ogre::OverlayManager* ovMan = Ogre::OverlayManager::getSingletonPtr();
116
117        // create actual overlay
118        this->consoleOverlay_ = ovMan->create("InGameConsoleConsole");
119
120        // create a container
121        this->consoleOverlayContainer_ = static_cast<OverlayContainer*>(ovMan->createOverlayElement("Panel", "InGameConsoleContainer"));
122        this->consoleOverlayContainer_->setMetricsMode(Ogre::GMM_RELATIVE);
123        this->consoleOverlayContainer_->setPosition((1 - this->relativeWidth) / 2, 0);
124        this->consoleOverlayContainer_->setDimensions(this->relativeWidth, this->relativeHeight);
125        this->consoleOverlay_->add2D(this->consoleOverlayContainer_);
126
127        // create BorderPanel
128        this->consoleOverlayBorder_ = static_cast<BorderPanelOverlayElement*>(ovMan->createOverlayElement("BorderPanel", "InGameConsoleBorderPanel"));
129        this->consoleOverlayBorder_->setMetricsMode(Ogre::GMM_PIXELS);
130        this->consoleOverlayBorder_->setMaterialName("ConsoleCenter");
131        this->consoleOverlayBorder_->setBorderSize(16, 16, 0, 16);
132        this->consoleOverlayBorder_->setBorderMaterialName("ConsoleBorder");
133        this->consoleOverlayBorder_->setLeftBorderUV(0.0, 0.49, 0.5, 0.51);
134        this->consoleOverlayBorder_->setRightBorderUV(0.5, 0.49, 1.0, 0.5);
135        this->consoleOverlayBorder_->setBottomBorderUV(0.49, 0.5, 0.51, 1.0);
136        this->consoleOverlayBorder_->setBottomLeftBorderUV(0.0, 0.5, 0.5, 1.0);
137        this->consoleOverlayBorder_->setBottomRightBorderUV(0.5, 0.5, 1.0, 1.0);
138        this->consoleOverlayContainer_->addChild(this->consoleOverlayBorder_);
139
140        // create the text lines
141        this->consoleOverlayTextAreas_ = new TextAreaOverlayElement*[LINES];
142        for (int i = 0; i < LINES; i++)
143        {
144            this->consoleOverlayTextAreas_[i] = static_cast<TextAreaOverlayElement*>(ovMan->createOverlayElement("TextArea", "InGameConsoleTextArea" + Ogre::StringConverter::toString(i)));
145            this->consoleOverlayTextAreas_[i]->setMetricsMode(Ogre::GMM_PIXELS);
146            this->consoleOverlayTextAreas_[i]->setFontName("Monofur");
147            this->consoleOverlayTextAreas_[i]->setCharHeight(18);
148            this->consoleOverlayTextAreas_[i]->setParameter("colour_top", "0.21 0.69 0.21");
149            this->consoleOverlayTextAreas_[i]->setLeft(8);
150            this->consoleOverlayTextAreas_[i]->setCaption("");
151            this->consoleOverlayContainer_->addChild(this->consoleOverlayTextAreas_[i]);
152        }
153
154        // create cursor (also a text area overlay element)
155        this->consoleOverlayCursor_ = static_cast<TextAreaOverlayElement*>(ovMan->createOverlayElement("TextArea", "InGameConsoleCursor"));
156        this->consoleOverlayCursor_->setMetricsMode(Ogre::GMM_PIXELS);
157        this->consoleOverlayCursor_->setFontName("Monofur");
158        this->consoleOverlayCursor_->setCharHeight(18);
159        this->consoleOverlayCursor_->setParameter("colour_top", "0.21 0.69 0.21");
160        this->consoleOverlayCursor_->setLeft(7);
161        this->consoleOverlayCursor_->setCaption(std::string(this->cursorSymbol_, 1));
162        this->consoleOverlayContainer_->addChild(this->consoleOverlayCursor_);
163
164        // create noise
165        this->consoleOverlayNoise_ = static_cast<PanelOverlayElement*>(ovMan->createOverlayElement("Panel", "InGameConsoleNoise"));
166        this->consoleOverlayNoise_->setMetricsMode(Ogre::GMM_PIXELS);
167        this->consoleOverlayNoise_->setPosition(5,0);
168        this->consoleOverlayNoise_->setMaterialName("ConsoleNoiseSmall");
169        // comment following line to disable noise
170        this->consoleOverlayContainer_->addChild(this->consoleOverlayNoise_);
171
172        this->resize();
173
174        // move overlay "above" the top edge of the screen
175        // we take -1.2 because the border makes the panel bigger
176        this->consoleOverlayContainer_->setTop(-1.2 * this->relativeHeight);
177
178        Shell::getInstance().addOutputLevel(true);
179
180        COUT(4) << "Info: InGameConsole initialized" << std::endl;
181    }
182
183    /**
184        @brief Destroys all the elements if necessary.
185    */
186    void InGameConsole::destroy()
187    {
188        Ogre::OverlayManager* ovMan = Ogre::OverlayManager::getSingletonPtr();
189        if (ovMan)
190        {
191            if (this->consoleOverlayNoise_)
192                Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayNoise_);
193            if (this->consoleOverlayCursor_)
194                Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayCursor_);
195            if (this->consoleOverlayBorder_)
196                Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayBorder_);
197            if (this->consoleOverlayTextAreas_)
198            {
199                for (int i = 0; i < LINES; i++)
200                {
201                    if (this->consoleOverlayTextAreas_[i])
202                      Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayTextAreas_[i]);
203                    this->consoleOverlayTextAreas_[i] = 0;
204                }
205
206            }
207            if (this->consoleOverlayContainer_)
208                Ogre::OverlayManager::getSingleton().destroyOverlayElement(this->consoleOverlayContainer_);
209        }
210        if (this->consoleOverlayTextAreas_)
211        {
212            delete[] this->consoleOverlayTextAreas_;
213            this->consoleOverlayTextAreas_ = 0;
214        }
215    }
216
217    // ###############################
218    // ###  ShellListener methods  ###
219    // ###############################
220
221    /**
222        @brief Called if all output-lines have to be redrawn.
223    */
224    void InGameConsole::linesChanged()
225    {
226        std::list<std::string>::const_iterator it = Shell::getInstance().getNewestLineIterator();
227        int max = 0;
228        for (int i = 1; i < LINES; ++i)
229        {
230            if (it != Shell::getInstance().getEndIterator())
231            {
232                ++it;
233                max = i;
234            }
235            else
236                break;
237        }
238
239        for (int i = LINES - 1; i > max; --i)
240            this->print("", i, true);
241
242        for (int i = max; i >= 1; --i)
243        {
244            --it;
245            this->print(*it, i, true);
246        }
247    }
248
249    /**
250        @brief Called if only the last output-line has changed.
251    */
252    void InGameConsole::onlyLastLineChanged()
253    {
254        if (LINES > 1)
255            this->print(*Shell::getInstance().getNewestLineIterator(), 1);
256    }
257
258    /**
259        @brief Called if a new output-line was added.
260    */
261    void InGameConsole::lineAdded()
262    {
263        this->numLinesShifted_ = 0;
264        this->shiftLines();
265        this->onlyLastLineChanged();
266    }
267
268    /**
269        @brief Called if the text in the input-line has changed.
270    */
271    void InGameConsole::inputChanged()
272    {
273        if (LINES > 0)
274            this->print(Shell::getInstance().getInput(), 0);
275
276        if (Shell::getInstance().getInput() == "" || Shell::getInstance().getInput().size() == 0)
277            this->inputWindowStart_ = 0;
278    }
279
280    /**
281        @brief Called if the position of the cursor in the input-line has changed.
282    */
283    void InGameConsole::cursorChanged()
284    {
285        unsigned int pos = Shell::getInstance().getCursorPosition() - inputWindowStart_;
286        if (pos > maxCharsPerLine_)
287            pos = maxCharsPerLine_;
288        else if (pos < 0)
289            pos = 0;
290
291        this->consoleOverlayCursor_->setCaption(std::string(pos,' ') + cursorSymbol_);
292        this->consoleOverlayCursor_->setTop((int) this->windowH_ * this->relativeHeight - 24);
293    }
294
295    /**
296        @brief Called if the console gets closed.
297    */
298    void InGameConsole::exit()
299    {
300        this->deactivate();
301    }
302
303    // ###############################
304    // ###  other external calls   ###
305    // ###############################
306
307    /**
308        @brief Used to control the actual scrolling and the cursor.
309    */
310    void InGameConsole::tick(float dt)
311    {
312        if (this->scroll_ != 0)
313        {
314            float oldTop = this->consoleOverlayContainer_->getTop();
315
316            if (this->scroll_ > 0)
317            {
318                // scrolling down
319                // enlarge oldTop a little bit so that this exponential function
320                // reaches 0 before infinite time has passed...
321                float deltaScroll = (oldTop - 0.01) * dt * this->scrollSpeed_;
322                if (oldTop - deltaScroll >= 0)
323                {
324                    // window has completely scrolled down
325                    this->consoleOverlayContainer_->setTop(0);
326                    this->scroll_ = 0;
327                }
328                else
329                    this->consoleOverlayContainer_->setTop(oldTop - deltaScroll);
330            }
331
332            else
333            {
334                // scrolling up
335                // note: +0.01 for the same reason as when scrolling down
336                float deltaScroll = (1.2 * this->relativeHeight + 0.01 + oldTop) * dt * this->scrollSpeed_;
337                if (oldTop - deltaScroll <= -1.2 * this->relativeHeight)
338                {
339                    // window has completely scrolled up
340                    this->consoleOverlayContainer_->setTop(-1.2 * this->relativeHeight);
341                    this->scroll_ = 0;
342                    this->consoleOverlay_->hide();
343                }
344                else
345                    this->consoleOverlayContainer_->setTop(oldTop - deltaScroll);
346            }
347        }
348
349        if (this->bActive_)
350        {
351            this->cursor_ += dt;
352            if (this->cursor_ >= this->blinkTime)
353            {
354                this->cursor_ = 0;
355                bShowCursor_ = !bShowCursor_;
356                if (bShowCursor_)
357                    this->consoleOverlayCursor_->show();
358                else
359                    this->consoleOverlayCursor_->hide();
360            }
361
362            // this creates a flickering effect (extracts exactly 80% of the texture at a random location)
363            float uRand = (rand() & 1023) / 1023.0f * 0.2f;
364            float vRand = (rand() & 1023) / 1023.0f * 0.2f;
365            this->consoleOverlayNoise_->setUV(uRand, vRand, 0.8f + uRand, 0.8f + vRand);
366        }
367    }
368
369    /**
370        @brief Resizes the console elements. Call if window size changes.
371    */
372    void InGameConsole::resize()
373    {
374        this->windowW_ = GraphicsEngine::getSingleton().getWindowWidth();
375        this->windowH_ = GraphicsEngine::getSingleton().getWindowHeight();
376        this->consoleOverlayBorder_->setWidth((int) this->windowW_* this->relativeWidth);
377        this->consoleOverlayBorder_->setHeight((int) this->windowH_ * this->relativeHeight);
378        this->consoleOverlayNoise_->setWidth((int) this->windowW_ * this->relativeWidth - 10);
379        this->consoleOverlayNoise_->setHeight((int) this->windowH_ * this->relativeHeight - 5);
380        this->consoleOverlayNoise_->setTiling(consoleOverlayNoise_->getWidth() / 80.0f * this->noiseSize_, consoleOverlayNoise_->getHeight() / 80.0f * this->noiseSize_);
381
382        // now adjust the text lines...
383        this->desiredTextWidth_ = (int) (this->windowW_ * this->relativeWidth) - 12;
384
385        if (LINES > 0)
386            this->maxCharsPerLine_ = max((unsigned int)10, (unsigned int) ((float)this->desiredTextWidth_ / CHAR_WIDTH));
387        else
388            this->maxCharsPerLine_ = 10;
389
390        for (int i = 0; i < LINES; i++)
391        {
392            this->consoleOverlayTextAreas_[i]->setWidth(this->desiredTextWidth_);
393            this->consoleOverlayTextAreas_[i]->setTop((int) this->windowH_ * this->relativeHeight - 24 - 14*i);
394        }
395
396        this->linesChanged();
397        this->cursorChanged();
398    }
399
400    // ###############################
401    // ###    internal methods     ###
402    // ###############################
403
404    /**
405        @brief Prints string to bottom line.
406        @param s String to be printed
407    */
408    void InGameConsole::print(const std::string& text, int index, bool alwaysShift)
409    {
410        char level = 0;
411        if (text.size() > 0)
412            level = text[0];
413
414        std::string output = text;
415
416        if (level >= -1 && level <= 5)
417            output.erase(0, 1);
418
419        if (LINES > index)
420        {
421            this->colourLine(level, index);
422
423            if (index > 0)
424            {
425                unsigned int linesUsed = 1;
426                while (output.size() > this->maxCharsPerLine_)
427                {
428                    ++linesUsed;
429                    this->consoleOverlayTextAreas_[index]->setCaption(convert2UTF(output.substr(0, this->maxCharsPerLine_)));
430                    output.erase(0, this->maxCharsPerLine_);
431                    output.insert(0, 1, ' ');
432                    if (linesUsed > numLinesShifted_ || alwaysShift)
433                        this->shiftLines();
434                    this->colourLine(level, index);
435                }
436                this->consoleOverlayTextAreas_[index]->setCaption(convert2UTF(output));
437                this->displayedText_ = output;
438                this->numLinesShifted_ = linesUsed;
439            }
440            else
441            {
442                if (output.size() > this->maxCharsPerLine_)
443                {
444                    if (Shell::getInstance().getInputBuffer().getCursorPosition() < this->inputWindowStart_)
445                        this->inputWindowStart_ = Shell::getInstance().getInputBuffer().getCursorPosition();
446                    else if (Shell::getInstance().getInputBuffer().getCursorPosition() >= (this->inputWindowStart_ + this->maxCharsPerLine_ - 1))
447                        this->inputWindowStart_ = Shell::getInstance().getInputBuffer().getCursorPosition() - this->maxCharsPerLine_ + 1;
448
449                    output = output.substr(this->inputWindowStart_, this->maxCharsPerLine_);
450                }
451                else
452                  this->inputWindowStart_ = 0;
453                this->displayedText_ = output;
454                this->consoleOverlayTextAreas_[index]->setCaption(convert2UTF(output));
455            }
456        }
457    }
458
459    /**
460        @brief Shows the InGameConsole.
461    */
462    void InGameConsole::activate()
463    {
464        if (!this->bActive_)
465        {
466            this->bActive_ = true;
467            InputManager::setInputState(InputManager::IS_CONSOLE);
468            Shell::getInstance().registerListener(this);
469
470            this->resize();
471            this->linesChanged();
472            this->cursorChanged();
473            this->consoleOverlay_->show();
474
475            // scroll down
476            this->scroll_ = 1;
477            // the rest is done by tick
478        }
479    }
480
481    /**
482    @brief Hides the InGameConsole.
483    */
484    void InGameConsole::deactivate()
485    {
486        if (this->bActive_)
487        {
488            this->bActive_ = false;
489            InputManager::setInputState(InputManager::IS_NORMAL);
490            Shell::getInstance().unregisterListener(this);
491
492            // scroll up
493            this->scroll_ = -1;
494            // the rest is done by tick
495        }
496    }
497
498    /**
499        @brief Shifts all output lines one line up
500    */
501    void InGameConsole::shiftLines()
502    {
503        for (unsigned int i = LINES - 1; i > 1; --i)
504        {
505            this->consoleOverlayTextAreas_[i]->setCaption(this->consoleOverlayTextAreas_[i - 1]->getCaption());
506            this->consoleOverlayTextAreas_[i]->setColourTop(this->consoleOverlayTextAreas_[i - 1]->getColourTop());
507            this->consoleOverlayTextAreas_[i]->setColourBottom(this->consoleOverlayTextAreas_[i - 1]->getColourBottom());
508        }
509    }
510
511    void InGameConsole::colourLine(int colourcode, int index)
512    {
513        if (colourcode == -1)
514        {
515            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.90, 0.90, 0.90, 1.00));
516            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(1.00, 1.00, 1.00, 1.00));
517        }
518        else if (colourcode == 1)
519        {
520            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.95, 0.25, 0.25, 1.00));
521            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(1.00, 0.50, 0.50, 1.00));
522        }
523        else if (colourcode == 2)
524        {
525            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.95, 0.50, 0.20, 1.00));
526            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(1.00, 0.70, 0.50, 1.00));
527        }
528        else if (colourcode == 3)
529        {
530            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.50, 0.50, 0.95, 1.00));
531            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(0.80, 0.80, 1.00, 1.00));
532        }
533        else if (colourcode == 4)
534        {
535            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.65, 0.48, 0.44, 1.00));
536            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(1.00, 0.90, 0.90, 1.00));
537        }
538        else if (colourcode == 5)
539        {
540            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.40, 0.20, 0.40, 1.00));
541            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(0.80, 0.60, 0.80, 1.00));
542        }
543        else
544        {
545            this->consoleOverlayTextAreas_[index]->setColourTop   (ColourValue(0.21, 0.69, 0.21, 1.00));
546            this->consoleOverlayTextAreas_[index]->setColourBottom(ColourValue(0.80, 1.00, 0.80, 1.00));
547        }
548    }
549
550    // ###############################
551    // ###      satic methods      ###
552    // ###############################
553
554    /**
555        @brief Activates the console.
556    */
557    /*static*/ void InGameConsole::openConsole()
558    {
559        InGameConsole::getInstance().activate();
560    }
561
562    /**
563        @brief Deactivates the console.
564    */
565    /*static*/ void InGameConsole::closeConsole()
566    {
567        InGameConsole::getInstance().deactivate();
568    }
569
570    /**
571        @brief Converts a string into an Ogre::UTFString.
572        @param s The string to convert
573        @return The converted string
574    */
575    /*static*/ Ogre::UTFString InGameConsole::convert2UTF(std::string s)
576    {
577        Ogre::UTFString utf;
578        Ogre::UTFString::code_point cp;
579        for (unsigned int i = 0; i < s.size(); ++i)
580        {
581          cp = s[i];
582          cp &= 0xFF;
583          utf.append(1, cp);
584        }
585        return utf;
586    }
587}
Note: See TracBrowser for help on using the repository browser.