Implement screenshots in SaveLoad menu

Also refactor item selection in ListBox.
This commit is contained in:
Vsevolod Kremianskii 2021-05-31 16:09:37 +07:00
parent bdedd88d9d
commit 61ac0f320d
9 changed files with 95 additions and 30 deletions

View file

@ -565,6 +565,11 @@ void Game::openInGame() {
}
void Game::openInGameMenu(InGameMenu::Tab tab) {
// Take a screenshot to be used in SaveLoad menu
_graphics.window().clear();
_scene.worldRenderPipeline().setTakeScreenshot(true);
_scene.worldRenderPipeline().render();
setCursorType(CursorType::Default);
switch (tab) {
case InGameMenu::Tab::Equipment:
@ -805,10 +810,6 @@ bool Game::handleKeyDown(const SDL_KeyboardEvent &event) {
}
break;
case SDLK_p:
_scene.worldRenderPipeline().setTakeScreenshot(true);
return true;
default:
break;
}

View file

@ -24,6 +24,7 @@
#include <boost/iostreams/stream.hpp>
#include "../common/streamutil.h"
#include "../graphics/texture/tgawriter.h"
#include "../resource/format/erfreader.h"
#include "../resource/format/erfwriter.h"
#include "../resource/format/gffwriter.h"
@ -33,6 +34,7 @@ namespace io = boost::iostreams;
using namespace std;
using namespace reone::graphics;
using namespace reone::resource;
namespace reone {
@ -40,8 +42,11 @@ namespace reone {
namespace game {
static constexpr int kNfoBufferSize = 1024;
static constexpr int kScreenBufferSize = 262144;
void Game::saveToFile(const fs::path &path) {
// Prepare savenfo RES
GffStruct::Field nfoLastModuleFld(GffStruct::FieldType::CExoString, "LastModule");
nfoLastModuleFld.strValue = _module->name();
@ -65,8 +70,28 @@ void Game::saveToFile(const fs::path &path) {
nfoRes.resType = ResourceType::Res;
nfoRes.data = move(nfoResData);
// Prepare screen TGA
ByteArray screenBuffer;
screenBuffer.resize(kScreenBufferSize);
io::array_sink screenSink(&screenBuffer[0], kScreenBufferSize);
auto screenStream = make_shared<io::stream<io::array_sink>>(screenSink);
shared_ptr<Texture> screenshot(_scene.worldRenderPipeline().screenshot());
TgaWriter tga(screenshot);
tga.save(*screenStream);
screenBuffer.resize(screenStream->tellp());
ErfWriter::Resource screenRes;
screenRes.resRef = "screen";
screenRes.resType = ResourceType::Tga;
screenRes.data = move(screenBuffer);
// Save ERF
ErfWriter erf;
erf.add(move(nfoRes));
erf.add(move(screenRes));
erf.save(ErfWriter::FileType::ERF, path);
}

View file

@ -24,6 +24,7 @@
#include "../../common/log.h"
#include "../../common/streamutil.h"
#include "../../graphics/texture/tgareader.h"
#include "../../gui/control/listbox.h"
#include "../../resource/format/erfreader.h"
#include "../../resource/format/gffreader.h"
@ -37,6 +38,7 @@ namespace fs = boost::filesystem;
using namespace std;
using namespace reone::graphics;
using namespace reone::gui;
using namespace reone::resource;
@ -68,7 +70,7 @@ void SaveLoad::load() {
hideControl("LBL_AREANAME");
ListBox &lbGames = getControl<ListBox>("LB_GAMES");
lbGames.setSelectionMode(ListBox::SelectionMode::Hilight);
lbGames.setSelectionMode(ListBox::SelectionMode::OnClick);
lbGames.setPadding(3);
Control &protoItem = lbGames.protoItem();
@ -124,11 +126,19 @@ static SavedGame peekSavedGame(const fs::path &path) {
erf.load(path);
shared_ptr<ByteArray> nfoData(erf.find("savenfo", ResourceType::Res));
GffReader nfo;
nfo.load(wrap(nfoData));
shared_ptr<Texture> screen;
shared_ptr<ByteArray> screenData(erf.find("screen", ResourceType::Tga));
if (screenData) {
TgaReader tga("screen", TextureUsage::GUI);
tga.load(wrap(screenData));
screen = tga.texture();
}
SavedGame result;
result.screen = move(screen);
result.lastModule = nfo.root()->getString("LastModule");
return move(result);
@ -199,7 +209,7 @@ void SaveLoad::onClick(const string &control) {
int SaveLoad::getSelectedSaveNumber() const {
ListBox &lbGames = getControl<ListBox>("LB_GAMES");
int hilightedIdx = lbGames.hilightedIndex();
int hilightedIdx = lbGames.selectedItemIndex();
if (hilightedIdx == -1) return -1;
string tag(lbGames.getItemAt(hilightedIdx).tag);
@ -242,6 +252,36 @@ void SaveLoad::deleteGame(int number) {
}
}
void SaveLoad::onListBoxItemClick(const string &control, const string &item) {
if (control != "LB_GAMES") return;
// Get save number by item tag
int selectedSaveNumber = -1;
auto &lbGames = getControl<ListBox>("LB_GAMES");
for (int i = 0; i < lbGames.getItemCount(); ++i) {
auto &lbItem = lbGames.getItemAt(i);
if (lbItem.tag == item) {
selectedSaveNumber = stoi(lbItem.tag);
break;
}
}
// Get save screenshot by save number
shared_ptr<Texture> screenshot;
if (selectedSaveNumber != -1) {
for (auto &save : _saves) {
if (save.number == selectedSaveNumber) {
screenshot = save.save.screen;
break;
}
}
}
// Set screenshot
Label &lblScreenshot = getControl<Label>("LBL_SCREENSHOT");
lblScreenshot.setBorderFill(move(screenshot));
}
} // namespace game
} // namespace reone

View file

@ -57,6 +57,7 @@ private:
void indexSavedGame(boost::filesystem::path path);
void onClick(const std::string &control) override;
void onListBoxItemClick(const std::string &control, const std::string &item) override;
void saveGame(int number);
void loadGame(int number);

View file

@ -19,11 +19,14 @@
#include <string>
#include "../graphics/texture/texture.h"
namespace reone {
namespace game {
struct SavedGame {
std::shared_ptr<graphics::Texture> screen;
std::string lastModule;
};

View file

@ -47,7 +47,7 @@ ListBox::ListBox(GUI *gui) : Control(gui, ControlType::ListBox) {
void ListBox::clearItems() {
_items.clear();
_itemOffset = 0;
_hilightedIndex = -1;
_selectedItemIndex = -1;
}
void ListBox::addItem(Item &&item) {
@ -74,7 +74,7 @@ void ListBox::addTextLinesAsItems(const string &text) {
}
void ListBox::clearSelection() {
_hilightedIndex = -1;
_selectedItemIndex = -1;
}
void ListBox::load(const GffStruct &gffs) {
@ -94,8 +94,8 @@ void ListBox::load(const GffStruct &gffs) {
}
bool ListBox::handleMouseMotion(int x, int y) {
if (_mode == SelectionMode::Propagate) {
_hilightedIndex = getItemIndex(y);
if (_selectionMode == SelectionMode::OnHover) {
_selectedItemIndex = getItemIndex(y);
}
return false;
}
@ -168,14 +168,10 @@ bool ListBox::handleClick(int x, int y) {
int itemIdx = getItemIndex(y);
if (itemIdx == -1) return false;
switch (_mode) {
case SelectionMode::Hilight:
_hilightedIndex = itemIdx;
break;
case SelectionMode::Propagate:
_gui->onListBoxItemClick(_tag, _items[itemIdx].tag);
break;
if (_selectionMode == SelectionMode::OnClick) {
_selectedItemIndex = itemIdx;
}
_gui->onListBoxItemClick(_tag, _items[itemIdx].tag);
return true;
}
@ -197,7 +193,7 @@ void ListBox::draw(const glm::ivec2 &offset, const vector<string> &text) {
if (_protoMatchContent) {
_protoItem->setHeight(static_cast<int>(item._textLines.size() * (_protoItem->text().font->height() + _padding)));
}
_protoItem->setFocus(_hilightedIndex == itemIdx);
_protoItem->setFocus(_selectedItemIndex == itemIdx);
auto imageButton = dynamic_pointer_cast<ImageButton>(_protoItem);
if (imageButton) {
@ -239,8 +235,8 @@ void ListBox::stretch(float x, float y, int mask) {
void ListBox::setFocus(bool focus) {
Control::setFocus(focus);
if (!focus && _mode == SelectionMode::Propagate) {
_hilightedIndex = -1;
if (!focus && _selectionMode == SelectionMode::OnHover) {
_selectedItemIndex = -1;
}
}
@ -259,7 +255,7 @@ void ListBox::setProtoItemType(ControlType type) {
}
void ListBox::setSelectionMode(SelectionMode mode) {
_mode = mode;
_selectionMode = mode;
}
void ListBox::setProtoMatchContent(bool match) {

View file

@ -30,8 +30,8 @@ constexpr int kDefaultSlotCount = 6;
class ListBox : public Control {
public:
enum class SelectionMode {
Hilight,
Propagate
OnHover,
OnClick
};
struct Item {
@ -71,17 +71,17 @@ public:
Control &protoItem() const { return *_protoItem; }
Control &scrollBar() const { return *_scrollBar; }
int hilightedIndex() const { return _hilightedIndex; }
int selectedItemIndex() const { return _selectedItemIndex; }
private:
SelectionMode _mode { SelectionMode::Propagate };
SelectionMode _selectionMode { SelectionMode::OnHover };
ControlType _protoItemType { ControlType::Invalid };
std::shared_ptr<Control> _protoItem;
std::shared_ptr<Control> _scrollBar;
std::vector<Item> _items;
int _slotCount { 0 };
int _itemOffset { 0 };
int _hilightedIndex { -1 };
int _selectedItemIndex { -1 };
int _itemMargin { 0 };
bool _protoMatchContent { false }; /**< proto item height must match its content */

View file

@ -439,9 +439,6 @@ void WorldRenderPipeline::drawResult() {
void WorldRenderPipeline::saveScreenshot() {
_screenshotColor->bind();
_screenshotColor->flushGPUToCPU();
TgaWriter tga(_screenshotColor);
tga.save("screen.tga");
}
} // namespace scene

View file

@ -40,6 +40,8 @@ public:
void init();
void render();
std::shared_ptr<graphics::Texture> screenshot() { return _screenshotColor; }
void setTakeScreenshot(bool take) { _takeScreenshot = take; }
private: