Peoplemon  0.1.0
Peoplemon 3 game source documentation
CatchRegionWindow.cpp
Go to the documentation of this file.
2 
3 #include <iomanip>
4 #include <numeric>
5 
6 namespace editor
7 {
8 namespace component
9 {
10 namespace
11 {
12 constexpr float FreqRange = static_cast<float>(std::numeric_limits<std::uint16_t>::max() - 1);
13 
14 bool isnum(const std::string& n) {
15  for (char c : n) {
16  if (!std::isdigit(c)) return false;
17  }
18  if (n.empty()) return false;
19  return true;
20 }
21 } // namespace
22 
23 using namespace bl::gui;
24 
26 : onSave(cb) {
27  window =
28  Window::create(LinePacker::create(LinePacker::Vertical, 4.f), "Peoplemon Region Editor");
29  window->getSignal(Event::Closed).willAlwaysCall(std::bind(&CatchRegionWindow::close, this));
30  window->setRequisition({700.f, 600.f});
31 
32  Box::Ptr row = Box::create(LinePacker::create(LinePacker::Horizontal, 4.f));
33  row->pack(Label::create("Name:"), false, true);
34  nameEntry = TextEntry::create();
35  row->pack(nameEntry, true, true);
36  window->pack(row, true, false);
37 
38  peopleScroll = ScrollArea::create(LinePacker::create(LinePacker::Vertical, 8.f));
39  peopleScroll->setMaxSize({700.f, 550.f});
40  window->pack(peopleScroll, true, true);
41 
42  row = Box::create(LinePacker::create(LinePacker::Horizontal, 4.f));
43  Button::Ptr addBut = Button::create("Add Peoplemon");
44  addBut->getSignal(Event::LeftClicked).willAlwaysCall(std::bind(&CatchRegionWindow::add, this));
45  row->pack(addBut, false, true);
46 
47  Box::Ptr subrow = Box::create(LinePacker::create(
48  LinePacker::Horizontal, 4.f, LinePacker::Compact, LinePacker::RightAlign));
49  Button::Ptr cancelBut = Button::create("Cancel");
50  cancelBut->setColor(sf::Color::Red, sf::Color::Black);
51  cancelBut->getSignal(Event::LeftClicked)
52  .willAlwaysCall(std::bind(&CatchRegionWindow::close, this));
53  subrow->pack(cancelBut, false, true);
54  Button::Ptr saveBut = Button::create("Save");
55  saveBut->setColor(sf::Color(3, 252, 244), sf::Color::Black);
56  saveBut->getSignal(Event::LeftClicked)
57  .willAlwaysCall(std::bind(&CatchRegionWindow::save, this));
58  subrow->pack(saveBut, false, true);
59  row->pack(subrow, true, true);
60  window->pack(row, true, false);
61 }
62 
63 void CatchRegionWindow::open(bl::gui::GUI* gui, const core::map::CatchRegion& v) {
64  nameEntry->setInput(v.name);
65  rows.reserve(v.wilds.size());
66  for (const core::pplmn::WildPeoplemon& wild : v.wilds) {
67  rows.emplace_back(std::bind(&CatchRegionWindow::freqChange, this, std::placeholders::_1),
68  std::bind(&CatchRegionWindow::remove, this, std::placeholders::_1));
69  rows.back().idSelect->setPeoplemon(wild.id);
70  rows.back().minLevel->setInput(std::to_string(wild.minLevel));
71  rows.back().maxLevel->setInput(std::to_string(wild.maxLevel));
72  rows.back().freqSlider->setValue(static_cast<float>(wild.frequency) / FreqRange);
73  peopleScroll->pack(rows.back().row, true, false);
74  }
75 
76  gui->pack(window);
77  window->setForceFocus(true);
78 
79  value = v;
80 }
81 
83  value.name = nameEntry->getInput();
84  value.wilds.clear();
85  value.wilds.reserve(rows.size());
86  for (const Row& row : rows) {
87  value.wilds.emplace_back();
88  value.wilds.back().id = row.idSelect->currentPeoplemon();
89  value.wilds.back().minLevel = std::atoi(row.minLevel->getInput().c_str());
90  value.wilds.back().maxLevel = std::atoi(row.maxLevel->getInput().c_str());
91  value.wilds.back().frequency =
92  static_cast<std::uint16_t>(row.freqSlider->getValue() * FreqRange);
93  }
94 
95  return value;
96 }
97 
98 void CatchRegionWindow::close() {
99  window->setForceFocus(false);
100  window->remove();
101  for (auto& row : rows) { row.row->remove(); }
102  rows.clear();
103 }
104 
105 CatchRegionWindow::Row::Row(const Callback& cb, const Callback& delcb) {
106  row = Box::create(LinePacker::create(LinePacker::Horizontal, 8.f));
107  row->setColor(sf::Color(119, 252, 35, 100), sf::Color::Green);
108  row->setOutlineThickness(1.f);
109 
110  Box::Ptr col = Box::create(LinePacker::create(LinePacker::Vertical, 4.f));
111  col->pack(Label::create("Peoplemon"));
112  idSelect = PeoplemonSelector::create();
113  idSelect->setVerticalAlignment(RenderSettings::Bottom);
114  idSelect->setMaxHeight(300.f);
115  col->pack(idSelect, true, false);
116  row->pack(col, false, true);
117 
118  col = Box::create(LinePacker::create(LinePacker::Vertical, 4.f));
119  col->pack(Label::create("Min Lvl"), true, false);
120  minLevel = TextEntry::create();
121  minLevel->setMode(TextEntry::Mode::Integer | TextEntry::Mode::Unsigned);
122  col->pack(minLevel, true, false);
123  row->pack(col, false, true);
124 
125  col = Box::create(LinePacker::create(LinePacker::Vertical, 4.f));
126  col->pack(Label::create("Max Lvl"), true, false);
127  maxLevel = TextEntry::create();
128  maxLevel->setMode(TextEntry::Mode::Integer | TextEntry::Mode::Unsigned);
129  col->pack(maxLevel, true, false);
130  row->pack(col, false, true);
131 
132  col = Box::create(LinePacker::create(LinePacker::Vertical, 4.f));
133  Box::Ptr subrow = Box::create(LinePacker::create(LinePacker::Horizontal, 10.f));
134  freqLabel = Label::create("Freq: 0%");
135  freqLabel->setRequisition({90.f, 10.f});
136  lockBut = CheckButton::create("Lock");
137  freqLabel->setHorizontalAlignment(RenderSettings::Left);
138  subrow->pack(freqLabel, true, true);
139  subrow->pack(lockBut, false, true);
140  col->pack(subrow, true, false);
141  freqSlider = Slider::create(Slider::Horizontal);
142  freqSlider->getSignal(Event::ValueChanged).willAlwaysCall(std::bind(cb, std::placeholders::_2));
143  freqSlider->setRequisition({200.f, 20.f});
144  freqSlider->setSliderIncrement(0.001f);
145  col->pack(freqSlider, true, false);
146  row->pack(col, false, true);
147 
148  col = Box::create(LinePacker::create(
149  LinePacker::Vertical, 4.f, LinePacker::Compact, LinePacker::BottomAlign));
150  delBut = Button::create("Remove");
151  delBut->setColor(sf::Color::Red, sf::Color::Black);
152  delBut->getSignal(Event::LeftClicked).willAlwaysCall(std::bind(delcb, std::placeholders::_2));
153  col->pack(delBut);
154  row->pack(col, false, true);
155 }
156 
157 void CatchRegionWindow::save() {
158  if (validate()) {
159  onSave();
160  close();
161  }
162 }
163 
164 void CatchRegionWindow::add() {
165  unsigned int unlocked = 1;
166  float unlockedAmount = 0.f;
167  for (const auto& row : rows) {
168  if (!row.lockBut->getValue()) {
169  ++unlocked;
170  unlockedAmount += row.freqSlider->getValue();
171  }
172  }
173  const float avg = unlockedAmount / static_cast<float>(unlocked);
174 
175  rows.emplace_back(std::bind(&CatchRegionWindow::freqChange, this, std::placeholders::_1),
176  std::bind(&CatchRegionWindow::remove, this, std::placeholders::_1));
177  rows.back().minLevel->setInput("20");
178  rows.back().maxLevel->setInput("30");
179  peopleScroll->pack(rows.back().row, true, false);
180 
181  if (rows.size() == 1) { rows.front().freqSlider->setValue(1.f); }
182  else {
183  for (auto& row : rows) {
184  if (!row.lockBut->getValue()) {
185  row.freqSlider->setValue(avg, false);
186  std::stringstream ss;
187  ss << "Freq: ";
188  ss << std::setprecision(5) << (row.freqSlider->getValue() * 100.f) << "%";
189  row.freqLabel->setText(ss.str());
190  }
191  }
192  }
193 }
194 
195 void CatchRegionWindow::remove(bl::gui::Element* e) {
196  for (auto it = rows.begin(); it != rows.end(); ++it) {
197  if (static_cast<Element*>(it->delBut.get()) == e) {
198  it->row->remove();
199  rows.erase(it);
200  return;
201  }
202  }
203 
204  unsigned int unlocked = 0;
205  float unlockedAmount = 0.f;
206  for (const auto& row : rows) {
207  if (!row.lockBut->getValue()) {
208  ++unlocked;
209  unlockedAmount += row.freqSlider->getValue();
210  }
211  }
212  const float avg = unlockedAmount / static_cast<float>(unlocked);
213 
214  for (auto& row : rows) {
215  if (!row.lockBut->getValue()) {
216  row.freqSlider->setValue(avg, false);
217  std::stringstream ss;
218  ss << "Freq: ";
219  ss << std::setprecision(5) << (row.freqSlider->getValue() * 100.f) << "%";
220  row.freqLabel->setText(ss.str());
221  }
222  }
223 }
224 
225 void CatchRegionWindow::freqChange(Element* e) {
226  float sum = 0.f;
227  unsigned int unlocked = 0;
228  for (const auto& row : rows) {
229  sum += row.freqSlider->getValue();
230  if (static_cast<Element*>(row.freqSlider.get()) != e && !row.lockBut->getValue()) {
231  ++unlocked;
232  }
233  }
234 
235  const float diff = 1.f - sum;
236  const float shared = diff / static_cast<float>(unlocked);
237 
238  for (auto& row : rows) {
239  if (static_cast<Element*>(row.freqSlider.get()) != e && !row.lockBut->getValue()) {
240  float f = row.freqSlider->getValue() + shared;
241  if (f <= 0.f) f = 0.001f;
242  if (f > 1.f) f = 1.f;
243  row.freqSlider->setValue(f, false);
244  }
245  std::stringstream ss;
246  ss << "Freq: ";
247  ss << std::setprecision(5) << (row.freqSlider->getValue() * 100.f) << "%";
248  row.freqLabel->setText(ss.str());
249  }
250 }
251 
252 bool CatchRegionWindow::validate() const {
253  if (rows.empty()) {
254  bl::dialog::tinyfd_messageBox(
255  "Error", "Region must have at least one Peoplemon", "ok", "error", 1);
256  return false;
257  }
258  if (nameEntry->getInput().empty()) {
259  bl::dialog::tinyfd_messageBox("Error", "Please enter a name", "ok", "error", 1);
260  return false;
261  }
262 
263  float sum = 0.f;
264  for (const Row& row : rows) {
265  if (row.idSelect->currentPeoplemon() == core::pplmn::Id::Unknown) {
266  bl::dialog::tinyfd_messageBox("Error", "Peoplemon cannot be blank", "ok", "error", 1);
267  return false;
268  }
269  if (!isnum(row.minLevel->getInput()) || !isnum(row.maxLevel->getInput())) {
270  bl::dialog::tinyfd_messageBox("Error", "Levels must be numbers", "ok", "error", 1);
271  return false;
272  }
273  const int mn = std::atoi(row.minLevel->getInput().c_str());
274  const int mx = std::atoi(row.maxLevel->getInput().c_str());
275  if (mn <= 0 || mx <= 0) {
276  bl::dialog::tinyfd_messageBox("Error", "Levels must be non-zero", "ok", "error", 1);
277  return false;
278  }
279  if (mn > 100 || mx > 100) {
280  bl::dialog::tinyfd_messageBox(
281  "Error", "Levels must be less than or equal to 100", "ok", "error", 1);
282  return false;
283  }
284  if (mx < mn) {
285  bl::dialog::tinyfd_messageBox(
286  "Error", "Min level must be below max level", "ok", "error", 1);
287  return false;
288  }
289  sum += row.freqSlider->getValue();
290  }
291 
292  if (std::abs(1.f - sum) > 0.001f) {
293  bl::dialog::tinyfd_messageBox("Error", "Frequencies must sum to 100%", "ok", "error", 1);
294  return false;
295  }
296 
297  return true;
298 }
299 
300 } // namespace component
301 } // namespace editor
All classes and functionality used for implementing the game editor.
Definition: Tile.hpp:11
Represents a class of catchable peoplemon.
Definition: CatchRegion.hpp:18
std::vector< pplmn::WildPeoplemon > wilds
Definition: CatchRegion.hpp:20
Template struct to generate OwnedPeoplemon for random encounters.
Id id
The type of peoplemon to generate.
std::uint8_t minLevel
The minimum level that may spawn, inclusive.
std::uint8_t maxLevel
The maximum level that may spawn, inclusive.
std::uint16_t frequency
Frequency of spawns relative to other wild peoplemon in the region.
CatchRegionWindow(const TriggerCb &onSave)
Construct a new Catch Region Window.
std::function< void()> TriggerCb
Called when the region is saved.
const core::map::CatchRegion & getValue()
Returns the current region value as inputed by the user.
void open(bl::gui::GUI *gui, const core::map::CatchRegion &region)
Opens the window with the given region value.
static Ptr create(bool allowUnknown=false)
Creates a new peoplemon selector.