├── .gitattributes ├── res ├── sounds │ ├── fall.wav │ ├── hit.wav │ ├── jump.wav │ └── click.wav ├── textures │ ├── arrow.png │ ├── logo.png │ ├── ground │ │ ├── hole.png │ │ ├── left.png │ │ ├── right.png │ │ └── center.png │ ├── death_overlay.png │ ├── highscore_box.png │ └── player │ │ ├── player_0.png │ │ ├── player_1.png │ │ ├── player_2.png │ │ ├── player_3.png │ │ └── player_4.png └── fonts │ └── cocogoose.ttf ├── include ├── groundtile.h ├── ground.h ├── player.h ├── renderwindow.h └── entity.h ├── src ├── groundtile.cpp ├── entity.cpp ├── ground.cpp ├── renderwindow.cpp ├── player.cpp └── main.cpp ├── Makefile └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /res/sounds/fall.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/sounds/fall.wav -------------------------------------------------------------------------------- /res/sounds/hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/sounds/hit.wav -------------------------------------------------------------------------------- /res/sounds/jump.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/sounds/jump.wav -------------------------------------------------------------------------------- /res/sounds/click.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/sounds/click.wav -------------------------------------------------------------------------------- /res/textures/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/textures/arrow.png -------------------------------------------------------------------------------- /res/textures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/textures/logo.png -------------------------------------------------------------------------------- /res/fonts/cocogoose.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/fonts/cocogoose.ttf -------------------------------------------------------------------------------- /res/textures/ground/hole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/textures/ground/hole.png -------------------------------------------------------------------------------- /res/textures/ground/left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/textures/ground/left.png -------------------------------------------------------------------------------- /res/textures/ground/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/textures/ground/right.png -------------------------------------------------------------------------------- /res/textures/death_overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/textures/death_overlay.png -------------------------------------------------------------------------------- /res/textures/ground/center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/textures/ground/center.png -------------------------------------------------------------------------------- /res/textures/highscore_box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/textures/highscore_box.png -------------------------------------------------------------------------------- /res/textures/player/player_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/textures/player/player_0.png -------------------------------------------------------------------------------- /res/textures/player/player_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/textures/player/player_1.png -------------------------------------------------------------------------------- /res/textures/player/player_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/textures/player/player_2.png -------------------------------------------------------------------------------- /res/textures/player/player_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/textures/player/player_3.png -------------------------------------------------------------------------------- /res/textures/player/player_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PolyMarsDev/Cursor-Custodian/HEAD/res/textures/player/player_4.png -------------------------------------------------------------------------------- /include/groundtile.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include "entity.h" 7 | 8 | class GroundTile : public Entity 9 | { 10 | public: 11 | GroundTile(SDL_Texture* p_tex, int p_index); 12 | int getStatus(); 13 | void setStatus(int p_status, SDL_Texture* groundTex[4]); 14 | private: 15 | int status; 16 | }; -------------------------------------------------------------------------------- /src/groundtile.cpp: -------------------------------------------------------------------------------- 1 | #include "groundtile.h" 2 | 3 | const int SCREEN_WIDTH = 800; 4 | const int SCREEN_HEIGHT = 480; 5 | 6 | GroundTile::GroundTile(SDL_Texture* p_tex, int p_index) 7 | : Entity{p_index * 64.0f, SCREEN_HEIGHT - 64.0f, p_tex} 8 | { 9 | status = 1; 10 | } 11 | 12 | int GroundTile::getStatus() 13 | { 14 | return status; 15 | } 16 | 17 | void GroundTile::setStatus(int p_status, SDL_Texture* groundTex[4]) 18 | { 19 | status = p_status; 20 | setTex(groundTex[status]); 21 | } -------------------------------------------------------------------------------- /include/ground.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include "groundtile.h" 7 | class Ground { 8 | public: 9 | Ground(SDL_Texture* left, SDL_Texture* center, SDL_Texture* right, SDL_Texture* hole); 10 | GroundTile& getTile(int p_index); 11 | int getStatus(int p_index); 12 | int getLength(); 13 | bool isTileBelow(float x, int width); 14 | void update(int score); 15 | void reset(); 16 | private: 17 | std::vector groundTiles; 18 | SDL_Texture* groundTex[4]; 19 | int lastStatus = 1; 20 | int holeCount = 0; 21 | }; -------------------------------------------------------------------------------- /include/player.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "entity.h" 8 | #include "ground.h" 9 | 10 | 11 | class Player : public Entity { 12 | public: 13 | Player(float p_x, float p_y, std::vector p_tex); 14 | float distanceFromCursor(); 15 | bool jump(); 16 | void update(Ground& ground); 17 | const char* getScore(); 18 | const char* getHighscore(); 19 | int getScoreInt(); 20 | int isDead(); 21 | void reset(); 22 | private: 23 | float velocityX, velocityY; 24 | bool grounded; 25 | void animEyes(); 26 | float clamp(float p_value, float p_min, float p_max); 27 | int score = 0; 28 | int highscore = 0; 29 | int timer = 0; 30 | int dead = 0; 31 | }; -------------------------------------------------------------------------------- /include/renderwindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include "entity.h" 7 | 8 | class RenderWindow 9 | { 10 | private: 11 | SDL_Window* window; 12 | SDL_Renderer* renderer; 13 | public: 14 | RenderWindow(); 15 | void create(const char* p_title, int p_w, int p_h); 16 | SDL_Texture* loadTexture(const char* p_filePath); 17 | void clear(); 18 | void render(Entity& p_entity); 19 | void render(float p_x, float p_y, SDL_Texture* p_tex); 20 | void render(SDL_Texture* p_tex); 21 | void render(float p_x, float p_y, const char* p_text, TTF_Font* font, SDL_Color textColor); 22 | void renderCenter(float p_x, float p_y, const char* p_text, TTF_Font* font, SDL_Color textColor); 23 | void display(); 24 | void cleanUp(); 25 | }; -------------------------------------------------------------------------------- /include/entity.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | class Entity 7 | { 8 | private: 9 | float x, y; 10 | std::vector animOffsetsX; 11 | std::vector animOffsetsY; 12 | SDL_Rect currentFrame; 13 | std::vector tex; 14 | public: 15 | Entity(float p_x, float p_y, std::vector p_tex); 16 | Entity(float p_x, float p_y, SDL_Texture* p_tex); 17 | void init(); 18 | float getX(); 19 | float getY(); 20 | int getWidth(); 21 | int getHeight(); 22 | int getSize(); 23 | void setX(float p_x); 24 | void setY(float p_y); 25 | float getAnimOffsetX(int p_index); 26 | float getAnimOffsetY(int p_index); 27 | void setAnimOffsetX(int p_index, int p_value); 28 | void setAnimOffsetY(int p_index, int p_value); 29 | SDL_Texture* getTex(int p_index); 30 | SDL_Rect getCurrentFrame(); 31 | void setTex(SDL_Texture* p_tex); 32 | }; -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # set compiler 2 | CC = g++ 3 | # include files 4 | INCLUDE = -I ./include 5 | #compilers flags for compiling object files 6 | CFLAGSO = -std=c++14 -Wall -m64 -O3 -c ${INCLUDE} 7 | # libraries 8 | LIBS = -lSDL2main -lSDL2 -lSDL2_image -lSDL2_ttf -lSDL2_mixer 9 | #compilers flags for compiling binary file 10 | CFLAGSB = -s ${LIBS} 11 | 12 | default: objCompile 13 | mkdir -p ./make/bin 14 | ${CC} ./make/build/*.o -o ./make/bin/main ${CFLAGSB} 15 | cp -r ./res ./make/bin/res 16 | 17 | objCompile: 18 | mkdir -p ./make/build 19 | ${CC} ./src/*.cpp ${CFLAGSO} 20 | # laymans way to move object files to make/build folder 21 | mv *.o ./make/build 22 | windows: winObjCompile 23 | mkdir -p ./make/bin 24 | ${CC} ./make/build/*.o -o ./make/bin/main ${CFLAGSB} -mwindows 25 | cp -r ./res ./make/bin/res 26 | 27 | winObjCompile: 28 | mkdir -p ./make/build 29 | ${CC} ./src/*.cpp ${CFLAGSO} -mwindows 30 | # laymans way to move object files to make/build folder 31 | mv *.o ./make/build -------------------------------------------------------------------------------- /src/entity.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "entity.h" 6 | 7 | Entity::Entity(float p_x, float p_y, std::vector p_tex) 8 | :x(p_x), y(p_y), tex(p_tex) 9 | { 10 | currentFrame.x = 0; 11 | currentFrame.y = 0; 12 | SDL_QueryTexture(p_tex.at(0), NULL, NULL, ¤tFrame.w, ¤tFrame.h); 13 | for (int i = 0; i < getSize(); i++) 14 | { 15 | animOffsetsX.push_back(0); 16 | animOffsetsY.push_back(0); 17 | } 18 | } 19 | 20 | Entity::Entity(float p_x, float p_y, SDL_Texture* p_tex) 21 | :x(p_x), y(p_y) 22 | { 23 | tex.push_back(p_tex); 24 | currentFrame.x = 0; 25 | currentFrame.y = 0; 26 | SDL_QueryTexture(p_tex, NULL, NULL, ¤tFrame.w, ¤tFrame.h); 27 | for (int i = 0; i < getSize(); i++) 28 | { 29 | animOffsetsX.push_back(0); 30 | animOffsetsY.push_back(0); 31 | } 32 | } 33 | 34 | float Entity::getX() 35 | { 36 | return x; 37 | } 38 | 39 | float Entity::getY() 40 | { 41 | return y; 42 | } 43 | 44 | int Entity::getWidth() 45 | { 46 | return currentFrame.w; 47 | } 48 | 49 | int Entity::getHeight() 50 | { 51 | return currentFrame.h; 52 | } 53 | 54 | int Entity::getSize() 55 | { 56 | return tex.size(); 57 | } 58 | 59 | void Entity::setX(float p_x) 60 | { 61 | x = p_x; 62 | } 63 | 64 | void Entity::setY(float p_y) 65 | { 66 | y = p_y; 67 | } 68 | 69 | float Entity::getAnimOffsetX(int p_index) 70 | { 71 | return animOffsetsX.at(p_index); 72 | } 73 | 74 | float Entity::getAnimOffsetY(int p_index) 75 | { 76 | return animOffsetsY.at(p_index); 77 | } 78 | 79 | void Entity::setAnimOffsetX(int p_index, int p_value) 80 | { 81 | animOffsetsX[p_index] = p_value; 82 | } 83 | 84 | void Entity::setAnimOffsetY(int p_index, int p_value) 85 | { 86 | animOffsetsY[p_index] = p_value; 87 | } 88 | 89 | SDL_Texture* Entity::getTex(int p_index) 90 | { 91 | return tex.at(p_index); 92 | } 93 | 94 | SDL_Rect Entity::getCurrentFrame() 95 | { 96 | return currentFrame; 97 | } 98 | 99 | void Entity::setTex(SDL_Texture* p_tex) 100 | { 101 | tex[0] = p_tex; 102 | } -------------------------------------------------------------------------------- /src/ground.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "ground.h" 8 | #include "groundtile.h" 9 | 10 | Ground::Ground(SDL_Texture* left, SDL_Texture* center, SDL_Texture* right, SDL_Texture* hole) 11 | { 12 | groundTex[0] = left; 13 | groundTex[1] = center; 14 | groundTex[2] = right; 15 | groundTex[3] = hole; 16 | 17 | for (int i = 0; i < 14; i++) 18 | { 19 | groundTiles.push_back(GroundTile(groundTex[1], i)); 20 | } 21 | } 22 | 23 | GroundTile& Ground::getTile(int p_index) 24 | { 25 | return groundTiles.at(p_index); 26 | } 27 | 28 | int Ground::getStatus(int p_index) 29 | { 30 | return groundTiles[p_index].getStatus(); 31 | } 32 | 33 | int Ground::getLength() 34 | { 35 | return groundTiles.size(); 36 | } 37 | 38 | bool Ground::isTileBelow(float x, int width) 39 | { 40 | for (int i = 0; i < getLength(); i++) 41 | { 42 | switch (getStatus(i)) { 43 | case 0: 44 | if (x + width > groundTiles[i].getX() + 24 && x < groundTiles[i].getX() + 64) 45 | { 46 | return true; 47 | } 48 | break; 49 | case 1: 50 | if (x + width > groundTiles[i].getX() && x < groundTiles[i].getX() + 64) 51 | { 52 | return true; 53 | } 54 | break; 55 | case 2: 56 | if (x + width > groundTiles[i].getX() && x < groundTiles[i].getX() + 40) 57 | { 58 | return true; 59 | } 60 | break; 61 | } 62 | } 63 | return false; 64 | } 65 | 66 | void Ground::reset() 67 | { 68 | for (int i = 0; i < 14; i++) 69 | { 70 | groundTiles[i].setStatus(1, groundTex); 71 | groundTiles[i].setX(i * 64.0f); 72 | } 73 | lastStatus = 1; 74 | holeCount = 0; 75 | } 76 | 77 | void Ground::update(int score) 78 | { 79 | for (int i = 0; i < getLength(); i++) 80 | { 81 | groundTiles[i].setX(groundTiles[i].getX() - 1); 82 | if (groundTiles[i].getX() + 64 < 0) 83 | { 84 | groundTiles[i].setX(64 * (getLength() - 1) - 1); 85 | switch (lastStatus) { 86 | case 0: 87 | { 88 | groundTiles[i].setStatus(1, groundTex); 89 | lastStatus = 1; 90 | holeCount = 0; 91 | break; 92 | } 93 | case 1: 94 | { 95 | int randomInt = rand()%2 + 1; 96 | groundTiles[i].setStatus(randomInt, groundTex); 97 | lastStatus = randomInt; 98 | holeCount = 0; 99 | break; 100 | } 101 | case 2: 102 | { 103 | groundTiles[i].setStatus(3, groundTex); 104 | lastStatus = 3; 105 | holeCount = 0; 106 | break; 107 | } 108 | case 3: 109 | { 110 | int randomInt = rand()%2; 111 | if (randomInt == 1) 112 | { 113 | randomInt = 3; 114 | holeCount++; 115 | } 116 | else { 117 | holeCount = 0; 118 | } 119 | if ((holeCount > 4 && score > 99) || (holeCount > 3 && score < 100)) 120 | { 121 | randomInt = 0; 122 | holeCount = 0; 123 | } 124 | groundTiles[i].setStatus(randomInt, groundTex); 125 | lastStatus = randomInt; 126 | break; 127 | } 128 | } 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cursor Custodian 2 | 3 | Cursor Custodian is a game created in 48 hours for the [2020 GMTK Game Jam](https://itch.io/jam/gmtk-2020) using C++ and [SDL2](https://www.libsdl.org/). It can be played in your web browser [here](https://polymars.itch.io/cursor-custodian). 4 | ## Screenshots 5 | ![](https://img.itch.zone/aW1hZ2UvNzAwMzMzLzM4NzgxNjkuZ2lm/347x500/fqtnfO.gif) 6 | 7 | ![](https://img.itch.zone/aW1hZ2UvNzAwMzMzLzM4NjIyOTgucG5n/347x500/9sZ24Q.png) ![](https://img.itch.zone/aW1hZ2UvNzAwMzMzLzM4NjIyOTkucG5n/347x500/qe2Eg7.png) ![](https://img.itch.zone/aW1hZ2UvNzAwMzMzLzM4NjIzMjAucG5n/347x500/yZPiM2.png) ![](https://img.itch.zone/aW1hZ2UvNzAwMzMzLzM4NjIzMjIucG5n/347x500/c6hUSn.png) 8 | 9 | 10 | ## Background 11 | Cursor Custodian is a simple endless platformer where you use your cursor to guide the player away from obstacles. The player moves away from your cursor and will jump if you click the left mouse button near him. More information on how to play is available on the game's [itch.io page](https://polymars.itch.io/cursor-custodian). 12 | 13 | ## Compiling 14 | ### Windows 15 | After installing [Mingw64](https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/8.1.0/threads-win32/seh/x86_64-8.1.0-release-win32-seh-rt_v6-rev0.7z/download), [SDL2](https://www.libsdl.org/download-2.0.php), [SDL_Image](https://www.libsdl.org/projects/SDL_image/), [SDL_TTF](https://www.libsdl.org/projects/SDL_ttf/), and [SDL_Mixer](https://www.libsdl.org/projects/SDL_mixer/), execute the following command in the project's root directory: 16 | ``` 17 | g++ -c src/*.cpp -std=c++14 -O3 -Wall -m64 -I include -I C:/SDL2-w64/include && g++ *.o -o bin/release/main -s -L C:/SDL2-w64/lib -lmingw32 -lSDL2main -lSDL2 -lSDL2_image -lSDL2_ttf -lSDL2_mixer && start bin/release/main 18 | ``` 19 | The compiled ``.exe`` is located in ``./bin``. For it to run, you must copy the ``./res`` folder as well as all ``.dll`` files from your SDL installation to its directory. 20 | ### Linux 21 | After installing the dev packages of SDL2 for your distribution, execute the following command in the project's root directory: 22 | ``` 23 | g++ -c src/*.cpp -std=c++14 -O3 -Wall -m64 -I include && mkdir -p bin/release && g++ *.o -o bin/release/main -s -lSDL2main -lSDL2 -lSDL2_image -lSDL2_ttf -lSDL2_mixer 24 | ``` 25 | The compiled binary ``main`` is located in ``./bin``. For it to run, you must copy the ``./res`` folder to its directory. 26 | ### Web 27 | Install [emscripten](https://emscripten.org/docs/getting_started/downloads.html) and execute the following command in the project's root directory: 28 | ``` 29 | emcc src/main.cpp src/entity.cpp src/renderwindow.cpp src/player.cpp src/ground.cpp src/groundtile.cpp -I include -O2 -s USE_SDL=2 -s USE_SDL_IMAGE=2 -s \"SDL2_IMAGE_FORMATS=['png']\" -s USE_SDL_TTF=2 -s USE_SDL_MIXER=2 --preload-file res -o index.html 30 | ``` 31 | The compiled ``.js``, ``.wasm``, ``.data``, and ``.html`` files are located in the project's root. 32 | 33 | 34 | ## Contributing 35 | Pull requests are welcome! For major refactors, please open an issue first to discuss what you would like to improve. Feel free to create a fork of this repository or use the code for any other noncommercial purposes. 36 | -------------------------------------------------------------------------------- /src/renderwindow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "renderwindow.h" 7 | #include "entity.h" 8 | 9 | const int SCREEN_WIDTH = 800; 10 | const int SCREEN_HEIGHT = 480; 11 | 12 | RenderWindow::RenderWindow() 13 | { 14 | 15 | } 16 | 17 | void RenderWindow::create(const char* p_title, int p_w, int p_h) 18 | { 19 | window = SDL_CreateWindow(p_title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, p_w, p_h, SDL_WINDOW_SHOWN); 20 | 21 | if (window == NULL) 22 | std::cout << "Window failed to init. Error: " << SDL_GetError() << std::endl; 23 | 24 | renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); 25 | } 26 | 27 | 28 | SDL_Texture* RenderWindow::loadTexture(const char* p_filePath) 29 | { 30 | SDL_Texture* texture = NULL; 31 | texture = IMG_LoadTexture(renderer, p_filePath); 32 | 33 | if (texture == NULL) 34 | std::cout << "Failed to load texture. Error: " << SDL_GetError() << std::endl; 35 | 36 | return texture; 37 | } 38 | 39 | void RenderWindow::clear() 40 | { 41 | SDL_SetRenderDrawColor(renderer, 90, 90, 90, 255); 42 | SDL_RenderClear(renderer); 43 | } 44 | 45 | void RenderWindow::render(Entity& p_entity) 46 | { 47 | 48 | for (int i = p_entity.getSize() - 1; i >= 0; i--) 49 | { 50 | SDL_Rect src; 51 | src.x = p_entity.getCurrentFrame().x; 52 | src.y = p_entity.getCurrentFrame().y; 53 | src.w = p_entity.getCurrentFrame().w; 54 | src.h = p_entity.getCurrentFrame().h; 55 | 56 | SDL_Rect dst; 57 | dst.x = p_entity.getX() + p_entity.getAnimOffsetX(i); 58 | dst.y = p_entity.getY() + p_entity.getAnimOffsetY(i); 59 | dst.w = p_entity.getCurrentFrame().w; 60 | dst.h = p_entity.getCurrentFrame().h; 61 | 62 | SDL_RenderCopy(renderer, p_entity.getTex(i), &src, &dst); 63 | } 64 | } 65 | 66 | void RenderWindow::render(float p_x, float p_y, SDL_Texture* p_tex) 67 | { 68 | SDL_Rect src; 69 | src.x = 0; 70 | src.y = 0; 71 | SDL_QueryTexture(p_tex, NULL, NULL, &src.w, &src.h); 72 | 73 | SDL_Rect dst; 74 | dst.x = p_x; 75 | dst.y = p_y; 76 | dst.w = src.w; 77 | dst.h = src.h; 78 | 79 | SDL_RenderCopy(renderer, p_tex, &src, &dst); 80 | } 81 | 82 | void RenderWindow::render(SDL_Texture* p_tex) 83 | { 84 | render(0, 0, p_tex); 85 | } 86 | 87 | void RenderWindow::render(float p_x, float p_y, const char* p_text, TTF_Font* font, SDL_Color textColor) 88 | { 89 | SDL_Surface* surfaceMessage = TTF_RenderText_Blended( font, p_text, textColor); 90 | SDL_Texture* message = SDL_CreateTextureFromSurface(renderer, surfaceMessage); 91 | 92 | SDL_Rect src; 93 | src.x = 0; 94 | src.y = 0; 95 | src.w = surfaceMessage->w; 96 | src.h = surfaceMessage->h; 97 | 98 | SDL_Rect dst; 99 | dst.x = p_x; 100 | dst.y = p_y; 101 | dst.w = src.w; 102 | dst.h = src.h; 103 | 104 | SDL_RenderCopy(renderer, message, &src, &dst); 105 | SDL_FreeSurface(surfaceMessage); 106 | } 107 | 108 | void RenderWindow::renderCenter(float p_x, float p_y, const char* p_text, TTF_Font* font, SDL_Color textColor) 109 | { 110 | SDL_Surface* surfaceMessage = TTF_RenderText_Blended( font, p_text, textColor); 111 | SDL_Texture* message = SDL_CreateTextureFromSurface(renderer, surfaceMessage); 112 | 113 | SDL_Rect src; 114 | src.x = 0; 115 | src.y = 0; 116 | src.w = surfaceMessage->w; 117 | src.h = surfaceMessage->h; 118 | 119 | SDL_Rect dst; 120 | dst.x = SCREEN_WIDTH/2 - src.w/2 + p_x; 121 | dst.y = SCREEN_HEIGHT/2 - src.h/2 + p_y; 122 | dst.w = src.w; 123 | dst.h = src.h; 124 | 125 | SDL_RenderCopy(renderer, message, &src, &dst); 126 | SDL_FreeSurface(surfaceMessage); 127 | } 128 | 129 | void RenderWindow::display() 130 | { 131 | SDL_RenderPresent(renderer); 132 | } 133 | 134 | void RenderWindow::cleanUp() 135 | { 136 | SDL_DestroyWindow(window); 137 | } -------------------------------------------------------------------------------- /src/player.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "player.h" 8 | #include "entity.h" 9 | #include "ground.h" 10 | 11 | const float GRAVITY = 0.09f; 12 | const int SCREEN_WIDTH = 800; 13 | const int SCREEN_HEIGHT = 480; 14 | const int ALIVE = 0; 15 | const int CURSOR_DEATH = 1; 16 | const int HOLE_DEATH = 2; 17 | 18 | Player::Player(float p_x, float p_y, std::vector p_tex) 19 | : Entity{ p_x, p_y, p_tex} 20 | { 21 | setX(SCREEN_WIDTH/2 - getWidth()/2); 22 | setY(SCREEN_HEIGHT - getHeight() - 64); 23 | } 24 | 25 | float Player::distanceFromCursor() 26 | { 27 | int mouseX = 0; 28 | int mouseY = 0; 29 | SDL_GetMouseState(&mouseX, &mouseY); 30 | 31 | return sqrt(pow((getX() + getWidth()/2) - mouseX, 2) + pow((getY() + getHeight()/2) - mouseY, 2)); 32 | } 33 | bool Player::jump() 34 | { 35 | if (distanceFromCursor() < 100) 36 | { 37 | if (grounded) 38 | { 39 | velocityY = -(1/distanceFromCursor() * 200); 40 | grounded = false; 41 | return true; 42 | } 43 | } 44 | return false; 45 | } 46 | 47 | float Player::clamp(float p_value, float p_min, float p_max) 48 | { 49 | if (p_value > p_max) 50 | return p_max; 51 | if (p_value < p_min) 52 | return p_min; 53 | return p_value; 54 | } 55 | 56 | void Player::animEyes() 57 | { 58 | int mouseX = 0; 59 | int mouseY = 0; 60 | SDL_GetMouseState(&mouseX, &mouseY); 61 | 62 | setAnimOffsetX(0, clamp(mouseX - getX() - getWidth()/2, -2.5, 2.5)); 63 | setAnimOffsetY(0, clamp(mouseY - getY() - getHeight()/2 + 15, -2.5, 2.5)); 64 | } 65 | 66 | void Player::update(Ground& ground) 67 | { 68 | timer++; 69 | score = timer/50; 70 | if (score > highscore) 71 | { 72 | highscore = score; 73 | } 74 | 75 | setX(getX() - 1); //autoscroll 76 | int mouseX = 0; 77 | int mouseY = 0; 78 | SDL_GetMouseState(&mouseX, &mouseY); 79 | 80 | animEyes(); 81 | setAnimOffsetY(3, 0); 82 | setAnimOffsetY(4, 0); 83 | 84 | if (distanceFromCursor() < 100) 85 | { 86 | setAnimOffsetY(3, sin(SDL_GetTicks()/50) * velocityX - 2); 87 | setAnimOffsetY(4, -sin(SDL_GetTicks()/50) * velocityX - 2 ); 88 | if (mouseX < getX()) 89 | { 90 | velocityX = 1/distanceFromCursor() * 100; 91 | } 92 | else if (mouseX > getX() + getWidth()) 93 | { 94 | velocityX = 1/distanceFromCursor() * -100; 95 | } 96 | else 97 | { 98 | velocityX = 0; 99 | if (mouseY > getY() && mouseY < getY() + getHeight()) 100 | { 101 | //kill player 102 | dead = CURSOR_DEATH; 103 | } 104 | } 105 | } 106 | else 107 | { 108 | velocityX = 0; 109 | } 110 | setX(getX() + velocityX); 111 | 112 | setY(getY() + velocityY); 113 | if (ground.isTileBelow(getX(), getWidth())) 114 | { 115 | if (getY() + getHeight() < SCREEN_HEIGHT - 64) 116 | { 117 | velocityY += GRAVITY; 118 | grounded = false; 119 | } 120 | else 121 | { 122 | if (getY() < SCREEN_HEIGHT - getHeight() - 64 + 20) 123 | { 124 | setY(SCREEN_HEIGHT - getHeight() - 64); 125 | grounded = true; 126 | } 127 | } 128 | } 129 | else 130 | { 131 | velocityY += GRAVITY; 132 | grounded = false; 133 | if (getY() > SCREEN_HEIGHT) 134 | { 135 | dead = HOLE_DEATH; 136 | } 137 | } 138 | 139 | } 140 | 141 | const char* Player::getScore() 142 | { 143 | std::string s = std::to_string(score); 144 | s = "DISTANCE: " + s; 145 | return s.c_str(); 146 | } 147 | 148 | const char* Player::getHighscore() 149 | { 150 | std::string s = std::to_string(highscore); 151 | s = "BEST: " + s; 152 | return s.c_str(); 153 | } 154 | 155 | int Player::getScoreInt() 156 | { 157 | return score; 158 | } 159 | 160 | int Player::isDead() 161 | { 162 | return dead; 163 | } 164 | 165 | void Player::reset() 166 | { 167 | setX(SCREEN_WIDTH/2 - getWidth()/2); 168 | setY(SCREEN_HEIGHT - getHeight() - 64); 169 | score = 0; 170 | timer = 0; 171 | velocityX = 0; 172 | velocityY = 0; 173 | dead = 0; 174 | } 175 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #ifdef __EMSCRIPTEN__ 2 | #include 3 | #include 4 | #endif 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "renderwindow.h" 18 | #include "entity.h" 19 | #include "player.h" 20 | #include "ground.h" 21 | 22 | const int SCREEN_WIDTH = 800; 23 | const int SCREEN_HEIGHT = 480; 24 | 25 | const int ALIVE = 0; 26 | const int CURSOR_DEATH = 1; 27 | const int HOLE_DEATH = 2; 28 | 29 | const Uint8 *keyState; 30 | 31 | RenderWindow window; 32 | 33 | std::vector playerTex; 34 | SDL_Texture* groundTex[4]; 35 | SDL_Texture* arrow; 36 | SDL_Texture* highscoreBox; 37 | SDL_Texture* deathOverlay; 38 | SDL_Texture* logo; 39 | 40 | TTF_Font* font32; 41 | TTF_Font* font32_outline; 42 | TTF_Font* font24; 43 | TTF_Font* font16; 44 | 45 | SDL_Color white = { 255, 255, 255 }; 46 | SDL_Color black = { 0, 0, 0 }; 47 | 48 | Mix_Chunk* jumpSfx; 49 | Mix_Chunk* fallSfx; 50 | Mix_Chunk* hitSfx; 51 | Mix_Chunk* clickSfx; 52 | 53 | bool gameRunning = true; 54 | bool playedDeathSFX = false; 55 | bool mainMenu = true; 56 | 57 | bool init() 58 | { 59 | SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); 60 | IMG_Init(IMG_INIT_PNG); 61 | TTF_Init(); 62 | 63 | window.create("Cursor Custodian", SCREEN_WIDTH, SCREEN_HEIGHT); 64 | Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048); 65 | 66 | srand((unsigned)time(0)); 67 | 68 | playerTex.push_back(window.loadTexture("res/textures/player/player_0.png")); 69 | playerTex.push_back(window.loadTexture("res/textures/player/player_1.png")); 70 | playerTex.push_back(window.loadTexture("res/textures/player/player_2.png")); 71 | playerTex.push_back(window.loadTexture("res/textures/player/player_3.png")); 72 | playerTex.push_back(window.loadTexture("res/textures/player/player_4.png")); 73 | 74 | groundTex[0] = window.loadTexture("res/textures/ground/left.png"); 75 | groundTex[1] = window.loadTexture("res/textures/ground/center.png"); 76 | groundTex[2] = window.loadTexture("res/textures/ground/right.png"); 77 | groundTex[3] = window.loadTexture("res/textures/ground/hole.png"); 78 | 79 | arrow = window.loadTexture("res/textures/arrow.png"); 80 | highscoreBox = window.loadTexture("res/textures/highscore_box.png"); 81 | deathOverlay = window.loadTexture("res/textures/death_overlay.png"); 82 | logo = window.loadTexture("res/textures/logo.png"); 83 | 84 | font32 = TTF_OpenFont("res/fonts/cocogoose.ttf", 32); 85 | font32_outline = TTF_OpenFont("res/fonts/cocogoose.ttf", 32); 86 | font24 = TTF_OpenFont("res/fonts/cocogoose.ttf", 24); 87 | font16 = TTF_OpenFont("res/fonts/cocogoose.ttf", 16); 88 | TTF_SetFontOutline(font32_outline, 3); 89 | 90 | jumpSfx = Mix_LoadWAV("res/sounds/jump.wav"); 91 | fallSfx = Mix_LoadWAV("res/sounds/fall.wav"); 92 | hitSfx = Mix_LoadWAV("res/sounds/hit.wav"); 93 | clickSfx = Mix_LoadWAV("res/sounds/click.wav"); 94 | Mix_PlayChannel(-1, jumpSfx, 0); 95 | 96 | return true; 97 | } 98 | 99 | bool load = init(); 100 | 101 | Player player(0, 0, playerTex); 102 | Ground ground(groundTex[0], groundTex[1], groundTex[2], groundTex[3]); 103 | 104 | void reset() 105 | { 106 | player.reset(); 107 | ground.reset(); 108 | } 109 | void gameLoop() 110 | { 111 | SDL_Event event; 112 | while (SDL_PollEvent(&event)) 113 | { 114 | switch (event.type) { 115 | case SDL_QUIT: 116 | { 117 | gameRunning = false; 118 | break; 119 | } 120 | case SDL_MOUSEBUTTONDOWN: 121 | { 122 | if (mainMenu) 123 | { 124 | if (event.button.button == SDL_BUTTON_LEFT && SDL_GetTicks() > 2500) 125 | { 126 | mainMenu = false; 127 | Mix_PlayChannel(-1, clickSfx, 0); 128 | } 129 | } 130 | else 131 | { 132 | if (event.button.button == SDL_BUTTON_LEFT && player.isDead() == ALIVE) 133 | { 134 | if (player.jump()) 135 | { 136 | Mix_PlayChannel(-1, jumpSfx, 0); 137 | } 138 | } 139 | else if (player.isDead() != ALIVE) 140 | { 141 | Mix_PlayChannel(-1, clickSfx, 0); 142 | reset(); 143 | playedDeathSFX = false; 144 | } 145 | } 146 | break; 147 | } 148 | } 149 | } 150 | if (mainMenu) 151 | { 152 | if (SDL_GetTicks() < 2500) 153 | { 154 | window.clear(); 155 | window.renderCenter(0, sin(SDL_GetTicks()/100) * 2 - 4, "POLYMARS", font24, white); 156 | window.display(); 157 | } 158 | else 159 | { 160 | window.clear(); 161 | 162 | window.render(SCREEN_WIDTH/2 - 234, SCREEN_HEIGHT/2 - 94 - 30, logo); 163 | window.renderCenter(0, 90 + sin(SDL_GetTicks()/100) * 2, "Click to start", font24, white); 164 | 165 | for (int i = 0; i < ground.getLength(); i++) 166 | { 167 | window.render(ground.getTile(i)); 168 | } 169 | window.display(); 170 | } 171 | } 172 | else 173 | { 174 | if (player.isDead() != CURSOR_DEATH) 175 | { 176 | ground.update(player.getScoreInt()); 177 | } 178 | 179 | if (player.isDead() == ALIVE) 180 | { 181 | player.update(ground); 182 | } 183 | else if (!playedDeathSFX) { 184 | if (player.isDead() == CURSOR_DEATH) 185 | { 186 | Mix_PlayChannel(-1, hitSfx, 0); 187 | } 188 | else if (player.isDead() == HOLE_DEATH) 189 | { 190 | Mix_PlayChannel(-1, fallSfx, 0); 191 | } 192 | playedDeathSFX = true; 193 | } 194 | 195 | window.clear(); 196 | 197 | window.render(player); 198 | for (int i = 0; i < ground.getLength(); i++) 199 | { 200 | window.render(ground.getTile(i)); 201 | } 202 | window.render(25, 30, arrow); 203 | window.render(62, 20, player.getScore(), font32_outline, black); 204 | window.render(65, 23, player.getScore(), font32, white); 205 | window.render(0, 65, highscoreBox); 206 | window.render(65, 64, player.getHighscore(), font16, white); 207 | 208 | if (player.isDead() != ALIVE) 209 | { 210 | window.render(deathOverlay); 211 | if (player.isDead() == CURSOR_DEATH) 212 | { 213 | window.renderCenter(0, -24, "The cursor is deadly to the player...", font24, white); 214 | } 215 | else if (player.isDead() == HOLE_DEATH) 216 | { 217 | window.renderCenter(0, -24, "The hole had no bottom...", font24, white); 218 | } 219 | window.renderCenter(0, 12, "Click to retry.", font16, white); 220 | } 221 | window.display(); 222 | } 223 | } 224 | 225 | int main(int argc, char* args[]) 226 | { 227 | #ifdef __EMSCRIPTEN__ 228 | emscripten_set_main_loop(gameLoop, 0, 1); 229 | #else 230 | while (gameRunning) 231 | { 232 | gameLoop(); 233 | SDL_Delay(16); 234 | } 235 | #endif 236 | 237 | window.cleanUp(); 238 | TTF_CloseFont(font32); 239 | TTF_CloseFont(font32_outline); 240 | TTF_CloseFont(font24); 241 | TTF_CloseFont(font16); 242 | TTF_Quit(); 243 | SDL_Quit(); 244 | 245 | return 0; 246 | } --------------------------------------------------------------------------------