#endif
// third party includes
+#include <raylib.h>
#include <raymath.h>
// local includes
camera_target{0.0F, 0.0F, 0.0F},
mouse_hit{0.0F, 0.0F, 0.0F},
idx_hit(SURFACE_UNIT_WIDTH / 2 +
- (SURFACE_UNIT_HEIGHT / 2) * SURFACE_UNIT_WIDTH) {
+ (SURFACE_UNIT_HEIGHT / 2) * SURFACE_UNIT_WIDTH),
+ controlled_walker_idx(std::nullopt),
+ left_text_width(MeasureText("Left", BUTTON_FONT_SIZE)),
+ right_text_width(MeasureText("Right", BUTTON_FONT_SIZE)),
+ forward_text_width(MeasureText("Forward", BUTTON_FONT_SIZE)) {
#ifndef NDEBUG
std::cout << "idx_hit initialized to " << idx_hit << std::endl;
#endif
}
bool TRunnerScreen::update(float dt) {
+ if (controlled_walker_idx.has_value()) {
+ auto walker_body_pos =
+ walkers[controlled_walker_idx.value()].get_body_pos();
+
+ camera_target = walker_body_pos + Vector3{0.0F, 1.0F, 0.0F};
+
+ float rotation = walkers[controlled_walker_idx.value()].get_rotation();
+
+ Vector3 offset = get_rotation_matrix_about_y(rotation + PI) *
+ Vector3{1.0F, 0.0F, 0.0F} * 4.0F +
+ Vector3{0.0F, 4.0F, 0.0F};
+
+ camera_pos = walkers[controlled_walker_idx.value()].get_body_pos() + offset;
+ }
+
+ if (controlled_walker_idx.has_value() && IsMouseButtonDown(0)) {
+ // Check if clicked on button.
+ if (GetTouchX() >= 0 && GetTouchX() <= left_text_width &&
+ GetTouchY() >= GetScreenHeight() - BUTTON_FONT_SIZE &&
+ GetTouchY() <= GetScreenHeight()) {
+ walkers[controlled_walker_idx.value()].player_turn_left();
+ goto post_check_click;
+ } else if (GetTouchX() >= left_text_width &&
+ GetTouchX() <= left_text_width + right_text_width &&
+ GetTouchY() >= GetScreenHeight() - BUTTON_FONT_SIZE &&
+ GetTouchY() <= GetScreenHeight()) {
+ walkers[controlled_walker_idx.value()].player_turn_right();
+ goto post_check_click;
+ } else if (int width_mid = (left_text_width + right_text_width) / 2 -
+ forward_text_width / 2;
+ GetTouchX() >= width_mid &&
+ GetTouchX() <= width_mid + forward_text_width &&
+ GetTouchY() >= GetScreenHeight() - BUTTON_FONT_SIZE * 2 &&
+ GetTouchY() <= GetScreenHeight() - BUTTON_FONT_SIZE) {
+ walkers[controlled_walker_idx.value()].player_go_forward();
+ goto post_check_click;
+ }
+ }
+
if (IsMouseButtonPressed(0)) {
float press_x = GetTouchX();
float press_y = GetTouchY();
<< ray.direction.y << ", " << ray.direction.z << std::endl;
#endif
+ // Check if clicked on a Walker.
+ for (unsigned int idx = 0; idx < walkers.size(); ++idx) {
+ if (auto walker_bb = walkers[idx].get_body_bb();
+ GetRayCollisionBox(ray, walker_bb).hit) {
+ if (controlled_walker_idx.has_value()) {
+ walkers[controlled_walker_idx.value()].set_player_controlled(false);
+ }
+ controlled_walker_idx = idx;
+ walkers[controlled_walker_idx.value()].set_player_controlled(true);
+
+ idx_hit = SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT;
+
+ goto post_check_click;
+ }
+ }
+
+ // Check if clicked on ground.
for (unsigned int idx = 0; idx < SURFACE_UNIT_WIDTH * SURFACE_UNIT_HEIGHT;
++idx) {
int x = idx % SURFACE_UNIT_WIDTH;
this->camera_pos.z = 0.0F;
}
this->camera_target.y += 1.0F;
+ if (this->controlled_walker_idx.has_value()) {
+ this->walkers[this->controlled_walker_idx.value()]
+ .set_player_controlled(false);
+ this->controlled_walker_idx = std::nullopt;
+ }
};
if (auto bb_collision = GetRayCollisionBox(ray, surface_bbs[idx]);
if (auto collision = GetRayCollisionTriangle(ray, nw, sw, ne);
collision.hit) {
on_collide_fn(collision.point);
- break;
+ goto post_check_click;
} else if (auto collision = GetRayCollisionTriangle(ray, ne, sw, se);
collision.hit) {
on_collide_fn(collision.point);
- break;
+ goto post_check_click;
}
}
}
+ } else if (IsMouseButtonReleased(0)) {
+ if (controlled_walker_idx.has_value()) {
+ walkers[controlled_walker_idx.value()].player_idle();
+ }
}
+post_check_click:
+
camera_to_targets(dt);
for (auto &walker : walkers) {
}
// TODO DEBUG
- DrawLine3D(Vector3{0.0F, 3.0F, 0.0F}, mouse_hit, BLACK);
+ if (!controlled_walker_idx.has_value()) {
+ DrawLine3D(Vector3{0.0F, 3.0F, 0.0F}, mouse_hit, BLACK);
+ }
EndMode3D();
+
+ if (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, 120});
+ DrawText("Left", 0, GetScreenHeight() - BUTTON_FONT_SIZE, BUTTON_FONT_SIZE,
+ BLACK);
+
+ total_width += left_text_width;
+ DrawRectangle(total_width, GetScreenHeight() - BUTTON_FONT_SIZE,
+ right_text_width, BUTTON_FONT_SIZE,
+ Color{255, 255, 255, 120});
+ DrawText("Right", total_width, GetScreenHeight() - BUTTON_FONT_SIZE,
+ BUTTON_FONT_SIZE, BLACK);
+
+ total_width = (total_width + right_text_width) / 2 - forward_text_width / 2;
+ DrawRectangle(total_width, GetScreenHeight() - BUTTON_FONT_SIZE * 2,
+ forward_text_width, BUTTON_FONT_SIZE,
+ Color{255, 255, 255, 120});
+ DrawText("Forward", total_width, GetScreenHeight() - BUTTON_FONT_SIZE * 2,
+ BUTTON_FONT_SIZE, BLACK);
+ }
+
EndDrawing();
return false;
constexpr float SURFACE_HEIGHT_INTERVAL = 0.7F;
+constexpr int BUTTON_FONT_SIZE = 30;
+
class TRunnerScreen : public Screen {
public:
TRunnerScreen(std::weak_ptr<ScreenStack> stack);
Vector3 camera_target;
Vector3 mouse_hit;
unsigned int idx_hit;
+ std::optional<unsigned int> controlled_walker_idx;
+ const int left_text_width;
+ const int right_text_width;
+ const int forward_text_width;
void camera_to_targets(float dt);
};
flags |= 1;
}
}
+
+void Walker::set_player_controlled(bool player_controlled) {
+ if (player_controlled) {
+ flags &= ~0x3B;
+ flags |= 8;
+ target_body_pos = body_pos;
+ } else {
+ flags &= ~0x38;
+ }
+}
+
+void Walker::player_idle() { flags &= ~0x30; }
+
+void Walker::player_turn_left() {
+ flags &= ~0x30;
+ flags |= 0x10;
+ target_body_pos = body_pos;
+}
+
+void Walker::player_turn_right() {
+ flags &= ~0x30;
+ flags |= 0x20;
+ target_body_pos = body_pos;
+}
+
+void Walker::player_go_forward() { flags |= 0x30; }
+
+BoundingBox Walker::get_body_bb() const {
+ return BoundingBox{
+ .min = body_pos - Vector3{0.5F,
+ 0.5F + BODY_IDLE_MOVE_AMOUNT *
+ std::sin(body_idle_move_timer + PI),
+ 0.5F},
+ .max = body_pos + Vector3{0.5F,
+ 0.5F + BODY_IDLE_MOVE_AMOUNT *
+ std::sin(body_idle_move_timer + PI),
+ 0.5F}};
+}
+
+float Walker::get_rotation() const { return rotation; }
+
+Vector3 Walker::get_body_pos() const { return body_pos; }
void set_body_pos(Vector3 pos);
+ void set_player_controlled(bool player_controlled);
+ void player_idle();
+ void player_turn_left();
+ void player_turn_right();
+ void player_go_forward();
+
+ BoundingBox get_body_bb() const;
+ float get_rotation() const;
+ Vector3 get_body_pos() const;
+
private:
Vector3 body_pos;
Vector3 target_body_pos;
// ???? ??01 - rotating to move
// ???? ??10 - moving
// ???? ?1?? - auto roaming
+ // ???? 1??? - player controlled
+ // ??00 ???? - player controlled: idle
+ // ??01 ???? - player controlled: turn left
+ // ??10 ???? - player controlled: turn right
+ // ??11 ???? - player controlled: go forward
unsigned int flags;
const float body_height;
template <std::size_t BBCount>
void Walker::update(float dt, const std::array<BoundingBox, BBCount> &bbs,
unsigned int width, unsigned int height) {
- if ((flags & 4) != 0 && (flags & 3) == 0) {
+ if ((flags & 8) == 0 && (flags & 4) != 0 && (flags & 3) == 0) {
roaming_timer += dt;
if (roaming_timer > roaming_time) {
roaming_timer = 0.0F;
}
// body rotation
- if ((flags & 3) == 1) {
- float diff = target_rotation - rotation;
- if (diff > PI) {
- rotation -= dt * BODY_ROTATION_SPEED;
- if (rotation < 0.0F) {
- rotation += PI * 2.0F;
+ if ((flags & 8) == 0) {
+ if ((flags & 3) == 1) {
+ float diff = target_rotation - rotation;
+ if (diff > PI) {
+ rotation -= dt * BODY_ROTATION_SPEED;
+ if (rotation < 0.0F) {
+ rotation += PI * 2.0F;
+ }
+ } else if (diff < -PI) {
+ rotation += dt * BODY_ROTATION_SPEED;
+ if (rotation > PI * 2.0F) {
+ rotation -= PI * 2.0F;
+ }
+ } else if (diff > 0.0F) {
+ rotation += dt * BODY_ROTATION_SPEED;
+ if (rotation > PI * 2.0F) {
+ rotation -= PI * 2.0F;
+ }
+ } else {
+ rotation -= dt * BODY_ROTATION_SPEED;
+ if (rotation < 0.0F) {
+ rotation += PI * 2.0F;
+ }
}
- } else if (diff < -PI) {
- rotation += dt * BODY_ROTATION_SPEED;
- if (rotation > PI * 2.0F) {
- rotation -= PI * 2.0F;
+
+ if (std::abs(target_rotation - rotation) < dt * BODY_ROTATION_SPEED) {
+ rotation = target_rotation;
+ flags &= ~3;
+ flags |= 2;
}
- } else if (diff > 0.0F) {
+ }
+ } else {
+ if ((flags & 0x30) == 0x10) {
rotation += dt * BODY_ROTATION_SPEED;
- if (rotation > PI * 2.0F) {
- rotation -= PI * 2.0F;
- }
- } else {
+ } else if ((flags & 0x30) == 0x20) {
rotation -= dt * BODY_ROTATION_SPEED;
- if (rotation < 0.0F) {
- rotation += PI * 2.0F;
- }
- }
-
- if (std::abs(target_rotation - rotation) < dt * BODY_ROTATION_SPEED) {
- rotation = target_rotation;
- flags &= ~3;
- flags |= 2;
}
}
+
+ const Matrix rotationMatrix = get_rotation_matrix_about_y(rotation);
+
// body to target pos
- if ((flags & 3) == 2) {
- float diff = Vector3Distance(target_body_pos, body_pos);
- body_pos = body_pos + Vector3Normalize(target_body_pos - body_pos) *
- (dt * BODY_TARGET_SPEED);
- if (Vector3Distance(target_body_pos, body_pos) > diff) {
- flags &= ~3;
- body_pos = target_body_pos;
+ if ((flags & 8) == 0) {
+ if ((flags & 3) == 2) {
+ float diff = Vector3Distance(target_body_pos, body_pos);
+ body_pos = body_pos + Vector3Normalize(target_body_pos - body_pos) *
+ (dt * BODY_TARGET_SPEED);
+ if (Vector3Distance(target_body_pos, body_pos) > diff) {
+ flags &= ~3;
+ body_pos = target_body_pos;
+ }
+ }
+ } else if ((flags & 0x30) == 0x30) {
+ Vector3 dir = rotationMatrix * Vector3{1.0F, 0.0F, 0.0F};
+ Vector3 prev_body_pos = body_pos;
+ body_pos = body_pos + dir * (dt * BODY_TARGET_SPEED);
+ if (body_pos.x < SURFACE_X_OFFSET - (float)SURFACE_UNIT_WIDTH ||
+ body_pos.x > SURFACE_X_OFFSET + (float)SURFACE_UNIT_WIDTH ||
+ body_pos.z < SURFACE_Y_OFFSET - (float)SURFACE_UNIT_HEIGHT ||
+ body_pos.z > SURFACE_Y_OFFSET + (float)SURFACE_UNIT_HEIGHT) {
+ body_pos = prev_body_pos;
}
+ target_body_pos = body_pos + dir * 1.0F;
}
// moving legs
- const Matrix rotationMatrix = get_rotation_matrix_about_y(rotation);
const auto update_leg_fn = [this, &bbs, dt, &rotationMatrix](
Vector3 &leg_target, Vector3 &leg_pos,
unsigned int &flags,
((nw_flags & 7) == 1 ? 1 : 0) + ((ne_flags & 7) == 1 ? 1 : 0) +
((se_flags & 7) == 1 ? 1 : 0));
- if ((flags & 3) == 0) {
- body_idle_move_timer += dt * BODY_IDLE_TIMER_RATE;
- if (body_idle_move_timer > PI * 2.0F) {
- body_idle_move_timer -= PI * 2.0F;
+ if ((flags & 8) == 0) {
+ if ((flags & 3) == 0) {
+ body_idle_move_timer += dt * BODY_IDLE_TIMER_RATE;
+ if (body_idle_move_timer > PI * 2.0F) {
+ body_idle_move_timer -= PI * 2.0F;
+ }
+ } else if (!FloatEquals(body_idle_move_timer, 0.0F)) {
+ if (body_idle_move_timer < PI) {
+ body_idle_move_timer += dt * BODY_IDLE_TIMER_RATE;
+ if (body_idle_move_timer > PI) {
+ body_idle_move_timer = 0;
+ }
+ } else {
+ body_idle_move_timer += dt * BODY_IDLE_TIMER_RATE;
+ if (body_idle_move_timer > PI * 2.0F) {
+ body_idle_move_timer = 0.0F;
+ }
+ }
}
} else if (!FloatEquals(body_idle_move_timer, 0.0F)) {
if (body_idle_move_timer < PI) {