13 constexpr
float ChoiceHeight = 25.f;
14 constexpr
float ChoicePadding = 8.f;
15 constexpr
float TextPadding = 10.f;
16 constexpr
float FlashOn = 0.75f;
17 constexpr
float FlashOff = 0.65f;
19 bool isNextCommand(
unsigned int cmd) {
28 , inputListener(*this)
29 , screenKeyboard(owner.engine(), std::bind(&
HUD::keyboardSubmit, this, std::placeholders::_1))
30 , entryCard(owner.engine())
31 , currentOverlay(nullptr)
32 , qtyEntry(owner.engine()) {}
34 void HUD::init(bl::engine::Engine&) {
38 const glm::vec2& boxSize = textboxTxtr->size();
39 textbox.create(owner.
engine(), textboxTxtr);
42 textbox.getTransform().setDepth(bl::cam::OverlayCamera::MinDepth);
43 choiceBoxX = boxSize.x * 2.f * 0.75f + 3.f;
47 displayText.getTransform().setPosition(TextPadding, 8.f);
48 displayText.wordWrap(textboxTxtr->size().x - TextPadding * 2.f);
49 displayText.setParent(textbox);
51 promptTriangle.create(owner.
engine(), {0.f, 0.f}, {12.f, 5.5f}, {0.f, 11.f});
52 promptTriangle.setFillColor(sf::Color(255, 77, 0));
53 promptTriangle.setOutlineColor(sf::Color(255, 238, 128, 185));
54 promptTriangle.setOutlineThickness(1.5f);
55 promptTriangle.getTransform().setPosition(boxSize - glm::vec2(18.f, 14.f));
56 promptTriangle.setParent(textbox);
58 choiceMenu.create(owner.
engine(),
59 owner.
engine().renderer().getObserver(),
60 bl::menu::ArrowSelector::create(10.f, sf::Color::Black));
61 choiceMenu.setPadding({0.f, ChoicePadding});
62 choiceMenu.setMinHeight(ChoiceHeight);
63 choiceMenu.configureBackground(sf::Color::White, sf::Color::Black, 2.f, {18.f, 2.f, 4.f, 4.f});
66 {textbox.getTransform().getLocalPosition().x + textboxTxtr->size().x - 50.f,
67 textbox.getTransform().getLocalPosition().y - 70.f});
70 void HUD::update(std::mutex&,
float dt,
float,
float,
float) {
73 if (currentMessage.update(dt)) {
74 displayText.getSection().setString(std::string(currentMessage.getVisible()));
76 if (currentMessage.finished()) printDoneStateTransition();
93 queuedOutput.emplace(msg,
false,
true, cb);
98 queuedOutput.emplace(msg,
true, ghost, cb);
103 if (queuedOutput.empty())
return false;
104 if (queuedOutput.front().getType() != Item::Message)
return false;
105 if (!queuedOutput.front().isSticky())
return false;
106 if (!ignoreGhost && !currentMessage.finished())
return false;
112 void HUD::promptUser(
const std::string& prompt,
const std::vector<std::string>& choices,
114 queuedOutput.emplace(prompt, choices, cb);
120 queuedOutput.emplace(prompt, mn, mx, cb);
125 queuedOutput.emplace(prompt, minQty, maxQty, cb);
133 void HUD::ensureActive() {
134 if (state == Hidden && !queuedOutput.empty()) {
135 owner.
engine().inputSystem().getActor().addListener(inputListener);
140 void HUD::startPrinting() {
142 currentMessage.setContent(queuedOutput.front().getMessage());
143 if (!queuedOutput.front().ghostWrite()) { currentMessage.showAll(); }
144 displayText.getSection().setString(std::string{currentMessage.getVisible()});
147 void HUD::printDoneStateTransition() {
148 switch (queuedOutput.front().getType()) {
150 if (!queuedOutput.front().isSticky()) { setState(WaitingContinue); }
152 setState(WaitingSticky);
153 queuedOutput.front().getCallback()(queuedOutput.front().getMessage());
158 const std::vector<std::string>& choices = queuedOutput.front().getChoices();
159 bl::menu::Item::Ptr mitem =
160 bl::menu::TextItem::create(choices.empty() ?
"INVALID" : choices.front(),
164 mitem->getSignal(bl::menu::Item::Activated)
165 .willAlwaysCall(std::bind(&HUD::choiceMade,
this, 0));
166 choiceMenu.setRootItem(mitem);
168 bl::menu::Item* prev = mitem.get();
169 for (
unsigned int i = 1; i < choices.size(); ++i) {
170 mitem = bl::menu::TextItem::create(
172 mitem->getSignal(bl::menu::Item::Activated)
173 .willAlwaysCall(std::bind(&HUD::choiceMade,
this, i));
174 choiceMenu.addItem(mitem, prev, bl::menu::Item::Bottom);
177 const sf::FloatRect bounds = choiceMenu.getBounds();
179 choiceMenu.setPosition({choiceBoxX + 18.f, y + 2.f});
180 choiceDriver.drive(&choiceMenu);
181 setState(WaitingPrompt);
185 setState(WaitingKeyboard);
186 screenKeyboard.
start(queuedOutput.front().minInputLength(),
187 queuedOutput.front().maxInputLength());
190 textbox.getTransform().getLocalPosition().y - screenKeyboard.
getSize().y - 2.f});
194 setState(WaitingQty);
195 qtyEntry.
configure(queuedOutput.front().getMinQty(), queuedOutput.front().getMaxQty(), 1);
205 void HUD::choiceMade(
unsigned int i) {
206 queuedOutput.front().getCallback()(queuedOutput.front().getChoices()[i]);
207 choiceDriver.drive(
nullptr);
211 void HUD::qtySelected(
int qty) {
212 queuedOutput.front().getQtyCallback()(qty);
218 if (!queuedOutput.empty()) { startPrinting(); }
221 owner.
engine().inputSystem().getActor().removeListener(inputListener);
225 void HUD::keyboardSubmit(
const std::string& i) {
226 queuedOutput.front().getCallback()(i);
227 screenKeyboard.
stop();
231 HUD::HudListener::HudListener(HUD& o)
234 bool HUD::HudListener::observe(
const bl::input::Actor&,
unsigned int ctrl, bl::input::DispatchType,
235 bool eventTriggered) {
236 switch (owner.state) {
238 if (isNextCommand(ctrl) && eventTriggered) {
239 owner.currentMessage.showAll();
240 owner.displayText.getSection().setString(owner.currentMessage.getContent());
241 owner.printDoneStateTransition();
245 case WaitingContinue:
246 if (isNextCommand(ctrl) && eventTriggered) {
247 owner.queuedOutput.front().getCallback()(owner.queuedOutput.front().getMessage());
253 owner.choiceDriver.sendControl(ctrl, eventTriggered);
256 case WaitingKeyboard:
257 owner.screenKeyboard.process(ctrl, eventTriggered);
263 owner.qtyEntry.down(1, eventTriggered);
266 owner.qtyEntry.up(1, eventTriggered);
269 owner.qtyEntry.up(10, eventTriggered);
272 owner.qtyEntry.down(10, eventTriggered);
275 owner.qtySelected(owner.qtyEntry.curQty());
278 owner.qtySelected(0);
285 case HUD::WaitingSticky:
290 BL_LOG_WARN <<
"Input received by HUD while in invalid state: " << owner.state;
297 void HUD::setState(State ns) {
298 textbox.setHidden(
false);
299 if (state == WaitingKeyboard && ns != WaitingKeyboard) { screenKeyboard.
stop(); }
300 if (state == WaitingQty && ns != WaitingQty) { qtyEntry.
hide(); }
301 if (ns != WaitingContinue) { promptTriangle.stopFlashing(); }
302 if (state == Hidden || !currentOverlay ||
303 owner.
engine().renderer().getObserver().getOrCreateSceneOverlay() != currentOverlay) {
304 currentOverlay = owner.
engine().renderer().getObserver().getOrCreateSceneOverlay();
305 textbox.addToScene(currentOverlay, bl::rc::UpdateSpeed::Static);
306 displayText.addToScene(currentOverlay, bl::rc::UpdateSpeed::Static);
307 promptTriangle.addToScene(currentOverlay, bl::rc::UpdateSpeed::Static);
309 if (state == WaitingPrompt && ns != WaitingPrompt) { choiceMenu.removeFromScene(); }
314 textbox.setHidden(
true);
315 textbox.removeFromScene();
316 displayText.removeFromScene();
317 promptTriangle.removeFromScene();
318 choiceMenu.removeFromScene();
319 currentOverlay =
nullptr;
321 case WaitingContinue:
322 promptTriangle.flash(FlashOn, FlashOff);
325 choiceMenu.addToOverlay();
330 case WaitingKeyboard:
337 HUD::Item::Item(
const std::string& msg,
bool sticky,
bool ghost,
const HUD::Callback& cb)
341 , data(std::in_place_type_t<std::pair<bool, bool>>{}, sticky, ghost) {}
343 HUD::Item::Item(
const std::string& p,
const std::vector<std::string>& c,
const HUD::Callback& cb)
347 , data(std::in_place_type_t<std::vector<std::string>>{}, c) {}
349 HUD::Item::Item(
const std::string& prompt,
unsigned int minLen,
unsigned int maxLen,
354 , data(std::in_place_type_t<std::pair<unsigned int, unsigned int>>{}, minLen, maxLen) {}
356 HUD::Item::Item(
const std::string& prompt,
int minQty,
int maxQty,
const QtyCallback& cb)
360 , data(std::in_place_type_t<std::pair<int, int>>{}, minQty, maxQty) {}
364 const std::string& HUD::Item::getMessage()
const {
return message; }
366 bool HUD::Item::isSticky()
const {
return std::get_if<std::pair<bool, bool>>(&data)->first; }
368 bool HUD::Item::ghostWrite()
const {
369 const auto* p = std::get_if<std::pair<bool, bool>>(&data);
370 return p ? p->second :
true;
373 const std::vector<std::string>& HUD::Item::getChoices()
const {
374 return *std::get_if<std::vector<std::string>>(&data);
377 unsigned int HUD::Item::minInputLength()
const {
378 return std::get_if<std::pair<unsigned int, unsigned int>>(&data)->first;
381 unsigned int HUD::Item::maxInputLength()
const {
382 return std::get_if<std::pair<unsigned int, unsigned int>>(&data)->second;
385 int HUD::Item::getMinQty()
const {
return std::get_if<std::pair<int, int>>(&data)->first; }
387 int HUD::Item::getMaxQty()
const {
return std::get_if<std::pair<int, int>>(&data)->second; }
389 const HUD::Callback& HUD::Item::getCallback()
const {
return *std::get_if<Callback>(&cb); }
391 const HUD::QtyCallback& HUD::Item::getQtyCallback()
const {
return *std::get_if<QtyCallback>(&cb); }
393 HUD::EntryCard::EntryCard(bl::engine::Engine& engine)
395 , currentOverlay(nullptr) {}
397 void HUD::EntryCard::ensureCreated() {
399 txtr = engine.renderer().texturePool().getOrLoadTexture(
401 card.create(engine, txtr);
403 text.setParent(card);
404 text.wordWrapToParent(0.98f);
407 if (!currentOverlay ||
408 engine.renderer().getObserver().getOrCreateSceneOverlay() != currentOverlay) {
409 currentOverlay = engine.renderer().getObserver().getOrCreateSceneOverlay();
410 card.addToScene(currentOverlay, bl::rc::UpdateSpeed::Dynamic);
411 text.addToScene(currentOverlay, bl::rc::UpdateSpeed::Dynamic);
415 void HUD::EntryCard::update(
float dt) {
416 constexpr
float MoveSpeed = 150.f;
417 constexpr
float HoldTime = 2.5f;
421 stateVar -= MoveSpeed * dt;
422 card.getTransform().setPosition(0.f, -stateVar);
423 if (stateVar <= 0.f) {
426 card.getTransform().setPosition(0.f, 0.f);
431 stateVar += MoveSpeed * dt;
432 card.getTransform().setPosition(0.f, -stateVar);
433 if (stateVar >= txtr->size().y) { hide(); }
438 if (stateVar >= HoldTime) {
450 void HUD::EntryCard::display(
const std::string& t) {
451 constexpr
float Padding = 10.f;
454 text.getSection().setString(t);
455 text.getTransform().setPosition(txtr->size() * 0.5f - text.getLocalSize() * 0.5f);
458 stateVar = txtr->size().y;
461 void HUD::EntryCard::hide() {
462 if (state != State::Hidden) {
463 state = State::Hidden;
464 card.removeFromScene();
Type
The type classification of an item. This is used to determine when an item may be used and how to use...
Core classes and functionality for both the editor and game.
static const sf::VulkanFont & MenuFont()
static const std::string & MenuImagePath()
static sf::Vector2f WindowSize()
static const std::string & TextboxFile()
static unsigned int HudFontSize()
void configure(int minQty, int maxQty, int qty)
Configures the entry with the given parameters and adds to the overlay.
void setPosition(const sf::Vector2f &position)
Sets the position of the selector.
void hide()
Removes the quantity entry from the overlay.
sf::Vector2f getSize() const
Returns the size of the screen keyboard.
void start(unsigned int minLen=0, unsigned int maxLen=16)
Subscribes the keyboard to the event bus.
void setPosition(const sf::Vector2f &position)
Sets the position of the keyboard. The default is (??)
void stop()
Unsubscribes the keyboard from the event bus.
The primary HUD system for the player. Manages displaying messages and asking questions....
void displayEntryCard(const std::string &name)
Displays a card to indicate entering a new town, route, or map.
void hideEntryCard()
Hides the entry card.
std::function< void(const std::string &value)> Callback
Signature for HUD callbacks. Used for both messages completing and choices made.
void displayMessage(const std::string &message, const Callback &cb=[](const std::string &) {})
Displays a message in the HUD textbox. Messages are queued in order that they arrive.
void getQty(const std::string &prompt, int minQty, int maxQty, const QtyCallback &cb)
Gets a number from the player.
void promptUser(const std::string &prompt, const std::vector< std::string > &choices, const Callback &cb)
Asks the player a question through the HUD.
std::function< void(int qty)> QtyCallback
Called when a qty is entered.
void displayStickyMessage(const std::string &message, bool ghostWrite, const Callback &cb=[](const std::string &) {})
Displays a message in the HUD textbox. Sticky messages stay displayed until programmatically dismisse...
void getInputString(const std::string &prompt, unsigned int minLen, unsigned int maxLen, const Callback &cb)
Prompts the player to input a string using the screen keyboard.
HUD(Systems &owner)
Construct a new HUD system.
bool dismissStickyMessage(bool ignoreGhostWrite=true)
Dismisses the currently active sticky message.
Owns all primary systems and a reference to the engine.
const bl::engine::Engine & engine() const
Const accessor for the Engine.