Guest User

Untitled

a guest
Nov 12th, 2016
216
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.79 KB | None | 0 0
  1. #include <SDL.h>
  2. #include <SDL_image.h>
  3. #include <stdio.h>
  4. #include <string>
  5. #include <iostream>
  6.  
  7. using namespace std;
  8.  
  9. //The dimensions of the level
  10. const int LEVEL_WIDTH = 1280;
  11. const int LEVEL_HEIGHT = 960;
  12.  
  13. //Screen dimension constants
  14. const int SCREEN_WIDTH = 1200;
  15. const int SCREEN_HEIGHT = 600;
  16.  
  17. //Button constants
  18. const int BUTTON_WIDTH = 300;
  19. const int BUTTON_HEIGHT = 100;
  20. const int TOTAL_BUTTONS = 4;
  21.  
  22. enum LButtonSprite
  23. {
  24.     BUTTON_SPRITE_MOUSE_OUT = 0,
  25.     BUTTON_SPRITE_MOUSE_OVER_MOTION = 1,
  26.     BUTTON_SPRITE_MOUSE_DOWN = 2,
  27.     BUTTON_SPRITE_MOUSE_UP = 3,
  28.     BUTTON_SPRITE_TOTAL = 4
  29. };
  30. //The mouse button
  31. class LButton
  32. {
  33. public:
  34.     //Initializes internal variables
  35.     LButton();
  36.  
  37.     //Sets position
  38.     void setPosition(int x, int y);
  39.  
  40.     void setNumber(int x);
  41.     //Handles mouse event
  42.     void handleEvent(SDL_Event* e);
  43.  
  44.     //Shows button sprite
  45.     void render();
  46.  
  47.    
  48.  
  49. private:
  50.     //Top left position
  51.     SDL_Point mPosition;
  52.  
  53.     //Currently used global sprite
  54.     LButtonSprite mCurrentSprite;
  55.  
  56.  
  57.    
  58.    
  59. };
  60.  
  61. //Texture wrapper class
  62. class LTexture
  63. {
  64. public:
  65.     //Initializes variables
  66.     LTexture();
  67.  
  68.     //Deallocates memory
  69.     ~LTexture();
  70.  
  71.     //Loads image at specified path
  72.     bool loadFromFile(std::string path);
  73.  
  74. #ifdef _SDL_TTF_H
  75.     //Creates image from font string
  76.     bool loadFromRenderedText(std::string textureText, SDL_Color textColor);
  77. #endif
  78.  
  79.     //Deallocates texture
  80.     void free();
  81.  
  82.     //Set color modulation
  83.     void setColor(Uint8 red, Uint8 green, Uint8 blue);
  84.  
  85.     //Set blending
  86.     void setBlendMode(SDL_BlendMode blending);
  87.  
  88.     //Set alpha modulation
  89.     void setAlpha(Uint8 alpha);
  90.  
  91.     //Renders texture at given point
  92.     void render(int x, int y, SDL_Rect* clip = NULL, double angle = 0.0, SDL_Point* center = NULL, SDL_RendererFlip flip = SDL_FLIP_NONE);
  93.  
  94.     //Gets image dimensions
  95.     int getWidth();
  96.     int getHeight();
  97.  
  98. private:
  99.     //The actual hardware texture
  100.     SDL_Texture* mTexture;
  101.  
  102.     //Image dimensions
  103.     int mWidth;
  104.     int mHeight;
  105. };
  106.  
  107. //The dot that will move around on the screen
  108. class Dot
  109. {
  110. public:
  111.     //The dimensions of the dot
  112.     static const int DOT_WIDTH = 20;
  113.     static const int DOT_HEIGHT = 20;
  114.  
  115.     //Maximum axis velocity of the dot
  116.     static const int DOT_VEL = 10;
  117.  
  118.     //Initializes the variables
  119.     Dot();
  120.  
  121.     //Takes key presses and adjusts the dot's velocity
  122.     void handleEvent(SDL_Event& e);
  123.  
  124.     //Moves the dot
  125.     void move();
  126.  
  127.     //Shows the dot on the screen relative to the camera
  128.     void render(int camX, int camY);
  129.  
  130.     //Position accessors
  131.     int getPosX();
  132.     int getPosY();
  133.  
  134. private:
  135.     //The X and Y offsets of the dot
  136.     int mPosX, mPosY;
  137.  
  138.     //The velocity of the dot
  139.     int mVelX, mVelY;
  140. };
  141.  
  142. bool init();
  143.  
  144. //Loads media
  145. bool loadMedia();
  146.  
  147. //Frees media and shuts down SDL
  148. void close();
  149.  
  150. //The window we'll be rendering to
  151. SDL_Window* gWindow = NULL;
  152.  
  153. //The window renderer
  154. SDL_Renderer* gRenderer = NULL;
  155.  
  156.  
  157.  
  158. //Mouse button sprites
  159. SDL_Rect gSpriteClips[BUTTON_SPRITE_TOTAL];
  160. LTexture gButtonSpriteSheetTexture;
  161.  
  162. //MOUSE BUTTON objects
  163. LButton gButtons[TOTAL_BUTTONS];
  164.  
  165. //Scene textures
  166. LTexture gDotTexture;
  167. LTexture gBGTexture;
  168.  
  169. LTexture::LTexture()
  170. {
  171.     //Initialize
  172.     mTexture = NULL;
  173.     mWidth = 0;
  174.     mHeight = 0;
  175. }
  176.  
  177. LTexture::~LTexture()
  178. {
  179.     //Deallocate
  180.     free();
  181. }
  182.  
  183. bool LTexture::loadFromFile(std::string path)
  184. {
  185.     //Get rid of preexisting texture
  186.     free();
  187.  
  188.     //The final texture
  189.     SDL_Texture* newTexture = NULL;
  190.  
  191.     //Load image at specified path
  192.     SDL_Surface* loadedSurface = IMG_Load(path.c_str());
  193.     if (loadedSurface == NULL)
  194.     {
  195.         printf("Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError());
  196.     }
  197.     else
  198.     {
  199.         //Color key image
  200.         SDL_SetColorKey(loadedSurface, SDL_TRUE, SDL_MapRGB(loadedSurface->format, 0, 0xFF, 0xFF));
  201.  
  202.         //Create texture from surface pixels
  203.         newTexture = SDL_CreateTextureFromSurface(gRenderer, loadedSurface);
  204.         if (newTexture == NULL)
  205.         {
  206.             printf("Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError());
  207.         }
  208.         else
  209.         {
  210.             //Get image dimensions
  211.             mWidth = loadedSurface->w;
  212.             mHeight = loadedSurface->h;
  213.         }
  214.  
  215.         //Get rid of old loaded surface
  216.         SDL_FreeSurface(loadedSurface);
  217.     }
  218.  
  219.     //Return success
  220.     mTexture = newTexture;
  221.     return mTexture != NULL;
  222. }
  223.  
  224. //we dont use this
  225. #ifdef _SDL_TTF_H
  226. bool LTexture::loadFromRenderedText(std::string textureText, SDL_Color textColor)
  227. {
  228.     //Get rid of preexisting texture
  229.     free();
  230.  
  231.     //Render text surface
  232.     SDL_Surface* textSurface = TTF_RenderText_Solid(gFont, textureText.c_str(), textColor);
  233.     if (textSurface != NULL)
  234.     {
  235.         //Create texture from surface pixels
  236.         mTexture = SDL_CreateTextureFromSurface(gRenderer, textSurface);
  237.         if (mTexture == NULL)
  238.         {
  239.             printf("Unable to create texture from rendered text! SDL Error: %s\n", SDL_GetError());
  240.         }
  241.         else
  242.         {
  243.             //Get image dimensions
  244.             mWidth = textSurface->w;
  245.             mHeight = textSurface->h;
  246.         }
  247.  
  248.         //Get rid of old surface
  249.         SDL_FreeSurface(textSurface);
  250.     }
  251.     else
  252.     {
  253.         printf("Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError());
  254.     }
  255.  
  256.  
  257.     //Return success
  258.     return mTexture != NULL;
  259. }
  260. #endif
  261.  
  262. void LTexture::free()
  263. {
  264.     //Free texture if it exists
  265.     if (mTexture != NULL)
  266.     {
  267.         SDL_DestroyTexture(mTexture);
  268.         mTexture = NULL;
  269.         mWidth = 0;
  270.         mHeight = 0;
  271.     }
  272. }
  273.  
  274. void LTexture::setColor(Uint8 red, Uint8 green, Uint8 blue)
  275. {
  276.     //Modulate texture rgb
  277.     SDL_SetTextureColorMod(mTexture, red, green, blue);
  278. }
  279.  
  280. void LTexture::setBlendMode(SDL_BlendMode blending)
  281. {
  282.     //Set blending function
  283.     SDL_SetTextureBlendMode(mTexture, blending);
  284. }
  285.  
  286. void LTexture::setAlpha(Uint8 alpha)
  287. {
  288.     //Modulate texture alpha
  289.     SDL_SetTextureAlphaMod(mTexture, alpha);
  290. }
  291.  
  292. void LTexture::render(int x, int y, SDL_Rect* clip, double angle, SDL_Point* center, SDL_RendererFlip flip)
  293. {
  294.     //Set rendering space and render to screen
  295.     SDL_Rect renderQuad = { x, y, mWidth, mHeight };
  296.  
  297.     //Set clip rendering dimensions
  298.     if (clip != NULL)
  299.     {
  300.         renderQuad.w = clip->w;
  301.         renderQuad.h = clip->h;
  302.     }
  303.  
  304.     //Render to screen
  305.     SDL_RenderCopyEx(gRenderer, mTexture, clip, &renderQuad, angle, center, flip);
  306. }
  307.  
  308. int LTexture::getWidth()
  309. {
  310.     return mWidth;
  311. }
  312.  
  313. int LTexture::getHeight()
  314. {
  315.     return mHeight;
  316. }
  317.  
  318. Dot::Dot()
  319. {
  320.     //Initialize the offsets
  321.     mPosY = SCREEN_HEIGHT/2;
  322.     mPosX = SCREEN_WIDTH/2;
  323.  
  324.     //Initialize the velocity
  325.     mVelX = 0;
  326.     mVelY = 0;
  327.  
  328. }
  329.  
  330.  
  331. void Dot::handleEvent(SDL_Event& e)
  332. {
  333.     //If a key was pressed
  334.     if (e.type == SDL_KEYDOWN && e.key.repeat == 0)
  335.     {
  336.         //Adjust the velocity
  337.         switch (e.key.keysym.sym)
  338.         {
  339.         case SDLK_UP: mVelY -= DOT_VEL; break;
  340.         case SDLK_DOWN: mVelY += DOT_VEL; break;
  341.         case SDLK_LEFT: mVelX -= DOT_VEL; break;
  342.         case SDLK_RIGHT: mVelX += DOT_VEL; break;
  343.         }
  344.     }
  345.     //If a key was released
  346.     else if (e.type == SDL_KEYUP && e.key.repeat == 0)
  347.     {
  348.         //Adjust the velocity
  349.         switch (e.key.keysym.sym)
  350.         {
  351.         case SDLK_UP: mVelY += DOT_VEL; break;
  352.         case SDLK_DOWN: mVelY -= DOT_VEL; break;
  353.         case SDLK_LEFT: mVelX += DOT_VEL; break;
  354.         case SDLK_RIGHT: mVelX -= DOT_VEL; break;
  355.         }
  356.     }
  357.  
  358.    
  359. }
  360.  
  361. void Dot::move()
  362. {
  363.     //Move the dot left or right
  364.     mPosX += mVelX;
  365.  
  366.     //If the dot went too far to the left or right
  367.     if ((mPosX < 0) || (mPosX + DOT_WIDTH > LEVEL_WIDTH))
  368.     {
  369.         //Move back
  370.         mPosX -= mVelX;
  371.     }
  372.  
  373.     //Move the dot up or down
  374.     mPosY += mVelY;
  375.  
  376.     //If the dot went too far up or down
  377.     if ((mPosY < 0) || (mPosY + DOT_HEIGHT > LEVEL_HEIGHT))
  378.     {
  379.         //Move back
  380.         mPosY -= mVelY;
  381.     }
  382. }
  383.  
  384. void Dot::render(int camX, int camY)
  385. {
  386.     //Show the dot relative to the camera
  387.     gDotTexture.render(mPosX - camX, mPosY - camY);
  388. }
  389.  
  390. int Dot::getPosX()
  391. {
  392.     return mPosX;
  393. }
  394.  
  395. int Dot::getPosY()
  396. {
  397.     return mPosY;
  398. }
  399.  
  400. LButton::LButton()
  401. {
  402.     mPosition.x = 0;
  403.     mPosition.y = SCREEN_HEIGHT - BUTTON_HEIGHT;
  404.     mCurrentSprite = BUTTON_SPRITE_MOUSE_OUT;
  405.  
  406. }
  407.  
  408. void LButton::setPosition(int x, int y)
  409. {
  410.     mPosition.x = x;
  411.     mPosition.y = y;
  412.  
  413. }
  414.  
  415. void LButton::handleEvent(SDL_Event* e)
  416. {
  417.     //If mouse event happened
  418.     if (e->type == SDL_MOUSEMOTION || e->type == SDL_MOUSEBUTTONDOWN || e->type == SDL_MOUSEBUTTONUP)
  419.     {
  420.         //Get mouse position
  421.         int x, y;
  422.         int bNumber;
  423.         SDL_GetMouseState(&x, &y);
  424.        
  425.         //Check if mouse is in button
  426.         bool inside = true;
  427.        
  428.        
  429.  
  430.         //Mouse is left of the button
  431.         if (x < mPosition.x)
  432.         {
  433.             inside = false;
  434.         }
  435.         //Mouse is right of the button
  436.         else if (x > mPosition.x + BUTTON_WIDTH)
  437.         {
  438.             inside = false;
  439.         }
  440.         //Mouse above the button
  441.         else if (y < mPosition.y)
  442.         {
  443.             inside = false;
  444.         }
  445.         //Mouse below the button
  446.         else if (y > mPosition.y + BUTTON_HEIGHT)
  447.         {
  448.             inside = false;
  449.         }
  450.  
  451.         //Mouse is outside button
  452.         if (!inside)
  453.         {
  454.             mCurrentSprite = BUTTON_SPRITE_MOUSE_OUT;
  455.            
  456.         }
  457.         //Mouse is inside button
  458.         else
  459.         {
  460.             //Set mouse over sprite
  461.             switch (e->type)
  462.             {
  463.             case SDL_MOUSEMOTION:
  464.                 mCurrentSprite = BUTTON_SPRITE_MOUSE_OVER_MOTION;
  465.                 break;
  466.  
  467.             case SDL_MOUSEBUTTONDOWN:
  468.                 mCurrentSprite = BUTTON_SPRITE_MOUSE_DOWN;
  469.                 //call function
  470.                 //what button did we push
  471.                     if (x < BUTTON_WIDTH) {
  472.                         cout << "first button!!" << endl;
  473.                        
  474.  
  475.                     }
  476.                     else if (x < (BUTTON_WIDTH * 2)) {
  477.  
  478.                         cout << "second button!!" << endl;
  479.                     }
  480.                     else if (x < (BUTTON_WIDTH * 3)) {
  481.  
  482.                         cout << "third button!!" << endl;
  483.                     }
  484.                     else if (x < (BUTTON_WIDTH * 4)) {
  485.  
  486.                         cout << "fourth button!!" << endl;
  487.                     }
  488.                
  489.                
  490.             case SDL_MOUSEBUTTONUP:
  491.                 mCurrentSprite = BUTTON_SPRITE_MOUSE_UP;
  492.                 break;
  493.             }
  494.        
  495.        
  496.         }
  497.        
  498.     }
  499. }
  500. void LButton::setNumber(int x){
  501.     for (int i = 0; i < TOTAL_BUTTONS; ++i) {
  502.  
  503.     }
  504. }
  505.  
  506. void LButton::render()
  507. {
  508.     //Show current button sprite
  509.     gButtonSpriteSheetTexture.render(mPosition.x, mPosition.y, &gSpriteClips[mCurrentSprite]);
  510. }
  511.  
  512. bool init()
  513. {
  514.     //Initialization flag
  515.     bool success = true;
  516.  
  517.     //Initialize SDL
  518.     if (SDL_Init(SDL_INIT_VIDEO) < 0)
  519.     {
  520.         printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
  521.         success = false;
  522.     }
  523.     else
  524.     {
  525.         //Set texture filtering to linear
  526.         if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"))
  527.         {
  528.             printf("Warning: Linear texture filtering not enabled!");
  529.         }
  530.  
  531.         //Create window
  532.         gWindow = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
  533.         if (gWindow == NULL)
  534.         {
  535.             printf("Window could not be created! SDL Error: %s\n", SDL_GetError());
  536.             success = false;
  537.         }
  538.         else
  539.         {
  540.             //Create vsynced renderer for window
  541.             gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
  542.             if (gRenderer == NULL)
  543.             {
  544.                 printf("Renderer could not be created! SDL Error: %s\n", SDL_GetError());
  545.                 success = false;
  546.             }
  547.             else
  548.             {
  549.                 //Initialize renderer color
  550.                 SDL_SetRenderDrawColor(gRenderer, 0xFF, 0xFF, 0xFF, 0xFF);
  551.  
  552.                 //Initialize PNG loading
  553.                 int imgFlags = IMG_INIT_PNG;
  554.                 if (!(IMG_Init(imgFlags) & imgFlags))
  555.                 {
  556.                     printf("SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError());
  557.                     success = false;
  558.                 }
  559.             }
  560.         }
  561.     }
  562.  
  563.     return success;
  564. }
  565.  
  566. bool loadMedia()
  567. {
  568.     //Loading success flag
  569.     bool success = true;
  570.  
  571.     //Load dot texture
  572.     if (!gDotTexture.loadFromFile("30_scrolling/dot.bmp"))
  573.     {
  574.         printf("Failed to load dot texture!\n");
  575.         success = false;
  576.     }
  577.  
  578.     //Load background texture
  579.     if (!gBGTexture.loadFromFile("30_scrolling/bg.png"))
  580.     {
  581.         printf("Failed to load background texture!\n");
  582.         success = false;
  583.     }
  584.  
  585.     if (!gButtonSpriteSheetTexture.loadFromFile("30_scrolling/button.png"))
  586.     {
  587.         printf("Failed to load button button sprite texture!\n");
  588.         success = false;
  589.     }
  590.     else
  591.     {
  592.         //Set sprites
  593.         for (int i = 0; i < BUTTON_SPRITE_TOTAL; ++i)
  594.         {
  595.             gSpriteClips[i].x = 0;
  596.             gSpriteClips[i].y = i * 200;
  597.             gSpriteClips[i].w = BUTTON_WIDTH;
  598.             gSpriteClips[i].h = BUTTON_HEIGHT;
  599.         }
  600.  
  601.         //Set buttons
  602.        
  603.         for (int i = 0; i < BUTTON_SPRITE_TOTAL; ++i)
  604.         {
  605.             gButtons[i].setPosition(0, SCREEN_HEIGHT - BUTTON_HEIGHT);
  606.             gButtons[i].setPosition(i * BUTTON_WIDTH, SCREEN_HEIGHT - BUTTON_HEIGHT);
  607.             gButtons[i].setPosition(i * BUTTON_WIDTH, SCREEN_HEIGHT - BUTTON_HEIGHT);
  608.             gButtons[i].setPosition(i * BUTTON_WIDTH, SCREEN_HEIGHT - BUTTON_HEIGHT);
  609.         }
  610.        
  611.     }
  612.  
  613.     return success;
  614. }
  615.  
  616. void close()
  617. {
  618.     //Free loaded images
  619.     gDotTexture.free();
  620.     gBGTexture.free();
  621.     gButtonSpriteSheetTexture.free();
  622.  
  623.     //Destroy window   
  624.     SDL_DestroyRenderer(gRenderer);
  625.     SDL_DestroyWindow(gWindow);
  626.     gWindow = NULL;
  627.     gRenderer = NULL;
  628.  
  629.     //Quit SDL subsystems
  630.     IMG_Quit();
  631.     SDL_Quit();
  632. }
  633.  
  634. int main(int argc, char* args[])
  635. {
  636.     //Start up SDL and create window
  637.     if (!init())
  638.     {
  639.         printf("Failed to initialize!\n");
  640.     }
  641.     else
  642.     {
  643.         //Load media
  644.         if (!loadMedia())
  645.         {
  646.             printf("Failed to load media!\n");
  647.         }
  648.         else
  649.         {
  650.             //Main loop flag
  651.             bool quit = false;
  652.  
  653.             //Event handler
  654.             SDL_Event e;
  655.  
  656.             //The dot that will be moving around on the screen
  657.             Dot dot;
  658.            
  659.             //The camera area
  660.             SDL_Rect camera = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT };
  661.  
  662.             //While application is running
  663.             while (!quit)
  664.             {
  665.                 //Handle events on queue
  666.                 while (SDL_PollEvent(&e) != 0)
  667.                 {
  668.                     //User requests quit
  669.                     if (e.type == SDL_QUIT)
  670.                     {
  671.                         quit = true;
  672.                     }
  673.                    
  674.                     //Handle button events
  675.                     for (int i = 0; i < TOTAL_BUTTONS; ++i)
  676.                     {
  677.                         gButtons[i].handleEvent(&e);
  678.                        
  679.                     }
  680.                     if (e.type == SDL_MOUSEBUTTONDOWN) {
  681.                         cout << "this is inside the main function" << endl;
  682.                         SDL_Rect fillRect = { SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 };
  683.                         SDL_SetRenderDrawColor(gRenderer, 0xFF, 0x00, 0x00, 0xFF);
  684.                         SDL_RenderFillRect(gRenderer, &fillRect);
  685.                         //Update screen
  686.                         SDL_RenderPresent(gRenderer);
  687.                     }
  688.                     //Handle input for the dot
  689.                     dot.handleEvent(e);
  690.                 }
  691.  
  692.                 //Move the dot
  693.                 dot.move();
  694.                 //Center the camera over the dot
  695.                 camera.x = (dot.getPosX() + Dot::DOT_WIDTH / 2) - SCREEN_WIDTH / 2;
  696.                 camera.y = (dot.getPosY() + Dot::DOT_HEIGHT / 2) - SCREEN_HEIGHT / 2;
  697.  
  698.                 //Keep the camera in bounds
  699.                 if (camera.x < 0)
  700.                 {
  701.                     camera.x = 0;
  702.                 }
  703.                 if (camera.y < 0)
  704.                 {
  705.                     camera.y = 0;
  706.                 }
  707.                 if (camera.x > LEVEL_WIDTH - camera.w)
  708.                 {
  709.                     camera.x = LEVEL_WIDTH - camera.w;
  710.                 }
  711.                 if (camera.y > LEVEL_HEIGHT - camera.h)
  712.                 {
  713.                     camera.y = LEVEL_HEIGHT - camera.h;
  714.                 }
  715.  
  716.                 //Clear screen
  717.                 SDL_SetRenderDrawColor(gRenderer, 0xFF, 0xFF, 0xFF, 0xFF);
  718.                 SDL_RenderClear(gRenderer);
  719.  
  720.                 //Render background
  721.                 gBGTexture.render(0, 0, &camera);
  722.  
  723.                 //Render objects
  724.                 dot.render(camera.x, camera.y);
  725.                
  726.                 //Render buttons
  727.                 for (int i = 0; i < TOTAL_BUTTONS; ++i)
  728.                 {
  729.                     gButtons[i].render();
  730.  
  731.                 }
  732.                
  733.                 //Update screen
  734.                 SDL_RenderPresent(gRenderer);
  735.             }
  736.         }
  737.     }
  738.  
  739.     //Free resources and close SDL
  740.     close();
  741.  
  742.     return 0;
  743. }
Advertisement
Add Comment
Please, Sign In to add comment