mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-15 03:49:55 +00:00
doom: fix reboot, unreachable L1 demon and no exit; get mem: don't lock IRQs
- doom: each turn shrank the view vectors (cos = 1 - ROT/8, scale ~0.993, plus fixed-point truncation), so the level seemed to grow and after ~10 turns a zero wall height was divided by: usage fault, reboot. The view is renormalised to unit length after every turn; the ray step and wall height are clamped - doom: the L1 demon spawned inside a wall tile, so it could not be shot and the portal never opened; it now mirrors the imp at (11,4) - doom: the 60 s UI lock engaged mid-game, so the exit tap went to the lock screen; the Doom tick now keeps the lock and auto-off timers alive. The user button no longer also fires (joystick press does) - doom: sprites keep a brightness floor at distance (L2 boss is still hard to see; open) - get mem (MEM_STATS builds): walk threads with k_thread_foreach_unlocked; the stack scan under the thread-list spinlock held IRQs off long enough to trip the BLE controller's radio-ISR latency assert
This commit is contained in:
@@ -202,7 +202,12 @@ static void memStatsLine(int n, char *reply)
|
||||
struct mem_stats_walk w = { n, 0, reply };
|
||||
|
||||
reply[0] = '\0';
|
||||
k_thread_foreach(memStatsThread, &w);
|
||||
/* Unlocked: the callback scans a whole stack for its high-water mark,
|
||||
* and k_thread_foreach() holds the thread-list spinlock (interrupts off)
|
||||
* across it -- a few hundred us on nRF52, past the BLE controller's
|
||||
* 150 us inter-frame deadline: an advertising event asserted
|
||||
* (lll_adv.c "Radio ISR latency") and rebooted the node. */
|
||||
k_thread_foreach_unlocked(memStatsThread, &w);
|
||||
if (reply[0] != '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -929,6 +929,13 @@ void JoystickUITask::loop()
|
||||
|
||||
#ifdef CONFIG_ZEPHCORE_EASTER_EGG_DOOM
|
||||
if (doom_game_is_running()) {
|
||||
/* The game takes the joystick straight from the input callback, so
|
||||
* no key reaches the paths that normally keep these alive: the UI
|
||||
* locked itself mid-game and the exit key then went to the lock
|
||||
* screen (and an auto-off swallowed it as a wake press). Playing is
|
||||
* activity. */
|
||||
scheduleLockTimer();
|
||||
mc_display_reset_auto_off();
|
||||
_next_refresh = now + 500;
|
||||
if (s_schedule_render_fn) s_schedule_render_fn(500);
|
||||
return;
|
||||
|
||||
@@ -374,6 +374,54 @@ static void doom_map_init(void)
|
||||
: &level_1[0][0];
|
||||
}
|
||||
|
||||
static uint32_t isqrt64(uint64_t v)
|
||||
{
|
||||
uint64_t r = 0;
|
||||
uint64_t bit = 1ULL << 62;
|
||||
|
||||
while (bit > v) {
|
||||
bit >>= 2;
|
||||
}
|
||||
while (bit) {
|
||||
if (v >= r + bit) {
|
||||
v -= r + bit;
|
||||
r = (r >> 1) + bit;
|
||||
} else {
|
||||
r >>= 1;
|
||||
}
|
||||
bit >>= 2;
|
||||
}
|
||||
return (uint32_t)r;
|
||||
}
|
||||
|
||||
/* Turn by sin_r (signed) and put the view back on the unit circle.
|
||||
*
|
||||
* The rotation used to be cos = 1 - ROT/8, sin = ROT: a matrix with scale
|
||||
* ~0.993, plus truncation in every fp_mul, so each turn tick shrank dir and
|
||||
* plane. Half a view vector after ~1.3 full turns, 1/16 after ~5: walls read
|
||||
* as ever farther away (the level "stretches for kilometres", steps get
|
||||
* short), and around ten turns 1/ray_dir overflowed int32 in the raycaster
|
||||
* and a zero wall height divided by zero - a usage fault that rebooted the
|
||||
* node. Now: the small-angle cosine, then dir renormalised to exactly unit
|
||||
* length and plane rebuilt perpendicular to it at its 2/3 field of view. */
|
||||
static void rotate_view(struct doom_player *p, fixed_t sin_r)
|
||||
{
|
||||
fixed_t cos_r = FP_ONE - fp_mul(sin_r, sin_r) / 2;
|
||||
fixed_t dx = fp_mul(p->dir_x, cos_r) - fp_mul(p->dir_y, sin_r);
|
||||
fixed_t dy = fp_mul(p->dir_x, sin_r) + fp_mul(p->dir_y, cos_r);
|
||||
uint32_t len = isqrt64((uint64_t)((int64_t)dx * dx) + (uint64_t)((int64_t)dy * dy));
|
||||
|
||||
if (len == 0) {
|
||||
dx = 0;
|
||||
dy = -FP_ONE;
|
||||
len = FP_ONE;
|
||||
}
|
||||
p->dir_x = (fixed_t)(((int64_t)dx << FP_SHIFT) / len);
|
||||
p->dir_y = (fixed_t)(((int64_t)dy << FP_SHIFT) / len);
|
||||
p->plane_x = -(p->dir_y * 2) / 3;
|
||||
p->plane_y = (p->dir_x * 2) / 3;
|
||||
}
|
||||
|
||||
static uint8_t doom_map_get(int x, int y)
|
||||
{
|
||||
if (x < 0 || x >= MAP_W || y < 0 || y >= MAP_H) {
|
||||
@@ -648,7 +696,14 @@ static void render_one_sprite(const uint16_t *sprite,
|
||||
int draw_start_x = sprite_screen_x - sprite_h / 2;
|
||||
int draw_end_x = sprite_screen_x + sprite_h / 2;
|
||||
|
||||
/* Floor for sprites: at 8+ tiles the wall fade (brightness 1) passes one
|
||||
* dither cell in 16, so the boss, which starts ~11 tiles away in a
|
||||
* corner, drew as a few dots or nothing while its fireballs hit. Walls
|
||||
* keep the full fade; sprites keep a silhouette. */
|
||||
int brightness = dist_to_brightness(transform_y);
|
||||
if (brightness < 7) {
|
||||
brightness = 7;
|
||||
}
|
||||
|
||||
for (int sc = draw_start_x; sc < draw_end_x; sc++) {
|
||||
if (sc < 0 || sc >= SCREEN_W) continue;
|
||||
@@ -756,11 +811,13 @@ static void raycaster_render(void)
|
||||
int map_x = fp_to_int(p->x);
|
||||
int map_y = fp_to_int(p->y);
|
||||
|
||||
fixed_t delta_dist_x = (ray_dir_x == 0)
|
||||
? INT32_MAX
|
||||
/* 1/|ray_dir|, capped: near an axis it exceeds int32 and the DDA
|
||||
* below adds it up. 1024 tiles is far past any ray in a 16x16 map. */
|
||||
fixed_t delta_dist_x = (fp_abs(ray_dir_x) < (FP_ONE >> 10))
|
||||
? fp_from_int(1024)
|
||||
: fp_abs(fp_div(FP_ONE, ray_dir_x));
|
||||
fixed_t delta_dist_y = (ray_dir_y == 0)
|
||||
? INT32_MAX
|
||||
fixed_t delta_dist_y = (fp_abs(ray_dir_y) < (FP_ONE >> 10))
|
||||
? fp_from_int(1024)
|
||||
: fp_abs(fp_div(FP_ONE, ray_dir_y));
|
||||
|
||||
int step_x, step_y;
|
||||
@@ -839,6 +896,12 @@ static void raycaster_render(void)
|
||||
|
||||
int line_height = fp_to_int(fp_div(fp_from_int(SCREEN_H),
|
||||
perp_dist));
|
||||
/* A wall farther than SCREEN_H rounds to 0 lines, and the texture
|
||||
* row below divides by this: a usage fault (divide by zero) that
|
||||
* rebooted the node mid-game. */
|
||||
if (line_height < 1) {
|
||||
line_height = 1;
|
||||
}
|
||||
|
||||
int draw_start = (SCREEN_H - line_height) / 2;
|
||||
int draw_end = draw_start + line_height;
|
||||
@@ -1192,10 +1255,13 @@ static void game_init(void)
|
||||
.y = fp_from_int(4) + FP_HALF,
|
||||
.health = 30,
|
||||
};
|
||||
/* x = 11, mirroring the imp: (12,4) is inside the NE wall block, where
|
||||
* line of sight never reached the demon, so it could not be shot and the
|
||||
* portal never opened. */
|
||||
game.enemies[1] = (struct doom_enemy){
|
||||
.type = ENEMY_DEMON,
|
||||
.state = ESTATE_IDLE,
|
||||
.x = fp_from_int(12) + FP_HALF,
|
||||
.x = fp_from_int(11) + FP_HALF,
|
||||
.y = fp_from_int(4) + FP_HALF,
|
||||
.health = 60,
|
||||
};
|
||||
@@ -1247,27 +1313,11 @@ static void handle_movement(uint32_t input)
|
||||
}
|
||||
|
||||
if (input & DINPUT_LEFT) {
|
||||
fixed_t old_dx = p->dir_x;
|
||||
fixed_t old_px = p->plane_x;
|
||||
fixed_t cos_r = FP_ONE - (ROT_SPEED / 8);
|
||||
fixed_t sin_r = ROT_SPEED;
|
||||
|
||||
p->dir_x = fp_mul(old_dx, cos_r) - fp_mul(p->dir_y, sin_r);
|
||||
p->dir_y = fp_mul(old_dx, sin_r) + fp_mul(p->dir_y, cos_r);
|
||||
p->plane_x = fp_mul(old_px, cos_r) - fp_mul(p->plane_y, sin_r);
|
||||
p->plane_y = fp_mul(old_px, sin_r) + fp_mul(p->plane_y, cos_r);
|
||||
rotate_view(p, ROT_SPEED);
|
||||
}
|
||||
|
||||
if (input & DINPUT_RIGHT) {
|
||||
fixed_t old_dx = p->dir_x;
|
||||
fixed_t old_px = p->plane_x;
|
||||
fixed_t cos_r = FP_ONE - (ROT_SPEED / 8);
|
||||
fixed_t sin_r = ROT_SPEED;
|
||||
|
||||
p->dir_x = fp_mul(old_dx, cos_r) + fp_mul(p->dir_y, sin_r);
|
||||
p->dir_y = -fp_mul(old_dx, sin_r) + fp_mul(p->dir_y, cos_r);
|
||||
p->plane_x = fp_mul(old_px, cos_r) + fp_mul(p->plane_y, sin_r);
|
||||
p->plane_y = -fp_mul(old_px, sin_r) + fp_mul(p->plane_y, cos_r);
|
||||
rotate_view(p, -ROT_SPEED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1779,7 +1829,9 @@ void doom_game_input(uint16_t code, int32_t value)
|
||||
bit = DINPUT_LEFT;
|
||||
break;
|
||||
case INPUT_KEY_ENTER:
|
||||
case INPUT_KEY_0:
|
||||
/* Not INPUT_KEY_0: on the Wio that is the user button, whose tap is
|
||||
* the way out (KEY_1 via the multi-tap filter); firing on it too
|
||||
* shot on every exit. */
|
||||
bit = DINPUT_FIRE;
|
||||
break;
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user