An hour or two on a previous evening were devoted to a quick test of the graphics idea, and some brainstorming about what the game should be like.
Some noteable game code elements:
The noise generator uses 64KB of "precomputed" random bytes, generated by XORing the Mersenne Twister with the linear congruential generator used by BCPL (in other words, stb_rand()^stb_randLCG() ), combined with 32K random indexes into the 64KB table.
Sample room (scaled-down):
Sample creature:
Creature art is dynamically stretched and squished when drawn
to give it more "life".
Here's one example:
void object_waypoint(Object *o)
{
switch (o->id) {
// wait only at end points of path
case 1:
case 22: case 23: case 24: case 25: case 26: case 27: case 28:
if (o->target != 1 && o->target != o->path->num_path) o->wait = 0;
break;
// pick a random next destination
case 6: case 7: {
int n = stb_rand() % (o->path->num_path-2);
if (o->target > o->prev_target) {
if (n >= o->prev_target) ++n;
if (n >= o->target) ++n;
} else {
if (n >= o->target) ++n;
if (n >= o->prev_target) ++n;
}
o->target = n;
break;
}
// teleport back to start
case 8: case 9: case 10: case 11:
if (o->target == o->path->num_path-1) {
o->target = 1;
o->prev_target = 0;
}
break;
// teleport back to start
case 19: case 20:
if (o->target == 0) {
o->target = 1;
o->prev_target = 0;
}
break;
// when we reach a waypoint, hide
case 18:
o->y = 4000;
break;
}
}
The inner loop of the noise display looks like this:
for (i=0; i < z->w; ++i) {
int c = z->pixels[j*z->w + i];
uint8 n = noises[c][i];
out->pixels[j*out->w+i] = clut[n];
}
Here's the entire music player:
// MUSIC PLAYER
#ifdef _DEBUG
int audio=1;
#else
int audio=1;
#endif
stb_vorbis *vorb;
short (*audio_data)[2];
int length_decoded;
uint next_measure_time;
stb_thread vthread;
void * decode_vorbis(void *p)
{
int pos=0;
for(;;) {
int n = stb_vorbis_get_frame_short_interleaved(vorb, 2, audio_data[pos], 8192);
if (n == 0) {
vthread = NULL;
return NULL;
}
pos += n;
length_decoded = pos;
stb_barrier();
if (next_measure_time) // once playback has started, start sleeping
Sleep(1);
}
}
void load_vorbis(void)
{
int total_len;
int len;
uint8 *data = packed_stb_file("merged.ogg", &len);
vorb = stb_vorbis_open_memory(data, len, NULL, NULL);
total_len = stb_vorbis_stream_length_in_samples(vorb);
audio_data = malloc(total_len * sizeof(audio_data[0]));
if (!audio_data) outofmem();
vthread = stb_create_thread(decode_vorbis, NULL);
}
typedef struct
{
int id;
int loc;
int len;
int start_offset;
int duration;
} Measure;
Measure *measures;
#define music ((void *) 1)
int play_measure(int m)
{
int i;
if (stb_arr_len(measures) == 0) return 0;
for (i=0; i < stb_arr_len(measures); ++i) {
if (measures[i].id == m) {
Measure *z = &measures[i];
if (z->loc + z->len <= length_decoded)
mixlow_add_playback(audio_data[z->loc], z->len, 1, 2, 0, next_measure_time - z->
t_offset, z->len, 1, FADE_none,0,0, 1,0,music);
else {
next_measure_time = mixhigh_time() + 4000;
return 0;
}
next_measure_time += z->duration;
return 1;
}
}
return 1;
}
int *music_loop, mloop_len, mloop_next, mloop_start;
void set_loop(int *loop, int len, int start)
{
if (music_loop == loop && mloop_len == len) return;
music_loop = loop;
mloop_len = len;
mloop_next = 0;
mloop_start = start;
}
void update_measure(void)
{
if (!audio) return;
while (next_measure_time < mixhigh_time() + 6000) {
if (mloop_next >= mloop_len) mloop_next = 0;
if (!play_measure(music_loop[mloop_next])) return;
++mloop_next;
if (mloop_next >= mloop_len) mloop_next = mloop_start;
}
}
int title_loop[] =
{
1,1,
2,27,5,30,
2,27,3,28,
4,29,5,27,
6,31,7,32,
6,31,7,32,
8,33,7,32,
};
#define MLOOP(x) set_loop(x, sizeof(x)/sizeof(x[0]), 0)
#define MLOOPS(x,y) set_loop(x, sizeof(x)/sizeof(x[0]), y)
short *intro;
int intro_len;
void load_music(void)
{
int i,n=0,c,t,mlen;
char **data, *mem;
if (!audio) return;
mem = packed_stb_file("static_title_3.ogg", &mlen);
if (mem)
n = stb_vorbis_decode_memory(mem, mlen, &c, &intro);
#ifdef EDITOR
if (n) n = 2000;
#endif
t = mixhigh_time() + 4000;
if (n)
mixlow_add_playback(intro, n, TRUE, c, 0, t, n, 1, FADE_none,0,0, 0.75, 0, music);
next_measure_time = t + n + 44100 * 0.5;
mem = packed_stb_file("merged_data.txt", &mlen);
if (mem) {
mem[mlen-1] = 0;
data = stb_tokens(mem, "\r\n", &n);
//data = stb_stringfile("merged_data.txt", &n);
for (i=0; i < n; ++i) {
Measure m;
sscanf(data[i], "%d %d %d %d %d", &m.id, &m.loc, &m.len, &m.start_offset, &m.duration);
stb_arr_push(measures, m);
}
}
load_vorbis();
MLOOPS(title_loop,6);
}
void audio_loopmode(void)
{
if (!audio) return;
// we're trying to run at at least 30hz, which is:
// 44100 / 30 = 4410 / 3 = 1300 samples
mixhigh_step(4000);
if (next_measure_time == 0) {
if (length_decoded >= 44100*4) { // wait until 4 seconds of audio is decoded
next_measure_time = mixhigh_time() + 2000;
}
return;
}
update_measure();
}
// atexit function
void music_stop(int nice)
{
int t = mixhigh_time();
if (vthread) stb_destroy_thread(vthread);
mixlow_end_set(music, FADE_linear, 0, nice ? 32000 : 3000);
while (mixlow_num_active()) {
mixhigh_step(4000);
Sleep(10);
}
mixhigh_deinit();
}
![]() |