13 const std::string EmptyFile =
"<no file selected>";
22 int parsePayout(
const std::string& payout) {
23 for (
char c : payout) {
24 if (c < '0' || c >
'9')
return -1;
26 const int p = std::atoi(payout.c_str());
27 return p <= 200 ? p : -1;
32 using namespace bl::gui;
42 window->setForceFocus(
true);
43 const auto val = pplWindow.getValue();
44 if (editPplIndex.has_value()) {
45 peoplemon[editPplIndex.value()] = val;
46 pplBox->editOptionText(editPplIndex.value(), pplToStr(val));
49 peoplemon.emplace_back(val);
50 pplBox->addOption(pplToStr(val));
54 [
this]() { window->setForceFocus(
true); })
56 std::bind(&TrainerEditorWindow::onChooseFile,
this, std::placeholders::_1),
59 window->setForceFocus(
true);
61 , animWindow(
true, std::bind(&TrainerEditorWindow::onChooseAnimation,
this, std::placeholders::_1),
62 std::bind(&TrainerEditorWindow::forceWindowOnTop,
this))
64 std::bind(&TrainerEditorWindow::makeDirty,
this), [
this]() { window->setForceFocus(
false); },
65 std::bind(&TrainerEditorWindow::forceWindowOnTop,
this))
67 std::bind(&TrainerEditorWindow::onChooseConversation,
this, std::placeholders::_1),
68 std::bind(&TrainerEditorWindow::forceWindowOnTop,
this)) {
69 window = Window::create(LinePacker::create(LinePacker::Vertical, 8),
"Trainer Editor");
70 window->getSignal(Event::Closed).willAlwaysCall([
this](
const Event&, Element*) { hide(); });
72 Box::Ptr row = Box::create(LinePacker::create(LinePacker::Horizontal, 4));
73 Button::Ptr newBut = Button::create(
"New");
74 newBut->setTooltip(
"Create a new Trainer file");
75 newBut->getSignal(Event::LeftClicked).willAlwaysCall([
this](
const Event&, Element*) {
76 if (confirmDiscard()) {
78 window->setForceFocus(
false);
79 filePicker.open(FilePicker::CreateNew,
"New Trainer", parent,
true);
82 Button::Ptr setBut = Button::create(
"Set File");
83 setBut->setTooltip(
"Change the file to save this Trainer to");
84 setBut->getSignal(Event::LeftClicked).willAlwaysCall([
this](
const Event&, Element*) {
85 if (fileLabel->getText() == EmptyFile || confirmDiscard()) {
87 window->setForceFocus(
false);
88 filePicker.open(FilePicker::CreateOrPick,
"Set Trainer File", parent,
false);
91 Button::Ptr openBut = Button::create(
"Open");
92 openBut->setTooltip(
"Open a different Trainer file");
93 openBut->getSignal(Event::LeftClicked).willAlwaysCall([
this](
const Event&, Element*) {
94 if (confirmDiscard()) {
96 window->setForceFocus(
false);
97 filePicker.open(FilePicker::PickExisting,
"Open Trainer", parent,
false);
100 saveBut = Button::create(
"Save");
101 saveBut->setTooltip(
"Overwrite the current Trainer file");
102 saveBut->setColor(sf::Color::Yellow, sf::Color::Blue);
103 saveBut->getSignal(Event::LeftClicked).willAlwaysCall([
this](
const Event&, Element*) {
104 if (validate(
true)) {
106 trainer.
name = nameEntry->getInput();
110 trainer.
animation = animLabel->getText();
111 trainer.
behavior = behaviorEditor.getValue();
112 trainer.
visionRange = visionRangeEntry->getSelectedOption();
113 trainer.
items = items;
115 trainer.
payout = parsePayout(payoutEntry->getInput());
117 fileLabel->getText()))) {
118 bl::dialog::tinyfd_messageBox(
"Error",
"Failed to save Trainer",
"ok",
"error", 1);
120 else { makeClean(); }
123 fileLabel = Label::create(
"");
124 fileLabel->setColor(sf::Color::Cyan, sf::Color::Cyan);
125 row->pack(newBut,
false,
true);
126 row->pack(setBut,
false,
true);
127 row->pack(openBut,
false,
true);
128 row->pack(saveBut,
false,
true);
129 row->pack(fileLabel,
true,
true);
130 window->pack(row,
true,
false);
132 row = Box::create(LinePacker::create(LinePacker::Horizontal, 4));
133 Label::Ptr label = Label::create(
"Name:");
134 nameEntry = TextEntry::create();
135 nameEntry->getSignal(Event::ValueChanged)
136 .willAlwaysCall(std::bind(&TrainerEditorWindow::makeDirty,
this));
137 row->pack(label,
false,
true);
138 row->pack(nameEntry,
true,
true);
139 window->pack(row,
true,
false);
141 row = Box::create(LinePacker::create(LinePacker::Horizontal, 4));
142 Button::Ptr animBut = Button::create(
"Select Anim");
143 animBut->getSignal(Event::LeftClicked).willAlwaysCall([
this](
const Event&, Element*) {
144 window->setForceFocus(
false);
147 animLabel = Label::create(
"animation.anim");
148 animLabel->setColor(sf::Color::Cyan, sf::Color::Cyan);
149 row->pack(animBut,
false,
true);
150 row->pack(animLabel,
true,
true);
151 window->pack(row,
true,
false);
153 row = Box::create(LinePacker::create(LinePacker::Horizontal, 4));
154 Button::Ptr convBut = Button::create(
"Before Battle Conversation");
155 convBut->getSignal(Event::LeftClicked).willAlwaysCall([
this](
const Event&, Element*) {
156 window->setForceFocus(
false);
158 conversationWindow.open(parent, bbLabel->getText());
160 bbLabel = Label::create(
"conversation.conv");
161 bbLabel->setColor(sf::Color::Cyan, sf::Color::Cyan);
162 row->pack(convBut,
false,
true);
163 row->pack(bbLabel,
true,
true);
164 window->pack(row,
true,
false);
166 row = Box::create(LinePacker::create(LinePacker::Horizontal, 4));
167 convBut = Button::create(
"After Battle Conversation");
168 convBut->getSignal(Event::LeftClicked).willAlwaysCall([
this](
const Event&, Element*) {
169 window->setForceFocus(
false);
171 conversationWindow.open(parent, abLabel->getText());
173 abLabel = Label::create(
"conversation.conv");
174 abLabel->setColor(sf::Color::Cyan, sf::Color::Cyan);
175 row->pack(convBut,
false,
true);
176 row->pack(abLabel,
true,
true);
177 window->pack(row,
true,
false);
179 row = Box::create(LinePacker::create(LinePacker::Horizontal, 4));
180 row->pack(Label::create(
"Lose Line:"),
false,
true);
181 loseLineEntry = TextEntry::create();
182 loseLineEntry->getSignal(Event::ValueChanged)
183 .willAlwaysCall(std::bind(&TrainerEditorWindow::makeDirty,
this));
184 row->pack(loseLineEntry,
true,
true);
185 window->pack(row,
true,
false);
187 row = Box::create(LinePacker::create(LinePacker::Horizontal, 4));
188 behaviorEditor.pack(row);
189 window->pack(row,
true,
false);
191 row = Box::create(LinePacker::create(LinePacker::Horizontal, 4));
192 row->pack(Label::create(
"Vision Range (tiles):"),
false,
true);
193 visionRangeEntry = ComboBox::create();
194 visionRangeEntry->setMaxHeight(300.f);
195 for (
int i = 0; i <= 20; ++i) { visionRangeEntry->addOption(std::to_string(i)); }
196 visionRangeEntry->setSelectedOption(4);
197 visionRangeEntry->getSignal(Event::ValueChanged)
198 .willAlwaysCall(std::bind(&TrainerEditorWindow::makeDirty,
this));
199 row->pack(visionRangeEntry,
false,
true);
202 Label::Ptr l = Label::create(
"Items:");
203 l->setHorizontalAlignment(RenderSettings::Alignment::Left);
204 window->pack(l,
true,
false);
205 row = Box::create(LinePacker::create(LinePacker::Horizontal, 4));
206 itemSelectBox = SelectBox::create();
207 itemSelectBox->setMaxSize({1200.f, 90.f});
208 itemSelectBox->setRequisition({250.f, 108.f});
209 row->pack(itemSelectBox,
true,
true);
210 Box::Ptr column = Box::create(LinePacker::create(LinePacker::Vertical, 4.f));
211 Box::Ptr subRow = Box::create(LinePacker::create(LinePacker::Horizontal));
212 subRow->pack(itemSelector);
213 Button::Ptr but = Button::create(
"Add");
214 but->getSignal(Event::LeftClicked).willAlwaysCall([
this](
const Event&, Element*) {
216 items.push_back(curItem);
222 column->pack(subRow,
true,
false);
223 but = Button::create(
"Remove");
224 but->getSignal(Event::LeftClicked).willAlwaysCall([
this](
const Event&, Element*) {
225 const auto sel = itemSelectBox->getSelectedOption();
226 if (sel.has_value()) {
227 items.erase(items.begin() + sel.value());
228 itemSelectBox->removeOption(sel.value());
232 but->setColor(sf::Color::Red, sf::Color::Black);
234 row->pack(column,
true,
true);
235 window->pack(row,
true,
false);
237 l = Label::create(
"Peoplemon:");
238 l->setHorizontalAlignment(RenderSettings::Alignment::Left);
239 window->pack(l,
true,
false);
240 row = Box::create(LinePacker::create(LinePacker::Horizontal, 4));
241 pplBox = SelectBox::create();
242 pplBox->setMaxSize({1200.f, 90.f});
243 pplBox->setRequisition({250.f, 108.f});
244 row->pack(pplBox,
true,
true);
245 column = Box::create(LinePacker::create(LinePacker::Vertical, 4.f));
246 but = Button::create(
"Add");
247 but->getSignal(Event::LeftClicked).willAlwaysCall([
this](
const Event&, Element*) {
248 if (peoplemon.size() < 6) {
249 editPplIndex.reset();
250 window->setForceFocus(false);
251 pplWindow.show(parent);
255 but = Button::create(
"Edit");
256 but->getSignal(Event::LeftClicked).willAlwaysCall([
this](
const Event&, Element*) {
257 editPplIndex = pplBox->getSelectedOption();
258 window->setForceFocus(
false);
259 pplWindow.show(parent,
260 editPplIndex.has_value() ? peoplemon[editPplIndex.value()] :
264 but = Button::create(
"Remove");
265 but->getSignal(Event::LeftClicked).willAlwaysCall([
this](
const Event&, Element*) {
266 const auto sel = pplBox->getSelectedOption();
267 if (sel.has_value()) {
268 peoplemon.erase(peoplemon.begin() + sel.value());
269 pplBox->removeOption(sel.value());
273 but->setColor(sf::Color::Red, sf::Color::Black);
275 row->pack(column,
false,
true);
276 window->pack(row,
true,
false);
278 row = Box::create(LinePacker::create(LinePacker::Horizontal, 4));
279 row->pack(Label::create(
"Battle Base Payout (0-200):"),
false,
true);
280 payoutEntry = TextEntry::create();
281 payoutEntry->setInput(
"40");
282 payoutEntry->setRequisition({60.f, 10.f});
283 row->pack(payoutEntry,
false,
true);
284 window->pack(row,
true,
false);
286 row = Box::create(LinePacker::create(LinePacker::Horizontal, 4));
287 Button::Ptr selectBut = Button::create(
"Use Trainer");
288 selectBut->setTooltip(
"Use the current Trainer file");
289 selectBut->getSignal(Event::LeftClicked).willAlwaysCall([
this](
const Event&, Element*) {
290 if (confirmDiscard()) {
291 if (validate(
false)) {
292 selectCb(fileLabel->getText());
297 selectBut->setColor(sf::Color::Blue, sf::Color::Black);
298 Button::Ptr cancelBut = Button::create(
"Cancel");
299 cancelBut->getSignal(Event::LeftClicked).willAlwaysCall([
this](
const Event&, Element*) {
302 cancelBut->setColor(sf::Color::Red, sf::Color::Black);
303 row->pack(selectBut,
false,
true);
304 row->pack(cancelBut,
false,
true);
305 window->pack(row,
true,
false);
312 fileLabel->setText(file);
316 parent->pack(window);
317 window->setForceFocus(
true);
318 itemSelector->refresh();
321 void TrainerEditorWindow::onChooseFile(
const std::string& file) {
322 const std::string f =
327 window->setForceFocus(
true);
328 if (makingNew) { makeDirty(); }
334 fileLabel->setText(f);
337 void TrainerEditorWindow::makeClean() {
338 saveBut->setColor(sf::Color::Green, sf::Color::Blue);
342 void TrainerEditorWindow::makeDirty() {
343 saveBut->setColor(sf::Color::Yellow, sf::Color::Blue);
347 void TrainerEditorWindow::reset() {
348 fileLabel->setText(EmptyFile);
349 nameEntry->setInput(
"");
350 animLabel->setText(
"<no anim selected>");
351 bbLabel->setText(
"<no conv selected>");
352 abLabel->setText(
"<no conv selected>");
353 loseLineEntry->setInput(
"");
355 visionRangeEntry->setSelectedOption(4);
357 itemSelectBox->clearOptions();
358 pplBox->clearOptions();
360 payoutEntry->setInput(
"40");
363 void TrainerEditorWindow::load(
const std::string& file) {
366 nameEntry->setInput(trainer.
name);
371 visionRangeEntry->setSelectedOption(trainer.
visionRange);
373 items = trainer.
items;
376 for (
const auto& ppl : peoplemon) { pplBox->addOption(pplToStr(ppl)); }
377 payoutEntry->setInput(std::to_string(trainer.
payout));
380 bl::dialog::tinyfd_messageBox(
381 "Error", std::string(
"Failed to load Trainer:\n" + file).c_str(),
"ok",
"error", 1);
385 bool TrainerEditorWindow::validate(
bool saving)
const {
387 if (fileLabel->getText() == EmptyFile) {
388 bl::dialog::tinyfd_messageBox(
"Warning",
"Please select a file",
"ok",
"warning", 1);
393 if (!FileUtil::exists(
395 bl::dialog::tinyfd_messageBox(
"Warning",
"Bad file selected",
"ok",
"warning", 1);
399 if (nameEntry->getInput().empty()) {
400 bl::dialog::tinyfd_messageBox(
"Warning",
"Enter a name",
"ok",
"warning", 1);
403 if (!FileUtil::directoryExists(
405 bl::dialog::tinyfd_messageBox(
"Warning",
"Bad animation",
"ok",
"warning", 1);
409 if (!FileUtil::exists(p)) {
411 bl::dialog::tinyfd_messageBox(
412 "Warning",
"Bad before-battle conversation",
"ok",
"warning", 1);
416 if (!FileUtil::exists(p)) {
418 bl::dialog::tinyfd_messageBox(
419 "Warning",
"Bad afer-battle conversation",
"ok",
"warning", 1);
422 if (loseLineEntry->getInput().empty()) {
423 bl::dialog::tinyfd_messageBox(
"Warning",
"Lose line is empty",
"ok",
"warning", 1);
426 if (behaviorEditor.
getValue().
type() == core::file::Behavior::Type::FollowingPath &&
428 bl::dialog::tinyfd_messageBox(
"Warning",
"Behavior path is empty",
"ok",
"warning", 1);
431 if (peoplemon.empty()) {
432 bl::dialog::tinyfd_messageBox(
"Warning",
"Trainer has no peoplemon",
"ok",
"warning", 1);
435 if (parsePayout(payoutEntry->getInput()) <= 0) {
436 bl::dialog::tinyfd_messageBox(
437 "Warning",
"Payout must be number in range (0, 200]",
"ok",
"warning", 1);
443 bool TrainerEditorWindow::confirmDiscard()
const {
445 return 1 == bl::dialog::tinyfd_messageBox(
446 "Warning",
"Discard unsaved changes?",
"yesno",
"warning", 0);
455 behaviorEditor.
hide();
456 window->setForceFocus(
false);
460 void TrainerEditorWindow::onChooseAnimation(
const std::string& f) {
461 animLabel->setText(f);
465 void TrainerEditorWindow::onChooseConversation(
const std::string& c) {
466 if (selectingBB) { bbLabel->setText(c); }
467 else { abLabel->setText(c); }
468 window->setForceFocus(
true);
472 void TrainerEditorWindow::forceWindowOnTop() { window->setForceFocus(
true); }
Core classes and functionality for both the editor and game.
All classes and functionality used for implementing the game editor.
bl::util::FileUtil FileUtil
Type type() const
The type of behavior this is.
Path & path()
The data for path following.
std::vector< Pace > paces
The sections of the path.
Static data for trainers in the world.
std::string lostBattleLine
std::vector< pplmn::OwnedPeoplemon > peoplemon
std::string postBattleConversation
std::vector< item::Id > items
bool save(const std::string &file) const
Saves the trainer data to the given file.
bool load(const std::string &file, bl::tmap::Direction spawnDir=bl::tmap::Direction::Down)
Loads the trainer data from the given file.
std::string prebattleConversation
static const std::string & getName(item::Id item)
Returns the name of the given item.
Represents an instance of a peoplemon. Can be a wild peoplemon, trainer, or player peoplemon....
unsigned int currentLevel() const
Returns the current level of this peoplemon.
Id id() const
Returns the id of this peoplemon.
static const std::string & name(Id id)
Returns the name of the given Peoplemon.
static const std::string & TrainerPath()
static const std::string & ConversationPath()
static const std::string & CharacterAnimationPath()
static const std::string & TrainerFileExtension()
void hide()
Removes the window from view.
const core::file::Behavior & getValue() const
Returns the behavior value.
void hide()
Hides the editor window if opened.
void configure(bl::gui::GUI *parent, const core::file::Behavior &behavior)
Sets the parent GUI object and current value. Call before using.
Wrapper over ComboBox that allows selection of any item in the item database.
void show(bl::gui::GUI *parent, const std::string &file)
Opens the trainer editor.
TrainerEditorWindow(const SelectCb &cb, const CloseCb &closeCb)
Construct a new Trainer Editor Window.
std::function< void(const std::string &file)> SelectCb
Callback for when a trainer file is selected.
void hide()
Hides the window and all created child windows.
std::function< void()> CloseCb
Called when the window is closed.