feat: add finite lifetimes and despawn mechanics for rotten and cursed apples
This commit is contained in:
1 parent
9a3a0bd9fd
commit
adda5db2f4
8 files changed
+159
-82
No files matched your search
+88
-33
@@ -3,8 +3,8 @@
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <nds.h>
|
||||
#include <consts.h>
|
||||
#include <nds.h>
|
||||
|
||||
void update_snake(GameState &state) {
|
||||
std::array<int, 3> headpos = state.snake.front();
|
||||
@@ -79,8 +79,8 @@ bool check_collision_apples_rotten(GameState &state) {
|
||||
|
||||
auto it =
|
||||
std::find_if(state.bad_apples.begin(), state.bad_apples.end(),
|
||||
[&](const std::array<int, 2> &apple) {
|
||||
return apple[0] == headpos[0] && apple[1] == headpos[1];
|
||||
[&](const EvilItem &apple) {
|
||||
return apple.x == headpos[0] && apple.y == headpos[1];
|
||||
});
|
||||
|
||||
if (it == state.bad_apples.end()) {
|
||||
@@ -100,8 +100,8 @@ bool check_collision_apples_cursed(GameState &state) {
|
||||
|
||||
auto it =
|
||||
std::find_if(state.cursed_apples.begin(), state.cursed_apples.end(),
|
||||
[&](const std::array<int, 2> &apple) {
|
||||
return apple[0] == headpos[0] && apple[1] == headpos[1];
|
||||
[&](const EvilItem &apple) {
|
||||
return apple.x == headpos[0] && apple.y == headpos[1];
|
||||
});
|
||||
|
||||
if (it == state.cursed_apples.end()) {
|
||||
@@ -127,19 +127,64 @@ void spawn_apples(GameState &state) {
|
||||
}
|
||||
|
||||
void spawn_apples_rotten(GameState &state) {
|
||||
if ((state.ticks % 10 == 0) && state.hardMode && ((rand() % 10) < 10) &&
|
||||
if ((state.ticks % 10 == 0) && state.hardMode &&
|
||||
(state.bad_apples.size() < 20)) {
|
||||
std::array<int, 2> badApple = {rand() % (X_MAX + 1), rand() % (Y_MAX + 1)};
|
||||
state.bad_apples.push_back(badApple);
|
||||
int x = rand() % (X_MAX + 1);
|
||||
int y = rand() % (Y_MAX + 1);
|
||||
for (int attempt = 0; attempt < 16 && (is_barrier_cell(state, x, y, true) ||
|
||||
(!state.snake.empty() &&
|
||||
state.snake.front()[0] == x &&
|
||||
state.snake.front()[1] == y));
|
||||
attempt++) {
|
||||
x = rand() % (X_MAX + 1);
|
||||
y = rand() % (Y_MAX + 1);
|
||||
}
|
||||
state.bad_apples.push_back({static_cast<int8_t>(x), static_cast<int8_t>(y),
|
||||
BAD_APPLE_LIFETIME_TICKS});
|
||||
}
|
||||
}
|
||||
|
||||
void spawn_apples_cursed(GameState &state) {
|
||||
if (state.cursed_apples.empty() && (state.ticks % 20 == 0) &&
|
||||
((rand() % 5) == 0)) {
|
||||
std::array<int, 2> cursedApple = {rand() % (X_MAX + 1),
|
||||
rand() % (Y_MAX + 1)};
|
||||
state.cursed_apples.push_back(cursedApple);
|
||||
int x = rand() % (X_MAX + 1);
|
||||
int y = rand() % (Y_MAX + 1);
|
||||
for (int attempt = 0; attempt < 16 && (is_barrier_cell(state, x, y, true) ||
|
||||
(!state.snake.empty() &&
|
||||
state.snake.front()[0] == x &&
|
||||
state.snake.front()[1] == y));
|
||||
attempt++) {
|
||||
x = rand() % (X_MAX + 1);
|
||||
y = rand() % (Y_MAX + 1);
|
||||
}
|
||||
state.cursed_apples.push_back({static_cast<int8_t>(x),
|
||||
static_cast<int8_t>(y),
|
||||
CURSED_APPLE_LIFETIME_TICKS});
|
||||
}
|
||||
}
|
||||
|
||||
void tick_evil_items(GameState &state) {
|
||||
for (auto it = state.bad_apples.begin(); it != state.bad_apples.end();) {
|
||||
if (it->ticksRemaining > 0) {
|
||||
it->ticksRemaining--;
|
||||
}
|
||||
if (it->ticksRemaining == 0) {
|
||||
it = state.bad_apples.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = state.cursed_apples.begin();
|
||||
it != state.cursed_apples.end();) {
|
||||
if (it->ticksRemaining > 0) {
|
||||
it->ticksRemaining--;
|
||||
}
|
||||
if (it->ticksRemaining == 0) {
|
||||
it = state.cursed_apples.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,8 +196,8 @@ void tick_status_effects(GameState &state) {
|
||||
|
||||
uint8_t calculate_intensity_31(const GameState &state) {
|
||||
const uint16_t current_apples = state.applesEaten;
|
||||
const uint16_t apples =
|
||||
std::min<uint16_t>(current_apples, static_cast<uint16_t>(SPEED_APPLES_CAP));
|
||||
const uint16_t apples = std::min<uint16_t>(
|
||||
current_apples, static_cast<uint16_t>(SPEED_APPLES_CAP));
|
||||
return static_cast<uint8_t>((static_cast<uint32_t>(apples) * 31U) /
|
||||
static_cast<uint32_t>(SPEED_APPLES_CAP));
|
||||
}
|
||||
@@ -163,12 +208,12 @@ uint16_t calculate_timer_freq_mhz(const GameState &state) {
|
||||
const uint16_t max_mhz =
|
||||
state.hardMode ? SPEED_MAX_HARD_MHZ : SPEED_MAX_NORMAL_MHZ;
|
||||
const uint16_t current_apples = state.applesEaten;
|
||||
const uint16_t apples =
|
||||
std::min<uint16_t>(current_apples, static_cast<uint16_t>(SPEED_APPLES_CAP));
|
||||
const uint16_t apples = std::min<uint16_t>(
|
||||
current_apples, static_cast<uint16_t>(SPEED_APPLES_CAP));
|
||||
const uint32_t diff = static_cast<uint32_t>(max_mhz - base_mhz);
|
||||
return static_cast<uint16_t>(
|
||||
base_mhz + ((static_cast<uint32_t>(apples) * diff) /
|
||||
static_cast<uint32_t>(SPEED_APPLES_CAP)));
|
||||
return static_cast<uint16_t>(base_mhz +
|
||||
((static_cast<uint32_t>(apples) * diff) /
|
||||
static_cast<uint32_t>(SPEED_APPLES_CAP)));
|
||||
}
|
||||
|
||||
uint16_t calculate_timer_reload(const GameState &state) {
|
||||
@@ -208,11 +253,18 @@ bool cell_in_list(const std::vector<std::array<int, 2>> &list, int x, int y) {
|
||||
});
|
||||
}
|
||||
|
||||
bool cell_in_list(const std::vector<EvilItem> &list, int x, int y) {
|
||||
return std::any_of(list.begin(), list.end(), [&](const EvilItem &item) {
|
||||
return item.x == x && item.y == y;
|
||||
});
|
||||
}
|
||||
|
||||
bool cell_blocked_for_barrier(const GameState &state, int x, int y) {
|
||||
if (is_barrier_cell(state, x, y, true)) {
|
||||
return true;
|
||||
}
|
||||
if (cell_in_list(state.apples, x, y) || cell_in_list(state.bad_apples, x, y) ||
|
||||
if (cell_in_list(state.apples, x, y) ||
|
||||
cell_in_list(state.bad_apples, x, y) ||
|
||||
cell_in_list(state.cursed_apples, x, y)) {
|
||||
return true;
|
||||
}
|
||||
@@ -311,7 +363,8 @@ void tick_barriers_frame(GameState &state) {
|
||||
}
|
||||
|
||||
if (state.barrierSpawnCooldown > 0) {
|
||||
state.barrierSpawnCooldown = static_cast<uint16_t>(state.barrierSpawnCooldown - 1U);
|
||||
state.barrierSpawnCooldown =
|
||||
static_cast<uint16_t>(state.barrierSpawnCooldown - 1U);
|
||||
}
|
||||
|
||||
for (Barrier &barrier : state.barriers) {
|
||||
@@ -337,9 +390,9 @@ void spawn_barriers(GameState &state) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int in_use = static_cast<int>(
|
||||
std::count_if(state.barriers.begin(), state.barriers.end(),
|
||||
[](const Barrier &b) { return b.phase != BarrierPhase::Inactive; }));
|
||||
const int in_use = static_cast<int>(std::count_if(
|
||||
state.barriers.begin(), state.barriers.end(),
|
||||
[](const Barrier &b) { return b.phase != BarrierPhase::Inactive; }));
|
||||
const int max_allowed = max_concurrent_barriers(state);
|
||||
const int available = max_allowed - in_use;
|
||||
if (available <= 0) {
|
||||
@@ -359,9 +412,9 @@ void spawn_barriers(GameState &state) {
|
||||
const int length_range = current_max - BARRIER_MIN_LENGTH + 1;
|
||||
|
||||
for (int spawn_idx = 0; spawn_idx < to_spawn; spawn_idx++) {
|
||||
auto free_slot =
|
||||
std::find_if(state.barriers.begin(), state.barriers.end(),
|
||||
[](const Barrier &b) { return b.phase == BarrierPhase::Inactive; });
|
||||
auto free_slot = std::find_if(
|
||||
state.barriers.begin(), state.barriers.end(),
|
||||
[](const Barrier &b) { return b.phase == BarrierPhase::Inactive; });
|
||||
if (free_slot == state.barriers.end()) {
|
||||
break;
|
||||
}
|
||||
@@ -370,11 +423,13 @@ void spawn_barriers(GameState &state) {
|
||||
for (int attempt = 0; attempt < 48; attempt++) {
|
||||
Barrier candidate;
|
||||
candidate.vertical = (rand() % 2) == 0;
|
||||
candidate.length = static_cast<uint8_t>(
|
||||
BARRIER_MIN_LENGTH + (rand() % length_range));
|
||||
candidate.length =
|
||||
static_cast<uint8_t>(BARRIER_MIN_LENGTH + (rand() % length_range));
|
||||
// Keep the whole line on screen (barriers never wrap around edges)
|
||||
const int max_x = candidate.vertical ? X_MAX : (X_MAX + 1 - candidate.length);
|
||||
const int max_y = candidate.vertical ? (Y_MAX + 1 - candidate.length) : Y_MAX;
|
||||
const int max_x =
|
||||
candidate.vertical ? X_MAX : (X_MAX + 1 - candidate.length);
|
||||
const int max_y =
|
||||
candidate.vertical ? (Y_MAX + 1 - candidate.length) : Y_MAX;
|
||||
candidate.x = static_cast<int8_t>(rand() % (max_x + 1));
|
||||
candidate.y = static_cast<int8_t>(rand() % (max_y + 1));
|
||||
|
||||
@@ -401,9 +456,9 @@ void spawn_barriers(GameState &state) {
|
||||
}
|
||||
|
||||
if (spawned_count > 0) {
|
||||
state.barrierSpawnCooldown = static_cast<uint16_t>(
|
||||
calculate_barrier_interval_frames(state) +
|
||||
(rand() % (BARRIER_INTERVAL_JITTER + 1)));
|
||||
state.barrierSpawnCooldown =
|
||||
static_cast<uint16_t>(calculate_barrier_interval_frames(state) +
|
||||
(rand() % (BARRIER_INTERVAL_JITTER + 1)));
|
||||
} else {
|
||||
state.barrierSpawnCooldown = BARRIER_RETRY_FRAMES;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user