1 /*
2 * This file is part of EasyRPG Player.
3 *
4 * EasyRPG Player is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * EasyRPG Player is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 // Headers
19 #include <algorithm>
20 #include <sstream>
21 #include <vector>
22 #include "baseui.h"
23 #include "cache.h"
24 #include <lcf/data.h>
25 #include "game_system.h"
26 #include "game_party.h"
27 #include "input.h"
28 #include <lcf/lsd/reader.h>
29 #include "player.h"
30 #include "scene_file.h"
31 #include "bitmap.h"
32 #include <lcf/reader_util.h>
33 #include "output.h"
34
35 constexpr int arrow_animation_frames = 20;
36
Scene_File(std::string message)37 Scene_File::Scene_File(std::string message) :
38 message(message) {
39 }
40
MakeBorderSprite(int y)41 std::unique_ptr<Sprite> Scene_File::MakeBorderSprite(int y) {
42 auto bitmap = Bitmap::Create(SCREEN_TARGET_WIDTH, 8, Cache::System()->GetBackgroundColor());
43 auto sprite = std::unique_ptr<Sprite>(new Sprite());
44 sprite->SetVisible(true);
45 sprite->SetZ(Priority_Window + 1);
46 sprite->SetBitmap(bitmap);
47 sprite->SetX(0);
48 sprite->SetY(y);
49 return sprite;
50 }
51
MakeArrowSprite(bool down)52 std::unique_ptr<Sprite> Scene_File::MakeArrowSprite(bool down) {
53 Rect rect = Rect(40, (down ? 16 : 8), 16, 8);
54 auto bitmap = Bitmap::Create(*(Cache::System()), rect);
55 auto sprite = std::unique_ptr<Sprite>(new Sprite());
56 sprite->SetVisible(false);
57 sprite->SetZ(Priority_Window + 2);
58 sprite->SetBitmap(bitmap);
59 sprite->SetX(SCREEN_TARGET_WIDTH / 2 - 8);
60 sprite->SetY(down ? 232 : 32);
61 return sprite;
62 }
63
CreateHelpWindow()64 void Scene_File::CreateHelpWindow() {
65 help_window.reset(new Window_Help(0, 0, SCREEN_TARGET_WIDTH, 32));
66 help_window->SetText(message);
67 help_window->SetZ(Priority_Window + 1);
68 }
69
PopulatePartyFaces(Window_SaveFile & win,int,lcf::rpg::Save & savegame)70 void Scene_File::PopulatePartyFaces(Window_SaveFile& win, int /* id */, lcf::rpg::Save& savegame) {
71 win.SetParty(savegame.title);
72 win.SetHasSave(true);
73 }
74
UpdateLatestTimestamp(int id,lcf::rpg::Save & savegame)75 void Scene_File::UpdateLatestTimestamp(int id, lcf::rpg::Save& savegame) {
76 if (savegame.title.timestamp > latest_time) {
77 latest_time = savegame.title.timestamp;
78 latest_slot = id;
79 }
80 }
81
PopulateSaveWindow(Window_SaveFile & win,int id)82 void Scene_File::PopulateSaveWindow(Window_SaveFile& win, int id) {
83 // Try to access file
84 std::stringstream ss;
85 ss << "Save" << (id <= 8 ? "0" : "") << (id + 1) << ".lsd";
86
87 std::string file = fs.FindFile(ss.str());
88
89 if (!file.empty()) {
90 // File found
91 auto save_stream = FileFinder::Save().OpenInputStream(file);
92 if (!save_stream) {
93 Output::Debug("Save {} read error", file);
94 win.SetCorrupted(true);
95 return;
96 }
97
98 std::unique_ptr<lcf::rpg::Save> savegame = lcf::LSD_Reader::Load(save_stream, Player::encoding);
99
100 if (savegame) {
101 PopulatePartyFaces(win, id, *savegame);
102 UpdateLatestTimestamp(id, *savegame);
103 } else {
104 Output::Debug("Save {} corrupted", file);
105 win.SetCorrupted(true);
106 }
107 }
108 }
109
Start()110 void Scene_File::Start() {
111 CreateHelpWindow();
112 border_top = Scene_File::MakeBorderSprite(32);
113
114 // Refresh File Finder Save Folder
115 fs = FileFinder::Save();
116
117 for (int i = 0; i < Utils::Clamp<int32_t>(lcf::Data::system.easyrpg_max_savefiles, 3, 99); i++) {
118 std::shared_ptr<Window_SaveFile>
119 w(new Window_SaveFile(0, 40 + i * 64, SCREEN_TARGET_WIDTH, 64));
120 w->SetIndex(i);
121 w->SetZ(Priority_Window);
122 PopulateSaveWindow(*w, i);
123 w->Refresh();
124
125 file_windows.push_back(w);
126 }
127
128 border_bottom = Scene_File::MakeBorderSprite(232);
129
130 up_arrow = Scene_File::MakeArrowSprite(false);
131 down_arrow = Scene_File::MakeArrowSprite(true);
132
133 index = latest_slot;
134 top_index = std::max(0, index - 2);
135
136 Refresh();
137
138 for (auto& fw: file_windows) {
139 fw->Update();
140 }
141 }
142
Refresh()143 void Scene_File::Refresh() {
144 for (int i = 0; i < (int)file_windows.size(); i++) {
145 Window_SaveFile *w = file_windows[i].get();
146 w->SetY(40 + (i - top_index) * 64);
147 w->SetActive(i == index);
148 w->Refresh();
149 }
150 }
151
Update()152 void Scene_File::Update() {
153 UpdateArrows();
154
155 if (IsWindowMoving()) {
156 for (auto& fw: file_windows) {
157 fw->Update();
158 }
159 return;
160 }
161
162 if (Input::IsTriggered(Input::CANCEL)) {
163 Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cancel));
164 Scene::Pop();
165 } else if (Input::IsTriggered(Input::DECISION)) {
166 if (IsSlotValid(index)) {
167 Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Decision));
168 Action(index);
169 }
170 else {
171 Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Buzzer));
172 }
173 }
174
175 int old_top_index = top_index;
176 int old_index = index;
177 int max_index = static_cast<int>(file_windows.size()) - 1;
178
179 if (Input::IsRepeated(Input::DOWN) || Input::IsTriggered(Input::SCROLL_DOWN)) {
180 if (Input::IsTriggered(Input::DOWN) || Input::IsTriggered(Input::SCROLL_DOWN)
181 || index < max_index) {
182 Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor));
183 index = (index + 1) % file_windows.size();
184 }
185
186 //top_index = std::max(top_index, index - 3 + 1);
187 }
188 if (Input::IsRepeated(Input::UP) || Input::IsTriggered(Input::SCROLL_UP)) {
189 if (Input::IsTriggered(Input::UP) || Input::IsTriggered(Input::SCROLL_UP)
190 || index >= 1) {
191 Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor));
192 index = (index + max_index) % file_windows.size();
193 }
194
195 //top_index = std::min(top_index, index);
196 }
197
198 if (Input::IsRepeated(Input::PAGE_DOWN) && index < max_index) {
199 Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor));
200 index = (index + 3 <= max_index) ? index + 3 : max_index;
201 }
202 if (Input::IsRepeated(Input::PAGE_UP) && index >= 1) {
203 Main_Data::game_system->SePlay(Main_Data::game_system->GetSystemSE(Main_Data::game_system->SFX_Cursor));
204 index = (index > 3) ? index - 3 : 0;
205 }
206
207 if (index > top_index + 2) {
208 MoveFileWindows((top_index + 2 - index) * 64, 7);
209 top_index = std::max(top_index, index - 3 + 1);
210 }
211 else if (index < top_index) {
212 MoveFileWindows((top_index - index) * 64, 7);
213 top_index = std::min(top_index, index);
214 }
215
216 //top_index = std::min(top_index, std::max(top_index, index - 3 + 1));
217
218 if (top_index != old_top_index || index != old_index)
219 Refresh();
220
221 for (auto& fw: file_windows) {
222 fw->Update();
223 }
224 }
225
226
IsWindowMoving() const227 bool Scene_File::IsWindowMoving() const {
228 for (auto& fw: file_windows) {
229 if (fw->IsMovementActive()) {
230 return true;
231 }
232 }
233 return false;
234 }
235
MoveFileWindows(int dy,int dt)236 void Scene_File::MoveFileWindows(int dy, int dt) {
237 for (auto& fw: file_windows) {
238 fw->InitMovement(fw->GetX(), fw->GetY(), fw->GetX(), fw->GetY() + dy, dt);
239 }
240 }
241
UpdateArrows()242 void Scene_File::UpdateArrows() {
243 int max_index = static_cast<int>(file_windows.size()) - 1;
244
245 bool show_up_arrow = (top_index > 0);
246 bool show_down_arrow = (top_index < max_index - 2);
247
248 if (show_up_arrow || show_down_arrow) {
249 arrow_frame = (arrow_frame + 1) % (arrow_animation_frames * 2);
250 }
251 bool arrow_visible = (arrow_frame < arrow_animation_frames);
252 up_arrow->SetVisible(show_up_arrow && arrow_visible);
253 down_arrow->SetVisible(show_down_arrow && arrow_visible);
254 }
255