//+------------------------------------------------------------------+ //| Main.mqh | //| Copyright 2025, Niquel Mendoza. | //| https://www.mql5.com/es/users/nique_372/news | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, Niquel Mendoza." #property link "https://www.mql5.com/es/users/nique_372/news" #property strict //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "Espacio\\Main.mqh" #include "..\\..\\GrapichsByLeo\\General\\ARGBGenerator.mqh" #include "..\\..\\GrapichsByLeo\\General\\TextoCanvas.mqh" #include "..\\..\\MQLArticles\\Utils\\RandomSimple.mqh" #include #include enum ENUM_TETRIS_CURR_STATE { TETRIS_STATE_STOP = 0, // Fin ya no hacemos nada TETRIS_STATE_PROCESS_FIGURE = 1, TETRIS_STATE_ESPERAR_NUEVA_FIGURA = 2 }; const string g_tetris_str_status[3] { "Detenido", "Procesando figura", "Esperando nueva figura" }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CTetrisGame : public CAppDialog { private: // Pestaña 1 CPicture m_image; // Imagen del canvas //--- Del juego ENUM_TETRIS_CURR_STATE m_state; ENUM_TETRIS_CURR_STATE m_prev_state; CColorGeneratorArgb m_color_gen; //CRandomSimple m_random; CCanvasCustom* m_canvas; CEspacioUtilizable* m_espacio; CTetrisFigura* m_curr_figura; int m_factor_pixel; uint m_back_clr; uint m_clr_tetris; // int m_section_sizepx; int m_section_px_step; int m_x_inicial; // CTextCanvas* m_titulo; // CTextCanvas m_status_test; CTextCanvas m_the_status_text; // CTextCanvas* m_score_text; CTextCanvas* m_the_score_text; int m_score; // CTextCanvas* m_copyright; // bool m_stop_game; // CTetrisFigura* RandomFigure(); // Procesamiento de figura bool VerifyFigure(); // true: colisiona | false: no colisiona bool ProcessFigure(); // true: FIn | false: continua __forceinline void DeleteCurrFigure(); // Evento de tecla ushort m_tecla_abajo; ushort m_tecla_izquierda; ushort m_tecla_derecha; ushort m_tecla_pausar; void OnTeclaAbajo(); void OnTeclaIzquierda(); void OnTeclaDerecha(); // Estado void StateChange(ENUM_TETRIS_CURR_STATE state); void StateResetLatValue(); //void DrawFigureInStatus(); void ResetGame(); void OnGameOver(); void OnEspaciosEliminados(int s); void SetScoreValue(int value); void UpdateImage(); //--- void Minimize() override; void Maximize() override; public: CTetrisGame(void); ~CTetrisGame(void); //--- void Init(int width, int height, int xsize_seccion, uint back_clr, uint tetris_clr, int scale); void OnTimerEvent(); bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam); void OnDeinitEvent(const int reason); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CTetrisGame::CTetrisGame(void) { m_canvas = new CCanvasCustom(); m_score = 0; m_espacio = new CEspacioUtilizable(); m_curr_figura = NULL; m_titulo = new CTextCanvas(); m_score_text = new CTextCanvas(); m_copyright = new CTextCanvas(); m_the_score_text = new CTextCanvas(); m_tecla_abajo = 's'; m_tecla_derecha = 'd'; m_tecla_izquierda = 'a'; m_tecla_pausar = 'p'; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CTetrisGame::~CTetrisGame() { delete m_canvas; delete m_espacio; delete m_titulo; delete m_score_text; delete m_copyright; delete m_the_score_text; DeleteCurrFigure(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTetrisGame::ResetGame(void) { m_espacio.Resetear(); m_the_score_text.Text("0"); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTetrisGame::StateChange(ENUM_TETRIS_CURR_STATE state) { m_the_status_text.Text(g_tetris_str_status[state]); UpdateImage(); m_prev_state = m_state; m_state = state; } //+------------------------------------------------------------------+ void CTetrisGame::StateResetLatValue(void) { m_state = m_prev_state; m_the_status_text.Text(g_tetris_str_status[m_state]); UpdateImage(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTetrisGame::Minimize(void) { // m_stop_game = true; // Paramos el juego StateChange(TETRIS_STATE_STOP); CAppDialog::Minimize(); } //+------------------------------------------------------------------+ void CTetrisGame::Maximize() { //m_stop_game = false; StateResetLatValue(); CAppDialog::Maximize(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTetrisGame::Init(int width, int height, int xsize_seccion, uint back_clr, uint tetris_clr, int scale) { //--- Creacion del juego // Bitamp m_canvas.Create("restetrisgame", width, height, COLOR_FORMAT_ARGB_RAW); m_canvas.Erase(back_clr); m_back_clr = back_clr; m_clr_tetris = tetris_clr; m_factor_pixel = scale; // Fontset m_canvas.FontSet("Arial", 14); // Creacion del titulo m_titulo.Create(m_canvas, (width >> 1), 20, "Tetris", ::ColorToARGB(clrBlack), TA_CENTER | TA_VCENTER); // Creacion del lienzo int x1_l = 10; int x2_l = width - xsize_seccion - 10; int y1_l = 40; int y2_l = height - 15; int w2 = x2_l - x1_l; m_section_sizepx = w2 / scale; int s = m_section_sizepx >> 1; if(s * 2 != m_section_sizepx) // No es par { m_section_sizepx = s * 2; } x2_l = (m_section_sizepx * scale); m_section_px_step = m_section_sizepx >> 1; m_x_inicial = x1_l; PrintFormat("X Inicial = %d | Section size = %d | Section step size = %d", m_x_inicial, m_section_sizepx, m_section_px_step); //--- m_canvas.Rectangle(x1_l - 1, y1_l - 1, x2_l + 1, y2_l + 1, 0xFFFFFFFF); // Borde m_espacio.Init(m_canvas, y1_l, y2_l, x1_l, x2_l, m_clr_tetris); // Aqui pinta el fondo // Creacion del score int xt = (width - xsize_seccion) + (xsize_seccion >> 1); const uint clr_blacl = ColorToARGB(clrBlack); int curr_y = 45; m_score_text.Create(m_canvas, xt, curr_y, "Score", clr_blacl, TA_CENTER | TA_VCENTER, 15, "Arial"); curr_y += 15; m_the_score_text.Create(m_canvas, xt, curr_y, "0", clr_blacl, TA_CENTER | TA_VCENTER, 15, "Arial"); m_the_score_text.CleanColor(back_clr); curr_y += 30; m_status_test.Create(m_canvas, xt, curr_y, "Estado", clr_blacl, TA_CENTER | TA_VCENTER, 15, "Arial"); curr_y += 15; m_the_status_text.Create(m_canvas, xt, curr_y, g_tetris_str_status[TETRIS_STATE_ESPERAR_NUEVA_FIGURA], clr_blacl, TA_CENTER | TA_VCENTER, 15, "Arial"); m_the_status_text.CleanColor(back_clr); //--- Creacion del panel // Panel CAppDialog::Create(0, "Tetris By Leo", 0, 1, 1, width + 40, height + 100); // Imagen m_image.Create(0, "ImageTetris", 0, 20, 30, Width() - 30, Height() - 30); m_image.BmpName(m_canvas.ResourceName()); Add(m_image); // Finish UpdateImage(); ::Sleep(1000); m_state = TETRIS_STATE_ESPERAR_NUEVA_FIGURA; ::EventSetMillisecondTimer(1000); } //+------------------------------------------------------------------+ //| Procesamiento de una figura | //+------------------------------------------------------------------+ #define TESTRIS_PROCESS_FIGURA_OUT_RANGE 1 #define TESTRIS_PROCESS_FIGURA_FINALIZA 2 //+------------------------------------------------------------------+ bool CTetrisGame::VerifyFigure() { //--- int out_px[]; //--- Verificacion inicial m_curr_figura.GetOcupatePixels(TETRIS_DIR_ABAJO, out_px); if(m_espacio.YColision(out_px, m_curr_figura.Y2())) // La nueva figuira ya colisionca, se perdio { m_score = 0; ::Print("Perdiste XD"); DeleteCurrFigure(); // Eliminamos la figura OnGameOver(); // Procesamos y paramos por ahora return true; // Finalizaiocn, pero con fuera de rango } return false; } //+------------------------------------------------------------------+ bool CTetrisGame::ProcessFigure(void) { //--- Declracion de array de pixles int out_px[]; //--- Paso la veriirfacion inicial int curr_y = m_curr_figura.Y1(); // Y1 incial de la figura curr_y += m_section_px_step + 1; // Aumentamos el y Print("Nuevo y1: ", curr_y, " X1 Actual: ", m_curr_figura.X1()); m_curr_figura.GetOcupatePixels(curr_y, TETRIS_DIR_Y_ABAJO, out_px); // Obtenemos los pixceles ocuapdos por debajo //--- Verificacion de colision if(m_espacio.YColision(out_px, m_curr_figura.Y2())) // Verificamos si los proximos puixxles colisionan con el muro img { ::PrintFormat("Colision | Curr y = %d | Curr y2 = %d| MaxY: %d", curr_y, m_curr_figura.Y2(), m_espacio.YEnd()); // El siguiente y colisiona // Requerimos un resize // Calcularemos el error que hay entre las cordenas "proximas" de abajo con el muro int max_err = m_espacio.GetMaxErrorY(out_px, m_curr_figura.X1()); //::Print("Max Error: ", max_err); const int next_y2 = curr_y + m_curr_figura.DistY(); const int new_y2 = next_y2 - (max_err); const int new_y1 = new_y2 - m_curr_figura.DistY(); m_curr_figura.ChangeY(new_y1); // Corrgeimos el y dado que es menor o igual al y que tenem,os const int e = m_espacio.AddFigure(m_curr_figura); OnEspaciosEliminados(e); UpdateImage(); return true; // Colisiona } else { // No hay colision, dibujamos m_curr_figura.ChangeY(curr_y); UpdateImage(); return false; } } //+------------------------------------------------------------------+ void CTetrisGame::DeleteCurrFigure(void) { if(::CheckPointer(m_curr_figura) == POINTER_DYNAMIC) delete m_curr_figura; m_curr_figura = NULL; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTetrisGame::OnTimerEvent(void) { //::PrintFormat("Estado actual: %s", EnumToString(m_state)); switch(m_state) { case TETRIS_STATE_STOP: break; case TETRIS_STATE_ESPERAR_NUEVA_FIGURA: { m_curr_figura = RandomFigure(); if(!VerifyFigure()) { // No hay errores continuamos StateChange(TETRIS_STATE_PROCESS_FIGURE); } break; // Se sale } case TETRIS_STATE_PROCESS_FIGURE: { if(ProcessFigure()) // FIn de la figura actual { DeleteCurrFigure(); StateChange(TETRIS_STATE_ESPERAR_NUEVA_FIGURA); } break; } default: break; } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CTetrisGame::OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //Print("Hola"); #define ret return(CAppDialog::OnEvent(id, lparam, dparam, sparam)) if(id == CHARTEVENT_KEYDOWN) { const int k = (int)lparam; const ushort key = TranslateKey(k); //Print(ShortToString(key)); if(key == m_tecla_abajo) { OnTeclaAbajo(); } else if(key == m_tecla_derecha) { OnTeclaDerecha(); } else if(key == m_tecla_izquierda) { OnTeclaIzquierda(); } ret; } if(id == CHARTEVENT_KEYUP) { const int k = (int)lparam; const ushort key = TranslateKey(k); if(key == m_tecla_pausar) { if(m_state == TETRIS_STATE_STOP) { StateResetLatValue(); } else { StateChange(TETRIS_STATE_STOP); } } ret; } //--- ret; #undef ret } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTetrisGame::OnEspaciosEliminados(int s) { if(s <= 0) return; SetScoreValue((m_score + s * 100)); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTetrisGame::SetScoreValue(int value) { m_the_score_text.Text(::IntegerToString(value)); UpdateImage(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTetrisGame::OnDeinitEvent(const int reason) { Destroy(reason); ::EventKillTimer(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTetrisGame::UpdateImage(void) { m_canvas.Update(true); // Solo actulizo canvas el recurso } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTetrisGame::OnGameOver(void) { //--- StateChange(TETRIS_STATE_STOP); //--- const int res = ::MessageBox("Ha perdido el juego, desea continuar ?", "Game over", MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON1); if(res == IDYES) { ResetGame(); StateChange(TETRIS_STATE_ESPERAR_NUEVA_FIGURA); } else { ::ExpertRemove(); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTetrisGame::OnTeclaAbajo(void) { if(m_curr_figura == NULL) return; if(ProcessFigure()) // FIn de la figura actual { DeleteCurrFigure(); StateChange(TETRIS_STATE_ESPERAR_NUEVA_FIGURA); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTetrisGame::OnTeclaDerecha(void) { if(m_curr_figura == NULL || m_curr_figura.IsActiveColisionEnd()) return; //--- const int change = (m_section_px_step); //--- const int x2 = m_curr_figura.X2(); int new_x = m_curr_figura.X1() + change; int out_px[]; m_curr_figura.GetOcupatePixels(new_x, TETRIS_DIR_X_DERECHA, out_px); Print("Nuevo x1: ", new_x); if(m_espacio.XColisionAdelante(out_px, x2)) { Print("Colision"); int max_err = m_espacio.GetMaxErrorXAdelente(out_px, x2); Print("Maximo erorr = ", max_err); int next_x2 = new_x + m_curr_figura.DistX(); Print("X2 nex: ", next_x2); int new_x2 = next_x2 - (max_err); Print("Nuevo x2: ", new_x2); new_x = new_x2 - m_curr_figura.DistX(); Print("Nuevo x1: ", new_x); m_curr_figura.ChangeX(new_x); m_curr_figura.FlagColisionEndAdd(); // Colisiona con add m_curr_figura.FlagColisionInitRemove(); } else { m_curr_figura.ChangeX(new_x); m_curr_figura.FlagColisionInitRemove(); } UpdateImage(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CTetrisGame::OnTeclaIzquierda(void) { if(m_curr_figura == NULL || m_curr_figura.IsActiveColisionInit()) return; //--- const int change = (m_section_px_step); //--- const int x1 = m_curr_figura.X1(); int new_x = x1 - change; int out_px[]; m_curr_figura.GetOcupatePixels(new_x, TETRIS_DIR_X_IZQUIERDA, out_px); // Print("Nuevo x1: ", new_x); if(m_espacio.XColisionAtras(out_px, x1)) { // Print("Colision"); int max_err = m_espacio.GetMaxErrorXAtras(out_px, x1); new_x = new_x + max_err; m_curr_figura.ChangeX(new_x); m_curr_figura.FlagColisionInitAdd(); // Colisiona con add m_curr_figura.FlagColisionEndRemove(); } else { m_curr_figura.ChangeX(new_x); m_curr_figura.FlagColisionEndRemove(); } UpdateImage(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CTetrisFigura* CTetrisGame::RandomFigure(void) { //Print("Nueva figura"); CTetrisFigura* fig = new CTetrisFiguraCuadrado(); //--- const int x1 = m_x_inicial; const int x2 = m_x_inicial + m_section_sizepx; const int y1 = m_espacio.YInit(); const int y2 = y1 + m_section_sizepx; fig.Init(x1, y1, x2, y2, m_canvas, m_clr_tetris, m_color_gen.Next()); UpdateImage(); return fig; } //+------------------------------------------------------------------+