123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- // SuperTux
- // Copyright (C) 2021 A. Semphris <semphris@protonmail.com>
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program. If not, see <http://www.gnu.org/licenses/>.
- #include "worldmap/world_select.hpp"
- #include <algorithm>
- #include "control/controller.hpp"
- #include "math/util.hpp"
- #include "supertux/constants.hpp"
- #include "supertux/fadetoblack.hpp"
- #include "supertux/resources.hpp"
- #include "supertux/screen_manager.hpp"
- #include "util/log.hpp"
- #include "util/reader_document.hpp"
- #include "util/reader_mapping.hpp"
- #include "video/compositor.hpp"
- #include "video/drawing_context.hpp"
- #include "video/surface.hpp"
- #include "worldmap/worldmap.hpp"
- namespace worldmap {
- const float WorldSelect::s_torque = 0.75f;
- WorldSelect::WorldSelect(const std::string& current_world_filename) :
- m_enabled(false),
- m_worlds(),
- m_current_world(),
- m_selected_world(),
- m_angle(),
- m_bkg()
- {
- std::vector<std::string> worlds;
- auto& vm = SquirrelVirtualMachine::current()->get_vm();
- SQInteger oldtop = sq_gettop(vm.get_vm());
- sq_pushroottable(vm.get_vm());
- try {
- vm.get_table_entry("state");
- vm.get_table_entry("world_select");
- worlds = vm.get_table_keys();
- } catch(const std::exception&) {}
- if (worlds.size() > 0)
- std::reverse(worlds.begin(), worlds.end());
- // Only worlds with a set prefix, which also are numbered starting with 1, will be ordered properly.
- // This is a probably a poor solution, but I can't think of any other. - Daniel
- std::string prefix = "";
- vm.get_string("prefix", prefix);
- if (!prefix.empty())
- {
- for (int i = 0; unsigned(i) < worlds.size(); i++)
- {
- worlds[i] = prefix + std::to_string(i+1) + "/worldmap.stwm";
- }
- }
- int i = 0;
- for (const auto& world : worlds) {
- sq_pushroottable(vm.get_vm());
- try {
- vm.get_table_entry("state");
- vm.get_table_entry("world_select");
- vm.get_table_entry(world);
- bool unlocked = false;
- vm.get_bool("unlocked", unlocked);
- WMdata wm;
- wm.filename = world;
- wm.unlocked = unlocked;
- ReaderDocument doc = ReaderDocument::from_file(world);
- if (!doc.get_root().get_mapping().get("name", wm.name))
- {
- log_warning << "No name for worldmap " << world << std::endl;
- continue;
- }
- std::string icon_path = "";
- if (!doc.get_root().get_mapping().get(unlocked ? "icon" : "icon-locked", icon_path))
- {
- log_warning << "No icon (" << (unlocked ? "unlocked" : "locked") << ") for worldmap " << world << std::endl;
- continue;
- }
- wm.icon = Surface::from_file(icon_path);
- if (!wm.icon)
- {
- log_warning << "Icon not found for worldmap " << world << ": "
- << icon_path << std::endl;
- continue;
- }
- m_worlds.push_back(wm);
- if (current_world_filename == world)
- {
- m_current_world = i;
- std::string bkg_path = "";
- if (doc.get_root().get_mapping().get("bkg", bkg_path))
- {
- m_bkg = Surface::from_file(bkg_path);
- }
- else
- {
- m_bkg = Surface::from_file("/images/worlds/background/default.png");
- }
- }
- i++;
- } catch(const std::exception& e) {
- log_info << "Exception thrown while generating world state: " << e.what() << std::endl;
- }
- }
- sq_settop(vm.get_vm(), oldtop);
- m_selected_world = m_current_world;
- m_angle = static_cast<float>(m_current_world) / static_cast<float>(i) * math::PI * 2;
- if (m_worlds.empty())
- {
- log_warning << "No maps on world select" << std::endl;
- }
- }
- WorldSelect::~WorldSelect()
- {
- }
- void
- WorldSelect::setup()
- {
- if (m_worlds.empty())
- ScreenManager::current()->pop_screen(std::make_unique<FadeToBlack>(FadeToBlack::Direction::FADEOUT, 0.25f));
- m_enabled = true;
- }
- void
- WorldSelect::leave()
- {
- m_enabled = false;
- }
- void
- WorldSelect::draw(Compositor& compositor)
- {
- auto& context = compositor.make_context();
- context.color().draw_filled_rect(context.get_rect(), Color(), -1000);
- context.color().draw_surface_scaled(m_bkg, context.get_viewport(), -999);
- std::string name_to_display;
- float distance = 0.f;
- int i = 0;
- for (const auto& world : m_worlds)
- {
- float angle = m_angle - static_cast<float>(i) /
- static_cast<float>(m_worlds.size()) * math::PI * 2;
- float size = 1.f + (std::cos(angle) - 1.f) / 4.f;
- Rectf rect = world.icon->get_region();
- rect = Rectf(0, 0, rect.get_width() * size / 2.f, rect.get_height() * size / 2.f);
- rect.move(Vector(context.get_width() / 2.f - rect.get_width() / 2.f,
- context.get_height() / 2.f - rect.get_height() / 2.f));
- rect.move(Vector(std::sin(angle) * -context.get_width() / 4.f, 0.f));
- PaintStyle ps;
- ps.set_alpha(std::cos(angle) * .5f + .5f);
- context.color().draw_surface_scaled(world.icon, rect,
- static_cast<int>(rect.get_bottom()), ps);
- if (std::cos(angle) > distance)
- {
- distance = std::cos(angle);
- name_to_display = world.unlocked ? world.name : "???";
- }
- i++;
- }
- float halfangle = 1.f / static_cast<float>(m_worlds.size()) * math::PI * 2;
- float o = distance * (.5f - std::cos(halfangle));
- context.color().draw_text(Resources::big_font, name_to_display,
- Vector(context.get_width() / 2.f,
- context.get_height() * 3.f / 4.f + pow(10.f - o * 10.f, 2.f)),
- FontAlignment::ALIGN_CENTER,
- 10,
- Color(1.f, 1.f, 1.f,static_cast<float>(pow(o, 2.f)) * 4.f));
- }
- void
- WorldSelect::update(float dt_sec, const Controller& controller)
- {
- float target = static_cast<float>(m_selected_world) /
- static_cast<float>(m_worlds.size()) * math::PI * 2;
- while (m_angle - target > math::PI)
- target += math::PI * 2;
- while (m_angle - target < -math::PI)
- target -= math::PI * 2;
- m_angle = m_angle * s_torque + target * (1.f - s_torque);
- if (!m_enabled)
- return;
- if (controller.pressed_any(Control::ESCAPE, Control::ACTION))
- {
- m_enabled = false;
- ScreenManager::current()->pop_screen(std::make_unique<FadeToBlack>(FadeToBlack::Direction::FADEOUT, 0.25f));
- return;
- }
- if (controller.pressed(Control::LEFT)) {
- m_selected_world--;
- // Modulo doesn't work for some reason
- if (m_selected_world < 0)
- {
- m_selected_world += static_cast<int>(m_worlds.size());
- }
- }
- if (controller.pressed(Control::RIGHT)) {
- m_selected_world++;
- m_selected_world %= static_cast<int>(m_worlds.size());
- }
- if (controller.pressed(Control::JUMP) && m_worlds[m_selected_world].unlocked) {
- m_enabled = false;
- ScreenManager::current()->pop_screen(std::make_unique<FadeToBlack>(FadeToBlack::Direction::FADEOUT, 0.25f));
- worldmap::WorldMap::current()->change(m_worlds[m_selected_world].filename, "", DEFAULT_SPAWNPOINT_NAME);
- return;
- }
- }
- IntegrationStatus
- WorldSelect::get_status() const
- {
- IntegrationStatus status;
- status.m_details.push_back("In world select");
- if (!m_worlds.empty())
- {
- status.m_details.push_back(m_worlds[m_current_world].name);
- }
- return status;
- }
- } // namespace worldmap
- /* EOF */
|