From f63b445e52636f0e18bd9ea5e80387da79aaf070 Mon Sep 17 00:00:00 2001 From: shweet Date: Thu, 29 Aug 2024 21:47:29 -0400 Subject: [PATCH] stupid cross platform randomness issue, plus other fixes --- CMakeLists.txt | 1 + README.md | 2 + include/jsw_rand.c | 73 +++++++++++++++++++ include/jsw_rand.h | 13 ++++ src/COMMON.h | 6 -- src/engine/random.c | 39 ++++++++++ src/engine/random.h | 12 +++ src/game/ecs/ECS_COMMON.h | 2 + .../animation/component_animation_player.c | 2 +- .../animation/component_animation_target.c | 6 +- src/game/ecs/ecs.h | 1 - src/game/game.c | 2 +- src/game/game.h | 2 +- src/game/state/level/LEVEL_COMMON.h | 7 +- src/game/state/level/collectible_spawn.c | 8 +- src/game/state/level/countdown.c | 4 +- src/game/state/level/event.c | 67 ++++++++++++++--- src/game/state/level/event.h | 1 + src/game/state/level/level.c | 41 +---------- src/game/state/state.h | 2 +- 20 files changed, 217 insertions(+), 74 deletions(-) create mode 100644 include/jsw_rand.c create mode 100644 include/jsw_rand.h create mode 100644 src/engine/random.c create mode 100644 src/engine/random.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1de276b..a22dfe8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ include_directories(include) include_directories(${PROJECT_SOURCE_DIR}/src) file(GLOB src + "${PROJECT_SOURCE_DIR}/include/*.c" "${PROJECT_SOURCE_DIR}/src/*.c" "${PROJECT_SOURCE_DIR}/src/engine/*.c" "${PROJECT_SOURCE_DIR}/src/game/*.c" diff --git a/README.md b/README.md index 51c1114..f30a1ad 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ My entry for the 2024 WeightGaming Gain Jam. Action game where you collect stuff - SDL_ttf - GLEW +You'll also need the game [resources]((/files/games/frillrun/frillrun-resources-1.0.7z) in the same directory as the executable. + ### Linux This repository uses CMake to compile, so: diff --git a/include/jsw_rand.c b/include/jsw_rand.c new file mode 100644 index 0000000..24f28e1 --- /dev/null +++ b/include/jsw_rand.c @@ -0,0 +1,73 @@ +#include +#include +#include "jsw_rand.h" + +#define N 624 +#define M 397 +#define A 0x9908b0dfUL +#define U 0x80000000UL +#define L 0x7fffffffUL + +/* Internal state */ +static unsigned long x[N]; +static int next; + +/* Initialize internal state */ +void jsw_seed ( unsigned long s ) +{ + int i; + + x[0] = s & 0xffffffffUL; + + for ( i = 1; i < N; i++ ) { + x[i] = ( 1812433253UL + * ( x[i - 1] ^ ( x[i - 1] >> 30 ) ) + i ); + x[i] &= 0xffffffffUL; + } +} + +/* Mersenne Twister */ +unsigned long jsw_rand ( void ) +{ + unsigned long y, a; + int i; + + /* Refill x if exhausted */ + if ( next == N ) { + next = 0; + + for ( i = 0; i < N - 1; i++ ) { + y = ( x[i] & U ) | x[i + 1] & L; + a = ( y & 0x1UL ) ? A : 0x0UL; + x[i] = x[( i + M ) % N] ^ ( y >> 1 ) ^ a; + } + + y = ( x[N - 1] & U ) | x[0] & L; + a = ( y & 0x1UL ) ? A : 0x0UL; + x[N - 1] = x[M - 1] ^ ( y >> 1 ) ^ a; + } + + y = x[next++]; + + /* Improve distribution */ + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680UL; + y ^= (y << 15) & 0xefc60000UL; + y ^= (y >> 18); + + return y; +} + +/* Portable time seed */ +unsigned jsw_time_seed() +{ + time_t now = time ( 0 ); + unsigned char *p = (unsigned char *)&now; + unsigned seed = 0; + size_t i; + + for ( i = 0; i < sizeof now; i++ ) + seed = seed * ( UCHAR_MAX + 2U ) + p[i]; + + return seed; +} \ No newline at end of file diff --git a/include/jsw_rand.h b/include/jsw_rand.h new file mode 100644 index 0000000..b3d6850 --- /dev/null +++ b/include/jsw_rand.h @@ -0,0 +1,13 @@ +#ifndef JSW_RAND_H +#define JSW_RAND_H + +/* Seed the RNG. Must be called first */ +void jsw_seed ( unsigned long s ); + +/* Return a 32-bit random number */ +unsigned long jsw_rand ( void ); + +/* Seed with current system time */ +unsigned jsw_time_seed(); + +#endif diff --git a/src/COMMON.h b/src/COMMON.h index 5d8493c..79d8f14 100644 --- a/src/COMMON.h +++ b/src/COMMON.h @@ -25,12 +25,6 @@ #define MAX(x, max) (x > max ? max : x) #define CLAMP(x, min, max) (MIN(MAX(x, max), min)) -#define RANDOM_SEED_SET(seed) (srand(seed)) -#define RANDOM ((f32)rand() / (f32)RAND_MAX) -#define RANDOM_F32(min, max) ((f32)((RANDOM * (max - min + 1)) + min)) /* [min, max] */ -#define RANDOM_S32(min, max) ((s32)((RANDOM * (max - min + 1)) + min)) /* [min, max] */ -#define RANDOM_BOOL() (RANDOM_S32(0, 2)) - #define DISTANCE_2D(x1, x2, y1, y2) (sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2))) #define ATAN(x1, x2, y1, y2) (fmod((atan2(y2 - y1, x2 - x1) + TAU), TAU)) /* [0, 2PI] */ diff --git a/src/engine/random.c b/src/engine/random.c new file mode 100644 index 0000000..221e932 --- /dev/null +++ b/src/engine/random.c @@ -0,0 +1,39 @@ +#include "random.h" + +void +random_seed_set(u64 seed) +{ + jsw_seed(seed); +} + +void +random_seed_time_set(void) +{ + jsw_time_seed(); +} + +f32 +random_f32(void) +{ + return (f32)jsw_rand() / (f32)UINT_MAX; +} + +f32 +random_f32_in_range(f32 min, f32 max) +{ + return (f32)((random_f32() * (max - min + 1)) + min); +} + + +s32 +random_s32_in_range(s32 min, s32 max) +{ + return (s32)((random_f32() * (max - min + 1)) + min); +} + +bool +random_bool(void) +{ + return random_s32_in_range(0, 2); +} + diff --git a/src/engine/random.h b/src/engine/random.h new file mode 100644 index 0000000..fd856f5 --- /dev/null +++ b/src/engine/random.h @@ -0,0 +1,12 @@ +#pragma once + +#include "../COMMON.h" + +#include + +void random_seed_set(u64 seed); +void random_seed_time_set(void); +f32 random_f32(void); +f32 random_f32_in_range(f32 min, f32 max); +s32 random_s32_in_range(s32 min, s32 max); +bool random_bool(void); diff --git a/src/game/ecs/ECS_COMMON.h b/src/game/ecs/ECS_COMMON.h index 1453d83..c4531ad 100644 --- a/src/game/ecs/ECS_COMMON.h +++ b/src/game/ecs/ECS_COMMON.h @@ -3,6 +3,8 @@ #include "component/COMPONENT_COMMON.h" #include "../../engine/vector.h" +#include "../../engine/renderer.h" +#include "../../engine/random.h" #define ECS_FUNCTION_COUNT ECS_FUNCTION_DRAW + 1 typedef enum ECSFunctionType diff --git a/src/game/ecs/component/animation/component_animation_player.c b/src/game/ecs/component/animation/component_animation_player.c index aa0465c..b95dbfc 100644 --- a/src/game/ecs/component/animation/component_animation_player.c +++ b/src/game/ecs/component/animation/component_animation_player.c @@ -64,7 +64,7 @@ component_animation_player_tick(ComponentAnimationPlayer* self, ECS* ecs) if (playerControl->isLunge) { self->action = ANIMATION_PLAYER_ACTION_LUNGE; - sound_play(&ecs->resources->sounds[SOUND_LUNGE + RANDOM_S32(0, SOUND_LUNGE_COUNT - 1)]); + sound_play(&ecs->resources->sounds[SOUND_LUNGE + random_s32_in_range(0, SOUND_LUNGE_COUNT - 1)]); } else if (physics->isMoving) { diff --git a/src/game/ecs/component/animation/component_animation_target.c b/src/game/ecs/component/animation/component_animation_target.c index 9ae1b5b..0a67174 100644 --- a/src/game/ecs/component/animation/component_animation_target.c +++ b/src/game/ecs/component/animation/component_animation_target.c @@ -61,7 +61,7 @@ component_animation_target_tick(ComponentAnimationTarget* self, ECS* ecs) { self->action = ANIMATION_TARGET_ACTION_BURP; - sound_play(&ecs->resources->sounds[SOUND_BURP + RANDOM_S32(0, SOUND_BURP_COUNT - 1)]); + sound_play(&ecs->resources->sounds[SOUND_BURP + random_s32_in_range(0, SOUND_BURP_COUNT - 1)]); _component_animation_target_determine(self, ecs); @@ -73,7 +73,7 @@ component_animation_target_tick(ComponentAnimationTarget* self, ECS* ecs) if (self->action == ANIMATION_TARGET_ACTION_BURP && animation->isFinished) { - sound_play(&ecs->resources->sounds[SOUND_GURGLE + RANDOM_S32(0, SOUND_GURGLE_COUNT - 1)]); + sound_play(&ecs->resources->sounds[SOUND_GURGLE + random_s32_in_range(0, SOUND_GURGLE_COUNT - 1)]); takeFood->component.isDisabled = false; } @@ -86,7 +86,7 @@ component_animation_target_tick(ComponentAnimationTarget* self, ECS* ecs) takeFood->isTakeFood = false; takeFood->component.isDisabled = true; - sound_play(&ecs->resources->sounds[SOUND_GULP + RANDOM_S32(0, SOUND_GULP_COUNT - 1)]); + sound_play(&ecs->resources->sounds[SOUND_GULP + random_s32_in_range(0, SOUND_GULP_COUNT - 1)]); } else if (playerFood->value > 0) { diff --git a/src/game/ecs/ecs.h b/src/game/ecs/ecs.h index 962055a..43546fa 100644 --- a/src/game/ecs/ecs.h +++ b/src/game/ecs/ecs.h @@ -39,7 +39,6 @@ #include "component/visual/component_value_text.h" #include "../input/control.h" -#include "../../engine/renderer.h" #include "../render/postprocessing.h" #include "../resource/resources.h" diff --git a/src/game/game.c b/src/game/game.c index 46dfb29..42d883b 100644 --- a/src/game/game.c +++ b/src/game/game.c @@ -92,7 +92,7 @@ game_init(Game* self) memset(self, '\0', sizeof(Game)); - RANDOM_SEED_SET(time(NULL)); + random_seed_time_set(); resource_shader_read(&self->resources); diff --git a/src/game/game.h b/src/game/game.h index 113bf65..65b8af6 100644 --- a/src/game/game.h +++ b/src/game/game.h @@ -7,7 +7,7 @@ #define STRING_GAME_INIT "[INFO] Initializing game" #define STRING_GAME_EXIT "[INFO] Game exited" -#define STRING_WINDOW_TITLE "Game Engine" +#define STRING_WINDOW_TITLE "Frillrun" #define GAME_STATE_START STATE_TITLE static const ivec2 BUFFER_SIZES[RENDERER_BUFFER_COUNT] = diff --git a/src/game/state/level/LEVEL_COMMON.h b/src/game/state/level/LEVEL_COMMON.h index 1dedc9f..a8deb73 100644 --- a/src/game/state/level/LEVEL_COMMON.h +++ b/src/game/state/level/LEVEL_COMMON.h @@ -61,7 +61,7 @@ typedef struct LevelSettings u32 eventTime; vec4 colorCover; u32 collectibleMax; - u32 seedStart; + u64 seedStart; bool isPower; bool isEvent; bool isAmulet; @@ -94,12 +94,13 @@ typedef struct Level s32 timeToMedal; s32 timerValue; u32 levelValue; - u32 eventSeed; - u32 collectibleSeed; + u64 eventSeed; + u64 collectibleSeed; LevelMedal nextMedal; LevelEvent event; LevelEvent nextEvent; Vector textQueue; + Vector eventQueue; EntityID plane; EntityID bg; EntityID countdownTimer; diff --git a/src/game/state/level/collectible_spawn.c b/src/game/state/level/collectible_spawn.c index bfb57cc..1fc364c 100644 --- a/src/game/state/level/collectible_spawn.c +++ b/src/game/state/level/collectible_spawn.c @@ -407,7 +407,7 @@ _level_collectibles_spawn(Level* self) vector_init(&spawns, sizeof(u32)); - srand(self->collectibleSeed); + random_seed_set(self->collectibleSeed); collectibleCount = 0; @@ -416,7 +416,7 @@ _level_collectibles_spawn(Level* self) f32 random; u32 index; - random = RANDOM; + random = random_f32(); if (self->settings.isPower && self->isRottenFood) { @@ -504,8 +504,8 @@ _level_collectibles_spawn(Level* self) { vec2 testPosition; - position[0] = RANDOM_F32(LEVEL_COLLECTIBLE_SPAWN_BOUNDS[0], LEVEL_COLLECTIBLE_SPAWN_BOUNDS[2]); - position[1] = RANDOM_F32(LEVEL_COLLECTIBLE_SPAWN_BOUNDS[1], LEVEL_COLLECTIBLE_SPAWN_BOUNDS[3]); + position[0] = random_f32_in_range(LEVEL_COLLECTIBLE_SPAWN_BOUNDS[0], LEVEL_COLLECTIBLE_SPAWN_BOUNDS[2]); + position[1] = random_f32_in_range(LEVEL_COLLECTIBLE_SPAWN_BOUNDS[1], LEVEL_COLLECTIBLE_SPAWN_BOUNDS[3]); testPosition[0] = position[0]; testPosition[1] = position[1]; diff --git a/src/game/state/level/countdown.c b/src/game/state/level/countdown.c index 497e18f..18352bd 100644 --- a/src/game/state/level/countdown.c +++ b/src/game/state/level/countdown.c @@ -57,7 +57,7 @@ level_countdown_finish(Level* self) { ComponentTimer* eventTimer; - level_event_init(self); + level_event_choose(self); eventTimer = ecs_component_get(self->ecs, COMPONENT_TIMER, self->eventTimer); @@ -103,7 +103,7 @@ level_countdown_tick(Level* self) component_timer_init(countdownTimer, self->ecs, LEVEL_COUNTDOWN_TIMER_VALUE); } - if (control_pressed(self->ecs->control, CONTROL_ACTION_TERTIARY)) + if (control_pressed(self->ecs->control, CONTROL_ACTION_TERTIARY) && !self->isPaused) { level_countdown_finish(self); return; diff --git a/src/game/state/level/event.c b/src/game/state/level/event.c index d4dfa41..3d94d98 100644 --- a/src/game/state/level/event.c +++ b/src/game/state/level/event.c @@ -8,6 +8,22 @@ void _level_event_invigorate(Level* self); void _level_event_power_player_color_change_init(Level* self); void _level_event_sticky_traps_set(Level* self); void _level_event_sticky_traps_clear(Level* self); +void _level_event_queue_init(Level* self); + +void +_level_event_queue_init(Level* self) +{ + for (s32 i = 0; i < LEVEL_EVENT_COUNT; i++) + { + LevelEvent event; + + event = (LevelEvent)i; + + vector_push(&self->eventQueue, &event); + } + + random_seed_set(self->eventSeed); +} void _level_event_power_player_color_change_init(Level* self) @@ -105,7 +121,7 @@ _level_event_sticky_traps_set(Level* self) { s32 stickyTrapsCount; - stickyTrapsCount = RANDOM_S32(LEVEL_EVENT_STICKY_TRAPS_MIN, LEVEL_EVENT_STICKY_TRAPS_MAX); + stickyTrapsCount = random_s32_in_range(LEVEL_EVENT_STICKY_TRAPS_MIN, LEVEL_EVENT_STICKY_TRAPS_MAX); for (s32 i = 0; i < stickyTrapsCount; i++) { @@ -123,8 +139,8 @@ _level_event_sticky_traps_set(Level* self) { vec2 testPosition; - position[0] = RANDOM_F32(LEVEL_EVENT_STICKY_TRAPS_BOUNDS[0], LEVEL_EVENT_STICKY_TRAPS_BOUNDS[2]); - position[1] = RANDOM_F32(LEVEL_EVENT_STICKY_TRAPS_BOUNDS[1], LEVEL_EVENT_STICKY_TRAPS_BOUNDS[3]); + position[0] = random_f32_in_range(LEVEL_EVENT_STICKY_TRAPS_BOUNDS[0], LEVEL_EVENT_STICKY_TRAPS_BOUNDS[2]); + position[1] = random_f32_in_range(LEVEL_EVENT_STICKY_TRAPS_BOUNDS[1], LEVEL_EVENT_STICKY_TRAPS_BOUNDS[3]); testPosition[0] = position[0]; testPosition[1] = position[1]; @@ -373,6 +389,30 @@ level_event_return(Level* self) } } +void +level_event_choose(Level* self) +{ + u32 index; + LevelEvent* event; + + index = (u32)random_s32_in_range(0, self->eventQueue.count); + + event = (LevelEvent*)vector_get(&self->eventQueue, index); + + self->nextEvent = *event; + + vector_remove(&self->eventQueue, index); + + if (self->eventQueue.count <= 0) + _level_event_queue_init(self); + + self->eventSeed++; + + random_seed_set(self->eventSeed); +} + + + void _level_event_timer_init(Level* self) { @@ -410,11 +450,7 @@ _level_event_set(Level* self) _level_event_timer_init(self); - self->eventSeed++; - - srand(self->eventSeed); - - self->nextEvent = RANDOM_S32(LEVEL_EVENT_NONE, LEVEL_EVENT_COUNT - 1); + level_event_choose(self); } void @@ -422,6 +458,8 @@ level_event_init(Level* self) { ComponentTimer* eventTimer; + vector_init(&self->eventQueue, sizeof(EventType)); + _level_event_timer_init(self); eventTimer = ecs_component_get(self->ecs, COMPONENT_TIMER, self->eventTimer); @@ -430,9 +468,7 @@ level_event_init(Level* self) self->eventSeed = self->settings.seedStart; - srand(self->eventSeed); - - self->nextEvent = RANDOM_S32(LEVEL_EVENT_NONE, LEVEL_EVENT_COUNT - 1); + _level_event_queue_init(self); } void @@ -447,3 +483,12 @@ level_event_tick(Level* self) _level_event_power_tick(self); } + +void +level_event_free(Level* self) +{ + vector_free(&self->eventQueue); + + + +} diff --git a/src/game/state/level/event.h b/src/game/state/level/event.h index 029d878..fe81615 100644 --- a/src/game/state/level/event.h +++ b/src/game/state/level/event.h @@ -65,4 +65,5 @@ static const Rectangle LEVEL_EVENT_STICKY_TRAPS_NO_NO_RECTANGLE = void level_event_init(Level* self); void level_event_tick(Level* self); void level_event_handle(Level* self); +void level_event_choose(Level* self); void level_event_return(Level* self); diff --git a/src/game/state/level/level.c b/src/game/state/level/level.c index d9771a9..f01a529 100644 --- a/src/game/state/level/level.c +++ b/src/game/state/level/level.c @@ -4,45 +4,6 @@ static void _level_entity_init(Level* self); static void _level_finish(Level* self); static void _level_finish_text_add(Level* self); static void _level_finish_amulet_spawn(Level* self); -//static void _level_foliage_spawn(Level* self); - -/* -static void -_level_foliage_spawn(Level* self) -{ - s32 foliageCount; - - foliageCount = RANDOM_S32(LEVEL_FOLIAGE_COUNT_MIN, LEVEL_FOLIAGE_COUNT_MAX); - - for (s32 i = 0; i < foliageCount; i++) - { - FoliageType type; - vec3 position; - bool isInRectangle; - - glm_vec3_zero(position); - - type = (FoliageType)RANDOM_S32(0, FOLIAGE_TYPE_COUNT - 1); - - isInRectangle = true; - - while(isInRectangle) - { - vec2 testPosition; - - position[0] = RANDOM_F32(LEVEL_FOLIAGE_SPAWN_BOUNDS[0], LEVEL_FOLIAGE_SPAWN_BOUNDS[2]); - position[1] = RANDOM_F32(LEVEL_FOLIAGE_SPAWN_BOUNDS[1], LEVEL_FOLIAGE_SPAWN_BOUNDS[3]); - - testPosition[0] = position[0]; - testPosition[1] = position[1]; - - isInRectangle = rectangle_has_point(&LEVEL_FOLIAGE_SPAWN_NO_NO_RECTANGLE, testPosition); - } - - entity_foliage_add(self->ecs, position, type); - } -} -*/ static void _level_finish_text_add(Level* self) @@ -149,7 +110,7 @@ _level_finish(Level* self) if ((s32)targetStage->value < TARGET_ATLAS_SIZE[0] - 1) { targetStage->value++; - sound_play(&self->ecs->resources->sounds[SOUND_GURGLE + RANDOM_S32(0, SOUND_GURGLE_COUNT - 1)]); + sound_play(&self->ecs->resources->sounds[SOUND_GURGLE + random_s32_in_range(0, SOUND_GURGLE_COUNT - 1)]); } } diff --git a/src/game/state/state.h b/src/game/state/state.h index 44ad91b..bc95c52 100644 --- a/src/game/state/state.h +++ b/src/game/state/state.h @@ -396,7 +396,7 @@ static const LevelData LEVEL_DATA_DEFAULT[LEVEL_COUNT] = .time = -1 }, { - .status = LEVEL_STATUS_LOCKED, + .status = LEVEL_STATUS_UNLOCKED, .score = -1, .time = -1 },