stupid cross platform randomness issue, plus other fixes

This commit is contained in:
shweet 2024-08-29 21:47:29 -04:00
parent 3599baea22
commit f63b445e52
20 changed files with 217 additions and 74 deletions

View File

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

View File

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

73
include/jsw_rand.c Normal file
View File

@ -0,0 +1,73 @@
#include <limits.h>
#include <time.h>
#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;
}

13
include/jsw_rand.h Normal file
View File

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

View File

@ -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] */

39
src/engine/random.c Normal file
View File

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

12
src/engine/random.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include "../COMMON.h"
#include <jsw_rand.h>
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);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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