diff --git a/CMakeLists.txt b/CMakeLists.txt index a677e67..b38fc4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,8 @@ set(LD52Native_SOURCES src/main.cc src/game.cc src/ems.cc + src/helpers.cc + src/constants.cc ) add_executable(LD52Native ${LD52Native_SOURCES}) diff --git a/resources/produceStuff.png b/resources/produceStuff.png new file mode 100644 index 0000000..19a3bb9 Binary files /dev/null and b/resources/produceStuff.png differ diff --git a/src/constants.cc b/src/constants.cc new file mode 100644 index 0000000..5218267 --- /dev/null +++ b/src/constants.cc @@ -0,0 +1,6 @@ +#include "constants.h" + +// standard library includes +#include + +const float PI_F = std::acos(-1.0F); diff --git a/src/constants.h b/src/constants.h index 7079858..0864439 100644 --- a/src/constants.h +++ b/src/constants.h @@ -1,7 +1,51 @@ #ifndef LD52_HARVEST_FOOD_CUTS_CONSTANTS_H_ #define LD52_HARVEST_FOOD_CUTS_CONSTANTS_H_ +extern const float PI_F; + constexpr int DEFAULT_SCREEN_WIDTH = 500; constexpr int DEFAULT_SCREEN_HEIGHT = 800; +constexpr int FOOD_COUNT = 5; + +enum class FoodType { + FT_CORN = 0, + FT_GRAPES, + FT_APPLE, + FT_BROCCOLI, + FT_BANANA, +}; + +constexpr int CORN_COORDS[4] = {0, 0, 281, 676}; +constexpr int GRAPES_COORDS[4] = {281, 0, 232, 434}; +constexpr int APPLE_COORDS[4] = {513, 0, 250, 251}; +constexpr int BROCCOLI_COORDS[4] = {767, 2, 268, 153}; +constexpr int BANANA_COORDS[4] = {525, 254, 189, 355}; + +constexpr float CORN_EYE_OFFSET[2] = {-10, 0}; +constexpr float GRAPES_EYE_OFFSET[2] = {0, 0}; +constexpr float APPLE_EYE_OFFSET[2] = {0, 0}; +constexpr float BROCCOLI_EYE_OFFSET[2] = {0, 0}; +constexpr float BANANA_EYE_OFFSET[2] = {40, 0}; + +constexpr float EYE_WIDTH_RATIO = 0.3F; + +constexpr float AREA_REDUCTION_SCALE = 0.99F; +constexpr float MIN_AREA = 15.0F; + +constexpr float MAX_FOOD_WH = 500.0F; + +constexpr float EYE_RADIUS = 14.0F; +constexpr float BLINKING_EYE_SIZE = 4.0F; + +constexpr float MOUTH_RADIUS = 20.0F; + +constexpr float MIN_BLINK_TIME = 1.0F; +constexpr float MAX_BLINK_TIME = 20.0F; +constexpr float BLINK_DURATION = 0.7F; + +constexpr float CUT_TIMER_RATE = 1.0F; + +constexpr float CUT_TIMER_RATE_INC_AMT = 0.03F; + #endif diff --git a/src/game.cc b/src/game.cc index a8670d8..dd03827 100644 --- a/src/game.cc +++ b/src/game.cc @@ -1,20 +1,187 @@ #include "game.h" // third party includes +#include #include -Game::Game() {} +// local includes +#include "constants.h" +#include "helpers.h" + +Game::Game() + : re(std::random_device{}()), dist(0, FOOD_COUNT - 1), score(0), + areaSizeRatio(1.0F), currentFood(dist(re)), blinkTimer(10.0F), + cutTimer(0.0F), cutTimerRateInc(1.0F) { + flags.set(0); + + spriteSheet = LoadTexture("resources/produceStuff.png"); +} void Game::do_update() { update_impl(); draw_impl(); } -void Game::update_impl() {} +void Game::update_impl() { + const float dt = GetFrameTime(); + + if (flags.test(0)) { + flags.set(0); + scoreString.clear(); + if (score == 0) { + scoreString.push_back('0'); + } else { + std::string temp; + for (unsigned long long i = score; i > 0; i /= 10) { + temp.push_back((i % 10) + '0'); + } + for (auto c : temp) { + scoreString.push_back(c); + } + } + } + + blinkTimer -= dt; + if (blinkTimer <= 0.0F) { + flags.flip(1); + if (flags.test(1)) { + blinkTimer = BLINK_DURATION; + } else { + blinkTimer = std::uniform_real_distribution{MIN_BLINK_TIME, + MAX_BLINK_TIME}(re); + } + } + + cutTimer += dt * CUT_TIMER_RATE * cutTimerRateInc; + if (cutTimer > 1.0F) { + cutTimer -= 1.0F; + } +} void Game::draw_impl() { + BeginDrawing(); - ClearBackground(BLACK); - DrawText("Testing...", 100, 100, 30, RAYWHITE); + ClearBackground(RAYWHITE); + + float ratio; + float width; + float height; + float offsetX; + float offsetY; + + switch (currentFood) { + case (unsigned int)FoodType::FT_APPLE: + ratio = (float)APPLE_COORDS[2] / (float)APPLE_COORDS[3]; + break; + case (unsigned int)FoodType::FT_BANANA: + ratio = (float)BANANA_COORDS[2] / (float)BANANA_COORDS[3]; + break; + case (unsigned int)FoodType::FT_BROCCOLI: + ratio = (float)BROCCOLI_COORDS[2] / (float)BROCCOLI_COORDS[3]; + break; + case (unsigned int)FoodType::FT_CORN: + ratio = (float)CORN_COORDS[2] / (float)CORN_COORDS[3]; + break; + case (unsigned int)FoodType::FT_GRAPES: + ratio = (float)GRAPES_COORDS[2] / (float)GRAPES_COORDS[3]; + break; + default: + ratio = 1.0F; + break; + } + + if (ratio < 1.0F) { + height = MAX_FOOD_WH; + width = height * ratio; + } else { + width = MAX_FOOD_WH; + height = width / ratio; + } + + if (width > GetScreenWidth() && height <= GetScreenHeight()) { + if (ratio < 1.0F) { + height = GetScreenWidth() / ratio; + width = height * ratio; + } else { + width = GetScreenWidth(); + height = width / ratio; + } + } else if (width <= GetScreenWidth() && height > GetScreenHeight()) { + if (ratio < 1.0F) { + height = GetScreenHeight(); + width = height * ratio; + } else { + width = GetScreenHeight() * ratio; + height = width / ratio; + } + } + offsetX = (GetScreenWidth() - width) / 2.0F; + offsetY = (GetScreenHeight() - height) / 2.0F; + + switch (currentFood) { + case (unsigned int)FoodType::FT_APPLE: + DrawTexturePro( + spriteSheet, + {APPLE_COORDS[0], APPLE_COORDS[1], APPLE_COORDS[2], APPLE_COORDS[3]}, + {offsetX, offsetY, width, height}, {0.0F, 0.0F}, 0.0F, WHITE); + break; + case (unsigned int)FoodType::FT_BANANA: + DrawTexturePro(spriteSheet, + {BANANA_COORDS[0], BANANA_COORDS[1], BANANA_COORDS[2], + BANANA_COORDS[3]}, + {offsetX, offsetY, width, height}, {0.0F, 0.0F}, 0.0F, + WHITE); + break; + case (unsigned int)FoodType::FT_BROCCOLI: + DrawTexturePro(spriteSheet, + {BROCCOLI_COORDS[0], BROCCOLI_COORDS[1], BROCCOLI_COORDS[2], + BROCCOLI_COORDS[3]}, + {offsetX, offsetY, width, height}, {0.0F, 0.0F}, 0.0F, + WHITE); + break; + case (unsigned int)FoodType::FT_CORN: + DrawTexturePro( + spriteSheet, + {CORN_COORDS[0], CORN_COORDS[1], CORN_COORDS[2], CORN_COORDS[3]}, + {offsetX, offsetY, width, height}, {0.0F, 0.0F}, 0.0F, WHITE); + break; + case (unsigned int)FoodType::FT_GRAPES: + DrawTexturePro(spriteSheet, + {GRAPES_COORDS[0], GRAPES_COORDS[1], GRAPES_COORDS[2], + GRAPES_COORDS[3]}, + {offsetX, offsetY, width, height}, {0.0F, 0.0F}, 0.0F, + WHITE); + break; + default: + break; + } + + DrawRectangle(0, offsetY, GetScreenWidth(), height / 3.0F, + {255, 255, 255, 127}); + + float cutPos = cutTimer * height; + + DrawLine(0, cutPos + offsetY - height / 3.0F, GetScreenWidth(), + cutPos + offsetY - height / 3.0F, BLACK); + + Helpers::draw_eyes_full(offsetX + width / 2.0F, offsetY + height / 2.0F, + width, EYE_RADIUS, (FoodType)currentFood, + flags.test(1)); + + if (flags.test(2)) { + Helpers::draw_happy_mouth(offsetX + width / 2.0F, offsetY + height / 2.0F, + MOUTH_RADIUS); + } + + DrawText(scoreString.c_str(), 2, 2, 32, BLACK); EndDrawing(); } + +void Game::reset() { + flags.set(0); + score = 0; + areaSizeRatio = 1.0F; + currentFood = dist(re); + blinkTimer = 10.0F; + cutTimer = 0.0F; +} diff --git a/src/game.h b/src/game.h index 5c884ac..12eb9d6 100644 --- a/src/game.h +++ b/src/game.h @@ -1,6 +1,13 @@ #ifndef LD52_HARVEST_FOOD_CUTS_GAME_H_ #define LD52_HARVEST_FOOD_CUTS_GAME_H_ +// standard library includes +#include +#include + +// third party includes +#include + class Game { public: Game(); @@ -8,10 +15,28 @@ public: void do_update(); private: - void update_impl(); void draw_impl(); + void reset(); + + std::default_random_engine re; + std::uniform_int_distribution dist; + std::string scoreString; + Texture2D spriteSheet; + unsigned long long score; + /* + * 0 - score dirty + * 1 - is blinking + * 2 - happy + */ + std::bitset<32> flags; + float areaSizeRatio; + float currentArea; + unsigned int currentFood; + float blinkTimer; + float cutTimer; + float cutTimerRateInc; }; #endif diff --git a/src/helpers.cc b/src/helpers.cc new file mode 100644 index 0000000..c074355 --- /dev/null +++ b/src/helpers.cc @@ -0,0 +1,86 @@ +#include "helpers.h" + +// standard library includes +#include + +// third party includes +#include + +// local includes +#include "constants.h" + +void Helpers::draw_eye(float x, float y, float radius) { + DrawCircle(x + 0.5F, y + 0.5F, radius, BLACK); + DrawCircleSector({x, y}, radius / 1.5F, 180.0F, 270.0F, 32, WHITE); +} + +void Helpers::draw_blinking_eye(float x, float y, float radius) { + DrawLineEx({x - radius, y}, {x + radius, y}, BLINKING_EYE_SIZE, BLACK); +} + +void Helpers::draw_open_mouth(float x, float y, float radius) { + DrawCircle(x, y, radius, BLACK); +} + +void Helpers::draw_happy_mouth(float x, float y, float radius) { + DrawCircleSector({x, y}, radius, -90.0F, 90.0F, 32, BLACK); +} + +void Helpers::draw_eyes_full(float x, float y, float width, float radius, + FoodType foodType, bool isBlinking) { + float offsets[2]; + switch (foodType) { + case FoodType::FT_CORN: + offsets[0] = CORN_EYE_OFFSET[0]; + offsets[1] = CORN_EYE_OFFSET[1]; + break; + case FoodType::FT_GRAPES: + offsets[0] = GRAPES_EYE_OFFSET[0]; + offsets[1] = GRAPES_EYE_OFFSET[1]; + break; + case FoodType::FT_APPLE: + offsets[0] = APPLE_EYE_OFFSET[0]; + offsets[1] = APPLE_EYE_OFFSET[1]; + break; + case FoodType::FT_BROCCOLI: + offsets[0] = BROCCOLI_EYE_OFFSET[0]; + offsets[1] = BROCCOLI_EYE_OFFSET[1]; + break; + case FoodType::FT_BANANA: + offsets[0] = BANANA_EYE_OFFSET[0]; + offsets[1] = BANANA_EYE_OFFSET[1]; + break; + default: + offsets[0] = 0.0F; + offsets[1] = 0.0F; + break; + } + + const float eye_width = width * EYE_WIDTH_RATIO; + + if (isBlinking) { + draw_blinking_eye(x - eye_width / 2.0F + offsets[0], y + offsets[1], + radius); + draw_blinking_eye(x + eye_width / 2.0F + offsets[0], y + offsets[1], + radius); + } else { + draw_eye(x - eye_width / 2.0F + offsets[0], y + offsets[1], radius); + draw_eye(x + eye_width / 2.0F + offsets[0], y + offsets[1], radius); + } +} + +float Helpers::get_cut_pos(float timer, FoodType foodType) { + switch (foodType) { + case FoodType::FT_CORN: + return std::cos(timer) * (float)CORN_COORDS[3]; + case FoodType::FT_GRAPES: + return std::cos(timer) * (float)GRAPES_COORDS[3]; + case FoodType::FT_APPLE: + return std::cos(timer) * (float)APPLE_COORDS[3]; + case FoodType::FT_BROCCOLI: + return std::cos(timer) * (float)BROCCOLI_COORDS[3]; + case FoodType::FT_BANANA: + return std::cos(timer) * (float)BANANA_COORDS[3]; + } + return 0.0F; +} diff --git a/src/helpers.h b/src/helpers.h new file mode 100644 index 0000000..7944ca5 --- /dev/null +++ b/src/helpers.h @@ -0,0 +1,20 @@ +#ifndef LD52_HARVEST_FOOD_CUTS_H_ +#define LD52_HARVEST_FOOD_CUTS_H_ + +#include "constants.h" + +namespace Helpers { + +extern void draw_eye(float x, float y, float radius); +extern void draw_blinking_eye(float x, float y, float radius); +extern void draw_open_mouth(float x, float y, float radius); +extern void draw_happy_mouth(float x, float y, float radius); + +extern void draw_eyes_full(float x, float y, float width, float radius, + FoodType foodType, bool isBlinking); + +extern float get_cut_pos(float timer, FoodType foodType); + +} // namespace Helpers + +#endif diff --git a/src/main.cc b/src/main.cc index a57f8ad..245593a 100644 --- a/src/main.cc +++ b/src/main.cc @@ -31,7 +31,8 @@ void game_update(void *game_ptr) { ((Game *)game_ptr)->do_update(); } int main() { #ifdef __EMSCRIPTEN__ - InitWindow(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT, "LD52_Harvest_Food_Cuts"); + InitWindow(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT, + "LD52_Harvest_Food_Cuts"); #else InitWindow(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT, "LD52_Harvest_Food_Cuts_Native"); diff --git a/wasm_build/Makefile b/wasm_build/Makefile index ac64b5d..d693776 100644 --- a/wasm_build/Makefile +++ b/wasm_build/Makefile @@ -7,10 +7,15 @@ endif SOURCES = \ ../src/main.cc \ ../src/ems.cc \ - ../src/game.cc + ../src/game.cc \ + ../src/constants.cc \ + ../src/helpers.cc HEADERS = \ - ../src/constants.h + ../src/constants.h \ + ../src/game.h \ + ../src/ems.h \ + ../src/helpers.h CXX = source ${HOME}/git/emsdk/emsdk_env.sh && em++ @@ -22,6 +27,7 @@ ld52_harvest_cut.html: ${SOURCES} ${HEADERS} --shell-file custom_shell.html \ -sEXPORTED_FUNCTIONS=_main \ -sEXPORTED_RUNTIME_METHODS=ccall \ + --preload-file ../resources \ ${OTHER_FLAGS} \ ${SOURCES}