Impl. "hacking" a "Walker"
All checks were successful
Build and Publish WASM version of demo / Build-And-Deploy (push) Successful in 21s

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.
This commit is contained in:
Stephen Seo 2023-08-23 16:37:26 +09:00
parent 17aa7f6860
commit d7d1d4d7a0
10 changed files with 346 additions and 27 deletions

View file

@ -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}))

View file

@ -1,6 +1,8 @@
#include "screen.h"
// standard library includes
#include <raylib.h>
#include <cassert>
#ifndef NDEBUG
#include <iostream>
@ -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()) {

View file

@ -1,14 +1,17 @@
#ifndef JUMPARTIFACT_DOT_COM_DEMO_0_SCREEN_H_
#define JUMPARTIFACT_DOT_COM_DEMO_0_SCREEN_H_
#include <raylib.h>
#include <deque>
#include <functional>
#include <memory>
#include <variant>
#include <vector>
// Forward declaration.
// Forward declarations.
class ScreenStack;
struct RenderTexture;
class Screen {
public:
@ -17,6 +20,9 @@ class Screen {
template <typename SubScreen>
static Ptr new_screen(std::weak_ptr<ScreenStack> stack);
template <typename SubScreen, typename... Args>
static Ptr new_screen_args(std::weak_ptr<ScreenStack> 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<ScreenStack> 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 <typename SubScreen>
void push_constructing_screen();
template <typename SubScreen, typename... Args>
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<RenderTexture> render_texture;
Weak self_weak;
std::vector<Screen::Ptr> stack;
std::deque<PendingAction> actions;
@ -105,6 +119,12 @@ Screen::Ptr Screen::new_screen(std::weak_ptr<ScreenStack> stack) {
return std::unique_ptr<SubScreen>(new SubScreen{stack});
}
template <typename SubScreen, typename... Args>
Screen::Ptr Screen::new_screen_args(std::weak_ptr<ScreenStack> stack,
Args... args) {
return std::unique_ptr<SubScreen>(new SubScreen{stack, args...});
}
template <typename SubScreen>
void ScreenStack::push_screen() {
actions.emplace_back(Screen::new_screen<SubScreen>(self_weak));
@ -117,4 +137,11 @@ void ScreenStack::push_constructing_screen() {
});
}
template <typename SubScreen, typename... Args>
void ScreenStack::push_constructing_screen_args(Args... args) {
actions.emplace_back([args...](ScreenStack::Weak ss) -> Screen::Ptr {
return Screen::new_screen_args<SubScreen>(ss, args...);
});
}
#endif

View file

@ -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;
}

View file

@ -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;

View file

@ -16,6 +16,7 @@
// local includes
#include "3d_helpers.h"
#include "ems.h"
#include "screen_walker_hack.h"
TRunnerScreen::TRunnerScreen(std::weak_ptr<ScreenStack> stack)
: Screen(stack),
@ -55,7 +56,8 @@ TRunnerScreen::TRunnerScreen(std::weak_ptr<ScreenStack> 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<WalkerHackScreen>(
&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) {

View file

@ -37,8 +37,8 @@ class TRunnerScreen : public Screen {
TRunnerScreen(std::weak_ptr<ScreenStack> 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();

191
src/screen_walker_hack.cc Normal file
View file

@ -0,0 +1,191 @@
#include "screen_walker_hack.h"
// standard library includes
#include <cassert>
#ifndef NDEBUG
#include <iostream>
#endif
// third party includes
#include <raylib.h>
// 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);
}

50
src/screen_walker_hack.h Normal file
View file

@ -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

View file

@ -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})))