]> git.localhorst.tv Git - blobs.git/blob - src/app/app.cpp
read (basic) tile information from file
[blobs.git] / src / app / app.cpp
1 #include "Application.hpp"
2 #include "Assets.hpp"
3 #include "State.hpp"
4
5 #include "init.hpp"
6 #include "../graphics/Viewport.hpp"
7 #include "../io/Token.hpp"
8 #include "../io/TokenStreamReader.hpp"
9
10 #include <fstream>
11 #include <SDL.h>
12 #include <SDL_image.h>
13
14 using std::string;
15
16
17 namespace blobs {
18 namespace app {
19
20 Application::Application(Window &win, graphics::Viewport &vp)
21 : window(win)
22 , viewport(vp)
23 , states() {
24 }
25
26 Application::~Application() {
27 }
28
29
30 void Application::PushState(State *s) {
31         s->app = this;
32         if (!states.empty()) {
33                 states.top()->OnPause();
34         }
35         states.emplace(s);
36         ++s->ref_count;
37         if (s->ref_count == 1) {
38                 s->OnEnter();
39         }
40         s->OnResume();
41 }
42
43 State *Application::PopState() {
44         State *s = states.top();
45         states.pop();
46         s->OnPause();
47         s->OnExit();
48         if (!states.empty()) {
49                 states.top()->OnResume();
50         }
51         return s;
52 }
53
54 State *Application::SwitchState(State *s_new) {
55         s_new->app = this;
56         State *s_old = states.top();
57         states.top() = s_new;
58         --s_old->ref_count;
59         ++s_new->ref_count;
60         s_old->OnPause();
61         if (s_old->ref_count == 0) {
62                 s_old->OnExit();
63         }
64         if (s_new->ref_count == 1) {
65                 s_new->OnEnter();
66         }
67         s_new->OnResume();
68         return s_old;
69 }
70
71 State &Application::GetState() {
72         return *states.top();
73 }
74
75 bool Application::HasState() const noexcept {
76         return !states.empty();
77 }
78
79
80 void Application::Run() {
81         Uint32 last = SDL_GetTicks();
82         while (HasState()) {
83                 Uint32 now = SDL_GetTicks();
84                 int delta = now - last;
85                 Loop(delta);
86                 last = now;
87         }
88 }
89
90 void Application::Loop(int dt) {
91         HandleEvents();
92         if (!HasState()) return;
93         GetState().Update(dt);
94         if (!HasState()) return;
95         viewport.Clear();
96         GetState().Render(viewport);
97         window.Flip();
98 }
99
100 void Application::HandleEvents() {
101         SDL_Event event;
102         while (HasState() && SDL_PollEvent(&event)) {
103                 if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
104                         viewport.Resize(event.window.data1, event.window.data2);
105                 }
106                 GetState().Handle(event);
107         }
108 }
109
110 void State::Handle(const SDL_Event &event) {
111         switch (event.type) {
112                 case SDL_KEYDOWN:
113                         OnKeyDown(event.key);
114                         break;
115                 case SDL_KEYUP:
116                         OnKeyUp(event.key);
117                         break;
118                 case SDL_MOUSEBUTTONDOWN:
119                         OnMouseDown(event.button);
120                         break;
121                 case SDL_MOUSEBUTTONUP:
122                         OnMouseUp(event.button);
123                         break;
124                 case SDL_MOUSEMOTION:
125                         OnMouseMotion(event.motion);
126                         break;
127                 case SDL_MOUSEWHEEL:
128                         OnMouseWheel(event.wheel);
129                         break;
130                 case SDL_QUIT:
131                         OnQuit();
132                         break;
133                 case SDL_WINDOWEVENT:
134                         Handle(event.window);
135                         break;
136                 default:
137                         // ignore
138                         break;
139         }
140 }
141
142 void State::Handle(const SDL_WindowEvent &event) {
143         switch (event.event) {
144                 case SDL_WINDOWEVENT_FOCUS_GAINED:
145                         OnFocus();
146                         break;
147                 case SDL_WINDOWEVENT_FOCUS_LOST:
148                         OnBlur();
149                         break;
150                 case SDL_WINDOWEVENT_RESIZED:
151                         OnResize(event.data1, event.data2);
152                         break;
153                 default:
154                         break;
155         }
156 }
157
158 void State::Update(int dt) {
159         OnUpdate(dt);
160 }
161
162 void State::Render(graphics::Viewport &viewport) {
163         OnRender(viewport);
164 }
165
166 void State::OnQuit() {
167         while (App().HasState()) {
168                 App().PopState();
169         }
170 }
171
172
173 Assets::Assets()
174 : path("assets/")
175 , data_path(path + "data/")
176 , skin_path(path + "skins/")
177 , tile_path(path + "tiles/") {
178         data.resources.Add({ "air", "Air", 0 });
179         data.resources.Add({ "biomass", "Biomass", 0 });
180         data.resources.Add({ "dirt", "Dirt", 0 });
181         data.resources.Add({ "ice", "Ice", 0 });
182         data.resources.Add({ "rock", "Rock", 0 });
183         data.resources.Add({ "sand", "Sand", 0 });
184         data.resources.Add({ "water", "Water", 0 });
185         data.resources.Add({ "wood", "Wood", 0 });
186
187         {
188                 std::ifstream tile_file(data_path + "tiles");
189                 io::TokenStreamReader tile_reader(tile_file);
190                 ReadTileTypes(tile_reader);
191         }
192
193         data.tiles["algae"]   .resources.push_back({ data.resources["water"].id,   1.0  });
194         data.tiles["algae"]   .resources.push_back({ data.resources["biomass"].id, 0.5  });
195         data.tiles["desert"]  .resources.push_back({ data.resources["sand"].id,    1.0  });
196         data.tiles["forest"]  .resources.push_back({ data.resources["wood"].id,    1.0  });
197         data.tiles["forest"]  .resources.push_back({ data.resources["dirt"].id,    0.5  });
198         data.tiles["grass"]   .resources.push_back({ data.resources["dirt"].id,    0.5  });
199         data.tiles["grass"]   .resources.push_back({ data.resources["biomass"].id, 0.25 });
200         data.tiles["grass"]   .resources.push_back({ data.resources["water"].id,   0.25 });
201         data.tiles["ice"]     .resources.push_back({ data.resources["ice"].id,     1.0  });
202         data.tiles["ice"]     .resources.push_back({ data.resources["water"].id,   0.25 });
203         data.tiles["jungle"]  .resources.push_back({ data.resources["wood"].id,    0.5  });
204         data.tiles["jungle"]  .resources.push_back({ data.resources["biomass"].id, 0.5  });
205         data.tiles["mountain"].resources.push_back({ data.resources["rock"].id,    1.0  });
206         data.tiles["ocean"]   .resources.push_back({ data.resources["water"].id,   1.0  });
207         data.tiles["rock"]    .resources.push_back({ data.resources["rock"].id,    1.0  });
208         data.tiles["sand"]    .resources.push_back({ data.resources["sand"].id,    1.0  });
209         data.tiles["taiga"]   .resources.push_back({ data.resources["wood"].id,    1.0  });
210         data.tiles["taiga"]   .resources.push_back({ data.resources["water"].id,   0.5  });
211         data.tiles["tundra"]  .resources.push_back({ data.resources["rock"].id,    1.0  });
212         data.tiles["tundra"]  .resources.push_back({ data.resources["ice"].id,     0.5  });
213         data.tiles["water"]   .resources.push_back({ data.resources["water"].id,   1.0  });
214         data.tiles["water"]   .resources.push_back({ data.resources["biomass"].id, 0.25 });
215         data.tiles["wheat"]   .resources.push_back({ data.resources["biomass"].id, 1.0  });
216         data.tiles["wheat"]   .resources.push_back({ data.resources["water"].id,   0.25 });
217
218         graphics::Format format;
219         textures.tiles.Bind();
220         textures.tiles.Reserve(256, 256, 14, format);
221         LoadTileTexture("algae",    textures.tiles,  0);
222         LoadTileTexture("desert",   textures.tiles,  1);
223         LoadTileTexture("forest",   textures.tiles,  2);
224         LoadTileTexture("grass",    textures.tiles,  3);
225         LoadTileTexture("ice",      textures.tiles,  4);
226         LoadTileTexture("jungle",   textures.tiles,  5);
227         LoadTileTexture("mountain", textures.tiles,  6);
228         LoadTileTexture("ocean",    textures.tiles,  7);
229         LoadTileTexture("rock",     textures.tiles,  8);
230         LoadTileTexture("sand",     textures.tiles,  9);
231         LoadTileTexture("taiga",    textures.tiles, 10);
232         LoadTileTexture("tundra",   textures.tiles, 11);
233         LoadTileTexture("water",    textures.tiles, 12);
234         LoadTileTexture("wheat",    textures.tiles, 13);
235
236         textures.skins.Bind();
237         textures.skins.Reserve(256, 256, 9, format);
238         LoadSkinTexture("1", textures.skins, 0);
239         LoadSkinTexture("2", textures.skins, 1);
240         LoadSkinTexture("3", textures.skins, 2);
241         LoadSkinTexture("4", textures.skins, 3);
242         LoadSkinTexture("5", textures.skins, 4);
243         LoadSkinTexture("6", textures.skins, 5);
244         LoadSkinTexture("7", textures.skins, 6);
245         LoadSkinTexture("8", textures.skins, 7);
246         LoadSkinTexture("9", textures.skins, 8);
247 }
248
249 Assets::~Assets() {
250 }
251
252 void Assets::ReadTileTypes(io::TokenStreamReader &in) {
253         while (in.HasMore()) {
254                 string name;
255                 in.ReadIdentifier(name);
256                 in.Skip(io::Token::EQUALS);
257
258                 int id = 0;
259                 if (data.tiles.Has(name)) {
260                         id = data.tiles[name].id;
261                 } else {
262                         world::TileType type;
263                         type.name = name;
264                         id = data.tiles.Add(type);
265                 }
266
267                 in.Skip(io::Token::ANGLE_BRACKET_OPEN);
268                 while (in.Peek().type != io::Token::ANGLE_BRACKET_CLOSE) {
269                         in.ReadIdentifier(name);
270                         in.Skip(io::Token::EQUALS);
271                         if (name == "label") {
272                                 in.ReadString(data.tiles[id].label);
273                         } else if (name == "texture") {
274                                 data.tiles[id].texture = in.GetInt();
275                         } else {
276                                 throw std::runtime_error("unknown tile type property '" + name + "'");
277                         }
278                         in.Skip(io::Token::SEMICOLON);
279                 }
280                 in.Skip(io::Token::ANGLE_BRACKET_CLOSE);
281                 in.Skip(io::Token::SEMICOLON);
282         }
283 }
284
285 void Assets::LoadTileTexture(const string &name, graphics::ArrayTexture &tex, int layer) const {
286         string path = tile_path + name + ".png";
287         SDL_Surface *srf = IMG_Load(path.c_str());
288         if (!srf) {
289                 throw SDLError("IMG_Load");
290         }
291         try {
292                 tex.Data(layer, *srf);
293         } catch (...) {
294                 SDL_FreeSurface(srf);
295                 throw;
296         }
297         SDL_FreeSurface(srf);
298 }
299
300 void Assets::LoadSkinTexture(const string &name, graphics::ArrayTexture &tex, int layer) const {
301         string path = skin_path + name + ".png";
302         SDL_Surface *srf = IMG_Load(path.c_str());
303         if (!srf) {
304                 throw SDLError("IMG_Load");
305         }
306         try {
307                 tex.Data(layer, *srf);
308         } catch (...) {
309                 SDL_FreeSurface(srf);
310                 throw;
311         }
312         SDL_FreeSurface(srf);
313 }
314
315 }
316 }