1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-14 10:12:59 +02:00
vcmi/client/adventureMap/CInGameConsole.cpp
Alexander Wilms 02e429e973 Fix typos using https://github.com/crate-ci/typos
Changes were reviewed manually
2024-06-24 03:47:19 +02:00

334 lines
7.8 KiB
C++

/*
* CInGameConsole.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "CInGameConsole.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../CServerHandler.h"
#include "../GameChatHandler.h"
#include "../ClientCommandManager.h"
#include "../gui/CGuiHandler.h"
#include "../gui/WindowHandler.h"
#include "../gui/Shortcut.h"
#include "../gui/TextAlignment.h"
#include "../media/ISoundPlayer.h"
#include "../render/Colors.h"
#include "../render/Canvas.h"
#include "../render/IScreenHandler.h"
#include "../adventureMap/AdventureMapInterface.h"
#include "../windows/CMessage.h"
#include "../../CCallback.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/CThreadHelper.h"
#include "../../lib/TextOperations.h"
#include "../../lib/mapObjects/CArmedInstance.h"
#include "../../lib/MetaString.h"
CInGameConsole::CInGameConsole()
: CIntObject(KEYBOARD | TIME | TEXTINPUT)
, prevEntDisp(-1)
{
setRedrawParent(true);
}
void CInGameConsole::showAll(Canvas & to)
{
show(to);
}
void CInGameConsole::show(Canvas & to)
{
if (LOCPLINT->cingconsole != this)
return;
int number = 0;
for(auto & text : texts)
{
Point leftBottomCorner(0, pos.h);
Point textPosition(leftBottomCorner.x + 50, leftBottomCorner.y - texts.size() * 20 - 80 + number * 20);
to.drawText(pos.topLeft() + textPosition, FONT_MEDIUM, Colors::GREEN, ETextAlignment::TOPLEFT, text.text);
number++;
}
}
void CInGameConsole::tick(uint32_t msPassed)
{
// Check whether text input is active - we want to keep recent messages visible during this period
if(isEnteringText())
return;
size_t sizeBefore = texts.size();
for(auto & text : texts)
text.timeOnScreen += msPassed;
vstd::erase_if(
texts,
[&](const auto & value)
{
return value.timeOnScreen > defaultTimeout;
}
);
if(sizeBefore != texts.size())
GH.windows().totalRedraw(); // FIXME: ingame console has no parent widget set
}
void CInGameConsole::addMessageSilent(const std::string & timeFormatted, const std::string & senderName, const std::string & messageText)
{
MetaString formatted = MetaString::createFromRawString("[%s] %s: %s");
formatted.replaceRawString(timeFormatted);
formatted.replaceRawString(senderName);
formatted.replaceRawString(messageText);
// Maximum width for a text line is limited by:
// 1) width of adventure map terrain area, for when in-game console is on top of advmap
// 2) width of castle/battle window (fixed to 800) when this window is open
// 3) arbitrary selected left and right margins
int maxWidth = std::min( 800, adventureInt->terrainAreaPixels().w) - 100;
auto splitText = CMessage::breakText(formatted.toString(), maxWidth, FONT_MEDIUM);
for(const auto & entry : splitText)
texts.push_back({entry, 0});
while(texts.size() > maxDisplayedTexts)
texts.erase(texts.begin());
}
void CInGameConsole::addMessage(const std::string & timeFormatted, const std::string & senderName, const std::string & messageText)
{
addMessageSilent(timeFormatted, senderName, messageText);
GH.windows().totalRedraw(); // FIXME: ingame console has no parent widget set
int volume = CCS->soundh->getVolume();
if(volume == 0)
CCS->soundh->setVolume(settings["general"]["sound"].Integer());
int handle = CCS->soundh->playSound(AudioPath::builtin("CHAT"));
if(volume == 0)
CCS->soundh->setCallback(handle, [&]() { if(!GH.screenHandler().hasFocus()) CCS->soundh->setVolume(0); });
}
bool CInGameConsole::captureThisKey(EShortcut key)
{
if (!isEnteringText())
return false;
switch (key)
{
case EShortcut::GLOBAL_ACCEPT:
case EShortcut::GLOBAL_CANCEL:
case EShortcut::GAME_ACTIVATE_CONSOLE:
case EShortcut::GLOBAL_BACKSPACE:
case EShortcut::MOVE_UP:
case EShortcut::MOVE_DOWN:
return true;
default:
return false;
}
}
void CInGameConsole::keyPressed (EShortcut key)
{
if (LOCPLINT->cingconsole != this)
return;
if(!isEnteringText() && key != EShortcut::GAME_ACTIVATE_CONSOLE)
return; //because user is not entering any text
switch(key)
{
case EShortcut::GLOBAL_CANCEL:
if(!enteredText.empty())
endEnteringText(false);
break;
case EShortcut::GAME_ACTIVATE_CONSOLE:
if(!enteredText.empty())
endEnteringText(false);
else
startEnteringText();
break;
case EShortcut::GLOBAL_ACCEPT:
{
if(!enteredText.empty())
{
bool anyTextExceptCaret = enteredText.size() > 1;
endEnteringText(anyTextExceptCaret);
}
break;
}
case EShortcut::GLOBAL_BACKSPACE:
{
if(enteredText.size() > 1)
{
TextOperations::trimRightUnicode(enteredText,2);
enteredText += '_';
refreshEnteredText();
}
break;
}
case EShortcut::MOVE_UP:
{
if(previouslyEntered.empty())
break;
if(prevEntDisp == -1)
{
prevEntDisp = static_cast<int>(previouslyEntered.size() - 1);
enteredText = previouslyEntered[prevEntDisp] + "_";
refreshEnteredText();
}
else if( prevEntDisp > 0)
{
--prevEntDisp;
enteredText = previouslyEntered[prevEntDisp] + "_";
refreshEnteredText();
}
break;
}
case EShortcut::MOVE_DOWN:
{
if(prevEntDisp != -1 && prevEntDisp+1 < previouslyEntered.size())
{
++prevEntDisp;
enteredText = previouslyEntered[prevEntDisp] + "_";
refreshEnteredText();
}
else if(prevEntDisp+1 == previouslyEntered.size()) //useful feature
{
prevEntDisp = -1;
enteredText = "_";
refreshEnteredText();
}
break;
}
}
}
void CInGameConsole::textInputted(const std::string & inputtedText)
{
if (LOCPLINT->cingconsole != this)
return;
if(!isEnteringText())
return;
enteredText.resize(enteredText.size()-1);
enteredText += inputtedText;
enteredText += "_";
refreshEnteredText();
}
void CInGameConsole::textEdited(const std::string & inputtedText)
{
//do nothing here
}
void CInGameConsole::showRecentChatHistory()
{
auto const & history = CSH->getGameChat().getChatHistory();
texts.clear();
int entriesToShow = std::min<int>(maxDisplayedTexts, history.size());
int firstEntryToShow = history.size() - entriesToShow;
for (int i = firstEntryToShow; i < history.size(); ++i)
addMessageSilent(history[i].dateFormatted, history[i].senderName, history[i].messageText);
GH.windows().totalRedraw();
}
void CInGameConsole::startEnteringText()
{
if (!isActive())
return;
if(isEnteringText())
{
// force-reset text input to re-show on-screen keyboard
GH.statusbar()->setEnteringMode(false);
GH.statusbar()->setEnteringMode(true);
GH.statusbar()->setEnteredText(enteredText);
return;
}
assert(currentStatusBar.expired());//effectively, nullptr check
currentStatusBar = GH.statusbar();
enteredText = "_";
GH.statusbar()->setEnteringMode(true);
GH.statusbar()->setEnteredText(enteredText);
showRecentChatHistory();
}
void CInGameConsole::endEnteringText(bool processEnteredText)
{
prevEntDisp = -1;
if(processEnteredText)
{
std::string txt = enteredText.substr(0, enteredText.size()-1);
previouslyEntered.push_back(txt);
if(txt.at(0) == '/')
{
//some commands like gosolo don't work when executed from GUI thread
auto threadFunction = [=]()
{
setThreadName("processCommand");
ClientCommandManager commandController;
commandController.processCommand(txt.substr(1), true);
};
boost::thread clientCommandThread(threadFunction);
clientCommandThread.detach();
}
else
CSH->getGameChat().sendMessageGameplay(txt);
}
enteredText.clear();
auto statusbar = currentStatusBar.lock();
assert(statusbar);
if (statusbar)
statusbar->setEnteringMode(false);
currentStatusBar.reset();
}
void CInGameConsole::refreshEnteredText()
{
auto statusbar = currentStatusBar.lock();
assert(statusbar);
if (statusbar)
statusbar->setEnteredText(enteredText);
}
bool CInGameConsole::isEnteringText() const
{
return !enteredText.empty();
}