mirror of
https://github.com/opentibiabr/remeres-map-editor
synced 2026-08-15 18:26:04 -04:00
Removed boosts (replaced with equivalent on std 14/17/20) With this modification, there are only 16 libs for the project to add, simplifying a lot. So two additional libraries will be needed to compile: asio, nlohmann-json I also removed OTGZ support by default and disabled it, making it optional (needs to be enabled). Anyone who wants to use it will need to activate this, I think it's ideal since few should use it, so a majority don't use it, which is fairer that those who need to activate it to use it than those who don't need to have this in their program.
878 lines
19 KiB
C++
878 lines
19 KiB
C++
//////////////////////////////////////////////////////////////////////
|
|
// This file is part of Remere's Map Editor
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Remere's Map Editor is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Remere's Map Editor is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#include "main.h"
|
|
|
|
#include "gui.h"
|
|
|
|
#include "about_window.h"
|
|
#include <fstream>
|
|
#include <typeinfo>
|
|
#include <memory>
|
|
|
|
class GamePanel : public wxPanel
|
|
{
|
|
public:
|
|
GamePanel(wxWindow* parent, int width, int height);
|
|
virtual ~GamePanel();
|
|
|
|
void OnPaint(wxPaintEvent&);
|
|
void OnKeyDown(wxKeyEvent&);
|
|
void OnKeyUp(wxKeyEvent&);
|
|
void OnIdle(wxIdleEvent&);
|
|
|
|
void pause() { paused_val = true; }
|
|
void unpause() { paused_val = false; }
|
|
bool paused() const { return paused_val || dead; }
|
|
|
|
protected:
|
|
virtual void Render(wxDC& pdc) = 0;
|
|
virtual void GameLoop(int time) = 0;
|
|
virtual void OnKey(wxKeyEvent& event, bool down) = 0;
|
|
|
|
virtual int getFPS() const = 0;
|
|
|
|
protected:
|
|
wxStopWatch game_timer;
|
|
|
|
private:
|
|
bool paused_val;
|
|
|
|
DECLARE_EVENT_TABLE()
|
|
|
|
protected:
|
|
bool dead;
|
|
};
|
|
|
|
const int TETRIS_MAPHEIGHT = 20;
|
|
const int TETRIS_MAPWIDTH = 10;
|
|
|
|
class TetrisPanel : public GamePanel
|
|
{
|
|
public:
|
|
TetrisPanel(wxWindow* parent);
|
|
~TetrisPanel();
|
|
|
|
protected:
|
|
virtual void Render(wxDC& pdc);
|
|
virtual void GameLoop(int time);
|
|
virtual void OnKey(wxKeyEvent& event, bool down);
|
|
|
|
virtual int getFPS() const { return lines / 10 + 3; }
|
|
|
|
enum Color {
|
|
NO_COLOR,
|
|
RED,
|
|
BLUE,
|
|
GREEN,
|
|
STEEL,
|
|
YELLOW,
|
|
PURPLE,
|
|
WHITE,
|
|
};
|
|
|
|
enum BlockType {
|
|
FIRST_BLOCK,
|
|
BLOCK_TOWER = FIRST_BLOCK,
|
|
BLOCK_SQUARE,
|
|
BLOCK_TRIANGLE,
|
|
BLOCK_L,
|
|
BLOCK_J,
|
|
BLOCK_Z,
|
|
BLOCK_S,
|
|
LAST_BLOCK = BLOCK_S
|
|
};
|
|
|
|
struct Block {
|
|
Color structure[4][4];
|
|
int x, y;
|
|
} block;
|
|
|
|
const wxBrush& GetBrush(Color color) const;
|
|
bool BlockCollisionTest(int mx, int my) const;
|
|
void RemoveRow(int row);
|
|
void NewBlock();
|
|
void MoveBlock(int x, int y);
|
|
void RotateBlock();
|
|
void NewGame();
|
|
void EndGame();
|
|
void AddScore(int lines);
|
|
|
|
int score;
|
|
int lines;
|
|
Color map[TETRIS_MAPWIDTH][TETRIS_MAPHEIGHT];
|
|
};
|
|
|
|
const int SNAKE_MAPHEIGHT = 20;
|
|
const int SNAKE_MAPWIDTH = 20;
|
|
|
|
class SnakePanel : public GamePanel
|
|
{
|
|
public:
|
|
SnakePanel(wxWindow* parent);
|
|
~SnakePanel();
|
|
|
|
protected:
|
|
virtual void Render(wxDC& pdc);
|
|
virtual void GameLoop(int time);
|
|
virtual void OnKey(wxKeyEvent& event, bool down);
|
|
|
|
virtual int getFPS() const { return 7; }
|
|
|
|
enum {
|
|
NORTH,
|
|
SOUTH,
|
|
WEST,
|
|
EAST,
|
|
};
|
|
|
|
void NewApple();
|
|
void Move(int dir);
|
|
void NewGame();
|
|
void EndGame();
|
|
void UpdateTitle();
|
|
|
|
// -1 is apple, 0 is nothing, >0 is snake (will decay in n rounds)
|
|
int length;
|
|
int last_dir;
|
|
int map[SNAKE_MAPWIDTH][SNAKE_MAPHEIGHT];
|
|
};
|
|
|
|
//=============================================================================
|
|
// About Window - Information window about the application
|
|
|
|
BEGIN_EVENT_TABLE(AboutWindow, wxDialog)
|
|
EVT_BUTTON(wxID_OK, AboutWindow::OnClickOK)
|
|
EVT_BUTTON(ABOUT_VIEW_LICENSE, AboutWindow::OnClickLicense)
|
|
EVT_MENU(ABOUT_RUN_TETRIS, AboutWindow::OnTetris)
|
|
EVT_MENU(ABOUT_RUN_SNAKE, AboutWindow::OnSnake)
|
|
EVT_MENU(wxID_CANCEL, AboutWindow::OnClickOK)
|
|
END_EVENT_TABLE()
|
|
|
|
AboutWindow::AboutWindow(wxWindow* parent) :
|
|
wxDialog(parent, wxID_ANY, "About", wxDefaultPosition, wxSize(300, 320), wxRESIZE_BORDER | wxCAPTION | wxCLOSE_BOX),
|
|
game_panel(nullptr)
|
|
{
|
|
wxString about;
|
|
|
|
std::string compiler;
|
|
#if defined(__clang__)
|
|
compiler = fmt::format("Clang++ {}.{}.{}", __clang_major__, __clang_minor__, __clang_patchlevel__);
|
|
#elif defined(_MSC_VER)
|
|
compiler = fmt::format("Microsoft Visual Studio {}", _MSC_VER);
|
|
#elif defined(__GNUC__)
|
|
compiler = fmt::format("G++ {}.{}.{}", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
|
|
#else
|
|
compiler = "unknown";
|
|
#endif
|
|
|
|
about << "This is an OpenTibia Map Editor created by Remere.\n";
|
|
about << "Version " << __W_RME_VERSION__ << " for ";
|
|
about <<
|
|
#ifdef __WINDOWS__
|
|
"Windows";
|
|
#elif __LINUX__
|
|
"Linux";
|
|
#elif __APPLE__
|
|
"macOS";
|
|
#else
|
|
"Unsupported OS";
|
|
#endif
|
|
about << "\n\n";
|
|
|
|
about << "Using " << wxVERSION_STRING << " interface\n";
|
|
about << "OpenGL version " << wxString((char*)glGetString(GL_VERSION), wxConvUTF8) << "\n";
|
|
about << "\n";
|
|
about << "This program comes with ABSOLUTELY NO WARRANTY;\n";
|
|
about << "for details see the LICENSE file.\n";
|
|
about << "This is free software, and you are welcome to redistribute it\n";
|
|
about << "under certain conditions.\n";
|
|
about << "\n";
|
|
about << "Compiled on: " << __TDATE__ << " : " << __TTIME__ << "\n";
|
|
about << "Compiled with: " << compiler << "\n";
|
|
|
|
topsizer = newd wxBoxSizer(wxVERTICAL);
|
|
|
|
topsizer->Add(newd wxStaticText(this, wxID_ANY, about), 1, wxALL, 20);
|
|
|
|
wxSizer* choicesizer = newd wxBoxSizer(wxHORIZONTAL);
|
|
choicesizer->Add(newd wxButton(this, wxID_OK, "OK"), wxSizerFlags(1).Center());
|
|
topsizer->Add(choicesizer, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT | wxBOTTOM, 20);
|
|
|
|
wxAcceleratorEntry entries[3];
|
|
entries[0].Set(wxACCEL_NORMAL, WXK_ESCAPE, wxID_CANCEL);
|
|
entries[1].Set(wxACCEL_NORMAL, 't', ABOUT_RUN_TETRIS);
|
|
entries[2].Set(wxACCEL_NORMAL, 's', ABOUT_RUN_SNAKE);
|
|
wxAcceleratorTable accel(3, entries);
|
|
SetAcceleratorTable(accel);
|
|
|
|
SetSizerAndFit(topsizer);
|
|
Centre(wxBOTH);
|
|
}
|
|
|
|
AboutWindow::~AboutWindow()
|
|
{
|
|
////
|
|
}
|
|
|
|
void AboutWindow::OnClickOK(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
EndModal(0);
|
|
}
|
|
|
|
void AboutWindow::OnClickLicense(wxCommandEvent& WXUNUSED(event))
|
|
{
|
|
FileName path;
|
|
try {
|
|
path = wxStandardPaths::Get().GetExecutablePath();
|
|
} catch(std::bad_cast&) {
|
|
return;
|
|
}
|
|
path.SetFullName("COPYING.txt");
|
|
std::ifstream gpl(path.GetFullPath().mb_str());
|
|
|
|
std::string gpl_str;
|
|
char ch;
|
|
while(gpl.get(ch)) {
|
|
gpl_str += ch;
|
|
}
|
|
|
|
g_gui.ShowTextBox(this, "License", wxstr(gpl_str.size()? gpl_str : "The COPYING.txt file is not available."));
|
|
}
|
|
|
|
void AboutWindow::OnTetris(wxCommandEvent&)
|
|
{
|
|
if(!game_panel) {
|
|
DestroyChildren();
|
|
game_panel = newd TetrisPanel(this);
|
|
topsizer->Add(game_panel, 1, wxALIGN_CENTER | wxALL, 7);
|
|
Fit();
|
|
game_panel->SetFocus();
|
|
SetWindowStyleFlag(wxRESIZE_BORDER | wxCAPTION | wxCLOSE_BOX);
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
void AboutWindow::OnSnake(wxCommandEvent&)
|
|
{
|
|
if(!game_panel) {
|
|
DestroyChildren();
|
|
game_panel = newd SnakePanel(this);
|
|
topsizer->Add(game_panel, 1, wxALIGN_CENTER | wxALL, 7);
|
|
Fit();
|
|
game_panel->SetFocus();
|
|
SetWindowStyleFlag(wxRESIZE_BORDER | wxCAPTION | wxCLOSE_BOX);
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// GamePanel - Abstract class for games
|
|
|
|
BEGIN_EVENT_TABLE(GamePanel, wxPanel)
|
|
EVT_KEY_DOWN(GamePanel::OnKeyDown)
|
|
EVT_KEY_UP(GamePanel::OnKeyUp)
|
|
EVT_PAINT(GamePanel::OnPaint)
|
|
EVT_IDLE(GamePanel::OnIdle)
|
|
END_EVENT_TABLE()
|
|
|
|
GamePanel::GamePanel(wxWindow *parent, int width, int height) :
|
|
wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(width, height), wxWANTS_CHARS),
|
|
paused_val(false),
|
|
dead(false)
|
|
{
|
|
// Receive idle events
|
|
SetExtraStyle(wxWS_EX_PROCESS_IDLE);
|
|
// Complete redraw
|
|
SetBackgroundStyle(wxBG_STYLE_CUSTOM);
|
|
}
|
|
|
|
GamePanel::~GamePanel()
|
|
{
|
|
////
|
|
}
|
|
|
|
void GamePanel::OnPaint(wxPaintEvent&)
|
|
{
|
|
wxBufferedPaintDC pdc(this);
|
|
Render(pdc);
|
|
}
|
|
|
|
void GamePanel::OnKeyDown(wxKeyEvent& event)
|
|
{
|
|
switch(event.GetKeyCode()) {
|
|
case WXK_ESCAPE: {
|
|
if(dead) return;
|
|
wxDialog* dlg = (wxDialog*)GetParent();
|
|
dlg->EndModal(0);
|
|
break;
|
|
}
|
|
default: {
|
|
OnKey(event, true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GamePanel::OnKeyUp(wxKeyEvent& event)
|
|
{
|
|
OnKey(event, false);
|
|
}
|
|
|
|
void GamePanel::OnIdle(wxIdleEvent& event)
|
|
{
|
|
int time = game_timer.Time();
|
|
if(time > 1000 / getFPS()) {
|
|
game_timer.Start();
|
|
if(!paused()) GameLoop(time);
|
|
}
|
|
if(!paused()) {
|
|
event.RequestMore(true);
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// TetrisPanel - A window with a Tetris game!
|
|
|
|
TetrisPanel::TetrisPanel(wxWindow* parent) : GamePanel(parent, 16*TETRIS_MAPWIDTH, 16*TETRIS_MAPHEIGHT)
|
|
{
|
|
NewGame();
|
|
}
|
|
|
|
TetrisPanel::~TetrisPanel()
|
|
{
|
|
////
|
|
}
|
|
|
|
const wxBrush& TetrisPanel::GetBrush(Color color) const
|
|
{
|
|
static std::unique_ptr<wxBrush> yellow_brush;
|
|
static std::unique_ptr<wxBrush> purple_brush;
|
|
|
|
if(yellow_brush.get() == nullptr) yellow_brush.reset(newd wxBrush(wxColor(255, 255, 0)));
|
|
if(purple_brush.get() == nullptr) purple_brush.reset(newd wxBrush(wxColor(128, 0, 255)));
|
|
|
|
const wxBrush* brush = nullptr;
|
|
switch(color) {
|
|
case RED: brush = wxRED_BRUSH; break;
|
|
case BLUE: brush = wxCYAN_BRUSH; break;
|
|
case GREEN: brush = wxGREEN_BRUSH; break;
|
|
case PURPLE: brush = purple_brush.get(); break;
|
|
case YELLOW: brush = yellow_brush.get(); break;
|
|
case WHITE: brush = wxWHITE_BRUSH; break;
|
|
case STEEL: brush = wxGREY_BRUSH; break;
|
|
default: brush = wxBLACK_BRUSH; break;
|
|
}
|
|
return *brush;
|
|
}
|
|
|
|
void TetrisPanel::Render(wxDC& pdc)
|
|
{
|
|
pdc.SetBackground(*wxBLACK_BRUSH);
|
|
pdc.Clear();
|
|
|
|
for(int y = 0; y < TETRIS_MAPHEIGHT; ++y) {
|
|
for(int x = 0; x < TETRIS_MAPWIDTH; ++x) {
|
|
pdc.SetBrush(GetBrush(map[x][y]));
|
|
pdc.DrawRectangle(x * 16, y * 16, 16, 16);
|
|
}
|
|
}
|
|
|
|
for(int y = 0; y < 4; ++y) {
|
|
for(int x = 0; x < 4; ++x) {
|
|
if(block.structure[x][y] != NO_COLOR) {
|
|
pdc.SetBrush(GetBrush(block.structure[x][y]));
|
|
pdc.DrawRectangle((block.x + x) * 16, (block.y + y) * 16, 16, 16);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void TetrisPanel::OnKey(wxKeyEvent& event, bool down)
|
|
{
|
|
if(!down || dead) return;
|
|
|
|
switch(event.GetKeyCode()) {
|
|
case WXK_SPACE: {
|
|
if(paused())
|
|
unpause();
|
|
else pause();
|
|
break;
|
|
}
|
|
case WXK_NUMPAD_UP:
|
|
case WXK_UP: {
|
|
if(dead) return;
|
|
unpause();
|
|
RotateBlock();
|
|
break;
|
|
}
|
|
case WXK_NUMPAD_DOWN:
|
|
case WXK_DOWN: {
|
|
if(dead) return;
|
|
unpause();
|
|
MoveBlock(0, 1);
|
|
break;
|
|
}
|
|
case WXK_NUMPAD_LEFT:
|
|
case WXK_LEFT: {
|
|
if(dead) return;
|
|
unpause();
|
|
MoveBlock(-1, 0);
|
|
break;
|
|
}
|
|
case WXK_NUMPAD_RIGHT:
|
|
case WXK_RIGHT: {
|
|
if(dead) return;
|
|
unpause();
|
|
MoveBlock(1, 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void TetrisPanel::NewGame()
|
|
{
|
|
NewBlock();
|
|
score = 0;
|
|
lines = 0;
|
|
game_timer.Start();
|
|
unpause();
|
|
dead = false;
|
|
|
|
// Clear map
|
|
for(int y = 0; y < TETRIS_MAPHEIGHT; ++y) {
|
|
for(int x = 0; x < TETRIS_MAPWIDTH; ++x) {
|
|
map[x][y] = NO_COLOR;
|
|
}
|
|
}
|
|
AddScore(0); // Update title
|
|
}
|
|
|
|
void TetrisPanel::AddScore(int lines_added)
|
|
{
|
|
lines += lines_added;
|
|
score += lines_added*lines_added * 10;
|
|
wxString title = "Remere's Tetris : ";
|
|
title << score << " points ";
|
|
title << lines << " lines";
|
|
((wxTopLevelWindow*)GetParent())->SetTitle(title);
|
|
}
|
|
|
|
void TetrisPanel::GameLoop(int time)
|
|
{
|
|
MoveBlock(0, 1);
|
|
}
|
|
|
|
bool TetrisPanel::BlockCollisionTest(int mx, int my) const
|
|
{
|
|
int nx = block.x + mx;
|
|
int ny = block.y + my;
|
|
|
|
for(int y = 0; y < 4; ++y) {
|
|
for(int x = 0; x < 4; ++x) {
|
|
if(block.structure[x][y] != NO_COLOR) {
|
|
if(nx + x < 0 || nx + x > TETRIS_MAPWIDTH - 1 || ny + y < 0 || ny + y > TETRIS_MAPHEIGHT - 1) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for(int y = 0; y < TETRIS_MAPHEIGHT; ++y) {
|
|
for(int x = 0; x < TETRIS_MAPWIDTH; ++x) {
|
|
if(x >= nx && x < nx + 4 && y >= ny && y < ny + 4) {
|
|
if(map[x][y] != NO_COLOR && block.structure[x - nx][y - ny] != NO_COLOR) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void TetrisPanel::RemoveRow(int row)
|
|
{
|
|
for(int x = 0; x < TETRIS_MAPWIDTH; ++x) {
|
|
for(int y = row; y > 0; --y) { //Move all above one step down
|
|
map[x][y] = map[x][y-1];
|
|
}
|
|
}
|
|
}
|
|
|
|
void TetrisPanel::NewBlock()
|
|
{
|
|
block.x = TETRIS_MAPWIDTH / 2;
|
|
block.y = -1;
|
|
|
|
for(int y = 0; y < 4; ++y) {
|
|
for(int x = 0; x < 4; ++x) {
|
|
block.structure[x][y] = NO_COLOR;
|
|
}
|
|
}
|
|
|
|
switch(random(FIRST_BLOCK, LAST_BLOCK)) {
|
|
case BLOCK_TOWER: {
|
|
block.structure[1][0] = RED;
|
|
block.structure[1][1] = RED;
|
|
block.structure[1][2] = RED;
|
|
block.structure[1][3] = RED;
|
|
block.y = 0;
|
|
break;
|
|
}
|
|
default: case BLOCK_SQUARE: {
|
|
block.structure[1][1] = BLUE;
|
|
block.structure[1][2] = BLUE;
|
|
block.structure[2][1] = BLUE;
|
|
block.structure[2][2] = BLUE;
|
|
break;
|
|
}
|
|
case BLOCK_TRIANGLE: {
|
|
block.structure[1][1] = STEEL;
|
|
block.structure[0][2] = STEEL;
|
|
block.structure[1][2] = STEEL;
|
|
block.structure[2][2] = STEEL;
|
|
break;
|
|
}
|
|
case BLOCK_Z: {
|
|
block.structure[0][1] = YELLOW;
|
|
block.structure[1][1] = YELLOW;
|
|
block.structure[1][2] = YELLOW;
|
|
block.structure[2][2] = YELLOW;
|
|
break;
|
|
}
|
|
case BLOCK_S: {
|
|
block.structure[2][1] = GREEN;
|
|
block.structure[1][1] = GREEN;
|
|
block.structure[1][2] = GREEN;
|
|
block.structure[0][2] = GREEN;
|
|
break;
|
|
}
|
|
case BLOCK_J: {
|
|
block.structure[1][1] = WHITE;
|
|
block.structure[2][1] = WHITE;
|
|
block.structure[2][2] = WHITE;
|
|
block.structure[2][3] = WHITE;
|
|
break;
|
|
}
|
|
case BLOCK_L: {
|
|
block.structure[2][1] = PURPLE;
|
|
block.structure[1][1] = PURPLE;
|
|
block.structure[1][2] = PURPLE;
|
|
block.structure[1][3] = PURPLE;
|
|
// break; missing add it?
|
|
}
|
|
}
|
|
}
|
|
|
|
void TetrisPanel::MoveBlock(int x, int y)
|
|
{
|
|
if(BlockCollisionTest(x, y)) {
|
|
if(y == 1) { // moving down...
|
|
if(block.y < 1) { // Out of bounds!
|
|
dead = true;
|
|
g_gui.PopupDialog("Game Over", "You reached a score of " + i2ws(score) + "!", wxOK);
|
|
NewGame();
|
|
SetFocus();
|
|
} else {
|
|
// Freeze the old block onto the map
|
|
for(int y = 0; y < 4; ++y) {
|
|
for(int x = 0; x < 4; ++x) {
|
|
if(block.structure[x][y] != NO_COLOR) {
|
|
map[block.x+x][block.y+y] = block.structure[x][y];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Any cleared rows?
|
|
int cleared = 0;
|
|
for(int y = 0; y < TETRIS_MAPHEIGHT; ++y) {
|
|
bool full = true;
|
|
for(int x = 0; x < TETRIS_MAPWIDTH; ++x) {
|
|
if(map[x][y] == NO_COLOR) {
|
|
full = false;
|
|
}
|
|
}
|
|
if(full) {
|
|
RemoveRow(y);
|
|
++cleared;
|
|
}
|
|
}
|
|
AddScore(cleared);
|
|
NewBlock();
|
|
}
|
|
} // If we're not moving down, we're not moving the block either
|
|
} else {
|
|
// No collision so move the block!
|
|
block.x += x;
|
|
block.y += y;
|
|
}
|
|
|
|
Refresh();
|
|
}
|
|
|
|
void TetrisPanel::RotateBlock()
|
|
{
|
|
Block temp;
|
|
|
|
for(int y = 0; y < 4; ++y) {
|
|
for(int x = 0; x < 4; ++x) {
|
|
temp.structure[3-x][y] = block.structure[y][x];
|
|
}
|
|
}
|
|
|
|
for(int y = 0; y < 4; ++y) {
|
|
for(int x = 0; x < 4; ++x) {
|
|
if(temp.structure[x][y] != NO_COLOR) {
|
|
if(block.x + x < 0 || block.x + x > TETRIS_MAPWIDTH - 1 || block.y + y < 0 || block.y + y > TETRIS_MAPHEIGHT - 1) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for(int y = 0; y < TETRIS_MAPWIDTH; ++y) {
|
|
for(int x = 0; x < TETRIS_MAPHEIGHT; ++x) {
|
|
if(x >= block.x && x < block.x + 4 && y >= block.y && y < block.y +4) {
|
|
if(map[x][y] != NO_COLOR && temp.structure[x - block.x][y - block.y] != NO_COLOR) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for(int y = 0; y < 4; ++y) {
|
|
for(int x = 0; x < 4; ++x) {
|
|
block.structure[x][y] = temp.structure[x][y];
|
|
}
|
|
}
|
|
|
|
Refresh();
|
|
}
|
|
|
|
//=============================================================================
|
|
// SnakePanel - A window with a Snake game!
|
|
|
|
SnakePanel::SnakePanel(wxWindow* parent) : GamePanel(parent, 16*SNAKE_MAPWIDTH, 16*SNAKE_MAPHEIGHT)
|
|
{
|
|
NewGame();
|
|
}
|
|
|
|
SnakePanel::~SnakePanel()
|
|
{
|
|
////
|
|
}
|
|
|
|
void SnakePanel::Render(wxDC& pdc)
|
|
{
|
|
pdc.SetBackground(*wxBLACK_BRUSH);
|
|
pdc.Clear();
|
|
|
|
wxBrush snakebrush(wxColor(0, 0, 255));
|
|
wxBrush applebrush(wxColor(255, 0, 0));
|
|
|
|
double lblue = 1.0;
|
|
double lred = 0.5;
|
|
double lgreen = 0.0;
|
|
|
|
for(int y = 0; y < SNAKE_MAPHEIGHT; ++y) {
|
|
for(int x = 0; x < SNAKE_MAPWIDTH; ++x) {
|
|
if(map[x][y] == -1) { // Apple
|
|
pdc.SetBrush(applebrush);
|
|
pdc.DrawRectangle(x*16, y*16, 16, 16);
|
|
} else if(map[x][y] > 0) { // Snake
|
|
double snook = double(map[x][y]) / length;
|
|
snakebrush.SetColour(wxColor(
|
|
int(255.0 * (1.0 - abs(lred - snook))),
|
|
int(255.0 * (1.0 - abs(lgreen - snook))),
|
|
int(255.0 * (1.0 - abs(lblue - snook)))
|
|
)
|
|
);
|
|
pdc.SetBrush(snakebrush);
|
|
pdc.DrawRectangle(x*16, y*16, 16, 16);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SnakePanel::OnKey(wxKeyEvent& event, bool down)
|
|
{
|
|
if(!down) return;
|
|
|
|
int keyCode = event.GetKeyCode();
|
|
if(keyCode == WXK_SPACE) {
|
|
if(paused())
|
|
unpause();
|
|
else
|
|
pause();
|
|
} else if(!dead) {
|
|
switch (keyCode) {
|
|
case WXK_NUMPAD_UP:
|
|
case WXK_UP: {
|
|
unpause();
|
|
Move(NORTH);
|
|
break;
|
|
}
|
|
case WXK_NUMPAD_DOWN:
|
|
case WXK_DOWN: {
|
|
unpause();
|
|
Move(SOUTH);
|
|
break;
|
|
}
|
|
case WXK_NUMPAD_LEFT:
|
|
case WXK_LEFT: {
|
|
unpause();
|
|
Move(WEST);
|
|
break;
|
|
}
|
|
case WXK_NUMPAD_RIGHT:
|
|
case WXK_RIGHT: {
|
|
unpause();
|
|
Move(EAST);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SnakePanel::NewGame()
|
|
{
|
|
length = 3;
|
|
game_timer.Start();
|
|
last_dir = NORTH;
|
|
unpause();
|
|
dead = false;
|
|
|
|
// Clear map
|
|
for(int y = 0; y < SNAKE_MAPHEIGHT; ++y) {
|
|
for(int x = 0; x < SNAKE_MAPWIDTH; ++x) {
|
|
map[x][y] = 0;
|
|
}
|
|
}
|
|
map[SNAKE_MAPWIDTH / 2][SNAKE_MAPHEIGHT / 2] = length;
|
|
NewApple();
|
|
UpdateTitle(); // Update title
|
|
}
|
|
|
|
void SnakePanel::UpdateTitle()
|
|
{
|
|
wxString title = "Remere's Snake : ";
|
|
title << length << " segments";
|
|
((wxTopLevelWindow*)GetParent())->SetTitle(title);
|
|
}
|
|
|
|
void SnakePanel::GameLoop(int time)
|
|
{
|
|
Move(last_dir);
|
|
}
|
|
|
|
void SnakePanel::NewApple()
|
|
{
|
|
bool possible = false;
|
|
for(int y = 0; y < SNAKE_MAPHEIGHT; ++y) {
|
|
for(int x = 0; x < SNAKE_MAPWIDTH; ++x) {
|
|
if(map[x][y] == 0) {
|
|
possible = true;
|
|
}
|
|
if(possible) break;
|
|
}
|
|
if(possible) break;
|
|
}
|
|
|
|
if(possible) {
|
|
while(true) {
|
|
int x = random(0, SNAKE_MAPWIDTH - 1);
|
|
int y = random(0, SNAKE_MAPHEIGHT - 1);
|
|
if(map[x][y] == 0) {
|
|
map[x][y] = -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SnakePanel::Move(int dir)
|
|
{
|
|
if((last_dir == NORTH && dir == SOUTH) ||
|
|
(last_dir == WEST && dir == EAST) ||
|
|
(last_dir == EAST && dir == WEST) ||
|
|
(last_dir == SOUTH && dir == NORTH)) {
|
|
return;
|
|
}
|
|
|
|
int nx = 0, ny = 0;
|
|
int head_x = 0, head_y = 0;
|
|
for(int y = 0; y < SNAKE_MAPHEIGHT; ++y) {
|
|
for(int x = 0; x < SNAKE_MAPWIDTH; ++x) {
|
|
if(map[x][y] == length) {
|
|
head_x = x;
|
|
head_y = y;
|
|
}
|
|
}
|
|
}
|
|
switch(dir) {
|
|
case NORTH: {
|
|
nx = head_x;
|
|
ny = head_y - 1;
|
|
break;
|
|
}
|
|
case SOUTH: {
|
|
nx = head_x;
|
|
ny = head_y + 1;
|
|
break;
|
|
}
|
|
case WEST: {
|
|
nx = head_x - 1;
|
|
ny = head_y;
|
|
break;
|
|
}
|
|
case EAST: {
|
|
nx = head_x + 1;
|
|
ny = head_y;
|
|
break;
|
|
}
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if(map[nx][ny] > 0 || nx < 0 || ny < 0 || nx >= SNAKE_MAPWIDTH || ny >= SNAKE_MAPHEIGHT) {
|
|
// Crash
|
|
dead = true;
|
|
g_gui.PopupDialog("Game Over", "You reached a length of " + i2ws(length) + "!", wxOK);
|
|
NewGame();
|
|
SetFocus();
|
|
} else {
|
|
// Walk!
|
|
if(map[nx][ny] == -1) {
|
|
// Took apple!
|
|
length += 1;
|
|
UpdateTitle();
|
|
NewApple();
|
|
} else {
|
|
for(int y = 0; y < SNAKE_MAPHEIGHT; ++y) {
|
|
for(int x = 0; x < SNAKE_MAPWIDTH; ++x) {
|
|
if(map[x][y] > 0) {
|
|
map[x][y] -= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
map[nx][ny] = length;
|
|
}
|
|
last_dir = dir;
|
|
|
|
Refresh();
|
|
}
|