Mana
Loading...
Searching...
No Matches
npcdialog.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * 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, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "gui/npcdialog.h"
23
24#include "client.h"
25#include "configuration.h"
26#include "event.h"
27#include "eventlistener.h"
28#include "playerinfo.h"
29
30#include "gui/npcpostdialog.h"
31
33#include "gui/widgets/button.h"
36#include "gui/widgets/layout.h"
37#include "gui/widgets/listbox.h"
40
41#include "net/net.h"
42#include "net/npchandler.h"
43
44#include "utils/gettext.h"
45#include "utils/stringutils.h"
46
47#include <guichan/font.hpp>
48
49#define CAPTION_WAITING _("Waiting for server")
50#define CAPTION_NEXT _("Next")
51#define CAPTION_CLOSE _("Close")
52#define CAPTION_SUBMIT _("Submit")
53
55{
56public:
57 void event(Event::Channel channel, const Event &event) override;
58
59 NpcDialog *getDialog(int id, bool make = true);
60
61 void removeDialog(int id);
62
63private:
64 std::map<int, NpcDialog *> mNpcDialogs;
65};
66
67static NpcEventListener *npcListener = nullptr;
68
70
72 : Window(_("NPC"))
73 , mNpcId(npcId)
74 , mItemLinkHandler(std::make_unique<ItemLinkHandler>(this))
75{
76 // Basic Window Setup
77 setWindowName("NpcText");
78 setResizable(true);
79 setCloseButton(false);
80 setFocusable(true);
81
82 setMinWidth(200);
83 setMinHeight(150);
84
86
87 // Setup output text box
92
94 mScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
95 mScrollArea->setVerticalScrollPolicy(gcn::ScrollArea::SHOW_ALWAYS);
96
97 // Setup listbox
98 mItemList = new ListBox(this);
99 mItemList->setWrappingEnabled(true);
100 setContentSize(260, 175);
101
103 mListScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
104
105 mItemList->setVisible(true);
106
107 // Setup string input box
108 mTextField = new TextField;
109 mTextField->setVisible(true);
110
111 // Setup int input box
113 mIntField->setVisible(true);
114
115 mClearButton = new Button(_("Clear log"), "clear", this);
116
117 // Setup button
118 mNextButton = new Button("", "ok", this);
119
120 //Setup more and less buttons (int input)
121 mPlusButton = new Button(_("+"), "inc", this);
122 mMinusButton = new Button(_("-"), "dec", this);
123
124 int width = std::max(mNextButton->getFont()->getWidth(CAPTION_WAITING),
125 mNextButton->getFont()->getWidth(CAPTION_NEXT));
126 width = std::max(width, mNextButton->getFont()->getWidth(CAPTION_CLOSE));
127 width = std::max(width, mNextButton->getFont()->getWidth(CAPTION_SUBMIT));
128
129 mNextButton->setWidth(8 + width);
130
131 mResetButton = new Button(_("Reset"), "reset", this);
132
133 // Place widgets
134 buildLayout();
135
136 center();
138
139 instances.push_back(this);
140 setVisible(true);
141 requestFocus();
142
144 + 1);
145}
146
148{
149 // These might not actually be in the layout, so lets be safe
150 delete mScrollArea;
151 delete mItemList;
152 delete mTextField;
153 delete mIntField;
154 delete mResetButton;
155 delete mPlusButton;
156 delete mMinusButton;
157 delete mNextButton;
158
159 instances.remove(this);
160
162 - 1);
163
164 npcListener->removeDialog(mNpcId);
165}
166
167void NpcDialog::setText(const std::vector<std::string> &text)
168{
170 for (const std::string &row : text)
171 mTextBox->addRow(row);
172}
173
174void NpcDialog::addText(const std::string &text, bool save)
175{
176 if (save || config.logNpcInGui)
177 {
178 mNewText.push_back(text);
179 mTextBox->addRow(text);
180 }
181 mScrollArea->setVerticalScrollAmount(mScrollArea->getVerticalMaxScroll());
183 buildLayout();
184}
185
191
197
198void NpcDialog::action(const gcn::ActionEvent &event)
199{
200 if (event.getId() == "ok")
201 {
203 {
204 nextDialog();
205 addText(std::string(), false);
206 }
207 else if (mActionState == NPC_ACTION_CLOSE)
208 {
209 close();
210 }
211 else if (mActionState == NPC_ACTION_INPUT)
212 {
213 std::string printText; // Text that will get printed in the textbox
214
216 {
217 int selectedIndex = mItemList->getSelected();
218
219 if (selectedIndex >= (int) mItems.size() || selectedIndex < 0)
220 return;
221
222 printText = mItems[selectedIndex];
223
224 Net::getNpcHandler()->menuSelect(mNpcId, selectedIndex + 1);
225 }
226 else if (mInputState == NPC_INPUT_STRING)
227 {
228 printText = mTextField->getText();
229
231 }
232 else if (mInputState == NPC_INPUT_INTEGER)
233 {
234 printText = strprintf("%d", mIntField->getValue());
235
237 }
238 // addText will auto remove the input layout
239 addText(strprintf("> \"%s\"", printText.c_str()), false);
240 addText(std::string(), false);
241
242 mNewText.clear();
243 }
244
245 if (!config.logNpcInGui)
246 setText({});
247 }
248 else if (event.getId() == "reset")
249 {
251 {
252 mTextField->setText(mDefaultString);
253 }
254 else if (mInputState == NPC_INPUT_INTEGER)
255 {
257 }
258 }
259 else if (event.getId() == "inc")
260 {
262 }
263 else if (event.getId() == "dec")
264 {
266 }
267 else if (event.getId() == "clear")
268 {
270 }
271}
272
277
283
285{
286 return mItems.size();
287}
288
289std::string NpcDialog::getElementAt(int i)
290{
291 return mItems[i];
292}
293
301
302void NpcDialog::addChoice(const std::string &choice)
303{
304 mItems.push_back(choice);
305}
306
308{
309 mItemList->setSelected(0);
310}
311
312void NpcDialog::textRequest(const std::string &defaultText)
313{
316 mDefaultString = defaultText;
317 mTextField->setText(defaultText);
318 buildLayout();
319}
320
322{
323 return mTextField->isFocused();
324}
325
327{
328 return mTextField->isFocused() || mIntField->isFocused();
329}
330
332{
333 for (auto dialog : instances)
334 if (dialog->isInputFocused())
335 return true;
336
337 return false;
338}
339
340void NpcDialog::integerRequest(int defaultValue, int min, int max)
341{
344 mDefaultInt = defaultValue;
345 mIntField->setRange(min, max);
346 mIntField->setValue(defaultValue);
347 buildLayout();
348}
349
350void NpcDialog::move(int amount)
351{
353 return;
354
355 switch (mInputState)
356 {
358 mIntField->setValue(mIntField->getValue() + amount);
359 break;
360 case NPC_INPUT_LIST:
361 mItemList->setSelected(mItemList->getSelected() - amount);
362 break;
363 case NPC_INPUT_NONE:
364 case NPC_INPUT_STRING:
365 break;
366 }
367}
368
369void NpcDialog::setVisible(bool visible)
370{
371 Window::setVisible(visible);
372
373 if (!visible)
374 {
376 }
377}
378
379void NpcDialog::mouseClicked(gcn::MouseEvent &mouseEvent)
380{
381 if (mouseEvent.getSource() == mItemList &&
382 isDoubleClick(mItemList->getSelected()))
383 {
384 action(gcn::ActionEvent(mNextButton, mNextButton->getActionEventId()));
385 }
386 if (mouseEvent.getSource() == mTextBox && isDoubleClick((int)(intptr_t)mTextBox))
387 {
389 action(gcn::ActionEvent(mNextButton,
390 mNextButton->getActionEventId()));
391 }
392}
393
395{
396 if (instances.size() == 1)
397 return instances.front();
398
399 for (auto dialog : instances)
400 if (dialog->isFocused())
401 return dialog;
402
403 return nullptr;
404}
405
407{
408 for (auto dialog : instances)
409 dialog->close();
410}
411
413{
414 if (npcListener)
415 return;
416
417 npcListener = new NpcEventListener();
418
419 npcListener->listen(Event::NpcChannel);
420}
421
423{
424 clearLayout();
425
427 {
429 {
431 }
432 else if (mActionState == NPC_ACTION_NEXT)
433 {
435 }
436 else if (mActionState == NPC_ACTION_CLOSE)
437 {
439 }
440 place(0, 0, mScrollArea, 5, 3);
441 place(3, 3, mClearButton);
442 place(4, 3, mNextButton);
443 }
444 else if (mInputState != NPC_INPUT_NONE)
445 {
446 if (!config.logNpcInGui)
448
451 {
452 place(0, 0, mScrollArea, 6, 3);
453 place(0, 3, mListScrollArea, 6, 3);
454 place(2, 6, mClearButton, 2);
455 place(4, 6, mNextButton, 2);
456
457 mItemList->setSelected(-1);
458 }
459 else if (mInputState == NPC_INPUT_STRING)
460 {
461 place(0, 0, mScrollArea, 6, 3);
462 place(0, 3, mTextField, 6);
463 place(0, 4, mResetButton, 2);
464 place(2, 4, mClearButton, 2);
465 place(4, 4, mNextButton, 2);
466 }
467 else if (mInputState == NPC_INPUT_INTEGER)
468 {
469 place(0, 0, mScrollArea, 6, 3);
470 place(0, 3, mMinusButton, 1);
471 place(1, 3, mIntField, 4);
472 place(5, 3, mPlusButton, 1);
473 place(0, 4, mResetButton, 2);
474 place(2, 4, mClearButton, 2);
475 place(4, 4, mNextButton, 2);
476 }
477 }
478
479 Layout &layout = getLayout();
481
482 bool waitState = isWaitingForTheServer();
483 mNextButton->setEnabled(!waitState);
484 setCloseButton(waitState);
485
486 redraw();
487
488 mScrollArea->setVerticalScrollAmount(mScrollArea->getVerticalMaxScroll());
489}
490
492 const Event &event)
493{
494 if (channel != Event::NpcChannel)
495 return;
496
497 if (event.getType() == Event::Message)
498 {
499 NpcDialog *dialog = getDialog(event.getInt("id"));
500
501 dialog->addText(event.getString("text"));
502 }
503 else if (event.getType() == Event::Menu)
504 {
505 NpcDialog *dialog = getDialog(event.getInt("id"));
506
507 dialog->choiceRequest();
508
509 int count = event.getInt("choiceCount");
510 for (int i = 1; i <= count; i++)
511 dialog->addChoice(event.getString("choice" + toString(i)));
512 dialog->initChoiceSelection();
513 }
514 else if (event.getType() == Event::IntegerInput)
515 {
516 NpcDialog *dialog = getDialog(event.getInt("id"));
517
518 int defaultValue = event.getInt("default", 0);
519 int min = event.getInt("min", 0);
520 int max = event.getInt("max", 2147483647);
521
522 dialog->integerRequest(defaultValue, min, max);
523 }
524 else if (event.getType() == Event::StringInput)
525 {
526 NpcDialog *dialog = getDialog(event.getInt("id"));
527
528 try
529 {
530 dialog->textRequest(event.getString("default"));
531 }
532 catch (BadEvent)
533 {
534 dialog->textRequest(std::string());
535 }
536 }
537 else if (event.getType() == Event::Next)
538 {
539 int id = event.getInt("id");
540
541 if (NpcDialog *dialog = getDialog(id, false))
542 dialog->showNextButton();
543 else
545 }
546 else if (event.getType() == Event::ClearDialog)
547 {
548 if (NpcDialog *dialog = getDialog(event.getInt("id"), false))
549 dialog->setText({});
550 }
551 else if (event.getType() == Event::Close)
552 {
553 int id = event.getInt("id");
554
555 if (NpcDialog *dialog = getDialog(id, false))
556 dialog->showCloseButton();
557 else
559 }
560 else if (event.getType() == Event::CloseAll)
561 {
563 }
564 else if (event.getType() == Event::CloseDialog)
565 {
566 if (NpcDialog *dialog = getDialog(event.getInt("id"), false))
567 dialog->close();
568 }
569 else if (event.getType() == Event::Post)
570 {
571 new NpcPostDialog(event.getInt("id"));
572 }
573}
574
576{
577 auto diag = mNpcDialogs.find(id);
578 NpcDialog *dialog = nullptr;
579
580 if (diag == mNpcDialogs.end())
581 {
582 // Empty dialogs don't help
583 if (make)
584 {
585 dialog = new NpcDialog(id);
586 mNpcDialogs[id] = dialog;
587 }
588 }
589 else
590 {
591 dialog = diag->second;
592 }
593
594 return dialog;
595}
596
598{
599 auto it = mNpcDialogs.find(id);
600 if (it != mNpcDialogs.end())
601 mNpcDialogs.erase(it);
602}
A simple browser box able to handle links and forward events to the parent conteiner.
Definition browserbox.h:74
void setEnableKeys(bool enable)
Enable or disable the replacement of keys.
Definition browserbox.h:125
void addRow(std::string_view row)
Adds a text row to the browser.
void setLinkHandler(LinkHandler *handler)
Sets the handler for links.
Definition browserbox.h:88
void clearRows()
Remove all rows.
@ AUTO_WRAP
Maybe it needs a fix or to be redone.
Definition browserbox.h:79
void setWrapIndent(int indent)
Sets the wrap indent for the browser box.
Definition browserbox.h:100
Button widget.
Definition button.h:38
void setCaption(const std::string &caption)
Definition button.cpp:226
void listen(Event::Channel channel)
Definition event.h:42
@ Next
Definition event.h:90
@ CloseAll
Definition event.h:66
@ CloseDialog
Definition event.h:67
@ Menu
Definition event.h:88
@ Close
Definition event.h:65
@ Post
Definition event.h:93
@ Message
Definition event.h:89
@ ClearDialog
Definition event.h:64
@ StringInput
Definition event.h:98
@ IntegerInput
Definition event.h:86
Channel
Definition event.h:45
@ NpcChannel
Definition event.h:55
TextBox which only accepts numbers as input.
void setRange(int minimum, int maximum)
Sets the minimum and maximum values of the text box.
int getValue()
Returns the value in the text box.
void setValue(int value)
Set the value of the text box to the specified value.
void setRowHeight(int n, int h)
Definition layout.h:221
This class is an helper for setting the position of widgets.
Definition layout.h:281
@ AUTO_SET
Uses the share as the new size.
Definition layout.h:304
A list box, meant to be used inside a scroll area.
Definition listbox.h:36
virtual void closeDialog(int npcId)=0
virtual void menuSelect(int npcId, int choice)=0
virtual void integerInput(int npcId, int value)=0
virtual void stringInput(int npcId, const std::string &value)=0
virtual void nextDialog(int npcId)=0
The npc dialog.
Definition npcdialog.h:49
static bool isAnyInputFocused()
void initChoiceSelection()
Put focus on the first choice.
NpcDialog(int npcId)
Definition npcdialog.cpp:71
void addChoice(const std::string &)
Adds a choice to the list box.
gcn::ScrollArea * mScrollArea
Definition npcdialog.h:172
static DialogList instances
Definition npcdialog.h:162
void action(const gcn::ActionEvent &event) override
Called when receiving actions from the widgets.
void setVisible(bool visible) override
Overloads window setVisible by Guichan to allow sticky window handling.
~NpcDialog() override
void integerRequest(int defaultValue, int min, int max)
Requests an integer from the user.
BrowserBox * mTextBox
Definition npcdialog.h:173
void nextDialog()
Notifies the server that client has performed a next action.
Button * mMinusButton
Definition npcdialog.h:187
bool isWaitingForTheServer() const
Definition npcdialog.h:150
bool isTextInputFocused() const
void move(int amount)
static NpcDialog * getActive()
Returns the first active instance.
TextField * mTextField
Definition npcdialog.h:184
static void setup()
std::unique_ptr< ItemLinkHandler > mItemLinkHandler
Definition npcdialog.h:176
std::vector< std::string > mNewText
Definition npcdialog.h:174
IntTextField * mIntField
Definition npcdialog.h:185
Button * mClearButton
Definition npcdialog.h:189
void buildLayout()
void choiceRequest()
Makes this dialog request a choice selection from the user.
int mDefaultInt
Definition npcdialog.h:168
std::list< NpcDialog * > DialogList
Definition npcdialog.h:161
NpcActionState mActionState
Definition npcdialog.h:214
void showNextButton()
When called, the widget will show a "Next" button.
@ NPC_INPUT_LIST
Definition npcdialog.h:200
@ NPC_INPUT_NONE
Definition npcdialog.h:199
@ NPC_INPUT_STRING
Definition npcdialog.h:201
@ NPC_INPUT_INTEGER
Definition npcdialog.h:202
bool isInputFocused() const
Button * mResetButton
Definition npcdialog.h:195
std::vector< std::string > mItems
Definition npcdialog.h:181
ListBox * mItemList
Definition npcdialog.h:179
std::string mDefaultString
Definition npcdialog.h:169
Button * mNextButton
Definition npcdialog.h:192
@ NPC_ACTION_NEXT
Definition npcdialog.h:208
@ NPC_ACTION_CLOSE
Definition npcdialog.h:210
@ NPC_ACTION_WAIT
Definition npcdialog.h:207
@ NPC_ACTION_INPUT
Definition npcdialog.h:209
void setText(const std::vector< std::string > &string)
Sets the text shows in the dialog.
void showCloseButton()
When called, the widget will show a "Close" button and will close the dialog when clicked.
std::string getElementAt(int i) override
Returns the name of item number i of the choices list.
void mouseClicked(gcn::MouseEvent &mouseEvent) override
void textRequest(const std::string &defaultText=std::string())
Requests a text string from the user.
void addText(const std::string &string, bool save=true)
Adds the text to the text shows in the dialog.
static void closeAll()
Closes all instances.
Button * mPlusButton
Definition npcdialog.h:186
void close() override
Notifies the server that the client has performed a close action.
gcn::ScrollArea * mListScrollArea
Definition npcdialog.h:180
NpcInputState mInputState
Definition npcdialog.h:213
int getNumberOfElements() override
Returns the number of items in the choices list.
void removeDialog(int id)
NpcDialog * getDialog(int id, bool make=true)
void event(Event::Channel channel, const Event &event) override
std::map< int, NpcDialog * > mNpcDialogs
Definition npcdialog.cpp:64
A scroll area.
Definition scrollarea.h:38
A text field.
Definition textfield.h:72
A window.
Definition window.h:59
void center()
Positions the window in the center of it's parent.
Definition window.cpp:768
virtual void setVisible(bool visible)
Overloads window setVisible by Guichan to allow sticky window handling.
Definition window.cpp:282
void setMinHeight(int height)
Sets the minimum height of the window.
Definition window.cpp:197
virtual void close()
Overrideable functionality for when the window is to close.
Definition window.cpp:327
void clearLayout()
Clears the window's layout (useful for redesigning the window).
Definition window.cpp:721
void setContentSize(int width, int height)
Sets the size of this window.
Definition window.cpp:151
Layout & getLayout()
Gets the layout handler for this window.
Definition window.cpp:714
void setWindowName(const std::string &name)
Sets the name of the window.
Definition window.h:272
void setResizable(bool resize)
Sets whether or not the window can be resized.
Definition window.cpp:212
LayoutCell & place(int x, int y, gcn::Widget *, int w=1, int h=1)
Adds a widget to the window and sets it at given cell.
Definition window.cpp:737
void scheduleDelete()
Schedule this window for deletion.
Definition window.cpp:299
void setCloseButton(bool flag)
Sets whether or not the window has a close button.
Definition window.cpp:262
void setDefaultSize()
Set the default win pos and size to the current ones.
Definition window.cpp:545
void loadWindowState()
Reads the position (and the size for resizable windows) in the configuration based on the given strin...
Definition window.cpp:467
void redraw()
Definition window.cpp:757
void setMinWidth(int width)
Sets the minimum width of the window.
Definition window.cpp:192
Config config
Global settings (config.xml)
Definition client.cpp:97
bool isDoubleClick(int selected)
Returns whether this call and the last call were done for the same selected index and within a short ...
Definition client.cpp:126
BadEvent
Definition event.h:32
#define _(s)
Definition gettext.h:38
NpcHandler * getNpcHandler()
Definition net.cpp:100
void setNPCInteractionCount(int count)
Sets the number of currently open NPC interaction windows.
int getNPCInteractionCount()
Returns the number of currently open NPC interaction windows.
#define CAPTION_CLOSE
Definition npcdialog.cpp:51
#define CAPTION_WAITING
Definition npcdialog.cpp:49
#define CAPTION_NEXT
Definition npcdialog.cpp:50
#define CAPTION_SUBMIT
Definition npcdialog.cpp:52
std::string strprintf(char const *format,...)
A safe version of sprintf that returns a std::string of the result.
std::string toString(const T &arg)
Converts the given value to a string using std::stringstream.
Definition stringutils.h:68
bool logNpcInGui