]> gitweb.ps.run Git - npengine/blob - src/main.cpp
wie vorher
[npengine] / src / main.cpp
1 #include <fstream>\r
2 #include <stdio.h>\r
3 #include <string>\r
4 #include <time.h>\r
5 #include <windows.h>\r
6 #include <Richedit.h>\r
7 \r
8 #pragma comment(lib, "user32.lib")\r
9 \r
10 #define CONSOLE\r
11 \r
12 #ifdef CONSOLE\r
13 #pragma comment(linker, "/subsystem:console")\r
14 #else\r
15 #pragma comment(linker, "/subsystem:windows")\r
16 #endif\r
17 \r
18 using namespace std;\r
19 \r
20 \r
21 const int WIDTH = 56, HEIGHT = 29;\r
22 \r
23 std::string map;\r
24 \r
25 clock_t game_clock;\r
26 double frame_time = 30;\r
27 \r
28 int lvl = 0;\r
29 \r
30 // Util\r
31 \r
32 double get_dur(clock_t then) {\r
33   double result = (double)(clock() - then) / CLOCKS_PER_SEC;\r
34   return result * 1000;\r
35 }\r
36 \r
37 // Edit\r
38 \r
39 HWND hwnd_notepad = NULL;\r
40 HWND hwnd_edit = NULL;\r
41 \r
42 string get_text() {\r
43   int len = SendMessageA(hwnd_edit, WM_GETTEXTLENGTH, 0, 0);\r
44   char *buffer = new char[len + 1];\r
45   int read = SendMessageA(hwnd_edit, WM_GETTEXT, len + 1, (LPARAM)buffer);\r
46   if (read != len)\r
47     puts("???");\r
48   buffer[read] = 0;\r
49   string result(buffer);\r
50   delete[] buffer;\r
51   return result;\r
52 }\r
53 \r
54 void select_all() {\r
55   SendMessage(hwnd_edit, EM_SETSEL, 0, get_text().size());\r
56 }\r
57 \r
58 void select_length(int from, int length) {\r
59   SendMessage(hwnd_edit, EM_SETSEL, from, from + length);\r
60 }\r
61 \r
62 void select_from_to(int from, int to) {\r
63   SendMessage(hwnd_edit, EM_SETSEL, from, to);\r
64 }\r
65 \r
66 void replace(string str) {\r
67   SendMessage(hwnd_edit, EM_REPLACESEL, FALSE, (LPARAM)str.c_str());\r
68 }\r
69 \r
70 int get_pos(int x, int y) {\r
71   return (y + 1) * (WIDTH + 4) + x + 1;\r
72 }\r
73 \r
74 char get_block(int x, int y) {\r
75   return map[get_pos(x, y)];\r
76 }\r
77 \r
78 // Player\r
79 struct Player {\r
80   bool left = false;\r
81   int x, y;\r
82   int x_spawn, y_spawn;\r
83   int jumping = 0;\r
84 \r
85   void clear() {\r
86     int pos = get_pos(x, y);\r
87     select_length(pos, 1);\r
88     replace({ map[pos], 0 });\r
89   }\r
90   \r
91   void draw() {\r
92     int pos = get_pos(x, y);\r
93     select_length(pos, 1);\r
94     replace({ left ? '<' : '>', 0 });\r
95   }\r
96 \r
97   void move_to(int x, int y) {\r
98     clear();\r
99     if (this->x > x) left = true;\r
100     if (this->x < x) left = false;\r
101     this->x = x;\r
102     this->y = y;\r
103     draw();\r
104   }\r
105 \r
106   void move(int dx, int dy) {\r
107     move_to(x + dx, y + dy);\r
108   }\r
109 \r
110   bool collision_x(int n) {\r
111     if (x + n < 0 || x + n >= WIDTH)\r
112       return true;\r
113     for (int i = 0; i != n; i += (n < 0 ? -1 : 1))\r
114       if (get_block(x + i + (n < 0 ? -1 : 1), y) == 'X')\r
115         return true;\r
116     return false;\r
117   }\r
118   bool collision_y(int n) {\r
119     if (y + n < 0 || y + n >= HEIGHT)\r
120       return true;\r
121     for (int i = 0; i != n; i += (n < 0 ? -1 : 1))\r
122       if (get_block(x, y + i + (n < 0 ? -1 : 1)) == 'X')\r
123         return true;\r
124     return false;\r
125   }\r
126 };\r
127 Player player { 0, 0 };\r
128 \r
129 // Lvl\r
130 \r
131 std::string read_map(int lvl) {\r
132   std::ifstream ifs("lvl/" + std::to_string(lvl) + ".txt",\r
133                     std::ios::in | std::ios::binary);\r
134   if (!ifs.good())\r
135     puts("mist");\r
136   ifs.seekg(0, std::ios::end);\r
137   int length = ifs.tellg();\r
138   ifs.seekg(0, std::ios::beg);\r
139   char *buffer = new char[length + 1];\r
140   ifs.read(buffer, length);\r
141   ifs.close();\r
142   buffer[length] = 0;\r
143   std::string result(buffer);\r
144   delete buffer;\r
145   return result;\r
146 }\r
147 \r
148 void redraw() {\r
149   select_all();\r
150   replace(map);\r
151   player.draw();\r
152 }\r
153 \r
154 void load_level(int l) {\r
155   lvl = l;\r
156   map = read_map(l);\r
157   redraw();\r
158   for (int x = 0; x < WIDTH; x++) {\r
159     for (int y = 0; y < HEIGHT; y++) {\r
160       if (map[get_pos(x, y)] == 'S') {\r
161         player.x_spawn = x;\r
162         player.y_spawn = y;\r
163         player.move_to(x, y);\r
164         return;\r
165       }\r
166     }\r
167   }\r
168 }\r
169 \r
170 // Notepad\r
171 \r
172 STARTUPINFOA si;\r
173 PROCESS_INFORMATION pi;\r
174 \r
175 BOOL CALLBACK ew_cb(HWND hwnd, LPARAM lp) {\r
176   DWORD pid;\r
177   DWORD tid = GetWindowThreadProcessId(hwnd, &pid);\r
178   if (pi.dwProcessId == pid && pi.dwThreadId == tid) {\r
179     hwnd_notepad = hwnd;\r
180     return false;\r
181   }\r
182   return true;\r
183 }\r
184 BOOL CALLBACK ecw_cb(HWND child, LPARAM in) {\r
185   char buffer[1024 + 1];\r
186   int len = GetClassNameA(child, buffer, 1024);\r
187   buffer[len] = 0;\r
188   if (strcmp(buffer, "Edit") == 0) {\r
189     hwnd_edit = child;\r
190     return false;\r
191   }\r
192   return true;\r
193 }\r
194 \r
195 void start_notepad() {\r
196   ZeroMemory(&si, sizeof(si));\r
197   si.cb = sizeof(si);\r
198   ZeroMemory(&pi, sizeof(pi));\r
199 \r
200   if (!CreateProcessA(NULL,  // No module name (use command line)\r
201                       "notepad.exe",   // Command line\r
202                       NULL,  // Process handle not inheritable\r
203                       NULL,  // Thread handle not inheritable\r
204                       FALSE, // Set handle inheritance to FALSE\r
205                       0,     // No creation flags\r
206                       NULL,  // Use parent's environment block\r
207                       NULL,  // Use parent's starting directory\r
208                       &si,   // Pointer to STARTUPINFO structure\r
209                       &pi)   // Pointer to PROCESS_INFORMATION structure\r
210   ) {\r
211     printf("CreateProcess failed (%d).\n", GetLastError());\r
212   }\r
213   Sleep(100);\r
214   EnumWindows(ew_cb, 0);\r
215   EnumChildWindows(hwnd_notepad, ecw_cb, 0);\r
216   SendMessage(hwnd_edit, EM_SETREADONLY, TRUE, NULL);\r
217 }\r
218 void close_notepad() {\r
219   TerminateProcess(pi.hProcess, 0);\r
220   CloseHandle(pi.hProcess);\r
221   CloseHandle(pi.hThread);\r
222 }\r
223 \r
224 // Keys\r
225 \r
226 enum class Key {\r
227   Left,\r
228   Right,\r
229   Jump,\r
230   Exit,\r
231   Redraw,\r
232   COUNT\r
233 };\r
234 \r
235 bool key_state[(uint64_t)Key::COUNT];\r
236 bool key_state_old[(uint64_t)Key::COUNT];\r
237 \r
238 int key_get_vk(Key key) {\r
239   switch (key) {\r
240     case Key::Left: return 'A';\r
241     case Key::Right: return 'D';\r
242     case Key::Jump: return VK_SPACE;\r
243     case Key::Exit: return VK_ESCAPE;\r
244     case Key::Redraw: return 'R';\r
245     default: return 0;\r
246   }\r
247 }\r
248 \r
249 void update_key_state() {\r
250   for (int i = 0; i < (int)Key::COUNT; i++) {\r
251     key_state[i] = GetAsyncKeyState(key_get_vk((Key)i));\r
252   }\r
253 }\r
254 \r
255 void update_key_state_old() {\r
256   for (int i = 0; i < (int)Key::COUNT; i++) {\r
257     key_state_old[i] = key_state[i];\r
258   }\r
259 }\r
260 \r
261 bool key_pressed(Key key) {\r
262   return key_state[(int)key] && !key_state_old[(int)key];\r
263 }\r
264 \r
265 bool key_down(Key key) {\r
266   return key_state[(int)key];\r
267 }\r
268 \r
269 bool key_up(Key key) {\r
270   return !key_state[(int)key];\r
271 }\r
272 \r
273 // Gameplay\r
274   \r
275 clock_t jump_clock = clock();\r
276 int jump_height = 3;\r
277 double jump_time1 = 50;\r
278 double jump_time2 = 100;\r
279 int text_speed = 50;\r
280 \r
281 void update_play(bool can_jump = true, int x_min = 0, int x_max = WIDTH - 1) {\r
282   if (key_down(Key::Left) &&\r
283       !player.collision_x(-1) &&\r
284       player.x > x_min)\r
285       player.move(-1, 0);\r
286   if (key_down(Key::Right) &&\r
287       !player.collision_x(1) &&\r
288       player.x < x_max)\r
289     player.move(+1, 0);\r
290 \r
291   if (key_pressed(Key::Jump) &&\r
292       can_jump &&\r
293       player.jumping == 0 &&\r
294       !player.collision_y(-1)) {\r
295     player.jumping = 1;\r
296     player.move(0, -1);\r
297     jump_clock = clock();\r
298   }\r
299   if (player.jumping != 0) {\r
300     if (player.jumping < jump_height && get_dur(jump_clock) > jump_time1) {\r
301       if (!player.collision_y(-1)) {\r
302         player.move(0, -1);\r
303         player.jumping++;\r
304         jump_clock = clock();\r
305       } else {\r
306         player.jumping = jump_height;\r
307       }\r
308     } else if (player.jumping == jump_height && get_dur(jump_clock) > jump_time2) {\r
309       player.jumping = 0;\r
310     }\r
311   }\r
312   if (!player.jumping && !player.collision_y(1))\r
313     player.move(0, +1);\r
314 \r
315   char b = get_block(player.x, player.y);\r
316   if (b == '/' || b == '\\' || b == '<' || b == '>')\r
317     player.move_to(player.x_spawn, player.y_spawn);\r
318 }\r
319 \r
320 void print_text(int x, int y, string text, int delay) {\r
321   for (int i = 0; i < text.size(); i++) {\r
322     select_length(get_pos(x + i, y), 1);\r
323     replace(text.substr(i, 1));\r
324     Sleep(delay);\r
325   }\r
326 }\r
327 \r
328 void intro() {\r
329   static int progress = 0;\r
330   switch (progress) {\r
331   case 0:\r
332     print_text(4, 2, "Move with left/right.", text_speed);\r
333 \r
334     progress++;\r
335     break;\r
336   case 1:\r
337     update_play(false);\r
338     if (player.x == 17) {\r
339       print_text(4, 4, "Jump with up.", text_speed);\r
340       print_text(4, 6, "Stand on x.", text_speed);\r
341       progress++;\r
342     }\r
343     break;\r
344   case 2:\r
345     update_play();\r
346     if (player.x == 22) {\r
347       print_text(4, 8, "Collect ? for ???.", text_speed);\r
348       progress++;\r
349     }\r
350     break;\r
351   case 3:\r
352     update_play(true, 0, 33);\r
353     if (get_block(player.x, player.y) == '?') {\r
354       print_text(4, 10, "Avoid /\\.", text_speed);\r
355       progress++;\r
356     }\r
357     break;\r
358   case 4:\r
359     update_play();\r
360     if (player.x == 39) {\r
361       print_text(4, 14, "Finish lvl by reaching O.", text_speed);\r
362       progress++;\r
363     }\r
364     break;\r
365   case 5:\r
366     update_play();\r
367     if (get_block(player.x, player.y) == 'O') {\r
368       load_level(1);\r
369     }\r
370     break;\r
371   }\r
372 }\r
373 \r
374 void lvl1() {\r
375   static int progress = 0;\r
376   switch (progress) {\r
377   case 0:\r
378     print_text(4, 2, "Also avoid > and <.", text_speed);\r
379     progress++;\r
380     break;\r
381   case 1:\r
382     update_play();\r
383     break;\r
384   }\r
385 }\r
386 \r
387 void update_game() {\r
388   switch (lvl) {\r
389   case 0:\r
390     intro();\r
391     break;\r
392   case 1:\r
393     lvl1();\r
394     break;\r
395   }\r
396 }\r
397 \r
398 #ifdef CONSOLE\r
399 int main(int argc, char **argv) {\r
400 #else\r
401 int WinMain(HINSTANCE a0, HINSTANCE a1, LPSTR a2, int a3) {\r
402 #endif\r
403   start_notepad();\r
404   if (hwnd_notepad == NULL || hwnd_edit == NULL) {\r
405     puts("error");\r
406     return 1;\r
407   }\r
408 \r
409   load_level(0);\r
410   \r
411   while (true) {\r
412     //dt = ((double)clock() - game_clock) / CLOCKS_PER_SEC * 1000;\r
413     if (get_dur(game_clock) < frame_time) continue;\r
414     game_clock = clock();\r
415     update_key_state();\r
416 \r
417     if (key_pressed(Key::Exit))\r
418       break;\r
419     if (key_pressed(Key::Redraw))\r
420       redraw();\r
421     \r
422     update_game();\r
423 \r
424     update_key_state_old();\r
425   }\r
426   close_notepad();\r
427 \r
428   return 0;\r
429 }