about_window.cpp

main 888 lines · 18.8 KB Raw
Alex Alex Commit Map Editor 18/10/2026 23:40
1//////////////////////////////////////////////////////////////////////
2// This file is part of Remere's Map Editor
3//////////////////////////////////////////////////////////////////////
4// Remere's Map Editor 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// Remere's Map Editor 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 this program. If not, see <http://www.gnu.org/licenses/>.
16//////////////////////////////////////////////////////////////////////
17
18#include "main.h"
19
20#include "gui.h"
21
22#include "about_window.h"
23#include <fstream>
24#include <typeinfo>
25#include <memory>
26
27class GamePanel : public wxPanel {
28public:
29 GamePanel(wxWindow* parent, int width, int height);
30 virtual ~GamePanel();
31
32 void OnPaint(wxPaintEvent&);
33 void OnKeyDown(wxKeyEvent&);
34 void OnKeyUp(wxKeyEvent&);
35 void OnIdle(wxIdleEvent&);
36
37 void pause() {
38 paused_val = true;
39 }
40 void unpause() {
41 paused_val = false;
42 }
43 bool paused() const {
44 return paused_val || dead;
45 }
46
47protected:
48 virtual void Render(wxDC& pdc) = 0;
49 virtual void GameLoop(int time) = 0;
50 virtual void OnKey(wxKeyEvent& event, bool down) = 0;
51
52 virtual int getFPS() const = 0;
53
54protected:
55 wxStopWatch game_timer;
56
57private:
58 bool paused_val;
59
60 DECLARE_EVENT_TABLE()
61
62protected:
63 bool dead;
64};
65
66const int TETRIS_MAPHEIGHT = 20;
67const int TETRIS_MAPWIDTH = 10;
68
69class TetrisPanel : public GamePanel {
70public:
71 TetrisPanel(wxWindow* parent);
72 ~TetrisPanel();
73
74protected:
75 virtual void Render(wxDC& pdc);
76 virtual void GameLoop(int time);
77 virtual void OnKey(wxKeyEvent& event, bool down);
78
79 virtual int getFPS() const {
80 return lines / 10 + 3;
81 }
82
83 enum Color {
84 NO_COLOR,
85 RED,
86 BLUE,
87 GREEN,
88 STEEL,
89 YELLOW,
90 PURPLE,
91 WHITE,
92 };
93
94 enum BlockType {
95 FIRST_BLOCK,
96 BLOCK_TOWER = FIRST_BLOCK,
97 BLOCK_SQUARE,
98 BLOCK_TRIANGLE,
99 BLOCK_L,
100 BLOCK_J,
101 BLOCK_Z,
102 BLOCK_S,
103 LAST_BLOCK = BLOCK_S
104 };
105
106 struct Block {
107 Color structure[4][4];
108 int x, y;
109 } block;
110
111 const wxBrush& GetBrush(Color color) const;
112 bool BlockCollisionTest(int mx, int my) const;
113 void RemoveRow(int row);
114 void NewBlock();
115 void MoveBlock(int x, int y);
116 void RotateBlock();
117 void NewGame();
118 void EndGame();
119 void AddScore(int lines);
120
121 int score;
122 int lines;
123 Color map[TETRIS_MAPWIDTH][TETRIS_MAPHEIGHT];
124};
125
126const int SNAKE_MAPHEIGHT = 20;
127const int SNAKE_MAPWIDTH = 20;
128
129class SnakePanel : public GamePanel {
130public:
131 SnakePanel(wxWindow* parent);
132 ~SnakePanel();
133
134protected:
135 virtual void Render(wxDC& pdc);
136 virtual void GameLoop(int time);
137 virtual void OnKey(wxKeyEvent& event, bool down);
138
139 virtual int getFPS() const {
140 return 7;
141 }
142
143 enum {
144 NORTH,
145 SOUTH,
146 WEST,
147 EAST,
148 };
149
150 void NewApple();
151 void Move(int dir);
152 void NewGame();
153 void EndGame();
154 void UpdateTitle();
155
156 // -1 is apple, 0 is nothing, >0 is snake (will decay in n rounds)
157 int length;
158 int last_dir;
159 int map[SNAKE_MAPWIDTH][SNAKE_MAPHEIGHT];
160};
161
162//=============================================================================
163// About Window - Information window about the application
164
165BEGIN_EVENT_TABLE(AboutWindow, wxDialog)
166EVT_BUTTON(wxID_OK, AboutWindow::OnClickOK)
167EVT_BUTTON(ABOUT_VIEW_LICENSE, AboutWindow::OnClickLicense)
168EVT_MENU(ABOUT_RUN_TETRIS, AboutWindow::OnTetris)
169EVT_MENU(ABOUT_RUN_SNAKE, AboutWindow::OnSnake)
170EVT_MENU(wxID_CANCEL, AboutWindow::OnClickOK)
171END_EVENT_TABLE()
172
173AboutWindow::AboutWindow(wxWindow* parent) :
174 wxDialog(parent, wxID_ANY, "About", wxDefaultPosition, wxSize(300, 320), wxRESIZE_BORDER | wxCAPTION | wxCLOSE_BOX),
175 game_panel(nullptr) {
176 wxString about;
177
178 about << "RD Map Editor (OTARME RD)\n";
179 about << "(based on OTA Remere's Map Editor)\n\n";
180 about << "This program is a map editor for game servers\nthat derivied from OpenTibia project.\n\n";
181 about << "RD edition\n\n";
182
183 about << "Version " << __W_RME_VERSION__ << " for ";
184 about <<
185#ifdef __WINDOWS__
186 "Windows";
187#elif __LINUX__
188 "Linux";
189#elif __APPLE__
190 "macOS";
191#else
192 "other OS";
193#endif
194 about << "\n\n";
195
196 about << "Using " << wxVERSION_STRING << " interface\n";
197 about << "OpenGL version " << wxString((char*)glGetString(GL_VERSION), wxConvUTF8) << "\n";
198 about << "\n";
199 about << "This program comes with ABSOLUTELY NO WARRANTY;\n";
200 about << "for details see the LICENSE file.\n";
201 about << "This is free software, and you are welcome to redistribute it\n";
202 about << "under certain conditions.\n";
203 about << "\n";
204 about << "Compiled on: " << __TDATE__ << " : " << __TTIME__ << "\n";
205 about << "Compiled with: " << BOOST_COMPILER << "\n";
206
207 topsizer = newd wxBoxSizer(wxVERTICAL);
208
209 topsizer->Add(newd wxStaticText(this, wxID_ANY, about), 1, wxALL, 20);
210
211 wxSizer* choicesizer = newd wxBoxSizer(wxHORIZONTAL);
212 choicesizer->Add(newd wxButton(this, wxID_OK, "OK"), wxSizerFlags(1).Center());
213 topsizer->Add(choicesizer, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, 20);
214
215 wxAcceleratorEntry entries[3];
216 entries[0].Set(wxACCEL_NORMAL, WXK_ESCAPE, wxID_CANCEL);
217 entries[1].Set(wxACCEL_NORMAL, 't', ABOUT_RUN_TETRIS);
218 entries[2].Set(wxACCEL_NORMAL, 's', ABOUT_RUN_SNAKE);
219 wxAcceleratorTable accel(3, entries);
220 SetAcceleratorTable(accel);
221
222 SetSizerAndFit(topsizer);
223 Centre(wxBOTH);
224}
225
226AboutWindow::~AboutWindow() {
227 ////
228}
229
230void AboutWindow::OnClickOK(wxCommandEvent& WXUNUSED(event)) {
231 EndModal(0);
232}
233
234void AboutWindow::OnClickLicense(wxCommandEvent& WXUNUSED(event)) {
235 FileName path;
236 try {
237 path = wxStandardPaths::Get().GetExecutablePath();
238 } catch (std::bad_cast&) {
239 return;
240 }
241 path.SetFullName("COPYING.txt");
242 std::ifstream gpl(path.GetFullPath().mb_str());
243
244 std::string gpl_str;
245 char ch;
246 while (gpl.get(ch)) {
247 gpl_str += ch;
248 }
249
250 g_gui.ShowTextBox(this, "License", wxstr(gpl_str.size() ? gpl_str : "The COPYING.txt file is not available."));
251}
252
253void AboutWindow::OnTetris(wxCommandEvent&) {
254 if (!game_panel) {
255 DestroyChildren();
256 game_panel = newd TetrisPanel(this);
257 topsizer->Add(game_panel, 1, wxALIGN_CENTER | wxALL, 7);
258 Fit();
259 game_panel->SetFocus();
260 SetWindowStyleFlag(wxRESIZE_BORDER | wxCAPTION | wxCLOSE_BOX);
261 Refresh();
262 }
263}
264
265void AboutWindow::OnSnake(wxCommandEvent&) {
266 if (!game_panel) {
267 DestroyChildren();
268 game_panel = newd SnakePanel(this);
269 topsizer->Add(game_panel, 1, wxALIGN_CENTER | wxALL, 7);
270 Fit();
271 game_panel->SetFocus();
272 SetWindowStyleFlag(wxRESIZE_BORDER | wxCAPTION | wxCLOSE_BOX);
273 Refresh();
274 }
275}
276
277//=============================================================================
278// GamePanel - Abstract class for games
279
280BEGIN_EVENT_TABLE(GamePanel, wxPanel)
281EVT_KEY_DOWN(GamePanel::OnKeyDown)
282EVT_KEY_UP(GamePanel::OnKeyUp)
283EVT_PAINT(GamePanel::OnPaint)
284EVT_IDLE(GamePanel::OnIdle)
285END_EVENT_TABLE()
286
287GamePanel::GamePanel(wxWindow* parent, int width, int height) :
288 wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(width, height), wxWANTS_CHARS),
289 paused_val(false),
290 dead(false) {
291 // Receive idle events
292 SetExtraStyle(wxWS_EX_PROCESS_IDLE);
293 // Complete redraw
294 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
295}
296
297GamePanel::~GamePanel() {
298 ////
299}
300
301void GamePanel::OnPaint(wxPaintEvent&) {
302 wxBufferedPaintDC pdc(this);
303 Render(pdc);
304}
305
306void GamePanel::OnKeyDown(wxKeyEvent& event) {
307 switch (event.GetKeyCode()) {
308 case WXK_ESCAPE: {
309 if (dead) {
310 return;
311 }
312 wxDialog* dlg = (wxDialog*)GetParent();
313 dlg->EndModal(0);
314 break;
315 }
316 default: {
317 OnKey(event, true);
318 break;
319 }
320 }
321}
322
323void GamePanel::OnKeyUp(wxKeyEvent& event) {
324 OnKey(event, false);
325}
326
327void GamePanel::OnIdle(wxIdleEvent& event) {
328 int time = game_timer.Time();
329 if (time > 1000 / getFPS()) {
330 game_timer.Start();
331 if (!paused()) {
332 GameLoop(time);
333 }
334 }
335 if (!paused()) {
336 event.RequestMore(true);
337 }
338}
339
340//=============================================================================
341// TetrisPanel - A window with a Tetris game!
342
343TetrisPanel::TetrisPanel(wxWindow* parent) :
344 GamePanel(parent, 16 * TETRIS_MAPWIDTH, 16 * TETRIS_MAPHEIGHT) {
345 NewGame();
346}
347
348TetrisPanel::~TetrisPanel() {
349 ////
350}
351
352const wxBrush& TetrisPanel::GetBrush(Color color) const {
353 static std::unique_ptr<wxBrush> yellow_brush;
354 static std::unique_ptr<wxBrush> purple_brush;
355
356 if (yellow_brush.get() == nullptr) {
357 yellow_brush.reset(newd wxBrush(wxColor(255, 255, 0)));
358 }
359 if (purple_brush.get() == nullptr) {
360 purple_brush.reset(newd wxBrush(wxColor(128, 0, 255)));
361 }
362
363 const wxBrush* brush = nullptr;
364 switch (color) {
365 case RED:
366 brush = wxRED_BRUSH;
367 break;
368 case BLUE:
369 brush = wxCYAN_BRUSH;
370 break;
371 case GREEN:
372 brush = wxGREEN_BRUSH;
373 break;
374 case PURPLE:
375 brush = purple_brush.get();
376 break;
377 case YELLOW:
378 brush = yellow_brush.get();
379 break;
380 case WHITE:
381 brush = wxWHITE_BRUSH;
382 break;
383 case STEEL:
384 brush = wxGREY_BRUSH;
385 break;
386 default:
387 brush = wxBLACK_BRUSH;
388 break;
389 }
390 return *brush;
391}
392
393void TetrisPanel::Render(wxDC& pdc) {
394 pdc.SetBackground(*wxBLACK_BRUSH);
395 pdc.Clear();
396
397 for (int y = 0; y < TETRIS_MAPHEIGHT; ++y) {
398 for (int x = 0; x < TETRIS_MAPWIDTH; ++x) {
399 pdc.SetBrush(GetBrush(map[x][y]));
400 pdc.DrawRectangle(x * 16, y * 16, 16, 16);
401 }
402 }
403
404 for (int y = 0; y < 4; ++y) {
405 for (int x = 0; x < 4; ++x) {
406 if (block.structure[x][y] != NO_COLOR) {
407 pdc.SetBrush(GetBrush(block.structure[x][y]));
408 pdc.DrawRectangle((block.x + x) * 16, (block.y + y) * 16, 16, 16);
409 }
410 }
411 }
412}
413
414void TetrisPanel::OnKey(wxKeyEvent& event, bool down) {
415 if (!down || dead) {
416 return;
417 }
418
419 switch (event.GetKeyCode()) {
420 case WXK_SPACE: {
421 if (paused()) {
422 unpause();
423 } else {
424 pause();
425 }
426 break;
427 }
428 case WXK_NUMPAD_UP:
429 case WXK_UP: {
430 if (dead) {
431 return;
432 }
433 unpause();
434 RotateBlock();
435 break;
436 }
437 case WXK_NUMPAD_DOWN:
438 case WXK_DOWN: {
439 if (dead) {
440 return;
441 }
442 unpause();
443 MoveBlock(0, 1);
444 break;
445 }
446 case WXK_NUMPAD_LEFT:
447 case WXK_LEFT: {
448 if (dead) {
449 return;
450 }
451 unpause();
452 MoveBlock(-1, 0);
453 break;
454 }
455 case WXK_NUMPAD_RIGHT:
456 case WXK_RIGHT: {
457 if (dead) {
458 return;
459 }
460 unpause();
461 MoveBlock(1, 0);
462 break;
463 }
464 }
465}
466
467void TetrisPanel::NewGame() {
468 NewBlock();
469 score = 0;
470 lines = 0;
471 game_timer.Start();
472 unpause();
473 dead = false;
474
475 // Clear map
476 for (int y = 0; y < TETRIS_MAPHEIGHT; ++y) {
477 for (int x = 0; x < TETRIS_MAPWIDTH; ++x) {
478 map[x][y] = NO_COLOR;
479 }
480 }
481 AddScore(0); // Update title
482}
483
484void TetrisPanel::AddScore(int lines_added) {
485 lines += lines_added;
486 score += lines_added * lines_added * 10;
487 wxString title = "Remere's Tetris : ";
488 title << score << " points ";
489 title << lines << " lines";
490 ((wxTopLevelWindow*)GetParent())->SetTitle(title);
491}
492
493void TetrisPanel::GameLoop(int time) {
494 MoveBlock(0, 1);
495}
496
497bool TetrisPanel::BlockCollisionTest(int mx, int my) const {
498 int nx = block.x + mx;
499 int ny = block.y + my;
500
501 for (int y = 0; y < 4; ++y) {
502 for (int x = 0; x < 4; ++x) {
503 if (block.structure[x][y] != NO_COLOR) {
504 if (nx + x < 0 || nx + x > TETRIS_MAPWIDTH - 1 || ny + y < 0 || ny + y > TETRIS_MAPHEIGHT - 1) {
505 return true;
506 }
507 }
508 }
509 }
510
511 for (int y = 0; y < TETRIS_MAPHEIGHT; ++y) {
512 for (int x = 0; x < TETRIS_MAPWIDTH; ++x) {
513 if (x >= nx && x < nx + 4 && y >= ny && y < ny + 4) {
514 if (map[x][y] != NO_COLOR && block.structure[x - nx][y - ny] != NO_COLOR) {
515 return true;
516 }
517 }
518 }
519 }
520 return false;
521}
522
523void TetrisPanel::RemoveRow(int row) {
524 for (int x = 0; x < TETRIS_MAPWIDTH; ++x) {
525 for (int y = row; y > 0; --y) { // Move all above one step down
526 map[x][y] = map[x][y - 1];
527 }
528 }
529}
530
531void TetrisPanel::NewBlock() {
532 block.x = TETRIS_MAPWIDTH / 2;
533 block.y = -1;
534
535 for (int y = 0; y < 4; ++y) {
536 for (int x = 0; x < 4; ++x) {
537 block.structure[x][y] = NO_COLOR;
538 }
539 }
540
541 switch (random(FIRST_BLOCK, LAST_BLOCK)) {
542 case BLOCK_TOWER: {
543 block.structure[1][0] = RED;
544 block.structure[1][1] = RED;
545 block.structure[1][2] = RED;
546 block.structure[1][3] = RED;
547 block.y = 0;
548 break;
549 }
550 default:
551 case BLOCK_SQUARE: {
552 block.structure[1][1] = BLUE;
553 block.structure[1][2] = BLUE;
554 block.structure[2][1] = BLUE;
555 block.structure[2][2] = BLUE;
556 break;
557 }
558 case BLOCK_TRIANGLE: {
559 block.structure[1][1] = STEEL;
560 block.structure[0][2] = STEEL;
561 block.structure[1][2] = STEEL;
562 block.structure[2][2] = STEEL;
563 break;
564 }
565 case BLOCK_Z: {
566 block.structure[0][1] = YELLOW;
567 block.structure[1][1] = YELLOW;
568 block.structure[1][2] = YELLOW;
569 block.structure[2][2] = YELLOW;
570 break;
571 }
572 case BLOCK_S: {
573 block.structure[2][1] = GREEN;
574 block.structure[1][1] = GREEN;
575 block.structure[1][2] = GREEN;
576 block.structure[0][2] = GREEN;
577 break;
578 }
579 case BLOCK_J: {
580 block.structure[1][1] = WHITE;
581 block.structure[2][1] = WHITE;
582 block.structure[2][2] = WHITE;
583 block.structure[2][3] = WHITE;
584 break;
585 }
586 case BLOCK_L: {
587 block.structure[2][1] = PURPLE;
588 block.structure[1][1] = PURPLE;
589 block.structure[1][2] = PURPLE;
590 block.structure[1][3] = PURPLE;
591 // break; missing add it?
592 }
593 }
594}
595
596void TetrisPanel::MoveBlock(int x, int y) {
597 if (BlockCollisionTest(x, y)) {
598 if (y == 1) { // moving down...
599 if (block.y < 1) { // Out of bounds!
600 dead = true;
601 g_gui.PopupDialog("Game Over", "You reached a score of " + i2ws(score) + "!", wxOK);
602 NewGame();
603 SetFocus();
604 } else {
605 // Freeze the old block onto the map
606 for (int y = 0; y < 4; ++y) {
607 for (int x = 0; x < 4; ++x) {
608 if (block.structure[x][y] != NO_COLOR) {
609 map[block.x + x][block.y + y] = block.structure[x][y];
610 }
611 }
612 }
613
614 // Any cleared rows?
615 int cleared = 0;
616 for (int y = 0; y < TETRIS_MAPHEIGHT; ++y) {
617 bool full = true;
618 for (int x = 0; x < TETRIS_MAPWIDTH; ++x) {
619 if (map[x][y] == NO_COLOR) {
620 full = false;
621 }
622 }
623 if (full) {
624 RemoveRow(y);
625 ++cleared;
626 }
627 }
628 AddScore(cleared);
629 NewBlock();
630 }
631 } // If we're not moving down, we're not moving the block either
632 } else {
633 // No collision so move the block!
634 block.x += x;
635 block.y += y;
636 }
637
638 Refresh();
639}
640
641void TetrisPanel::RotateBlock() {
642 Block temp;
643
644 for (int y = 0; y < 4; ++y) {
645 for (int x = 0; x < 4; ++x) {
646 temp.structure[3 - x][y] = block.structure[y][x];
647 }
648 }
649
650 for (int y = 0; y < 4; ++y) {
651 for (int x = 0; x < 4; ++x) {
652 if (temp.structure[x][y] != NO_COLOR) {
653 if (block.x + x < 0 || block.x + x > TETRIS_MAPWIDTH - 1 || block.y + y < 0 || block.y + y > TETRIS_MAPHEIGHT - 1) {
654 return;
655 }
656 }
657 }
658 }
659
660 for (int y = 0; y < TETRIS_MAPWIDTH; ++y) {
661 for (int x = 0; x < TETRIS_MAPHEIGHT; ++x) {
662 if (x >= block.x && x < block.x + 4 && y >= block.y && y < block.y + 4) {
663 if (map[x][y] != NO_COLOR && temp.structure[x - block.x][y - block.y] != NO_COLOR) {
664 return;
665 }
666 }
667 }
668 }
669
670 for (int y = 0; y < 4; ++y) {
671 for (int x = 0; x < 4; ++x) {
672 block.structure[x][y] = temp.structure[x][y];
673 }
674 }
675
676 Refresh();
677}
678
679//=============================================================================
680// SnakePanel - A window with a Snake game!
681
682SnakePanel::SnakePanel(wxWindow* parent) :
683 GamePanel(parent, 16 * SNAKE_MAPWIDTH, 16 * SNAKE_MAPHEIGHT) {
684 NewGame();
685}
686
687SnakePanel::~SnakePanel() {
688 ////
689}
690
691void SnakePanel::Render(wxDC& pdc) {
692 pdc.SetBackground(*wxBLACK_BRUSH);
693 pdc.Clear();
694
695 wxBrush snakebrush(wxColor(0, 0, 255));
696 wxBrush applebrush(wxColor(255, 0, 0));
697
698 double lblue = 1.0;
699 double lred = 0.5;
700 double lgreen = 0.0;
701
702 for (int y = 0; y < SNAKE_MAPHEIGHT; ++y) {
703 for (int x = 0; x < SNAKE_MAPWIDTH; ++x) {
704 if (map[x][y] == -1) { // Apple
705 pdc.SetBrush(applebrush);
706 pdc.DrawRectangle(x * 16, y * 16, 16, 16);
707 } else if (map[x][y] > 0) { // Snake
708 double snook = double(map[x][y]) / length;
709 snakebrush.SetColour(wxColor(
710 int(255.0 * (1.0 - abs(lred - snook))),
711 int(255.0 * (1.0 - abs(lgreen - snook))),
712 int(255.0 * (1.0 - abs(lblue - snook)))
713 )
714 );
715 pdc.SetBrush(snakebrush);
716 pdc.DrawRectangle(x * 16, y * 16, 16, 16);
717 }
718 }
719 }
720}
721
722void SnakePanel::OnKey(wxKeyEvent& event, bool down) {
723 if (!down) {
724 return;
725 }
726
727 int keyCode = event.GetKeyCode();
728 if (keyCode == WXK_SPACE) {
729 if (paused()) {
730 unpause();
731 } else {
732 pause();
733 }
734 } else if (!dead) {
735 switch (keyCode) {
736 case WXK_NUMPAD_UP:
737 case WXK_UP: {
738 unpause();
739 Move(NORTH);
740 break;
741 }
742 case WXK_NUMPAD_DOWN:
743 case WXK_DOWN: {
744 unpause();
745 Move(SOUTH);
746 break;
747 }
748 case WXK_NUMPAD_LEFT:
749 case WXK_LEFT: {
750 unpause();
751 Move(WEST);
752 break;
753 }
754 case WXK_NUMPAD_RIGHT:
755 case WXK_RIGHT: {
756 unpause();
757 Move(EAST);
758 break;
759 }
760 }
761 }
762}
763
764void SnakePanel::NewGame() {
765 length = 3;
766 game_timer.Start();
767 last_dir = NORTH;
768 unpause();
769 dead = false;
770
771 // Clear map
772 for (int y = 0; y < SNAKE_MAPHEIGHT; ++y) {
773 for (int x = 0; x < SNAKE_MAPWIDTH; ++x) {
774 map[x][y] = 0;
775 }
776 }
777 map[SNAKE_MAPWIDTH / 2][SNAKE_MAPHEIGHT / 2] = length;
778 NewApple();
779 UpdateTitle(); // Update title
780}
781
782void SnakePanel::UpdateTitle() {
783 wxString title = "Remere's Snake : ";
784 title << length << " segments";
785 ((wxTopLevelWindow*)GetParent())->SetTitle(title);
786}
787
788void SnakePanel::GameLoop(int time) {
789 Move(last_dir);
790}
791
792void SnakePanel::NewApple() {
793 bool possible = false;
794 for (int y = 0; y < SNAKE_MAPHEIGHT; ++y) {
795 for (int x = 0; x < SNAKE_MAPWIDTH; ++x) {
796 if (map[x][y] == 0) {
797 possible = true;
798 }
799 if (possible) {
800 break;
801 }
802 }
803 if (possible) {
804 break;
805 }
806 }
807
808 if (possible) {
809 while (true) {
810 int x = random(0, SNAKE_MAPWIDTH - 1);
811 int y = random(0, SNAKE_MAPHEIGHT - 1);
812 if (map[x][y] == 0) {
813 map[x][y] = -1;
814 break;
815 }
816 }
817 }
818}
819
820void SnakePanel::Move(int dir) {
821 if ((last_dir == NORTH && dir == SOUTH) || (last_dir == WEST && dir == EAST) || (last_dir == EAST && dir == WEST) || (last_dir == SOUTH && dir == NORTH)) {
822 return;
823 }
824
825 int nx = 0, ny = 0;
826 int head_x = 0, head_y = 0;
827 for (int y = 0; y < SNAKE_MAPHEIGHT; ++y) {
828 for (int x = 0; x < SNAKE_MAPWIDTH; ++x) {
829 if (map[x][y] == length) {
830 head_x = x;
831 head_y = y;
832 }
833 }
834 }
835 switch (dir) {
836 case NORTH: {
837 nx = head_x;
838 ny = head_y - 1;
839 break;
840 }
841 case SOUTH: {
842 nx = head_x;
843 ny = head_y + 1;
844 break;
845 }
846 case WEST: {
847 nx = head_x - 1;
848 ny = head_y;
849 break;
850 }
851 case EAST: {
852 nx = head_x + 1;
853 ny = head_y;
854 break;
855 }
856 default:
857 return;
858 }
859
860 if (map[nx][ny] > 0 || nx < 0 || ny < 0 || nx >= SNAKE_MAPWIDTH || ny >= SNAKE_MAPHEIGHT) {
861 // Crash
862 dead = true;
863 g_gui.PopupDialog("Game Over", "You reached a length of " + i2ws(length) + "!", wxOK);
864 NewGame();
865 SetFocus();
866 } else {
867 // Walk!
868 if (map[nx][ny] == -1) {
869 // Took apple!
870 length += 1;
871 UpdateTitle();
872 NewApple();
873 } else {
874 for (int y = 0; y < SNAKE_MAPHEIGHT; ++y) {
875 for (int x = 0; x < SNAKE_MAPWIDTH; ++x) {
876 if (map[x][y] > 0) {
877 map[x][y] -= 1;
878 }
879 }
880 }
881 }
882 map[nx][ny] = length;
883 }
884 last_dir = dir;
885
886 Refresh();
887}
888
Top