10 using namespace bl::gui;
16 std::string moveToStr(
MoveId move) {
18 ss <<
"Id: " <<
static_cast<int>(move) <<
" | ";
23 std::string learnMoveToStr(
const std::pair<unsigned int, MoveId>& lm) {
25 ss <<
"Lvl " << lm.first <<
" | " <<
Move::name(lm.second);
30 T parseInput(
const bl::gui::TextEntry::Ptr& input) {
31 const long long val = std::atoll(input->getInput().c_str());
32 return static_cast<T
>(val);
35 void error(
const std::string& e) {
36 bl::dialog::tinyfd_messageBox(
"Error", e.c_str(),
"ok",
"error", 1);
39 bool warning(
const std::string& e) {
40 return 1 == bl::dialog::tinyfd_messageBox(
"Warning", e.c_str(),
"yesno",
"warning", 0);
49 , evAwardEntry(
StatBox::IV, false) {
50 window = Window::create(LinePacker::create(LinePacker::Vertical, 4.f),
"Editing Peoplemon");
51 window->getSignal(Event::Closed)
52 .willAlwaysCall(std::bind(&PeoplemonEditorWindow::onCancel,
this));
54 LinePacker::Ptr rowPack = LinePacker::create(LinePacker::Horizontal, 4.f);
55 LinePacker::Ptr colPack = LinePacker::create(LinePacker::Vertical, 4.f);
56 const auto onEdit = std::bind(&PeoplemonEditorWindow::makeDirty,
this);
57 applyBut = Button::create(
"Save");
59 Button::Ptr cancelBut = Button::create(
"Cancel");
60 cancelBut->getSignal(Event::LeftClicked)
61 .willAlwaysCall(std::bind(&PeoplemonEditorWindow::onCancel,
this));
62 applyBut->getSignal(Event::LeftClicked)
63 .willAlwaysCall(std::bind(&PeoplemonEditorWindow::onSave,
this));
65 idEntry = TextEntry::create();
66 idEntry->setMode(TextEntry::Mode::Unsigned | TextEntry::Mode::Integer);
67 idEntry->getSignal(Event::TextEntered).willAlwaysCall(onEdit);
68 idEntry->setRequisition({60.f, 1.f});
69 Button::Ptr imgReload = Button::create(
"Reload Images");
70 imgReload->getSignal(Event::LeftClicked)
71 .willAlwaysCall(std::bind(&PeoplemonEditorWindow::reloadImages,
this));
72 nameEntry = TextEntry::create();
73 nameEntry->getSignal(Event::TextEntered).willAlwaysCall(onEdit);
74 nameEntry->setRequisition({160.f, 1.f});
75 descEntry = TextEntry::create();
76 descEntry->getSignal(Event::TextEntered).willAlwaysCall(onEdit);
77 descEntry->setRequisition({400.f, 1.f});
81 movePoolEntry = SelectBox::create();
82 movePoolEntry->setRequisition({200.f, 1.f});
83 movePoolEntry->setMaxSize({250.f, 300.f});
85 Button::Ptr movePoolAddBut = Button::create(
"Add");
86 movePoolAddBut->getSignal(Event::LeftClicked)
87 .willAlwaysCall(std::bind(&PeoplemonEditorWindow::onMovePoolAdd,
this));
88 Button::Ptr movePoolRmBut = Button::create(
"Remove");
89 movePoolRmBut->getSignal(Event::LeftClicked)
90 .willAlwaysCall(std::bind(&PeoplemonEditorWindow::onMovePoolDelete,
this));
91 movePoolRmBut->setColor(sf::Color::Red, sf::Color::Black);
93 learnMoveEntry = SelectBox::create();
94 learnMoveEntry->setRequisition({200.f, 1.f});
95 learnMoveEntry->setMaxSize({250.f, 300.f});
97 moveLevelEntry = TextEntry::create();
98 moveLevelEntry->setRequisition({45.f, 1.f});
99 Button::Ptr moveLearnAddBut = Button::create(
"Add");
100 moveLearnAddBut->getSignal(Event::LeftClicked)
101 .willAlwaysCall(std::bind(&PeoplemonEditorWindow::onLearnMoveAdd,
this));
102 Button::Ptr moveLearnRmBut = Button::create(
"Remove");
103 moveLearnRmBut->getSignal(Event::LeftClicked)
104 .willAlwaysCall(std::bind(&PeoplemonEditorWindow::onLearnMoveDelete,
this));
105 moveLearnRmBut->setColor(sf::Color::Red, sf::Color::Black);
107 abilitySelect = ComboBox::create();
108 abilitySelect->addOption(
"None");
109 abilitySelect->addOption(
"BoardGameMaster");
110 abilitySelect->addOption(
"Chillaxed");
111 abilitySelect->addOption(
"Classy");
112 abilitySelect->addOption(
"Goon");
113 abilitySelect->addOption(
"QuickDraw");
114 abilitySelect->addOption(
"AlwaysFriendly");
115 abilitySelect->addOption(
"Sassy");
116 abilitySelect->addOption(
"ImpulseBuy");
117 abilitySelect->addOption(
"RunAway");
118 abilitySelect->addOption(
"MrExtra");
119 abilitySelect->addOption(
"BeefedUp");
120 abilitySelect->addOption(
"Reserved");
121 abilitySelect->addOption(
"DukeOfJokes");
122 abilitySelect->addOption(
"Engaging");
123 abilitySelect->addOption(
"SnackShare");
124 abilitySelect->addOption(
"DerpDerp");
125 abilitySelect->addOption(
"Klutz");
126 abilitySelect->addOption(
"GenderBend");
127 abilitySelect->addOption(
"Garbage");
128 abilitySelect->addOption(
"BikeMechanic");
129 abilitySelect->addOption(
"SidetrackTeach");
130 abilitySelect->addOption(
"NoJokeTeach");
131 abilitySelect->addOption(
"FieryTeach");
132 abilitySelect->addOption(
"ExperiencedTeach");
133 abilitySelect->addOption(
"NewTeach");
134 abilitySelect->addOption(
"DozeOff");
135 abilitySelect->addOption(
"DouseFlames");
136 abilitySelect->addOption(
"Flirty");
137 abilitySelect->addOption(
"UndyingFaith");
138 abilitySelect->addOption(
"TooCool");
139 abilitySelect->addOption(
"FakeStudy");
140 abilitySelect->addOption(
"Alcoholic");
141 abilitySelect->addOption(
"TotalBro");
142 abilitySelect->addOption(
"TotalMom");
143 abilitySelect->addOption(
"CantSwim");
144 abilitySelect->addOption(
"AllNighter");
145 abilitySelect->addOption(
"AilmentSaturated");
146 abilitySelect->addOption(
"Adament");
147 abilitySelect->addOption(
"AbsolutePitch");
148 abilitySelect->addOption(
"GamemakerVirus");
149 abilitySelect->addOption(
"Snapshot");
150 abilitySelect->addOption(
"GetBaked");
151 abilitySelect->setMaxHeight(300.f);
152 xpGroupSelect = ComboBox::create();
153 xpGroupSelect->addOption(
"Slow");
154 xpGroupSelect->addOption(
"Standard");
155 xpGroupSelect->addOption(
"Fast");
156 xpGroupSelect->getSignal(Event::ValueChanged).willAlwaysCall(onEdit);
157 evolveLevelEntry = TextEntry::create();
158 evolveLevelEntry->setMode(TextEntry::Mode::Unsigned | TextEntry::Mode::Integer);
159 evolveLevelEntry->getSignal(Event::TextEntered).willAlwaysCall(onEdit);
160 evolveLevelEntry->setRequisition({60.f, 1.f});
162 evolveIdSelect->getSignal(Event::ValueChanged).willAlwaysCall(onEdit);
163 xpRewardMultEntry = TextEntry::create();
164 xpRewardMultEntry->setMode(TextEntry::Mode::Unsigned | TextEntry::Mode::Integer);
165 xpRewardMultEntry->setRequisition({30.f, 1.f});
166 xpRewardMultEntry->getSignal(Event::TextEntered).willAlwaysCall(onEdit);
167 catchRateEntry = TextEntry::create();
168 catchRateEntry->setMode(TextEntry::Mode::Unsigned | TextEntry::Mode::Integer);
169 catchRateEntry->setRequisition({30.f, 1.f});
170 catchRateEntry->getSignal(Event::TextEntered).willAlwaysCall(onEdit);
174 thumbImg = Image::create(img);
175 thumbImg->scaleToSize({100.f, 100.f});
176 playerImg = Image::create(img);
177 playerImg->scaleToSize({150.f, 150.f});
178 opImg = Image::create(img);
179 opImg->scaleToSize({150.f, 150.f});
181 Box::Ptr row = Box::create(rowPack);
182 Box::Ptr col = Box::create(colPack);
183 Box::Ptr subRow = Box::create(rowPack);
185 row->pack(Label::create(
"ID:"),
false,
true);
186 row->pack(idEntry,
false,
true);
187 row->pack(Label::create(
"Name:"),
false,
true);
188 row->pack(nameEntry,
false,
true);
189 row->pack(Label::create(
"Type:"),
false,
true);
190 row->pack(type1Select,
false,
true);
191 row->pack(Label::create(
"Secondary Type:"),
false,
true);
192 row->pack(type2Select,
false,
true);
193 window->pack(row,
true,
false);
195 row = Box::create(rowPack);
196 row->pack(Label::create(
"Description:"),
false,
true);
197 row->pack(descEntry,
true,
true);
198 window->pack(row,
true,
false);
200 row = Box::create(rowPack);
201 col->pack(Label::create(
"Move Pool"));
202 col->pack(movePoolEntry,
true,
true);
203 row->pack(col,
false,
true);
204 col = Box::create(colPack);
205 col->pack(movePoolSelect,
true,
false);
206 col->pack(movePoolAddBut);
207 col->pack(movePoolRmBut);
209 col = Box::create(colPack);
210 col->pack(Label::create(
"Learned Moves"));
211 col->pack(learnMoveEntry,
true,
true);
212 row->pack(col,
false,
true);
213 col = Box::create(colPack);
214 subRow->pack(Label::create(
"Lvl:"),
false,
true);
215 subRow->pack(moveLevelEntry,
true,
true);
216 col->pack(subRow,
true,
false);
217 col->pack(moveLearnSelect,
true,
false);
218 col->pack(moveLearnAddBut);
219 col->pack(moveLearnRmBut);
221 window->pack(row,
true,
false);
223 row = Box::create(rowPack);
224 col = Box::create(colPack);
225 subRow = Box::create(rowPack);
226 col->pack(Label::create(
"Base Stats:"));
227 statEntry.
pack(*col);
228 row->pack(col,
false,
true);
229 col = Box::create(colPack);
230 col->pack(Label::create(
"EV Award"));
231 evAwardEntry.
pack(*col);
232 row->pack(col,
false,
true);
234 col = Box::create(colPack);
235 subRow->pack(Label::create(
"Special Ability:"),
false,
true);
236 subRow->pack(abilitySelect,
true,
true);
237 col->pack(subRow,
true,
false);
238 subRow = Box::create(rowPack);
239 subRow->pack(Label::create(
"Level-up Speed:"),
false,
true);
240 subRow->pack(xpGroupSelect,
true,
true);
241 col->pack(subRow,
true,
false);
242 subRow = Box::create(rowPack);
243 subRow->pack(Label::create(
"Evolve Level:"),
false,
true);
244 subRow->pack(evolveLevelEntry,
true,
true);
245 col->pack(subRow,
true,
false);
246 subRow = Box::create(rowPack);
247 subRow->pack(Label::create(
"Evolve Into:"),
false,
true);
248 subRow->pack(evolveIdSelect,
true,
true);
249 col->pack(subRow,
true,
false);
250 subRow = Box::create(rowPack);
251 subRow->pack(Label::create(
"XP-Yield Multiplier:"),
false,
true);
252 subRow->pack(xpRewardMultEntry,
true,
true);
253 col->pack(subRow,
true,
false);
254 subRow = Box::create(rowPack);
255 subRow->pack(Label::create(
"Wild Catch Rate:"),
false,
true);
256 subRow->pack(catchRateEntry,
true,
true);
257 col->pack(subRow,
true,
false);
258 subRow = Box::create(LinePacker::create(
259 LinePacker::Horizontal, 4.f, LinePacker::Compact, LinePacker::RightAlign));
260 subRow->pack(cancelBut);
261 col->pack(subRow,
true,
false);
262 subRow->pack(applyBut,
false,
true);
263 row->pack(col,
false,
true);
265 col = Box::create(colPack);
266 subRow = Box::create(rowPack);
267 Box::Ptr subCol = Box::create(colPack);
268 subCol->pack(Label::create(
"Thumbnail:"));
269 subCol->pack(thumbImg);
270 subRow->pack(subCol,
false,
true);
271 subRow->pack(imgReload);
272 col->pack(subRow,
true,
false);
273 subRow = Box::create(rowPack);
274 subCol = Box::create(colPack);
275 subCol->pack(Label::create(
"Player:"));
276 subCol->pack(playerImg);
277 subRow->pack(subCol,
false,
true);
278 subCol = Box::create(colPack);
279 subCol->pack(Label::create(
"Opponent:"));
281 subRow->pack(subCol,
false,
true);
282 col->pack(subRow,
true,
false);
283 row->pack(col,
false,
true);
284 window->pack(row,
true,
false);
289 doingNewPeoplemon = ppl == Id::Unknown;
291 movePoolSelect->refresh();
292 moveLearnSelect->refresh();
293 evolveIdSelect->refresh();
295 const std::pair<Type, Type> types =
299 if (!doingNewPeoplemon) {
301 movePool.reserve(mp.size());
302 for (
const MoveId move : mp) { movePool.emplace_back(move); }
305 learnMoves.reserve(lm.size());
306 for (
const auto& mp : lm) { learnMoves.emplace_back(mp.first, mp.second); }
309 idEntry->setInput(doingNewPeoplemon ?
"0" : std::to_string(
static_cast<int>(ppl)));
312 type1Select->setCurrentType(types.first);
313 type2Select->setCurrentType(types.second);
316 for (
const MoveId move : movePool) { movePoolEntry->addOption(moveToStr(move)); }
317 movePoolSelect->selectRandom();
318 for (
const auto& mp : learnMoves) { learnMoveEntry->addOption(learnMoveToStr(mp)); }
319 moveLevelEntry->setInput(
"30");
320 moveLearnSelect->selectRandom();
322 abilitySelect->setSelectedOption(
324 evolveLevelEntry->setInput(doingNewPeoplemon ?
"0" :
327 xpGroupSelect->setSelectedOption(doingNewPeoplemon ? 1 : peoplemonDb.
xpGroups[ppl]);
328 xpRewardMultEntry->setInput(
331 catchRateEntry->setInput(
337 applyBut->setColor(sf::Color(20, 240, 50), sf::Color::Black);
338 parent->pack(window);
339 window->setForceFocus(
true);
342 void PeoplemonEditorWindow::reloadImages() {
343 const Id ppl = parseInput<Id>(idEntry);
352 void PeoplemonEditorWindow::makeDirty() {
354 applyBut->setColor(sf::Color(230, 30, 30), sf::Color::Black);
357 void PeoplemonEditorWindow::onCancel() {
359 if (!warning(
"Discard unsaved changes?")) {
return; }
364 void PeoplemonEditorWindow::close() {
365 window->setForceFocus(
false);
369 void PeoplemonEditorWindow::onMovePoolAdd() {
370 const MoveId move = movePoolSelect->currentMove();
371 if (move == MoveId::Unknown) {
372 error(
"Please select a move to add");
375 for (
const MoveId vm : movePool) {
377 error(
"This move is already in the move pool");
381 movePool.emplace_back(move);
382 movePoolEntry->addOption(moveToStr(move));
386 void PeoplemonEditorWindow::onMovePoolDelete() {
387 const auto io = movePoolEntry->getSelectedOption();
388 if (!io.has_value()) {
389 error(
"Please select a move to delete");
392 const unsigned int i = io.value();
393 if (i >= movePool.size() || i >= movePoolEntry->optionCount()) {
394 error(
"Somehow in invalid state. Move index is greater than move count");
397 movePool.erase(movePool.begin() + i);
398 movePoolEntry->removeOption(i);
402 void PeoplemonEditorWindow::onLearnMoveAdd() {
403 const unsigned int lvl = std::atoi(moveLevelEntry->getInput().c_str());
404 const MoveId move = moveLearnSelect->currentMove();
406 if (lvl == 0 || lvl > 100) {
407 error(
"Level is out of range [1, 100]");
410 if (move == MoveId::Unknown) {
411 error(
"Please select a move to learn");
414 for (
const auto& lp : learnMoves) {
415 if (lp.first == lvl) {
416 error(
"A move is already being learned at this level");
420 learnMoves.emplace_back(lvl, move);
421 learnMoveEntry->addOption(learnMoveToStr(learnMoves.back()));
425 void PeoplemonEditorWindow::onLearnMoveDelete() {
426 const auto io = learnMoveEntry->getSelectedOption();
427 if (!io.has_value()) {
428 error(
"Please select a learned move to delete");
431 const unsigned int i = io.value();
432 if (i >= learnMoves.size() || i >= learnMoveEntry->optionCount()) {
433 error(
"In invalid state. Index is out of range");
436 learnMoves.erase(learnMoves.begin() + i);
437 learnMoveEntry->removeOption(i);
441 void PeoplemonEditorWindow::onSave() {
442 const Id id = parseInput<Id>(idEntry);
443 const Id eid = evolveIdSelect->currentPeoplemon();
444 const unsigned int el = parseInput<unsigned int>(evolveLevelEntry);
445 const unsigned int xpGroup = xpGroupSelect->getSelectedOption();
446 const int xpYieldMult = parseInput<int>(xpRewardMultEntry);
447 const unsigned int cr = parseInput<unsigned int>(catchRateEntry);
448 const Type type = type1Select->currentType() | type2Select->currentType();
451 if (
id == Id::Unknown) {
455 if (openId !=
id && peoplemonDb.
names.find(
id) != peoplemonDb.
names.end()) {
456 if (!warning(
"Id already exists. Overwrite it?")) {
return; }
459 if (nameEntry->getInput().empty()) {
460 error(
"Please enter a name");
463 if (descEntry->getInput().empty()) {
464 error(
"Please a description");
468 error(
"Please select at least one type");
472 if (!warning(
"Base stats are all 0. Continue anyways?")) {
return; }
475 if (movePool.empty()) {
476 if (!warning(
"Valid move pool is empty. Continue?")) {
return; }
478 if (learnMoves.empty()) {
479 if (!warning(
"No moves are learned by leveling. Continue?")) {
return; }
482 if (eid != Id::Unknown) {
483 if (el == 0 || el > 100) {
484 error(
"Evolve level is out of range [1, 100]");
490 error(
"Invalid level-up speed");
493 if (xpYieldMult < 0) {
494 error(
"Invalid XP yield multiplier");
497 if (xpYieldMult == 0) {
498 if (!warning(
"Rewards 0 XP on defeat. Continue?")) {
return; }
501 if (!warning(
"Rewards no EVs. Continue?")) {
return; }
503 if (cr <= 0 || cr > 255) {
504 error(
"Catch rate is out of range [1, 255]");
508 peoplemonDb.
names[id] = nameEntry->getInput();
510 peoplemonDb.
types[id] = type;
516 for (
const MoveId move : movePool) { mp.emplace(move); }
519 for (
const auto& lm : learnMoves) { lms.emplace(lm.first, lm.second); }
525 peoplemonDb.
xpMults[id] = xpYieldMult;
Type
Represents a type that a move or Peoplemon can be. Types may be combined.
SpecialAbility
Represents a special ability that a Peoplemon may have.
All classes and functionality used for implementing the game editor.
Collection of classes and enums pertaining to Peoplemon themselves.
Data structure that holds the underlaying data for all the peoplemon.
std::unordered_map< pplmn::Id, int > catchRates
std::unordered_map< pplmn::Id, pplmn::Id > evolveIds
std::unordered_map< pplmn::Id, pplmn::SpecialAbility > abilities
std::unordered_map< pplmn::Id, int > xpMults
std::unordered_map< pplmn::Id, pplmn::Type > types
std::unordered_map< pplmn::Id, std::unordered_set< pplmn::MoveId > > validMoves
std::unordered_map< pplmn::Id, unsigned int > xpGroups
std::unordered_map< pplmn::Id, pplmn::Stats > stats
std::unordered_map< pplmn::Id, unsigned int > evolveLevels
std::unordered_map< pplmn::Id, pplmn::Stats > evAwards
std::unordered_map< pplmn::Id, std::string > descriptions
std::unordered_map< pplmn::Id, std::unordered_map< unsigned int, pplmn::MoveId > > learnedMoves
std::unordered_map< pplmn::Id, std::string > names
static const std::string & name(MoveId move)
Returns the name of the given move.
static unsigned int evolveLevel(Id id)
Returns the minimum level that the given peoplemon will evolve at.
static std::string playerImage(Id id)
Returns the full path to the image to use in battle for the player peoplemon.
static float catchRate(Id id)
Returns the base catch rate for the given peoplemon.
static SpecialAbility specialAbility(Id id)
Returns the ability of the given peoplemon, if any.
static std::string opponentImage(Id id)
Returns the full path to the image to use in battle for the opponent peoplemon.
static int xpYieldMultiplier(Id id)
Returns the multiplier used when computing the XP award for defeating the given peoplemon.
static Id evolvesInto(Id orig)
Returns what the given peoplemon will evolve into.
static const std::string & description(Id id)
Returns the description of the given Peoplemon.
static const Stats & baseStats(Id id)
Returns the base stats for the given peoplemon.
static const std::string & name(Id id)
Returns the name of the given Peoplemon.
static const Stats & evAward(Id id)
Returns the ev points awarded for defeating the given peoplemon.
static Type type(Id id)
Returns the type of the Peoplemon.
static std::string thumbnailImage(Id id)
Returns the path of the image to render as the peoplemon thumbnail.
Stats for Peoplemon. This struct is used for base stats, EVs, IVs, battle increases/decreases,...
int sum() const
Returns the sum of the 6 stats that are used for EV calculations.
static std::pair< Type, Type > getTypes(Type type)
Returns the primary and secondary type of the given composite type.
static Ptr create(bool enableFilter, const ChangeCb &changeCb={})
Create a new MoveSelector.
std::function< void()> OnChange
void open(bl::gui::GUI *parent, core::pplmn::Id ppl)
PeoplemonEditorWindow(core::file::PeoplemonDB &db, const OnChange &onChange)
static Ptr create(bool allowUnknown=false)
Creates a new peoplemon selector.
Provides an inline GUI entry for stats in either EV mode or IV mode.
void pack(bl::gui::Box &container)
Packs the GUI elements into the given container.
core::pplmn::Stats currentValue() const
Returns the current Stats value entered.
void update(const core::pplmn::Stats &value)
Updates the GUI elements with the given value.
static Ptr create()
Create a new TypeSelector.