From d7d1d4d7a017b6cdd44289f7da3100a8398cbc8a Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Wed, 23 Aug 2023 16:37:26 +0900 Subject: [PATCH] Impl. "hacking" a "Walker" The "hack" is to press a button in a time limit after clicking on a Walker's head. If successful, the player can control the Walker. On failure, nothing happens. --- Makefile | 6 +- src/screen.cc | 34 ++++++- src/screen.h | 33 ++++++- src/screen_test.cc | 8 +- src/screen_test.h | 4 +- src/screen_trunner.cc | 35 +++++-- src/screen_trunner.h | 6 +- src/screen_walker_hack.cc | 191 ++++++++++++++++++++++++++++++++++++++ src/screen_walker_hack.h | 50 ++++++++++ wasm_build/Makefile | 6 +- 10 files changed, 346 insertions(+), 27 deletions(-) create mode 100644 src/screen_walker_hack.cc create mode 100644 src/screen_walker_hack.h diff --git a/Makefile b/Makefile index f64f735..e1ececd 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,8 @@ SOURCES = \ src/raymath.cc \ src/ems.cc \ src/walker.cc \ - src/surface_triangle.cc + src/surface_triangle.cc \ + src/screen_walker_hack.cc HEADERS = \ src/game.h \ @@ -29,7 +30,8 @@ HEADERS = \ src/3d_helpers.h \ src/ems.h \ src/walker.h \ - src/surface_triangle.h + src/surface_triangle.h\ + src/screen_walker_hack.h OBJECTS = $(addprefix ${OBJDIR}/,$(subst .cc,.cc.o,${SOURCES})) diff --git a/src/screen.cc b/src/screen.cc index 4730408..6fd59ea 100644 --- a/src/screen.cc +++ b/src/screen.cc @@ -1,6 +1,8 @@ #include "screen.h" // standard library includes +#include + #include #ifndef NDEBUG #include @@ -45,9 +47,19 @@ ScreenStack::Ptr ScreenStack::new_instance() { return ptr; } +ScreenStack::~ScreenStack() { + UnloadRenderTexture(*render_texture); + render_texture.reset(); +} + void ScreenStack::update(float dt) { handle_pending_actions(); + bool resized = IsWindowResized(); + if (resized) { + reset_render_texture(); + } + auto idx = stack.size(); if (idx == 0) { #ifndef NDEBUG @@ -57,14 +69,21 @@ void ScreenStack::update(float dt) { update(dt); return; } - while (idx > 0 && stack.at(--idx)->update(dt)) { + while (idx > 0 && stack.at(--idx)->update(dt, resized)) { } } void ScreenStack::draw() { for (decltype(stack.size()) idx = 0; - idx < stack.size() && stack.at(idx)->draw(); ++idx) { + idx < stack.size() && stack.at(idx)->draw(render_texture.get()); ++idx) { } + + BeginDrawing(); + DrawTextureRec( + render_texture->texture, + Rectangle{0, 0, (float)GetScreenWidth(), (float)-GetScreenHeight()}, + {0, 0}, WHITE); + EndDrawing(); } void ScreenStack::push_screen(Screen::Ptr &&screen) { @@ -79,7 +98,16 @@ void ScreenStack::clear_screens() { actions.push_back(PendingAction(Action::CLEAR_SCREENS)); } -ScreenStack::ScreenStack() : self_weak(), stack(), actions() {} +void ScreenStack::reset_render_texture() { + UnloadRenderTexture(*render_texture); + + *render_texture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight()); +} + +ScreenStack::ScreenStack() + : render_texture(new RenderTexture), self_weak(), stack(), actions() { + *render_texture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight()); +} void ScreenStack::handle_pending_actions() { while (!actions.empty()) { diff --git a/src/screen.h b/src/screen.h index 9c29c38..00b2318 100644 --- a/src/screen.h +++ b/src/screen.h @@ -1,14 +1,17 @@ #ifndef JUMPARTIFACT_DOT_COM_DEMO_0_SCREEN_H_ #define JUMPARTIFACT_DOT_COM_DEMO_0_SCREEN_H_ +#include + #include #include #include #include #include -// Forward declaration. +// Forward declarations. class ScreenStack; +struct RenderTexture; class Screen { public: @@ -17,6 +20,9 @@ class Screen { template static Ptr new_screen(std::weak_ptr stack); + template + static Ptr new_screen_args(std::weak_ptr stack, Args... args); + virtual ~Screen() {} // No copy. @@ -28,9 +34,9 @@ class Screen { Screen& operator=(Screen&&) = default; /// Return true if next screen should be updated. - virtual bool update(float dt) = 0; + virtual bool update(float dt, bool screen_resized) = 0; /// Return true if next screen should be drawn. - virtual bool draw() = 0; + virtual bool draw(RenderTexture* renderTexture) = 0; protected: Screen(std::weak_ptr stack); @@ -67,6 +73,8 @@ class ScreenStack { public: static Ptr new_instance(); + ~ScreenStack(); + // No copy. ScreenStack(const ScreenStack&) = delete; ScreenStack& operator=(const ScreenStack&) = delete; @@ -86,15 +94,21 @@ class ScreenStack { template void push_constructing_screen(); + template + void push_constructing_screen_args(Args... args); + void pop_screen(); void clear_screens(); + void reset_render_texture(); + private: ScreenStack(); void handle_pending_actions(); + std::unique_ptr render_texture; Weak self_weak; std::vector stack; std::deque actions; @@ -105,6 +119,12 @@ Screen::Ptr Screen::new_screen(std::weak_ptr stack) { return std::unique_ptr(new SubScreen{stack}); } +template +Screen::Ptr Screen::new_screen_args(std::weak_ptr stack, + Args... args) { + return std::unique_ptr(new SubScreen{stack, args...}); +} + template void ScreenStack::push_screen() { actions.emplace_back(Screen::new_screen(self_weak)); @@ -117,4 +137,11 @@ void ScreenStack::push_constructing_screen() { }); } +template +void ScreenStack::push_constructing_screen_args(Args... args) { + actions.emplace_back([args...](ScreenStack::Weak ss) -> Screen::Ptr { + return Screen::new_screen_args(ss, args...); + }); +} + #endif diff --git a/src/screen_test.cc b/src/screen_test.cc index 0bf5569..966a66e 100644 --- a/src/screen_test.cc +++ b/src/screen_test.cc @@ -10,19 +10,19 @@ TestScreen::TestScreen(ScreenStack::Weak weak_ptr) : Screen(weak_ptr), TEMP_cached_dt(0.0F) {} TestScreen::~TestScreen() {} -bool TestScreen::update(float dt) { +bool TestScreen::update(float dt, bool) { TEMP_cached_dt = dt; return false; } -bool TestScreen::draw() { +bool TestScreen::draw(RenderTexture *render_texture) { std::string dt_string = std::string("Delta-time: ") + std::to_string(TEMP_cached_dt); - BeginDrawing(); + BeginTextureMode(*render_texture); ClearBackground(BLACK); DrawText("Testing...", 100, 100, 30, RAYWHITE); DrawText(dt_string.c_str(), 100, 140, 30, RAYWHITE); - EndDrawing(); + EndTextureMode(); return false; } diff --git a/src/screen_test.h b/src/screen_test.h index 56469c5..d353fc8 100644 --- a/src/screen_test.h +++ b/src/screen_test.h @@ -8,8 +8,8 @@ class TestScreen : public Screen { TestScreen(ScreenStack::Weak weak_ptr); ~TestScreen() override; - bool update(float dt) override; - bool draw() override; + bool update(float dt, bool is_resized) override; + bool draw(RenderTexture *render_texture) override; private: float TEMP_cached_dt; diff --git a/src/screen_trunner.cc b/src/screen_trunner.cc index da7e49a..91070a4 100644 --- a/src/screen_trunner.cc +++ b/src/screen_trunner.cc @@ -16,6 +16,7 @@ // local includes #include "3d_helpers.h" #include "ems.h" +#include "screen_walker_hack.h" TRunnerScreen::TRunnerScreen(std::weak_ptr stack) : Screen(stack), @@ -55,7 +56,8 @@ TRunnerScreen::TRunnerScreen(std::weak_ptr stack) right_text_width(MeasureText("Right", BUTTON_FONT_SIZE)), forward_text_width(MeasureText("Forward", BUTTON_FONT_SIZE)), reset_surface_text_width(MeasureText("Reset Surface", BUTTON_FONT_SIZE)), - surface_reset_anim_timer(0.0F) { + surface_reset_anim_timer(0.0F), + walker_hack_success(false) { #ifndef NDEBUG std::cout << "idx_hit initialized to " << idx_hit << std::endl; #endif @@ -85,8 +87,8 @@ TRunnerScreen::~TRunnerScreen() { UnloadModel(TEMP_cube_model); } -bool TRunnerScreen::update(float dt) { - if (IsWindowResized()) { +bool TRunnerScreen::update(float dt, bool is_resized) { + if (is_resized) { UnloadRenderTexture(fgRenderTexture); UnloadRenderTexture(bgRenderTexture); @@ -94,6 +96,15 @@ bool TRunnerScreen::update(float dt) { fgRenderTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight()); } + if (flags.test(1)) { + if (walker_hack_success && controlled_walker_idx.has_value()) { + walkers[controlled_walker_idx.value()].set_player_controlled(true); + } else { + controlled_walker_idx.reset(); + } + flags.reset(1); + } + if (controlled_walker_idx.has_value()) { auto walker_body_pos = walkers[controlled_walker_idx.value()].get_body_pos(); @@ -173,7 +184,13 @@ bool TRunnerScreen::update(float dt) { walkers[controlled_walker_idx.value()].set_player_controlled(false); } controlled_walker_idx = idx; - walkers[controlled_walker_idx.value()].set_player_controlled(true); + auto s_stack = stack.lock(); + if (s_stack) { + s_stack->push_constructing_screen_args( + &walker_hack_success); + flags.set(1); + } + // walkers[controlled_walker_idx.value()].set_player_controlled(true); idx_hit = SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT; @@ -264,7 +281,7 @@ post_check_click: return false; } -bool TRunnerScreen::draw() { +bool TRunnerScreen::draw(RenderTexture *render_texture) { BeginTextureMode(bgRenderTexture); ClearBackground(PixelToColor(Pixel::PIXEL_SKY)); BeginMode3D(camera); @@ -317,7 +334,7 @@ bool TRunnerScreen::draw() { EndMode3D(); if (!flags.test(0)) { - if (controlled_walker_idx.has_value()) { + if (!flags.test(1) && controlled_walker_idx.has_value()) { int total_width = 0; DrawRectangle(0, GetScreenHeight() - BUTTON_FONT_SIZE, left_text_width, BUTTON_FONT_SIZE, Color{255, 255, 255, 180}); @@ -387,7 +404,7 @@ bool TRunnerScreen::draw() { EndTextureMode(); } - BeginDrawing(); + BeginTextureMode(*render_texture); DrawTextureRec( bgRenderTexture.texture, Rectangle{0, 0, (float)GetScreenWidth(), (float)-GetScreenHeight()}, @@ -398,9 +415,9 @@ bool TRunnerScreen::draw() { Rectangle{0, 0, (float)GetScreenWidth(), (float)-GetScreenHeight()}, {0, 0}, WHITE); } - EndDrawing(); + EndTextureMode(); - return false; + return true; } Color TRunnerScreen::PixelToColor(Pixel p) { diff --git a/src/screen_trunner.h b/src/screen_trunner.h index ab27884..27674cd 100644 --- a/src/screen_trunner.h +++ b/src/screen_trunner.h @@ -37,8 +37,8 @@ class TRunnerScreen : public Screen { TRunnerScreen(std::weak_ptr stack); ~TRunnerScreen() override; - bool update(float dt) override; - bool draw() override; + bool update(float dt, bool is_resized) override; + bool draw(RenderTexture *render_texture) override; private: enum Pixel : unsigned char { @@ -68,6 +68,7 @@ class TRunnerScreen : public Screen { Camera3D camera; /* * 0 - resetting surface + * 1 - walker hack attempted */ std::bitset<64> flags; Model TEMP_cube_model; @@ -88,6 +89,7 @@ class TRunnerScreen : public Screen { const int forward_text_width; const int reset_surface_text_width; float surface_reset_anim_timer; + bool walker_hack_success; void camera_to_targets(float dt); void generate_surface(); diff --git a/src/screen_walker_hack.cc b/src/screen_walker_hack.cc new file mode 100644 index 0000000..dccf42c --- /dev/null +++ b/src/screen_walker_hack.cc @@ -0,0 +1,191 @@ +#include "screen_walker_hack.h" + +// standard library includes +#include +#ifndef NDEBUG +#include +#endif + +// third party includes +#include + +// local includes +#include "ems.h" + +static const char *WALKER_HACK_SCREEN_INSTRUCTIONS = + "Press The Correct Button!"; + +WalkerHackScreen::WalkerHackScreen(ScreenStack::Weak ss_weak, + bool *walker_hack_success) + : Screen(ss_weak), + walker_hack_success(walker_hack_success), + timer(WALKER_HACK_SCREEN_DURATION), + font_size(10), + instructions_size(1), + instructions_font_size(font_size), + type_f_size(1), + type_j_size(1), + type_a_size(1), + type_l_size(1), + button_type(BUTTON_TYPE_F) { + button_type = + (ButtonType)((int)(call_js_get_random() * (float)BUTTON_TYPE_SIZE)); + *walker_hack_success = false; + set_sizes(); +} + +WalkerHackScreen::~WalkerHackScreen() {} + +bool WalkerHackScreen::update(float dt, bool resized) { + if (resized) { + set_sizes(); + } + + timer -= dt; + if (timer < 0.0F) { +#ifndef NDEBUG + std::clog << "WalkerHackScreen: timer ended.\n"; +#endif + timer = WALKER_HACK_SCREEN_DURATION; + auto s_stack = stack.lock(); + if (s_stack) { + s_stack->pop_screen(); + } + } + + if (IsMouseButtonPressed(0)) { + switch (button_type) { + case BUTTON_TYPE_F: + if (GetTouchX() >= + GetScreenWidth() / 2 - type_f_size - BUTTON_DRAW_OFFSET && + GetTouchX() <= GetScreenWidth() / 2 - BUTTON_DRAW_OFFSET && + GetTouchY() <= font_size) { + *walker_hack_success = true; + button_type = BUTTON_TYPE_SIZE; + } + break; + case BUTTON_TYPE_J: + if (GetTouchX() >= GetScreenWidth() / 2 + BUTTON_DRAW_OFFSET && + GetTouchX() <= + GetScreenWidth() / 2 + BUTTON_DRAW_OFFSET + type_j_size && + GetTouchY() <= font_size) { + *walker_hack_success = true; + button_type = BUTTON_TYPE_SIZE; + } + break; + case BUTTON_TYPE_A: + if (GetTouchX() >= + GetScreenWidth() / 2 - type_f_size - BUTTON_DRAW_OFFSET && + GetTouchX() <= GetScreenWidth() / 2 - BUTTON_DRAW_OFFSET && + GetTouchY() >= font_size + BUTTON_DRAW_OFFSET && + GetTouchY() <= font_size * 2 + BUTTON_DRAW_OFFSET) { + *walker_hack_success = true; + button_type = BUTTON_TYPE_SIZE; + } + break; + case BUTTON_TYPE_L: + if (GetTouchX() >= GetScreenWidth() / 2 + BUTTON_DRAW_OFFSET && + GetTouchX() <= + GetScreenWidth() / 2 + BUTTON_DRAW_OFFSET + type_l_size && + GetTouchY() >= font_size + BUTTON_DRAW_OFFSET && + GetTouchY() <= font_size * 2 + BUTTON_DRAW_OFFSET) { + *walker_hack_success = true; + button_type = BUTTON_TYPE_SIZE; + } + break; + default: + break; + } + } + + switch (GetKeyPressed()) { + case KEY_F: + if (button_type == BUTTON_TYPE_F) { + *walker_hack_success = true; + button_type = BUTTON_TYPE_SIZE; + } + break; + case KEY_J: + if (button_type == BUTTON_TYPE_J) { + *walker_hack_success = true; + button_type = BUTTON_TYPE_SIZE; + } + break; + case KEY_A: + if (button_type == BUTTON_TYPE_A) { + *walker_hack_success = true; + button_type = BUTTON_TYPE_SIZE; + } + break; + case KEY_L: + if (button_type == BUTTON_TYPE_L) { + *walker_hack_success = true; + button_type = BUTTON_TYPE_SIZE; + } + break; + default: + break; + } + return false; +} + +bool WalkerHackScreen::draw(RenderTexture *render_texture) { + BeginTextureMode(*render_texture); + + DrawRectangle(GetScreenWidth() / 2 - instructions_size / 2, + (GetScreenHeight() / 4) * 3, instructions_size, + instructions_font_size, BLACK); + DrawText(WALKER_HACK_SCREEN_INSTRUCTIONS, + GetScreenWidth() / 2 - instructions_size / 2, + (GetScreenHeight() / 4) * 3, instructions_font_size, WHITE); + + float ratio = + (WALKER_HACK_SCREEN_DURATION - timer) / WALKER_HACK_SCREEN_DURATION; + DrawRectangle(GetScreenWidth() * ratio, + (float)GetScreenHeight() * BUTTON_FONT_SIZE_RATIO * 2.0F + + BUTTON_DRAW_OFFSET * 3.0F, + GetScreenWidth() * (1.0F - ratio), 30, GREEN); + + DrawRectangle(GetScreenWidth() / 2 - type_f_size - BUTTON_DRAW_OFFSET, 0, + type_f_size, font_size, + button_type == BUTTON_TYPE_F ? GREEN : BLACK); + DrawText("F", GetScreenWidth() / 2 - type_f_size - BUTTON_DRAW_OFFSET, 0, + font_size, WHITE); + + DrawRectangle(GetScreenWidth() / 2 + BUTTON_DRAW_OFFSET, 0, type_j_size, + font_size, button_type == BUTTON_TYPE_J ? GREEN : BLACK); + DrawText("J", GetScreenWidth() / 2 + BUTTON_DRAW_OFFSET, 0, font_size, WHITE); + + DrawRectangle(GetScreenWidth() / 2 - type_a_size - BUTTON_DRAW_OFFSET, + font_size + BUTTON_DRAW_OFFSET, type_a_size, font_size, + button_type == BUTTON_TYPE_A ? GREEN : BLACK); + DrawText("A", GetScreenWidth() / 2 - type_a_size - BUTTON_DRAW_OFFSET, + font_size + BUTTON_DRAW_OFFSET, font_size, WHITE); + + DrawRectangle(GetScreenWidth() / 2 + BUTTON_DRAW_OFFSET, + font_size + BUTTON_DRAW_OFFSET, type_l_size, font_size, + button_type == BUTTON_TYPE_L ? GREEN : BLACK); + DrawText("L", GetScreenWidth() / 2 + BUTTON_DRAW_OFFSET, + font_size + BUTTON_DRAW_OFFSET, font_size, WHITE); + + EndTextureMode(); + + return true; +} + +void WalkerHackScreen::set_sizes() { + font_size = GetScreenHeight() * BUTTON_FONT_SIZE_RATIO; + instructions_font_size = font_size; + instructions_size = + MeasureText(WALKER_HACK_SCREEN_INSTRUCTIONS, instructions_font_size); + while (instructions_size > GetScreenWidth() && instructions_font_size > 1) { + --instructions_font_size; + instructions_size = + MeasureText(WALKER_HACK_SCREEN_INSTRUCTIONS, instructions_font_size); + } + + type_f_size = MeasureText("F", font_size); + type_j_size = MeasureText("J", font_size); + type_a_size = MeasureText("A", font_size); + type_l_size = MeasureText("L", font_size); +} diff --git a/src/screen_walker_hack.h b/src/screen_walker_hack.h new file mode 100644 index 0000000..9ca1fb9 --- /dev/null +++ b/src/screen_walker_hack.h @@ -0,0 +1,50 @@ +#ifndef JUMPARTIFACT_DOT_COM_DEMO_0_WALKER_HACK_SCREEN_H_ +#define JUMPARTIFACT_DOT_COM_DEMO_0_WALKER_HACK_SCREEN_H_ + +// local includes +#include "screen.h" + +constexpr float WALKER_HACK_SCREEN_DURATION = 2.0F; +constexpr float BUTTON_FONT_SIZE_RATIO = 0.28F; +constexpr int BUTTON_DRAW_OFFSET = 5; + +class WalkerHackScreen : public Screen { + public: + WalkerHackScreen(ScreenStack::Weak ss_weak, bool *walkerHackSuccess); + ~WalkerHackScreen() override; + + // Disallow copy. + WalkerHackScreen(const WalkerHackScreen &) = delete; + WalkerHackScreen &operator=(const WalkerHackScreen &) = delete; + + // Allow move. + WalkerHackScreen(WalkerHackScreen &&) = default; + WalkerHackScreen &operator=(WalkerHackScreen &&) = default; + + bool update(float dt, bool is_resized) override; + bool draw(RenderTexture *render_texture) override; + + private: + enum ButtonType { + BUTTON_TYPE_F = 0, + BUTTON_TYPE_J, + BUTTON_TYPE_A, + BUTTON_TYPE_L, + BUTTON_TYPE_SIZE + }; + + bool *walker_hack_success; + float timer; + int font_size; + int instructions_size; + int instructions_font_size; + int type_f_size; + int type_j_size; + int type_a_size; + int type_l_size; + ButtonType button_type; + + void set_sizes(); +}; + +#endif diff --git a/wasm_build/Makefile b/wasm_build/Makefile index 2c4f795..2fc73b3 100644 --- a/wasm_build/Makefile +++ b/wasm_build/Makefile @@ -16,7 +16,8 @@ SOURCES = \ ../src/3d_helpers.cc \ ../src/raymath.cc \ ../src/walker.cc \ - ../src/surface_triangle.cc + ../src/surface_triangle.cc \ + ../src/screen_walker_hack.cc HEADERS = \ ../src/ems.h \ @@ -26,7 +27,8 @@ HEADERS = \ ../src/screen_trunner.h \ ../src/3d_helpers.h \ ../src/walker.h \ - ../src/surface_triangle.h + ../src/surface_triangle.h \ + ../src/screen_walker_hack.h OBJECTS = $(addprefix ${OBJDIR}/,$(subst ..,PREVDIR,$(subst .cc,.cc.o,${SOURCES})))