15 constexpr
float MoveTime = 0.55f;
23 case Ctx::StorageSelect:
26 case Ctx::UseItemBattle:
28 case Ctx::BattleFaint:
29 case Ctx::BattleMustSwitch:
30 case Ctx::BattleReviveSwitch:
31 case Ctx::BattleSwitch:
46 :
State(s,
bl::engine::StateMask::Menu)
53 menu.create(s.
engine(), s.
engine().renderer().getObserver(), bl::menu::NoSelector::create());
54 actionMenu.create(s.
engine(),
55 s.
engine().renderer().getObserver(),
56 bl::menu::ArrowSelector::create(10.f, sf::Color::Black));
59 s.
engine().renderer().texturePool().getOrLoadTexture(bl::util::FileUtil::joinPath(
61 background.create(s.
engine(), backgroundTxtr);
63 const glm::vec2 MenuPosition(41.f, 5.f);
66 menu.setRootItem(ppl);
67 menu.setPosition(MenuPosition);
68 menu.configureBackground(
69 sf::Color::Transparent, sf::Color::Transparent, 0.f, {0.f, 0.f, 0.f, 0.f});
71 backBut = bl::menu::ImageItem::create(s.
engine().renderer().texturePool().getOrLoadTexture(
73 backBut->overridePosition(
75 backBut->getSize() - MenuPosition - glm::vec2(10.f, 5.f));
76 backBut->getSignal(bl::menu::Item::Selected).willAlwaysCall([
this]() {
77 backBut->getSprite().setColor(sf::Color(180, 100, 80));
79 backBut->getSignal(bl::menu::Item::Deselected).willAlwaysCall([
this]() {
80 backBut->getSprite().setColor(sf::Color::White);
82 backBut->getSignal(bl::menu::Item::Activated).willAlwaysCall([
this]() {
87 bl::menu::TextItem::Ptr info = bl::menu::TextItem::create(
89 bl::menu::TextItem::Ptr move =
91 bl::menu::TextItem::Ptr item = bl::menu::TextItem::create(
93 bl::menu::TextItem::Ptr back =
96 info->getSignal(bl::menu::Item::Activated)
97 .willAlwaysCall(std::bind(&PeoplemonMenu::showInfo,
this));
98 move->getSignal(bl::menu::Item::Activated)
99 .willAlwaysCall(std::bind(&PeoplemonMenu::startMove,
this));
100 item->getSignal(bl::menu::Item::Activated)
101 .willAlwaysCall(std::bind(&PeoplemonMenu::takeItem,
this));
102 back->getSignal(bl::menu::Item::Activated)
103 .willAlwaysCall(std::bind(&PeoplemonMenu::resetAction,
this));
105 actionMenu.setRootItem(info);
106 actionMenu.addItem(item, info.get(), bl::menu::Item::Bottom);
107 actionMenu.addItem(back, item.get(), bl::menu::Item::Right);
108 actionMenu.addItem(move, back.get(), bl::menu::Item::Top);
109 actionMenu.attachExisting(move.get(), info.get(), bl::menu::Item::Right);
110 actionRoot = info.get();
113 bl::menu::TextItem::Ptr sel = bl::menu::TextItem::create(
115 bl::menu::TextItem::Ptr back =
118 sel->getSignal(bl::menu::Item::Activated)
119 .willAlwaysCall(std::bind(&PeoplemonMenu::chosen,
this));
120 back->getSignal(bl::menu::Item::Activated)
121 .willAlwaysCall(std::bind(&PeoplemonMenu::resetAction,
this));
123 actionMenu.setRootItem(sel);
124 actionMenu.addItem(back, sel.get(), bl::menu::Item::Right);
125 actionRoot = back.get();
127 actionMenu.setMinHeight(36.f);
128 actionMenu.setPadding({24.f, 12.f});
129 actionMenu.configureBackground(sf::Color::White, sf::Color::Black, 3.f, {18.f, 4.f, 4.f, 4.f});
130 actionMenu.setDepth(bl::cam::OverlayCamera::MinDepth + 1.f);
136 for (
unsigned int i = 0; i < 6; ++i) { buttons[i].reset(); }
139 const unsigned int col1N = team.size() / 2 + team.size() % 2;
140 const unsigned int col2N = team.size() / 2;
141 for (
unsigned int i = 0; i < col1N; ++i) {
144 for (
unsigned int i = 0; i < col2N; ++i) {
147 for (
unsigned int i = 0; i < team.size(); ++i) {
148 if (team[i].currentHp() == 0) { buttons[i]->setHighlightColor(sf::Color(200, 10, 10)); }
149 else if (outNow >= 0 &&
static_cast<unsigned int>(outNow) == i) {
151 buttons[i]->setHighlightColor(sf::Color(90, 110, 250));
152 buttons[i]->setSelectable(
false);
157 ->getSignal(bl::menu::Item::Activated)
158 .willAlwaysCall(std::bind(&PeoplemonMenu::selected,
this, buttons[i].get()));
160 menu.setRootItem(buttons[0]);
161 for (
unsigned int i = 1; i < col1N; ++i) {
162 menu.addItem(buttons[i * 2], buttons[(i - 1) * 2].get(), bl::menu::Item::Bottom);
164 for (
unsigned int i = 0; i < col2N; ++i) {
165 menu.addItem(buttons[i * 2 + 1], buttons[i * 2].get(), bl::menu::Item::Right);
168 buttons[i * 2 + 1].get(), buttons[(i - 1) * 2 + 1].get(), bl::menu::Item::Bottom);
173 menu.addItem(backBut, buttons[(col2N - 1) * 2 + 1].get(), bl::menu::Item::Bottom);
175 backBut.get(), buttons[(col1N - 1) * 2].get(), bl::menu::Item::Bottom,
false);
177 else { menu.addItem(backBut, buttons[(col1N - 1) * 2].get(), bl::menu::Item::Bottom); }
180 if (canCancel()) { menu.setSelectedItem(backBut.get()); }
181 else { menu.setSelectedItem(buttons[0].get()); }
182 for (
unsigned int i = 0; i < team.size(); ++i) {
183 if (buttons[i]->isSelectable()) {
184 menu.setSelectedItem(buttons[i].get());
188 if (!menu.getSelectedItem()->isSelectable()) {
189 BL_LOG_CRITICAL <<
"In menu that cannot be canceled with no selectable Peoplemon!";
192 state = MenuState::Browsing;
194 inputDriver.drive(&menu);
195 systems.
engine().inputSystem().getActor().addListener(*
this);
196 if (chosenPeoplemon) *chosenPeoplemon = -1;
198 auto overlay = engine.renderer().getObserver().pushScene<bl::rc::Overlay>();
199 background.addToScene(overlay, bl::rc::UpdateSpeed::Static);
200 menu.addToOverlay(background.entity());
201 actionMenu.addToOverlay(background.entity());
202 actionMenu.setHidden(
true);
206 inputDriver.drive(
nullptr);
207 systems.
engine().inputSystem().getActor().removeListener(*
this);
208 engine.renderer().getObserver().popScene();
211 bool PeoplemonMenu::canCancel()
const {
222 void PeoplemonMenu::setSelectable(
unsigned int i) {
227 buttons[i]->setSelectable(team[i].currentHp() > 0);
230 buttons[i]->setSelectable(team[i].currentHp() > 0 &&
static_cast<int>(i) != outNow);
233 buttons[i]->setSelectable(team[i].currentHp() == 0 &&
static_cast<int>(i) != outNow);
242 !team[i].knowsMove(mid)) {
243 buttons[i]->setSelectable(
true);
244 buttons[i]->setHighlightColor(sf::Color(78, 191, 191));
247 buttons[i]->setSelectable(
false);
248 buttons[i]->setHighlightColor(sf::Color(242, 83, 51));
251 else { buttons[i]->setSelectable(
true); }
254 buttons[i]->setSelectable(
true);
260 const auto updateItems = [
this, dt]() {
261 for (
int i = 0; i < 6; ++i) {
262 if (buttons[i]) { buttons[i]->update(dt); }
265 const auto buttonsSynced = [
this]() ->
bool {
266 for (
int i = 0; i < 6; ++i) {
267 if (buttons[i] && !buttons[i]->synced())
return false;
273 case MenuState::Moving: {
274 const glm::vec2 d = moveVel * dt;
275 const float mx = std::abs(std::max(d.x, 5.f));
276 const float my = std::abs(std::max(d.y, 5.f));
277 buttons[mover1]->overridePosition(buttons[mover1]->getPosition() + d);
278 buttons[mover2]->overridePosition(buttons[mover2]->getPosition() - d);
279 if (std::abs(buttons[mover1]->getPosition().x - mover1Dest.x) <= mx &&
280 std::abs(buttons[mover1]->getPosition().y - mover1Dest.y) < my) {
285 case MenuState::UsingItem:
287 if (buttonsSynced()) { state = MenuState::UsingItemWaitMessage; }
290 case MenuState::UsingItemWaitView:
295 case MenuState::WaitingForgetConfirm:
296 case MenuState::WaitingForgetChoice:
297 case MenuState::UsingItemWaitMessage:
298 case MenuState::ShowingMessage:
304 bool PeoplemonMenu::observe(
const bl::input::Actor&,
unsigned int ctrl, bl::input::DispatchType,
308 if (state == MenuState::SelectingMove) { cleanupMove(
false); }
309 else if (state == MenuState::Browsing) {
310 if (actionOpen) { resetAction(); }
314 else { inputDriver.sendControl(ctrl, fromEvent); }
318 void PeoplemonMenu::selected(menu::PeoplemonButton* b) {
321 if (buttons[i].get() == b)
break;
323 if (buttons[i].get() != b) {
324 BL_LOG_ERROR <<
"Invalid button pointer";
331 actionMenu.setPosition(
332 buttons[i]->getPosition() + buttons[i]->getSize() +
333 glm::vec2(menu.getBounds().left, menu.getBounds().top) -
334 glm::vec2(actionMenu.getBounds().width, actionMenu.getBounds().height));
335 actionMenu.setSelectedItem(actionRoot);
336 actionMenu.setHidden(
false);
337 inputDriver.drive(&actionMenu);
343 mover1Dest = buttons[mover2]->getPosition();
344 mover2Dest = buttons[mover1]->getPosition();
345 moveVel = (mover1Dest - mover2Dest) / MoveTime;
346 state = MenuState::Moving;
347 inputDriver.drive(
nullptr);
357 void PeoplemonMenu::showInfo() {
362 void PeoplemonMenu::startMove() {
364 state = MenuState::SelectingMove;
365 inputDriver.drive(&menu);
366 buttons[mover1]->setHighlightColor(sf::Color(40, 120, 230));
368 if (mover1 % 2 == 0) {
369 if (buttons[mover1 + 1]) { menu.setSelectedItem(buttons[mover1 + 1].get()); }
370 else { menu.setSelectedItem(buttons[mover1 - 2].get()); }
372 else { menu.setSelectedItem(buttons[mover1 - 1].get()); }
376 void PeoplemonMenu::cleanupMove(
bool c) {
379 sf::Color(200, 10, 10));
380 state = MenuState::Browsing;
384 inputDriver.drive(&actionMenu);
385 menu.setSelectedItem(buttons[mover1].get());
388 buttons[mover1]->overridePosition(mover1Dest);
389 buttons[mover2]->overridePosition(mover2Dest);
390 std::swap(buttons[mover1], buttons[mover2]);
394 menu.setSelectedItem(buttons[mover2].get());
395 inputDriver.drive(&menu);
399 void PeoplemonMenu::takeItem() {
407 std::bind(&PeoplemonMenu::messageDone,
this));
411 std::bind(&PeoplemonMenu::messageDone,
this));
413 state = MenuState::ShowingMessage;
414 inputDriver.drive(
nullptr);
418 void PeoplemonMenu::resetAction() {
419 state = MenuState::Browsing;
420 inputDriver.drive(&menu);
424 void PeoplemonMenu::closeActionMenu() {
426 actionMenu.setHidden(
true);
429 void PeoplemonMenu::connectButtons() {
434 for (
unsigned int i = 0; i < n; i += 2) {
438 menu.attachExisting(buttons[i].get(), buttons[i - 2].get(), bl::menu::Item::Bottom);
440 if (buttons[i + 1]) {
441 menu.attachExisting(buttons[i + 1].get(), buttons[i].get(), bl::menu::Item::Right);
444 buttons[i + 1].get(), buttons[i - 1].get(), bl::menu::Item::Bottom);
453 menu.attachExisting(backBut.get(), buttons[mr].get(), bl::menu::Item::Bottom);
454 menu.attachExisting(backBut.get(), buttons[ml].get(), bl::menu::Item::Bottom,
false);
456 else { menu.attachExisting(backBut.get(), buttons[ml].get(), bl::menu::Item::Bottom); }
460 void PeoplemonMenu::chosen() {
461 bool closeMenu =
true;
469 state = MenuState::ShowingMessage;
471 std::bind(&PeoplemonMenu::messageDone,
this));
479 state = MenuState::ShowingMessage;
481 "Choose a dead Peoplemon so the sacrifice was not in vain!",
482 std::bind(&PeoplemonMenu::messageDone,
this));
490 state = MenuState::ShowingMessage;
492 " is already holding something",
493 std::bind(&PeoplemonMenu::messageDone,
this));
508 std::bind(&PeoplemonMenu::messageDone,
this));
509 buttons[mover1]->sync(ppl);
510 state = MenuState::UsingItem;
519 for (
int j = 0; j < 4; ++j) {
529 " but already knows 4 moves.");
531 "Should " + ppl.
name() +
" forget a move to learn " + move +
"?",
533 std::bind(&PeoplemonMenu::confirmMoveDelete,
this, std::placeholders::_1));
534 state = MenuState::WaitingForgetConfirm;
540 std::bind(&PeoplemonMenu::messageDone,
this));
541 state = MenuState::UsingItemWaitMessage;
545 BL_LOG_ERROR <<
"Invalid tm: " << useItem;
547 std::bind(&PeoplemonMenu::messageDone,
this));
548 state = MenuState::ShowingMessage;
553 state = MenuState::ShowingMessage;
555 std::bind(&PeoplemonMenu::messageDone,
this));
568 inputDriver.drive(
nullptr);
570 if (chosenPeoplemon) *chosenPeoplemon = mover1;
574 void PeoplemonMenu::messageDone() {
576 case MenuState::UsingItem:
577 state = MenuState::UsingItemWaitView;
579 case MenuState::UsingItemWaitMessage:
583 state = MenuState::Browsing;
584 inputDriver.drive(&menu);
589 void PeoplemonMenu::confirmMoveDelete(
const std::string& choice) {
591 if (choice ==
"Yes") {
592 std::vector<std::string> moves;
594 for (
int i = 0; i < 4; ++i) {
597 moves.emplace_back(
"Cancel");
601 std::bind(&PeoplemonMenu::delMove,
this, std::placeholders::_1));
602 state = MenuState::WaitingForgetChoice;
608 std::bind(&PeoplemonMenu::messageDone,
this));
609 state = MenuState::ShowingMessage;
613 void PeoplemonMenu::delMove(
const std::string& mn) {
619 for (
int j = 0; j < 4; ++j) {
631 std::bind(&PeoplemonMenu::messageDone,
this));
632 state = MenuState::UsingItemWaitMessage;
636 std::bind(&PeoplemonMenu::messageDone,
this));
637 state = MenuState::ShowingMessage;
Id
Represents an item in its simplist form.
@ TM
TM's. Covers ids [201, 499].
Parent namespace for all functionality unique to the game.
Context
Represents where the menu is being opened from.
static std::string getUseLine(Id item, const pplmn::OwnedPeoplemon &ppl)
Returns the text to display when the item is used on the given peoplemon.
static Category getCategory(Id item)
Returns the category of the given item.
static pplmn::MoveId getTmMove(Id tm)
Returns the move taught by the TM.
static const std::string & getName(item::Id item)
Returns the name of the given item.
static void useOnPeoplemon(Id item, pplmn::OwnedPeoplemon &ppl, std::vector< pplmn::OwnedPeoplemon > *team=nullptr, std::vector< pplmn::BattlePeoplemon > *battleTeam=nullptr)
Applies the given item to the peoplemon.
static bool hasEffectOnPeoplemon(Id item, const pplmn::OwnedPeoplemon &ppl)
Returns whether or not the given item will affect the peoplemon.
static const std::string & name(MoveId move)
Returns the name of the given move.
Represents a move owned by a peoplemon.
MoveId id
The id of the move.
Represents an instance of a peoplemon. Can be a wild peoplemon, trainer, or player peoplemon....
const OwnedMove * knownMoves() const
Returns the moves known by this Peoplemon.
const std::string & name() const
Returns the name of this peoplemon, custom or defualt.
static bool canLearnMove(Id ppl, MoveId move)
Returns whether or not the given Peoplemon can learn the given move.
bool removeItem(item::Id item, unsigned int qty=1)
Removes the given item from the bag.
void addItem(item::Id item, unsigned int qty=1)
Adds the given item to the bag.
std::vector< pplmn::OwnedPeoplemon > peoplemon
static const sf::VulkanFont & MenuFont()
static bl::audio::AudioSystem::Handle MenuBackSound()
static const std::string & MenuImagePath()
static int WindowHeight()
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 promptUser(const std::string &prompt, const std::vector< std::string > &choices, const Callback &cb)
Asks the player a question through the HUD.
player::State & state()
Returns the state of the player.
Owns all primary systems and a reference to the engine.
const bl::engine::Engine & engine() const
Const accessor for the Engine.
Player & player()
Returns the player system.
HUD & hud()
Returns the HUD.
static Ptr create(const core::pplmn::OwnedPeoplemon &ppl)
Creates a new peoplemon button from the peoplemon.
std::shared_ptr< PeoplemonButton > Ptr
static bl::engine::State::Ptr create(core::system::Systems &systems, const core::pplmn::OwnedPeoplemon &ppl)
Create a new PeoplemonInfo engine state.
Provides the menu for viewing and selecting Peoplemon.
virtual void activate(bl::engine::Engine &engine) override
Subscribes the menu to the engine event bus.
virtual const char * name() const override
Returns "PeoplemonMenu".
virtual void update(bl::engine::Engine &, float dt, float) override
Does nothing.
virtual void deactivate(bl::engine::Engine &engine) override
Unsubscribes the menu to the engine event bus.
static bl::engine::State::Ptr create(core::system::Systems &systems, Context ctx, int outNow=-1, int *chosen=nullptr, core::item::Id item=core::item::Id::None)
Creates a new PeoplemonMenu state.
Parent to all game states. Provides some commonly required data like core game systems.
core::system::Systems & systems