Angel 3.2
A 2D Game Prototyping Engine
Console.cpp
1 
2 // Copyright (C) 2008-2013, Shane Liesegang
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of the copyright holder nor the names of any
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "stdafx.h"
31 #include "../Infrastructure/Console.h"
32 
33 #include "../Infrastructure/Common.h"
34 #include "../Infrastructure/Camera.h"
35 #include "../Infrastructure/Vector2.h"
36 #include "../Infrastructure/TextRendering.h"
37 #include "../Util/MathUtil.h"
38 #include "../Util/StringUtil.h"
39 
40 
41 #define MAX_AUTO_COMPLETE 7
42 
43 
45 : _enabled(false),
46 _currentInput(""),
47 _inputHistoryPos(0),
48 _cursorPos(0),
49 _cursorDispTime(0.0f),
50 _bCursorDisp(true),
51 _tabWidth(8)
52 {
53  if (!IsFontRegistered("Console") || !IsFontRegistered("ConsoleSmall"))
54  {
55  RegisterFont("Resources/Fonts/Inconsolata.otf", 24, "Console");
56  RegisterFont("Resources/Fonts/Inconsolata.otf", 18, "ConsoleSmall");
57  }
58 }
59 
61 {
62 
63 }
64 
65 void DrawTile( int xPos, int yPos, int width, int height )
66 {
67  Vec2i winDimensions;
68  winDimensions.X = theCamera.GetWindowWidth();
69  winDimensions.Y = theCamera.GetWindowHeight();
70  yPos = winDimensions.Y - yPos;
71 
72  //set up projection
73  glMatrixMode(GL_PROJECTION);
74  glPushMatrix();
75  glLoadIdentity();
76  gluOrtho2D(0, winDimensions.X, 0, winDimensions.Y);
77 
78  //set up modelview
79  glMatrixMode(GL_MODELVIEW);
80  glPushMatrix();
81  glLoadIdentity();
82  glTranslatef((GLfloat)(xPos), (GLfloat)(yPos), 0);
83 
84  glDisable(GL_DEPTH_TEST);
85 
86  // glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT);
87 
88  //Render console background
89  float vertices[] = {
90  (GLfloat)width, 0.0f,
91  (GLfloat)width, (GLfloat)height,
92  0.0f, 0.0f,
93  0.0f, (GLfloat)height,
94  };
95  glEnableClientState(GL_VERTEX_ARRAY);
96  glVertexPointer(2, GL_FLOAT, 0, vertices);
97  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
98 
99  // glPopAttrib();
100 
101  glEnable(GL_DEPTH_TEST);
102 
103  glPopMatrix();
104  glMatrixMode(GL_PROJECTION);
105  glPopMatrix();
106 }
107 
108 void Console::Enable(bool bEnable /* = true */)
109 {
110  _enabled = bEnable;
111 }
112 
113 bool Console::GetInput( int key )
114 {
115  if( !IsEnabled() )
116  return false;
117 
118  if (key == GetToggleConsoleKey())
119  {
120  Enable(false);
121  }
122  else if( IsTextKey(key) )
123  {
124  String oldInput = _currentInput;
125  _currentInput = oldInput.substr(0, _cursorPos);
126  _currentInput += key;
127 
128  if (_cursorPos < oldInput.length())
129  {
130  _currentInput += oldInput.substr(_cursorPos, oldInput.length());
131  }
132 
133  ++_cursorPos;
134 
135  RefreshAutoCompletes();
136  }
137 
138  return true;
139 }
140 
142 {
143  if( !IsEnabled() )
144  return false;
145 
146  if( key == GLFW_KEY_ESCAPE || key == GetToggleConsoleKey() )
147  {
148  Enable(false);
149  }
150  else if( key == GLFW_KEY_ENTER )
151  {
152  AcceptCurrentInput();
153  _autoCompleteList.clear();
154  }
155  else if( key == GLFW_KEY_TAB )
156  {
158  }
159  else if( key == GLFW_KEY_DELETE )
160  {
161  if (_cursorPos < _currentInput.length())
162  {
163  String oldInput = _currentInput;
164 
165  _currentInput = oldInput.substr( 0, _cursorPos );
166  _currentInput += oldInput.substr(_cursorPos+1, oldInput.length());
167 
168  RefreshAutoCompletes();
169  }
170 
171  }
172  else if( key == GLFW_KEY_BACKSPACE )
173  {
174  if( _cursorPos > 0 )
175  {
176  String oldInput = _currentInput;
177 
178  _currentInput = oldInput.substr( 0, _cursorPos-1 );
179 
180  if (_cursorPos < oldInput.length())
181  {
182  _currentInput += oldInput.substr(_cursorPos, oldInput.length());
183  }
184 
185  --_cursorPos;
186 
187  RefreshAutoCompletes();
188  }
189  }
190  else if( key == GLFW_KEY_UP )
191  {
192  AdvanceInputHistory( -1 );
193  }
194  else if( key == GLFW_KEY_DOWN )
195  {
196  AdvanceInputHistory( 1 );
197  }
198  else if( key == GLFW_KEY_RIGHT )
199  {
200  if (_cursorPos < _currentInput.length())
201  {
202  ++_cursorPos;
203  }
204  }
205  else if( key == GLFW_KEY_LEFT )
206  {
207  if (_cursorPos > 0)
208  {
209  --_cursorPos;
210  }
211  }
212  else if( key == GLFW_KEY_END )
213  {
214  _cursorPos = _currentInput.length();
215  }
216  else if( key == GLFW_KEY_HOME )
217  {
218  _cursorPos = 0;
219  }
220  //TODO: Restore
221  //else if( key == GLFW_KEY_PAGEUP )
222  //{
223  // AdvanceConsoleLog(-1);
224  //}
225  //else if( key == GLFW_KEY_PAGEDOWN )
226  //{
227  // AdvanceConsoleLog(1);
228  //}
229 
230  return true;
231 
232 }
233 
234 void Console::RefreshAutoCompletes()
235 {
236  if (_currentInput.size() > 2)
237  {
238  _autoCompleteList = GetCompletions(_currentInput);
239  }
240  else
241  {
242  _autoCompleteList.clear();
243  }
244 }
245 
246 void Console::WriteToOutput(String output)
247 {
248  // convert tabs first
249  size_t tabIndex = output.find_first_of('\t');
250  while (tabIndex != std::string::npos)
251  {
252  int numSpaces = _tabWidth - (tabIndex % _tabWidth);
253  String replacement = "";
254  for(int i=0; i < numSpaces; i++)
255  {
256  replacement += " ";
257  }
258  output = output.substr(0, tabIndex) + replacement + output.substr(tabIndex + 1, output.size() - 1);
259  tabIndex = output.find_first_of('\t');
260  }
261 
262 
263  _unsplitBuffer += output;
264  _buffer = SplitString(_unsplitBuffer, "\n", false);
265 
266  float largest = 0.0f;
267  StringList::iterator it = _buffer.begin();
268  Vector2 extents = Vector2::Zero;
269  while(it != _buffer.end())
270  {
271  extents = GetTextExtents((*it), "ConsoleSmall");
272  if (extents.Y > largest)
273  {
274  largest = extents.Y;
275  }
276 
277  it++;
278  }
279  _lineHeight = largest;
280 }
281 
282 bool Console::IsTextKey(unsigned char key)
283 {
284  if( key >= ' ' && key <= '}' )
285  return true;
286 
287  return false;
288 }
289 
290 void Console::AcceptCurrentInput()
291 {
292  Execute(_currentInput);
293  if (_currentInput.size() > 0)
294  {
295  _inputHistory.push_back(_currentInput);
296  _inputHistoryPos = _inputHistory.size();
297  }
298  _currentInput = "";
299  _cursorPos = 0;
300 }
301 
303 {
304  if( _autoCompleteList.size() == 0 )
305  return;
306 
307  //cycle through the available completions with tab
308  int found = -1;
309  for (int i=0; i < _autoCompleteList.size(); i++)
310  {
311  if (i > MAX_AUTO_COMPLETE)
312  {
313  break;
314  }
315  if (_currentInput == _autoCompleteList[i])
316  {
317  //found our existing match, try to cycle to the next one
318  if ( (i == _autoCompleteList.size() - 1) || (i == MAX_AUTO_COMPLETE - 2) )
319  {
320  //found it, but at the end of the list, so cycle to top
321  break;
322  }
323  //set the found index to the next line
324  found = i+1;
325  break;
326  }
327  }
328 
329  if (-1 == found)
330  {
331  found = 0;
332  }
333 
334  _currentInput = _autoCompleteList[found];
335  _cursorPos = _currentInput.length();
336 }
337 
339 {
340  //If we have no input history, ignore this
341  if( _inputHistory.size() == 0 )
342  return;
343 
344  //If we're at the bottom of our input history, do nothing
345  int lastInputIndex = _inputHistory.size();
346  if( byVal >= 0 && (_inputHistoryPos + byVal) >= lastInputIndex )
347  {
348  _currentInput = "";
349  _cursorPos = 0;
350  _inputHistoryPos = lastInputIndex;
351  return;
352  }
353 
354 
355  _inputHistoryPos += byVal;
356  if( _inputHistoryPos > lastInputIndex )
357  _inputHistoryPos = lastInputIndex;
358  else if( _inputHistoryPos < 0 )
359  _inputHistoryPos = 0;
360 
361  //otherwise, write over our current input
362  _currentInput = _inputHistory[_inputHistoryPos];
363  _cursorPos = _currentInput.length();
364 }
365 
367 {
368  Enable( !IsEnabled() );
369 }
370 
371 void Console::SetPrompt(const String& prompt)
372 {
373  _prompt = prompt;
374 }
375 
376 void Console::SetTabWidth(unsigned int newTabWidth)
377 {
378  _tabWidth = newTabWidth;
379 }
380 
381 const unsigned int Console::GetTabWidth()
382 {
383  return _tabWidth;
384 }
385 
386 void Console::Update( float dt )
387 {
388  static const float CURSOR_DISPLAY_TIME = 0.5f;
389 
390  _cursorDispTime += dt;
391 
392  if (_cursorDispTime > CURSOR_DISPLAY_TIME)
393  {
394  _cursorDispTime = 0.0f;
395  _bCursorDisp = !_bCursorDisp;
396  }
397 }
398 
400 {
401  if( !IsEnabled() )
402  return;
403 
404  //TODO: Clean up this nonsense
405 
406  static float sTestAlpha = 0.75;
407  Vec2i winDimensions;
408  winDimensions.X = theCamera.GetWindowWidth();
409  winDimensions.Y = theCamera.GetWindowHeight();
410 
411  static float fScreenHeightPct = 0.5f;
412  static int sTextBoxBorder = winDimensions.Y/192;
413  static int sTextBoxHeight = winDimensions.Y/32 + sTextBoxBorder;
414 
415  int consoleBGHeight = (int)(fScreenHeightPct * (float)winDimensions.Y);
416 
417  int consoleBGBottomY = consoleBGHeight;
418 
419  glColor4f(0.0f,0.0f,0.0f,sTestAlpha);
420  DrawTile(0, consoleBGBottomY, winDimensions.X, consoleBGHeight );
421 
422  //Draw log
423  static int sLogXPos = sTextBoxBorder;
424  int logYPos = consoleBGBottomY - sTextBoxBorder;
425  if (_buffer.size() > 0)
426  {
427  glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
428  StringList::iterator it = _buffer.end();
429  while (it != _buffer.begin())
430  {
431  it--;
432  /* Vector2 textSize = */ DrawGameText( *it, "ConsoleSmall", sLogXPos, logYPos );
433  logYPos -= (int)_lineHeight + sTextBoxBorder;
434  }
435  }
436 
437  //Draw text box border
438  glColor4f(0.0f,1.0f,0.0f,sTestAlpha/2.0f);
439  int textBoxBottomY = consoleBGBottomY + sTextBoxHeight;
440  DrawTile(0, textBoxBottomY, winDimensions.X, sTextBoxHeight );
441 
442  //Draw text box
443 
444  int textBoxHeight = sTextBoxHeight - sTextBoxBorder;
445  int textBoxWidth = winDimensions.X - sTextBoxBorder;
446  int textBoxXPos = (winDimensions.X - textBoxWidth)/2;
447  int textBoxYPos = textBoxBottomY - (sTextBoxHeight-textBoxHeight)/2;
448 
449  glColor4f(0.0f,0.0f,0.0f,sTestAlpha);
450  DrawTile(textBoxXPos, textBoxYPos, textBoxWidth, textBoxHeight);
451 
452  textBoxXPos += sTextBoxBorder;
453  textBoxYPos -= sTextBoxBorder + (sTextBoxBorder/2);
454 
455  glColor4f(0.0f,1.0f,0.0f,1.0f);
456  String printInput = _prompt;
457  printInput += _currentInput.substr(0, _cursorPos);
458 
459  if(_bCursorDisp)
460  {
461  printInput += "|";
462  }
463  else
464  {
465  printInput += " ";
466  }
467 
468  if (_cursorPos < _currentInput.length())
469  {
470  printInput += _currentInput.substr(_cursorPos, _currentInput.length());
471  }
472 
473  DrawGameText(printInput.c_str(), "ConsoleSmall", textBoxXPos, textBoxYPos);
474 
475  //Draw autocomplete
476  static int sMaxAutoCompleteLines = MAX_AUTO_COMPLETE;
477  int numAutoCompleteLines = MathUtil::Min(sMaxAutoCompleteLines, (int)_autoCompleteList.size() );
478  int autoCompleteBottomY = textBoxBottomY + (numAutoCompleteLines * sTextBoxHeight);
479  int autoCompleteStartY = textBoxBottomY + 2*sTextBoxHeight/3;
480 
481  int autoCompleteXPos = textBoxXPos + winDimensions.Y / 24;
482  int autoCompleteBoxXPos = autoCompleteXPos - sTextBoxBorder;
483 
484  glColor4f(0.0f,0.0f,0.0f,sTestAlpha);
485  DrawTile(autoCompleteBoxXPos, autoCompleteBottomY, winDimensions.X-autoCompleteBoxXPos, numAutoCompleteLines * sTextBoxHeight);
486 
487  glColor4f(0.0f,1.0f,0.0f,1.0f);
488  Vector2 outPos((float)autoCompleteXPos, (float)autoCompleteStartY);
489  for( int i = 0; i < numAutoCompleteLines; i++ )
490  {
491  if( (int)_autoCompleteList.size() > sMaxAutoCompleteLines-1 && i == sMaxAutoCompleteLines-1 )
492  DrawGameText( "...", "ConsoleSmall", autoCompleteXPos, (int)outPos.Y );
493  else
494  DrawGameText( _autoCompleteList[i].c_str(), "ConsoleSmall", autoCompleteXPos, (int)outPos.Y );
495  outPos.Y += sTextBoxHeight;
496  }
497 
498 
499 }
500